Ismail[Ariyan]
← Index
01 // Backend · Correctness

Subscription Billing & Idempotent Webhooks

RemixPostgreSQLPrisma
00

Summary

Payment webhooks don't arrive exactly once, in order, or at all. I made the handlers idempotent and added a scheduled job that re-checks our records against the provider — so a duplicate delivery is a no-op, and a dropped event gets caught instead of quietly costing revenue.

Role
Software Engineer, Inument Solutions Limited
Period
2025—Present
Status
In production
Stack
Remix · PostgreSQL · Prisma
01

Problem

Payment providers deliver webhooks at-least-once, not exactly-once. Process one twice and you double-charge a customer; miss one and revenue disappears with no error anywhere. Neither failure shows up in tests — they show up in production, in someone's bank account.

02

Constraints

  • C01This is money, not a UX nice-to-have — no tolerance for double-processing or dropped events
  • C02Deliveries retry, duplicate and arrive out of order; the handler can't assume clean input
  • C03Five engineers, no dedicated payments or data-reliability specialist
03

Architecture

01
Webhook ingress

Handlers key on the provider's event ID, so a repeat delivery is a no-op.

02
Reconciliation

A scheduled job re-checks our records against the provider and repairs the gaps.

03
Schema layer

Prisma over PostgreSQL, with the migration path fixed so schema changes ship without downtime.

04
App layer

Remix serves the billing-facing application logic.

04

Decisions

D01Idempotent handlers vs. trusting delivery guarantees
Options
Key every handler on the event ID / Trust the provider to deliver once
Chosen
Idempotent handlers
Rationale
The provider documents at-least-once delivery. Building for exactly-once processing on my side was the only way to actually guarantee it.
What it cost
Every handler needs an existence check and a bit of state before it does anything.
D02Webhooks alone vs. webhooks plus reconciliation
Options
Trust the event stream / Add a scheduled re-sync against the provider
Chosen
Both
Rationale
Idempotency stops double-processing but does nothing about events that never arrive. A periodic sync is what turns 'usually correct' into correct.
What it cost
A second system to build, monitor, and keep in step with every schema change.
05

Results

100%
Data integrity across billing records
25%
Faster deployment cycles
Zero
Downtime schema updates
06

Retrospective

I built the handlers first and the reconciliation job second. That was backwards. Idempotency prevents the loud failure; missed events are the quiet one, and quiet is where money actually leaks. I'd build the reconciler first now and treat the webhook path as the optimization.

07

Artifacts

Employer
Inument Solutions Limited
Status
In production
Note
Described in generalized terms — no client data or proprietary code
Next — 02
Rectify Learn