You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 17:13:18