System Design
Case Study: Payment System
**Stripe:** - Fees: 2.9% + $0.30 (US) - Best for: developers, startups - Features: excellent API, docs **PayPal:** - Fees: 2.9% + $0.30 - Best for: buyer trust - Features: buyer protection **Adyen:** - Fees: IC++ (interchange plus) - Best for: enterprise, global - Features: unified platform **Square:** - Fees: 2.6% + $0.10 - Best for: omnichannel (online + POS)
**Visa:** - Standard: < 0.9% and < 100 disputes/month - Early Warning: 0.9-1.8% - Excessive: > 1.8% - Penalty: $25K-$50K/month + monitoring **Mastercard:** - Excessive Chargeback Program: > 1.5% - Penalty: fines + potential termination **Target**: Keep chargeback rate < 0.5%
EU PSD2 requires SCA for online payments: - Two of three factors required: 1. Something you know (password, PIN) 2. Something you have (phone, card) 3. Something you are (fingerprint, face) - 3D Secure 2.0 satisfies SCA - Reduces fraud and chargebacks - Exemptions: < €30, recurring, low risk
Payment System Architecture
Payment System Architecture
Why is this needed?
Payments are a critical part of any e-commerce. Mistakes cost real money and reputation. The right architecture ensures reliability, security, and compliance.
What is it?
A payment system includes: checkout flow, PSP (Payment Service Provider) integrations, order management, a ledger for transaction accounting, and reconciliation. Key requirements: exactly-once semantics and PCI DSS compliance.
How does it work?
Example
**Payment flow with two-phase capture:** ```typescript // Step 1: Authorize (reserve funds) const payment = await paymentService.authorize({ orderId: 'order_123', amount: { amount: 9999, currency: 'USD' }, // $99.99 paymentMethod: { type: 'card', token: 'tok_xxx' } }); // payment.status = 'authorized' // Funds reserved on customer's card // Step 2: Capture (charge) after fulfillment ready await paymentService.capture(payment.id); // payment.status = 'captured' // Money transferred // OR: Cancel if out of stock await paymentService.cancel(payment.id); // payment.status = 'cancelled' // Reservation released ```
What did you learn about Payment System architecture?
PCI DSS Compliance
PCI DSS Compliance
Why is this needed?
PCI DSS (Payment Card Industry Data Security Standard) - mandatory requirements for anyone who processes card data. Violation leads to fines up to $500K/month and a ban on accepting cards.
What is it?
PCI DSS has 4 compliance levels depending on transaction volume. The core principle: minimize scope - don't store or process full card data. Use tokenization via PSP.
How does it work?
Example
**PCI DSS requirements summary:** ``` 12 Requirements: 1. Install/maintain firewall 2. No vendor-supplied default passwords 3. Protect stored cardholder data 4. Encrypt transmission over networks 5. Use/update anti-virus 6. Develop secure systems 7. Restrict access to cardholder data 8. Assign unique IDs to users 9. Restrict physical access 10. Track/monitor network access 11. Regularly test security 12. Maintain security policy With tokenization (SAQ-A): → Only #12 applies fully → Minimal infrastructure requirements → Annual self-questionnaire (~20 questions) ```
What did you learn about PCI DSS Compliance?
Idempotency: Preventing Duplicate Payments
Idempotency: Preventing Duplicate Payments
Why is this needed?
Network errors, timeouts, and retries can lead to duplicate payments. The user clicked 'Pay' twice, or a retry after a timeout - and they were charged twice. This is unacceptable.
What is it?
An idempotency key is a unique identifier for a request. On a repeated request with the same key, the result of the previous request is returned rather than creating a new payment. The key is generated by the client.
How does it work?
Example
**Stripe's idempotency implementation:** ```typescript // Stripe automatically handles idempotency const stripe = new Stripe(apiKey); // Same idempotency key = same result const payment1 = await stripe.paymentIntents.create( { amount: 1000, currency: 'usd', ... }, { idempotencyKey: 'order_123_attempt_1' } ); // Retry with same key returns cached result const payment2 = await stripe.paymentIntents.create( { amount: 1000, currency: 'usd', ... }, { idempotencyKey: 'order_123_attempt_1' } ); // payment1.id === payment2.id (same payment) ```
What did you learn about idempotency and preventing duplicate payments?
Ledger and Reconciliation
Ledger and Reconciliation
Why is this needed?
Financial accounting requires penny-perfect accuracy. The ledger provides double-entry bookkeeping - each transaction is reflected in at least two accounts. Reconciliation checks data against the PSP to detect discrepancies.
What is it?
Double-entry ledger: debit of one account = credit of another. Reconciliation: daily matching of transactions between your system, the PSP, and the bank. Discrepancies require investigation.
How does it work?
Example
**Daily reconciliation report:** ``` Reconciliation Report - 2024-01-15 ────────────────────────────────── Transactions processed: 1,234 Matched: 1,230 (99.7%) Unmatched: 4 Discrepancies: 2 Unmatched Internal → Stripe: • pay_abc123: $99.00 - No Stripe record (pending?) Unmatched Stripe → Internal: • ch_xyz789: $50.00 - Missing internal record • ch_def456: $25.00 - Missing internal record Amount Discrepancies: • pay_ghi012: Internal $100.00, Stripe $99.97 (diff: $0.03) Likely cause: Currency conversion rounding Status: NEEDS REVIEW Assigned to: finance-team@company.com ```
What did you learn about ledger and reconciliation?
Refunds and Chargebacks
Refunds and Chargebacks
Why is this needed?
Refunds are part of normal business. Chargebacks (disputes) are when a customer disputes a transaction through their bank. A high chargeback rate (> 1%) leads to fines and loss of the ability to accept cards.
What is it?
Refunds are initiated by the merchant and always succeed. Chargebacks are initiated by the customer through their bank - the merchant can dispute them by providing evidence. A lost dispute = lost money + fee.
How does it work?
Example
**Chargeback prevention tips:** ```typescript // 1. Clear billing descriptor // Customer sees "MYSTORE" not "STRIPE*ABCD123" await stripe.paymentIntents.create({ amount: 1000, currency: 'usd', statement_descriptor: 'MYSTORE ORDER', statement_descriptor_suffix: '1234' // Order ID }); // 2. Send order confirmation immediately await sendEmail(customer.email, { subject: 'Order Confirmed - MyStore', body: `Thank you! Your order #${order.id} for $${order.total}...` }); // 3. Proactive communication on shipping await sendEmail(customer.email, { subject: 'Your order has shipped!', body: `Track your package: ${trackingUrl}` }); // 4. Easy refund process // Better to refund than get chargeback // Chargeback = $15-25 fee + counts toward rate ```
What did you learn about refunds and chargebacks?