支付寶支付
例項化客戶端
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 協議》,轉載必須註明作者和本文連結