Skip to content

Stripe Integration

Terminal window
pnpm add @smta/billing stripe

Instantiate StripeProvider with your Stripe secret key. The constructor takes a single string argument.

import { StripeProvider } from '@smta/billing/stripe';
const billing = new StripeProvider(process.env.STRIPE_SECRET_KEY!);
VariableDescription
STRIPE_SECRET_KEYYour Stripe secret API key (sk_live_… or sk_test_…)
STRIPE_WEBHOOK_SECRETThe signing secret from your Stripe webhook endpoint (whsec_…)

Call createCheckout with the organization and plan details. Stripe embeds organizationId into the subscription metadata automatically, so it will be available in every future webhook event.

const { checkoutUrl, sessionId } = await billing.createCheckout({
organizationId: org.id,
planId: 'pro',
priceId: 'price_1234567890',
billingEmail: user.email,
successUrl: 'https://app.example.com/billing/success',
cancelUrl: 'https://app.example.com/billing/cancel',
});
// Redirect the user to checkoutUrl
import express from 'express';
const app = express();
// Use raw body parser for the webhook route only
app.post(
'/webhooks/stripe',
express.raw({ type: 'application/json' }),
async (req, res) => {
const signature = req.headers['stripe-signature'] as string;
let parsed;
try {
parsed = await billing.handleWebhook({
provider: 'stripe',
rawBody: req.body, // Buffer — not parsed JSON
signature,
});
} catch (err) {
// Signature verification failed or unrecognized event
console.error('Stripe webhook error:', err);
return res.status(400).send('Webhook error');
}
await billing.recordSubscriptionUpdate(parsed, db);
res.json({ received: true });
}
);

The StripeProvider processes the following Stripe event types:

Stripe EventEffect
customer.subscription.updatedUpdates plan, status, and period end in platform.billing_subscriptions
customer.subscription.deletedMarks the subscription as canceled

The provider reads sub.metadata.organization_id on the Stripe subscription object to associate the event with the correct organization. This value is set automatically by createCheckout.

  • platform.billing_customers — org-to-Stripe-customer mapping
  • platform.billing_subscriptions — subscription status per org

Fetch current subscription state:

const subscription = await billing.getSubscription(providerSubscriptionId);
// { plan, status, currentPeriodEnd, cancelAtPeriodEnd, ... }

Cancel a subscription at period end:

await billing.cancelSubscription(providerSubscriptionId);