Facebook Pixel

Testing and Debugging Razorpay Payments in Node.js Test Mode and Error Handling

Learn how to test Razorpay payments in Node.js using test mode, test cards, debugging common errors, and testing webhooks locally.

Testing and Debugging Razorpay Payments

Thoroughly testing your payment integration before going live is essential. This guide covers test mode, debugging, and testing webhooks.

Razorpay Test Mode

Test mode allows full testing without real money:

// Test mode keys start with rzp_test_ const razorpay = new Razorpay({ key_id: "rzp_test_XXXXXXXXXX", key_secret: "xxxxxxxxxxxxxxxxxxxxxxxx" });

Test Cards

Card TypeNumberResult
Visa success4111 1111 1111 1111Success
Mastercard success5104 1111 1111 1111Success
Visa failure4111 1111 1111 1112Failure
3D auth required4111 1111 1111 1111Requires OTP
Invalid CVVAny card + wrong CVVFailure

Expiry: Any future date (e.g., 12/30) CVV: Any 3 digits (e.g., 123)

Test UPI

UPI ID: success@razorpay  → Success
UPI ID: failure@razorpay  → Failure

Testing Webhooks Locally

Razorpay webhooks need a public URL. Use ngrok for local testing:

# Install ngrok npm install -g ngrok # Expose your local server ngrok http 3000 # You get a public URL: # https://abc123.ngrok.io # Set this as your webhook URL in Razorpay Dashboard: # https://abc123.ngrok.io/api/payment/webhook

Testing the Complete Flow

// test/payment.test.js const request = require("supertest"); const app = require("../src/app"); const razorpay = require("../src/config/razorpay"); describe("Payment Flow", () => { let orderId; it("should create an order", async () => { const res = await request(app) .post("/api/payment/create-order") .set("Cookie", `token=${testToken}`) .send({ plan: "premium" }); expect(res.status).toBe(200); expect(res.body.orderId).toBeDefined(); orderId = res.body.orderId; }); it("should verify a payment", async () => { // Simulate a test payment const payment = await razorpay.payments.createTestPayment({ order_id: orderId, method: "card", card: { number: "4111111111111111", expiry_month: "12", expiry_year: "30", cvv: "123" } }); const res = await request(app) .post("/api/payment/verify") .send({ orderId: orderId, paymentId: payment.id, signature: "test_signature" // In test mode, signatures are different }); expect(res.status).toBe(200); expect(res.body.success).toBe(true); }); });

Common Errors and Solutions

1. Invalid API Key

Error: The key_id and key_secret don't match

Solution: Check that you're using the correct key pair from the Razorpay dashboard. Test keys start with rzp_test_, live keys with rzp_live_.

2. Amount Mismatch

Error: The amount doesn't match the order amount

Solution: Ensure the frontend sends the exact order amount. Don't modify the amount after order creation. Amounts are in paise (₹500 = 50000).

3. Order Already Paid

Error: The order has already been paid

Solution: Each order can only be paid once. Create a new order for retry. Don't reuse order IDs.

4. Webhook Not Received

Solutions:

  • Check the webhook URL is HTTPS (not HTTP)
  • Verify the URL in the Razorpay dashboard
  • Use ngrok for local testing
  • Check that the webhook secret matches
  • View webhook delivery logs in the dashboard

5. Signature Verification Failed

// Debug signature verification console.log("Expected:", expectedSignature); console.log("Received:", signature); console.log("Body:", req.body.toString());

Common causes:

  • Using express.json() instead of express.raw() for webhooks
  • Different webhook secret in .env vs dashboard
  • Body parsing modifies the raw content

Debugging Checklist

  • Using test mode keys (rzp_test_)
  • Test cards working (4111 1111 1111 1111)
  • Webhook URL is HTTPS
  • Using express.raw() for webhook route
  • Webhook secret matches dashboard
  • ngrok running for local webhook testing
  • Amounts in paise (not rupees)
  • Order ID, payment ID, and signature all present
  • No typos in API keys
  • Logs show webhook events

The Takeaway

Testing Razorpay involves: using test mode (rzp_test_ keys), test cards (4111 1111 1111 1111 for success), test UPI IDs (success@razorpay), ngrok for local webhook testing, and debugging common errors (invalid keys, amount mismatch, order already paid, webhook not received, signature verification failed). Always test the complete flow in test mode before switching to live mode.

Use test mode with rzp_test_ keys. Use test card 4111 1111 1111 1111 (Visa) with any future expiry and any CVV for success. For UPI, use success@razorpay. No real money is charged. Test all payment methods, failures, and webhooks before going live.

Use ngrok to expose your local server: ngrok http 3000. You get a public HTTPS URL. Set this URL as the webhook URL in the Razorpay dashboard. Now Razorpay can send webhooks to your local server for testing.

Common causes: using express.json() instead of express.raw() for the webhook route (the signature is on the raw body), different webhook secret in .env vs the dashboard, or body parsing modifying the content. Use express.raw({ type: 'application/json' }) for the webhook route.

Invalid API key (check test vs live keys), amount mismatch (ensure frontend sends exact order amount in paise), order already paid (create a new order for retry), webhook not received (check HTTPS URL, use ngrok for local), signature verification failed (use express.raw, check webhook secret).

Visa success: 4111 1111 1111 1111. Mastercard success: 5104 1111 1111 1111. Visa failure: 4111 1111 1111 1112. Use any future expiry date and any 3-digit CVV. For UPI, use success@razorpay (success) or failure@razorpay (failure).

Ready to master Node.js completely?

Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.

Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.