# Payments Taking money is the part of commerce with the least tolerance for bugs. The payment layer is built around three principles: **never double-charge**, **always reconcile**, and **survive a flaky gateway.** ## Two gateways The platform integrates two payment providers behind a common surface: - **Razorpay** — primary Indian gateway: order creation, card/UPI payments, saved payment methods, payment links. - **PayU** — a second gateway for redundancy and coverage. Each is wrapped in its own service (`RazorpayPaymentService`, `PayuPaymentService`) so the rest of the app talks to payments through a stable interface rather than a vendor SDK. ## Never double-charge: idempotency Every order-creation call carries an **idempotency key**. If a request is retried — because the network blipped, or the shopper double-clicked — the gateway returns the *existing* order rather than creating a second one. `CheckoutIdempotencyService` enforces the same guarantee on the app side. ## Survive a flaky gateway: circuit breaker Payment services include `PaymentCircuitBreaker`. When a gateway starts failing or timing out, the breaker **opens** — subsequent calls fail fast instead of piling up threads against a dead endpoint — then probes for recovery before closing again. A degraded gateway becomes a clear, fast error instead of a cascading outage. ## Always reconcile A payment is not "done" when the shopper sees a success page. Money settles later, asynchronously, and the platform's records must match the gateway's: - **Webhooks** — `RazorpayWebhookService` verifies signatures and processes payment, refund, and settlement events. - **Settlement sync** — `RazorpaySettlementSyncService` pulls settlement batches so payouts can be traced to underlying orders. - **Refund sync** — `RazorpayRefundSyncService` keeps refund state aligned between the app and the gateway. - **Reconciliation** — `RazorpayReconciliationService` runs end-to-end checks, recording results in `reconciliation_runs` and flagging mismatches. ## Payouts to vendors Because the platform is [multi-vendor](/features/vendor-multistore/), settlement money has to be split. `PayoutCalculationService` computes what each vendor is owed from the orders that settled, and payouts are tracked as first-class `Payout` records. ## Key files | Concern | Files | |---------|-------| | Models | `PaymentIntent`, `RazorpaySettlement`, `Refund`, `Payout`, `SavedPaymentMethod`, `ReconciliationRun` | | Gateways | `RazorpayPaymentService`, `PayuPaymentService`, `PaymentGatewayService` | | Reconciliation | `RazorpayWebhookService`, `RazorpaySettlementSyncService`, `RazorpayRefundSyncService`, `RazorpayReconciliationService` | | Resilience | `PaymentCircuitBreaker`, `CheckoutIdempotencyService`, `RazorpayRateLimiter` |