Skip to content
This repository was archived by the owner on May 6, 2026. It is now read-only.

Repository files navigation

Warning This package (creem_io) is deprecated. Please use the official creem package instead:

npm install creem

Creem SDK

The official Creem SDK for JavaScript & TypeScript

Universal payment integration that works with any framework - Express, Fastify, Hono, Remix, Koa, and more.

Installation · Quick Start · Documentation


Introduction

The Creem SDK is a framework-agnostic JavaScript/TypeScript library for integrating Creem payments into any application. This SDK provides:

  • 🚀 Framework Agnostic - Works with Express, Fastify, Next.js, Remix, Hono, Koa, and any Node.js environment
  • 🔐 Type-Safe - Full TypeScript support with comprehensive type definitions
  • 📦 Zero Dependencies - Lightweight with minimal footprint (only crypto for webhooks)
  • 🎯 Simple API - Intuitive methods for all Creem operations
  • 🪝 Webhook Support - Built-in webhook verification and type-safe handlers
  • ⚡ CamelCase - All responses automatically converted from snake_case to camelCase for better DX

Installation

Install the package using your preferred package manager:

npm

npm install creem_io

yarn

yarn add creem_io

pnpm

pnpm install creem_io

Requirements


Quick Start

1. Initialize the SDK

import { createCreem } from "creem_io";

const creem = createCreem({
  apiKey: "your_api_key_here",
  testMode: true, // Set to false for production
  webhookSecret: "your_webhook_secret_here", // Optional, for webhooks
});

2. Create a checkout session

const checkout = await creem.checkouts.create({
  productId: "prod_abc123",
  units: 1,
  successUrl: "https://yoursite.com/success",
  customer: {
    email: "customer@example.com",
    name: "John Doe",
  },
  metadata: {
    userId: "user_123",
  },
});

// Redirect user to checkout
console.log(checkout.checkoutUrl);

3. Handle webhooks

Express Example

import express from "express";
import { createCreem } from "creem_io";

const app = express();
const creem = createCreem({
  apiKey: process.env.CREEM_API_KEY!,
  webhookSecret: process.env.CREEM_WEBHOOK_SECRET!,
});

app.post("/webhook/creem", express.raw({ type: "application/json" }), async (req, res) => {
  try {
    const signature = req.headers["creem-signature"] as string;

    await creem.webhooks.handleEvents(req.body, signature, {
      onCheckoutCompleted: async ({ customer, product }) => {
        console.log(`${customer?.email} purchased ${product.name}`);
      },
      onGrantAccess: async ({ reason, customer, metadata }) => {
        // Grant user access to your platform
        console.log(`Grant access: ${reason} to ${customer.email}`);
      },
      onRevokeAccess: async ({ reason, customer, metadata }) => {
        // Revoke user access from your platform
        console.log(`Revoke access: ${reason} from ${customer.email}`);
      },
    });

    res.status(200).send("OK");
  } catch (error) {
    console.error("Webhook error:", error);
    res.status(400).send("Invalid signature");
  }
});

That's it! You now have Creem payments integrated. 🎉


Documentation

Initialization

import { createCreem } from "creem_io";

const creem = createCreem({
  apiKey: string;          // Required: Your Creem API key
  testMode?: boolean;      // Optional: Use test environment (default: false)
  webhookSecret?: string;  // Optional: For webhook signature verification
});

Checkouts

Create a checkout session

const checkout = await creem.checkouts.create({
  productId: "prod_abc123",
  requestId: "unique_request_id", // Optional: Idempotency key
  units: 2,
  discountCode: "SUMMER2024",
  customer: {
    email: "customer@example.com",
    name: "John Doe",
  },
  customField: [
    // Optional: Max 3 custom fields
    {
      key: "company",
      label: "Company Name",
      type: "text",
      optional: false,
    },
  ],
  successUrl: "https://yoursite.com/success",
  metadata: {
    userId: "user_123",
    source: "web",
  },
});

Get a checkout session

const checkout = await creem.checkouts.get({
  checkoutId: "checkout_abc123",
});

Products

List products

const products = await creem.products.list({
  page: 1,
  limit: 10,
});

Get a product

const product = await creem.products.get({
  productId: "prod_abc123",
});

Create a product

const product = await creem.products.create({
  name: "Pro Plan",
  description: "Professional tier with all features",
  price: 2999, // $29.99 in cents
  currency: "USD",
  billingType: "recurring",
  billingPeriod: "every-month",
  taxMode: "inclusive",
  taxCategory: "saas",
  defaultSuccessUrl: "https://yoursite.com/success",
});

Customers

List customers

const customers = await creem.customers.list({
  page: 1,
  limit: 10,
});

Get a customer

// By ID
const customer = await creem.customers.get({
  customerId: "cust_abc123",
});

