What Is a Payment Processing System?
A payment processing system moves money from a buyer to a seller by orchestrating communication between the merchant, payment gateway, payment processor, card networks (Visa/Mastercard), and issuing/acquiring banks. Core requirements: exactly-once transaction processing (double charges are catastrophic), sub-second authorization, and compliance with PCI-DSS (cardholder data security). Stripe processes hundreds of billions of dollars annually.
Payment Flow
- Tokenization: raw card number never touches merchant servers. Browser/app calls Stripe.js, which sends card data directly to Stripe and returns a payment_method_id (token). Merchant stores and uses the token, never the PAN (Primary Account Number). Reduces PCI scope dramatically.
- Authorization: merchant’s backend calls payment gateway (Stripe) to authorize the card. Gateway sends to card network (Visa). Card network routes to issuing bank (Chase). Bank checks: sufficient funds, card not blocked, fraud score. Returns approve/decline. Money is reserved (not moved) at this step.
- Capture: merchant confirms the amount to capture (can be <= authorized amount). Money moves from buyer's account to acquiring bank's escrow.
- Settlement: acquiring bank sends funds to merchant’s bank account, typically T+1 or T+2 business days. Batch process.
Idempotency
Exactly-once is the hardest problem in payments. Network timeouts cause ambiguity: did the charge succeed before the timeout? If the client retries, will it double-charge? Solution: idempotency keys. Every payment API call includes a unique idempotency_key (UUID). Server stores the key → response mapping. If the same key arrives again, return the stored response without re-executing. Implementation:
BEGIN TRANSACTION;
SELECT response FROM idempotency_keys WHERE key = ?;
IF found: ROLLBACK; RETURN stored_response;
-- not found: execute payment
[execute charge against card network]
INSERT INTO idempotency_keys (key, response, created_at) VALUES (?, ?, NOW());
COMMIT;
Ledger (Double-Entry Accounting)
Every financial movement is recorded as a pair of entries: a debit from one account and a credit to another. The sum of all entries must be zero (invariant). This prevents money from being created or destroyed. Example: customer charges $100 →
- DEBIT customer_account $100
- CREDIT merchant_account $100 (pending settlement)
Ledger table: append-only, never update or delete. Balance = SUM of all entries. Audit trail is implicit.
Reconciliation
Compare internal ledger against bank statements and card network reports. Discrepancies: fees not recorded, failed settlements, reversed transactions. Nightly batch job: fetch settlement files from Visa/MC (ISO 8583 format), compare with internal ledger, flag mismatches for manual review. This is how payment companies detect bugs and fraud that slips through real-time checks.
Refunds and Chargebacks
Refund: merchant-initiated return of funds. New transaction on the original payment_intent. Chargeback: bank-initiated dispute by the cardholder. Bank reverses the charge and sends a chargeback notice. Merchant must respond with evidence (transaction records, shipping proof) or accept the loss. High chargeback rate (>1%) triggers account termination by card networks. Fraud detection reduces chargebacks; dispute management system handles evidence submission.
PCI DSS Compliance
Payment Card Industry Data Security Standard. Never store raw card numbers (PAN), CVV, or full magnetic stripe data after authorization. Use tokenization (Stripe handles this). Encrypt data in transit (TLS 1.2+). Network segmentation: payment servers isolated from public internet. Annual audits and penetration tests for Level 1 merchants (>6M transactions/year).
Interview Tips
- Idempotency key is the central correctness mechanism — explain it clearly.
- Double-entry ledger demonstrates financial systems knowledge — distinguishes senior candidates.
- Tokenization: card data goes directly to gateway, never through merchant backend.
- Auth + capture separation: important for hotels (auth at booking, capture at checkout).
{
“@context”: “https://schema.org”,
“@type”: “FAQPage”,
“mainEntity”: [
{
“@type”: “Question”,
“name”: “What is an idempotency key in payment systems and how is it implemented?”,
“acceptedAnswer”: { “@type”: “Answer”, “text”: “An idempotency key is a unique token provided by the client that guarantees a payment operation is executed at most once, regardless of retries. Network failures create ambiguity: did the charge succeed before the connection dropped? Without idempotency, retrying the payment could double-charge the customer. Implementation: the client generates a UUID (or uses the order ID) as the idempotency key and includes it in every API request header. The payment server stores the mapping: (idempotency_key → response) in a database table (e.g., idempotency_keys: key, response_json, status, created_at). Before processing: check if the key exists. If yes, return the stored response immediately — no re-execution. If no, execute the payment, then store the key+response atomically. The key expires after 24 hours (payment retries should complete within a day). Race condition: two concurrent requests with the same key — use a unique constraint on the key column; the second INSERT fails, triggering a retry that fetches the first response.” }
},
{
“@type”: “Question”,
“name”: “How does double-entry bookkeeping work in a payment system?”,
“acceptedAnswer”: { “@type”: “Answer”, “text”: “Double-entry bookkeeping ensures money is never created or destroyed — every transaction debits one account and credits another by the same amount. The invariant: SUM of all ledger entries = 0. Implementation: a ledger table stores immutable records: (id, account_id, amount, direction[debit/credit], transaction_id, created_at). Amount is always positive; direction indicates debit or deduction. Account balance = SUM(credits) – SUM(debits). Example: customer pays merchant $100: INSERT ledger (customer_account, -100, debit, txn_123); INSERT ledger (merchant_account, +100, credit, txn_123). Both rows have the same transaction_id — they are linked. Reconciliation: sum all rows grouped by transaction_id — should always equal zero. Any non-zero sum indicates a bug or fraud. Benefits: complete audit trail, balance is derivable at any point in time (replay), prevents negative balances if you check before inserting. Never update or delete ledger rows — append only.” }
},
{
“@type”: “Question”,
“name”: “What is the difference between payment authorization and capture?”,
“acceptedAnswer”: { “@type”: “Answer”, “text”: “Authorization: the payment gateway verifies that the cardholder has sufficient funds and the card is valid, then reserves (holds) the authorized amount on the cardholder's account. Money doesn't move yet. The merchant receives an authorization code. Typical use: hotel check-in (authorize the full stay amount), car rental (authorize a deposit). Capture: the merchant confirms the actual amount to charge (can be <= authorized amount) and triggers the actual movement of funds. This happens when the service is delivered — hotel checkout, car return, item shipped. If capture never happens, the authorization is automatically released after 5-7 days (card network policy). Two-step auth+capture allows: (1) adjusting the amount (room service charges added to hotel bill), (2) canceling without a charge if the order is canceled before capture, (3) pre-authorizing for uncertain amounts. Single-step (auth+capture simultaneously): used for immediate purchases (e-commerce at the moment of checkout). Stripe supports both: payment_intent with capture_method=automatic (immediate) or capture_method=manual (two-step).” }
}
]
}