golang微信支付介绍

本次只介绍单个普通商户支付功能

下载微信提供的第三方包wechatpay-go

 下载地址

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

支付接口文档

微信支付-普通下单开发者文档

由于我下载的是V2,由于和第一版有差异,文件名有修改

github.com/wechatpay-apiv3/wechatpay-go-0.2

商户平台--证书密钥信息

登录到微信支付-商户平台,到账户中心-API安全,做API证书申请、APIv3密钥设置;API密钥没有设置,暂时也没有了解过

go语言Native支付 golang 支付系统_go语言Native支付

商户平台--jsapi设置

登录到微信支付-商户平台,到产品中心-开发配置,设置支付成功回调服务器域名。由于开发期间可能需要用到自己电脑调试,或有自己的域名可设置指定,若没有,可使用钉钉内容穿透等工具

go语言Native支付 golang 支付系统_golang_02

商户平台--appid

Appid,使用商户平台注册时绑定的公众号,或是小程序对应的appid。Mchid即是商户平台的商户号

go语言Native支付 golang 支付系统_go语言Native支付_03

go语言Native支付 golang 支付系统_go语言Native支付_04

jsapi调起支付

go语言Native支付 golang 支付系统_go语言Native支付_05

先设置需要有固定参数值

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才可使用

go语言Native支付 golang 支付系统_开发语言_06

  1. 转账到零钱需要到 产品中心-转账到零钱-产品设置 中设置对应提现IP才可使用

补充说明:pjs包

import pjs "github.com/wechatpay-apiv3/wechatpay-go-0.2/services/payments/jsapi"