Installation
Install with your preferred package manager:Copy
npm install creem_io
Quick Start
Copy
import { createCreem } from 'creem_io';
const creem = createCreem({
apiKey: process.env.CREEM_API_KEY!,
webhookSecret: process.env.CREEM_WEBHOOK_SECRET, // optional, for webhooks
testMode: false, // set to true for test mode
});
// Retrieve a product
const product = await creem.products.get({
productId: 'prod_7CIbZEZnRC5DWibmoOboOu',
});
console.log(product);
// Create a checkout session
const checkout = await creem.checkouts.create({
productId: 'prod_xxxxx',
successUrl: 'https://yourapp.com/success',
metadata: {
userId: 'user_123',
},
});
console.log(checkout.checkoutUrl); // Redirect user to this URL
Environment Variables
We recommend storing your credentials in environment variables:Copy
CREEM_API_KEY=your_api_key
CREEM_WEBHOOK_SECRET=your_webhook_secret
API Resources
The SDK organizes all operations into logical resources:Products
Copy
// List products
const products = await creem.products.list({
page: 1,
limit: 10,
});
// Get a product
const product = await creem.products.get({
productId: 'prod_7CIbb...',
});
// Create a product
creem.products.create({
name: 'Test Product',
description: 'Test Product Description',
price: 1000, // In cents
currency: 'USD',
billingType: 'recurring',
billingPeriod: 'every-month',
});
// Search products
const products = await creem.products.list({
page: 1,
limit: 10,
});
Checkouts
Copy
// Create a checkout session
const checkout = await creem.checkouts.create({
productId: 'prod_xxxxx',
units: 2, // Optional: Number of units (default: 1)
discountCode: 'SUMMER2024', // Optional: Apply discount
customer: {
email: '[email protected]', // Optional: Pre-fill customer info
},
customField: [
// Optional: Max 3 custom fields
{
key: 'company',
label: 'Company Name',
type: 'text',
optional: false,
},
],
successUrl: 'https://yourapp.com/success',
metadata: {
userId: 'user_123',
source: 'web',
},
});
console.log(checkout.checkoutUrl); // Redirect user to this URL
// Get a checkout session
const retrievedCheckout = await creem.checkouts.get({
checkoutId: 'chck_1234567890',
});
Customers
Copy
// 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',
});
// Get a customer by email
const customerByEmail = await creem.customers.get({
email: '[email protected]',
});
// Create customer portal link
const portal = await creem.customers.createPortal({
customerId: 'cust_abc123',
});
console.log(portal.customerPortalLink); // Redirect user to portal
Subscriptions
Copy
// Get a subscription
const subscription = await creem.subscriptions.get({
subscriptionId: 'sub_abc123',
});
// Cancel a subscription
const canceledSubscription = await creem.subscriptions.cancel({
subscriptionId: 'sub_abc123',
});
// Update a subscription (change units/seats)
const updated = await creem.subscriptions.update({
subscriptionId: 'sub_abc123',
items: [
{
id: 'item_abc123', // Subscription item ID
units: 5, // Update to 5 seats
},
],
updateBehavior: 'proration-charge-immediately',
});
// Upgrade a subscription to a different product
const upgraded = await creem.subscriptions.upgrade({
subscriptionId: 'sub_abc123',
productId: 'prod_premium', // New product ID
updateBehavior: 'proration-charge-immediately',
});
Update Behavior Options: -
proration-charge-immediately: Calculate
proration and charge immediately - proration-charge: Calculate proration and
charge at next billing cycle - proration-none: No proration, just switch the
planLicenses
Copy
// Activate a license
const license = await creem.licenses.activate({
key: 'license_key_here',
instanceName: 'Production Server',
});
console.log(license.instance?.id); // Use this instance ID for validation
// Validate a license
const validatedLicense = await creem.licenses.validate({
key: 'license_key_here',
instanceId: 'inst_abc123',
});
console.log(validatedLicense.status); // "active" | "inactive" | "expired" | "disabled"
// Deactivate a license
const deactivatedLicense = await creem.licenses.deactivate({
key: 'license_key_here',
instanceId: 'inst_abc123',
});
Discounts
Copy
// Create a discount code
const discount = await creem.discounts.create({
name: 'Summer Sale 2024',
code: 'SUMMER2024', // Optional: Auto-generated if not provided
type: 'percentage',
percentage: 20, // 20% off
duration: 'forever', // "forever" | "once" | "repeating"
maxRedemptions: 100,
});
// Retrieve a discount by ID
const discount = await creem.discounts.get({
discountId: 'disc_xxxxx',
});
// Retrieve a discount by code
const discountByCode = await creem.discounts.get({
discountCode: 'SUMMER2024',
});
// Delete a discount
await creem.discounts.delete({
discountId: 'disc_xxxxx',
});
Transactions
Copy
// Get a transaction
const transaction = await creem.transactions.get({
transactionId: 'txn_xxxxx',
});
// List transactions
const transactions = await creem.transactions.list({
customerId: 'cust_xxxxx', // Optional: filter by customer
page: 1,
limit: 50,
});
Webhooks
Handle Creem webhook events in your application. The SDK provides automatic signature verification and type-safe event handlers.Basic Webhook Setup
Copy
import { createCreem } from 'creem_io';
const creem = createCreem({
apiKey: process.env.CREEM_API_KEY!,
webhookSecret: process.env.CREEM_WEBHOOK_SECRET!,
});
// In your webhook endpoint
app.post('/webhook', async (req, res) => {
try {
await creem.webhooks.handleEvents(
req.body, // raw body as string
req.headers['creem-signature'],
{
onCheckoutCompleted: async (data) => {
console.log('Checkout completed:', data.customer?.email);
},
onGrantAccess: async (context) => {
// Grant user access when subscription is active/trialing/paid
const userId = context.metadata?.userId;
await grantUserAccess(userId);
},
onRevokeAccess: async (context) => {
// Revoke access when subscription is paused/expired
const userId = context.metadata?.userId;
await revokeUserAccess(userId);
},
}
);
res.status(200).send('OK');
} catch (error) {
console.error('Webhook error:', error);
res.status(400).send('Invalid signature');
}
});
Access Management Callbacks
TheonGrantAccess and onRevokeAccess callbacks simplify subscription access management:
Copy
onGrantAccess: async ({ reason, customer, product, metadata }) => {
// Called for: subscription.active, subscription.trialing, subscription.paid
const userId = metadata?.userId as string;
await db.user.update({
where: { id: userId },
data: { subscriptionActive: true },
});
console.log(`Granted ${reason} to ${customer.email}`);
},
onRevokeAccess: async ({ reason, customer, product, metadata }) => {
// Called for: subscription.paused, subscription.expired
const userId = metadata?.userId as string;
await db.user.update({
where: { id: userId },
data: { subscriptionActive: false },
});
console.log(`Revoked access (${reason}) from ${customer.email}`);
},
All Available Webhook Events
Copy
await creem.webhooks.handleEvents(body, signature, {
// Checkout events
onCheckoutCompleted: async (data) => {},
// Access management (simplified)
onGrantAccess: async (context) => {},
onRevokeAccess: async (context) => {},
// 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-Specific Examples
- Next.js App Router
- Express
- Fastify
- Hono
Copy
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 completion
},
onGrantAccess: async (context) => {
// Grant access to user
},
});
return new Response("OK", { status: 200 });
} catch (error) {
return new Response("Invalid signature", { status: 400 });
}
}
Copy
import express from "express";
app.post(
"/webhook",
express.raw({ type: "application/json" }),
async (req, res) => {
try {
await creem.webhooks.handleEvents(
req.body,
req.headers["creem-signature"],
{
onCheckoutCompleted: async (data) => {
// Handle checkout
},
}
);
res.status(200).send("OK");
} catch (error) {
res.status(400).send("Invalid signature");
}
}
);
Copy
fastify.post("/webhook", async (request, reply) => {
try {
await creem.webhooks.handleEvents(
request.rawBody,
request.headers["creem-signature"],
{
onCheckoutCompleted: async (data) => {
// Handle checkout
},
}
);
reply.code(200).send("OK");
} catch (error) {
reply.code(400).send("Invalid signature");
}
});
Copy
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, {
onCheckoutCompleted: async (data) => {
// Handle checkout
},
});
return c.text("OK");
} catch (error) {
return c.text("Invalid signature", 400);
}
});
TypeScript Support
The SDK is written in TypeScript and provides comprehensive type definitions:Copy
import type {
Checkout,
Customer,
Product,
Subscription,
Transaction,
License,
Discount,
WebhookOptions,
CheckoutCompletedEvent,
SubscriptionEvent,
GrantAccessContext,
RevokeAccessContext,
} from "creem_io";
Error Handling
The SDK throws errors when API calls fail. Always wrap SDK calls in try-catch blocks:Copy
try {
const product = await creem.products.get({
productId: 'prod_xxxxx',
});
} catch (error) {
console.error('Failed to retrieve product:', error);
// Handle error appropriately
}
References
For feedback or issues, open a PR or issue on the Creem SDK GitHub.