微信支付Native支付对接实战:Go语言实现与踩坑记录

2025-05-26T00:00:00Z | 3分钟阅读 | 更新于 2025-05-26T00:00:00Z

@

最近在项目中对接微信支付,服务端逻辑实现使用的是我最熟悉的Go语言。

按照惯例,优先寻找微信支付的官方SDK for Go,结果发现官方SDK最后版本v0.2.20,发布时间Aug 20,2024。并且在github上SDK的Issues里,发现有不少问题都还是Open状态。

https://github.com/wechatpay-apiv3/wechatpay-go

因为是新项目,又有AI的加持,所以我就斗胆决定跳过官方SDK,直接从零开始实现微信支付的对接。期间有些踩坑,逐一解决后记录一下。

目前主要对接微信支付的Native支付(后续H5支付审核通过后,再做H5支付的对接),Native支付中要求必须带有“支付成功回调URL”,而遇到的问题也都出在接收支付成功回调的接口上。

问题1 Nonce大小写

微信支付的POST回调请求头中包含以下信息:

参数 说明
Wechatpay-Serial 验签的微信支付平台证书序列号/微信支付公钥ID
Wechatpay-Signature 验签的签名值
Wechatpay-Timestamp 验签的时间戳
Wechatpay-Nonce 验签的随机字符串

其中 WeChatpay-Nonce是一条字符串,我收到的Nonce字母都是大写。实际在微信支付验签时,希望Nonce是小写

在微信支付的文档中并没有相关说明,我也是对照示例,发现示例中打印出的Nonce全是小写,才发现了这个问题。

问题2 Body体需要原始数据

现在实现Restful API,一般都会选用一些框架,比如我使用的是gin。这些框架默认会将Body体转换为JSON等格式,供后续程序处理使用。

但是微信支付回调在验签处理时,需要保持Body数据原始不变,不但是数据结构,甚至连顺序都不能改变。

所以在接收微信支付回调的代码中,第一步需要先把原始body数据保存下来。相关代码如下:

func WechatPaymentNotify(c *gin.Context) {
	body, err := io.ReadAll(c.Request.Body)
	if err != nil {
		log.Printf("读取请求体失败: %v", err)
		c.JSON(http.StatusBadRequest, gin.H{"code": "FAIL", "message": "读取请求失败"})
		return
	}
	// 恢复请求体
	c.Request.Body = io.NopCloser(bytes.NewBuffer(body))

    //其他代码
}

问题3 验签名串

微信支付回调的验签,需要将请求头中的信息,以及Body体中的信息,拼接成一个字符串,然后进行验签。格式如下:

<Wechatpay-Timestamp>\n<Wechatpay-Nonce>\n<body>\n

我最初分别使用Claude 3.5和 Google Gemini 2.5 pro,都生成了类似的代码:


message := fmt.Sprintf("%s\n%s\n%s\n%s\n", timestamp, nonce, string(body),"")

在解决掉前面Nonce大小写的问题后,回调的验签始终还是无法通过。反复调试无果后,我回到微信支付文档,才发现在“2.2开始构造验签串”这一节的第2点,明确说明: “签名串共有三行,行尾以\n结束,包括最后一行。若应答报文主体为空,最后一行仅为一个\n。”

所以,正确的代码应该是


message := fmt.Sprintf("%s\n%s\n%s\n", timestamp, nonce, string(body))

在AI辅助编程中,这样的情况已经出现过好几次。某些隐藏在角落里,不起眼的一点小问题,会让我花费大量的时间去排查调试。

comments powered by Disqus

© 2020 - 2025 Dank's Blog - 发现问题,分享解决.

Powered by Dank

🇨🇳 中文简体
关于我

20多年,依然在写代码的开发者。

  • 2000年参与第一波互联网,太年轻没赚到钱
  • 2004年最早淘宝电商,自建管理系统,20多个加盟商,第一批皇冠店铺
  • 2009年AWS早期用户,云计算技术开发布道
  • 2014年Docker 1.0发布,尝试容器集群运营工具开发
  • 2024年再出发,AI应用EatEase开发者。