Tools: Payment System Design at Scale

Tools: Payment System Design at Scale

Source: Dev.to

What really happens when Maria taps “Confirm Ride”? ## The Illusion of Simplicity ## 1️⃣ The First Problem: You Can’t Store Card Data ## Tokenization. ## 2️⃣ Authorization vs Capture (Where Things Get Subtle) ## Step 1: Authorize ## Step 2: Capture ## 3️⃣ The Money Doesn’t Go Rider → Driver ## 4️⃣ The Hidden Hero: Internal Ledger System ## 5️⃣ Reliability: External Systems Will Fail ## Idempotency keys. ## 6️⃣ Smart Retries (Not Blind Retries) ## 7️⃣ Fraud Layer (Before Money Moves) ## 8️⃣ Refunds Aren’t Simple ## 9️⃣ Driver Payouts: A Different System ## 🔟 Reconciliation (Where Adults Work) ## 1️⃣1️⃣ Scaling to Millions of Rides ## 1️⃣2️⃣ Multi-Provider Strategy ## What Looks Simple Is Actually Distributed Finance ## Final Thought Maria has an important meeting in 15 minutes. She doesn’t have cash. She opens Uber. Requests a ride. Gets dropped off. The payment? Invisible. Instant. Effortless. But behind that single tap is one of the most complex distributed systems in modern software. Today, we’re breaking it down. Not just “how to charge a card.” But how to build a secure, reliable, scalable payment system that can process millions of rides per day. From the user’s perspective: Trip ends → $20 charged → Done. From the backend’s perspective: This is not a feature. This is infrastructure. Storing it directly means: So what do modern systems do? The mobile app integrates a payment provider SDK (Stripe, Adyen, etc.). Instead of sending card details to your servers: The token acts as a reusable, scoped permission to charge the card. If someone steals it? It’s useless outside your merchant account. Security solved. (Mostly.) When the ride ends, you don’t just “charge.” Check if the card has funds and lock the amount. Actually move the money. Large systems often authorize early (estimated fare) and capture later (final fare). Massive architectural impact. The rider does NOT pay the driver directly. Direct peer-to-peer payments would break accounting. Here’s what most engineers underestimate: You cannot rely on your payment provider as your source of truth. You must build your own ledger service. A simplified double-entry example: Every movement is recorded. Because money cannot disappear. If debits ≠ credits → something is broken. At scale, this is the difference between: Your payment system depends on: Common nightmare scenario: Each payment attempt includes a unique key (e.g., ride_id). If retried, provider recognizes the key and avoids duplicate processing. You will double-charge users. And you will lose trust. Not all failures are equal. Blind retries create chaos. Intelligent retries create resilience. Payment systems are also fraud systems. Ignore this, and chargebacks will destroy margins. Refunding isn’t “reverse transaction.” Sometimes, the platform absorbs temporary loss. Complexity compounds over time. Charging cards is one system. Paying drivers is another. This uses bank rails like ACH/SEPA. Completely different from card networks. Two financial systems under one product. Without reconciliation? Small inconsistencies compound into millions. Decoupling prevents cascading failures. Never depend on one payment provider. With an abstraction layer: Underneath, routing logic decides where to send it. Because outages are not “if.” A ride payment system is not: That’s why payment infrastructure is one of the hardest backend domains in the world. When Maria stepped out of that taxi in Prague, she didn’t think about: She just walked into her meeting. Great engineering makes complexity invisible. If you’re building systems: Don’t just design features. Because money systems don’t forgive mistakes. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK: Rider → Uber Merchant Account → Split → → Driver → Uber Commission → Taxes → Fees Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: Rider → Uber Merchant Account → Split → → Driver → Uber Commission → Taxes → Fees CODE_BLOCK: Rider → Uber Merchant Account → Split → → Driver → Uber Commission → Taxes → Fees CODE_BLOCK: Ride → Immediate Charge Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: Ride → Immediate Charge CODE_BLOCK: Ride → Immediate Charge CODE_BLOCK: RideCompleted Event → Payment Queue → Worker → Provider Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: RideCompleted Event → Payment Queue → Worker → Provider CODE_BLOCK: RideCompleted Event → Payment Queue → Worker → Provider CODE_BLOCK: charge(amount, token) Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: charge(amount, token) CODE_BLOCK: charge(amount, token) - Securely collect payment details - Avoid storing sensitive card data - Prevent fraud - Handle bank outages - Split money across multiple parties - Maintain financial correctness - Reconcile mismatches - Survive retries and timeouts - Support global scale - Card number - Heavy PCI DSS compliance - Massive breach risk - Legal exposure - The SDK sends card data directly to the provider. - The provider returns a token. - You store only that token. - The ride price may change. - You may need to adjust final fare. - You don’t want unpaid rides. - You need commission control. - You must handle taxes. - You need dispute handling. - You need fraud protection. - “Works fine” - “Lost $3M silently” - Card networks - Payment providers - Network calls - Authorization succeeds. - Capture request times out. - Customer gets double-charged. - Velocity checks - Device fingerprinting - Location mismatch - Behavioral anomaly detection - OTP verification - Manual review - Updating internal ledger - Issuing refund request - Adjusting driver balance - Handling payout already completed - Aggregate earnings daily - Settle weekly - Offer instant payout (for a fee) - Pull reports from payment provider - Compare with internal ledger - Identify mismatches - Flag for review - Trigger investigation - 1M+ rides/day - 1000+ transactions per second at peak - Stateless payment services - Event-driven architecture - Message queues (Kafka/PubSub) - Horizontal scaling - Primary provider - Secondary fallback - Just API calls - Just token storage - Just Stripe integration - Distributed systems - Financial accounting - Legal compliance - Fault tolerance - Fraud modeling - Bank integrations - Event-driven infrastructure - Idempotency keys - Double-entry accounting - Multi-provider failover - Fraud scoring - Reconciliation pipelines - Auditability - Correctness