经过上节课的功能开发,我们成功地用自己购物车里的商品项下了单,下了单不支付,人家也不可能给你送货或者给你开会员的对吧。
这节课我们主要拆解一下要完成这笔订单的支付我们的系统需要做些什么以及相关的代码实现。
想要完成订单的支付我们首先得在系统中接入支付的能力--即申请成为支付宝/微信的商户,由它们替我们把钱从用户那里收上来然后再通知我们。
别看我说的这么简单,其实没那么简单,这里直接上一张微信支付提供的开发指引中,用顺序图讲解的支付流程。
图片来自:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_3.shtml
我带大家读一下这个顺序图
如果想更深入地了解在流程分析中怎么使用顺序图,掌握画流程图的精髓,可以参考我另外一个专栏《程序员的全能画图课》中的这两节:
订单使用微信支付的业务流程
我们在介绍项目分层规划时介绍过,三方能力的接入都放在library层,所以我们先新建一个 WxPayLib 负责与微信支付的对接,这里使用的是微信的JSAPI,主要提供H5和网页支付能力,其实App内支付的接入流程跟这个差不多。
另外还有签约支付等等形式,各种支付形式和支付平台怎么才能在项目中有序地进行管理呢?下节课我会介绍一下如何通过使用模版和策略模式对它们进行有序管理。
首先我们新建WxPayLib,并定义号微信支付使用到的配置信息的结构。
type WxPayLib struct {
ctx context.Context
payConfig WxtPayConfig
}
type WxtPayConfig struct {
AppId string
MchId string
PrivateSerialNo string
AesKey string
NotifyUrl string
}
func NewWxPayLib(ctx context.Context, config WxtPayConfig) *WxPayLib {
return &WxPayLib{
ctx: ctx,
payConfig: config,
}
}
然后我们根据微信支付接口文档中给出的请求和响应结构信息来定义好请求和响应的结构体。
const prePayApiUrl = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"
type PrePayParam struct {
AppId string`json:"app_id"`
MchId string`json:"mch_id"` // 商户号ID
Description string`json:"description"`// 商品描述
OutTradeNo string`json:"out_trade_no"`// 业务的订单号
NotifyUrl string`json:"notify_url"` // 结果回调通知url
Amount struct {
Total int `json:"total"`
Currency string`json:"currency"`
} `json:"amount"`
Payer struct {
OpenId string`json:"open_id"`
} `json:"payer"`
}
// WxPayInvokeInfo 前端用JSAPI调起支付的参数信息
// 微信支付文档: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_4.shtml
type WxPayInvokeInfo struct {
AppId string`json:"appId"`
TimeStamp string`json:"timeStamp"`
NonceStr string`json:"nonceStr"`
Package string`json:"package"`
SignType string`json:"signType"`
PaySign string`json:"paySign"`
}
type WxPayNotifyResponse struct {
CreateTime string `json:"create_time"`
Resource WxPayNotifyResource `json:"resource"`
}
type WxPayNotifyResource struct {
Ciphertext string`json:"ciphertext"`
AssociatedData string`json:"associated_data"`
Nonce string`json:"nonce"`
}
// WxPayNotifyResourceData 微信支付结果通知中解密后的resource数据
type WxPayNotifyResourceData struct {
TransactionID string`json:"transaction_id"`
Amount struct {
PayerTotal int `json:"payer_total"`
Total int `json:"total"`
Currency string`json:"currency"`
PayerCurrency string`json:"payer_currency"`
} `json:"amount"`
Mchid string `json:"mchid"`
TradeState string `json:"trade_state"`
BankType string `json:"bank_type"`
SuccessTime time.Time `json:"success_time"`
Payer struct {
Openid string`json:"openid"`
} `json:"payer"`
OutTradeNo string`json:"out_trade_no"`
AppID string`json:"AppID"`
TradeStateDesc string`json:"trade_state_desc"`
TradeType string`json:"trade_type"`
Attach string`json:"attach"`
}
准备好后我们按照支付流程首先接入微信支付,在 CreateOrderPay 我们会用商户订单信息组装微信支付需要的请求信息,进行预下单,拿到预下单ID后再生成前端唤起微信客户端进行支付的支付参数,把支付参数返回给用户前端。
// CreateOrderPay 创建支付信息
// @param order *do.Order 业务的订单信息
// @param userOpenId string 用户的Openid
// @return payInvokeInfo *WxPayInvokeInfo 前端用于调起微信支付的参数
// @return err error 错误信息
func (wpl *WxPayLib) CreateOrderPay(order *do.Order, userOpenId string) (payInvokeInfo *WxPayInvokeInfo, err error) {
// 详细的代码实现和注释
// 请订阅专栏加入项目后查看
payDescription fmt.Sprintf("GOMALL 商场购买%s 等商品", order.Items[0].CommodityName)
....
}
请求微信支付下单时的参数中的 OutTradeNo 参数是我们项目生成订单的订单号,未来微信在调用 NotifyUrl 进行结果通知时我们能从它传递的结果数据中解密出这个订单号,这样我们在收到支付结果通知后才能在我们的系统中找到对应的订单,并根据结果更新成相应的订单状态。
本文后续内容请扫码订阅专栏阅读完整版。