Stripe

A full-stack library for Stripe subscriptions.

Solving split brain integrations with a full-stack library.

Install

npm install @fragno-dev/stripe @fragno-dev/db

Mount route

/api/stripe

Includes

Webhooks + state

Best for

Subscriptions

Flow

A billing event should move cleanly through your stack.

Billing flow

  1. 1

    Checkout session

    Stripe creates a session and redirects the customer.

  2. 2

    Webhook event

    The fragment syncs subscription state into your database.

  3. 3

    In-app hooks

    Use typed hooks to trigger upgrades, cancellations, or portals.

BackendStripe Fragment Server/api/stripe/upgrade/api/stripe/webhook
FrontendStripe Fragment ClientupgradeSubscription()cancelSubscription()
Stripe API
subscriptionsDBWebhookEventsAPIDeveloperStripe FragmentStripe

Capabilities

The common billing seams are already packaged.

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.

Blueprint

Configure the server once, then expose typed billing actions.

1. Install

Install the fragment and the Fragno DB package.

npm install @fragno-dev/stripe @fragno-dev/db

2. 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.ts

4. Create a client

Generate typed hooks for billing flows.

import { createStripeFragmentClient } from "@fragno-dev/stripe/react";export const stripeClient = createStripeFragmentClient();

Use it

Billing logic stays typed on both sides of the boundary.

Frontend action

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>  );}

Server services

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);