微信小程序开发实战11_1 微信支付下单

微信支付流程图

微信支付存在多个业务流程,包括微信支付流程、退款流程等。本章节主要介绍微信的支付下单流程,图12-1是微信支付流程的交互图:
在这里插入图片描述
重点环节说明

  • 步骤1:小程序端用户向商户服务器发起支付请求,重点是提供用户信息、商品信息、支付金额等参数。
  • 步骤3:商户服务器调用支付统一下单接口生成微信支付订单,调用成功后返回预支付交易会话标识(prepay_id),该参数将用于小程序前端的接口调用。
  • 步骤6:获取prepay_id后,需要再次签名,然后把签名参数以及prepay_id等参数返回给小程序。
  • 步骤9:小程序调用接口:wx.requestPayment调起微信支付,发起支付请求。
  • 步骤15:用户支付成功后,商户服务器可接收到微信支付支付结果通知,商户服务器收到通知后更新订单的支付状态。
  • 步骤20:商户在没有接收到微信支付结果通知的情况下需要主动调用查询订单API查询支付结果。

12.1提交支付订单

商户系统调用该接口生成预支付交易单。提交支付订单的请求URL为:
https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi
提交支付订单的接口的请求参数如下:

参数名变量描述
服务商应用IDsp_appid必填)服务商申请的公众号appid。
服务商户号sp_mchid(必填)服务商户号,由微信支付生成并下发
子商户应用IDsub_appid(必填)子商户申请的公众号appid。若sub_openid有传的情况下,sub_appid必填,且sub_appid需与sub_openid对应
子商户号sub_mchid(必填)子商户的商户号,由微信支付生成并下发。
商品描述description(必填)商品描述
商户订单号out_trade_no(必填)商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一。
结束时间time_expire订单失效时间。
附加数据attach附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用
通知地址notify_url(必填)通知URL必须为直接可访问的URL,不允许携带查询串。
优惠标记goods_tag订单优惠标记
+结算信息settle_info结算信息
-是否分账profit_sharing是否指定分账,枚举值true:是false:否
+订单金额amount(必填)订单金额信息
-总金额total(必填)订单总金额,单位为分。
-货币类型currencyCNY:人民币,境内商户号仅支持人民币。
+支付者payer支付者信息
-用户标识sp_openid用户在服务商appid下的唯一标识。
-用户子标识sub_openid用户在子商户appid下的唯一标识。若传sub_openid,那sub_appid必填

调用成功后返回:预支付交易会话标识(prepay_id),该参数用于小程序前端的接口调用中,该值的有效期为2小时。以下是实现提交支付订单的代码逻辑,首先给出的是统一下接口使用的数据结构:

//订单数据
type WxAppOrderData struct {
   //子商户用ID(服务商)
   Sub_appid          string `json:"sub_appid,omitempty"`
   //子商户的商户号(服务商)
   Sub_mchid          string `json:"sub_mchid,omitempty"`
   //商品描述
   Description        string `json:"description"`
   //商户系统内部订单号
   Out_trade_no       string `json:"out_trade_no"`
   //交易结束时间
   Time_expire        string `json:"time_expire,omitempty"`
   //附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用
   Attach                string `json:"attach,omitempty"`
   //通知URL必须为直接可访问的URL,不允许携带查询串
   Notify_url           string `json:"notify_url"`
   //订单优惠标记
   Goods_tag          string `json:"goods_tag,omitempty"`
   //订单金额信息
   Amount OrderCreateAmount      `json:"amount"`
   //支付者信息
   Payer OrderPayer            `json:"payer"`
   //统一下单结算信息
   Settle_info OrderCreateSettle  `json:"settle_info"`
}

//统一下单请求参数
type WxAppOrderCreateReq struct {
   //应用ID(普通商户)
   Appid             string `json:"appid,omitempty"`
   //直连商户号(普通商户)
   Mchid                 string `json:"mchid,omitempty"`
   //服务商用ID(服务商)
   Sp_appid              string `json:"sp_appid,omitempty"`
   //服务商户号(服务商)
   Sp_mchid           string `json:"sp_mchid,omitempty"`
   //订单数据
   WxAppOrderData
}

//统一下单返回参数
type WxAppOrderCreateRet struct {
   //详细错误码
   Return_code       string     `json:"code"`
   //错误描述
   Return_msg        string     `json:"message"`
   //预支付交易会话标识。用于后续接口调用中使用,该值有效期为2小时
   Prepay_id         string     `json:"prepay_id"`
}

微信支付为直连商户模式以及服务商模式提供了不同的调用接口(具有不同的接口地址以及请求参数)。本文中直连商户模式以及服务商模式共用一套数据结构,并通过json的omitempty标签来适应两种模式的区别。接下来给出服务商模式下接口调用的实现逻辑:

