Stripe Payment Integration
Webhook orchestration, reconciliation, and failure handling for a transactional platform.
01 — The problem
A transactional product needed to take payments at scale without a finance team manually chasing mismatches every month. The naive integration — charge the card, trust the response — falls apart the first time a webhook is delivered twice, arrives out of order, or never arrives at all. Money moves, the ledger disagrees, and someone has to reconcile it by hand.
02 — Constraints
Webhooks are at-least-once: every handler must be idempotent.
Card data can never touch our infrastructure — PCI scope stays minimal.
Payment state must survive a total application restart mid-flow.
Disputes and refunds arrive asynchronously, sometimes weeks later.
Finance needs a ledger they can trust without asking engineering.
03 — Architecture
Intent-first payment flow
Payment intents are created server-side and persisted before the client is ever handed a token. The database row is the source of truth, not the API response — so a dropped connection or a crashed process leaves a recoverable record rather than an orphaned charge.
Idempotent webhook orchestration
Every inbound event is signature-verified, deduplicated by event ID, and written to an append-only event log before any business logic runs. Handlers are pure state transitions over that log, which makes replay safe and makes out-of-order delivery a non-event.
Reconciliation as a first-class system
A scheduled reconciler walks provider-side records against internal ledger entries, classifies every divergence, and auto-resolves the known categories — late webhooks, partial captures, refund races. What's left is a short, human-readable exception queue instead of a spreadsheet.
Failure handling with teeth
Retries use bounded exponential backoff with a dead-letter path. Anything that lands in the dead-letter queue raises an alert with the full event payload attached, so the on-call engineer starts from evidence rather than from a log search.
04 — Outcome
Manual reconciliation replaced by an automated pass with a small exception queue.
Duplicate and out-of-order webhooks became a non-issue by construction, not by luck.
PCI scope kept minimal — no card data in application infrastructure.
Payment failures surface as actionable alerts with full context attached.