Treat a billing webhook as a notification that needs verified, repeatable processing. Verify its signature, persist accepted work durably and update entitlements through an idempotent worker. Keep reception separate from completion so a duplicate delivery or a crashed worker cannot grant access twice or lose a paid customer’s update.
Start with the access rule, not the checkout screen
A fitness platform might sell a coach workspace subscription, individual memberships or both. Write down which account receives each entitlement and when access begins or ends. An owner paying for a workspace must not accidentally create a personal entitlement for whichever browser user completed checkout. Resolve billing identifiers to internal accounts on the server.
Define trial, payment-pending, active, grace-period and ended behaviour before implementing handlers. These are product states with support consequences. A payment failure might lead to a clearly defined recovery period, while a cancellation at period end may preserve access until the effective date. The provider’s lifecycle and your access policy need an explicit mapping.
Verify delivery before accepting work
Stripe documents signature verification against the raw request body, duplicate deliveries and events arriving out of order. Use the official SDK with the endpoint’s signing secret, preserve the raw payload required for verification, and separate test and live configuration. Do not accept a JSON object merely because it contains a familiar event name.
Make the inbox durable and processing recoverable
One implementation pattern is an event inbox with provider, account context, event ID, received time and processing status. Use a unique constraint for the delivery identity. Persist the accepted event and its pending work atomically, or use a transactional outbox when dispatching to another queue. Return success only after that durable handoff, without making the provider wait for unrelated emails or reports.
A deduplication row should not mean “finished” when work has only started. Suppose the server inserts an event ID, then crashes before updating access. If the next delivery sees the row and simply skips it, the update is lost. Keep pending, processing, failed and completed states distinct, with a recoverable claim or lease for workers.
Separate event deduplication from business idempotency
An event ID helps recognize a repeated delivery. Business idempotency prevents the same outcome from being applied twice through different processing paths. A renewal update should not issue a second welcome credit just because a reconciliation job and a webhook worker both run. Give side effects stable business keys and enforce uniqueness where the data is committed.
Where an email or external API call cannot share the entitlement transaction, record an outbox action alongside the committed state. Process that action with its own retry and deduplication policy. This makes the remaining uncertainty visible instead of implying that a single HTTP handler can guarantee exactly-once effects across several services.
Handle ordering and reconciliation explicitly
Do not assume arrival order is the order of business changes. Serialize or otherwise coordinate updates for the same subscription, and decide when the handler needs the provider’s current record instead of an older event snapshot. Fetching fresh state is useful, but concurrent workers still need a policy for committing their results.
Run a bounded reconciliation process for accounts whose local access may have drifted. Record what was compared, what changed and whether manual review is required. A support view should distinguish “payment pending,” “event received” and “access updated.” That is more useful than an unexplained boolean named paid.
Test the failures before enabling live billing
- Deliver the same verified event twice and confirm only one business effect.
- Crash after inbox persistence and confirm the worker can resume.
- Deliver a cancellation and an earlier update in reverse order.
- Run webhook processing and reconciliation concurrently.
- Reject invalid signatures and isolate test-mode data.
- Check renewal, trial expiry, payment recovery and workspace ownership changes.
Where this applies to healthtech and B2B products
The pattern fits coach portals, memberships and other recurring SaaS services. It is implementation guidance, not a claim that UltraFit360 currently uses Stripe. Mobile-store purchases have their own provider and policy requirements; a web subscription design should not be presented as a shortcut around them. AptixLabs can help define the entitlement model and recovery cases before wiring payment screens.
Stripe: signature verification, retries and event delivery
Stripe: subscription lifecycle events
Is saving a Stripe event ID sufficient for idempotency?
No. Record processing status and recover unfinished work. Also make the business effect idempotent so different events or reconciliation paths cannot apply the same outcome twice.
Should a webhook wait for every downstream task?
Keep the handler short, but acknowledge only after verified work is durably accepted. Complete slow side effects asynchronously with explicit retries and processing states.
Can checkout success alone grant subscription access?
Do not rely solely on the browser success screen. Resolve the billing account and apply server-verified lifecycle information through an explicit entitlement policy.
