Build on the BazPay REST API
No connector for your stack, or you outgrew one. Three routes reach the same gateway — they differ in who renders the card form, who carries the PCI scope and how many days it takes.
- TypeGuide
- Runs onAny language over HTTPS
- First installTwo days to two weeks
- Reviewed
Choosing a path
Three variables decide this, and only three. Everything else follows from them: who renders the card inputs, what that does to your PCI scope, and how many engineering days you can spend before the first live payment.
| Route | You build | PCI scope | Time | Right when |
|---|---|---|---|---|
| Hosted checkout | A redirect and a webhook handler | SAQ A | About a day | You want method selection, localisation and retries maintained for you. |
| Hosted fields | Your checkout, our iframes for the inputs | SAQ A | Two to five days | Brand control matters and you want one page, not a hand-off. |
| Direct API | Everything above the token | SAQ A with fields, higher without | One to two weeks | You are a platform or marketplace and own routing and payouts. |
SAQ A applies while every cardholder-data function is outsourced and your page never touches the primary account number. Our iframes satisfy that because the inputs belong to our origin. The moment you copy a card number into a field you own — even to reformat it for display — you leave SAQ A for SAQ A-EP and inherit a materially larger audit. There is no partial version of this rule.
Hosted checkout
Create a payment without naming a method, and we return a URL. Send the shopper there. We render every method enabled for their market, run authentication, and send you a signed event when it is done.
# hosted checkout: omit the method and we render the pickercurl -X POST https://api.bazpay.com/v1/payments \ -H "Authorization: Bearer sk_test_9f2c…" \ -H "BazPay-Version: 2026-04-01" \ -H "Idempotency-Key: ord_10482" \ -d '{ "amount": 4200, "currency": "EUR", "reference": "ORDER-10482", "return_url": "https://shop.example/thanks" }'{ "id": "pay_3fJ2Qk8vTn", "status": "requires_action", "next_action": { "type": "redirect", "url": "https://pay.bazpay.com/c/3fJ2Qk8vTn" } } Omitting method is what selects hosted checkout. Name a method instead and you get that method directly, which is how the plugins drive a one-method button.
What you get for a day of work: method selection by billing country and IP, localisation into the shopper's language, retry handling when a bank redirect fails, and a page that stays current as schemes change their rules. What you give up: the last few percent of brand control, and a page transition in the middle of your checkout.
Hosted fields
The middle route, and the one most bespoke storefronts should take. Your checkout, your layout, your validation copy — with the card inputs supplied as iframes from our origin, so the card number never lands on your server.
<!-- your checkout page, your markup, our inputs --><div id="card-field"></div><script src="https://js.bazpay.com/v1/bazpay.js"></script>// the publishable key is safe in the browserconst bazpay = BazPay('pk_test_4c81…');const card = bazpay.field('card');card.mount('#card-field');// on submit: tokenise, then POST the token to YOUR serverconst result = await card.tokenise();await fetch('/checkout/pay', { method: 'POST', body: JSON.stringify({ token: result.token })});// the PAN never reaches your origin. That is the whole point. Styling is passed as options at mount time rather than by writing CSS into the iframe, which is what keeps the frame inaccessible to your page and therefore keeps you at SAQ A.
Budget most of the two-to-five days for error states rather than the happy path. A card form is easy; a card form that behaves correctly when the issuer declines for insufficient funds, when the shopper mistypes an expiry, and when the network drops mid-authentication is the work. Sandbox behaviour lists the cards that force each of those on demand, which is the fastest way to build them properly.
Direct API
Server-to-server over REST, with the token from hosted fields. You drive capture policy, exemption requests, payouts and refunds yourself, and every response carries the raw issuer reason code rather than a simplified status.
# your server, with the token the browser producedcurl -X POST https://api.bazpay.com/v1/payments \ -H "Authorization: Bearer sk_test_9f2c…" \ -H "BazPay-Version: 2026-04-01" \ -H "Idempotency-Key: ord_10482" \ -d '{ "amount": 4200, "currency": "EUR", "token": "tok_7Hs2Nb", "capture": false, "return_url": "https://shop.example/3ds-return" }'# authentication is required more often than not under PSD2{ "id": "pay_3fJ2Qk8vTn", "status": "requires_action", "next_action": { "type": "three_ds_redirect", "url": "https://pay.bazpay.com/3ds/…" } } capture:false authorises without taking the money. Capture later against the payment id when the goods actually ship.
This is the route for platforms, marketplaces and anyone splitting a payment across sellers. It is also the route that needs the most testing, because you now own the state machine: what happens when a capture is attempted twice, when an authorisation expires before shipment, and when a refund is requested for a payment that has not settled.
- Every mutating call takes an idempotency key, so a retry after a timeout is safe.
- The API version is pinned by a dated header, so nothing changes under you.
- Rate limits are per key and published; you will not discover them in production.
- Payouts, subscriptions and reports are separate resources on the same contract.
The endpoint-by-endpoint detail lives in the API reference, and authentication and idempotency covers key scopes, the four request headers and the retry semantics in full.
Why the webhook is the truth
Whichever path you take, the same rule holds: the API response tells you what happened at the moment you asked. The webhook tells you what happened. Those are not the same thing, and integrations that confuse them fail in exactly one direction — goods shipped against money that never arrived.
- A shopper who closes the tab after paying still generates the success event.
- A bank method can confirm minutes after the redirect returns.
- A dispute or a late reversal has no API call of yours to answer.
- Delivery is at least once, so your handler must be idempotent on the event id.
Verify the signature on every event before you act on it. The header, the signing scheme, the retry ladder and how to replay history are all in the webhooks guide.
What we own, what you own
Worth agreeing before the build starts, because most integration disputes are really disagreements about this table.
| Concern | Owner | Notes |
|---|---|---|
| Card data storage | BazPay | Vaulting is gateway-side only. Never store a PAN on your infrastructure. |
| 3-D Secure 2 | BazPay | You handle a redirect; we implement the protocol and the exemption logic. |
| Scheme compliance | BazPay | Mandates, descriptor rules and certification stay on our side. |
| Order state | You | We report the payment; what that means for an order is your decision. |
| Idempotency of your handler | You | Delivery is at least once. Dedupe on the event id. |
| Refund policy | You | We execute refunds; deciding who gets one is a commercial question. |
| Dispute evidence | Shared | You supply the proof of delivery; we file it and manage the deadline. |
Changing path later
None of these choices is permanent, because all three sit on the same resources. Moving is a change to how the payment is initiated, not a re-integration.
- Hosted checkout to hosted fields: keep your webhook handler, replace the redirect.
- Hosted fields to direct: the token flow is unchanged; you take over capture and refunds.
- Plugin to API: the plugin was already calling these endpoints with your keys.
- API back to a plugin: also fine, if the store moved onto a supported cart.
Payment ids, stored credentials and settlement history all survive the move, so reporting does not restart from zero. If you are weighing this against installing one of the five connectors, the capability matrix on the hub shows exactly what a plugin would and would not do for you.
Common questions
- Is anything reserved for the official plugins?
- No. The five cart connectors call the same public endpoints with the same keys, and there is no partner-only parameter or private route behind them. Anything a plugin can do, your own integration can do, which is also why moving off a plugin later is not a migration.
- How long does a first integration realistically take?
- Hosted checkout is a day for one engineer who already has a webhook handler. Hosted fields is two to five days, most of it styling and error states rather than payment logic. A full server-to-server integration with routing, exemptions and reconciliation is closer to two weeks, and the second half of that is testing the unhappy paths.
- Do we need to handle 3-D Secure ourselves?
- You need to handle a redirect. When the issuer wants authentication, the payment comes back with a next action and a URL; you send the shopper there and they return to your return URL. You never implement the protocol itself, and the exemption logic that avoids the challenge where the rules allow runs on our side.
- Can we test everything before signing anything?
- Yes. The sandbox opens at sign-up with no approval step, no sales call and no time limit. Underwriting only applies to live keys, and it runs in parallel with your build rather than in front of it.
- What happens if we send the same request twice?
- Nothing, if you send an idempotency key with it. A repeated key returns the original response instead of creating a second payment, which makes retries safe after a timeout — the case where you genuinely do not know whether the first call landed.
Somewhere else to be
Cart plugins
- WooCommerce Plugin Block checkout and the classic shortcode, HPOS-ready, refunds from the order screen and a REST webhook route.
- Magento Extension Composer install, per-store-view method mixes, invoices mapped to captures and credit memos mapped to refunds.
- PrestaShop Module Installed from the back office, worked from the order screen. Credit slips, custom order states and multistore keys.
- Shopware Plugin Rule Builder decides which methods appear, the state machine records what happened, Flow Builder reacts to it.
- OpenCart Extension A deliberately small build for small catalogues. Hosted fields, status mapping, and a short list of things it does not do.
Continue
Not sure this is the right route?
Send the platform, the version and the markets you sell into. An engineer answers with the plugin, the API path or an honest "not yet" — not a discovery call.