Files
2026-11-07 10:47:32 +08:00

182 lines
5.5 KiB
Go

package payCenter
import (
"bytes"
"context"
"errors"
"fmt"
"net/http"
"videoweb/pub/common/httputil"
"videoweb/pub/common/log"
"videoweb/pub/common/srvdef"
)
// Recharge 使用新支付中心的元字符串金额和商户 MD5 签名协议。
type Recharge struct {
Ctx context.Context `json:"-"`
Time string `json:"time"`
Sign string `json:"sign"`
MercID string `json:"mercId"`
Type string `json:"type"`
Money string `json:"money"`
TradeNo string `json:"tradeNo"`
NotifyUrl string `json:"notifyUrl"`
CancelNotifyUrl string `json:"cancelNotifyUrl,omitempty"`
Info PayInfo `json:"info"`
SignType string `json:"signType"`
Mode string `json:"mode"`
Payload string `json:"payload"`
}
type PayInfo struct {
App string `json:"app"`
PlayerId string `json:"playerId"`
PlayerIp string `json:"playerIp"`
DeviceType string `json:"deviceType"`
DeviceId string `json:"deviceId"`
Name string `json:"name"`
Tel string `json:"tel"`
AlipayAct string `json:"alipayAct"`
}
type PayResp struct {
Code srvdef.CodeType `json:"code"`
OID string `json:"oid"`
PayUrl string `json:"payUrl"`
Mode string `json:"mode"`
Rebate string `json:"rebate"`
Sign string `json:"sign"`
}
type RechargeResp struct {
Code int `json:"code"`
Info string `json:"info"`
Err string `json:"err"`
Msg PayResp `json:"msg"`
Tip string `json:"tip"`
}
type RechargeCallbackResp struct {
Code int `json:"code" binding:"required"`
MercID string `json:"mercId" binding:"required"`
OID string `json:"oid" binding:"required"`
PayMoney string `json:"payMoney" binding:"required"`
TradeNo string `json:"tradeNo" binding:"required"`
Sign string `json:"sign" binding:"required"`
}
type RefundCallbackResp struct {
Code int `json:"code"`
MercID string `json:"mercId"`
OID string `json:"oid"`
TradeNo string `json:"tradeNo"`
Sign string `json:"sign"`
}
type rechargePostFunc func(context.Context, *Recharge, *RechargeResp) (int, error)
func (in *Recharge) ToPayNew() (PayResp, error) {
return in.toPayNew(func(ctx context.Context, request *Recharge, response *RechargeResp) (int, error) {
return httputil.NewCtx(ctx).POSTJsonWithJResp(response, request.GetURL(), nil, request)
})
}
func (in *Recharge) toPayNew(post rechargePostFunc) (result PayResp, err error) {
defer func() {
if recovered := recover(); recovered != nil {
result = PayResp{}
err = fmt.Errorf("pay center request panic: %v", recovered)
if in != nil {
log.Error("【新支付中心 /api/shark/topay】请求异常", log.Any("tradeNo", in.TradeNo), log.E(err))
}
}
if in != nil {
if err != nil {
log.Error("【新支付中心 /api/shark/topay】下单失败", log.Any("tradeNo", in.TradeNo), log.E(err))
} else {
log.Info("【新支付中心 /api/shark/topay】下单成功", log.Any("tradeNo", in.TradeNo), log.Any("oid", result.OID))
}
}
}()
if in == nil {
return result, errors.New("pay center request: nil recharge")
}
if post == nil {
return result, errors.New("pay center request: nil poster")
}
if err := validateConfig(true); err != nil {
return result, err
}
amount, err := ParseYuanToFen(in.Money)
if err != nil || amount <= 0 || in.TradeNo == "" || in.Type == "" {
return result, errors.New("新支付中心下单参数错误:订单号、支付方式和正数金额不能为空")
}
in.fill()
var response RechargeResp
code, requestErr := post(in.Ctx, in, &response)
if requestErr != nil {
return result, fmt.Errorf("post pay center order: %w", requestErr)
}
if code != http.StatusOK {
return result, fmt.Errorf("pay center returned HTTP status %d", code)
}
if response.Code != http.StatusOK {
return result, fmt.Errorf("pay center returned provider code %d", response.Code)
}
if response.Err != "" {
return result, fmt.Errorf("pay center rejected order: %s", response.Err)
}
// 外层成功但没有订单号/支付链接不能落为处理中订单。
if response.Msg.OID == "" || response.Msg.PayUrl == "" {
return result, errors.New("新支付中心下单结果缺少订单号或支付链接")
}
return response.Msg, nil
}
func (in *Recharge) fill() {
in.MercID = in.GetMercID()
in.Time = Generate13DigitString()
in.Info.App = payCenterConf.AppName
in.NotifyUrl = in.GetNotifyURL()
in.SignType = "1"
in.Sign = in.sign()
}
func (in *Recharge) GetNotifyURL() string {
return payCenterConf.CallbackUrl + "/api/web/third/newpaycenter/recharge/callback"
}
func (in *Recharge) GetCancelNotifyURL() string {
return payCenterConf.CallbackUrl + "/api/web/third/newpaycenter/refund/callback"
}
func (in *Recharge) sign() string {
// 字段顺序必须与 girl 及支付中心约定一致,金额使用原始元字符串。
var body bytes.Buffer
body.WriteString(in.MercID)
body.WriteString(in.Money)
body.WriteString(in.NotifyUrl)
body.WriteString(in.TradeNo)
body.WriteString(in.Type)
body.WriteString(in.GetAppSecret())
return md5Sign(body.String())
}
func (in *Recharge) GetURL() string {
return payCenterConf.ApiUrl + "/api/shark/topay"
}
func (in *Recharge) Success() string {
return "success"
}
func (in *Recharge) GetAppSecret() string {
return payCenterConf.MercSecret
}
func (in *Recharge) GetMercID() string {
return payCenterConf.MercID
}