LogoTanStarter Docs
LogoTanStarter Docs
HomepageIntroductionCodebaseGetting StartedEnvironments
Configuration
Deployment

Integrations

CloudflareDatabaseAuthenticationEmailNewsletterStorage
Payment
NotificationsAnalyticsChatboxAffiliates

Customization

MetadataPagesLanding PageBlogComponentsUser ManagementAPI Key Management

Codebase

Project StructureFormatting & LintingEditor SetupUpdating the Codebase
X (Twitter)

Creem

How to set up and use Creem for payments and subscriptions

TanStarter uses Creem for payment processing, supporting both one-time payments and subscriptions. Creem is a Merchant of Record (MoR) payment platform designed for indie developers, with built-in global tax compliance across 190+ countries — no need to handle VAT/GST/sales tax yourself.

Setup

TanStarter template provides three pricing plans by default: a free plan, a pro subscription plan (monthly/yearly), and a lifetime plan (one-time payment). Follow these steps to set up:

Create Creem Account

Create a Creem account at creem.io. No credit card required to sign up.

Get API Keys

Get your API keys from the Creem Dashboard:

  • Go to Creem Dashboard > Developers
  • Copy the API key (Note: test mode starts with creem_test_, production mode starts with creem_live_)
  • Save it to your environment file as CREEM_API_KEY

Set Up Webhook

Set up Webhook and get your Webhook secret:

  • Go to Creem Dashboard > Developers > Webhooks
  • Add Webhook URL: https://YOUR-DOMAIN.com/api/webhooks/creem
  • Creem automatically listens for all payment and subscription related events, including:
    • checkout.completed
    • subscription.active
    • subscription.paid
    • subscription.canceled
    • subscription.expired
    • subscription.past_due
    • subscription.paused
    • subscription.trialing
  • Copy the Webhook signing secret
  • Save it to your environment file as CREEM_WEBHOOK_SECRET

Create Products and Pricing Plans

Create products in Creem and set up pricing plans:

  • Go to Creem Dashboard > Products
  • Create the Pro subscription plan product:
    • Click to create a product
    • Name: Pro Plan
    • Description: Premium features with subscription pricing
    • Add monthly price:
      • Price: $9.90 (currency: USD)
      • Billing type: recurring
      • Billing period: every-month
      • Save and copy the Product ID (starts with prod_), this will be used for VITE_CREEM_PRODUCT_PRO_MONTHLY
    • Add yearly price (create a separate product):
      • Price: $99.00 (currency: USD)
      • Billing type: recurring
      • Billing period: every-year
      • Save and copy the Product ID (starts with prod_), this will be used for VITE_CREEM_PRODUCT_PRO_YEARLY
  • Create the Lifetime plan product:
    • Click to create a product
    • Name: Lifetime Plan
    • Description: One-time payment for lifetime access
    • Add price:
      • Price: $199.00 (currency: USD)
      • Billing type: one_time
      • Save and copy the Product ID (starts with prod_), this will be used for VITE_CREEM_PRODUCT_LIFETIME

Add Environment Variables

Add the following environment variables:

.env
# Payment provider
VITE_PAYMENT_PROVIDER=creem

CREEM_API_KEY=creem_test_...
CREEM_WEBHOOK_SECRET=...

# Set to 'true' to use Creem test API, omit or set to 'false' for production
# CREEM_DEBUG=true

# Product IDs
VITE_CREEM_PRODUCT_PRO_MONTHLY=prod_...
VITE_CREEM_PRODUCT_PRO_YEARLY=prod_...
VITE_CREEM_PRODUCT_LIFETIME=prod_...

Update Website Configuration

Update the payment section in src/config/website.ts to configure pricing plans — amounts, currencies, intervals, and plan metadata. The enable, provider, and priceId fields are automatically resolved from your environment variables (VITE_PAYMENT_PROVIDER and VITE_CREEM_PRODUCT_*), so you don't need to hardcode them.

You must configure this section to match the products you created in Creem:

src/config/website.ts
payment: {
  enable: isPaymentEnabled,              // ← auto: true when VITE_PAYMENT_PROVIDER is set
  provider: isPaymentEnabled ? paymentProvider : undefined, // ← auto: 'creem'
  price: {
    plans: {
      free: {
        id: 'free',
        prices: [],
        isFree: true,
        isLifetime: false,
      },
      pro: {
        id: 'pro',
        prices: [
          {
            type: 'subscription',
            priceId: priceIds.proMonthly,  // ← auto: from VITE_CREEM_PRODUCT_PRO_MONTHLY
            amount: 990,                   // amount in cents ($9.90)
            currency: 'USD',
            interval: 'month',
          },
          {
            type: 'subscription',
            priceId: priceIds.proYearly,   // ← auto: from VITE_CREEM_PRODUCT_PRO_YEARLY
            amount: 9900,                  // amount in cents ($99.00)
            currency: 'USD',
            interval: 'year',
          },
        ],
        isFree: false,
        isLifetime: false,
        popular: true,
      },
      lifetime: {
        id: 'lifetime',
        prices: [
          {
            type: 'one_time',
            priceId: priceIds.lifetime,     // ← auto: from VITE_CREEM_PRODUCT_LIFETIME
            amount: 19900,                  // amount in cents ($199.00)
            currency: 'USD',
            allowPromotionCode: true,
          },
        ],
        isFree: false,
        isLifetime: true,
      },
    },
  },
},

