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 .env file

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 bureaucratic process of getting a real account later on.

Step 1: Get Your API Keys

Test Mode Keys (Development)

  1. Log in to your Stripe Dashboard
  2. Ensure you're in Test mode or you can use the newer Sandbox
  3. Copy your Secret key (starts with sk_test_)
Stripe Sandbox

Add to your .env.local file:

STRIPE_SECRET_KEY=sk_test_51O...your_test_key_here

This is a testing key and won't be used in production.

Step 2: Create Products and Prices

Using Stripe Dashboard

Stripe Add Product

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:

  1. Navigate to ProductsAdd product
  2. Create a product for each tier (e.g., "Starter", "Pro", "Enterprise")
  3. For each product, add prices:
    • Set billing period (monthly/yearly)
    • Set amount
    • Enable "Recurring" billing
  4. 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)
Stripe Products

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,
},
],
},
],
};

Allow switching between plans in the Customer Portal

In the Stripe Customer Portal, you can allow users to switch between plans. To do this, you need to configure the Customer Portal in the Stripe Dashboard.

For more information, please see the Stripe Billing Portal documentation.

Per-Organization Customers (B2B Billing)

When using Stripe, Makerkit supports true per-organization billing:

  • Users have user.customerId (personal billing)
  • Organizations have organization.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:listen

Note: 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/webhook

The 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.

Stripe CLI Login

Once you're logged in, please run the command again:

pnpm --filter web run stripe:listen

The 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:

  1. In Stripe Dashboard, navigate to DevelopersWebhooks
  2. Click Add endpoint
  3. Set endpoint URL: https://yourdomain.com/api/auth/stripe/webhook
  4. Select events to listen for at least the following:
    • checkout.session.completed
    • customer.subscription.created
    • customer.subscription.updated
    • customer.subscription.deleted
  5. More webhooks events may be required depending on your needs (eg. if you need to handle other events)
  6. Copy the webhook signing secret
  7. Add to production environment variables (always prefer using the hosting provider's environment variables editor)
STRIPE_WEBHOOK_SECRET=whsec_...production_secret

Step 5: Test the Integration

Test Checkout

Use Stripe test cards:

Card NumberDescription
4242 4242 4242 4242Successful payment
4000 0025 0000 3155Requires 3D Secure authentication
4000 0000 0000 9995Payment declined

Test Trial Period

  1. Create a subscription with trial enabled
  2. Verify trial end date in Stripe Dashboard
  3. Check that status: 'trialing' appears in your app

Environment Variables Reference

Complete .env configuration for Stripe:

# Stripe API Keys
STRIPE_SECRET_KEY=sk_test_... # or sk_live_... for production
# Webhook Secret
STRIPE_WEBHOOK_SECRET=whsec_...
# Optional Stripe behavior toggles (defaults shown)
CREATE_CUSTOMER_ON_SIGN_UP=true
STRIPE_ENABLE_TRIAL_WITHOUT_CC=false
ENABLE_AUTOMATIC_TAX_CALCULATION=false
ENABLE_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 domain

Security 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