@qazuor/qzpay-cli
v1.1.0
Published
CLI for scaffolding QZPay billing setups
Maintainers
Readme
@qazuor/qzpay-cli
CLI for scaffolding QZPay billing setups. Generate production-ready billing code with interactive prompts.
Features
- Interactive project scaffolding with beautiful prompts
- Support for Stripe and MercadoPago payment providers
- Framework integrations: Hono, NestJS, or library-only
- Multiple billing models: subscriptions, one-time payments, usage-based, marketplace
- TypeScript-first with full type definitions
- Ready-to-run generated code
Installation
# Global installation
npm install -g @qazuor/qzpay-cli
# Or use directly with npx
npx @qazuor/qzpay-cli init
# Or with pnpm
pnpm dlx @qazuor/qzpay-cli initQuick Start
# Run the interactive setup
qzpay init
# Or specify output directory
qzpay init -d ./my-billingCommand Reference
qzpay init
Scaffold a complete QZPay billing setup with interactive prompts.
qzpay init [options]
Options:
-d, --dir <directory> Output directory (default: ".")
-y, --yes Skip prompts and use defaults
-h, --help Display helpInteractive Prompts
The CLI guides you through these configuration steps:
1. Project Info
◆ Project name
│ my-billing
│
◆ Output directory
│ ./billing
│
◆ Project description (optional)
│ Billing system for my SaaS2. Payment Provider
◆ Payment provider
│ ○ Stripe (Recommended for most use cases)
│ ○ MercadoPago (Best for Latin America)
│ ○ Both (Multi-provider setup)3. Storage Adapter
◆ Storage adapter
│ ○ Drizzle (PostgreSQL) - Production-ready
│ ○ In-memory - Development/testing only4. Framework
◆ Framework integration
│ ○ Hono - Fast, lightweight web framework
│ ○ NestJS - Enterprise Node.js framework
│ ○ Library only - No HTTP layer5. Features
◆ Features to include (multi-select)
│ ◼ Subscriptions - Recurring billing
│ ◻ One-time payments - Single purchases
│ ◻ Usage-based billing - Metered usage
│ ◻ Marketplace - Multi-vendor support
│ ◻ Add-ons - Subscription add-ons6. Plan Structure
◆ Use predefined plan structure?
│ Yes
│
◆ Plan structure
│ ○ Freemium (Free + Pro + Enterprise)
│ ○ Tiered (Basic + Professional + Agency)
│ ○ Usage-based (Starter + Growth + Business + Enterprise)Or define custom plans with name, display name, monthly price, and yearly price.
Generated Files
Based on your selections, the CLI generates:
Core Files (Always Generated)
| File | Description |
|------|-------------|
| qzpay.config.ts | Database connection, payment provider setup, QZPayBilling initialization, event listeners |
| .env.example | Template with all required environment variables |
| types.ts | TypeScript types: plan tiers, limits, pricing constants, customer interface |
| plans.ts | Plan initialization script with price creation |
| services.ts | Business logic: customer registration, subscriptions, payments |
Framework-Specific Files
Hono
| File | Description |
|------|-------------|
| routes.ts | API routes with middleware, billing routes, custom endpoints |
| webhooks.ts | Webhook handlers for payment events |
NestJS
| File | Description |
|------|-------------|
| billing.module.ts | Dynamic NestJS module with forRoot and forRootAsync |
| billing.service.ts | Injectable service wrapping QZPayBilling |
| webhooks.controller.ts | Controller for webhook endpoints |
| webhooks.ts | Webhook handler functions |
Example Output
Generated qzpay.config.ts
import { QZPayBilling } from '@qazuor/qzpay-core';
import { QZPayDrizzleStorageAdapter } from '@qazuor/qzpay-drizzle';
import { QZPayStripeAdapter } from '@qazuor/qzpay-stripe';
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import Stripe from 'stripe';
// Environment validation
function getEnvVar(name: string): string {
const value = process.env[name];
if (!value) throw new Error(`Missing: ${name}`);
return value;
}
// Database
const client = postgres(getEnvVar('DATABASE_URL'));
export const db = drizzle(client);
// Stripe
export const stripe = new Stripe(getEnvVar('STRIPE_SECRET_KEY'), {
apiVersion: '2024-12-18.acacia'
});
// Adapters
export const storageAdapter = new QZPayDrizzleStorageAdapter({ db, livemode: process.env.NODE_ENV === 'production' });
export const stripeAdapter = new QZPayStripeAdapter({ client: stripe, livemode: process.env.NODE_ENV === 'production' });
// Billing instance
export const billing = new QZPayBilling({
storage: storageAdapter,
provider: stripeAdapter,
livemode: process.env.NODE_ENV === 'production'
});
// Event listeners
billing.on('subscription.created', async (event) => {
console.log(`New subscription: ${event.data.id}`);
});
export default billing;Generated types.ts
// Plan tiers
export type MyBillingPlanTier = 'free' | 'pro' | 'enterprise';
// Customer interface
export interface MyBillingCustomer {
id: string;
email: string;
name: string;
planTier: MyBillingPlanTier;
createdAt: Date;
}
// Plan limits
export const MY_BILLING_PLAN_LIMITS: Record<MyBillingPlanTier, MyBillingPlanLimits> = {
free: { maxItems: 5, maxUsers: 1, features: { analytics: false } },
pro: { maxItems: 50, maxUsers: 10, features: { analytics: true } },
enterprise: { maxItems: -1, maxUsers: -1, features: { analytics: true } }
};
// Pricing (in cents)
export const MY_BILLING_PRICING = {
plans: {
free: { monthly: 0, yearly: 0 },
pro: { monthly: 1999, yearly: 19990 },
enterprise: { monthly: 9999, yearly: 99990 }
}
} as const;Complete Workflow Example
# 1. Create new billing project
qzpay init -d ./my-saas-billing
# 2. Navigate to directory
cd my-saas-billing
# 3. Copy environment template
cp .env.example .env
# 4. Edit .env with your credentials
# DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
# STRIPE_SECRET_KEY=sk_test_...
# STRIPE_WEBHOOK_SECRET=whsec_...
# 5. Install dependencies (in your project)
pnpm add @qazuor/qzpay-core @qazuor/qzpay-stripe @qazuor/qzpay-drizzle
pnpm add stripe drizzle-orm postgres
# 6. Run database migrations
pnpm drizzle-kit push
# 7. Initialize plans in Stripe
npx tsx plans.ts
# 8. Start the server (Hono)
npx tsx routes.ts
# Server running on http://localhost:3000
# Webhooks: POST http://localhost:3000/webhooks/stripeConfiguration by Use Case
SaaS with Subscriptions
Provider: Stripe
Storage: Drizzle
Framework: Hono
Features: Subscriptions, Add-ons
Plans: Freemium (Free + Pro + Enterprise)E-commerce in Latin America
Provider: MercadoPago
Storage: Drizzle
Framework: NestJS
Features: One-time payments
Plans: Custom (single product pricing)Usage-Based API
Provider: Stripe
Storage: Drizzle
Framework: Hono
Features: Subscriptions, Usage-based billing
Plans: Usage-based (Starter + Growth + Business + Enterprise)Multi-Vendor Marketplace
Provider: Both (Stripe for vendors, MercadoPago for buyers)
Storage: Drizzle
Framework: Hono
Features: Subscriptions, Marketplace
Plans: Tiered (for vendors)Troubleshooting
"Missing environment variable" error
Ensure all required variables are set in your .env file. Check .env.example for the complete list.
Webhook signature verification fails
- Make sure
STRIPE_WEBHOOK_SECRETmatches your Stripe dashboard - For local development, use Stripe CLI:
stripe listen --forward-to localhost:3000/webhooks/stripe
Plans not creating
- Verify your Stripe API key is valid
- Check if you're using test vs live keys
- Run
plans.tswithDEBUG=*for verbose output
TypeScript errors after generation
- Install all peer dependencies
- Run
pnpm typecheckto see specific errors - Ensure TypeScript version >= 5.0
Environment Variables Reference
| Variable | Required | Description |
|----------|----------|-------------|
| DATABASE_URL | Drizzle | PostgreSQL connection string |
| STRIPE_SECRET_KEY | Stripe | Stripe API secret key |
| STRIPE_WEBHOOK_SECRET | Stripe | Stripe webhook signing secret |
| STRIPE_PUBLISHABLE_KEY | Stripe | Stripe publishable key (frontend) |
| MERCADOPAGO_ACCESS_TOKEN | MercadoPago | MercadoPago access token |
| MERCADOPAGO_WEBHOOK_SECRET | MercadoPago | MercadoPago webhook secret |
| PORT | Hono/NestJS | Server port (default: 3000) |
| NODE_ENV | All | Environment: development/production |
| JWT_SECRET | Hono/NestJS | JWT signing secret |
API Reference
Generated Service Functions
// Customer management
registerCustomer({ email, name, planTier? }): Promise<Customer>
getCustomer(customerId): Promise<Customer | null>
getCustomerLimits(planTier): PlanLimits
// Subscriptions
subscribeToPlan(customerId, planTier, interval?): Promise<{ subscriptionId, checkoutUrl }>
changePlan(customerId, newTier, interval?): Promise<void>
cancelSubscription(customerId, immediately?): Promise<void>
// Add-ons (if enabled)
addAddOn(customerId, addOnKey): Promise<void>
removeAddOn(customerId, addOnKey): Promise<void>
// One-time (if enabled)
purchaseService(customerId, serviceKey): Promise<{ paymentId, checkoutUrl }>
// Usage tracking (if enabled)
trackUsage(customerId, metric, quantity): Promise<void>
checkUsageLimit(customerId, metric): Promise<{ allowed, used, limit }>
getUsageSummary(customerId): Promise<UsageSummary>
// Payments
getPaymentHistory(customerId): Promise<Payment[]>
getInvoices(customerId): Promise<Invoice[]>Related Packages
- @qazuor/qzpay-core - Core billing library
- @qazuor/qzpay-stripe - Stripe payment adapter
- @qazuor/qzpay-mercadopago - MercadoPago payment adapter
- @qazuor/qzpay-drizzle - Drizzle ORM storage adapter
- @qazuor/qzpay-hono - Hono framework integration
- @qazuor/qzpay-nestjs - NestJS framework integration
- @qazuor/qzpay-react - React hooks and components
License
MIT
