How API payments work for providers
The payment API is a contract between your application and the provider that turns "create payment," "confirm 3DS," "return funds," "pay the client" and "get status" into reliable, repeatable calls. Under the hood are dozens of rules: tokenization, idempotency, webhooks, anti-fraud, queues, SLA and auditing. Below is a practical map of how this works for most providers.
Basic model: what entities are almost always
Payment/Charge - an attempt to write off (authorize + capture or immediately purchase).
Payment Method - card (PAN/token), bank account/alias, wallet, local method.
Customer - customer/payer entity (sometimes optional).
Payout/Transfer - outgoing payment to the client/merchant.
Refund/Reversal - return to the original payment (closed-loop).
Webhook Event - asynchronous status notification.
Dispute/Chargeback - dispute over payment network (for cards).
Order/Invoice - business context around the payment.
Authentication and Security
API keys/OAuth 2. 0/mTLS - for server-to-server.
Client tokens are disposable tokens for the frontend (so as not to shine secrets).
Card tokenization - PAN goes to the provider; you only store the token.
PCI DSS - if you see a PAN, you are in the PCI zone; it is better to avoid and use hosted fields/SDKs.
HMAC signature webhooks - checking that the event came from the provider.
Dual-zone architecture - public front (JS/SDK) and private backend (keys, risk logic).
Idempotency, queues and consistency
Idempotency-Key in header: repeating the request (with timeout) does not create a duplicate.
Outbox/Saga with you: to "send a payment → write to the ledger → wait for the webhook" was agreed.
Retrai with backoff - only for temporary errors. The retryable/non-retryable matrix is required.
Typical endpoints (schemes and flow)
1) Cards (Visa/Mastercard, etc.)
POST/payments - create a payment ('amount', 'currency', 'payment _ method _ token', 'capture' = false/true).
POST /payments/{id}/confirm — шаг 3-D Secure 2 (challenge/redirect result).
POST/payments/{ id }/capture - capture after authorization (partial/full).
POST/payments/{ id }/refund - return (in parts, up to the original amount).
GET /payments/{id} — статус (authorized, captured, failed, requires_action).
3-D Secure/SCA: provider will return'requires _ action '+ 'client _ secret '/URL for challenge. After confirmation by the client, the front will return the result to your backend → you pull '/confirm '.
2) A2A / Open Banking (Pay by Bank)
POST/payments → response from 'redirect _ url' to customer's bank.
The client is authorized by the bank → webhook'payment. succeeded/failed`.
GET/payments/{ id} - final status (often only asynchronous).
3) Local methods (PIX, PayID, FPX, iDEAL, etc.)
POST/payments → get 'qr _ code '/' deeplink '/' copy _ code'.
Show the user, wait for the webhook about enrollment.
Code timeouts and TTLs are part of the contract.
4) Payouts
POST /payouts (`amount`, `currency`, `destination_token` или `card_token`/`bank_alias`).
GET /payouts/{id} — статусы (`queued`, `sent`, `paid`, `failed`).
Webhooks'payout. paid/failed 'is the source of truth.
Closed loop: it is preferable to pay to the source (reversal) before alternatives.
Webhooks: "the source of truth"
Events: 'payment. pending/authorized/captured/failed`, `payment. requires_action`, `refund. succeeded`, `payout. paid`, `dispute. created ', etc.
Delivery: with retrays signed by HMAC, often "at least once" → keep the handler idempotent.
Best practice: process the webhook → update the ledger → respond 2xx. Any business logic after is asynchronous.
Error and status processing
HTTP codes: '2xx' success; '4xx' client error (validation, fraud/bank failure, incorrect token); '5xx' - provider.
Причины отказов: `insufficient_funds`, `do_not_honor`, `stolen_card`, `limit_exceeded`, `risk_decline`, `bank_unavailable`.
Redo windows: for 3DS - you cannot endlessly retract; for A2A - the redirect/code TTLs are valid; for payouts - backoff.
Antifraud and risk
Device-fingerprinting and behavioral data - via the provider's JS/SDK or your own layer.
Lists: BIN risk, black/white lists of methods/ASN/countries.
Adjustable gates: limits on new tools, cool-off, velocity, RG/AML threshold checks.
Policies: 'pass/step-up/hold/block' based on scoring + rules.
Features on rails
Cards: authorization vs clearing (amount can be shifted), 3DS2, returns on the original transaction, disputes/chargebacks.
A2A/Open Banking: redirect/consent flow, high apruv, low cost; everything is asynchronous and highly dependent on the bank.
Local fast: QR/alias, instant webhook, TTL and strict compilation.
OCT/Push-to-Card (card payments): you need card tokens, Fast Funds support from the issuer, without 3DS.
Versioning and compatibility
API versions: '/v1 ', "data versions" in the header or phicheflags.
Backwards-compatible changes: non-destructive fields only.
Deprections: schedule for the withdrawal of old versions, migration guides, duplicate webhooks during the migration.
Observability and SLA
Metrics: p95 authorization/payment time, approve rate by BIN/bank/method, share of retrays, webhooks-lag.
Logs: correlation by 'request _ id', 'idempotency _ key', 'payment _ id'.
Alerts: a surge in refusals for a specific bank/country, an increase in timeouts, duplicate events.
Sweep and Finance
Ledger: double entry, unchangeable logs, 'authorized/captured/refunded' statuses.
Three-sided compilation: your ledger ↔ webhooks ↔ provider/bank reports.
References: keep 'provider _ reference', 'network _ reference', 'payout _ reference' - this saves the support.
Sandbox, certification and production
Sandbox: test tokens/cards/banks, simulation of 3DS/redirects, "buttons" for webhook statuses.
Test cases: success/failure, 3DS challenge, provider timeout, duplicate webhook, partial capture/refund, redirect cancellation, payout fail.
Go-live: prod keys, webhook domains, IP-allowlist, enable anti-fraud, set limits and alerts.
Mini-examples (schematic)
Create payment (card)
POST /v1/payments
{
"amount": 9232,  "currency": "EUR",  "payment_method_token": "pm_tok_123",  "capture": true,  "metadata": {"order_id": "ORD-1001"}
}
→ 200 { "id": "pay_abc", "status": "requires_action", "next_action": {"type":"redirect", "url":"..."} }Webhook on successful enrollment
POST /webhooks
{
"type": "payment. captured",  "data": {"id":"pay_abc","amount":9232,"currency":"EUR","metadata":{"order_id":"ORD-1001"}}
}Payout
POST /v1/payouts
{
"amount": 5000,  "currency": "EUR",  "destination_token": "dest_tok_456",  "metadata": {"user_id":"u_77"}
}Implementation checklist
1. Key architecture: server secrets separately, client secrets - one-time.
2. Idempotency: in each write call + idempotent webhook handlers.
3. Webhooks: HMAC signature, retrai, task queue, lag monitoring.
4. 3DS/SCA and redirects: handling cancellations/timeouts, trying friend-UX again.
5. Antifraud/limits: velocity, new details, blacklists, RG/AML thresholds.
6. Ledger and reconciliation: double entry, three-sided reconciliation, anomaly alerts.
7. Orchestration: spare provider, routing rules by BIN/country/amount.
8. Monitoring: dashboards approve rate/p95/retrays, alerts by banks/methods.
9. Legal/PCI: tokenization, returns policy, closed payouts loop.
10. Degradation plan: kill-switch channel, queues, manual mode for critical operations.
Frequent mistakes
Storing PAN on its side without PCI and tokenization.
No idempotency → duplicate payments/payouts with timeouts.
Logic on synchronous responses without taking into account webhooks.
One provider to the market, no fallback/cascades.
No 3DS undo/redirect processing → lost recycle bins.
A weak bundle → "frozen" and "lost" transactions.
The lack of clear SLAs and alerts → the problem the client sees, not you.
Mini-FAQ
Which is more reliable: synchronous status or webhook?
Webhook is the source of truth; synchronous response - only "action accepted/needed."
Can you do without 3DS?
Depends on regulation/risk. In EC/UK - SCA is mandatory; for high-risk, step-up is better.
How to increase the approve rate?
BIN/bank/method orchestration, local rails, smart retras, correct descriptors, low FPR anti-fraud.
Why is payout "not instant"?
Depends on the rail (OST/A2A/local), recipient's bank and limits. Let's have an honest SLA window and status stream.
API payments are not only "create a payment." This discipline: secure authentication → tokenization → idempotency → asynchronous webhooks → ledger and folding → anti-fraud/AML → orchestration and monitoring. Build the process according to this scheme, keep spare channels and transparent UX - and your payment layer will be fast, predictable and resistant to failures of banks and providers.
