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 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
39
粉丝
11
喜欢
71
收藏
102
排名:466
访问:1.9 万
私信
所有博文
社区赞助商