A B2B SaaS subscription, end to end
A full integration for a typical B2B SaaS. Checkout signup, recurring billing, failed charges, cancellation.
This is a complete subscription billing integration for a Next.js app. Your database helpers and email sender are left to you.
The scenario: you're shipping a SaaS product called Acme. You have a Pro plan at $99/month (well, 99 USDC/month). Customers sign up, pay monthly, and you want to know when they're churning before it becomes a problem.
What we're building
Two routes plus a webhook handler:
/pricing: shows the plan and a "Subscribe" button./api/checkout/subscribe: redirects to the plan's checkout link./api/webhooks/strimz: receives the events that matter.
We'll skip a bunch of standard Next.js boilerplate (Tailwind setup, env config, etc.) and focus on the Strimz parts.
One-time setup
Create the plan once, from a script or the dashboard:
Save the plan id in your env, for example STRIMZ_PRO_PLAN_ID=cmq4wck6a0002je6lfmb5bgjp. You'll use it in the checkout route.
Also create a webhook endpoint, subscribed to the events you care about:
The pricing page
A "Subscribe" button. When clicked, POSTs to the checkout route and redirects to the URL the route returns.
The checkout route
The checkout link is built from the plan id. Strimz hosts the page, which handles wallet connect and the two signatures (the EIP-2612 permit and the SubscriptionIntent). Enrolment moves no money. With the 14-day trial the first charge is due when the trial ends; without a trial the scheduler takes it within about 15 minutes. There is no SDK call for the signup itself, and no way to attach metadata to it today.
The webhook handler
This is where the meaningful logic lives. The events you care about are subscription state transitions.
The actual handlers. What your business logic does for each:
subscription.created, subscription.cancelled and subscription.lapsed carry the subscription itself as event.data. subscription.charged carries { subscription, charge, transaction } and subscription.charge_failed carries { subscription, charge }, so the subscription id is at event.data.subscription.id.
A cancel from your API keeps access until the paid period ends, as in the handler above. Strimz stops charging at once either way.
trialEndsAt is null when the payer gets no trial, for example on a second subscription to the same plan, so the handler falls back to currentPeriodEndAt.
After checkout
Once the enrolment confirms, the checkout sends the customer to the plan's successUrl, with {SUBSCRIPTION_ID} filled in. If they cancel, it sends them to cancelUrl.
To keep customers on your pricing page instead of redirecting, swap the form for <StrimzSubscribeButton planId /> from @strimz/sdk-react. It opens the checkout in a popup and calls onSuccess({ subscriptionId, txHash }) when it's done.
Either way, grant access from the webhook handler, not from the browser. subscription.created usually arrives within a few seconds of the enrolment.
What's not in this recipe
Cancellation flow. See Self-serve cancel.
The customer-facing dashboard showing subscription state. Standard CRUD; nothing Strimz-specific.
Failed-charge recovery UI. The email above is the minimum; for a smoother UX you'd build a "Top up your wallet" page in your product and link the customer to it.
Plans-by-tier (Pro + Team + Enterprise). Create one plan per tier; the rest of the integration is the same.
What's also not in this recipe
What happens if Strimz is down. New signups fail while checkout is unavailable. Renewals that fall due during an outage are charged on the next sweep after it ends. If an outage lasts longer than a whole billing period, the contract charges one period and skips the missed ones rather than charging for each.

