How I Integrated Razorpay Into a Production Ecommerce Platform
A complete deep dive into integrating Razorpay with secure payment verification, order processing, inventory management, and production deployment.
By Uttam Thapa · · Payments
💳 Introduction
Payment systems are one of the most critical components of any ecommerce platform. While building Velvet Loop and Golden Leaf Knots, I needed a secure, reliable, and production-ready payment solution capable of handling real customer transactions.
📌 What This Guide Covers: Complete Razorpay integration – order creation, payment verification, inventory sync, duplicate protection, security measures, and production deployment.
After evaluating multiple options, I chose Razorpay and implemented a complete payment workflow that included order creation, payment verification, inventory synchronization, email notifications, and duplicate payment protection.
This article documents the entire integration process, challenges faced, security decisions, and lessons learned while deploying Razorpay in production.
🎯 Why I Chose Razorpay
The primary audience for both ecommerce platforms was based in India. Razorpay provides first-class support for Indian payment methods including:
UPI
Credit Cards
Debit Cards
Net Banking
Wallets
Compared to international payment providers, Razorpay offered a significantly better experience for Indian customers.
✅ Why Razorpay?
- • Localized checkout experience
- • Better Indian payment method support
- • Competitive pricing for Indian businesses
- • Excellent developer documentation
❌ Why Not Stripe?
- • International focus, not India-optimized
- • Fewer local payment method integrations
- • Higher fees for Indian transactions
📦 Why Not Cash On Delivery? COD introduces operational challenges: fake orders, inventory blocking, return-to-origin losses, and manual verification. Online payments automate order processing and improve reliability.
⚠️ The Initial Payment Flow (What Went Wrong)
My original payment architecture was extremely simple – and problematic:
🛒 Checkout
↓
📝 Create Order
↓
🔓 Open Razorpay
↓
✅ Payment Success
↓
💰 Mark Order Paid
🚨 Problems with This Approach
- • Orders could exist without successful payments
- • Inventory could be reduced unnecessarily
- • Duplicate orders could be generated
- • Frontend responses could potentially be manipulated
This forced a complete redesign of the payment workflow.
🔐 The Final Production Payment Flow
1️⃣ Customer Checkout → Enter shipping details
↓
2️⃣ Create Payment Session → Temporary session (PENDING)
↓
3️⃣ Create Razorpay Order → Generate order_id (INITIATED)
↓
4️⃣ Payment Collection → Customer completes payment
↓
5️⃣ Signature Verification → HMAC SHA256 validation
↓
6️⃣ Order Creation → Database transaction (COMPLETED)
↓
7️⃣ Inventory Update → Atomic stock reduction
↓
8️⃣ Email Notifications → Customer + Admin alerts
Step 1: Customer Checkout
The customer adds products to the cart and enters personal information including name, email, phone number, and delivery address.
Step 2: Create Payment Session
Instead of immediately creating an order, the backend creates a temporary PaymentSession record with status PENDING storing customer details, cart items, and total amount.
Step 3: Create Razorpay Order
The frontend calls POST /api/payments/create-order. The backend validates the session, verifies the amount, generates a Razorpay order, and updates session status to INITIATED.
Step 4: Payment Collection
The frontend opens the Razorpay Checkout modal. Razorpay returns razorpay_order_id, razorpay_payment_id, and razorpay_signature.
Step 5: Verification
The frontend sends the payment response to POST /api/payments/verify. The backend performs signature verification before processing the order.
🗄️ Database Design
📦 Product Table
- • id, name, description
- • price, stock, images
- • category, tags, slug
💳 PaymentSession Table
- • session_id (unique)
- • customer details (JSON)
- • cart items (JSON)
- • total_amount, status
- • razorpay_order_id
- • razorpay_payment_id
📋 Order Table
- • order_number (unique)
- • customer information
- • payment details
- • order_status, items
- • total_amount
Payment Session Statuses
PENDING
INITIATED
COMPLETED
FAILED
This approach prevents data pollution and improves transaction reliability.
🔒 Secure Payment Verification
Payment verification is the most important security layer of the entire system.
// Backend Signature Verification
const generatedSignature = crypto
.createHmac('sha256', process.env.RAZORPAY_SECRET)
.update(`${order_id}|${payment_id}`)
.digest('hex');
const isValid = generatedSignature === razorpay_signature;
if (!isValid) {
throw new Error('Invalid payment signature');
}
✅ Security Guarantee: Only matching signatures are considered valid. This ensures successful payments cannot be spoofed from the frontend. The frontend is never trusted for critical payment information.
Complete Security Measures
✓ Server-side amount validation
✓ Session validation
✓ Signature verification
✓ Duplicate payment protection
✓ Database transactions
✓ Inventory consistency checks
🐛 The Duplicate Payment Bug
One of the most frustrating issues occurred during payment verification.
🚨 Problem
Users could potentially trigger the verification endpoint multiple times, causing:
- • Duplicate Orders
- • Multiple Inventory Deductions
Root Cause: The verification endpoint was not idempotent.
✅ Final Solution
Before creating an order, check whether an order already exists using the Razorpay Payment ID:
Find Order By Payment ID
↓
Exists? → Yes → Return Existing Order → Skip Creation
↓
No → Create New Order
This completely eliminated duplicate order creation.
📦 Inventory Synchronization
🔄 Key Design Decision: Inventory updates occur only after successful payment verification. Failed payments never affect stock levels.
Prisma database transactions ensure all operations execute as a single atomic unit:
- Order Creation
- Inventory Update
- Payment Session Completion
📧 Email Notifications
Automated notifications are triggered after successful payment verification.
📨 Customer Emails
- • Order Confirmation
- • Payment Success
- • Purchase Summary
🔔 Admin Emails
- • New Order Alerts
- • Customer Details
- • Order Information
The implementation uses Nodemailer with Gmail SMTP.
🚀 Deployment Challenges
Render
- • Cold starts affecting webhook response times
- • Health monitoring endpoint:
/health
Vercel
- • Environment variable synchronization
- • Build-time vs runtime configuration
📧 Email Infrastructure Evolution: The project initially used Resend but later migrated to Gmail SMTP due to domain verification limitations.
Razorpay Production Deployment: Managing test and production credentials required careful environment separation with different API keys for development, staging, and production.
📚 Key Lessons Learned
🔒 Never trust frontend payment data
✅ Verification is mandatory – always
🔄 Idempotency is critical for APIs
📦 Inventory updates after payment, not before
🗄️ Transactions protect data consistency
🛡️ Production payment systems need multiple validation layers
🔮 Future Improvements
Webhook-based verification
Inventory locking
Payment audit logs
Advanced monitoring
Retry mechanisms
Refund handling
🎯 Conclusion
💳
Integrating Razorpay was far more than simply collecting payments. It required careful attention to security, verification, database consistency, deployment challenges, and user experience.
✅ The final implementation powers production ecommerce workflows across Velvet Loop and Golden Leaf Knots – ensuring reliable transactions, accurate inventory management, and secure order processing.
Frequently asked questions
Is it safe to verify a Razorpay payment on the frontend?
No. The browser can be modified, so any success signal it reports is untrusted input. Verify the signature on your server using your secret key, and only then create the order and decrement stock.
What happens if a customer closes the browser after paying?
The payment succeeds and the client-side callback never runs. This is why webhooks matter: the gateway notifies your server independently of the browser, so the order completes even when nobody is watching. Treat the webhook as the source of truth and the callback as an optimisation.
How do you test a payment integration without real money?
Use the gateway's test mode with its published test cards, and drive the failure paths deliberately — declined cards, timeouts, duplicate webhooks. The success path is the easy one; the refunds and retries are where production bugs live.
Home · Projects · Blog · Services · Résumé · Contact