Payment integration is an architecture decision
Most teams choose their payment provider based on pricing tables and documentation quality. Those matter, but the real decision is architectural: how does this payment system integrate with your order management, subscription logic, and financial reporting?
Stripe vs Razorpay vs Adyen
Stripe: Best developer experience in the industry. Excellent for SaaS, marketplaces, and subscription products. Strong in US/EU markets. Higher fees in emerging markets. Their webhook system and idempotency support are best-in-class.
Razorpay: Dominant in India with UPI, netbanking, and local wallet support. Essential if you serve Indian customers. API is solid but less polished than Stripe. Settlement times and dispute handling differ significantly.
Adyen: Enterprise-grade with global coverage. Best for companies processing across multiple countries. Higher minimums but better rates at volume. Complex integration but unmatched regional payment method support.
PCI compliance isn't optional
If you handle card data, you must be PCI compliant. The simplest path:
Use hosted payment fields (Stripe Elements, Razorpay Checkout) so card numbers never touch your servers
This keeps you at PCI SAQ-A, the simplest compliance level
Never log, store, or transmit raw card numbers through your backend
Use tokenization for recurring payments
Webhook reliability patterns
Webhooks are how payment providers tell you what happened. They're also unreliable by nature — they can arrive late, out of order, or not at all. Build for this:
Idempotency: Process each webhook exactly once using the event ID as a deduplication key
Signature verification: Always validate webhook signatures to prevent spoofing
Retry handling: Return 200 immediately, process asynchronously. If processing fails, your system should reconcile via API polling
Event ordering: Don't assume webhooks arrive in order. Use event timestamps and state machines
Handling edge cases
The complexity of payments lives in the edge cases:
Refunds: Partial refunds, refunds after settlement, refunds on expired cards. Each has different timelines and failure modes.
Disputes: Chargebacks require evidence submission within 7-21 days. Automate evidence collection from the start.
Currency conversion: Decide whether you charge in local currency (better UX) or your base currency (simpler accounting). Multi-currency support adds real complexity.
Failed payments: Implement smart retry logic with exponential backoff. Dunning emails for failed subscription payments recover 10-30% of otherwise-lost revenue.
Architecture for multi-payment systems
If you serve multiple markets, you'll likely need multiple payment providers. Abstract your payment layer behind an internal interface: create, capture, refund, and webhook-handle methods that work identically regardless of the underlying provider. This lets you add new providers without rewriting business logic.
Follow me to keep in touch
Where I share my creative journey, design experiments, and industry thoughts.




