A production-ready, plug-and-play NPM package that provides complete subscription management for SaaS applications. Built with TypeScript, Express, PostgreSQL, and Prisma ORM.
- ✅ Zero-config deployment: Install, configure, and run on a separate port
- ✅ Multi-tenant ready: Automatic tenant isolation with shared database
- ✅ Payment integration: DoDo Payments adapter with extensible system
- ✅ Complete API: REST endpoints + programmatic access
- ✅ Session management: Checkout sessions with automatic cleanup
- ✅ User management: User creation, billing status, payment history
- ✅ Subscription lifecycle: Active, on-hold, failed, cancelled, expired states
- ✅ Plan changes: Upgrades/downgrades with pending change tracking
- ✅ Production features: Webhooks, rate limiting, CORS, error handling
- ✅ Type-safe: Full TypeScript support with comprehensive types
- ✅ Bring-your-own-auth: Integrates with existing authentication systems
npm install keycard-subscription-backendimport{createSubscriptionBackend}from'keycard-subscription-backend';constbackend=awaitcreateSubscriptionBackend({port: 4000,database: {url: process.env.DATABASE_URL,},payment: {provider: 'dodo_payments',config: {apiKey: process.env.DODO_PAYMENTS_API_KEY,environment: process.env.DODO_PAYMENTS_ENVIRONMENT,webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_KEY,},},auth: {validateRequest: async(req)=>{consttoken=req.headers.authorization?.replace('Bearer ','');constuser=awaityourAuthService.verify(token);return{userId: user.id,tenantId: user.tenantId,isValid: !!user,};},},});console.log('Subscription backend running on port 4000');importexpressfrom'express';import{createSubscriptionBackend}from'keycard-subscription-backend';constapp=express();// Your existing routesapp.get('/',(req,res)=>res.send('Main app'));// Initialize subscription backend (runs on separate port)constsubscriptionBackend=awaitcreateSubscriptionBackend({port: 4000,// ... configuration});// Your app runs on port 3000, subscriptions on port 4000app.listen(3000,()=>console.log('Main app on port 3000'));- Create PostgreSQL database:
CREATEDATABASEsubscriptions;- Set environment variables (all required):
DATABASE_URL=postgresql://user:password@localhost:5432/subscriptionsDODO_PAYMENTS_API_KEY=your_dodo_payments_api_keyDODO_PAYMENTS_ENVIRONMENT=test_modeDODO_PAYMENTS_WEBHOOK_KEY=your_webhook_keyCHECKOUT_RETURN_URL=http://localhost:3000VITE_TEST_TIER_MAPPING='{"default":"free"}'VITE_PROD_TIER_MAPPING='{"default":"free"}'- Run migrations:
npx prisma migrate deployconstconfig={port: 4000,database: {url: process.env.DATABASE_URL,// OR individual parametershost: 'localhost',port: 5432,database: 'subscriptions',username: 'postgres',password: 'password',ssl: false,poolSize: 10,},payment: {provider: 'dodo_payments',config: {apiKey: process.env.DODO_PAYMENTS_API_KEY,environment: process.env.DODO_PAYMENTS_ENVIRONMENT,webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_KEY,},},auth: {validateRequest: async(req)=>{// Your authentication logicreturn{userId: 'user_123',tenantId: 'tenant_abc',isValid: true,};},},// Optional featuresfeatures: {autoMigration: true,webhooks: true,},cors: {origin: ['http://localhost:3000'],credentials: true,},rateLimit: {windowMs: 15*60*1000,// 15 minutesmax: 100,// requests per window},sessionCleanup: {enabled: true,sessionTimeoutMs: 30*60*1000,// 30 minutescleanupIntervalMs: 5*60*1000,// 5 minutesverbose: false,},};The system uses 3 main tables:
userUuid(Primary Key)email(Unique)dodoCustomerIdsubscriptionId(Active subscription from DoDo)activeTier,activeLengthtierExpiresAtsubscriptionStatus(ACTIVE, ON_HOLD, FAILED, CANCELLED, EXPIRED, GRACE)- Plan change tracking fields for upgrades/downgrades
- Checkout session management
- Links to user and payments
- Status tracking (PENDING, COMPLETED, FAILED, EXPIRED)
- Automatic cleanup of expired sessions
- Payment records from DoDo Payments
- Status tracking with comprehensive states
- Links to users and sessions
- Raw JSON storage for webhook data
All endpoints available at http://localhost:4000/api/v1
POST /user
{
"email": "user@example.com"
}GET /user/:email/billingResponse:
{
"activeTier": "PRO",
"activeLength": "MONTHLY",
"tierExpiresAt": "2024-02-01T00:00:00.000Z",
"subscriptionStatus": "ACTIVE",
"pendingChange": {
"tier": "BASIC",
"activeLength": "MONTHLY",
"effectiveDate": "2024-02-01T00:00:00.000Z",
"changeType": "downgrade"
},
"latestPayment": {
"status": "COMPLETED",
"paidAt": "2024-01-01T12:00:00.000Z",
"amountCents": 2999,
"currency": "USD",
"tier": "PRO"
}
}GET /user/:email/payments?limit=10&offset=0GET /user/:email/sessions?limit=10&offset=0POST /dodopayments/checkout
{
"email": "user@example.com",
"product_id": "prod_123",
"quantity": 1,
"return_url": "https://yourapp.com/success"
}POST /dodopayments/webhook
# Automatically processes DoDo Payments webhooksGET /usersGET /user/:emailGET /health- User Creation: POST
/usercreates user with FREE tier - Checkout: POST
/dodopayments/checkoutcreates payment session - Payment: User completes payment on DoDo Payments
- Webhook: System receives webhook and updates user tier
- Billing Check: GET
/user/:email/billingreturns current status
- ACTIVE: Subscription is active and paid
- ON_HOLD: Payment failed, user has grace period
- FAILED: Payment failed permanently
- CANCELLED: User cancelled subscription
- EXPIRED: Subscription expired
- GRACE: In grace period after payment failure
The system supports plan upgrades/downgrades with pending change tracking:
- Immediate upgrades (take effect immediately)
- Scheduled downgrades (take effect at next billing cycle)
- Frequency changes (monthly ↔ yearly)
The system processes these DoDo Payments webhook events:
payment.completed- Payment successfulpayment.failed- Payment failedsubscription.created- New subscriptionsubscription.updated- Subscription modifiedsubscription.cancelled- Subscription cancelledsubscription.expired- Subscription expired
// Automatic webhook signature verification// Updates user tier and subscription status// Handles payment state transitions// Manages subscription lifecyclegit clone <repository>cd KeyCard
npm install
cp .env.example .env
# Edit .env with your values
npm run prisma:generate
npm run prisma:migratenpm run dev
# Server runs on http://localhost:4000npm test# Run tests
npm run test:watch # Watch mode
npm run test:coverage # Coverage reportnpm run prisma:studio # GUI at http://localhost:5555
npm run prisma:migrate # Run migrationsimport{createSubscriptionBackend}from'keycard-subscription-backend';// 1. Initialize backendconstbackend=awaitcreateSubscriptionBackend({port: 4000,database: {url: process.env.DATABASE_URL},payment: {provider: 'dodo_payments',config: {apiKey: process.env.DODO_PAYMENTS_API_KEY,environment: process.env.DODO_PAYMENTS_ENVIRONMENT,webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_KEY,},},auth: {validateRequest: async(req)=>{// Your auth logicreturn{userId: 'user_123',tenantId: 'tenant_abc',isValid: true};},},});// 2. Create userconstcreateUser=async(email: string)=>{constresponse=awaitfetch('http://localhost:4000/api/v1/user',{method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({ email }),});returnresponse.json();};// 3. Create checkout sessionconstcreateCheckout=async(email: string,productId: string)=>{constresponse=awaitfetch('http://localhost:4000/api/v1/dodopayments/checkout',{method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({
email,product_id: productId,quantity: 1,return_url: 'https://yourapp.com/success',}),});returnresponse.json();};// 4. Check billing statusconstgetBillingStatus=async(email: string)=>{constresponse=awaitfetch(`http://localhost:4000/api/v1/user/${email}/billing`);returnresponse.json();};// Usageconstuser=awaitcreateUser('user@example.com');constcheckout=awaitcreateCheckout('user@example.com','prod_pro_monthly');// User completes payment...constbilling=awaitgetBillingStatus('user@example.com');console.log('User tier:',billing.activeTier);// React component exampleconstSubscriptionButton=({ email, productId })=>{consthandleSubscribe=async()=>{try{// Create checkout sessionconstresponse=awaitfetch('/api/v1/dodopayments/checkout',{method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({
email,product_id: productId,quantity: 1,return_url: window.location.origin+'/success',}),});const{ checkout_url }=awaitresponse.json();// Redirect to DoDo Paymentswindow.location.href=checkout_url;}catch(error){console.error('Subscription failed:',error);}};return<buttononClick={handleSubscribe}>Subscribe Now</button>;};- SQL Injection Protection: Prisma ORM with parameterized queries
- Webhook Verification: HMAC SHA256 signature verification
- Rate Limiting: Configurable request limits
- CORS: Cross-origin request protection
- Authentication: Bring-your-own-auth integration
- Tenant Isolation: Multi-tenant data separation
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
RUN npx prisma generate
EXPOSE 4000
CMD ["node", "dist/server.js"]# DatabaseDATABASE_URL=postgresql://user:password@localhost:5432/subscriptions# DoDo PaymentsDODO_PAYMENTS_API_KEY=your_dodo_payments_api_keyDODO_PAYMENTS_ENVIRONMENT=test_modeDODO_PAYMENTS_WEBHOOK_KEY=your_webhook_key# ApplicationCHECKOUT_RETURN_URL=http://localhost:3000# Tier Mapping (JSON format)VITE_TEST_TIER_MAPPING='{"default":"free"}'VITE_PROD_TIER_MAPPING='{"default":"free"}'# OptionalPORT=4000NODE_ENV=production- Set up PostgreSQL database
- Configure environment variables
- Run database migrations
- Set up DoDo Payments webhooks
- Configure authentication
- Set up monitoring and logging
- Configure CORS for your domain
- Set up SSL/TLS
- Fork the repository
- Create feature branch:
git checkout -b feature/amazing-feature - Commit changes:
git commit -m 'Add amazing feature' - Push to branch:
git push origin feature/amazing-feature - Open Pull Request
- Write tests for new features
- Follow TypeScript best practices
- Update documentation
- Ensure backward compatibility
MIT License - see LICENSE file for details.
- Documentation: Check this README and inline code comments
- Issues: Open an issue on GitHub with detailed reproduction steps
- Discussions: Use GitHub Discussions for questions and ideas
- Additional payment providers (Stripe, PayPal)
- Subscription analytics dashboard
- Email notification templates
- Customer portal UI components
- GraphQL API support
- Advanced dunning management
- Revenue recognition reports
- Discount codes and promotions
Built with ❤️ for SaaS developers who want to ship fast
KeyCard Subscription Backend - Production-ready subscription management in minutes, not months.