//支付统一下单
//data:支付订单信息
func (ent *MchWxapp) createOrderX(data WxAppOrderData) (WxAppPayParam, error)  {
   var preq WxAppOrderCreateReq
   preq.Sp_appid = ent.Appid
   preq.Sp_mchid = ent.Mchid
   preq.WxAppOrderData = data
   data_body, _ := json.Marshal(preq)

   var param_ent WxAppPayParam
   var pret WxAppOrderCreateRet
   const url = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi"
   result, err := WxPayPostV3((*MchParam)(ent), url, data_body)
   if err != nil {
      return param_ent, err
   }
   err = json.Unmarshal([]byte(result), &pret)
   if err != nil {
      fmt.Println(err)
      return param_ent, err
   }
   if pret.Prepay_id == "" {
      return param_ent, errors.New(pret.Return_msg)
   }

   //小程序客户端支付参数
   param_ent.Appid = preq.Sub_appid
   param_ent.TimeStamp = fmt.Sprintf("%d", time.Now().Unix())
   param_ent.NonceStr, _= GenerateNonce()
   param_ent.Prepay_id = pret.Prepay_id
   param_ent.Package = "prepay_id=" + pret.Prepay_id
   param_ent.SignType = "RSA"
   param_ent.PaySign, _ = param_ent.GenPaySignV3(ent.MchPrivateKey)
   return param_ent, nil
}

通过JSAPI下单成功获取预支付交易会话标识(prepay_id)后,需要通过小程序调用支付API(wx.requestPayment)来拉起微信客户端进行支付。wx.requestPayment需要将请求参数进行签名,签名计算的逻辑如下:
1)构造签名串
签名串一共有四行,每一行为一个参数。行尾以\n结束,包括最后一行。参与签名字段及格式为:
小程序appId\n时间戳\n随机字符串\n订单详情扩展字符串\n
2)计算签名值
使用商户私钥对签名串进行SHA256 with RSA签名,并对签名结果进行Base64编码得到签名值。
接下来给出小程序参数签名计算的实现代码,首先给出返回给小程序所需的支付数据的数据结构:

type WxAppPayParam struct{
   Appid string
   TimeStamp string
   NonceStr string
   Prepay_id string
   Package string
   SignType string
   PaySign string
}

接下来使用商户私钥对签名串进行SHA256 with RSA签名,并对签名结果进行Base64编码来计算签名值:

func (ent *WxAppPayParam)GenPaySignV3(mch_pem_key *rsa.PrivateKey) (string, error) {
   SignatureMessageFormat := "%s\n%s\n%s\n%s\n"
   message := fmt.Sprintf(SignatureMessageFormat, ent.Appid, ent.TimeStamp, ent.NonceStr, ent.Package)
   signatureResult, err := SignSHA256WithRSA(mch_pem_key, message)
   if err != nil {
      return "", err
   }
   return signatureResult, nil
}

直连商户的下单接口与服务商下单接口基本一致,除了接口地址不同外,下单时不需要提供子商户号(sub_mchid), 也不需要提供子商户子商户的应用ID(sub_appid)。直连商户下单接口的请求URL为:
https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi
直连商户下单接口的请求参数如下:

参数名变量描述
应用IDappidbody 由微信生成的应用ID,全局唯一。请求基础下单接口时请注意APPID的应用属性,例如公众号场景下,需使用应用属性为公众号的APPID
直连商户号mchidbody 直连商户的商户号,由微信支付生成并下发。
商品描述descriptionbody 商品描述
商户订单号out_trade_nobody 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
交易结束时间time_expire body订单失效时间,time_expire只能第一次下单传值,不允许二次修改,二次修改系统将报错。如用户支付失败后,需再次支付,需更换原订单号重新下单。
附加数据attachbody 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用
通知地址notify_urlbody异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。 公网域名必须为https,如果是走专线接入,使用专线NAT IP或者私有回调域名可使用http
订单优惠标记goods_tagbody 订单优惠标记
订单金额amountbody 订单金额信息
支付者payerbody 支付者信息
优惠功能detailbody 优惠功能
场景信息scene_infobody 支付场景描述
结算信息settle_infobody 结算信息

以下是直连商户下单接口访问的代码实现:

func (ent *MchWxapp) createOrder(data WxAppOrderData) (WxAppPayParam, error)  {
   var preq WxAppOrderCreateReq
   preq.Appid = ent.Appid
   preq.Mchid = ent.Mchid
   preq.WxAppOrderData = data
   data_body, _ := json.Marshal(preq)

   var param_ent WxAppPayParam
   var pret WxAppOrderCreateRet
   const url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"
   result, err := WxPayPostV3((*MchParam)(ent), url, data_body)
   if err != nil {
      return param_ent, err
   }

   err = json.Unmarshal([]byte(result), &pret)
   if err != nil {
      return param_ent, err
   }

   if pret.Prepay_id == "" {
      return param_ent, errors.New(pret.Return_msg)
   }

   //小程序客户端支付参数
   param_ent.Appid = preq.Appid
   param_ent.TimeStamp = fmt.Sprintf("%d", time.Now().Unix())
   param_ent.NonceStr, _= GenerateNonce()
   param_ent.Prepay_id = pret.Prepay_id
   param_ent.Package = "prepay_id=" + pret.Prepay_id
   param_ent.SignType = "RSA"
   param_ent.PaySign, _ = param_ent.GenPaySignV3(ent.MchPrivateKey)
   return param_ent, nil
}
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

go lang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值