
Every gateway quickstart makes payments look like an afternoon of work: drop in a checkout component, paste the test keys, watch a card get charged. Then production arrives. A customer double-clicks the pay button, a webhook fires twice, a bank demands 3-D Secure, and your accountant asks why the database shows more paid orders than the gateway shows charges.
This guide covers payment gateway integration the way we approach it on client projects: choosing a provider, one-time vs subscription billing, webhooks and idempotency, PCI basics, and testing before real cards are involved.
How to Choose a Payment Gateway
Most teams compare headline card fees and stop there. Four things matter more.
Settlement coverage
The first filter is not features — it is whether the gateway can pay out to a business entity registered in your country. Most major gateways can charge customers almost anywhere, but they settle funds only to bank accounts in a limited list of supported countries. Verify settlement support for your legal entity before writing a line of code.
Real fees, not headline fees
The advertised percentage-plus-fixed fee is a floor, not the price. Cross-border card fees, currency conversion spread, dispute fees, and surcharges for non-card payment methods all stack on top. Businesses selling internationally usually find their true blended rate noticeably higher than the number on the pricing page, so model your real transaction mix before committing.
Payout timing
Gateways hold funds before settling — from a couple of business days to weekly cycles, often with rolling reserves for new accounts. If cash flow is tight, or you run a marketplace that pays sellers, payout timing is a business constraint, not a footnote.
Local payment methods
Cards are not the default everywhere. Bank-transfer rails, mobile wallets, and installment options dominate checkout in many markets across Europe, Asia, and Latin America. If you sell internationally, a gateway with strong local-method support will convert better than a cards-only setup.
One-Time Payments vs Subscriptions
For one-time purchases, use the intent pattern every modern gateway offers: create a payment intent on your server with the amount and currency, confirm it on the client with the gateway's SDK, and fulfill the order only when the gateway's webhook confirms success. The client never decides whether payment happened; it only collects the details.
Subscriptions are a different problem. Recurring billing means trials, proration on plan changes, failed-renewal retries, dunning emails, and cancellation timing — a billing engine, not a charge. Use the gateway's native subscription objects instead of building your own renewal scheduler around one-time charges. A homegrown scheduler will eventually double-bill someone during a deploy, and you gain nothing for the risk. Billing structure also shapes your data model, which is why we treat it as an early decision in our guide to building a SaaS MVP that scales.
Webhooks and Idempotency: Where Payments Actually Live
The webhook, not the client-side success callback, is the source of truth for whether you got paid. Design every payment flow around that fact.
Redirects fail, browsers close, mobile apps get killed mid-checkout. The gateway's server-to-server webhook is the only reliable record of a payment's outcome, so fulfillment, receipts, and account upgrades should all hang off it. Getting this layer right is core API development and integration work, and it has a few non-negotiables:
- Verify signatures. Anyone can POST JSON to your endpoint. Reject events that fail signature validation.
- Make handlers idempotent. Gateways deliver events at least once, which means sometimes twice. Store processed event IDs and skip duplicates, or a retried webhook grants the same credit twice.
- Expect out-of-order delivery. A refund event can arrive before the charge event it refunds. Handlers should tolerate state they have not seen yet.
- Acknowledge fast, process async. Return a success response quickly and do heavy work in a queue. Slow handlers cause timeouts, timeouts cause retries, and retries cause duplicates.
Idempotency matters on the outbound side too. When your server creates a charge and the request times out, you do not know whether it succeeded. Send an idempotency key on every charge request so a retry returns the original result instead of charging the card again. Every serious gateway supports this; few integrations use it.
PCI Compliance: Why You Never Touch Raw Card Data
The plain-spoken version of PCI DSS: if raw card numbers ever touch your servers, you inherit an expensive, ongoing compliance burden — audits, scans, documented controls. If they never do, your obligations shrink to a short self-assessment questionnaire.
The mechanism is tokenization. The gateway's hosted fields on the web, or its native SDK components on mobile, collect card details inside the gateway's own context and hand your code an opaque token. Your server charges the token; it never sees the card. The rules that follow:
- Never build your own card form that posts numbers to your API, even "just temporarily".
- Never log request bodies on payment routes — full card numbers in logs is a compliance incident.
- Never store CVV codes; that is prohibited even for fully audited handlers.
- On mobile, use the gateway's official SDK rather than proxying card data through your backend.
Testing and Sandbox Strategy
Every gateway offers a sandbox with test cards that simulate specific outcomes: success, decline, insufficient funds, 3-D Secure challenges, disputes. A workable checklist:
- Keep sandbox and production fully separated — different API keys, different webhook endpoints, different databases.
- Forward webhooks to your local machine with the gateway's CLI or a tunnel so you develop against real event payloads.
- Test declines and 3-D Secure flows explicitly; these are where checkout UX usually breaks.
- For subscriptions, use the gateway's test clocks to simulate renewals, trial expirations, and failed payments without waiting a month.
- Replay the same webhook twice against your handler and confirm nothing double-fires.
Mistakes That Cause Lost or Duplicate Charges
The same handful of bugs shows up in most integrations we are asked to fix:
- Fulfilling on the client redirect. The user pays, their connection drops before the success page loads, and they never receive what they bought. Fulfill from the webhook.
- Creating a fresh payment intent on every click. A double-click or page refresh spawns parallel intents, and an eager retry can complete both. Reuse the existing intent for the session.
- Retrying failed API calls without idempotency keys. A timeout is not a failure — the charge may have gone through. Blind retries are the classic double-charge.
- Treating "processing" as "failed". Bank transfers and some local methods succeed hours or days later. Cancel too early and you keep the customer's money without fulfilling the order.
- Currency unit confusion. Some APIs take amounts in minor units (cents), others in major units. Mixing them up charges customers a hundred times the price, and this reaches production more often than anyone admits.
Plan the Integration Before You Write It
Payment gateway integration rewards upfront design: pick the provider against your settlement country and market mix, decide the billing model early, and build webhook handling and idempotency in from day one. If you are adding payments to a web or mobile product and want a second opinion on gateway choice or architecture, get in touch — we have built these flows across e-commerce, SaaS, and marketplace products.
