This is the creem_io wrapper package, which provides convenience methods and webhook helpers for common use cases.For full API access with all endpoints and maximum flexibility, see the Core SDK (creem).
Overview
The creem_io package is a convenience wrapper around the Creem API that provides:
- Simplified webhook handling with
onGrantAccess and onRevokeAccess callbacks
- Automatic signature verification for webhook events
- Type-safe event handlers for all webhook events
- Framework-agnostic webhook processing
This wrapper is ideal if you want to get started quickly with minimal configuration, especially for handling subscription access management.
Installation
Install with your preferred package manager:
Quick Start
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:
CREEM_API_KEY=your_api_key
CREEM_WEBHOOK_SECRET=your_webhook_secret
API Resources
The SDK organizes all operations into logical resources:
Products
// 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
// 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
// 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
// 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
plan
Licenses
// 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
// 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
// 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
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
The onGrantAccess and onRevokeAccess callbacks simplify subscription access management:
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
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
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 });
}
}
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");
}
}
);
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");
}
});
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:
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
The SDK throws errors when API calls fail. Always wrap SDK calls in try-catch blocks:
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.