// By email
const customer = await creem.customers.get({
  email: "customer@example.com",
});

Create portal link

const portal = await creem.customers.createPortal({
  customerId: "cust_abc123",
});

// Redirect user to portal
console.log(portal.url);

Subscriptions

Get a subscription

const subscription = await creem.subscriptions.get({
  subscriptionId: "sub_abc123",
});

Cancel a subscription

const subscription = await creem.subscriptions.cancel({
  subscriptionId: "sub_abc123",
  mode: "scheduled", // or "immediate"
});

Update a subscription

const subscription = await creem.subscriptions.update({
  subscriptionId: "sub_abc123",
  items: [
    {
      id: "item_abc123",
      units: 5,
    },
  ],
  updateBehavior: "immediate", // or "next_billing_cycle"
});

Upgrade a subscription

const subscription = await creem.subscriptions.upgrade({
  subscriptionId: "sub_abc123",
  productId: "prod_premium",
  updateBehavior: "immediate",
});

Licenses

Activate a license

const license = await creem.licenses.activate({
  key: "license_key_here",
  instanceName: "Production Server",
});

Deactivate a license

const license = await creem.licenses.deactivate({
  key: "license_key_here",
  instanceId: "inst_abc123",
});

Validate a license

const license = await creem.licenses.validate({
  key: "license_key_here",
  instanceId: "inst_abc123",
});

Discounts

Get a discount

// By ID
const discount = await creem.discounts.get({
  discountId: "disc_abc123",
});

// By code
const discount = await creem.discounts.get({
  discountCode: "SUMMER2024",
});

Create a discount

const discount = await creem.discounts.create({
  name: "Summer Sale",
  code: "SUMMER2024", // Optional: Auto-generated if not provided
  type: "percentage",
  percentage: 25, // 25% off
  duration: "forever",
  maxRedemptions: 100,
  expiryDate: "2024-12-31",
  appliesToProducts: ["prod_abc123"],
});

Delete a discount

await creem.discounts.delete({
  discountId: "disc_abc123",
});

Transactions

Get a transaction

const transaction = await creem.transactions.get({
  transactionId: "txn_abc123",
});

List transactions

const transactions = await creem.transactions.list({
  customerId: "cust_abc123",
  page: 1,
  limit: 10,
});

Webhooks

The SDK provides a framework-agnostic webhook handler with automatic signature verification.

Basic Setup (Any Framework)

// Get raw body as string or Buffer
const payload = await getRawBody(request);
const signature = request.headers["creem-signature"];

await creem.webhooks.handleEvents(payload, signature, {
  onCheckoutCompleted: async (data) => {
    // Access all fields directly
    const { customer, product, order, subscription, metadata } = data;
  },

  onGrantAccess: async (context) => {
    // Called for: subscription.active, subscription.trialing, subscription.paid
    const { reason, customer, product, metadata } = context;
    // Grant user access to your platform
  },

  onRevokeAccess: async (context) => {
    // Called for: subscription.paused, subscription.expired
    const { reason, customer, product, metadata } = context;
    // Revoke user access from your platform
  },

  // Individual subscription events
  onSubscriptionActive: async (data) => {},
  onSubscriptionTrialing: async (data) => {},
  onSubscriptionCanceled: async (data) => {},
  onSubscriptionPaid: async (data) => {},
  onSubscriptionExpired: async (data) => {},
  onSubscriptionUnpaid: async (data) => {},
  onSubscriptionPastDue: async (data) => {},
  onSubscriptionPaused: async (data) => {},
  onSubscriptionUpdate: async (data) => {},

  // Other events
  onRefundCreated: async (data) => {},
  onDisputeCreated: async (data) => {},
});

Framework Examples

Express

app.post("/webhook", express.raw({ type: "application/json" }), async (req, res) => {
  try {
    await creem.webhooks.handleEvents(req.body, req.headers["creem-signature"], {
      /* handlers */
    });
    res.status(200).send("OK");
  } catch (error) {
    res.status(400).send("Invalid signature");
  }
});

Fastify

fastify.post("/webhook", async (request, reply) => {
  try {
    await creem.webhooks.handleEvents(request.rawBody, request.headers["creem-signature"], {
      /* handlers */
    });
    reply.code(200).send("OK");
  } catch (error) {
    reply.code(400).send("Invalid signature");
  }
});

Hono

app.post("/webhook", async (c) => {
  try {
    const body = await c.req.text();
    const signature = c.req.header("creem-signature");

    await creem.webhooks.handleEvents(body, signature, {
      /* handlers */
    });

    return c.text("OK");
  } catch (error) {
    return c.text("Invalid signature", 400);
  }
});

Next.js App Router

import { NextRequest } from "next/server";
import { createCreem } from "creem_io";

const creem = createCreem({
  apiKey: process.env.CREEM_API_KEY!,
  webhookSecret: process.env.CREEM_WEBHOOK_SECRET!,
});

