Skip to content

Repository files navigation

KeyCard Subscription Backend

A production-ready, plug-and-play NPM package that provides complete subscription management for SaaS applications. Built with TypeScript, Express, PostgreSQL, and Prisma ORM.

🚀 Features

  • 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

📦 Installation

npm install keycard-subscription-backend

🏃‍♂️ Quick Start

Option 1: Standalone Server

import{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');

Option 2: Integrated with Express App

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'));

⚙️ Configuration

Database Setup

  1. Create PostgreSQL database:
CREATEDATABASEsubscriptions;
  1. 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"}'
  1. Run migrations:
npx prisma migrate deploy

Complete Configuration Options

constconfig={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,},};

🗄️ Database Schema

The system uses 3 main tables:

UserMapping

  • userUuid (Primary Key)
  • email (Unique)
  • dodoCustomerId
  • subscriptionId (Active subscription from DoDo)
  • activeTier, activeLength
  • tierExpiresAt
  • subscriptionStatus (ACTIVE, ON_HOLD, FAILED, CANCELLED, EXPIRED, GRACE)
  • Plan change tracking fields for upgrades/downgrades

Session

  • Checkout session management
  • Links to user and payments
  • Status tracking (PENDING, COMPLETED, FAILED, EXPIRED)
  • Automatic cleanup of expired sessions

Payment

  • Payment records from DoDo Payments
  • Status tracking with comprehensive states
  • Links to users and sessions
  • Raw JSON storage for webhook data

🔌 API Endpoints

All endpoints available at http://localhost:4000/api/v1

User Management

Create/Get User

POST /user
{
"email": "user@example.com"
}

Get User Billing Status

GET /user/:email/billing

Response:

{
"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 Payment History

GET /user/:email/payments?limit=10&offset=0

Get Session History

GET /user/:email/sessions?limit=10&offset=0

DoDo Payments Integration

Create Checkout Session

POST /dodopayments/checkout
{
"email": "user@example.com",
"product_id": "prod_123",
"quantity": 1,
"return_url": "https://yourapp.com/success"
}

Handle Webhooks

POST /dodopayments/webhook
# Automatically processes DoDo Payments webhooks

Admin Operations

List All Users

GET /users

Get User by Email

GET /user/:email

Health Check

GET /health

🔄 Subscription Lifecycle

User Journey

  1. User Creation: POST /user creates user with FREE tier
  2. Checkout: POST /dodopayments/checkout creates payment session
  3. Payment: User completes payment on DoDo Payments
  4. Webhook: System receives webhook and updates user tier
  5. Billing Check: GET /user/:email/billing returns current status

Subscription States

  • 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

Plan Changes

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)

🎣 Webhooks

Supported Events

The system processes these DoDo Payments webhook events:

  • payment.completed - Payment successful
  • payment.failed - Payment failed
  • subscription.created - New subscription
  • subscription.updated - Subscription modified
  • subscription.cancelled - Subscription cancelled
  • subscription.expired - Subscription expired

Webhook Processing

// Automatic webhook signature verification// Updates user tier and subscription status// Handles payment state transitions// Manages subscription lifecycle

🛠️ Development

Setup

git clone <repository>cd KeyCard
npm install
cp .env.example .env
# Edit .env with your values
npm run prisma:generate
npm run prisma:migrate

Development Server

npm run dev
# Server runs on http://localhost:4000

Testing

npm test# Run tests
npm run test:watch # Watch mode
npm run test:coverage # Coverage report

Database Management

npm run prisma:studio # GUI at http://localhost:5555
npm run prisma:migrate # Run migrations

📊 Usage Examples

Complete Integration Example

import{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);

Frontend Integration

// 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>;};

🔒 Security

  • 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

🚀 Deployment

Docker

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"]

Environment Variables (All Required)

# 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

Production Checklist

  • 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

🤝 Contributing

  1. Fork the repository
  2. Create feature branch: git checkout -b feature/amazing-feature
  3. Commit changes: git commit -m 'Add amazing feature'
  4. Push to branch: git push origin feature/amazing-feature
  5. Open Pull Request

Development Guidelines

  • Write tests for new features
  • Follow TypeScript best practices
  • Update documentation
  • Ensure backward compatibility

📄 License

MIT License - see LICENSE file for details.

🆘 Support

  • 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

🗺️ Roadmap

  • 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.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages