Stripe Integration
Add checkout, portal sessions, billing status, webhooks, product catalogs, metering, and storage-backed billing snapshots.
Install from the CLI
farm add integration stripe --ui
Config-first setup
src/lib/integrations.ts
import { stripe } from "@farmjs/integrations/stripe";export const integrations = { billing: stripe({ secretKey: process.env.STRIPE_SECRET_KEY, webhookSecret: process.env.STRIPE_WEBHOOK_SECRET, products: [ { id: "pro", name: "Pro", prices: [{ interval: "month", amount: 2900, currency: "usd" }], }, ], }),};
Usage
const checkout = await apiClient.billing.checkout.post({ body: { productId: "pro", successPath: "/success", cancelPath: "/pricing", },});if (checkout.data?.redirectTo) { window.location.href = checkout.data.redirectTo;}
Storage-aware billing
The Stripe integration can use Farm's integration ORM layer through ctx.args.db, so billing snapshot reads and writes can work across Prisma, Drizzle, SQLite SQL, and other farming-labs/orm compatible clients.
What Stripe adds
| Area | Details |
|---|---|
| Catalog | Public product and price metadata for pricing pages. |
| Checkout | A typed checkout route that can return JSON for callers or redirect for browser navigation. |
| Customer portal | A typed route for opening Stripe's billing portal. |
| Billing status | Subscription, trial, seats, cancellation, and plan state. |
| Entitlements | Feature, limit, usage, meter, and billing checks. |
| Webhooks | Event verification and snapshot updates for checkout and subscription events. |
| Storage | Schema-backed billing account snapshots through ctx.args.db. |
Common callers
const products = await api.billing.products.get();
const status = await api.billing.status.get();if (status.data?.status === "active") { console.log(status.data.planId);}
const portal = await api.billing.portal.post({ body: { returnTo: "/settings/billing", },});if (portal.data?.redirectTo) { window.location.href = portal.data.redirectTo;}
Billing owner
Production billing usually needs an owner resolver. That resolver decides whether the billing account belongs to a user, organization, workspace, or team.
stripe({ secretKey: process.env.STRIPE_SECRET_KEY, webhookSecret: process.env.STRIPE_WEBHOOK_SECRET, billing: { async resolveOwner(ctx) { const userId = ctx.req.get<string>("user.id"); if (!userId) { return null; } return { id: userId, kind: "user", email: ctx.req.get<string>("user.email") ?? null, }; }, },});
The same ctx includes ctx.args.db, ctx.data, request params, the raw request, and request-scoped context values from middleware or auth integrations.
Production notes
- Set
STRIPE_SECRET_KEY,STRIPE_WEBHOOK_SECRET, andAPP_BASE_URL. - Keep product IDs stable because they become the app-facing contract.
- Verify webhook signatures before mutating billing state.
- Store billing snapshots through the integration schema when the app needs fast entitlement reads.
- Use server callers for admin-only operations and browser callers for checkout/portal redirects.
- Test checkout success, cancel, webhook replay, portal return, subscription update, and trial edge cases.