Next.js项目集成Razorpay支付网关签名验证及头信息获取问题
Razorpay支付网关签名验证解决方案
问题说明
现有代码可正常运行,但需为Razorpay支付网关添加签名验证功能,当前存在两个核心问题:
- 无法正确获取
x-razorpay-signature请求头 - 交易完成后控制台能生成
razorpay_payment_id、razorpay_order_id、razorpay_signature三个值,但不知道如何在代码中获取并验证
修正后的代码
1. Webhook签名验证修正版
import crypto from 'crypto' import prisma from '@/utils/connect' import { NextResponse } from 'next/server' // 建议从环境变量读取密钥,避免硬编码 const RAZORPAY_WEBHOOK_SECRET = process.env.RAZORPAY_WEBHOOK_SECRET || 'oBtdfiQn3POvk0BtxYCUC6FY' export const POST = async (req) => { try { // 先获取原始请求体文本(签名验证必须用原始内容) const rawBody = await req.text() // 解析为JSON对象用于业务逻辑处理 const body = JSON.parse(rawBody) const { event, payload } = body // 正确获取请求头:Next.js的req.headers是Headers对象,需用get方法 const signature = req.headers.get('x-razorpay-signature') console.log('收到的签名:', signature) // 生成验证签名:基于原始请求体和webhook密钥 const generatedSignature = crypto .createHmac('sha256', RAZORPAY_WEBHOOK_SECRET) .update(rawBody) .digest('hex') console.log('生成的签名:', generatedSignature) // 签名校验不通过直接返回错误 if (signature !== generatedSignature) { console.log('签名验证失败!') return new NextResponse('Invalid Signature', { status: 403 }) } console.log('请求验证通过') switch (event) { case 'payment.authorized': break case 'payment.captured': const donationId = payload.payment.entity.notes.donationId if (donationId) { await prisma.donation.update({ where: { id: donationId }, data: { status: 'Success' } }) } else { console.error('Razorpay webhook payload中缺少或无效的donationId') return new NextResponse('Invalid or missing donationId', { status: 400 }) } break default: console.log('未处理的事件:', event) } return new NextResponse('Webhook Received', { status: 200 }) } catch (error) { console.error('处理webhook出错:', error) return new NextResponse(JSON.stringify({ message: '处理失败!' }), { status: 500 }) } }
2. 前端回调验证三个值的接口(可选)
如果需要处理前端传来的razorpay_payment_id、razorpay_order_id、razorpay_signature,可新增如下接口:
import crypto from 'crypto' import { NextResponse } from 'next/server' // 从环境变量读取Razorpay密钥 const RAZORPAY_KEY_SECRET = process.env.RAZORPAY_KEY_SECRET || '你的Razorpay密钥' export const POST = async (req) => { try { const { razorpay_order_id, razorpay_payment_id, razorpay_signature } = await req.json() // 生成验证签名 const generatedSignature = crypto .createHmac('sha256', RAZORPAY_KEY_SECRET) .update(`${razorpay_order_id}|${razorpay_payment_id}`) .digest('hex') // 签名对比 if (generatedSignature !== razorpay_signature) { return new NextResponse('支付签名验证失败', { status: 403 }) } // 验证通过后的业务逻辑,比如更新订单状态等 console.log('支付验证通过') return new NextResponse('支付验证成功', { status: 200 }) } catch (error) { console.error('验证支付签名出错:', error) return new NextResponse(JSON.stringify({ message: '验证失败!' }), { status: 500 }) } }
关键修改说明
- 请求头获取:Next.js的
req.headers是标准Headers对象,必须使用get('header-name')方法获取,不能直接用对象属性访问 - Webhook签名验证:必须使用原始请求体(
req.text())生成签名,因为Razorpay的签名是基于未修改的原始JSON字符串计算的,解析后再序列化可能改变格式(如空格、引号位置),导致签名不匹配 - 密钥安全:不要硬编码密钥,建议放在环境变量中,避免泄露风险
- 前端回调处理:前端支付成功后会从Razorpay回调中拿到三个值,需将它们传给后端,后端用Razorpay密钥生成签名并对比,完成支付合法性验证
内容的提问来源于stack exchange,提问作者Commander Abhishek
相关产品推荐
相关产品推荐

