A minimal Next.js demo showcasing how to accept one-time payments and subscriptions using Creem
- One-time product checkout (lifetime access)
- Subscription checkout (monthly billing)
- Webhook handler with signature verification
- Order persistence in a Neon PostgreSQL database via Drizzle ORM
- Payment success page
| Layer | Technology |
|---|---|
| Framework | Next.js 16 |
| Payments | Creem (@creem_io/nextjs) |
| Database | Neon (serverless PostgreSQL) |
| ORM | Drizzle ORM |
| Styling | Tailwind CSS v4 |
| Language | TypeScript |
app/
├── page.tsx # Landing page with checkout buttons
├── success/page.tsx # Post-payment success page
└── api/
├── checkout/route.ts # Creem checkout handler
└── webhook/creem/route.ts # Creem webhook receiver
db/
└── schema.ts # Drizzle ORM schema (orders table)
drizzle.config.ts # Drizzle Kit configuration
- Node.js 18+
- pnpm (
npm i -g pnpm) - A Creem account with at least one product created
- A Neon project (free tier works)
git clone https://github.com/your-username/creem-nextjs-demo.git
cd creem-nextjs-demo
pnpm installCopy the example env file and fill in your credentials:
cp .env.example .env| Variable | Description |
|---|---|
CREEM_API_KEY |
Your Creem API key (test or live). Found in Creem dashboard → API Keys. |
CREEM_WEBHOOK_SECRET |
Webhook signing secret. Found in Creem dashboard → Webhooks. |
DATABASE_URL |
Neon PostgreSQL connection string. Found in Neon console → Connection Details. |
pnpm drizzle-kit pushpnpm devOpen http://localhost:3000 to see the app.
Creem needs a publicly accessible URL to deliver webhook events. Use ngrok to expose your local server:
ngrok http 3000Then register the forwarding URL as a webhook endpoint in your Creem dashboard:
https://<your-ngrok-id>.ngrok-free.app/api/webhook/creem
| Event | Action |
|---|---|
checkout.completed |
Logs payment details (extend to grant access) |
subscription.paid |
Inserts a new order row into the database |
subscription.canceled |
Logs cancellation (extend to revoke access) |
CREATE TABLE "order" (
id SERIAL PRIMARY KEY,
customerId VARCHAR(255) NOT NULL,
productId VARCHAR(255) NOT NULL UNIQUE,
amount INTEGER NOT NULL,
status VARCHAR(50) NOT NULL
);MIT