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 }