经过上节课的功能开发,我们成功地用自己购物车里的商品项下了单,下了单不支付,人家也不可能给你送货或者给你开会员的对吧。
这节课我们主要拆解一下要完成这笔订单的支付我们的系统需要做些什么以及相关的代码实现。
订单支付的流程分析
想要完成订单的支付我们首先得在系统中接入支付的能力--即申请成为支付宝/微信的商户,由它们替我们把钱从用户那里收上来然后再通知我们。
别看我说的这么简单,其实没那么简单,这里直接上一张微信支付提供的开发指引中,用顺序图讲解的支付流程。
图片来自:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_3.shtml

我带大家读一下这个顺序图
如果想更深入地了解在流程分析中怎么使用顺序图,掌握画流程图的精髓,可以参考我另外一个专栏《程序员的全能画图课》中的这两节:
微信支付宝的开发者为什么都用它来演示对接
手把手教你画出大厂开放平台中的顺序图
专栏地址: https://xiaobot.net/p/dev_img
订单使用微信支付的业务流程
首先从顺序图中我们可以看到整个流程的参与者有四个
用户
微信客户端
商户系统后台 -- 也就是我们项目的服务
微信支付系统 -- 即微信提供支付能力和接口的服务
步骤2要求商户系统先生成订单 ,这个步骤即我们上节课实现的订单创建功能
这一步如果需要用户下单后再选择支付方式,则要先把订单信息返回给前端
然后步骤3中要求商户系统生成订单后,去请求微信支付的下单接口,生成微信平台中的预付单。
商户系统在拿到预付单ID后,使用微信平台的公钥进行进行签名后,生成唤起微信客户端进行支付的必要参数给到用户使用的前端。
PS:这一步应该是这个顺序图中画错了,应该由用户端唤起微信客户端才对。
用户在唤起的微信客户端中输入密码后,交给微信支付系统进行后续处理。
支付结果会由微信支付系统异步地调用下单生成支付参数时填写的回调地址,进行支付结果通知。
本文节选自我的专栏《Go项目搭建和整洁开发实战》欢迎扫码订阅

订阅后除了加入实战项目外,还可加入专属读者群一起学习。
接入微信支付
我们在介绍项目分层规划时介绍过,三方能力的接入都放在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 进行结果通知时我们能从它传递的结果数据中解密出这个订单号,这样我们在收到支付结果通知后才能在我们的系统中找到对应的订单,并根据结果更新成相应的订单状态。
本文后续内容请扫码订阅专栏阅读完整版。

加入专栏后除了能获得完整版教程,更有配套的实战项目和专属读者群,我在其中用git tag版本编排好了每节教程对应的代码更新,让你能更容易地追踪项目开发过程中的每一处代码变更。
《Go项目搭建和整洁开发实战》专栏分为五大部分,重点章节如下

第一部分介绍让框架变得好用的诸多实战技巧,比如通过自定义日志门面让项目日志更简单易用、支持自动记录请求的追踪信息和程序位置信息、通过自定义Error在实现Go error接口的同时支持给给错误添加错误链,方便追溯错误源头。
第二部分:讲解项目分层架构的设计和划分业务模块的方法和标准,让你以后无论遇到什么项目都能按这套标准自己划分出模块和逻辑分层。后面几个部分均是该部分所讲内容的实践。
第三部分:设计实现一个套支持多平台登录,Token泄露检测、同平台多设备登录互踢功能的用户认证体系,这套用户认证体系既可以在你未来开发产品时直接应用
第四部分:商城app C端接口功能的实现,强化分层架构实现的讲解,这里还会讲解用责任链、策略和模版等设计模式去解决订单结算促销、支付方式支付场景等多种多样的实际问题。
第五部分:单元测试、项目Docker镜像、K8s部署和服务保障相关的一些基础内容和注意事项。
扫描上方二维码或者访问****https://xiaobot.net/p/golang即刻订阅
此外想更详细地了解专栏内容,咨询专栏优惠,都可以添加下面我的微信