Quick Start

Install and configure the Stripe fragment

The Stripe fragment is new and under active development.

Overview

The Fragno Stripe fragment makes it easy to manage Stripe subscriptions in your application. This guide will walk you through installation and basic setup to get your first subscription working.

Prerequisites

Before you begin, make sure you have:

  • Stripe API Key
  • Stripe Webhook Secret
  • A Stripe Product & Price: Create at least one product with a price in your Stripe Dashboard

Installation

Install the Stripe fragment and Fragno db package:

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

Server Setup

1. Create the Fragment Instance

Create a file to instantiate your Stripe fragment (e.g., lib/stripe.ts or server/stripe.ts):

lib/stripe.ts
import { createStripeFragment } from "@fragno-dev/stripe";
/* These imports are illustrative and application specific */
import { databaseAdapter } from "@/db/fragno";
import { getSession } from "@/lib/auth";

export const stripeFragment = createStripeFragment(
  {
    stripeSecretKey: process.env.STRIPE_SECRET_KEY,
    webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,

    // Resolves the authenticated entity from the request
    resolveEntityFromRequest: async (context) => {
      // Get session using your authentication system
      const session = await getSession(context.headers);
      if (!session) {
        return null; // The fragment responds with 401 UNAUTHENTICATED
      }

      return {
        // Your application's identifier for the entity which owns the subscription
        referenceId: session.user.id,
        // Set on the Stripe customer the fragment creates for this entity
        customerEmail: session.user.email,
        customerMetadata: {},
      };
    },

    // Runs after a subscription changed, e.g. to grant or revoke access
    onSubscriptionChanged: async ({ subscription }) => {
      console.log(subscription.referenceId, subscription.status);
    },
  },
  { databaseAdapter },
);

The fragment creates a Stripe customer for an entity on its first checkout and remembers the link itself, so your application does not need to store Stripe IDs. See Subscriptions for all callbacks.

What is referenceId? This is your application's identifier for the entity that owns the subscription (typically a user ID or organization ID). The fragment uses this to link Stripe data back to your application's data model.

2. Mount the Fragment Routes

The fragment provides HTTP routes that need to be mounted in your application. How you do this depends on your framework. See Frameworks supported frameworks.

app/api/stripe/[...all]/route.ts
import { stripeFragment } from "@/lib/stripe";

export const { GET, POST, PUT, PATCH, DELETE } = stripeFragment.handlersFor("next-js");

3. Run Database Migrations

The fragment adds customer and subscription tables to your database. Generate the schema for these tables using the fragno-cli. For setting up Fragno DB schemas, see the Fragno DB Quickstart.

# Generate schema file
npx fragno-cli db generate lib/stripe.ts --format drizzle -o db/stripe.schema.ts

4. Run a Hook Processor

The webhook route only records each Stripe event; durable hooks process it afterwards and retry when Stripe or your callbacks fail. Run a hook processor so they run:

lib/stripe.ts
import { createDurableHooksProcessor } from "@fragno-dev/db/dispatchers/node";

createDurableHooksProcessor([stripeFragment], { pollIntervalMs: 2000 }).startPolling();

Creating a subscription

Create a client instance to use the fragment in your frontend:

lib/stripe-client.ts
import { createStripeFragmentClient } from "@fragno-dev/stripe/react";

export const stripeClient = createStripeFragmentClient();

This client exposes several mutators for managing subscriptions:

  • upgradeSubscription: create or update a subscription
  • cancelSubscription: cancel a subscription
  • useBillingPortal: redirect to Stripe's billing portal for managing payment methods and billing details
components/subscribe.tsx
import { stripeClient } from "@/lib/stripe-client";

export function SubscribeButton() {
  const { mutate, loading, error } = stripeClient.upgradeSubscription();

  const handleSubscribe = async () => {
    const { url } = await mutate({
      body: {
        priceId: "price_1234567890", // The Stripe price ID being subscribed to
        successUrl: `${window.location.origin}/success`,
        cancelUrl: window.location.href,
      },
    });

    window.location.href = url; // Redirect to Stripe Checkout
  };

  return (
    <button onClick={handleSubscribe} disabled={loading}>
      {loading ? "Loading..." : "Subscribe"}
    </button>
  );
}