Stripe Billing
Subscriptions, checkout, webhooks, and billing portal flows in one fragment.
Install
npm install @fragno-dev/stripe @fragno-dev/dbBilling flow
A Stripe event moves through your app in minutes.
- 1
Checkout session
Stripe creates a session and redirects the customer.
- 2
Webhook event
The fragment syncs subscription state into your database.
- 3
In-app hooks
Use typed hooks to trigger upgrades, cancellations, or portals.
Checkout + billing portal
Create subscriptions, upgrades, and cancellations directly from your frontend.
Automatic webhook sync
Keep subscription state up to date with Stripe events and sync helpers.
Database-backed state
The fragment stores subscription data in your own database.
Setup blueprint
The high-signal steps, with code you can paste.
1. Install
Install the fragment and the Fragno DB package.
npm install @fragno-dev/stripe @fragno-dev/db2. Create the fragment server
Configure Stripe secrets and map customers to your users.
import { createStripeFragment } from "@fragno-dev/stripe";import { getSession } from "@/lib/auth";import { updateEntity } from "@/db/repo";export const stripeFragment = createStripeFragment({ stripeSecretKey: process.env.STRIPE_SECRET_KEY!, webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!, onStripeCustomerCreated: async (stripeCustomerId, referenceId) => { await updateEntity(referenceId, { stripeCustomerId }); }, resolveEntityFromRequest: async (context) => { const session = getSession(context.headers); return { referenceId: session.user.id, customerEmail: session.user.email, stripeCustomerId: session.user.stripeCustomerId || undefined, stripeMetadata: {}, }; },});3. Mount routes + generate schema
Expose the Stripe handlers and generate schema migrations.
import { stripeFragment } from "@/lib/stripe";export const handlers = stripeFragment.handlersFor("react-router");export const action = handlers.action;export const loader = handlers.loader;// Generate schema// npx fragno-cli db generate lib/stripe.ts --format drizzle -o db/stripe.schema.ts4. Create a client
Generate typed hooks for billing flows.
import { createStripeFragmentClient } from "@fragno-dev/stripe/react";export const stripeClient = createStripeFragmentClient();Use it
Hook into upgrade/cancel flows directly in your UI.
import { stripeClient } from "@/lib/stripe-client";export function SubscribeButton() { const { mutate, loading } = stripeClient.upgradeSubscription(); const handleSubscribe = async () => { const { url, redirect } = await mutate({ body: { priceId: "price_123", successUrl: `${window.location.origin}/success`, cancelUrl: window.location.href, }, }); if (redirect) { window.location.href = url; } }; return ( <button onClick={handleSubscribe} disabled={loading}> {loading ? "Loading..." : "Subscribe"} </button> );}Query subscriptions
Access subscription data on the server for dashboards or admin flows.
import { stripeFragment } from "@/lib/stripe";const subscriptions = await stripeFragment.services.getSubscriptionsByReferenceId(user.id);const subscription = await stripeFragment.services.getSubscriptionById(subscriptionId);const byCustomer = await stripeFragment.services.getSubscriptionsByStripeCustomerId(customerId);We fix split brain
Keep Stripe and your database in sync
Split brain happens when Stripe is the source of truth but your app depends on a local copy of subscription state. The fragment ships the webhook handlers, schema, and client hooks together so you stay synchronized without re-implementing the hard parts.
Events flow from Stripe → fragment handlers → your database. Your UI reads from a single, consistent source instead of juggling webhook races.