Stripe Setup
Configure your Stripe account, API keys, and webhooks
This guide walks you through setting up Stripe for your SaaS application.
Prerequisites
- A Stripe account (sign up at stripe.com)
- Access to your project's
.envfile
Please make sure to have signed up for a Stripe account before you continue with this guide.
It's totally okay if it's not a real account - you can use the Sandbox mode keys for development as you build your application. You can deal with the bureacratic process of getting a real account later on.
Step 1: Get Your API Keys
Test Mode Keys (Development)
- Log in to your Stripe Dashboard
- Ensure you're in Test mode or you can use the newer Sandbox
- Copy your Secret key (starts with
sk_test_)

Add to your .env.local file:
STRIPE_SECRET_KEY=sk_test_51O...your_test_key_hereThis is a testing key and won't be used in production.
Step 2: Create Products and Prices
Using Stripe Dashboard

To create products and prices, you can use the Stripe Dashboard. Let's walk you through creating a Starter and Pro product.
For both products, you can create a monthly and yearly price. For the Starter product, you can create a $9.99 and $99.99 price. For the Pro product, you can create a $19.99 and $199.99 price.
Here's going to be the steps:
- Navigate to Products → Add product
- Create a product for each tier (e.g., "Starter", "Pro", "Enterprise")
- For each product, add prices:
- Set billing period (monthly/yearly)
- Set amount
- Enable "Recurring" billing
- Copy each Price ID (starts with
price_)
Example Product Setup
Below are some example product setups for a Starter and Pro product:
Starter Product
- Monthly Price ($9.99/month)
- Yearly Price ($99.99/year)
Pro Product
- Monthly Price ($19.99/month)
- Yearly Price ($199.99/year)

Configure Price IDs
Once created the products (Starter and Pro), you can copy the Price IDs.
Add your Price IDs to .env.development or .env.local (as these are used exclusively for development):
STRIPE_PRICE_STARTER_MONTHLY=price_...STRIPE_PRICE_STARTER_YEARLY=price_...STRIPE_PRICE_PRO_MONTHLY=price_...STRIPE_PRICE_PRO_YEARLY=price_...Note: the names of the environment variables are up to you. The key is that planId must be the correct identifier for the active provider. Please make sure you're using the Price ID and not the Product ID (can easily be identified by the prefix price_ and prod_ respectively). This is a common mistake when configuring the billing configuration.
Step 3: Configure Billing Plans
Update your billing configuration to include the details of your products:
export const billingConfig: BillingConfig = { products: [ { id: 'starter', name: 'Starter', description: 'For individuals and small teams', currency: 'USD', features: ['Core features', 'Email support'], plans: [ { name: 'starter-monthly', planId: process.env.STRIPE_PRICE_STARTER_MONTHLY!, displayName: 'Starter Monthly', interval: 'month', cost: 9.99, }, { name: 'starter-yearly', planId: process.env.STRIPE_PRICE_STARTER_YEARLY!, displayName: 'Starter Yearly', interval: 'year', cost: 99.99, }, ], }, ],};Per-Organization Customers (B2B Billing)
When using Stripe, Makerkit supports true per-organization billing:
- Users have
users.stripeCustomerId(personal billing) - Organizations have
organizations.stripeCustomerId(org billing)
Stripe customers are created and persisted automatically by the Better Auth Stripe plugin:
- The org Stripe customer is created on the first organization checkout/upgrade
- Organization name changes are synced to Stripe’s customer record
- Organizations with active subscriptions are protected from deletion
If you want to customize customer creation metadata or logging, see packages/billing/stripe/src/stripe-plugin.ts.
Step 4: Configure Webhooks
Webhooks are essential for receiving real-time subscription events from Stripe.
Local Development
We provide a Docker command that will start the Stripe CLI and forward webhooks to your local server.
Please run it using the following command:
pnpm --filter web run stripe:listenNote: if you don't want to use Docker, you can install the Stripe CLI globally on your machine and run the following command instead:
stripe listen --forward-to http://localhost:3000/api/auth/stripe/webhookThe first time you run this command, it will ask you to login to your Stripe account. Follow the instructions on the screen to do so.

