Official Creem SDK for JavaScript & TypeScript - Universal payment integration for any framework
npm install creem_ioThe official Creem SDK for JavaScript & TypeScript
Universal payment integration that works with any framework - Express, Fastify, Hono, Remix, Koa, and more.
---
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
---
Install the package using your preferred package manager:
#### npm
``bash`
npm install creem_io
#### yarn
`bash`
yarn add creem_io
#### pnpm
`bash`
pnpm install creem_io
- Node.js 16.0.0 or higher
- A Creem account with API keys
---
`typescript
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
});
`
`typescript
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);
`
#### Express Example
`typescript
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});Grant access: ${reason} to ${customer.email}
},
onGrantAccess: async ({ reason, customer, metadata }) => {
// Grant user access to your platform
console.log();Revoke access: ${reason} from ${customer.email}
},
onRevokeAccess: async ({ reason, customer, metadata }) => {
// Revoke user access from your platform
console.log();
},
});
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. ๐
---
`typescript
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
});
`
---
#### Create a checkout session
`typescript`
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
`typescript`
const checkout = await creem.checkouts.get({
checkoutId: "checkout_abc123",
});
---
#### List products
`typescript`
const products = await creem.products.list({
page: 1,
limit: 10,
});
#### Get a product
`typescript`
const product = await creem.products.get({
productId: "prod_abc123",
});
#### Create a product
`typescript`
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",
});
---
#### List customers
`typescript`
const customers = await creem.customers.list({
page: 1,
limit: 10,
});
#### Get a customer
`typescript
// 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
`typescript
const portal = await creem.customers.createPortal({
customerId: "cust_abc123",
});
// Redirect user to portal
console.log(portal.url);
`
---
#### Get a subscription
`typescript`
const subscription = await creem.subscriptions.get({
subscriptionId: "sub_abc123",
});
#### Cancel a subscription
`typescript`
const subscription = await creem.subscriptions.cancel({
subscriptionId: "sub_abc123",
mode: "scheduled" // or "immediate"
});
#### Update a subscription
`typescript`
const subscription = await creem.subscriptions.update({
subscriptionId: "sub_abc123",
items: [
{
id: "item_abc123",
units: 5,
},
],
updateBehavior: "immediate", // or "next_billing_cycle"
});
#### Upgrade a subscription
`typescript`
const subscription = await creem.subscriptions.upgrade({
subscriptionId: "sub_abc123",
productId: "prod_premium",
updateBehavior: "immediate",
});
---
#### Activate a license
`typescript`
const license = await creem.licenses.activate({
key: "license_key_here",
instanceName: "Production Server",
});
#### Deactivate a license
`typescript`
const license = await creem.licenses.deactivate({
key: "license_key_here",
instanceId: "inst_abc123",
});
#### Validate a license
`typescript`
const license = await creem.licenses.validate({
key: "license_key_here",
instanceId: "inst_abc123",
});
---
#### Get a discount
`typescript
// By ID
const discount = await creem.discounts.get({
discountId: "disc_abc123",
});
// By code
const discount = await creem.discounts.get({
discountCode: "SUMMER2024",
});
`
#### Create a discount
`typescript`
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
`typescript`
await creem.discounts.delete({
discountId: "disc_abc123",
});
---
#### Get a transaction
`typescript`
const transaction = await creem.transactions.get({
transactionId: "txn_abc123",
});
#### List transactions
`typescript`
const transactions = await creem.transactions.list({
customerId: "cust_abc123",
page: 1,
limit: 10,
});
---
The SDK provides a framework-agnostic webhook handler with automatic signature verification.
#### Basic Setup (Any Framework)
`typescript
// 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
`typescript`
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
`typescript`
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
`typescript
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
`typescript
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 });
}
}
`
---
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)
`typescript
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)
`typescript
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).
---
The SDK is written in TypeScript and provides comprehensive type definitions for all operations:
`typescript`
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.
---
`typescript`
try {
const checkout = await creem.checkouts.create({
productId: "prod_abc123",
// ...
});
} catch (error) {
if (error instanceof Error) {
console.error("Failed to create checkout:", error.message);
}
}
---
`typescript`
const creem = createCreem({
apiKey: process.env.CREEM_API_KEY!,
webhookSecret: process.env.CREEM_WEBHOOK_SECRET!,
testMode: process.env.NODE_ENV !== "production",
});
Webhook handlers may be called multiple times. Always make them idempotent:
`typescript`
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 },
});
};
`typescriptcheckout-${userId}-${Date.now()}
const checkout = await creem.checkouts.create({
requestId: ,`
productId: "prod_abc123",
// ...
});
Use metadata to track your internal IDs:
`typescript`
metadata: {
userId: user.id,
orderId: order.id,
source: "web",
campaign: "summer_sale",
}
Always test your integration in test mode first:
`typescript`
const creem = createCreem({
apiKey: process.env.CREEM_API_KEY!,
testMode: true, // Test mode
});
Webhook handlers should handle errors and return appropriate responses:
`typescript`
onGrantAccess: async (context) => {
try {
await grantUserAccess(context);
} catch (error) {
console.error("Failed to grant access:", error);
// Don't throw - webhook will retry
}
};
---
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
Use a tool like ngrok to expose your local server:
`bash`
ngrok http 3000
Then use the ngrok URL in your Creem webhook settings:
``
https://abc123.ngrok.io/api/webhook/creem
---
Looking for a ready-made integration for your framework?
- Next.js: @creem_io/nextjs` - React components and route handlers
---
We welcome contributions! Please open an issue or submit a pull request.
---
- ๐ง Email: support@creem.io
- ๐ฌ Discord: Join our community
- ๐ Documentation: docs.creem.io
- ๐ Issues: GitHub Issues
---
MIT ยฉ Creem
---
Built with โค๏ธ by the Creem team.