Production layer: subscriptions, invoicing, webhook idempotency, dunning, Stripe Tax, PCI-safe architecture. 2–3 weeks, monthly subscription delivery.
- Scope
- Ship
- Iterate
monthly subscription
Who this is for
Founder or product manager wiring Stripe into a custom app for the first time, where subscriptions, tax, webhooks, and dunning each carry their own minefield of edge cases — and one wrong assumption on day one becomes a billing incident six months later.
The pain today
- Stripe Products and Prices model is unclear — subscriptions break when Plan A upgrades to Plan B
- Webhooks fire twice or arrive out of order, corrupting downstream state
- Dunning never configured — failed card retries leak monthly revenue silently
- EU VAT or US sales tax deferred until an audit makes it urgent
- No test suite — billing logic breaks silently on every deploy
The outcome you get
- Subscriptions, metered billing, and invoices wired end-to-end
- Webhook handler with idempotency key storage, retry tolerance, and a replay endpoint
- Stripe Tax enabled with correct tax IDs, reverse charge, and B2B VAT handling
- Dunning configured — smart retries, custom email sequences, grace period
- Full test suite covering webhook lifecycle events, proration, and edge cases
Stripe Products, Prices, and Subscriptions
A Product is what you sell. A Price is how you charge for it — one Product can carry many Prices (monthly, yearly, per-seat, volume tiers). Subscriptions attach to a Price, not a Product, which means upgrading a customer from monthly to annual is a Price swap on the Subscription, not a new record. Usage-based billing adds a Metered Price where you post usage records throughout the billing period and Stripe calculates the final invoice.
I set up your Products and Prices in week one to match your actual pricing model exactly. The object hierarchy matters: get it wrong at the start and proration, invoice line items, and the Customer Portal all behave in unexpected ways. In 17 years I have seen that category of mistake happen most often when engineers copy a Stripe quickstart that assumes a simpler billing model than the product actually has.
Subscriptions, metered billing, and invoices wired end-to-end
Webhooks and idempotency patterns
Every Stripe event — payment succeeded, subscription updated, invoice finalized — fires a webhook to your endpoint. Two patterns break webhook handlers consistently: duplicate delivery and out-of-order arrival. Stripe guarantees at-least-once delivery with up to 72 hours of exponential backoff retries, so the same event.id can land twice.
The fix is straightforward but must be built from the start: store each event.id before processing, check on arrival, no-op if already handled. For ordering, never trust event timestamps — re-fetch the Stripe object (Subscription, Invoice, Customer) before acting on it. I also wire a replay endpoint so you can re-process any event manually when something downstream fails. That feature sounds like an edge case until your payment processor goes down for 10 minutes and you need to reconcile 300 events cleanly.
40+: Payment providers integrated.
PCI compliance and Stripe's security architecture
The reason founders choose Stripe over rolling their own payment form is PCI scope reduction. When you use Stripe.js or the Payment Element, card data never touches your server — it goes directly to Stripe's servers. That puts you in SAQ A, the lightest PCI compliance tier, rather than SAQ D, which requires quarterly penetration testing and on-site assessment.
I configure your integration to stay in SAQ A deliberately: Payment Element over manual card field handling, no card data stored server-side, webhook signature verification on every incoming event. For teams with SCA requirements (EU-based customers), I wire 3D Secure authentication through Stripe's automatic handling — Stripe decides when to request it based on the card issuer rules, and your code handles the redirect flow cleanly. These are not optional details. They are the difference between a PCI-compliant SaaS and a liability.
Tax, dunning, and proration
Stripe Tax calculates tax automatically once origin and destination addresses are configured. Registering in each jurisdiction is your accountant's job, not mine — but I wire the tax registration IDs so Stripe applies them correctly. For B2B EU sales, I enable reverse charge handling, which suppresses VAT when the buyer provides a valid EU VAT ID. US economic nexus registrations follow the same pattern.
Dunning: smart retries run three attempts spaced by Stripe's default ML-based schedule, with custom email content per retry stage and a configurable grace period before subscription cancellation. Proration on plan changes is automatic — Stripe computes the credit for unused days on the old plan and the prorated charge for the new one. Your code passes the new Price ID and Stripe handles the math. Where I add value is wiring the proration preview call before the upgrade so the user sees the exact charge before confirming.
Payment Element, Checkout, and Customer Portal
Three implementation paths, each correct in different situations. Stripe Checkout is a Stripe-hosted page — fastest to ship, minimal brand control, fine for one-off purchases or simple SaaS signups. Payment Element is an embedded UI component that Stripe maintains, supports every payment method they offer, and gives you reasonable brand control without building a custom form. Custom Elements (the older API) are almost never the right choice in 2026 — Payment Element replaced them.
Customer Portal is Stripe's hosted billing management page — plan changes, payment method updates, invoice history. You can embed it as an iframe or redirect to it. For products needing a custom cancellation flow, a credit balance display, or features the portal does not expose, I build a custom billing UI on top of the Stripe APIs instead. That adds scope but gives full control. I pick based on your product requirements, not a template.
Error handling and go-live checklist
Card declines are not all the same. A generic_decline is different from an insufficient_funds decline — one you should retry, one you should not. Stripe's error codes map to distinct customer-facing messages and retry strategies. I implement structured error handling with code-level routing: soft declines go to dunning retry, hard declines surface a clear user message, and API errors (rate limits, network timeouts) get exponential backoff at the integration layer.
Before going live, every integration I ship passes eight checks: webhook signature verification active, HTTPS endpoint with valid SSL, no card data logged anywhere, test-mode and live-mode keys in environment variables (never hardcoded), idempotency keys on all mutation calls, Stripe CLI smoke test on the local webhook listener, restricted API keys scoped to only the permissions used, and a full test suite run in Stripe test mode. That checklist exists because I have seen exactly one item from it skipped per production incident.
Case study: bolttech payment platform
The discipline behind my Stripe integration work comes directly from payment engineering at scale. At bolttech, a $1B+ unicorn, I led the Payment Service that unified 40+ payment providers across 15+ international markets. Webhook idempotency, retry architecture, and reconciliation were not best practices to follow — they were engineering requirements enforced by the volume.
Zero post-launch critical bugs on the Payment Service. 99.9% platform uptime. Those numbers come from applying the same patterns consistently: event deduplication, structured error handling, exhaustive test coverage on billing flows. I apply the same discipline on a single-Stripe SaaS integration. The surface is smaller. The methodology does not change.
Pricing and timeline
Stripe integration work runs on the Applications Standard tier at $4,999/mo. A standard subscription billing setup — Products, Prices, Subscriptions, dunning, Customer Portal, test suite — ships in 2–3 weeks. Complex patterns like usage-based metered billing, Stripe Connect for marketplaces, or migrating an existing subscriber base from another processor add 2–4 weeks depending on scope.
Most customers stay engaged for 3–6 months through the initial buildout and early customer feedback cycles: adding pricing tiers, refining the dunning sequence, wiring the subscription analytics dashboard. The 14-day money-back guarantee applies from day one. Work is Made for Hire — every line of code belongs to you.
Recent proof
A comparable engagement, delivered and documented.
Unified payment orchestration across Asia and Europe
Delivered the payment orchestration platform at bolttech, a $1B+ unicorn, with 40+ integrations across multiple regions.
Read the case studyFrequently asked questions
The questions prospects ask before they book.
Stripe Checkout is a Stripe-hosted page — it ships fast and requires no UI work, but you lose control over the layout and flow. Payment Element is an embedded component that lives inside your own page, supports every Stripe payment method, and gives you control over the surrounding UX. For most subscription SaaS products I recommend Payment Element. Checkout is fine for one-off purchases where the user expects to leave your site briefly.
By storing each Stripe event.id before processing and checking for it on every incoming webhook. If the ID already exists in the database, the handler returns 200 immediately without re-executing. Stripe guarantees at-least-once delivery with up to 72 hours of retries, so idempotency is not optional — it is the minimum viable webhook implementation. I also add a replay endpoint so any event can be re-processed manually when a downstream system fails.
Yes — Express, Custom, and Standard accounts, split payments, payout schedules, connected account onboarding, and 1099 reporting. Connect is significantly more complex than standard Stripe billing, so it prices up to the Applications Pro tier at $5,499/mo for the initial buildout. Timeline is typically 4–8 weeks depending on the payout model and the number of connected account types.
Stripe Tax calculates the correct tax amount at checkout once you configure your origin address and register tax IDs in each jurisdiction. Registering in US states with economic nexus, EU VAT MOSS, or UK VAT is your accountant's responsibility — I wire the registration IDs into Stripe and configure the tax behavior (inclusive vs. exclusive) on each Price. For B2B EU sales I enable reverse charge, which suppresses VAT when the buyer supplies a valid VAT ID.
Every webhook event type your app handles, using Stripe's test-mode event fixtures: subscription lifecycle (trialing, active, past_due, canceled), invoice finalized and paid, payment failure and dunning retry, proration on plan change, 3D Secure redirect flow, and dispute creation. The suite runs in CI on every pull request, so billing logic regressions surface before merge rather than after a customer's payment fails.
Yes, scoped as a separate workstream. The typical pattern: import customer records using Stripe's migration tools for major competitors, attach payment method tokens where available, preserve subscription start dates for accurate proration, and run a reconciliation pass before cutover. For large subscriber bases this adds roughly 2 weeks. I always run the migration in parallel (both systems active) until reconciliation passes, then cut over in a single window.
Eight items I verify before every production deployment: webhook signature verification active, HTTPS endpoint with valid SSL, no card data logged server-side, live-mode API keys in environment variables (not hardcoded), idempotency keys on all mutation API calls, Stripe CLI smoke test on the local webhook listener, restricted API keys scoped to minimum permissions, and a full test suite run in Stripe test mode against the actual product flows.