Accept a payment: cards, APMs and 3-D Secure 2
One object covers a Visa card in Berlin, an iDEAL debit in Rotterdam and a BLIK code in Kraków. What changes between them is the method, the refund window and who carries the chargeback risk.
The payment lifecycle
A payment moves through a small state machine. Only two states are final, and only one of them means you have money. Everything your order system does should be driven by these transitions rather than by the response to the call that created the object.
| Status | Meaning | Your move |
|---|---|---|
| requires_action | The payer must authenticate or approve in their bank. | Redirect to next_action.url. Hold the basket. |
| processing | Submitted to the rail, outcome not yet known. | Wait for the event. Never treat this as a failure. |
| requires_capture | Authorised, funds held, nothing taken yet. | Capture on dispatch, or cancel to release the hold. |
| succeeded | Final. Funds are captured and scheduled to settle. | Fulfil the order. This is the only status that means paid. |
| failed | Final. The issuer or the bank refused. | Show the reason, offer another method. See declines. |
| canceled | Final. Abandoned by the payer, or released by you. | Free any reserved stock. |
Fulfil on payment.succeeded, delivered to
your webhook endpoint. Not on the browser returning to your
return_url, and not on a status you polled. The redirect can be lost to a
closed laptop, and a poll can race the rail — the event cannot.
Creating a payment
The same POST /v1/payments creates every kind of charge. Below is a card
authorisation held for later capture, which is the shape most physical-goods merchants want.
# card charge, authorise now and capture on dispatchcurl -X POST https://api.bazpay.com/v1/payments \ -H "Authorization: Bearer sk_test_9f2c…" \ -H "Idempotency-Key: ord_10482" \ -d '{ "amount": 8990, "currency": "EUR", "method": "card", "token": "tok_1Hq8Vc4nRs", "capture": "manual", "reference": "ORDER-10482", "customer": { "email": "[email protected]" }, "return_url": "https://shop.example/thanks" }' token comes from hosted fields in the browser; your server never sees the PAN. Omit capture to take the money immediately.
The fields that change behaviour
- amount Integer, minor units. 8990 with EUR is 89.90 euro. Currency decides the exponent — JPY has none, so 8990 there is 8,990 yen.
- capture
automatictakes the money at authorisation.manualholds it, which is correct whenever you might not ship. - reference Your order id. It travels onto the payer's statement where the scheme allows, onto every event, and onto the settlement line — which is what makes reconciliation cheap.
- customer.email Optional but strongly advised: it feeds the risk score and is one of the data points that earns a transaction risk analysis exemption.
- return_url Required for any method that redirects. It must be HTTPS and must tolerate being opened twice.
Where the card form lives
This is the decision that fixes your PCI scope, so make it before you write checkout code. All three options keep you at SAQ A because the card number never reaches your systems in any of them — what changes is how much of the interface you own.
| Surface | You build | Effort | Best when |
|---|---|---|---|
| Hosted checkout | A redirect and a webhook handler. | One day | You want method selection, localisation and retry logic maintained for you. |
| Hosted fields | Your own checkout; our iframe supplies the inputs. | 2–5 days | Brand control matters and you want one page rather than a hand-off. |
| CMS plugin | Nothing. Install and paste two keys. | Hours | You are on WooCommerce, Magento 2, PrestaShop, Shopware or OpenCart. |
SAQ A applies when all cardholder data functions are outsourced to a validated third party and your page does not touch the PAN. Hosted fields satisfy this because the inputs belong to our origin, not yours. The moment you copy a card number into your own form field — even to reformat it — you leave SAQ A and enter SAQ A-EP. There is no partial version of this rule.
The EU method matrix
Every method below is created through the same call and reports the same events. They differ in three ways that affect your business rather than your code: how fast money settles, whether a partial refund is possible, and who carries the risk if the payer disputes it.
| Method | Markets | Refunds | Dispute risk |
|---|---|---|---|
| Card — Visa, Mastercard | All EU/EEA | Full and partial, up to 180 days | Chargebacks apply. Liability shifts on a 3-D Secure challenge. |
| iDEAL | Netherlands | Full and partial, to the same IBAN | None. A completed iDEAL payment is final. |
| Bancontact | Belgium | Full and partial | Very low. No cardholder-initiated chargeback path. |
| BLIK | Poland | Full and partial | None on the code flow. |
| EPS | Austria | Full and partial | None. |
| Open banking debit | SEPA-wide under PSD2 | Full and partial, same rail | None for a payer-approved push. See open banking. |
| SEPA Direct Debit | SEPA-wide | Full and partial | High: 8 weeks no-questions, 13 months if unauthorised. |
| Apple Pay · Google Pay | All EU/EEA | As the funding card | As cards, but authentication is on-device so challenges are rare. |
Show methods by the payer's country, not by your own. A Dutch checkout without iDEAL loses conversion no card optimisation will recover, and a Polish one without BLIK looks foreign. If you would rather not maintain that mapping, the hosted checkout does it from the payer's billing country and IP.
Pricing per method, including interchange++ breakdowns: pricing · method-level product detail: card and APM processing.
3-D Secure 2 and exemptions
Under PSD2, an electronic payment in the EEA needs strong customer authentication unless an exemption applies. We run 3-D Secure 2.2 for you: the authentication request is built, submitted and interpreted on the gateway, and you get the outcome on the payment object.
Frictionless is the normal case
Most authentications never show the payer anything. The issuer receives around 150 data points about the transaction, scores it, and returns an approval without a challenge. A challenge — a banking-app prompt or a one-time code — is the exception, and each one costs conversion.
Asking for an exemption
Where the rules allow it, you can ask to skip authentication. The issuer decides; a request is never a guarantee, and the response tells you what actually happened.
# ask for an exemption; the issuer may still force a challenge"sca": { "request": "exemption", "reason": "transaction_risk_analysis"}# what came back on the completed payment"sca": { "applied": "exemption", "reason": "transaction_risk_analysis", "liability_shift": false, "version": "2.2.0"} | Exemption | Applies when | The catch |
|---|---|---|
| Low value | Under 30 EUR. | Counters reset after five uses or 100 EUR cumulative — the issuer tracks this, you cannot. |
| Transaction risk analysis | Acquirer fraud rate is under the band for the amount. | Our rate, our call. Available up to 500 EUR at current performance. |
| Trusted beneficiary | The payer added you to their allow-list at their bank. | You cannot initiate it. Prompt on a challenge screen and the payer may opt in. |
| Merchant initiated | Subscription renewals after an authenticated first payment. | Needs the mandate from that first payment. See recurring billing. |
A completed challenge moves fraud-chargeback liability to the issuer. An exemption keeps it with you. On a low-value digital order that trade is usually worth it; on a high-value physical one it rarely is. Set the threshold per product category rather than site-wide, and read anti-fraud before you set it low.
Capture, cancel, refund
An authorisation is a hold, not a payment. Three calls resolve it, and choosing the right one is the difference between a clean ledger and a support queue.
# capture less than you authorised — the rest is releasedPOST /v1/payments/pay_3fJ2Qk8vTn/capture { "amount": 7490 }→ 200 { "status": "succeeded", "amount_captured": 7490 }# nothing shipped — release the hold instead of refundingPOST /v1/payments/pay_3fJ2Qk8vTn/cancel→ 200 { "status": "canceled", "amount_captured": 0 } - capture Takes the money, in full or in part. Capturing less releases the remainder to the payer's available balance straight away.
- cancel Releases an uncaptured hold. Use this — not a refund — when nothing shipped: it costs nothing and never appears on the payer's statement.
- refund Returns captured money over the original rail. Full or partial, and repeatable up to the captured amount.
The windows that matter
- A card authorisation expires after 7 days, or 30 for travel and lodging categories. Capture before then or it lapses.
- Card refunds are possible for 180 days after capture. Beyond that the money has to move as a payout.
- Bank-rail refunds have no scheme deadline but need the original payment to still be on file — we hold that for 7 years.
- A partial capture cannot be topped up. If you may ship more later, authorise the larger amount and capture down.
Reading a decline
A failed payment carries two codes: ours, which is stable and safe to branch on, and the issuer's raw reason, which is more specific but varies by bank. Show the payer a plain sentence, log both, and let the pair drive your retry policy.
| Code | What happened | Retry? |
|---|---|---|
| insufficient_funds | The account cannot cover it right now. | Yes, after a few days. This is the classic dunning case. |
| do_not_honor | A generic issuer refusal with no stated reason. | Once, later. Then offer a different method. |
| expired_card | The stored card is past its date. | No. Ask for new details, or use the updater on subscriptions. |
| authentication_failed | The payer abandoned or failed the challenge. | Yes, immediately, in the same session. |
| card_not_supported | The product cannot be used for this transaction type. | No. Offer an APM instead. |
| fraud_suspected | Blocked by the issuer or by our rules. | Never automatically. Repeat attempts raise your fraud ratio. |
Scheme rules cap retries of a declined card at four attempts in 30 days for the same transaction, and a hard decline may not be retried at all. Exceeding this earns fees and, eventually, scrutiny. Decline-reason distribution over time is on real-time analytics, which is the fastest way to see whether a drop in approvals is your rules, your traffic mix or one issuer having a bad day.
Forcing each of these codes on demand in test mode: sandbox behaviour and test data.
Keep reading
Stuck on this page?
Integration questions reach an engineer, not a queue. Send the request id and the response body — that is usually enough to answer in one reply.