Stripe Setup
Learn how to add Stripe payment processing to Better Billing
Stripe Setup
This guide shows you how to add Stripe payment processing to your existing Better Billing setup. Make sure you've completed the Getting Started guide first.
Installation
Install the Stripe SDK:
npm install stripeAdding Stripe Plugin
Update your Better Billing configuration to include the Stripe plugin:
import drizzleAdapter from "@better-billing/db/adapters/drizzle";
import { betterBilling } from "better-billing";
import { corePlugin } from "better-billing/plugins/core";
import { stripePlugin } from "better-billing/plugins/stripe";
import Stripe from "stripe";
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
import * as schema from "./schema";
// Initialize database
const connectionString = process.env.DATABASE_URL!;
const client = postgres(connectionString);
const drizzleDb = drizzle(client);
// Initialize Stripe
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: "2023-10-16",
});
// Create Better Billing instance with Stripe
const billing = betterBilling({
serverUrl: process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000",
adapter: drizzleAdapter(drizzleDb, {
provider: "pg",
schema,
}),
plugins: [
// Core plugin (required)
corePlugin({
subscriptionPlans: [
{
planName: "Free",
},
{
planName: "Pro",
trialDays: 7,
},
{
planName: "Enterprise",
trialDays: 14,
},
],
}),
// Stripe plugin for payment processing
stripePlugin({
stripe,
subscriptionPlans: {
monthly: [
{ planName: "Free", items: [{ price: "price_free_monthly" }] },
{ planName: "Pro", items: [{ price: "price_pro_monthly" }] },
{ planName: "Enterprise", items: [{ price: "price_enterprise_monthly" }] },
],
yearly: [
{ planName: "Free", items: [{ price: "price_free_yearly" }] },
{ planName: "Pro", items: [{ price: "price_pro_yearly" }] },
{ planName: "Enterprise", items: [{ price: "price_enterprise_yearly" }] },
],
},
postSuccessUrl: `${process.env.NEXT_PUBLIC_APP_URL}/dashboard?success=true`,
postCancelUrl: `${process.env.NEXT_PUBLIC_APP_URL}/pricing`,
webhookEndpointSecret: process.env.STRIPE_WEBHOOK_SECRET!,
}),
] as const,
});
export { billing };Stripe Configuration
1. Create Stripe Products & Prices
In your Stripe Dashboard:
- Go to Products → Add product
- Create products for each plan (Free, Pro, Enterprise)
- Add prices for different billing cycles (monthly/yearly)
- Copy the price IDs to use in your configuration
2. Environment Variables
Add these environment variables:
# Core
DATABASE_URL=postgresql://...
# Stripe
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
NEXT_PUBLIC_APP_URL=https://yourdomain.com3. Webhook Setup
- Go to Developers → Webhooks in your Stripe Dashboard
- Click Add endpoint
- Enter your webhook URL:
https://yourdomain.com/api/billing/stripe/webhook - Select these events or "Select all events":
customer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deletedinvoice.paidinvoice.payment_failed
- Copy the webhook signing secret to your environment variables
Using Stripe Features
Start Subscription Checkout
const checkoutSession = await billing.providers.stripe.startSubscriptionCheckout({
billableId: "user_123",
billableType: "user",
planName: "Pro",
cadence: "monthly",
email: "customer@example.com",
metadata: {
userId: "123",
source: "web_app",
},
allowPromotionCodes: true,
});
// Redirect to Stripe Checkout
window.location.href = checkoutSession.url;Subscription Management
// Cancel a subscription
await billing.providers.stripe.cancelSubscription({
subscriptionId: "sub_123",
cancelAtPeriodEnd: true, // Cancel at end of current period
});
// Update subscription plan
await billing.providers.stripe.updateSubscription({
subscriptionId: "sub_123",
planName: "Enterprise",
cadence: "yearly",
});
// Get subscription details
const subscription = await billing.providers.stripe.getSubscription({
subscriptionId: "sub_123",
});Customer Portal
Create a link to Stripe's Customer Portal for self-service:
const portalSession = await billing.providers.stripe.createPortalSession({
customerId: "cus_123",
returnUrl: `${process.env.NEXT_PUBLIC_APP_URL}/dashboard`,
});
// Redirect to Customer Portal
window.location.href = portalSession.url;API Endpoints Setup
Make sure you have API endpoints set up to handle webhooks:
// app/api/billing/[...path]/route.ts
import { billing } from "@/lib/billing";
import { toNextJsHandler } from "better-billing/integrations/next-js";
const handler = toNextJsHandler(billing.api);
export const { GET, POST, PUT, DELETE, PATCH } = handler;This creates the webhook endpoint: /api/billing/stripe/webhook
Error Handling
Always handle errors when calling Stripe methods:
try {
const checkoutSession = await billing.providers.stripe.startSubscriptionCheckout({
billableId: "user_123",
billableType: "user",
planName: "Pro",
cadence: "monthly",
email: "customer@example.com",
});
window.location.href = checkoutSession.url;
} catch (error) {
console.error("Failed to create checkout session:", error);
// Show user-friendly error message
alert("Something went wrong. Please try again.");
}Testing
Use Stripe's test mode for development:
- Use test API keys (starting with
sk_test_) - Use test webhook endpoints
- Use Stripe's test card numbers for testing
Production Checklist
Before going live:
- Switch to live API keys
- Update webhook endpoint to production URL
- Test all subscription flows
- Set up proper error handling and logging
- Configure tax collection if required
- Set up subscription lifecycle emails
Common Issues
Webhook Not Receiving Events
- Verify webhook URL is publicly accessible
- Check webhook signing secret is correct
- Ensure API endpoints are set up correctly
- Check server logs for webhook processing errors
Checkout Session Creation Fails
- Verify Stripe price IDs are correct
- Check that products are active in Stripe
- Ensure all required fields are provided
- Verify Stripe API key is valid
Next Steps
- Learn about Providers - Use all available billing methods
- Set up Framework Integration - Configure for your framework
- Explore Plugin Development - Create custom functionality