由于项目需要,对接了一下最新的apiv3版的微信支付,花了不少时间,为避免大家走弯路,把详细的对接过程给大家分析一下。

首先上官网开发者文档copy一下代码!由于我们这次接通的是小程序的支付,所以基本代码都在后端。

https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_2.shtml 小程序支付开发指引

整体的流程大致是 统一下单 — 支付通知

先上一段代码

/*Package core 微信支付api v3 go http-client 基础库,你可以使用它来创建一个client,并向微信支付发送http请求只需要你在初始化客户端的时候,传递credential以及validatorcredential用来生成http header中的authorization信息validator则用来校验回包是否被篡改如果http请求返回的err为nil,一般response.Body 都不为空,你可以尝试对其进行序列化请注意及时关闭response.Body注意:使用微信支付apiv3 go库需要引入相关的包,该示例代码必须引入的包名有以下信息"context""crypto/x509""fmt""io/ioutil""log""github.com/wechatpay-apiv3/wechatpay-go/core""github.com/wechatpay-apiv3/wechatpay-go/core/option""github.com/wechatpay-apiv3/wechatpay-go/utils"*/
func SetUp() (opt []option.ClientOption, err error) {//商户号mchID := ""//商户证书序列号mchCertSerialNumber := ""//商户私钥文件路径privateKeyPath := ""//平台证书文件路径wechatCertificatePath := ""// 加载商户私钥privateKey, err := utils.LoadPrivateKeyWithPath(privateKeyPath)if err != nil {log.Printf("load private err:%s", err.Error())return nil, err}// 加载微信支付平台证书wechatPayCertificate, err := utils.LoadCertificateWithPath(wechatCertificatePath)if err != nil {log.Printf("load certificate err:%s",err)return nil, err}//设置header头中authorization信息opts := []option.ClientOption{option.WithMerchant(mchID, mchCertSerialNumber, privateKey), // 设置商户相关配置option.WithWechatPay([]*x509.Certificate{wechatPayCertificate}), // 设置微信支付平台证书,用于校验回包信息用}return opts, nil
}

这段代码是为了统一下单发出请求做的前置准备,配置了一些必要的加密和签名参数,在发起请求前会需要签名和加密的数据包装好,具体的先不开,只解析其中需要配置的参数。

mchCertSerialNumber是商户证书序列号,登录微信商户平台去申请好证书,具体位置是 交易中心–api安全 然后右侧会有三个模块 从上到下 第一个就是申请证书,第二个是老版的,第三个是apiv3密码。证书申请的时候必须用window系统的,申请完毕之后就会有查看证书,点击查看证书,就会有商户证书序列号了。

privateKeyPath 商户私钥文件路径,wechatCertificatePath 平台证书文件路径

如图apiclient_key.pem是privateKeyPath,apiclient_cert.pem是wechatCertificatePath,另一个p12是windows平台上用的,我没接,就不细说了。

上一段代码 setup函数中 有这样一段代码:

//设置header头中authorization信息opts := []option.ClientOption{option.WithMerchant(mchID, mchCertSerialNumber, privateKey), // 设置商户相关配置option.WithWechatPay([]*x509.Certificate{wechatPayCertificate}), // 设置微信支付平台证书,用于校验回包信息用}

先做标记,待会说!

接着要发起请求了 上代码

func CreateOrder() {// 初始化客户端ctx := context.TODO()opts, err := SetUp() //第一段代码中的函数if err != nil {return}client, err := core.NewClient(ctx, opts...,)if err != nil{log.Printf("init client err:%s",err)return}//设置请求地址URL := "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"//设置请求信息,此处也可以使用结构体来进行请求mapInfo := map[string]interface{}{"mchid": "1900006XXX","out_trade_no": "1217752501201407033233368318","appid": "wxdace645e0bc2cXXX","description": "Image形象店-深圳腾大-QQ公仔","notify_url": "https://weixin.qq.com/","amount": map[string]interface{}{"total": 1,"currency": "CNY",},"payer": map[string]interface{}{"openid": "o4GgauInH_RCEdvrrNGrntXDuXXX",},}// 发起请求response, err := client.Post(ctx, URL, mapInfo)if err != nil{log.Printf("client post err:%s",err)return}// 校验回包内容是否有逻辑错误err = core.CheckResponse(response)if err != nil{log.Printf("check response err:%s",err)return}// 读取回包信息body, err := ioutil.ReadAll(response.Body)if err != nil{log.Printf("read response body err:%s",err)return}fmt.Println(string(body))
}

