Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.creem.io/llms.txt

Use this file to discover all available pages before exploring further.

Checkout sessions give you programmatic control over the payment flow. Unlike static payment links, checkout sessions are generated dynamically, allowing you to:
  • Pass custom tracking IDs for each payment
  • Pre-fill customer information like email
  • Set dynamic success URLs based on your app’s context
  • Apply discount codes programmatically
  • Add metadata for internal tracking

Prerequisites

Before creating checkout sessions, you’ll need:
  • A Creem account with an API key (Get your key)
  • At least one product created in your dashboard. One-time products can be paid or free with a 0 price.
Find your product ID by going to the Products tab, clicking on a product, and selecting “Copy ID” from the options menu.

Creating a Checkout Session

Choose the integration method that works best for your stack:
The Next.js adapter provides a route handler and React component for seamless integration.

Install the package

npm install @creem_io/nextjs

Create the checkout route

// app/api/checkout/route.ts
import { Checkout } from '@creem_io/nextjs';

export const GET = Checkout({
  apiKey: process.env.CREEM_API_KEY!,
  testMode: process.env.NODE_ENV !== 'production',
  defaultSuccessUrl: '/success',
});

Add a checkout button

// app/page.tsx
'use client'; // Optional: CreemCheckout also works in Server Components

import { CreemCheckout } from '@creem_io/nextjs';

export function CheckoutButton() {
  return (
    <CreemCheckout
      productId="prod_YOUR_PRODUCT_ID"
      referenceId="user_123" // Optional: Track this payment in your system
    >
      <button>Buy Now</button>
    </CreemCheckout>
  );
}
The CreemCheckout component automatically handles the checkout session creation and redirects the user to the payment page.

Next.js SDK Documentation

Explore advanced features, server components, and webhook handling.

Handling Successful Payments

After a successful payment, users are redirected to your success_url with payment details as query parameters:
https://yoursite.com/success?checkout_id=ch_xxx&order_id=ord_xxx&customer_id=cust_xxx&product_id=prod_xxx
Query parameterDescription
checkout_idThe ID of the checkout session created for this payment.
order_idThe ID of the order created after successful payment.
customer_idThe customer ID, based on the email that executed the successful payment.
subscription_idThe subscription ID of the product.
product_idThe product ID that the payment is related to.
request_idOptional. The request/reference ID you provided when creating this checkout.
signatureAll previous parameters signed by creem using your API-key, verifiable by you.
For production applications, we recommend using Webhooks to handle payment events.

Verifying Redirect Signatures

The signature query parameter allows you to verify that the redirect came from Creem. This prevents malicious users from spoofing successful payment redirects.
The signature is a SHA-256 hex digest of the redirect parameters joined with |, with salt={apiKey} appended at the end. Parameters appear in the order they arrive in the redirect URL (not alphabetically sorted), and null or empty values are excluded — only include parameters that have actual values.
The canonical string is built as:
key1=value1|key2=value2|...|keyN=valueN|salt={apiKey}
Then hashed with SHA-256 and hex-encoded to produce the signature value.
import * as crypto from 'crypto';

interface RedirectParams {
  request_id?: string | null;
  checkout_id: string;
  order_id: string | null;
  customer_id: string | null;
  subscription_id: string | null;
  product_id: string;
  signature: string;
}

function verifyRedirectSignature(
  params: RedirectParams,
  apiKey: string,
): boolean {
  const { signature, ...rest } = params;

  // Keep insertion order; exclude null/undefined/empty values.
  const data = Object.entries(rest)
    .filter(([, value]) => value !== null && value !== undefined && value !== '')
    .map(([key, value]) => `${key}=${value}`)
    .concat(`salt=${apiKey}`)
    .join('|');

  const expectedSignature = crypto
    .createHash('sha256')
    .update(data)
    .digest('hex');

  return signature === expectedSignature;
}

// Usage in your success page
export async function GET(request: Request) {
  const url = new URL(request.url);

  // Read fields in the order they appear in the redirect URL.
  const params: RedirectParams = {
    request_id: url.searchParams.get('request_id'),
    checkout_id: url.searchParams.get('checkout_id')!,
    order_id: url.searchParams.get('order_id'),
    customer_id: url.searchParams.get('customer_id'),
    subscription_id: url.searchParams.get('subscription_id'),
    product_id: url.searchParams.get('product_id')!,
    signature: url.searchParams.get('signature')!,
  };

  const isValid = verifyRedirectSignature(params, process.env.CREEM_API_KEY!);

  if (!isValid) {
    return new Response('Invalid signature', { status: 401 });
  }

  // Proceed with success page...
}
Important: Parameters with null or empty values (like order_id for subscription-only checkouts, or subscription_id for one-time payments) must be excluded from the signed string. Including them as "order_id=null" will cause verification to fail.

Advanced Features

Metadata

Add custom metadata to track additional information with each payment. Metadata is included in webhook events and can be retrieved later.
<CreemCheckout
  productId="prod_YOUR_PRODUCT_ID"
  referenceId="user_123"
  metadata={{
    userId: 'internal_user_id',
    planType: 'premium',
    source: 'marketing_campaign',
  }}
>
  <button>Subscribe</button>
</CreemCheckout>
Metadata is especially useful for tracking internal IDs, campaign sources, or any custom information you need to associate with a payment.

Custom Success URL

Override the default success URL on a per-checkout basis. This is useful for directing users to specific pages after payment based on context.
<CreemCheckout
  productId="prod_YOUR_PRODUCT_ID"
  successUrl="/account/welcome" // Overrides defaultSuccessUrl
>
  <button>Get Started</button>
</CreemCheckout>

Pre-fill Customer Email

Lock the customer email at checkout to ensure users complete payment with the email they registered with on your platform.
<CreemCheckout
  productId="prod_YOUR_PRODUCT_ID"
  customer={{ email: 'user@example.com' }}
>
  <button>Complete Purchase</button>
</CreemCheckout>

Apply Discount Codes

Apply discount codes programmatically to pre-fill them at checkout.
<CreemCheckout productId="prod_YOUR_PRODUCT_ID" discountCode="LAUNCH50">
  <button>Claim Offer</button>
</CreemCheckout>

Discount Codes

Learn how to create and manage discount codes in your dashboard.

Seat-Based Billing

Charge for multiple units or seats by specifying the units parameter. The total price will be calculated as base_price × units.
<CreemCheckout
  productId="prod_YOUR_PRODUCT_ID"
  units={seatCount} // Charge for 5 seats
>
  <button>Add {seatCount} Seats</button>
</CreemCheckout>

Seat-Based Billing

Learn more about implementing and managing seat-based pricing models.

Next Steps

Checkout Customization

Brand your checkout with custom colors, logos, and themes

Checkout Custom Fields

Collect additional information from customers during checkout

Subscriptions

Learn how to manage recurring billing and subscriptions

Revenue Splits

Split revenue between multiple parties automatically