The hardest part of integrating a payment service provider is not sending a request. It is deciding what our system is allowed to believe after the request returns — or does not return.
I learned this while integrating several local PSPs for a cross-border commerce system. Each provider used different endpoint names and exposed a slightly different sequence, but the underlying questions were the same: has the issuer approved the payment, has money actually entered clearing, can the hold still be released, and which system owns the final state?
The examples below use public documentation and simplified models, not a merchant’s internal interfaces, routing configuration or transaction records. The checklists are design recommendations, not a claim that every control existed in the original system.
A PSP is between the merchant and the banking rails
The logos can make this look simpler than it is. A typical card payment involves:
- the merchant, which owns the order and checkout;
- the PSP or gateway, which exposes the merchant-facing API and normalizes payment methods;
- the acquirer, which sponsors the merchant into the card network and receives settlement;
- the card network, which routes messages and defines scheme rules; and
- the issuer, the shopper’s bank, which decides whether to approve the transaction.
One company can play several roles. A PSP may also provide acquiring, fraud screening, tokenization and settlement reporting. The integration therefore needs to document the role, not infer it from the vendor name.
Authorization and settlement are separate movements. Authorization asks the issuer to
approve the transaction and reserve spending capacity. Capture submits an approved
amount for clearing. Settlement happens later, moving net funds toward the merchant
after fees and other adjustments. An authorized order is not cash in the merchant
bank account.
Pre-authorization is not a universal extra stage
Teams often draw several incompatible flows:
| Provider-facing flow | What it usually means | Cancellation path |
|---|---|---|
authorize → capture |
Place a hold, then collect it later | Void or cancel before capture; refund after capture |
pre-auth → adjust/finalize → capture |
Hold an estimated amount, then change or finalize it | Cancel the authorization or let it expire according to scheme rules |
pre-auth → capture |
Provider calls a delayed-capture authorization “pre-auth” | Void before capture; refund after capture |
sale or automatic capture |
Authorization and capture are presented as one merchant operation | Usually refund, although a fast reversal may occur internally |
At card-network level, pre-authorization is normally a type or usage of authorization, not a guaranteed, independent bank transaction that must always come before “authorization.” It is useful when the final amount is not known at the start — hotels, car rental, fuel, tips or deposits. Some providers allow incremental or adjusted authorization before the final capture. Others simply label manual capture as pre-authorization.
So I do not build the domain model around one provider’s words. I map each provider into an internal state machine:
- Pending: created, but the customer or provider still has work to do.
- Authorized: approved and held, but not yet captured.
- Captured: accepted for clearing; refund is now the relevant operation.
- Voided/cancelled: an uncaptured authorization was released.
- Refund pending/refunded: a captured payment is being or has been returned.
- Failed/expired: the provider has given a terminal negative result.
Void and refund are not synonyms. A void releases a hold before capture. A refund
creates a new money movement after capture, can take time, and may have separate fees
and failure states. Partial capture and partial refund make this a ledger, not a Boolean
paid field.
Authorization validity also expires. The exact window varies by scheme, payment method, merchant category and provider. Store the provider’s expiry time when available and do not promise capture after it.
A timeout means unknown
One state every PSP integration must represent explicitly is an ambiguous synchronous result:
- Our backend sends an authorization request.
- The PSP forwards it and the issuer approves it.
- The HTTP response is lost or arrives after our timeout.
- Our application sees an exception, while the cardholder sees a hold or charge.
If the application changes the order to failed and retries with a new reference, it
can create a duplicate payment. The safe interpretation is unknown.
Before the first request, create a stable merchant payment reference and idempotency key. Reuse them for a retry. Persist the PSP reference as soon as it exists. When the synchronous result is ambiguous, query the PSP by one of those references instead of blindly starting a new transaction.
Webhooks are authoritative input, not ordered truth
A webhook often brings the state that the original request could not deliver. That makes it essential, but not magical. Webhooks can be delayed, delivered more than once, or arrive out of order. A robust handler should:
- receive over HTTPS and verify the provider’s signature or HMAC, using the unmodified raw body when required;
- validate the event structure and persist the fields needed for processing using a predefined allow-list, without dumping unknown fields into ordinary logs;
- deduplicate by event ID, or by the provider’s documented composite key, and track processing state;
- acknowledge according to the provider’s contract only after durable queue or inbox acceptance, then perform business processing asynchronously;
- apply an idempotent state transition;
- fetch the current payment from the PSP API when events conflict or context is stale;
- link the necessary transaction identifiers, processing outcomes and investigation evidence, with explicit retention and access rules.
Needing the original body for verification does not mean keeping the entire payload indefinitely. Stripe, for example, requires an unmodified request body for signature verification. What to persist after that is a separate decision. If processing or a dispute genuinely requires the original event, first establish what may be retained, then use a restricted, encrypted evidence store with access auditing and an applicable retention policy—not an unrestricted log dump. Stripe: webhook signature verification
Unknown fields may introduce personal or sensitive information. Classify them and establish their purpose before adding them to processing. In a merchant integration, sensitive authentication data such as CVV must not be retained after authorization, even encrypted. Reconciliation convenience is not an exception. PCI SSC: card verification code storage
The source of truth is the PSP’s payment record and ultimately the financial reconciliation, not “the last webhook we happened to receive.” The webhook is the notification that tells us to reconcile our local state with that record.
This also changes the user experience. After a browser redirect, “payment processing” is more honest than “payment failed” when the backend has not reached a terminal state.
Do not build a raw card form unless that is really your business
Most PSPs offer hosted checkout, an iframe, hosted fields or UI components. The card number and CVV are entered into fields delivered by the PSP; the browser receives a token or encrypted payment-method value that the merchant backend can use without handling the raw card data.
That reduces — but does not eliminate — PCI DSS responsibility. The merchant still has to protect its page, redirect path, dependencies, access and integration configuration. The PCI SSC notes that SAQ A eligibility for an embedded iframe requires all elements that capture card data to originate from the compliant provider. If a merchant-owned field handles PAN or CVV, the scope changes substantially.
The practical rule is simple: if the PSP provides maintained card components, use them. Hand-building the same inputs means accepting responsibility for storage, logs, JavaScript supply-chain attacks, browser compromise, key management, PCI evidence and incident response. Never log PAN or CVV, and do not assume encryption makes sensitive data safe to retain.
“The PSP gave us keys” can mean four different things
I have integrated providers that required keys or certificates around a transaction, but the exact security function matters:
| Mechanism | Purpose | Where it belongs |
|---|---|---|
| Client key / PSP-hosted encryption | Encrypt or tokenize card fields before they reach merchant code | Browser, restricted to approved origins |
| Secret API key, OAuth credential or mTLS certificate | Authenticate the merchant backend to the PSP | Server-side secret store only |
| RSA request signature or payload encryption | Prove request integrity/authenticity or protect a provider-specific payload | Server-side signing module with rotation |
| Webhook HMAC/public-key verification | Prove an event came from the PSP and was not modified | Webhook ingress, before processing |
These mechanisms are not interchangeable. A signature does not hide data; encryption does not by itself authenticate the business intent; a public client key is not a server secret. Key ID, algorithm, canonical field order, timestamp, nonce, certificate expiry and rotation procedure are part of the integration contract.
The details I now ask every provider
- Does one merchant reference uniquely identify an attempt, an order, or a capture?
- Which operations support idempotency, partial amounts and multiple captures?
- When does authorization expire, and can the amount be adjusted or incremented?
- Does cancel mean void, reversal, refund, or “request accepted”?
- Which statuses are terminal? Can a failed-looking status later become successful?
- Are webhook events retried, duplicated or unordered? What is the event ID?
- How is the webhook signed, and how are old and new keys accepted during rotation?
- Is 3-D Secure asynchronous? What is trusted: browser return, webhook or status API?
- Are amounts integers in minor units? How are zero-decimal currencies handled?
- How are fees, FX, settlement batches, disputes and chargebacks reported?
- Can sandbox behaviour differ from production acquiring or issuer behaviour?
- Which report closes the gap between
capturedand money actually settled?
The API call starts a payment. The state machine, evidence and reconciliation make it a payment system.