TurboKit
Backend

Stripe Integration

Payment processing with Stripe

Stripe Integration

TurboKit includes Stripe integration for payment processing, subscriptions, and customer portal.

Setup

Environment Variables

STRIPE_SECRET_KEY=sk_test_your_secret_key
STRIPE_PUBLISHABLE_KEY=pk_test_your_publishable_key
STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret

Webhook Setup

# Install Stripe CLI
brew install stripe/stripe-cli/stripe

# Login to Stripe
stripe login

# Forward webhooks to your local server
stripe listen --forward-to localhost:4101/webhooks/stripe

Copy the webhook secret (starts with whsec_) and add it to your .env file.

API Endpoints

Products

EndpointMethodDescription
/stripe/productsGETList all products
/stripe/products/:idGETGet single product
/stripe/productsPOSTCreate new product
/stripe/products/:idPUTUpdate product
/stripe/products/:idDELETEDelete product

Checkout

EndpointMethodDescription
/stripe/checkout-sessionPOSTCreate checkout session
/stripe/customer-portalPOSTCreate customer portal

Webhooks

EndpointMethodDescription
/webhooks/stripePOSTHandle Stripe webhook

Checkout Flow

1. Create Checkout Session

// Frontend
const createCheckout = async (priceId: string) => {
  const response = await fetch("/stripe/checkout-session", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      priceId,
      successUrl: `${window.location.origin}/success`,
      cancelUrl: `${window.location.origin}/pricing`,
    }),
  });

  const data = await response.json();

  if (data.success && data.data.url) {
    window.location.href = data.data.url;
  }
};

2. Handle Webhook Events

export async function handleStripeWebhook(request: Request) {
  const signature = request.headers.get("stripe-signature");
  const event = stripe.webhooks.constructEvent(
    await request.text(),
    signature,
    process.env.STRIPE_WEBHOOK_SECRET
  );

  switch (event.type) {
    case "checkout.session.completed":
      await handleCheckoutComplete(event.data.object);
      break;
    case "customer.subscription.updated":
      await handleSubscriptionUpdate(event.data.object);
      break;
    case "customer.subscription.deleted":
      await handleSubscriptionCancel(event.data.object);
      break;
  }
}

Customer Portal

Allow customers to manage their subscriptions:

const openCustomerPortal = async () => {
  const response = await fetch("/stripe/customer-portal", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      returnUrl: window.location.href,
    }),
  });

  const data = await response.json();

  if (data.success && data.data.url) {
    window.location.href = data.data.url;
  }
};

Test Cards

Use these test cards in development:

Card NumberDescription
4242 4242 4242 4242Successful payment
4000 0000 0000 9995Declined payment
4000 0025 0000 31553D Secure required

Use any future expiry date and any 3-digit CVC.

Best Practices

  1. Verify Webhooks: Always verify webhook signatures
  2. Idempotency: Handle duplicate webhook events gracefully
  3. Error Handling: Log all Stripe API errors
  4. Metadata: Use metadata to link Stripe objects to your database
  5. Customer Portal: Let users manage their own subscriptions
  6. Test Thoroughly: Test all payment flows in test mode first

Warning: Never expose your secret key (sk_test_ or sk_live_) in client-side code.

On this page