Loading...
Loading...

Billing code is the scariest code in your codebase. A bug in your dashboard shows the wrong chart. A bug in your billing charges someone's credit card twice. Or doesn't charge them at all. Or charges them the wrong amount. Or loses track of their subscription state.
Every billing bug is either a customer support nightmare or a revenue leak. Usually both.
Stripe exists to make billing less terrifying. But "Stripe handles payments" undersells it dramatically. Stripe handles the entire billing lifecycle. The hard part isn't processing a credit card. It's managing subscriptions, handling upgrades, processing cancellations, tracking usage, generating invoices, and keeping everything synchronized with your application.
A subscription sounds simple. User pays monthly. Gets access. Done.
Then reality arrives. Trial periods. Annual vs monthly pricing. Proration when switching plans mid-cycle. Grace periods for failed payments. Pause and resume. Cancellation with end-of-period access. Reactivation of cancelled subscriptions. Grandfathered pricing for early adopters.
Every one of those scenarios is a state machine transition that needs to be handled correctly. Miss one and you either lose revenue or anger a customer.
Stripe models all of this. A subscription has a status: trialing, active, past_due, canceled, unpaid, paused. Each status transition triggers a webhook. Your application listens for those webhooks and updates access accordingly.
The critical insight is treating webhooks as the source of truth for subscription state. Don't check the user's plan by querying your database. Check it by querying Stripe or by keeping your database synchronized via webhooks. Your database is a cache of Stripe's state, not the other way around.
Plan changes trigger proration automatically. User upgrades from $10/month to $25/month halfway through the billing cycle? Stripe calculates the prorated credit and charge. The user sees a clear line item on their next invoice showing exactly what they're being charged and why. You wrote zero proration logic.
Fixed subscription pricing works for simple products. Every user gets the same thing, pays the same price. But AI products often deliver variable value. Some users generate 100 AI responses per month. Others generate 10,000. Charging them the same price is either too expensive for light users or too cheap for heavy users.
Stripe's metered billing solves this.
You report usage events to Stripe as they happen. "User X consumed 1 AI generation." Stripe tallies usage throughout the billing period and generates an invoice at the end. Simple.
But the details matter. Usage tiers add volume discounts automatically. First 100 generations at $0.10 each. Next 1,000 at $0.05 each. After that, $0.02 each. Stripe handles the tier calculation. You just report events.
Hybrid pricing combines a base subscription with usage charges. $29/month includes 500 generations. Additional generations billed at $0.05 each. This model gives users predictable baseline costs while allowing heavy users to scale without hitting artificial limits.
The revenue recognition implications of usage-based pricing are complex. Stripe handles the accounting treatment. Usage accrued but not yet billed gets tracked separately from recognized revenue. When audit time comes, the numbers are clean and defensible.
Webhooks are the nervous system of your billing integration. Every important event in the billing lifecycle arrives as a webhook. Payment succeeded. Payment failed. Subscription renewed. Subscription cancelled. Invoice finalized. Dispute created.
Missing a webhook means your application state diverges from billing reality. A user cancels but retains access because you didn't process the cancellation webhook. A payment fails but the user keeps using the product because you didn't process the failure webhook. A dispute is filed but you don't respond because you never saw the notification.
Building reliable webhook processing requires specific patterns.
Idempotent handling is non-negotiable. Stripe may send the same webhook more than once. Your handler must produce the same result whether it processes an event once or five times. Use the event ID to deduplicate. Store processed event IDs and skip duplicates.
Signature verification prevents spoofed webhooks. Stripe signs every webhook with a secret. Your endpoint verifies the signature before processing. Without verification, anyone who guesses your webhook URL can fake billing events and manipulate your application.
Async processing keeps your endpoint responsive. Acknowledge receipt immediately with a 200 response. Process the event asynchronously. If processing fails, retry from your queue. Don't make Stripe wait while you run database migrations triggered by a plan change.
Error handling needs to be comprehensive. What happens when a webhook references a user that doesn't exist in your database? What about a subscription you've never seen? A product that was deleted? Each edge case needs a graceful response, not a crash.
Failed payments are inevitable. Credit cards expire. Bank accounts get overdrawn. Spending limits get hit. How you handle failed payments determines how much revenue you recover versus how much you lose to involuntary churn.
Stripe's Smart Retries use machine learning to determine the optimal time to retry a failed payment. Not just "try again in 3 days." The model considers the failure reason, the customer's payment history, and patterns across Stripe's entire network to pick the moment with the highest probability of success.
Dunning emails notify customers about failed payments with clear instructions for updating their payment method. Stripe hosts the payment update page. The customer clicks a link, enters new card details, and the subscription resumes. You don't build or maintain any of this UI.
The dunning sequence is configurable. How many retry attempts. How many days between them. When to mark the subscription as unpaid. When to cancel. Different businesses need different grace periods, and Stripe lets you define yours.
Revenue recovery rates with proper dunning are significant. 30-50% of failed payments get recovered through Smart Retries and dunning emails. On a subscription business with $100K MRR, that's $30-50K per year that would otherwise evaporate.
Stripe's billing automation extends into areas most developers don't think about until they need them.
Tax calculation handles sales tax, VAT, and GST automatically based on customer location. Compliance across dozens of jurisdictions without building a tax engine.
Invoicing generates professional invoices with proper line items, tax details, and payment terms. For B2B customers who need invoices for expense reporting or purchase orders, this eliminates manual invoice creation entirely.
Revenue reporting provides the metrics your finance team needs. MRR, churn rate, LTV, expansion revenue. All calculated from actual billing data. No spreadsheet gymnastics.
Stripe isn't cheap. The processing fees add up. But compare that cost to the engineering time and operational risk of building billing infrastructure from scratch. The math isn't even close.

Implement secure, user-friendly authentication with Clerk -- social login, MFA, organization management, and custom sign-up flows.

Leverage Next.js 16 features with AI integration -- server components, streaming, and the app router patterns that power modern AI applications.

Implement WebSocket communication for AI applications — streaming responses, live collaboration, and real-time data synchronization patterns.
Stop reading about AI and start building with it. Book a free discovery call and see how AI agents can accelerate your business.