Once you're logged in, please run the command again:
pnpm --filter web run stripe:listenThe command will output the webhook signing secret. Please copy the webhook signing secret (starts with whsec_) and add to .env.development or .env.local:
STRIPE_WEBHOOK_SECRET=whsec_...Note: STRIPE_WEBHOOK_SECRET is effectively required for both development and production webhook processing. Without it, webhook signature verification cannot be performed and you will not be able to receive events from Stripe.
If you are testing against a remote server, then you need to set up a destination URL for the webhook from the Stripe Sandbox, effectively just like you would do for a production webhook. This is the URL that Stripe will send the webhook to. The guide below will walk you through how to do this.
Production Webhooks
When going to production, you will need to configure the webhook endpoint in the Stripe Dashboard:
- In Stripe Dashboard, navigate to Developers → Webhooks
- Click Add endpoint
- Set endpoint URL:
https://yourdomain.com/api/auth/stripe/webhook - Select events to listen for at least the following:
checkout.session.completedcustomer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deleted
- More webhooks events may be required depending on your needs (eg. if you need to handle othr events)
- Copy the webhook signing secret
- Add to production environment variables (always prefer using the hosting provider's environment variables editor)
STRIPE_WEBHOOK_SECRET=whsec_...production_secretStep 5: Test the Integration
Test Checkout
Use Stripe test cards:
| Card Number | Description |
|---|---|
4242 4242 4242 4242 | Successful payment |
4000 0025 0000 3155 | Requires 3D Secure authentication |
4000 0000 0000 9995 | Payment declined |
Test Trial Period
- Create a subscription with trial enabled
- Verify trial end date in Stripe Dashboard
- Check that
status: 'trialing'appears in your app
Environment Variables Reference
Complete .env configuration for Stripe:
# Stripe API KeysSTRIPE_SECRET_KEY=sk_test_... # or sk_live_... for production# Webhook SecretSTRIPE_WEBHOOK_SECRET=whsec_...# Optional Stripe behavior toggles (defaults shown)CREATE_CUSTOMER_ON_SIGN_UP=trueSTRIPE_ENABLE_TRIAL_WITHOUT_CC=falseENABLE_AUTOMATIC_TAX_CALCULATION=falseENABLE_TAX_ID_COLLECTION=false# Price IDs (from Stripe Dashboard) (names are up to you)STRIPE_PRICE_STARTER_MONTHLY=price_...STRIPE_PRICE_STARTER_YEARLY=price_...STRIPE_PRICE_PRO_MONTHLY=price_...STRIPE_PRICE_PRO_YEARLY=price_...STRIPE_PRICE_ENTERPRISE_MONTHLY=price_...STRIPE_PRICE_ENTERPRISE_YEARLY=price_...# Application URL (for redirects)NEXT_PUBLIC_SITE_URL=http://localhost:3000 # or your production domainSecurity Best Practices
API Key Security
- Never commit API keys to version control
- Use different keys for test/production
- Rotate keys periodically
- Restrict API key permissions in Stripe Dashboard
Webhook Security
The plugin automatically verifies webhook signatures:
stripe({ stripeClient, stripeWebhookSecret: STRIPE_WEBHOOK_SECRET, // Webhooks are verified before processing});Production Checklist
Before going live:
- [ ] Replace test API keys with live keys
- [ ] Configure production webhook endpoint
- [ ] Test all webhook events in production
- [ ] Set up billing email notifications
- [ ] Configure Customer Portal settings in Stripe Dashboard
- [ ] Review Stripe Radar rules for fraud prevention
Troubleshooting
Webhook Signature Verification Failed
Check that your STRIPE_WEBHOOK_SECRET matches the endpoint in Stripe Dashboard.
Price ID Not Found
Verify Price IDs exist in Stripe Dashboard and match your billing configuration.
Next Steps
- Configure checkout flow
- Implement subscription management
- Set up lifecycle hooks for subscription events