If you are setting up your environment, you can now go back to the Environment Configuration and continue. The rest of this document can be read later.

Environment Configuration

Set up environment variables


Core Features

  • One-time payment for lifetime membership
  • Recurring subscription payments (monthly/yearly)
  • Free trial period support
  • Built-in global tax compliance (automatic VAT/GST/sales tax across 190+ countries)
  • Subscription management with customer portal integration
  • Webhook handling for payment events
  • Subscription status tracking and verification
  • Built-in pricing components (table, card, button)
  • Server-side actions for secure payment operations
  • Multiple pricing plan support (free, pro, lifetime)
  • Built-in affiliate program and revenue sharing
  • Automatic license key generation and distribution

Development

For local development, you need to use ngrok or a similar tool to expose your local server to the internet so Creem can send webhook events:

ngrok http 3000

Then add the ngrok HTTPS URL as the Webhook URL in the Creem Dashboard:

  • Go to Creem Dashboard > Developers > Webhooks
  • Add Webhook URL: https://YOUR-NGROK-URL/api/webhooks/creem

Finally, you can make payment operations on the website to test whether the event processing flow works as expected.

Creem provides a full test environment. When using API keys starting with creem_test_, all operations run in a sandbox and no real transactions are made.

Production

  1. Go to Creem Dashboard > Developers > Webhooks
  2. Add the production Webhook URL: https://YOUR-DOMAIN.com/api/webhooks/creem
  3. Switch the API key from test key (creem_test_) to production key (creem_live_)
  4. Make sure your environment variables are updated with production keys and product IDs

Webhook Events

Creem supports the following webhook events:

EventDescription
checkout.completedCheckout session completed
subscription.activeNew subscription created (for sync only)
subscription.paidSubscription payment successful (use to activate access)
subscription.updateSubscription updated (plan change, period renewal)
subscription.canceledSubscription canceled
subscription.scheduled_cancelSubscription marked to cancel at end of billing period
subscription.past_duePayment failed, subscription awaiting retry
subscription.expiredBilling period ended without payment
subscription.trialingSubscription entered trial period
subscription.pausedSubscription paused
refund.createdMerchant created a refund
dispute.createdCustomer initiated a dispute (chargeback)

Creem recommends using the subscription.paid event to activate user access, rather than subscription.active. The subscription.active event should only be used for data synchronization.

Webhook Signature Verification

Creem uses the HMAC-SHA256 algorithm to sign webhook requests. The signature is sent via the creem-signature request header.

TanStarter uses Web Crypto API for signature verification (compatible with Cloudflare Workers):

src/payment/provider/creem.ts
async function verifySignature(payload: string, signature: string, secret: string): Promise<boolean> {
  const encoder = new TextEncoder();
  const key = await crypto.subtle.importKey(
    'raw',
    encoder.encode(secret),
    { name: 'HMAC', hash: 'SHA-256' },
    false,
    ['sign']
  );

  const signatureBuffer = await crypto.subtle.sign('HMAC', key, encoder.encode(payload));
  const computed = Array.from(new Uint8Array(signatureBuffer))
    .map((b) => b.toString(16).padStart(2, '0'))
    .join('');

  return computed === signature;
}

Webhook Retry Mechanism

If a webhook delivery fails, Creem automatically retries with the following intervals:

  • First retry after 30 seconds
  • Second retry after 1 minute
  • Third retry after 5 minutes
  • Fourth retry after 1 hour

You can also manually resend events from the Developers section in the Creem Dashboard.

Customer Portal

After every successful payment, your customers will receive an email with a link to their Customer Portal. The portal allows them to:

  • View subscription details
  • Manage subscriptions (upgrade, pause, cancel)
  • View payment history
  • Update payment methods

Test Cards

To test Creem integration, use Creem's test mode and test credit cards:

  • 4242 4242 4242 4242 - Successful payment
  • 4000 0000 0000 0002 - Payment failure

When using test mode (API keys starting with creem_test_), all transactions run in a sandbox environment with no real charges.

Creem vs Stripe

FeatureCreemStripe
RoleMerchant of Record (MoR)Payment Gateway
Tax ComplianceAutomatic global tax handlingManual configuration required
Fees3.9% + $0.402.9% + $0.30
Refund HandlingCreem handles on your behalfSelf-managed
Setup DifficultySimple, ideal for indie devsFull-featured, flexible config
WeChat/AlipayNot supportedSupported

Best Practices

  1. Protect API keys: Never expose your Creem API key in client-side code
  2. Validate webhook signatures: Always verify the creem-signature of webhook events
  3. Use subscription.paid event: Use this event to activate user access, not subscription.active
  4. Handle errors gracefully: Provide user-friendly error messages when payments fail
  5. Test webhooks thoroughly: Ensure all webhook events are handled correctly

References

  • Creem Documentation
  • Creem API Reference
  • Creem Webhooks Guide

Table of Contents

Setup
Create Creem Account
Get API Keys
Set Up Webhook
Create Products and Pricing Plans
Add Environment Variables
Update Website Configuration
Core Features
Development
Production
Webhook Events
Webhook Signature Verification
Webhook Retry Mechanism
Customer Portal
Test Cards
Creem vs Stripe
Best Practices
References