A Next.js SaaS starter template with MONEI Payments — Cards, Bizum, PayPal, Apple Pay, Google Pay & more.
Forked from nextjs/saas-starter — Stripe has been fully replaced with MONEI, Europe's multi-method payment gateway.
- Marketing landing page (
/) with animated Terminal element - Pricing page (
/pricing) with locally-configured plans via MONEI hosted checkout - Dashboard with subscription management portal (change plan, update payment method, cancel)
- Recurring billing via payment tokenization + Vercel Cron
- Trial period support (configurable per plan)
- Basic RBAC with Owner and Member roles
- Email/password authentication with JWTs stored to cookies
- Webhook signature verification via
@monei-js/node-sdk - GitHub Actions CI (type check + build on every PR)
- Activity logging system for any user events
- Framework: Next.js
- Database: Postgres
- ORM: Drizzle
- Payments: MONEI via
@monei-js/node-sdk - UI Library: shadcn/ui
git clone https://github.com/MONEI/nextjs-saas-starter-monei
cd nextjs-saas-starter-monei
pnpm installUse the included setup script to create your .env file:
pnpm db:setupYou'll need your MONEI API Key and Account ID from the MONEI Dashboard. Use test mode keys for development.
Run the database migrations and seed the database with a default user and team:
pnpm db:migrate
pnpm db:seedThis will create the following user and team:
- User:
test@test.com - Password:
admin123
You can also create new users through the /sign-up route.
Finally, run the Next.js development server:
pnpm devOpen http://localhost:3000 in your browser to see the app in action.
This template uses MONEI's hosted payment page for the initial checkout flow:
- User selects a plan → backend creates a MONEI payment with
generatePaymentToken: true - User is redirected to MONEI's hosted page to pay (Cards, Bizum, PayPal, etc.)
- After payment, MONEI redirects back and sends a webhook to confirm the status
- A
paymentTokenis saved for future recurring charges - A Vercel Cron job (
/api/cron/billing) runs daily and charges teams whosecurrentPeriodEndhas passed
Plans are defined locally in lib/payments/monei.ts — no external product catalog required.
The dashboard includes a full in-app subscription portal:
- Update Payment Method — redirects to MONEI hosted page with
transactionType: VERIF(zero-amount) to capture a new token - Change Plan — charges the new plan amount using the stored token, or redirects to checkout if no token exists
- Cancel Subscription — clears the stored token and sets status to
canceled
Each plan has a configurable trialDays value. When a new subscription is created, trialEndsAt and currentPeriodEnd are set accordingly. The cron job won't charge until the period expires.
Use MONEI's test mode with these test card details:
- Card Number:
4111 1111 1111 1111 - Expiration: Any future date
- CVC: Any 3-digit number
For Bizum testing, see the MONEI testing docs.
- Go to the MONEI Dashboard → Settings → Webhooks
- Add your production callback URL:
https://yourdomain.com/api/monei/webhook - MONEI will send payment status updates to this endpoint
- Push your code to a GitHub repository
- Connect your repository to Vercel and deploy it
- The Vercel Cron job for recurring billing is configured automatically via
vercel.json
In your Vercel project settings, add:
| Variable | Description |
|---|---|
BASE_URL |
Your production domain (e.g., https://yourdomain.com) |
MONEI_API_KEY |
Your live MONEI API key |
MONEI_ACCOUNT_ID |
Your MONEI Account ID |
POSTGRES_URL |
Your production database URL |
AUTH_SECRET |
A random string (openssl rand -base64 32) |
CRON_SECRET |
A random string to secure the billing cron endpoint |
DEMO_MODE |
Set to true for live demo with auto-refund (optional) |
NEXT_PUBLIC_DEMO_MODE |
Set to true to show demo banner in UI (optional) |
lib/payments/monei.ts → MONEI SDK client, plan config, all payment operations
lib/payments/actions.ts → Server Actions (checkout, cancel, change plan, update PM)
app/api/monei/webhook/ → Webhook handler with signature verification
app/api/monei/callback/ → Post-payment redirect handler
app/api/cron/billing/ → Vercel Cron for recurring charges
lib/db/schema.ts → Drizzle schema (teams with MONEI fields)
| Feature | Original (Stripe) | This Fork (MONEI) |
|---|---|---|
| Checkout | Stripe Checkout Sessions | MONEI Hosted Payment Page |
| Subscriptions | Stripe Subscriptions API | Payment tokenization + cron billing |
| Customer Portal | Stripe Customer Portal | In-app portal (change plan, update payment, cancel) |
| Webhook | stripe-signature HMAC |
MONEI-Signature via @monei-js/node-sdk |
| Payment methods | Cards only | Cards, Bizum, PayPal, Apple Pay, Google Pay |
| Currency | USD | EUR (configurable) |
| Products/Prices | Stripe Dashboard | Local config in lib/payments/monei.ts |
| Trial periods | Stripe-managed | App-managed with trialEndsAt column |
| Recurring billing | Stripe automatic | Vercel Cron + stored payment tokens |
| CI | None | GitHub Actions (type check + build) |
To deploy a live demo (like saas-starter.monei.com) where visitors can experience the full payment flow without actually being charged:
DEMO_MODE=true
NEXT_PUBLIC_DEMO_MODE=true
MONEI_API_KEY=pk_live_*** # Use LIVE keys for real payment methodsWhen enabled:
- An orange banner appears at the top: "Live Demo — All payments are automatically refunded"
- Every successful payment is immediately refunded via
monei.payments.refund() - The subscription is still activated so the full dashboard experience works
- Recurring billing cron is disabled (no charge-refund loops)
- Visitors can test with real Cards, Bizum, PayPal, Apple Pay, Google Pay
This is powerful for showcasing MONEI's capabilities with real payment method flows.
MIT