Razorpay Integration
Complete Razorpay payment integration — fee collection, plan subscriptions, webhook handling
Overview
OptiCRM uses Razorpay for two payment flows:
- Fee Collection - Schools collect fees from parents via online payment
- Plan Subscriptions - Schools pay for their OptiCRM subscription
Fee Collection Architecture
Hybrid Payment Model
Each institution chooses one of three modes:
| Mode | How It Works | Money Flow |
|---|---|---|
| OWN_RAZORPAY | Institution enters own API keys | Direct to school's bank |
| PLATFORM_LINKED | Onboards as Razorpay Route sub-merchant | Auto-split with commission |
| GATEWAY_NONE | Online payments disabled | Cash/cheque only |
Payment Flow
- Parent opens
/portal/feesand clicks "Pay Now" - Frontend calls
POST /api/portal/paymentswithaction: create-order - Server resolves institution's gateway config
- Creates Razorpay order via API
- Returns
orderIdandkeyto frontend - Frontend opens Razorpay Checkout modal
- Parent completes payment (card/UPI/netbanking)
- Frontend calls
POST /api/portal/paymentswithaction: verify - Server verifies HMAC-SHA256 signature
- On success: Payment marked SUCCESS, Invoice updated, Receipt generated, GL journal entries created
API: Create Order
POST /api/portal/payments
Content-Type: application/json
{
"action": "create-order",
"invoiceId": "clx..."
}Response:
{
"data": {
"orderId": "order_xxx",
"amount": 2500000,
"key": "rzp_live_xxx"
}
}API: Verify Payment
POST /api/portal/payments
Content-Type: application/json
{
"action": "verify",
"orderId": "order_xxx",
"paymentId": "pay_xxx",
"signature": "hmac_signature_hex"
}Gateway Configuration
Configure own Razorpay:
PATCH /api/admin/payment-gateway
{ "action": "configure-own", "keyId": "rzp_live_xxx", "keySecret": "secret", "webhookSecret": "secret" }Create linked account:
PATCH /api/admin/payment-gateway
{ "action": "request-linked", "name": "...", "email": "...", "pan": "...", "bankAccount": {...} }Plan Subscriptions
Plans
| Plan | Monthly | Annual | Key Features |
|---|---|---|---|
| Free | INR 0 | INR 0 | 50 students, basic modules |
| Starter | INR 999 | INR 9,990 | 500 students, academics, HR |
| Professional | INR 2,999 | INR 29,990 | Unlimited, all modules, AI |
| Enterprise | INR 9,999 | INR 99,990 | Everything, API, white-label |
API: Get Plans
GET /api/plan/subscribeReturns current plan, trial status, and all available plans with pricing.
API: Upgrade
POST /api/plan/subscribe
{ "planName": "PROFESSIONAL", "billingCycle": "monthly" }If Razorpay configured: returns payment link URL. If not: activates directly.
API: Usage
GET /api/plan/usageReturns current usage vs plan limits for students, staff, objects, reports, emails, SMS.
Webhook
POST /api/webhooks/razorpay
X-Razorpay-Signature: hmac_hex
{ "event": "payment.captured", "payload": {...} }Handles:
payment.captured- Mark payment SUCCESS, update invoicepayment.failed- Mark payment FAILEDrefund.processed- Mark payment REFUNDED
Signature verified against both platform and institution webhook secrets.
Security
- API keys encrypted with AES-256-GCM in database
- Webhook signatures verified with HMAC-SHA256
- Payment verification idempotent via Redis (24h TTL)
- Each institution's keys are isolated
- Platform keys used only for PLATFORM_LINKED mode
Test Cards
| Card | Result |
|---|---|
| 4111 1111 1111 1111 | Success |
| 5267 3181 8797 5449 | Success (Mastercard) |
| 4000 0000 0000 0002 | Decline |
UPI: success@razorpay | OTP: 1234
Environment Variables
RAZORPAY_KEY_ID=rzp_test_xxx # Platform Razorpay key
RAZORPAY_KEY_SECRET=xxx # Platform secret
RAZORPAY_WEBHOOK_SECRET=xxx # Webhook signature secretInstitution-specific keys are stored encrypted in the Institution model.