We've integrated Stripe billing into more products than we can count. And every time we inherit an existing integration, we find the same mistakes. Here are the patterns that work and the pitfalls to avoid.
Mistake #1: Not Using Webhooks Properly
The most common issue: relying on client-side redirect after checkout to activate a subscription. The redirect can fail (user closes tab, network hiccup), and now you have a paying customer with no access.
The fix: Always use webhooks as the source of truth. Listen for checkout.session.completed, invoice.paid, and customer.subscription.updated. Activate access in response to webhooks, not redirects.
Mistake #2: Ignoring Idempotency
Stripe sends webhooks at least once, but sometimes more. If your webhook handler creates a new record every time, you'll end up with duplicate entries.
The fix: Use the event ID as an idempotency key. Before processing any webhook, check if you've already handled that event. A simple processed_events table works fine.
Mistake #3: Wrong Proration Behavior
When a customer upgrades mid-cycle, Stripe prorates by default. This is usually what you want. But many teams don't handle the resulting invoice correctly — especially when it creates a $0 or negative invoice.
The fix: Set proration_behavior explicitly on every subscription update. Test the upgrade and downgrade flows with real Stripe test clocks. Handle credit notes and negative invoices in your billing dashboard.
Mistake #4: Hardcoding Prices
Don't hardcode Stripe Price IDs in your application code. Plans change, A/B tests need different prices, and international pricing requires flexibility.
The fix: Store plan configurations in your database with references to Stripe Price IDs. This lets you change pricing without deploying code, and enables per-customer pricing when needed.
Mistake #5: Skipping Tax
Tax compliance isn't optional. If you're selling to customers in multiple states or countries, you need to collect and remit sales tax or VAT.
The fix: Use Stripe Tax. It automatically calculates the correct tax based on the customer's location. Enable it from the start — retrofitting tax handling is painful.
Our Stripe Integration Checklist
- Webhook endpoint with signature verification
- Idempotent webhook processing with event deduplication
- Customer portal for self-service plan changes
- Stripe Tax enabled for automatic tax calculation
- Test clock coverage for subscription lifecycle testing
- Dunning emails configured for failed payments
- Billing dashboard showing MRR, churn, and payment status
- Graceful handling of past-due and canceled states
Get these right from the start and you'll avoid the most painful billing bugs we see in production SaaS products.