SkillPilotAI

Payment System Design

The money should be debited only once, reach the right account, and every rupee should be accounted for; that is the job of a payment system.

40.1 Why is it used?

  • A mistake in payments means real money loss, double charge or fraud, so correctness matters more than speed.
  • A payment involves many parties: merchant, payment gateway, card network/NPCI, issuer bank, acquirer bank.
  • On a network timeout the status is unknown, so idempotency, status polling and webhooks are essential.
  • Regulations like RBI and PCI-DSS impose rules on card data storage and the audit trail.
  • Every day, settlement and reconciliation must confirm that your records match the bank records.

40.2 How it works

  • Flow: create Order, create Payment intent (with an idempotency key), user authorizes via UPI/card, gateway callback, confirm order.
  • Payment state machine: CREATED, PENDING, AUTHORIZED, CAPTURED, FAILED, REFUNDED; allow only valid transitions.
  • Webhooks: the gateway sends an async event (payment.captured); verify the signature (HMAC), handle it idempotently, return 2xx quickly.
  • Status polling fallback: if a webhook is missed, periodically check the status of pending payments through the gateway API.
  • Double-entry ledger: every transaction has both debit and credit entries, total debits = total credits; entries are append-only, never updated or deleted.
  • Reconciliation: every day match the gateway/bank settlement file with your ledger; alert on and fix mismatches (missing, amount difference).
  • Tokenization: do not store card numbers yourself; use a gateway/network token to reduce PCI scope.
  • Store amounts as integers (paise), never float; keep the currency in a separate column.
  • Refunds and chargebacks: make refunds through the gateway API with their own idempotency key and add a reversal entry in the ledger (do not change the old entry).
Payment flow: from order to confirmation
  1. 1.Create Order, payment status CREATED
  2. 2.Create Payment intent (with an idempotency key)
  3. 3.User authorizes via UPI / card (NPCI / bank)
  4. 4.Gateway webhook payment.captured (verify HMAC signature)
  5. 5.One DB transaction: payment CAPTURED + ledger debit/credit entries
  6. 6.Kafka event: confirm order + notification

If a webhook is missed, a poller checks the status through the gateway API; the next day the settlement file is reconciled against the ledger.

40.3 Tools you can use

ToolTypeHow it is used
RazorpayPayment gateway (India)UPI, cards, netbanking, wallets; Orders API, webhooks, Route/settlements for Indian merchants.
StripePayment gateway (global)PaymentIntents API, Idempotency-Key, webhooks with signature; global cards and subscriptions.
UPI (NPCI)Real-time payment railInstant bank-to-bank transfer via VPA; integrate through PSP apps (PhonePe, Google Pay, Paytm) and gateways.
PayU / Cashfree / JuspayIndian PG / orchestrationAlternative gateways; orchestrators like Juspay do smart routing across multiple gateways.
PostgreSQL / MySQLRelational DB (ACID)ACID transactions, unique constraints and audit tables for the ledger and payment state.
KafkaEvent streamingReliably deliver events like payment.captured to the order, notification and accounting services.
TigerBeetleFinancial ledger DBA purpose-built high-throughput ledger database for double-entry accounting.

40.4 Comparison: Razorpay vs Stripe vs Direct UPI (NPCI via bank/PSP)

PointRazorpayStripeDirect UPI integration
MarketIndia focusedGlobal, limited in IndiaIndia only
Payment methodsUPI, cards, netbanking, wallets, EMICards, wallets, global local methodsUPI only
Integration effortLow, SDK + checkoutLow, best-in-class APIs/docsHigh, bank partnership + NPCI compliance
CostPer transaction MDR feesPer transaction feesCheap at scale, but a big setup cost
ControlMediumMediumFull control
Webhooks / idempotencyWebhooks with signatureWebhooks + Idempotency-Key headerYou have to build it yourself
Best forIndian startups and e-commerceGlobal SaaS, international cardsBig players at the scale of PhonePe, Flipkart

40.5 When to choose which

  • Razorpay / Cashfree / PayU: launching in India, need UPI + cards quickly.
  • Stripe: international customers, subscriptions, global cards.
  • Direct UPI / multiple gateways: when volume is so large that fees and routing control matter.
  • At large scale keep payment orchestration (multiple gateways) so that if one gateway is down you route through another.

40.6 Real-world example

Such a scenario: a UPI payment for a Swiggy-style order via Razorpay

  1. The user places an order of Rs 450; the backend creates an order_id with the Razorpay Orders API and the payment status in the DB is CREATED.
  2. The app opens Razorpay checkout, the user enters the PIN in a UPI app to pay; money is debited from the bank through NPCI.
  3. Razorpay sends the webhook payment.captured; the backend verifies the signature and ignores a duplicate webhook with a unique check on payment_id.
  4. In the same DB transaction: payment status CAPTURED and ledger entries: debit Customer/Gateway receivable Rs 450, credit Order revenue Rs 450.
  5. Through the Kafka event payment.captured the order service confirms the order and a notification goes out.
  6. If the webhook does not arrive for 10 minutes, a poller checks the status through the Razorpay API; if still pending, the auto-refund/fail flow runs.
  7. The next day the settlement report is reconciled with the ledger; any mismatch alerts the finance team.

40.7 Interview tips

  • Treating payment as successful on a client-side redirect; always verify through a server-side webhook/API.
  • Storing the amount as float causes rounding errors; keep paise as an integer.
  • Updating ledger rows; if there is a mistake add a reversal entry and do not change the old entry.
  • Interview tip: cover these 5 points: idempotency key, state machine, webhook + polling, double-entry ledger and reconciliation.