Aller au contenu principal

Stripe Setup — Complete Guide

Configure Stripe payment processing, subscriptions, and billing portal for HeartCo.


Overview

HeartCo integrates Stripe for:

  • Subscriptions (monthly/annual billing)
  • One-time charges (usage-based, add-ons)
  • Customer Portal (self-service billing)
  • Webhooks (payment events, subscription updates)

1. Create Products & Prices

Step 1: Create Subscription Products

In Stripe Dashboard:

  1. Click New Product
  2. Enter Product Name: HeartCo Starter
  3. Choose Recurring → Billing Period: Monthly
  4. Enter Price: 29.00 USD
  5. Click Create

Repeat for all plans:

PlanMonthlyAnnualBilling Period
Starter$29$290Monthly + Annual
Pro$79$790Monthly + Annual
Business$199$1990Monthly + Annual
EnterpriseCustomCustomCustom

Step 2: Add Annual Price to Monthly Products

For each product (e.g., "HeartCo Starter"):

  1. Open product → Pricing tab
  2. Click Add price
  3. Set Billing period: Annually
  4. Enter Price: 290.00 USD
  5. Click Create

Step 3: Copy Price IDs

For each plan, note the Price ID:

// Example format: price_1234567890abcdef

Starter Monthly:  price_1234567890111111
Starter Annual:   price_1234567890111112
Pro Monthly:      price_1234567890222221
Pro Annual:       price_1234567890222222

Update .env.local:

NEXT_PUBLIC_STRIPE_PRICE_STARTER_MONTHLY=price_1234567890111111
NEXT_PUBLIC_STRIPE_PRICE_STARTER_ANNUAL=price_1234567890111112
NEXT_PUBLIC_STRIPE_PRICE_PRO_MONTHLY=price_1234567890222221
NEXT_PUBLIC_STRIPE_PRICE_PRO_ANNUAL=price_1234567890222222
# ... continue for all plans

2. Configure Webhooks

Step 1: Create Webhook Endpoint

