The LMS backend includes a Razorpay integration designed for INR payments in Indian markets. Unlike the Stripe flow, Razorpay does not use a hosted checkout page — the backend creates an order, your frontend loads the Razorpay checkout widget directly, and then your client sends the payment identifiers back to the server for HMAC signature verification. There is no webhook listener; the verification step is client-driven.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Pragyat-Nikunj/Learning-Management-System-backend/llms.txt
Use this file to discover all available pages before exploring further.
Environment variables
| Variable | Purpose |
|---|---|
RAZORPAY_KEY_ID | Public key — used in both the server SDK and the client-side Razorpay widget |
RAZORPAY_KEY_SECRET | Private key — used server-side only to sign and verify payments |
Payment flow
Create an order
The client sends a ResponseThe
POST request with courseId. The server looks up the course price, creates a Razorpay order priced in INR (converted to paise), persists a CoursePurchase record with status pending, and returns the order object.amount field is in paise (1 INR = 100 paise). A course priced at ₹49 produces amount: 4900.Open the Razorpay checkout widget
Load the Razorpay checkout script in your frontend and open the widget using the order details returned in the previous step.
The
handler callback only fires when Razorpay considers the payment attempt successful on its side. You must still verify the signature on your server before treating the purchase as confirmed.Verify payment signature
After the widget calls your Success responseFailure response (signature mismatch)On success the server sets the
handler, send the three Razorpay identifiers to the server for HMAC verification. The server reconstructs the expected signature using RAZORPAY_KEY_SECRET and compares it against the one Razorpay provided.CoursePurchase status to completed. On failure it returns a 400 without modifying the record.Signature verification internals
The server verifies payments by recomputing the HMAC-SHA256 signature and doing a direct string comparison:pending.
Purchase status lifecycle
The
failed and refunded statuses exist in the CoursePurchase model but are not set by the Razorpay controller. There is no automatic status update if the user closes the widget without paying — those records remain pending indefinitely.Current limitations
No webhook listener
No webhook listener
The Razorpay integration uses client-driven verification only. There is no server-side webhook endpoint to receive asynchronous Razorpay events (e.g. delayed bank transfers, refunds). If you need server-side event handling, you must implement a webhook handler and register it in the Razorpay dashboard.
CoursePurchase record missing currency and paymentMethod
CoursePurchase record missing currency and paymentMethod
The
createRazorpayOrder controller creates the CoursePurchase document without setting the currency or paymentMethod fields, both of which are marked required in the schema. Depending on your Mongoose validation configuration this may cause a validation error at save time. The Stripe controller sets both fields correctly; the Razorpay controller does not. This is a known gap in the current implementation.INR only
INR only
The Razorpay order is always created with
currency: "INR". There is no mechanism to override the currency from the client request.