revel 接收微信支付异步通知需手动读取 c.request.body 原始 json,立即返回 200 空响应;验签须校验 wechatpay-signature 等三个 header,失败返回 403;重复通知靠数据库唯一约束+事务内 select for update + 条件 update 防重;统一下单需构造 authorization 头并透传 payment 结构体。

Revel 框架怎么接收微信支付的异步通知
Revel 默认不自动解析 application/json 请求体,而微信支付 v3 的支付结果通知是纯 JSON 格式(无 form 表单、无 query),直接读 c.Params 或 c.Params.Get 会拿不到数据。
必须手动读取原始 body,并注意:微信要求应答 HTTP 状态码为 200 或 204,且响应体为空(不能返回 HTML 或 JSON);否则微信会按 15/15/30/180… 秒策略重试,最多持续 2 天。
- 在 Controller 方法里用
c.Request.Body读取原始字节流,别依赖c.Params - 读完后立即调用
c.Response.WriteHeader(200),再return nil—— 不要写任何响应内容 - 签名验证前先校验
Wechatpay-Signature、Wechatpay-Timestamp、Wechatpay-Nonce这三个 header 是否存在,缺一不可 - 微信通知可能重复送达,务必在验签通过后,先查本地订单状态:
if order.Status == "SUCCESS" { return nil }
Revel 怎么做微信支付 v3 签名验证
微信支付 v3 要求用平台证书 + APIv3 密钥对请求头和 body 做 SHA256 with RSA 签名比对。Revel 没有内置封装,得自己实现或引入第三方库(如 github.com/wechatpay-apiv3/wechatpay-go)。
关键点在于:微信通知的 body 是加密的(resource 字段),需先解密才能拿到真实支付结果;但解密前必须先完成签名验证,否则无法信任该 body。
- 不要跳过签名验证直接解密 ——
resource字段本身可被篡改,签名才是唯一可信入口 - 平台证书需从微信商户平台下载
apiclient_cert.pem(含私钥)和apiclient_key.pem,Revel 启动时加载进内存,避免每次请求都 IO 读取 - 验签失败时,必须返回
http.StatusForbidden(403)或http.StatusBadRequest(400),不能返回 200,否则微信会认为你“成功处理了假通知” - 推荐把验签逻辑抽成中间件(
revel.Interceptor),统一拦截所有以/notify/pay开头的请求
Revel 处理微信通知时怎么防并发与重复
微信不保证通知只发一次,同一笔订单可能在秒级内收到多条相同通知;Revel 的 Controller 默认是并发执行的,若不做控制,可能同时插入两条支付成功记录或触发两次发货。
最轻量且可靠的做法不是加分布式锁,而是靠数据库唯一约束 + 冂余状态判断。
- 订单表必须有
transaction_id(微信返回的transaction_id)字段,并建唯一索引 - 收到通知后,先用
SELECT ... FOR UPDATE查询该订单当前状态(仅限 MySQL/PostgreSQL),再判断是否已处理 - 更新订单状态时,用带条件的
UPDATE ... WHERE status = 'WAITING',返回影响行数,为 0 则说明已被其他请求处理过 - 避免在事务外做「先查再更」,否则竞态下仍可能重复 —— 必须把查、判、更包在同一个事务里
Revel 里怎么调用微信统一下单并返回 payment 字段
Revel 本身不封装微信 SDK,所以统一下单需用 Go 官方 HTTP 客户端或 wechatpay-go 发起 POST 请求到 https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi。
注意微信 v3 接口要求所有请求都带 Authorization header(含签名),且 body 必须是 JSON、Content-Type 固定为 application/json;Revel 默认渲染模板会覆盖 Content-Type,必须显式设置。
- 请求前确保已正确构造
Authorization头:格式为WECHATPAY2-SHA256-RSA2048 mchid="...",nonce_str="...",signature="...",timestamp="..." - 调用成功后,微信返回的 JSON 中
payment字段是给小程序端wx.requestPayment用的,它不是字符串,而是结构体,需原样透传(别 JSON Marshal 再 Unmarshal) - 不要在 Revel 的
RenderJSON里直接返回整个微信响应体 —— 微信返回的prepay_id等字段需提取后拼进payment,且timeStamp必须转成字符串(微信要求 10 位时间戳) - 如果用云开发或云托管,
notify_url应填 Revel 服务的公网地址 + 路径,例如https://yourdomain.com/api/wechat/notify,不能带查询参数
微信支付通知最常出问题的地方不在验签逻辑,而在「以为通知只来一次」和「把空响应当成失败」——前者导致资金重复处理,后者让微信疯狂重推。Revel 的无框架感是优势,也是陷阱:它不会帮你拦住未处理的 panic,也不会自动 commit 事务。每一步都要亲手落锁、判重、清 body、设状态码。