In Stripe Dashboard Developers > Webhooks:

  1. Click Add endpoint

  2. Endpoint URL: https://yourdomain.com/api/stripe/webhook

    • (Use http://localhost:3000/api/stripe/webhook for local testing)
  3. Events to send:

    • checkout.session.completed — subscription started
    • invoice.paid — recurring payment succeeded
    • invoice.payment_failed — payment declined/failed
    • customer.subscription.updated — plan/billing changed
    • customer.subscription.deleted — subscription cancelled
  4. Click Create

Step 2: Get Webhook Secret

After creating the endpoint:

  1. Click the endpoint to view details
  2. Copy Signing secret (starts with whsec_)
  3. Add to Vercel environment:
STRIPE_WEBHOOK_SECRET=whsec_1234567890...

Step 3: Test Webhook Locally (Optional)

Install Stripe CLI:

# macOS
brew install stripe/stripe-cli/stripe
 
# Or download from https://stripe.com/docs/stripe-cli

Start webhook listener:

stripe listen --forward-to http://localhost:3000/api/stripe/webhook

Copy the signing secret and add to .env.local:

STRIPE_WEBHOOK_SECRET=whsec_test_...

3. Get API Keys

Step 1: Retrieve Keys

In Stripe Dashboard Settings > API keys:

  1. Copy Secret key (starts with sk_live_ or sk_test_)
  2. Copy Publishable key (starts with pk_live_ or pk_test_)

Step 2: Add to Environment

# Server-side (keep secret)
STRIPE_SECRET_KEY=sk_live_1234567890...
 
# Client-side (public)
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_1234567890...

Step 3: Test vs. Live

During development:

  • Use Test keys (dashboard shows "Viewing test data")
  • Use test cards: 4242 4242 4242 4242 (success), 4000 0000 0000 0002 (decline)

Before launch:

  • Switch to Live keys in Stripe Dashboard
  • Update .env.production in Vercel

4. Configure Customer Portal

Step 1: Enable Self-Service

In Stripe Dashboard Settings > Billing portal settings:

  1. Click Customize
  2. Under Features, enable:
    • Switch plans — customers can upgrade/downgrade
    • Cancel subscriptions — customers can cancel anytime
    • Update payment methods — add/change credit cards
    • Download invoices — access invoice history

Step 2: Set Return URL

  1. Allowed return URLs: Add https://yourdomain.com/dashboard/billing

  2. Click Save

Users can now access the portal via:

https://billing.stripe.com/b/login/{YOUR_CUSTOMER_ID}

Or by implementing a server action:

// src/app/api/stripe/portal/route.ts
import { stripe } from '~/lib/stripe';
 
export async function POST(req: Request) {
  const { customerId } = await req.json();
 
  const session = await stripe.billingPortal.sessions.create({
    customer: customerId,
    return_url: 'https://yourdomain.com/dashboard/billing',
  });
 
  return Response.json({ url: session.url });
}

5. Test Stripe Integration

Test Card Numbers

Use these in test mode (sk_test_...):

CardResultCVCDate
4242 4242 4242 4242SuccessAnyAny future date
4000 0000 0000 0002DeclineAnyAny future date
4000 0025 0000 31553D Secure requiredAnyAny future date
5555 5555 5555 4444Success (Mastercard)AnyAny future date

Simulate Payment Events

Trigger webhooks manually in Stripe Dashboard:

  1. Developers > Webhooks → click endpoint
  2. Sending events
  3. Select event type (e.g., checkout.session.completed)
  4. Click Send test webhook

Monitor webhook delivery under Event log.

End-to-End Test Flow

  1. Start dev server: pnpm dev
  2. Go to pricing page /pricing
  3. Click "Get Started" on Starter plan
  4. Stripe Checkout opens → enter test card
  5. Complete checkout → redirect to success page
  6. Check Stripe Dashboard → new subscription created
  7. Check database → subscription record created
  8. Customer can access billing portal

6. Production Checklist

Before deploying to production:

  • Switch Stripe Dashboard to Live mode (top right toggle)
  • Update .env.production in Vercel with Live keys
  • Update webhook endpoint URL to production domain
  • Re-create webhook with production signing secret
  • Test full payment flow with real card ($1 test charge, auto-refunded)
  • Verify webhook delivery in Stripe Dashboard
  • Configure email notifications for failed payments
  • Set up dunning rules (automated retry for failed payments)
  • Review billing portal settings
  • Test subscription cancellation and reactivation

7. Advanced Configuration

Dunning & Failed Payments

Enable automatic retry for failed payments:

  1. Settings > Billing settings > Dunning
  2. Configure retry schedule:
    • Day 1, 3, 5, 7 (default)
  3. Email templates for soft declines

Usage-Based Billing

For add-on charges (e.g., API overage):

// src/server/api/routers/billing.ts
const { stripe } = require('~/lib/stripe');
 
export const billingRouter = createTRPCRouter({
  reportUsage: staffProcedure
    .input(z.object({ subscriptionItemId: z.string(), quantity: z.number() }))
    .mutation(async ({ input }) => {
      await stripe.subscriptionItems.createUsageRecord(
        input.subscriptionItemId,
        { quantity: input.quantity }
      );
      return { success: true };
    }),
});

Tax Calculation (Optional)

Enable Stripe Tax for automatic tax calculation:

  1. Settings > Tax → Enable
  2. Configure tax rates by jurisdiction
  3. Stripe automatically calculates tax on checkout

Troubleshooting

Webhook Not Firing

  1. Check webhook endpoint URL is accessible (not localhost)
  2. Verify STRIPE_WEBHOOK_SECRET matches Stripe Dashboard
  3. Check webhook Event log in Stripe Dashboard for errors
  4. Test with Stripe CLI locally first

Checkout Redirect Issues

  1. Verify success/cancel URLs in checkout session creation
  2. Check Stripe API keys are correct
  3. Ensure domain is not restricted in Stripe settings

Customer Not Found Error

  1. Verify customer is created in Stripe before creating subscription
  2. Check customerId matches Stripe customer ID (not local ID)
  3. Sync customers from Stripe if out of sync

Payment Method Declined

  1. Test with valid test cards (see Step 5)
  2. Check 3D Secure flow (some cards require additional authentication)
  3. Verify card expiration and CVC in test

Support