@creem_io/nextjs is a small App Router adapter around the Creem SDK. It provides route-handler
factories for checkout, the customer portal, and webhooks, plus React link components for starting
checkout or opening the portal.
Source and example
Browse the package and its runnable Next.js application.
Install
Create a checkout route
Keep the API key on the server:.env.local
app/checkout/route.ts
app/pricing/page.tsx
CreemCheckout builds an anchor whose query parameters are consumed by Checkout. It supports:
Protect an authenticated checkout
The route below accepts the selected product from the link, checks it against a server-side allowlist, and replaces identity-related parameters with values from the authenticated session.successUrl is removed from the request so the handler always uses the server-configured default.
app/checkout/route.ts
requireUser represents your application’s server-side authentication check. It should reject an
unauthenticated request and return the user from the verified session.
getOrCreateCheckoutAttemptId represents a server-side idempotency record: retries of one purchase
attempt should reuse its ID, while a later intentional purchase gets a new one. Validate or
overwrite other commercial inputs, such as units and discountCode, when customers should not
be able to choose them freely.
Create a customer portal route
app/portal/route.ts
CreemPortal uses /portal by default. Set portalPath when your route lives elsewhere.
Handle webhooks
Create a POST route and register its public URL in the Creem dashboard:app/api/webhook/creem/route.ts
Webhook reads the raw request body, verifies the creem-signature header, parses the event, and
calls the matching handler. Keep every callback idempotent because delivery may be retried.
Access callbacks
onGrantAccess runs for active, trialing, and paid subscription events. onRevokeAccess runs for
paused, expired, and canceled subscription events. subscription.scheduled_cancel only calls
onSubscriptionScheduledCancel and does not change access.
Event callbacks
The adapter also exposes callbacks for individual events:onCheckoutCompletedonRefundCreatedonDisputeCreatedonSubscriptionActiveonSubscriptionTrialingonSubscriptionCanceledonSubscriptionPaidonSubscriptionExpiredonSubscriptionUnpaidonSubscriptionUpdateonSubscriptionPastDueonSubscriptionPausedonSubscriptionScheduledCancel
webhookId, webhookCreatedAt, and the
relevant customer, product, order, or subscription fields.
Common gotchas
Event names and access reasons
The event issubscription.canceled (one L), and its callback is onSubscriptionCanceled.
A misspelled event type sent to Webhook returns 400 Unknown event type; a misspelled string
comparison in your callback never matches. Access callback reasons use underscores, such as
subscription_expired, rather than dotted event names like subscription.expired.
subscription.canceled calls onRevokeAccess with reason subscription_canceled, then calls
onSubscriptionCanceled. The adapter awaits each callback; if revocation throws, it returns
an error for retry and does not call onSubscriptionCanceled on that attempt.
SDK and webhook field casing
Thecreem SDK exposes camelCase subscription fields such as currentPeriodEndDate and
canceledAt. Next.js webhook callbacks retain snake_case fields such as
current_period_end_date and canceled_at. Account for this difference when sharing utilities
between SDK responses and webhook callbacks.
Timestamp formats
The event envelope’screated_at becomes webhookCreatedAt in event callbacks and is a
millisecond timestamp. Transaction created_at fields also use milliseconds. Subscription date
fields such as current_period_end_date arrive as ISO 8601 strings in the webhook JSON;
the adapter does not convert them to Date objects. The SDK converts its subscription date
fields to Date objects when parsing API responses.
Checkout amounts
InonCheckoutCompleted, the checkout amount and currency are on order, not at the top level.
The order field is optional, so check it before reading the amount:
Metadata in access callbacks
onGrantAccess and onRevokeAccess receive subscription fields directly, including metadata.
Read metadata?.referenceId rather than context.object.metadata. Derive that reference ID
from trusted server-side state when creating an authenticated checkout, and check that it is a
string before using it, as shown in the webhook example.
Route factory reference
Checkout(options)
Portal(options)
Webhook(options)
webhookSecret is required. All event and access callbacks are optional.
Related guides
Better Auth
Use authenticated billing endpoints with persisted subscription state.
Next.js template
Start from a full application with authentication and a database.