Implementing a secure payment gateway is crucial for any e-commerce or SaaS application. In this comprehensive guide, we'll walk through integrating Razorpay with Next.js, focusing on security best practices and implementation details.
Razorpay offers several advantages for Indian businesses and international merchants:
Start by signing up for a Razorpay account and completing the verification process. Once verified, you'll get access to your API keys from the Dashboard.
Install the Razorpay checkout package:
npm install razorpay
Create a .env.local file in your Next.js project root and add your Razorpay API keys:
RAZORPAY_KEY_ID=your_key_id
RAZORPAY_KEY_SECRET=your_key_secret
Let's implement a complete payment flow with Razorpay and Next.js:
// pages/api/create-order.js
import Razorpay from 'razorpay';
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ message: 'Method not allowed' });
}
try {
const razorpay = new Razorpay({
key_id: process.env.RAZORPAY_KEY_ID,
key_secret: process.env.RAZORPAY_KEY_SECRET,
});
const { amount, currency = 'INR', receipt, notes } = req.body;
const options = {
amount: amount * 100, // Razorpay expects amount in paise
currency,
receipt,
notes,
};
const order = await razorpay.orders.create(options);
return res.status(200).json(order);
} catch (error) {
console.error(error);
return res.status(500).json({ message: 'Something went wrong', error: error.message });
}
}
// components/PaymentButton.js
import { useState } from 'react';
import Script from 'next/script';
export default function PaymentButton({ amount, productName, customerName, customerEmail }) {
const [loading, setLoading] = useState(false);
const makePayment = async () => {
setLoading(true);
try {
// Create order on the server
const response = await fetch('/api/create-order', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount,
receipt: 'order_' + Date.now(),
notes: {
productName,
customerName,
customerEmail,
},
}),
});
const order = await response.json();
if (!response.ok) {
throw new Error(order.message || 'Failed to create order');
}
// Initialize Razorpay payment
const options = {
key: process.env.NEXT_PUBLIC_RAZORPAY_KEY_ID,
amount: order.amount,
currency: order.currency,
name: 'Your Company Name',
description: productName,
order_id: order.id,
handler: function (response) {
// Handle successful payment
verifyPayment(response);
},
prefill: {
name: customerName,
email: customerEmail,
},
theme: {
color: '#3399cc',
},
};
const paymentObject = new window.Razorpay(options);
paymentObject.open();
} catch (error) {
console.error('Payment error:', error);
alert('Payment failed: ' + error.message);
} finally {
setLoading(false);
}
};
const verifyPayment = async (paymentResponse) => {
try {
const response = await fetch('/api/verify-payment', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(paymentResponse),
});
const data = await response.json();
if (response.ok) {
alert('Payment successful!');
// Redirect to success page or update UI
} else {
throw new Error(data.message || 'Payment verification failed');
}
} catch (error) {
console.error('Verification error:', error);
alert('Payment verification failed: ' + error.message);
}
};
return (
<>
>
);
}
// pages/api/verify-payment.js
import crypto from 'crypto';
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ message: 'Method not allowed' });
}
try {
const { razorpay_order_id, razorpay_payment_id, razorpay_signature } = req.body;
// Verify the payment signature
const generatedSignature = crypto
.createHmac('sha256', process.env.RAZORPAY_KEY_SECRET)
.update(razorpay_order_id + '|' + razorpay_payment_id)
.digest('hex');
if (generatedSignature === razorpay_signature) {
// Payment is legitimate
// Update your database, mark order as paid, etc.
return res.status(200).json({
verified: true,
message: 'Payment verified successfully',
});
} else {
// Payment signature verification failed
return res.status(400).json({
verified: false,
message: 'Payment verification failed',
});
}
} catch (error) {
console.error(error);
return res.status(500).json({
verified: false,
message: 'Server error during verification',
error: error.message,
});
}
}
// pages/checkout.js
import { useState } from 'react';
import PaymentButton from '../components/PaymentButton';
export default function Checkout() {
const [formData, setFormData] = useState({
name: '',
email: '',
});
const handleChange = (e) => {
setFormData({
...formData,
[e.target.name]: e.target.value,
});
};
// Example product details
const product = {
name: 'Premium Subscription',
price: 999,
};
return (
Checkout
Order Summary
{product.name}
₹{product.price}
Total
₹{product.price}
Customer Information
);
}
When implementing Razorpay or any payment gateway, security should be your top priority:
Always create orders on the server-side to prevent tampering with the amount or currency.
Always verify payment signatures on your server before confirming orders or providing access to paid content.
Never expose your Razorpay secret key in client-side code. Use environment variables and ensure they're properly secured.
Ensure your website uses HTTPS to encrypt data transmission between your users and your server.
Validate all user inputs on both client and server sides to prevent injection attacks.
Implement proper error handling without exposing sensitive information in error messages.
Razorpay can send webhooks to notify your application about payment events. Here's how to implement webhook handling:
// pages/api/razorpay-webhook.js
import crypto from 'crypto';
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ message: 'Method not allowed' });
}
try {
// Verify webhook signature
const webhookSecret = process.env.RAZORPAY_WEBHOOK_SECRET;
const signature = req.headers['x-razorpay-signature'];
const shasum = crypto.createHmac('sha256', webhookSecret);
shasum.update(JSON.stringify(req.body));
const digest = shasum.digest('hex');
if (digest !== signature) {
return res.status(400).json({ message: 'Invalid webhook signature' });
}
// Process the webhook event
const event = req.body;
switch (event.event) {
case 'payment.authorized':
// Handle payment authorization
await handlePaymentAuthorized(event.payload.payment.entity);
break;
case 'payment.failed':
// Handle payment failure
await handlePaymentFailed(event.payload.payment.entity);
break;
case 'refund.created':
// Handle refund creation
await handleRefundCreated(event.payload.refund.entity);
break;
// Handle other events as needed
}
return res.status(200).json({ received: true });
} catch (error) {
console.error('Webhook error:', error);
return res.status(500).json({ message: 'Webhook processing failed' });
}
}
async function handlePaymentAuthorized(payment) {
// Update order status in your database
// Send confirmation email to customer
// Update inventory, etc.
}
async function handlePaymentFailed(payment) {
// Update order status
// Notify customer about failed payment
}
async function handleRefundCreated(refund) {
// Update order with refund information
// Notify customer about refund
}
Razorpay provides a test mode for development and testing:
If your application serves international customers, consider these additional steps:
Integrating Razorpay with Next.js provides a secure and efficient payment solution for your web applications. By following the security best practices outlined in this guide, you can ensure a safe payment experience for your users while protecting your business from fraud and security issues.
Remember that payment security is an ongoing process. Stay updated with the latest security practices and regularly audit your implementation to maintain a secure payment system.
Learn how to integrate ImageKit with Next.js for optimized image delivery, transformations, and improved performance.
Troubleshooting and fixing hydration errors in Next.js applications to ensure consistent rendering between server and client.
With Create React App's deprecation, explore modern alternatives like Vite, Next.js, and Remix for your React projects.
Get the latest articles and project management insights delivered to your inbox.