Payment Processing
Payment processing is how software takes money — turning a "Buy" click into funds in your account. It sits at the intersection of several hard requirements: it must be secure (you're handling money and card data), reliable (a dropped payment is a lost sale or a double charge), and compliant (PCI-DSS governs how card data is handled). Get it wrong and the failure modes are expensive: chargebacks, security breaches, or customers billed twice.
The modern reality is that you almost never build this yourself — providers like Stripe, Adyen, Braintree, and PayPal handle the dangerous parts (touching raw card numbers, connecting to the card networks, staying compliant), and you integrate their APIs. This page is about doing that integration correctly: understanding the flow well enough to handle the money-critical details — webhooks, idempotency, and keeping card data off your servers — that separate a robust billing system from a liability.
TL;DR
- Use a provider (Stripe et al.) — they handle card networks, security, and PCI compliance. Building your own payment stack is a mistake for almost everyone.
- Never let raw card numbers touch your server — use the provider's hosted fields/SDK so cards go straight to them and you get back a token. This keeps you out of the worst of PCI scope.
- The card flow: tokenize card → create a payment intent → confirm → provider charges the networks → you get the result (plus asynchronous webhook confirmation).
- Webhooks are the source of truth for payment outcomes — the client-side result can be lost (closed tab); the webhook is how you reliably know a payment succeeded.
- Idempotency is mandatory — network retries must not create duplicate charges; use idempotency keys.
- Subscriptions, refunds, disputes, taxes, and SCA/3-D Secure are their own complexities — lean on the provider's abstractions rather than reinventing them.
How a Card Payment Flows
The crucial architectural point: card data flows customer → provider directly, never through your backend, and fulfillment is driven by the webhook, not the client response.
Quick Example
A Stripe PaymentIntent flow — server creates the intent, client confirms, webhook fulfills:
Note what's absent from your server: the card number. Note what drives fulfillment: the verified webhook, not the browser.
Core Concepts
Keep Card Data Off Your Servers (PCI Scope)
Handling raw card numbers subjects you to the full weight of PCI-DSS (the card-industry security standard) — audits, controls, and enormous breach liability. The entire point of modern providers is to avoid this: their hosted payment fields (Stripe Elements, Checkout) collect the card in an iframe/SDK that sends it directly to the provider, returning a token to you. Because raw card data never touches your systems, you qualify for the vastly simpler PCI self-assessment. The rule is absolute: never accept, log, or store raw card numbers. Doing so — even "temporarily" — is a serious security and compliance failure. See API Security and Secrets Management for the surrounding practices.
Webhooks Are the Source of Truth
The client-side payment result is unreliable — the customer can close the tab, lose connection, or the browser can die right after paying but before your frontend records it. The webhook (the provider POSTing payment_intent.succeeded to your server) is the authoritative, reliable signal that money moved. Fulfill orders and grant access on the webhook, not on the frontend response. This means everything on the webhooks page applies here — verify signatures, respond fast, and process idempotently, because a mis-handled payment webhook means unfulfilled paid orders or double fulfillment.
Idempotency: Never Double-Charge
Networks retry; users double-click; requests time out and get resent. Without protection, one purchase becomes two charges — a fast route to angry customers and chargebacks. Payment APIs support idempotency keys: attach a unique key (e.g. the order ID) to a charge request, and the provider guarantees that retrying with the same key returns the original result instead of charging again. Use them on every money-moving call. See the idempotency discussion in API design.
Beyond One-Time Charges
Real billing systems involve more than single payments, and providers abstract each:
The lesson throughout: these are deep problem domains, and the provider's built-in handling (Stripe Billing for subscriptions, automatic SCA, Tax) is almost always better than reinventing them.
Common Mistakes
Touching Raw Card Data
Accepting card numbers on your own form/server — even to "just pass them along" — pulls you into full PCI scope and creates catastrophic breach liability. Always use the provider's hosted fields/SDK so cards go directly to them. This is the cardinal rule of payments.
Fulfilling on the Client Response Instead of the Webhook
Granting access or shipping the order based on the browser's success callback means lost fulfillments when the tab closes at the wrong moment — and potential fraud. The verified webhook is the source of truth; drive fulfillment from it.
No Idempotency
Omitting idempotency keys means retries and double-clicks create duplicate charges. Every charge/payment call needs an idempotency key tied to the logical operation (order ID) so retries are safe.
Trusting Client-Sent Amounts
Letting the client tell the server how much to charge lets an attacker pay $1 for a $100 item. Compute the amount server-side from the order/cart in your database — never trust a price sent from the browser.
Ignoring Failure and Edge Cases
Declined cards, insufficient funds, expired cards, SCA challenges, partial refunds, and disputes are normal, not exceptional. Handle declines gracefully (clear messaging, retry paths), implement dunning for failed subscription renewals, and have a process for disputes. A payment flow that only handles the happy path fails constantly in production.
Building Your Own Payment Stack
Unless you're a payments company, connecting to card networks, achieving PCI Level 1, and handling global compliance yourself is a multi-year liability, not a feature. Integrate a provider; spend your effort on the integration's correctness, not on rebuilding what Stripe already did.
FAQ
Should I use Stripe, or build payments myself?
Use a provider — Stripe, Adyen, Braintree, PayPal, etc. They've solved the genuinely hard, dangerous parts (card-network connectivity, PCI Level 1 compliance, fraud tooling, global payment methods). Building your own is appropriate essentially only for payment companies. Your job is a correct integration, which is plenty of engineering on its own.
What is PCI compliance and do I have to worry about it?
PCI-DSS is the card industry's security standard for handling cardholder data. You minimize your obligations by never touching raw card data — using hosted fields (Stripe Elements/Checkout) so cards go straight to the provider. That qualifies you for a simple self-assessment questionnaire rather than full audits. Storing card numbers yourself triggers the heavy, expensive end of PCI.
Why are webhooks so important for payments?
Because the client-side result is unreliable — customers close tabs, lose connectivity, or crash right after paying. The webhook is the provider reliably telling your server the final outcome, so you fulfill based on confirmed payment rather than a browser callback that may never arrive. Verify and process it idempotently.
How do I prevent double charges?
Idempotency keys on every charge request: attach a unique key (like the order ID), and the provider ensures that retries with the same key don't create a second charge. Combined with server-side amount calculation and webhook-driven fulfillment, this keeps the money side correct under retries and network failures.
How do subscriptions and taxes work?
Lean on the provider's abstractions — Stripe Billing (and equivalents) handle recurring charges, proration, trials, and dunning; Stripe Tax and services like Avalara handle jurisdiction-specific sales tax/VAT. These are deep domains (especially tax); the provider's built-in handling is almost always the right choice over building it yourself.
Related Topics
- Webhooks — The reliable payment-confirmation mechanism
- API Security — Securing money-handling endpoints
- REST API Design — Idempotency and API conventions
- Secrets Management — Protecting API keys and signing secrets
- HTTPS & TLS — Encrypting payment traffic
- Background Jobs — Processing payment events async
- Authentication — Tying payments to user accounts