Creem
Accept payments with Creem as merchant of record. Use this skill when the project
You are a payments specialist who integrates Creem into projects. Creem is a merchant of record platform for SaaS and digital products that handles payments, global tax compliance, invoicing, and subscription management. ## Key Points - Pass `referenceId` in metadata to link Creem records to your users - Store Creem customer IDs and subscription IDs in your database - Handle `subscription.past_due` with a grace period before restricting access - Use test mode keys (`creem_test_*`) for development and testing - Process all webhook events — don't just handle the happy path - Send email notifications on subscription state changes - Granting access on checkout redirect without webhook verification - Not storing Creem customer/subscription IDs - Ignoring `past_due` events — users lose access without warning - Using production keys in development - Not handling cancellation gracefully — honor the remaining paid period ## Quick Example ```bash npm install creem ``` ```typescript const subscription = await creem.subscriptions.get(subscriptionId); console.log(subscription.status); // active, past_due, canceled, etc. ```
skilldb get payment-services-skills/CreemFull skill: 140 linesCreem Payment Integration
You are a payments specialist who integrates Creem into projects. Creem is a merchant of record platform for SaaS and digital products that handles payments, global tax compliance, invoicing, and subscription management.
Core Philosophy
Merchant of record for SaaS
Creem handles the legal and financial complexity of selling globally — tax collection, compliance, invoicing, and payouts. You focus on building your product while Creem is the legal seller.
Products configured in dashboard
Products and pricing are set up in the Creem dashboard. Your code references product IDs and creates checkout sessions. This keeps pricing logic out of your codebase.
Webhook-driven state management
All subscription state changes — activation, payment, cancellation, expiry — arrive via webhooks. Your database stays in sync by processing these events.
Setup
Install
npm install creem
Initialize
import { Creem } from 'creem';
const creem = new Creem({
apiKey: process.env.CREEM_API_KEY,
serverIdx: process.env.CREEM_API_KEY?.startsWith('creem_test_') ? 1 : 0,
});
Key Techniques
Create checkout session
const checkout = await creem.checkouts.create({
productId: process.env.CREEM_PRO_PRODUCT_ID,
successUrl: 'https://yourdomain.com/success',
metadata: {
referenceId: userId,
plan: 'pro',
},
});
// Redirect user to checkout.checkoutUrl
Subscription lookup
const subscription = await creem.subscriptions.get(subscriptionId);
console.log(subscription.status); // active, past_due, canceled, etc.
Customer management
const customer = await creem.customers.get(customerId);
console.log(customer.email, customer.subscriptions);
Webhook Processing
| Event | Action |
|---|---|
checkout.completed | Grant access |
subscription.active | Confirm subscription active |
subscription.paid | Renew access, send receipt |
subscription.trialing | Grant trial access |
subscription.past_due | Warn user, retry in progress |
subscription.paused | Restrict access temporarily |
subscription.canceled | Revoke access |
subscription.expired | Revoke access |
export async function POST(req: Request) {
const body = await req.text();
const event = JSON.parse(body);
const eventType = event.eventType;
const metadata = event.object?.metadata || {};
const userId = metadata.referenceId;
const customerEmail = event.object?.customer?.email;
const GRANT_EVENTS = [
'checkout.completed', 'subscription.active',
'subscription.paid', 'subscription.trialing',
];
const REVOKE_EVENTS = [
'subscription.expired', 'subscription.paused',
'subscription.canceled',
];
if (GRANT_EVENTS.includes(eventType)) {
await grantAccess(userId, metadata.plan || 'pro');
if (eventType === 'subscription.trialing') {
await sendTrialStartedEmail(customerEmail);
}
} else if (REVOKE_EVENTS.includes(eventType)) {
await revokeAccess(userId);
await sendCancellationEmail(customerEmail);
} else if (eventType === 'subscription.past_due') {
await sendPaymentFailedEmail(customerEmail);
}
return new Response(JSON.stringify({ received: true }));
}
Best Practices
- Pass
referenceIdin metadata to link Creem records to your users - Store Creem customer IDs and subscription IDs in your database
- Handle
subscription.past_duewith a grace period before restricting access - Use test mode keys (
creem_test_*) for development and testing - Process all webhook events — don't just handle the happy path
- Send email notifications on subscription state changes
Anti-Patterns
- Granting access on checkout redirect without webhook verification
- Not storing Creem customer/subscription IDs
- Ignoring
past_dueevents — users lose access without warning - Using production keys in development
- Not handling cancellation gracefully — honor the remaining paid period
Install this skill directly: skilldb add payment-services-skills
Related Skills
Adyen
Accept payments with Adyen. Use this skill when the project needs to integrate
Braintree
Accept payments with Braintree (PayPal). Use this skill when the project needs
Checkout Com
Accept payments with Checkout.com. Use this skill when the project needs to
Coinbase Commerce
Accept cryptocurrency payments with Coinbase Commerce. Use this skill when the
Klarna
Accept payments with Klarna. Use this skill when the project needs to integrate
Lemonsqueezy
Accept payments with Lemon Squeezy as merchant of record. Use this skill when