Payment Processing Architecture: How Stripe Moves Money Safely
SD
ScaleDojo
May 11, 2026
2 min read456 words
The $1 Trillion Problem: Moving Money Without Losing Any
In 2022, Stripe processed over $817 billion in payments. That is $25,000 every second. A single bug that charges customers twice, even for 0.001% of transactions, would mean 8 million duplicate charges per year. Stripe's entire architecture is built around one principle: it is better to fail a transaction than to process it incorrectly.
The Payment Flow
Card Payment Flow (Stripe Model):
[Customer Browser]
|
1. Card number entered
|
[Stripe.js] -- tokenizes client-side
| (raw card number NEVER hits your server)
| (reduces PCI DSS scope from SAQ-D to SAQ-A)
tok_abc123
|
[Your Server]
|
2. POST /charges {token: tok_abc123, amount: 2999, idempotency_key: uuid}
|
[Stripe API]
|
3. Route to card network
|
[Visa/Mastercard Network]
|
4. Forward to issuing bank
|
[Customer's Bank]
|
5. Check: fraud? balance? limits?
|
APPROVED / DECLINED
|
Response travels back through entire chain
|
Total time: ~1.5 seconds
|
6. If approved: authorization hold placed
7. Settlement: batch process, actual money moves T+2 days
Idempotency: The Most Critical Pattern
The Timeout Problem:
Your Server ---POST /charge---> Stripe
| |
| (network timeout) | charge succeeds
| |
X <--- response lost --- |
| |
Did it work? WHO KNOWS. $29.99 charged
Without idempotency key:
Retry -> DOUBLE CHARGE ($59.98) -> angry customer -> chargeback
With idempotency key:
Retry with same key -> Stripe returns original result
-> Single charge ($29.99) -> safe
Implementation:
POST /v1/charges
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Stripe stores: {key -> response} for 24 hours
Same key = same response, no reprocessing
Webhook Architecture
Asynchronous Payment Events:
[Stripe] -- webhook POST --> [Your /webhooks endpoint]
|
Events: Verify signature (HMAC)
- payment_intent.succeeded |
- charge.refunded Deduplicate (store event ID)
- invoice.payment_failed |
- customer.subscription.deleted Process in background job
|
Return 200 immediately
(within 20 seconds)
Rules:
1. ALWAYS verify webhook signature (prevent spoofing)
2. Handler MUST be idempotent (Stripe retries up to 3 days)
3. Return 200 BEFORE heavy processing
4. Use event ID to deduplicate
5. Process in background queue (not in webhook handler)
Interview Tip
When designing a payment system, say: 'I would never handle raw card numbers - use Stripe.js for client-side tokenization to minimize PCI scope. Every charge request includes an idempotency key (UUID generated before the first attempt) so retries are safe. Webhooks handle async events like refunds and chargebacks - the handler verifies the HMAC signature, deduplicates by event ID, returns 200 immediately, and processes in a background queue. All financial data lives in a PostgreSQL database with ACID guarantees and double-entry accounting.'
<Architecture overview
/blockquote>
Key Takeaway
Payment systems require idempotency keys on every write operation to handle retries safely. Use webhooks for async events. Store financial data in ACID databases with double-entry accounting. Tokenize card data to avoid PCI DSS scope. Reliability and correctness outweigh all other considerations.
No comments yet. Be the first to start the thread!
Related Articles
Enjoyed this content?
Your support keeps us creating free resources
We put a lot of hours into researching and writing these guides. If it helped you, consider buying us a coffee. Every bit goes toward keeping ScaleDojo's content free and growing.
$
One-time payment via Stripe. ScaleDojo account required.