export async function POST(req: NextRequest) {
  try {
    const body = await req.text();
    const signature = req.headers.get("creem-signature")!;

    await creem.webhooks.handleEvents(body, signature, {
      onCheckoutCompleted: async (data) => {
        // Handle checkout
      },
      onGrantAccess: async (context) => {
        // Grant access
      },
    });

    return new Response("OK", { status: 200 });
  } catch (error) {
    return new Response("Invalid signature", { status: 400 });
  }
}

Access Management

The onGrantAccess and onRevokeAccess callbacks provide a simple way to manage user access for subscription-based products.

onGrantAccess

Called when a user should be granted access. This happens when:

  • Subscription becomes active (after payment)
  • Subscription enters trialing period (free trial)
  • Subscription payment is paid (renewal)
onGrantAccess: async ({ reason, customer, product, metadata }) => {
  const userId = metadata?.userId as string;

  // Grant access in your database
  await db.user.update({
    where: { id: userId },
    data: { subscriptionActive: true },
  });

  console.log(`Granted ${reason} to ${customer.email}`);
};

onRevokeAccess

Called when a user's access should be revoked. This happens when:

  • Subscription is paused (manually by user or admin)
  • Subscription is expired (trial ended or canceled subscription period ended)
onRevokeAccess: async ({ reason, customer, product, metadata }) => {
  const userId = metadata?.userId as string;

  // Revoke access in your database
  await db.user.update({
    where: { id: userId },
    data: { subscriptionActive: false },
  });

  console.log(`Revoked access (${reason}) from ${customer.email}`);
};

⚠️ Important: Both callbacks may be called multiple times for the same user/subscription. Always implement these as idempotent operations (safe to call repeatedly).


TypeScript Support

The SDK is written in TypeScript and provides comprehensive type definitions for all operations:

import type {
  Checkout,
  Customer,
  Product,
  Subscription,
  Transaction,
  License,
  Discount,
  WebhookOptions,
  CheckoutCompletedEvent,
  SubscriptionEvent,
  GrantAccessContext,
  RevokeAccessContext,
} from "creem_io";

All API responses are fully typed, and the SDK automatically converts snake_case to camelCase for better TypeScript/JavaScript experience.


Error Handling

try {
  const checkout = await creem.checkouts.create({
    productId: "prod_abc123",
    // ...
  });
} catch (error) {
  if (error instanceof Error) {
    console.error("Failed to create checkout:", error.message);
  }
}

Best Practices

1. Use Environment Variables

const creem = createCreem({
  apiKey: process.env.CREEM_API_KEY!,
  webhookSecret: process.env.CREEM_WEBHOOK_SECRET!,
  testMode: process.env.NODE_ENV !== "production",
});

2. Implement Idempotent Webhooks

Webhook handlers may be called multiple times. Always make them idempotent:

onGrantAccess: async ({ customer, metadata }) => {
  // Use upsert instead of create
  await db.user.upsert({
    where: { id: metadata.userId },
    update: { subscriptionActive: true },
    create: { id: metadata.userId, subscriptionActive: true },
  });
};

3. Use Request IDs for Idempotency

const checkout = await creem.checkouts.create({
  requestId: `checkout-${userId}-${Date.now()}`,
  productId: "prod_abc123",
  // ...
});

4. Store Metadata

Use metadata to track your internal IDs:

metadata: {
  userId: user.id,
  orderId: order.id,
  source: "web",
  campaign: "summer_sale",
}

5. Test in Test Mode

Always test your integration in test mode first:

const creem = createCreem({
  apiKey: process.env.CREEM_API_KEY!,
  testMode: true, // Test mode
});

6. Handle Errors Gracefully

Webhook handlers should handle errors and return appropriate responses:

onGrantAccess: async (context) => {
  try {
    await grantUserAccess(context);
  } catch (error) {
    console.error("Failed to grant access:", error);
    // Don't throw - webhook will retry
  }
};

Webhook Configuration

Setting Up Webhooks in Creem Dashboard

  1. Go to your Creem Dashboard
  2. Navigate to Developers → Webhooks
  3. Click Add Endpoint
  4. Enter your webhook URL: https://yourdomain.com/api/webhook/creem
  5. Select the events you want to receive
  6. Copy the Webhook Secret to your environment variables

Testing Webhooks Locally

Use a tool like ngrok to expose your local server:

ngrok http 3000

Then use the ngrok URL in your Creem webhook settings:

https://abc123.ngrok.io/api/webhook/creem

Framework Adapters

Looking for a ready-made integration for your framework?


Contributing

We welcome contributions! Please open an issue or submit a pull request.


Support


License

MIT © Creem


Authors

Built with ❤️ by the Creem team.

About

No description, website, or topics provided.

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages