razorpay-subscription-kit
v0.1.0
Published
Backend-first TypeScript billing infrastructure for Razorpay subscriptions
Maintainers
Readme
razorpay-subscription-kit
Backend-first TypeScript utilities for Razorpay subscription orchestration. This package focuses on deterministic plan transitions, webhook ingestion with idempotency, and adapter-friendly persistence ports.
For the full roadmap and architecture, see docs/PRD.md.
Install
npm install razorpay-subscription-kit zodOptional peer tooling (recommended by the PRD): bullmq, pino.
Requirements
Node.js 18.17+ is required.
Quick start
1) Configure plans + adapters
Plans are modeled as ordered tiers (planOrder) plus a capability map (features). Your database remains the source of truth—implement SubscriptionStore for production.
Use MemorySubscriptionStore while prototyping.
Webhooks (Express)
Razorpay webhooks must be verified against the raw request body (string/bytes) before JSON parsing.
Minimal Express setup:
import express from "express";
import {
MemoryIdempotencyStore,
processRazorpaySubscriptionWebhook,
// store: your SubscriptionStore implementation
} from "razorpay-subscription-kit";
const app = express();
const idempotency = new MemoryIdempotencyStore(); // replace with Redis/DB in production
app.post("/webhooks/razorpay", express.raw({ type: "*/*" }), async (req, res) => {
const rawBody = req.body.toString("utf8");
const signatureHeader = req.header("x-razorpay-signature");
await processRazorpaySubscriptionWebhook({
rawBody,
signatureHeader,
webhookSecret: process.env.RZP_WEBHOOK_SECRET!,
idempotency,
store, // your SubscriptionStore
});
res.json({ ok: true });
});Prisma adapter (Postgres)
This package includes a zero-dependency Prisma adapter wrapper: PrismaSubscriptionStore. It does not import @prisma/client; you pass your Prisma client instance.
Example Prisma model (suggested):
model Subscription {
userId String @id
planId String
razorpaySubscriptionId String @unique
status String
currentPeriodEnd DateTime?
scheduledChange Json?
previousSubscription Json?
}Migration snippet (from your app repo):
npm i -D prisma
npm i @prisma/client
npx prisma init
# add the model above into `prisma/schema.prisma`
npx prisma migrate dev -n init_billingUsage:
import { PrismaClient } from "@prisma/client";
import { PrismaSubscriptionStore } from "razorpay-subscription-kit";
const prisma = new PrismaClient();
const store = new PrismaSubscriptionStore(prisma);2) Create a billing engine
import {
BillingEngine,
RazorpayProvider,
MemorySubscriptionStore,
UpgradeStrategies,
} from "razorpay-subscription-kit";
const store = new MemorySubscriptionStore();
const billing = new BillingEngine({
planOrder: ["basic", "pro"],
plans: {
basic: { features: { reports: false } },
pro: { features: { reports: true } },
},
provider: new RazorpayProvider({ keyId: process.env.RZP_KEY!, keySecret: process.env.RZP_SECRET! }),
store,
defaultUpgradeStrategy: UpgradeStrategies.DELAYED_CANCEL,
});3) Create / change / inspect
await billing.createSubscription({
userId: "user_123",
customerId: "cust_123",
planId: "basic",
});
await billing.changePlan({
userId: "user_123",
customerId: "cust_123",
newPlanId: "pro",
});
const snapshot = await billing.getSubscription("user_123");
const reports = billing.canAccess(snapshot?.planId, "reports");
await billing.validateActiveSubscription("user_123");Upgrade strategies mirror the PRD:
IMMEDIATE_CANCEL: cancel the old Razorpay subscription immediately, create the new subscription.CANCEL_AT_CYCLE_END: mark the old subscription to cancel at period end via Razorpay, create the new subscription, and persistpreviousSubscriptionmetadata.DELAYED_CANCEL(default): create the new subscription first, persistpreviousSubscription, and return{ delayedCancelPrevious }for your BullMQ worker to finalize cancellation.
Webhook helpers verify signatures (verifyRazorpayWebhookSignature / processRazorpaySubscriptionWebhook) against the raw request body before parsing JSON.
License
MIT — see LICENSE when published.
