golang微信支付介绍
本次只介绍单个普通商户支付功能
下载微信提供的第三方包wechatpay-go
下载地址
https://github.com/wechatpay-apiv3/wechatpay-go
支付接口文档
由于我下载的是V2,由于和第一版有差异,文件名有修改
github.com/wechatpay-apiv3/wechatpay-go-0.2
商户平台--证书密钥信息
登录到微信支付-商户平台,到账户中心-API安全,做API证书申请、APIv3密钥设置;API密钥没有设置,暂时也没有了解过
商户平台--jsapi设置
登录到微信支付-商户平台,到产品中心-开发配置,设置支付成功回调服务器域名。由于开发期间可能需要用到自己电脑调试,或有自己的域名可设置指定,若没有,可使用钉钉内容穿透等工具
商户平台--appid
Appid,使用商户平台注册时绑定的公众号,或是小程序对应的appid。Mchid即是商户平台的商户号
jsapi调起支付
先设置需要有固定参数值
Appid:开发的程序,公众号、小程序
Mchid:商户号
ServerURL:支付回调服务器地址
MchAPIv3Key:商户设置APIv3时,自己定义的密钥
MchCertificateSerialNumber:商户证书申请后,可查看到对应的序列号
MchPKFileName:下载的证书文件(路径)
//获取加解密处理
func getWechatClient() (context.Context,*core.Client, error) {
// 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
mchPrivateKey, err := payUtils.LoadPrivateKeyWithPath(MchPKFileName)
if err != nil {
log.Print("load merchant private key error")
return nil,nil, err
}
ctx := context.Background()
// 使用商户私钥等初始化 client,并使它具有自动定时获取微信支付平台证书的能力
opts := []core.ClientOption{
option.WithWechatPayAutoAuthCipher(Mchid, MchCertificateSerialNumber, mchPrivateKey, MchAPIv3Key),
}
client, err := core.NewClient(ctx, opts...)
if err != nil {
log.Printf("new wechat pay client err:%s", err)
return nil,nil, err
}
return ctx,client, nil
}
根据参数,生成微信预付订单,返回PrepayId,根据PrepayId前台即可调起支付,但前台仍需要加密,可使用另一个函数
func CreateWechatPrepayBill(outTradeNo, description, attach, spOpenid string, amount int64) (string, error) {
notifyUrl := ServerURL +"/api/thirdParty/wechat/pay/getPayResult/"
ctx,client, err := getWechatClient()
if err != nil {
log.Printf("new wechat pay client err:%s", err)
return "",err
}
tmp, _ := time.ParseDuration("5m")
endTime := time.Now().Add(tmp)
svc := pjs.JsapiApiService{Client: client}
resp, result, err := svc.Prepay(ctx,
pjs.PrepayRequest{
Appid: core.String(Appid),
Mchid: core.String(Mchid),
Description: core.String(description),
OutTradeNo: core.String(outTradeNo),
TimeExpire: core.Time(endTime),
Attach: core.String(attach),
NotifyUrl: core.String(notifyUrl),
GoodsTag: core.String("WXG"),
LimitPay: []string{"no_credit"},
SupportFapiao: core.Bool(false),
Amount: &pjs.Amount{
Currency: core.String("CNY"),
Total: core.Int64(amount),
},
Payer: &pjs.Payer{
Openid: core.String(spOpenid),
},
SettleInfo: &pjs.SettleInfo{
ProfitSharing: core.Bool(false),
},
},
)
if err != nil {
// 处理错误
log.Printf("call Prepay err:%s", err)
return "", nil
} else {
// 处理返回结果
log.Printf("status=%d resp=%s", result.Response.StatusCode, resp)
}
return *resp.PrepayId, nil
}
创建预付订单,并生成前端调起支付时的所有数据
//创建并生成待支付信息
func CreateWechatPrepayWithPayment(outTradeNo, description, attach, spOpenid string, amount int64)(map[string]interface{}, error){
notifyUrl := ServerURL +"/api/thirdParty/wechat/pay/getPayResult"
ctx,client, err := getWechatClient()
if err != nil {
return nil,err
}
tmp, _ := time.ParseDuration("5m")
endTime := time.Now().Add(tmp)
svc := pjs.JsapiApiService{Client: client}
resp, _, err := svc.PrepayWithRequestPayment(ctx,
pjs.PrepayRequest{
Appid: core.String(Appid),
Mchid: core.String(Mchid),
Description: core.String(description),
OutTradeNo: core.String(outTradeNo),
TimeExpire: core.Time(endTime),
Attach: core.String(attach),
NotifyUrl: core.String(notifyUrl),
GoodsTag: core.String("WXG"),
LimitPay: []string{"no_credit"},
SupportFapiao: core.Bool(false),
Amount: &pjs.Amount{
Currency: core.String("CNY"),
Total: core.Int64(amount),
},
Payer: &pjs.Payer{
Openid: core.String(spOpenid),
},
SettleInfo: &pjs.SettleInfo{
ProfitSharing: core.Bool(false),
},
},
)
if err != nil {
// 处理错误
return nil, err
} else {
// 处理返回结果
//log.Printf("status=%d resp=%s", result.Response.StatusCode, resp)
}
result := make(map[string]interface{})
tmpJson := utils.GetJsonStr(resp)
json.Unmarshal([]byte(tmpJson),&result)
return result, nil
}
支付回调
回调处理有两种,
第1:当你的回调接口是原生接口,即在回调的函数里之前还未使用ioutil.ReadAll(request.Body)获取返回数据时,可使用第三方包附带的函数
func getPayResult(param map[string]interface{}, w http.ResponseWriter, r *http.Request) (interface{}, int32) {
fmt.Println("微信支付回调")
handler := notify.NewNotifyHandler(
pay.MchAPIv3Key, verifiers.NewSHA256WithRSAVerifier(core.NewCertificateMapWithList(nil)),
)
content := make(map[string]interface{})
request, err := handler.ParseNotifyRequest(context.Background(), r, content)
utils.ThrowError(err)
fmt.Println(request)
fmt.Println(content)
return "success", 0
}
但,当你的回调接口是有自行封装过,即在到你的处理函数前,已有执行过ioutil.ReadAll(request.Body),则无法使用第三方包函数,需要自己处理解密和判断
if fmt.Sprint(vals["event_type"]) == "TRANSACTION.SUCCESS" {
fmt.Println("微信支付成功")
resourceObj := vals["resource"].(map[string]interface{})
nonce := fmt.Sprint(resourceObj["nonce"])
associated_data := fmt.Sprint(resourceObj["associated_data"])
ciphertext := fmt.Sprint(resourceObj["ciphertext"])
certificate, err := payUtils.DecryptAES256GCM(pay.MchAPIv3Key, associated_data, nonce, ciphertext)
logs.Info("回调函数内容", certificate)
if err != nil {
logs.Info("解密错误原因", err)
}
certificateObj := utils.JsonForceMashMap(certificate)
attach := fmt.Sprint(certificateObj["attach"])
fmt.Println("attach", attach)
tmp := make(map[string]interface{})
tmp["code"] = "SUCCESS"
tmp["message"] = "成功"
return tmp, 40
} else {
fmt.Println("微信支付失败")
tmp := make(map[string]interface{})
tmp["code"] = "500"
tmp["message"] = "失败"
return tmp, 40
}
第2,使用包里自带的处理函数函数payUtils.DecryptAES256GCM
func getPayResult(param map[string]interface{}, w http.ResponseWriter, r *http.Request) (interface{}, int32) {
defer func() {
//错误处理
if e := recover(); e != nil {
logs.Error("微信支付回调异常:",e)
}
}()
logs.Info("微信支付回调:",param)
request := notify.Request{}
tools.MapToSturct(param, &request)
if request.EventType == "TRANSACTION.SUCCESS" {
plaintext, err := payUtils.DecryptAES256GCM(
wechatPay.MchAPIv3Key, request.Resource.AssociatedData, request.Resource.Nonce, request.Resource.Ciphertext,
)
utils.ThrowError(err)
transaction := payments.Transaction{}
tools.JsonStrToStruct(plaintext, &transaction)
logs.Info("微信支付成功:",transaction)
/*
数据处理
*/
//微信支付回调返回特殊处理,Flag = 40
tmp := make(map[string]interface{})
tmp["code"] = "SUCCESS"
tmp["message"] = "成功"
return tmp, 40
} else {
fmt.Println("微信支付失败")
logs.Error("微信支付失败",request.Summary)
tmp := make(map[string]interface{})
tmp["code"] = "500"
tmp["message"] = "失败"
return tmp, 40
}
}
转到到零钱设置
转账到零钱需要到 产品中心-转账到零钱-产品设置 中设置对应提现IP才可使用
- 转账到零钱需要到 产品中心-转账到零钱-产品设置 中设置对应提现IP才可使用
补充说明:pjs包
import pjs "github.com/wechatpay-apiv3/wechatpay-go-0.2/services/payments/jsapi"