这是原版的代码,其中content.TODO()就是标准包里的包,可以按着第一段代码中注释的引入。代码基本无问题按着自己的逻辑修改即可。但是!!!!!会有报错

client post err:validate verify fail serial=4F601336BA663C5C17CE5A6CA05E80DC9E2DE5A5 request-id=08FCDF8D840610D80518EDAAB74C20F60328C867-0 err=no serial number:4F601336BA663C5C17CE5A6CA05E80DC9E2DE5A5 corresponding certificate

这个问题在微信给的官方包github中有解答,解决方案是:
setup函数中 我们单独拉出来了一段代码修改一下

//设置header头中authorization信息opts := []option.ClientOption{option.WithMerchant(mchID, mchCertSerialNumber, privateKey), // 设置商户相关配置option.WithWechatPay([]*x509.Certificate{wechatPayCertificate}), // 设置微信支付平台证书,用于校验回包信息用option.WithoutValidator(),//跳过证书的效验}

多出来一行代码option.WithoutValidator(),//跳过证书的效验
就可以拿出来prepay_id了
接着组装数据,进行签名
我们先看官方的文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_4.shtml 其中详细讲解了签名方式,但是没有给签名函数,文档中有个关键的话 (使用商户私钥对待签名串进行SHA256 with RSA签名)且同发起支付时的签名方式一致!!!!!!这就好说,在他的前置函数setup中一定有相关的函数可以用,我们来找找

option.WithMerchant(mchID, mchCertSerialNumber, privateKey),

这段中使用了私钥,肯定有加密 进去看看

// WithMerchant 通过商户号、商户证书序列号、私钥构建一个默认的credential的ClientOption,用于生成http request header 中authorization信息
func WithMerchant(mchID, certificateSerialNo string, privateKey *rsa.PrivateKey) ClientOption {credential := &credentials.WechatPayCredentials{Signer: &signers.Sha256WithRSASigner{PrivateKey: privateKey, MchCertificateSerialNo: certificateSerialNo},MchID:  mchID,}return withCredential{credential: credential}
}

进去Sha256WithRSASigner中看看去

package signersimport ("context""crypto/rsa""fmt""strings""github.com/wechatpay-apiv3/wechatpay-go/core/auth"
)// Sha256WithRSASigner Sha256WithRSA 签名器
type Sha256WithRSASigner struct {MchCertificateSerialNo string          // 商户证书序列号PrivateKey             *rsa.PrivateKey // 商户私钥
}// GetName 获取签名器的名称
func (s *Sha256WithRSASigner) GetName() string {return "SHA256withRSA"
}// 获取签名器的类型
func (s *Sha256WithRSASigner) GetType() string {return "PRIVATEKEY"
}// 获取签名器的版本
func (s *Sha256WithRSASigner) GetVersion() string {return "1.0"
}// 对信息使用Sha256WithRsa的方式进行签名
func (s *Sha256WithRSASigner) Sign(ctx context.Context, message string) (*auth.SignatureResult, error) {if s.PrivateKey == nil {return nil, fmt.Errorf("you must set privatekey to use Sha256WithRSASigner")}if strings.TrimSpace(s.MchCertificateSerialNo) == "" {return nil, fmt.Errorf("you must set mch certificate serial no to use Sha256WithRSASigner")}signature, err := Sha256WithRsa(message, s.PrivateKey)if err != nil {return nil, err}return &auth.SignatureResult{MchCertificateSerialNo: s.MchCertificateSerialNo, Signature: signature}, nil
}

好了 这么快就找到签名函数了 signature, err := Sha256WithRsa(message, s.PrivateKey) 就是我们要找的签名函数,和官方说的一样

给大家看一下我的签名方式

//开始签名timeStamp:=strconv.FormatInt(time.Now().Unix(),10)nonceStr:=utils.Md5(utils.RandChar(6))packageStr:="prepay_id="+prepaymap["prepay_id"]signBody:=appid+"\n"signBody+=timeStamp+"\n"signBody+=nonceStr+"\n"signBody+=packageStr+"\n"privateKeyPath := "./cert/apiclient_key.pem"privateKey, err := wx.LoadPrivateKeyWithPath(privateKeyPath)//我本身有utils包 所以吧微信的utils包重命名为了wx /*我引入包的方式"github.com/wechatpay-apiv3/wechatpay-go/core""github.com/wechatpay-apiv3/wechatpay-go/core/auth/signers""github.com/wechatpay-apiv3/wechatpay-go/core/option"wx "github.com/wechatpay-apiv3/wechatpay-go/utils"*///Signer:=&signers.Sha256WithRSASigner{PrivateKey: privateKey, MchCertificateSerialNo: mchCertSerialNumber}sign,err:=signers.Sha256WithRsa(signBody,privateKey)resultMap:=make(map[string]string)resultMap["timeStamp"]=timeStampresultMap["nonceStr"]=nonceStrresultMap["package"]=packageStrresultMap["signType"]="RSA"resultMap["paySign"]=sign

