Billing seems simple until you try to build it. Proration, upgrades, downgrades, failed payments, invoices, taxes—the complexity is endless. Use a payment platform and focus on your product instead.
A complete billing system has several layers:
Payment processor: Actually moves money (Stripe, Paddle)
Subscription management: Handles recurring billing, upgrades, downgrades
Invoicing: Generates and sends invoices
Tax compliance: Calculates and remits sales tax/VAT
Dunning: Handles failed payments and recovery
Modern platforms like Stripe handle all of these.
Recommended Setup: Stripe
For most startups, Stripe is the right choice:
•
Excellent developer experience
•
Handles subscriptions natively
•
Tax calculation available
1.
Create Stripe account at stripe.com
2.
Define your products in Stripe Dashboard
3.
Create prices for each product (monthly, annual)
4.
Integrate Stripe Checkout for payment flow
5.
Set up webhooks to handle events
Product: Your software (e.g., “Acme Pro”)
Prices: Specific pricing variations
•
Annual: $290/year (2 months free)
// Create a Checkout Session
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
payment_method_types: ['card'],
line_items: [{
price: 'price_monthly_plan_id',
quantity: 1,
}],
success_url: 'https://yoursite.com/success',
cancel_url: 'https://yoursite.com/cancel',
});
Stripe notifies you of events via webhooks:
// Common events to handle
switch (event.type) {
case 'checkout.session.completed':
// Provision access
break;
case 'customer.subscription.updated':
// Handle plan changes
break;
case 'customer.subscription.deleted':
// Remove access
break;
case 'invoice.payment_failed':
// Handle failed payment
break;
}
Alternative: Paddle/Lemon Squeezy
If you want simpler tax handling, consider Paddle or Lemon Squeezy:
Why Consider Alternatives
Stripe: You’re the merchant of record. You handle tax compliance.
Paddle/Lemon Squeezy: They’re the merchant of record. They handle tax compliance.
Merchant of Record Explained
When Paddle is merchant of record:
•
Paddle sells to the customer (legally)
•
Paddle handles sales tax, VAT, etc.
•
Paddle remits to tax authorities
•
You receive revenue minus fees and taxes
This simplifies international sales significantly.
For simple products selling globally, Paddle/Lemon Squeezy can be worth the higher fees.
•
Upgrades: Prorate and charge difference
•
Downgrades: Apply at end of billing period
•
Immediate vs. end of period
•
Automatic renewal (default)
•
Manual renewal (rarely used)
•
Duration (7, 14, 30 days)
•
Feature access during trial
•
Automatic conversion to paid
•
Cancellation before charge
1.
Retry logic: Stripe retries automatically over several days
2.
Customer notification: Inform customer of failure
3.
Update payment method: Provide easy way to update card
4.
Grace period: Give time before canceling access
5.
Cancellation: Eventually cancel if not resolved
This process is called “dunning” and Stripe handles it automatically with Smart Retries.
•
Stripe generates invoices automatically
•
Available in customer portal
•
May need Net-30 or Net-60 terms
•
Consider Stripe Invoicing or dedicated tools
If billing based on usage:
// Report usage to Stripe
await stripe.subscriptionItems.createUsageRecord(
subscriptionItemId,
{
quantity: 1000, // API calls, messages, etc.
timestamp: Math.floor(Date.now() / 1000),
action: 'increment',
}
);
Stripe calculates charges based on reported usage.
Stripe offers a hosted customer portal where users can:
•
View and download invoices
•
Manage subscription (upgrade, downgrade, cancel)
This saves significant development time.
US: Sales tax varies by state. Nexus rules determine where you must collect.
EU: VAT required on B2C sales. VAT rates vary by country.
Global: Complex patchwork of rules.
•
Stripe Tax: Automatic calculation and collection
•
Paddle/Lemon Squeezy: Handle as merchant of record
•
Manual: Track and remit yourself (not recommended)
•
Low revenue + simple geography: Handle manually
•
Scaling internationally: Use Stripe Tax or merchant of record
•
Enterprise: Get tax advice
•
[ ] Choose payment platform (Stripe recommended)
•
[ ] Define products and prices
•
[ ] Implement checkout flow
•
[ ] Set up webhook handlers
•
[ ] Handle failed payments (dunning)
•
[ ] Enable customer portal
•
[ ] Consider tax automation
•
[ ] Test everything in test mode
•
[ ] Set up production environment
Building billing yourself: Always a mistake. The complexity is endless.
Not handling webhooks properly: Missing events leads to access issues.
No dunning process: Lost revenue from easily recoverable payments.
Ignoring tax: Catches up with you eventually.
Hard-coded prices: Use Stripe’s price IDs so you can change without deploys.
No customer self-serve: Every billing change requiring support is overhead.
•
Use Stripe for most startups—don’t build billing yourself
•
Consider Paddle/Lemon Squeezy if tax compliance is a burden
•
Handle webhooks for all important events
•
Implement dunning for failed payment recovery
•
Enable Stripe’s customer portal for self-service
•
Automate tax calculation (Stripe Tax or MoR model)
•
Test thoroughly in test mode before going live