go-zero之App支付宝支付
支付宝支付
实例化客户端
func NewAliAppPayClient(appId, apiPrivateKey, notifyUrl, appPublicKeyCertPath, aliPayRootCertPath, aliPayCertPublicKeyPath string) *alipay.Client {
// 初始化支付宝客户端
// appId:应用ID
// privateKey:应用私钥,支持PKCS1和PKCS8
// isProd:是否是正式环境
client, err := alipay.NewClient(appId, apiPrivateKey, true)
if err != nil {
logx.Error(err)
return nil
}
// 打开Debug开关,输出日志,默认关闭
//client.DebugSwitch = gopay.DebugOn
// 设置支付宝请求 公共参数
// 注意:具体设置哪些参数,根据不同的方法而不同,此处列举出所有设置参数
client.SetLocation(alipay.LocationShanghai). // 设置时区,不设置或出错均为默认服务器时间
SetCharset(alipay.UTF8). // 设置字符编码,不设置默认 utf-8
SetSignType(alipay.RSA2). // 设置签名类型,不设置默认 RSA2
//SetReturnUrl("https://www.fmm.ink"). // 设置返回URL
SetNotifyUrl(notifyUrl) // 设置异步通知URL
//SetAppAuthToken() // 设置第三方应用授权
// 自动同步验签(只支持证书模式)
// 传入 alipayCertPublicKey_RSA2.crt 内容
appPublicKeyCertContent, err := ioutil.ReadFile(appPublicKeyCertPath)
if err != nil {
logx.Error(err)
return nil
}
client.AutoVerifySign(appPublicKeyCertContent)
// 公钥证书模式,需要传入证书,以下两种方式二选一
// 证书路径
err = client.SetCertSnByPath(appPublicKeyCertPath, aliPayRootCertPath, aliPayCertPublicKeyPath)
if err != nil {
logx.Error()
return nil
}
// 证书内容
//err := client.SetCertSnByContent("appCertPublicKey bytes", "alipayRootCert bytes", "alipayCertPublicKey_RSA2 bytes")
return client
}
生成支付信息
支付宝申请接口加密方式,并生成证书。请求参数需要至少4个。
// APP 支付宝支付
func (l *AliAppPayLogic) AliAppPay(in *order.AliPrePayRequest) (*order.Response, error) {
data := make(map[string]string, 0)
notifyUrl := l.svcCtx.Config.AliAppPay.NotifyUrl
appId := l.svcCtx.Config.AliAppPay.AppId
apiPrivateKey := l.svcCtx.Config.AliAppPay.ApiPrivateKey
appPublicKeyCertPath := tool.GetFilePath(l.svcCtx.Config.AliAppPay.AppPublicKeyCertPath)
aliPayRootCertPath := tool.GetFilePath(l.svcCtx.Config.AliAppPay.AliPayRootCertPath)
aliPayCertPublicKeyPath := tool.GetFilePath(l.svcCtx.Config.AliAppPay.AliPayCertPublicKeyPath)
//expireTime := l.svcCtx.Config.AliAppPay.ExpireTime
// 初始化
client := tool.NewAliAppPayClient(appId, apiPrivateKey, notifyUrl, appPublicKeyCertPath, aliPayRootCertPath, aliPayCertPublicKeyPath)
if client == nil {
return &order.Response{
Code: 201,
Msg: "初始化支付宝客户端失败",
Data: data,
}, nil
}
// 查询订单
orderInfo, err := model.NewOrder().FindOneByOrderId(in.OrderId)
if err != nil {
return &order.Response{
Code: 201,
Msg: "订单不存在",
Data: data,
}, nil
}
// 支付宝需要元为单位,所以转成浮点型数值
money := float64(orderInfo.Amount) / float64(100)
amount := fmt.Sprintf("%.2f", money)
bm := make(gopay.BodyMap)
bm.Set("subject", orderInfo.Desc).
Set("out_trade_no", in.OrderId).
Set("total_amount", amount).
Set("product_code", config.AppProductCode)
//logx.Info("支付宝支付金额:", amount)
aliPayResp, err := client.TradeAppPay(l.ctx, bm)
if err != nil {
logx.Error(err)
return &order.Response{
Code: 201,
Msg: err.Error(),
Data: data,
}, nil
}
data["info"] = aliPayResp
return &order.Response{
Code: 200,
Msg: "success",
Data: data,
}, nil
}
api返回信息
生成的信息是一段字符串,包含appid, 参数内容,证书序列号,数据类型,请求接口,通知地址,签名,签名类型,时间戳,版本等
// APP 支付
func (l *AppPayLogic) AppPay(req types.GenerateOrderReq) (*types.OrderResponse, error) {
data := make(map[string]interface{}, 0)
userId, _ := l.ctx.Value("userId").(json.Number).Int64()
// 创建订单
// 商品详情
goodsInfo, err := l.svcCtx.Lookclient.GoodsDetail(l.ctx, &lookclient.FindGoodsByIdRequest{Id: req.GoodsId})
if err != nil || goodsInfo.Code != 200 {
return &types.OrderResponse{
Code: 201,
Msg: goodsInfo.Msg,
Data: data,
}, nil
}
// 商品表价格字符串转int64
//price, _ := strconv.ParseFloat(goodsInfo.Data.Price, 10)
//goodsPrice := int64(price * 100)
goodsPrice := int64(goodsInfo.Data.Price * 100)
// 生成订单详情
goodsInfoData, _ := json.Marshal(goodsInfo.Data)
// rpc 生成订单
orderInfo, err := l.svcCtx.OrderClient.GenerateOrder(l.ctx, &orderclient.GenerateOrderRequest{
UserId: userId,
GoodsId: goodsInfo.Data.Id,
PayType: req.PayType,
Amount: goodsPrice,
Desc: goodsInfo.Data.Desc,
Data: string(goodsInfoData),
})
if err != nil || orderInfo.Code != 200 {
return &types.OrderResponse{
Code: 201,
Msg: orderInfo.Msg,
Data: data,
}, nil
}
// 判断支付方式
if req.PayType == config.WxPay {
// 生成预支付
wxPrePayResp, err := l.svcCtx.OrderClient.WxPrePay(l.ctx, &orderclient.WxPrePayRequest{
OrderId: orderInfo.Data["order_id"],
Desc: goodsInfo.Data.Desc,
Total: goodsPrice,
})
if err != nil || wxPrePayResp.Code != 200 {
return &types.OrderResponse{
Code: 201,
Msg: wxPrePayResp.Msg,
Data: data,
}, nil
}
// 生成支付信息
wxPayResp, err := l.svcCtx.OrderClient.WxPay(l.ctx, &orderclient.WxPayRequest{
PrePayId: wxPrePayResp.Data["prepay_id"],
})
if err != nil || wxPayResp.Code != 200 {
return &types.OrderResponse{
Code: 201,
Msg: wxPayResp.Msg,
Data: data,
}, nil
}
data["info"] = wxPayResp.Data
} else if req.PayType == config.AliPay {
// 支付宝支付
aliPayResp, err := l.svcCtx.OrderClient.AliAppPay(l.ctx, &orderclient.AliPrePayRequest{
OrderId: orderInfo.Data["order_id"],
Desc: goodsInfo.Data.Desc,
Total: goodsPrice,
})
if err != nil || aliPayResp.Code != 200 {
return &types.OrderResponse{
Code: 201,
Msg: aliPayResp.Msg,
Data: data,
}, nil
}
data["info"] = aliPayResp.Data
}
return &types.OrderResponse{
Code: 200,
Msg: "success",
Data: data,
}, nil
}
测试下
客户端拿到返回信息后,请求支付宝来完成支付
curl --location --request POST 'http://localhost/order/app_pay' \
--header 'User-Agent: Apipost client Runtime/+https://www.apipost.cn/' \
--form 'goods_id=2' \
--form 'pay_type=2'
{
"code": 200,
"msg": "success",
"data": {
"info": {
"info": "alipay_root_cert_sn=6998_02941eef..."
}
}
}
回调通知
支付成功后,会通知服务端提供的地址,服务端接收参数,解析,验签等操作,并修改订单状态等后续操作。
// rpc 代码
// APP 支付宝通知
func (l *AliAppPayNotifyLogic) AliAppPayNotify(in *order.AliAppPayNotifyRequest) (*order.Response, error) {
var data = make(map[string]string, 0)
// 解析请求参数
var bodyMap gopay.BodyMap
_ = json.Unmarshal([]byte(in.Body), &bodyMap)
// 获取证书公钥路径
aliPayCertPublicKeyPath := tool.GetFilePath(l.svcCtx.Config.AliAppPay.AliPayCertPublicKeyPath)
aliPayCertPublicKeyContent, err := ioutil.ReadFile(aliPayCertPublicKeyPath)
if err != nil {
logx.Error("支付宝回调读取公钥证书文件错误:", err)
return &order.Response{
Code: 201,
Msg: "支付宝回调读取公钥证书文件错误",
Data: data,
}, nil
}
ok, err := alipay.VerifySignWithCert(aliPayCertPublicKeyContent, bodyMap)
if err != nil || !ok {
logx.Error("支付宝回调验证错误:", err)
return &order.Response{
Code: 201,
Msg: "支付宝回调验证错误",
Data: data,
}, nil
}
//notifyReq.GetString("TradeStatus")
logx.Info("支付宝返回通知数据: ", bodyMap)
if bodyMap.GetString("trade_status") != "TRADE_SUCCESS" {
logx.Error("支付宝回调失败:", err)
return &order.Response{
Code: 201,
Msg: "支付宝回调失败",
Data: data,
}, nil
}
data["order_id"] = bodyMap.GetString("out_trade_no")
return &order.Response{
Code: 200,
Msg: "success",
Data: data,
}, nil
}
// api 代码
// APP 支付宝回调
func (l *AliPayNotifyLogic) AliPayNotify(req *http.Request, resp http.ResponseWriter) {
// 解析异步通知的参数
// req:*http.Request
notifyReq, err := alipay.ParseNotifyToBodyMap(req)
if err != nil {
logx.Error("支付宝回调解析req错误:", err)
return
}
notifyBody := notifyReq.JsonBody()
// rpc 获取通知结果
result, err := l.svcCtx.OrderClient.AliAppPayNotify(l.ctx, &orderclient.AliAppPayNotifyRequest{
Body: notifyBody,
})
if err != nil || result.Code != 200 {
logx.Error("支付宝回调错误:", err)
return
}
// 查询订单
orderInfo, _ := l.svcCtx.OrderClient.FindOneByOrderId(l.ctx, &orderclient.FindOrderRequest{
OrderId: result.Data["order_id"],
})
// 订单已支付
if orderInfo.Data.Status == config.Paid {
logx.Info("支付宝回调查询订单已支付成功")
return
} else {
// 异步更新订单状态
common := NewCommonHandleLogic(l.ctx, l.svcCtx)
go common.UpdateAppOrder(result.Data["order_id"], orderInfo.Data, config.Paid)
}
// 确认收到通知消息
resp.WriteHeader(http.StatusOK)
_, _ = resp.Write([]byte("success"))
}
本作品采用《CC 协议》,转载必须注明作者和本文链接