至此,统一下单就结束了小程序调起支付的参数我们也包装好传给前端了。注意包引入的方式!!!!!!!

接着做支付回调

我用的beego框架
直接上代码

//***微信支付回调
type WxPayNotify struct {controllers.IndexController
}func (this *WxPayNotify)Post(){body:=this.Ctx.Input.RequestBodyapiv3Key:=beego.AppConfig.String("apiv3key")var bodyMap map[string]interface{}err:=json.Unmarshal(body, &bodyMap)if err!=nil {this.Data["json"]=map[string]interface{}{"code": "faild", "message": "数据解析失败"}this.ServeJSON()return}var resource map[string]interface{}resource=make(map[string]interface{})resource,ok:=bodyMap["resource"].(map[string]interface{})if !ok{this.Data["json"]=map[string]interface{}{"code": "false", "message": "bodyMap 解析错误"}this.ServeJSON()return//return}jsonText,err:=wx.DecryptToString(apiv3Key,resource["associated_data"].(string),resource["nonce"].(string),resource["ciphertext"].(string))var resultMap map[string]interface{}err=json.Unmarshal([]byte(jsonText), &resultMap)if err!=nil{this.Data["json"]=map[string]interface{}{"code": "false", "message": "resultMap 解析错误"}this.ServeJSON()return}fmt.Println("resultMap:",resultMap)order_id:=resultMap["out_trade_no"].(string)money:=int64(resultMap["amount"].(map[string]interface{})["total"].(float64))orderinfo,err:=order.GetOrderByOrderId(order_id)if err!=nil{this.Data["json"]=map[string]interface{}{"code": "false", "message": "订单不存在"}this.ServeJSON()return}err=order.OrderPaid(order_id,money)if err!=nil{this.Data["json"]=map[string]interface{}{"code": "false", "message": err.Error()}this.ServeJSON()return}service_id,_:=service.GetService(orderinfo.StoreId)change:=cmd.SendOrderStatus{Cmd:5,Uid:orderinfo.Uid,Storeid:orderinfo.StoreId,Status:orderinfo.Status}go socket.SendMessageToPeer(service_id,change)this.Data["json"]=map[string]interface{}{"code": "success", "message": "成功"}this.ServeJSON()return
}

文档说数据body是json格式,所以我们直接把body拿出来就是所需的json数据了
回调回来的数据还需要我们自己解密才行
官方文档中

参数解密 下面详细描述对通知数据进行解密的流程:

1、用商户平台上设置的APIv3密钥【微信商户平台—>账户设置—>API安全—>设置APIv3密钥】,记为key;
2、针对resource.algorithm中描述的算法(目前为AEAD_AES_256_GCM),取得对应的参数nonce和associated_data;
3、使用key、nonce和associated_data,对数据密文resource.ciphertext进行解密,得到JSON形式的资源对象;

注:
AEAD_AES_256_GCM算法的接口细节,请参考rfc5116。微信支付使用的密钥key长度为32个字节,随机串nonce长度12个字节,associated_data长度小于16个字节并可能为空。

我们注意到算法AEAD_AES_256_GCM AES!!!!!!
在去翻翻微信的包 看看有没有这个方法

有个aes.go 看看是不是它!

package utilsimport ("crypto/aes""crypto/cipher""encoding/base64"
)//  DecryptToString 将下载证书的回包解析成证书
//
//  解析后的证书是这样的
//  -----BEGIN CERTIFICATE-----
//  -----END CERTIFICATE-----
func DecryptToString(apiv3Key, associatedData, nonce, ciphertext string) (certificate string, err error) {decodedCiphertext, err := base64.StdEncoding.DecodeString(ciphertext)if err != nil {return "", err}c, err := aes.NewCipher([]byte(apiv3Key))if err != nil {return "", err}gcm, err := cipher.NewGCM(c)if err != nil {return "", err}certificateByte, err := gcm.Open(nil, []byte(nonce), decodedCiphertext, []byte(associatedData))if err != nil {return "", err}certificate = string(certificateByte)return certificate, nil
}

看这函数,它不就是告诉我们,直接传参数就可以了!!!!! 直接用吧!!!

至此,我们这次微信小程序的支付就对接完毕了,有什么不懂地方需要帮助的,可以来安阳APP开发找我!

golang 微信支付全解析相关推荐

  1. 微信App支付全解析

    简单介绍了微信移动支付的申请.接入.使用.确认支付结果等相关流程 0 系列文章 系列一 微信App支付全解析 系列二 支付宝App支付全解析 系列三 微信公众号支付全解析 系列四 微信扫码支付全解析 ...

  2. 支付宝App支付全解析

    简单介绍了支付宝App支付的申请.接入.使用.确认支付结果等相关流程 0 系列文章 系列一 微信App支付全解析 系列二 支付宝App支付全解析 系列三 微信公众号支付全解析 系列四 微信扫码支付全解 ...

  3. golang 微信支付介绍

    golang微信支付介绍 本次只介绍单个普通商户支付功能 下载微信提供的第三方包wechatpay-go 下载地址 https://github.com/wechatpay-apiv3/wechatp ...

  4. 基于Python flask 框架的微信支付 全代码

    前台代码忽略 需要了解flask 框架 尤其是 模板传参 和重定向 传参 ###############################################<<各种需要用到的函 ...

  5. Android:微信授权登录与微信分享全解析

    前言 在移动互联网浪潮中,联网APP已经把单机拍死在沙滩上,很多公司都希望自家应用能够有一套帐号系统,可是许多用户却并不一定买账:我凭啥注册你家应用的帐号?微博,微信,QQ几乎成了每个人手机中的必装应 ...

  6. 微信小程序接入微信支付全流程(koa2)

    在微信公众平台配置request合法域名和业务域名 使用ten_pay插件接入微信支付 1.配置域名 开发管理->开发设置->在服务器域名在里面配置request合法域名(必须是https ...

  7. 微信支付退款解析 对加密串B做AES-256-ECB解密(PKCS7Padding)

    1.微信支付文档 https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_16&index=9 解密方式 解密步骤如下: (1)对加密串 ...

  8. php微信支付返回值,php微信支付全记录

    //支付方式 $payData['user_id']=$_SESSION['uid'];//用户id 此处保存在session中,其他自取 //0余额 1微信公众号 2微信app 3支付宝app $p ...

  9. Taro-RN使用 react-native-wechat-lib 集成微信支付-IOS(从微信注册应用到应用接入微信支付)全*

    组件库版本 "@tarojs/taro": "3.3.9", "@tarojs/taro-rn": "^3.3.9", ...

最新文章

  1. 降低数值精度以提高深度学习性能
  2. AI一分钟 | 蔚来赴美IPO,开盘跌破发行价;TensorFlow开源新库TFDV
  3. Enigma Virtual Box:生成可执行文件。
  4. js数组循环删除元素或对象
  5. c++枚举类型(二) c++11 枚举类
  6. DataTabel中关于ImpotRow的一点尝试
  7. 静态路由心法口诀:路由表少什么网络加什么网络
  8. 脚本实现补丁安装自动化
  9. 转:Java NIO系列教程(二) Channel
  10. php无参rce,php中无参函数的RCE
  11. 理解并取证:IPv6与IPv4在报文结构上的区别
  12. paip.输入法编程---词频调整--单字优先编码发音一致优先的问题跟调整
  13. 无利不起早:理性看待IBM倾“芯”中国
  14. docker 自动签到模板制作
  15. iOS的崩溃率高于Android?来自听云的数据告诉你真相
  16. php 只打印某个区域,PHP打印代码页面固定区域
  17. thinkphp + 腾讯云名片识别
  18. 你知道GDT放电管结构及基本知识吗?
  19. 【移动网络】Ch. 2 移动网络基本原理 (Part1. 无线信道与数据率)
  20. Springboot+Netty搭建TCP客户端-多客户端

热门文章

  1. 性能测试分层模型-选自书籍:小强软件测试疯狂讲义
  2. State详细代码和介绍-Topsis熵权法评分(含视频教程)、Stata灰色关联度分析
  3. 生活热水循环泵选型怎么选,如何选型计算?
  4. tkm转mp3并剪辑
  5. 第10章 最小二乘线性回归分析
  6. 如何解决origin导出图像有dome字样
  7. Windows基础排查之一 - 激活
  8. (整理)CAD快捷键
  9. 记录一次爬取知识星球的word文档
  10. 基于tensorflow 的中文语音识别模型