Skip to main content

Overview

SkillRise integrates Razorpay for payment processing with a robust webhook-based enrollment system. The architecture ensures reliable course enrollment even when frontend verification fails.

Payment Architecture

The webhook serves as a reliable fallback mechanism, ensuring enrollment completes even if the frontend verification fails due to network issues or browser closure.

Purchase Model

Purchase Schema

server/models/Purchase.js

Purchase Status Flow

1

created

Initial state when purchase record is created before Razorpay order.
2

pending

Razorpay order created, waiting for payment completion.
3

completed

Payment verified, user enrolled in course.
4

failed

Payment failed or was cancelled by user.
5

refunded

Payment was refunded to the user.

Razorpay Service

Create Order

Generate a Razorpay order with purchase metadata:
server/services/payments/razorpay.service.js
The purchaseId is stored in Razorpay’s notes field, allowing the webhook to identify which internal purchase to complete.

Verify Payment Signature

Verify payment authenticity using HMAC-SHA256:
server/services/payments/razorpay.service.js
Always use crypto.timingSafeEqual() for signature comparison to prevent timing attacks.

Purchase Completion Service

Idempotent Enrollment

The core enrollment logic is idempotent and centralized:
server/services/payments/order.service.js
This function is the single source of truth for enrollment. It’s called from both the frontend verification endpoint and the webhook handler.
The $addToSet operator ensures that calling this function multiple times won’t create duplicate enrollments.

Webhook Handler

Razorpay Webhook Verification

Securely handle Razorpay payment capture webhooks:
server/controllers/webhooks.js
1

Signature Verification

Verify the webhook signature using the raw body before parsing JSON.
2

Event Filtering

Only process payment.captured events to avoid handling incomplete payments.
3

Purchase ID Extraction

Extract the internal purchase ID from Razorpay’s notes field.
4

Complete Enrollment

Call the idempotent completePurchase function to enroll the user.
Critical: Verify the webhook signature on the raw body before parsing JSON. Parsing before verification will cause signature mismatch.

Express Middleware Configuration

The webhook endpoint requires special body parsing:
server/server.js
The raw body parser must be applied before the JSON parser to preserve the original request body for signature verification.

Environment Variables

.env

Payment Flow States

Purchase record created with status: 'created'. Razorpay order generated and checkout modal opened.
User completes payment on Razorpay. Status updated to pending while awaiting capture.
Frontend receives payment success and calls verify endpoint. If successful, enrollment completes immediately.
Razorpay sends payment.captured webhook. If frontend verification failed, webhook ensures enrollment completes.
User added to course’s enrolledStudents array and course added to user’s enrolledCourses array.

Security Features

HMAC Signature Verification

All webhook requests are verified using HMAC-SHA256 signatures.

Timing-Safe Comparison

Signature comparison uses timing-safe functions to prevent timing attacks.

Idempotent Operations

Purchase completion can be called multiple times safely without duplicate enrollments.

Raw Body Verification

Webhook signatures are verified on raw body to prevent tampering.

Error Handling

If payment fails, the purchase status remains created or changes to failed. User is not enrolled.

Best Practices

Dual Verification Path: Implement both frontend verification and webhook handling to ensure enrollment reliability.
Store Metadata in Notes: Use Razorpay’s notes field to store internal IDs that survive through their system.
Centralize Enrollment Logic: Keep enrollment logic in a single idempotent function called by both verification paths.

Next Steps

Course Management

Understand the course structure that users purchase

Analytics

Track purchase conversions and revenue

Authentication

Secure user accounts and payment authorization