355 lines
12 KiB
Go
355 lines
12 KiB
Go
package pay_for
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
"gateway/internal/config"
|
||
"gateway/internal/models/accounts"
|
||
"gateway/internal/models/merchant"
|
||
"gateway/internal/models/merchant_deploy"
|
||
"gateway/internal/models/payfor"
|
||
"gateway/internal/models/road"
|
||
"gateway/internal/otelTrace"
|
||
"gateway/internal/schema/response"
|
||
"gateway/internal/service/message"
|
||
"gateway/internal/service/supplier/third_party"
|
||
"gateway/internal/utils"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/duke-git/lancet/v2/convertor"
|
||
"go.uber.org/zap"
|
||
|
||
"github.com/beego/beego/v2/client/orm"
|
||
"github.com/beego/beego/v2/core/logs"
|
||
"github.com/rs/xid"
|
||
)
|
||
|
||
// AutoPayFor 程序自动代付
|
||
func AutoPayFor(ctx context.Context, params map[string]any, giveType string) *response.PayForResponse {
|
||
payForResponse := new(response.PayForResponse)
|
||
|
||
merchantInfo := merchant.GetMerchantByPasskey(ctx, convertor.ToString(params["merchantKey"]))
|
||
if !utils.Md5Verify(params, merchantInfo.MerchantSecret) {
|
||
otelTrace.Logger.WithContext(ctx).Error(fmt.Sprintf("下游商户代付请求,签名失败,商户信息: %+v", merchantInfo))
|
||
payForResponse.ResultCode = "01"
|
||
payForResponse.ResultMsg = "下游商户代付请求,签名失败。"
|
||
return payForResponse
|
||
} else {
|
||
res, msg := checkSettAmount(ctx, convertor.ToString(params["amount"]))
|
||
if !res {
|
||
payForResponse.ResultCode = "01"
|
||
payForResponse.ResultMsg = msg
|
||
|
||
return payForResponse
|
||
}
|
||
|
||
exist := payfor.IsExistPayForByMerchantOrderId(convertor.ToString(params["merchantOrderId"]))
|
||
if exist {
|
||
otelTrace.Logger.WithContext(ctx).Error(fmt.Sprintf("代付订单号重复:merchantOrderId = %s", params["merchantOrderId"]))
|
||
payForResponse.ResultMsg = "商户订单号重复"
|
||
payForResponse.ResultCode = "01"
|
||
|
||
return payForResponse
|
||
}
|
||
|
||
settAmount, err := strconv.ParseFloat(convertor.ToString(params["amount"]), 64)
|
||
if err != nil {
|
||
otelTrace.Logger.WithContext(ctx).Error("代付的金额错误:", zap.Error(err))
|
||
payForResponse.ResultMsg = "代付金额错误"
|
||
payForResponse.ResultCode = "01"
|
||
return payForResponse
|
||
}
|
||
|
||
p := payfor.PayforInfo{
|
||
PayforUid: "pppp" + xid.New().String(),
|
||
MerchantUid: merchantInfo.MerchantUid,
|
||
MerchantName: merchantInfo.MerchantName,
|
||
MerchantOrderId: convertor.ToString(params["merchantOrderId"]),
|
||
BankOrderId: "4444" + xid.New().String(),
|
||
PayforAmount: settAmount,
|
||
Status: config.PAYFOR_COMFRIM,
|
||
BankAccountName: convertor.ToString(params["realname"]),
|
||
BankAccountNo: convertor.ToString(params["cardNo"]),
|
||
BankAccountType: convertor.ToString(params["accType"]),
|
||
City: convertor.ToString(params["city"]),
|
||
Ares: convertor.ToString(params["province"]) + convertor.ToString(params["city"]),
|
||
PhoneNo: convertor.ToString(params["mobileNo"]),
|
||
GiveType: giveType,
|
||
CreateTime: time.Now(),
|
||
UpdateTime: time.Now(),
|
||
RequestTime: time.Now(),
|
||
}
|
||
|
||
// 获取银行编码和银行名称
|
||
p.BankCode = utils.GetBankCodeByBankCardNo(ctx, p.BankAccountNo)
|
||
p.BankName = utils.GetBankNameByCode(p.BankCode)
|
||
|
||
if !payfor.InsertPayFor(ctx, p) {
|
||
payForResponse.ResultCode = "01"
|
||
payForResponse.ResultMsg = "代付记录插入失败"
|
||
} else {
|
||
payForResponse.ResultMsg = "代付订单已生成"
|
||
payForResponse.ResultCode = "00"
|
||
payForResponse.SettAmount = convertor.ToString(params["amount"])
|
||
payForResponse.MerchantOrderId = convertor.ToString(params["MerchantOrderId"])
|
||
|
||
p = payfor.GetPayForByBankOrderId(ctx, p.BankOrderId)
|
||
|
||
if findPayForRoad(ctx, p) {
|
||
payForResponse.ResultCode = "00"
|
||
payForResponse.ResultMsg = "银行处理中"
|
||
} else {
|
||
payForResponse.ResultCode = "01"
|
||
payForResponse.ResultMsg = "系统处理失败"
|
||
}
|
||
|
||
}
|
||
|
||
return payForResponse
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 返回1表示需要手动打款,返回0表示银行已经受理,-1表示系统处理失败
|
||
*/
|
||
func findPayForRoad(ctx context.Context, p payfor.PayforInfo) bool {
|
||
m := merchant.GetMerchantByUid(ctx, p.MerchantUid)
|
||
|
||
md := merchant_deploy.GetMerchantDeployByUidAndRoadUid(ctx, m.MerchantUid, p.RoadUid)
|
||
|
||
// 检查商户是否设置了自动代付
|
||
if md.AutoPayfor == config.NO || md.AutoPayfor == "" {
|
||
logs.Notice(fmt.Sprintf("该商户uid=%s, 没有开通自动代付功能", p.MerchantUid))
|
||
p.Type = config.PAYFOR_HAND
|
||
payfor.UpdatePayFor(ctx, p)
|
||
} else {
|
||
if m.SinglePayForRoadUid != "" {
|
||
p.RoadUid = m.SinglePayForRoadUid
|
||
p.RoadName = m.SinglePayForRoadName
|
||
} else {
|
||
roadPoolInfo := road.GetRoadPoolByRoadPoolCode(ctx, m.RollPayForRoadCode)
|
||
roadUids := strings.Split(roadPoolInfo.RoadUidPool, "||")
|
||
roadInfoList := road.GetRoadInfosByRoadUids(ctx, roadUids)
|
||
if len(roadUids) == 0 || len(roadInfoList) == 0 {
|
||
otelTrace.Logger.WithContext(ctx).Error(fmt.Sprintf("通道轮询池=%s, 没有配置通道", m.RollPayForRoadCode))
|
||
} else {
|
||
p.RoadUid = roadInfoList[0].RoadUid
|
||
p.RoadName = roadInfoList[0].RoadName
|
||
}
|
||
}
|
||
|
||
if !payfor.UpdatePayFor(ctx, p) {
|
||
return false
|
||
}
|
||
|
||
if len(p.RoadUid) > 0 {
|
||
roadInfo := road.GetRoadInfoByRoadUid(ctx, p.RoadUid)
|
||
p.PayforFee = roadInfo.SettleFee
|
||
p.PayforTotalAmount = p.PayforFee + p.PayforAmount
|
||
|
||
if m.PayforFee > config.ZERO {
|
||
otelTrace.Logger.WithContext(ctx).Info(fmt.Sprintf("商户uid=%s,有单独的代付手续费。", m.MerchantUid))
|
||
p.PayforFee = m.PayforFee
|
||
p.PayforTotalAmount = p.PayforFee + p.PayforAmount
|
||
}
|
||
|
||
if !payfor.UpdatePayFor(ctx, p) {
|
||
return false
|
||
}
|
||
|
||
if p.GiveType == config.SELF_HELP {
|
||
if !MerchantSelf(ctx, p) {
|
||
return false
|
||
}
|
||
} else {
|
||
if !SendPayFor(ctx, p) {
|
||
return false
|
||
}
|
||
}
|
||
} else {
|
||
p.Status = config.PAYFOR_FAIL
|
||
if !payfor.UpdatePayFor(ctx, p) {
|
||
return false
|
||
}
|
||
p.ResponseContent = "没有设置代付通道"
|
||
}
|
||
}
|
||
|
||
return true
|
||
}
|
||
|
||
// MerchantSelf 商户自己体现
|
||
func MerchantSelf(ctx context.Context, p payfor.PayforInfo) bool {
|
||
o := orm.NewOrm()
|
||
|
||
if err := o.DoTxWithCtx(ctx, func(ctx context.Context, txOrm orm.TxOrmer) error {
|
||
p.UpdateTime = time.Now()
|
||
p.Status = config.PAYFOR_BANKING
|
||
p.RequestTime = time.Now()
|
||
p.IsSend = config.YES
|
||
if _, err := txOrm.Update(&p); err != nil {
|
||
return err
|
||
}
|
||
|
||
RequestPayFor(ctx, p)
|
||
|
||
return nil
|
||
}); err != nil {
|
||
return false
|
||
}
|
||
return true
|
||
}
|
||
|
||
func SendPayFor(ctx context.Context, p payfor.PayforInfo) bool {
|
||
o := orm.NewOrm()
|
||
|
||
if err := o.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
|
||
var account accounts.AccountInfo
|
||
if err := txOrm.Raw("select * from account_info where account_uid = ? for update", p.MerchantUid).QueryRow(&account); err != nil || account.AccountUid == "" {
|
||
otelTrace.Logger.WithContext(ctx).Error("send payfor select account fail:", zap.Error(err))
|
||
return err
|
||
}
|
||
|
||
// 支付金额不足,将直接判定为失败,不往下面邹逻辑了
|
||
if account.SettleAmount-account.PayforAmount < p.PayforAmount+p.PayforFee {
|
||
p.Status = config.PAYFOR_FAIL
|
||
p.UpdateTime = time.Now()
|
||
|
||
if _, err := txOrm.Update(&p); err != nil {
|
||
return err
|
||
} else {
|
||
return nil
|
||
}
|
||
}
|
||
|
||
account.UpdateTime = time.Now()
|
||
account.PayforAmount = account.PayforAmount + p.PayforAmount + p.PayforFee
|
||
|
||
if _, err := txOrm.Update(&account); err != nil {
|
||
otelTrace.Logger.WithContext(ctx).Error(fmt.Sprintf("商户uid=%s,在发送代付给上游的处理中,更新账户表出错, err: %s", p.MerchantUid, err))
|
||
return err
|
||
}
|
||
|
||
p.IsSend = config.YES
|
||
p.Status = config.PAYFOR_BANKING // 变为银行处理中
|
||
p.GiveType = config.PAYFOR_ROAD
|
||
p.RequestTime = time.Now()
|
||
p.UpdateTime = time.Now()
|
||
|
||
if _, err := txOrm.Update(&p); err != nil {
|
||
otelTrace.Logger.WithContext(ctx).Error(fmt.Sprintf("商户uid=%s,在发送代付给上游的处理中,更代付列表出错, err:%s", p.MerchantUid, err))
|
||
return err
|
||
}
|
||
|
||
RequestPayFor(ctx, p)
|
||
|
||
return nil
|
||
}); err != nil {
|
||
return false
|
||
}
|
||
return true
|
||
}
|
||
|
||
func RequestPayFor(ctx context.Context, p payfor.PayforInfo) {
|
||
if p.RoadUid == "" {
|
||
return
|
||
}
|
||
p.Type = config.PAYFOR_ROAD
|
||
roadInfo := road.GetRoadInfoByRoadUid(ctx, p.RoadUid)
|
||
supplierCode := roadInfo.ProductUid
|
||
supplier := third_party.GetPaySupplierByCode(supplierCode)
|
||
res := supplier.PayFor(p)
|
||
otelTrace.Logger.WithContext(ctx).Info(fmt.Sprintf("代付uid=%s,上游处理结果为:%s", p.PayforUid, res))
|
||
// 将代付订单号发送到消息队列
|
||
message.SendMessage(ctx, config.MqPayForQuery, p.BankOrderId)
|
||
}
|
||
|
||
// PayForResultQuery 代付结果查询
|
||
func PayForResultQuery(ctx context.Context, params map[string]any) string {
|
||
query := make(map[string]any)
|
||
query["merchantOrderId"] = params["merchantOrderId"]
|
||
merchantInfo := merchant.GetMerchantByPasskey(ctx, convertor.ToString(params["merchantKey"]))
|
||
if !utils.Md5Verify(params, merchantInfo.MerchantSecret) {
|
||
query["resultMsg"] = "签名错误"
|
||
query["settStatus"] = "03"
|
||
query["sign"] = utils.GetMD5Sign(params, utils.SortMap(params), merchantInfo.MerchantSecret)
|
||
} else {
|
||
payForInfo := payfor.GetPayForByMerchantOrderId(ctx, convertor.ToString(params["merchantOrderId"]))
|
||
if payForInfo.BankOrderId == "" {
|
||
query["resultMsg"] = "不存在这样的代付订单"
|
||
query["settStatus"] = "03"
|
||
query["sign"] = utils.GetMD5Sign(params, utils.SortMap(params), merchantInfo.MerchantSecret)
|
||
} else {
|
||
switch payForInfo.Status {
|
||
case config.PAYFOR_BANKING:
|
||
query["resultMsg"] = "打款中"
|
||
query["settStatus"] = "02"
|
||
case config.PAYFOR_SOLVING:
|
||
query["resultMsg"] = "打款中"
|
||
query["settStatus"] = "02"
|
||
case config.PAYFOR_COMFRIM:
|
||
query["resultMsg"] = "打款中"
|
||
query["settStatus"] = "02"
|
||
case config.PAYFOR_SUCCESS:
|
||
query["resultMsg"] = "打款成功"
|
||
query["settStatus"] = "00"
|
||
query["settAmount"] = strconv.FormatFloat(payForInfo.PayforAmount, 'f', 2, 64)
|
||
query["settFee"] = strconv.FormatFloat(payForInfo.PayforFee, 'f', 2, 64)
|
||
case config.PAYFOR_FAIL:
|
||
query["resultMsg"] = "打款失败"
|
||
query["settStatus"] = "01"
|
||
}
|
||
query["sign"] = utils.GetMD5Sign(query, utils.SortMap(query), merchantInfo.MerchantSecret)
|
||
}
|
||
}
|
||
|
||
mJson, err := json.Marshal(query)
|
||
if err != nil {
|
||
otelTrace.Logger.WithContext(ctx).Error("PayForQuery json marshal fail:", zap.Error(err))
|
||
return fmt.Sprintf("PayForQuery json marshal fail:%s", err.Error())
|
||
} else {
|
||
return string(mJson)
|
||
}
|
||
}
|
||
|
||
// BalanceQuery 商户查询余额
|
||
func BalanceQuery(ctx context.Context, params map[string]any) string {
|
||
balanceResponse := new(response.BalanceResponse)
|
||
str := ""
|
||
merchantInfo := merchant.GetMerchantByPasskey(ctx, convertor.ToString(params["merchantKey"]))
|
||
if !utils.Md5Verify(params, merchantInfo.MerchantSecret) {
|
||
balanceResponse.ResultCode = "-1"
|
||
balanceResponse.ResultMsg = "签名错误"
|
||
mJson, _ := json.Marshal(balanceResponse)
|
||
str = string(mJson)
|
||
} else {
|
||
accountInfo := accounts.GetAccountByUid(ctx, merchantInfo.MerchantUid)
|
||
tmp := make(map[string]any)
|
||
tmp["resultCode"] = "00"
|
||
tmp["balance"] = strconv.FormatFloat(accountInfo.Balance, 'f', 2, 64)
|
||
tmp["availableAmount"] = strconv.FormatFloat(accountInfo.SettleAmount, 'f', 2, 64)
|
||
tmp["freezeAmount"] = strconv.FormatFloat(accountInfo.FreezeAmount, 'f', 2, 64)
|
||
tmp["waitAmount"] = strconv.FormatFloat(accountInfo.WaitAmount, 'f', 2, 64)
|
||
tmp["loanAmount"] = strconv.FormatFloat(accountInfo.LoanAmount, 'f', 2, 64)
|
||
tmp["payforAmount"] = strconv.FormatFloat(accountInfo.PayforAmount, 'f', 2, 64)
|
||
tmp["resultMsg"] = "查询成功"
|
||
tmp["sign"] = utils.GetMD5Sign(tmp, utils.SortMap(tmp), merchantInfo.MerchantSecret)
|
||
mJson, _ := json.Marshal(tmp)
|
||
str = string(mJson)
|
||
}
|
||
|
||
return str
|
||
}
|
||
|
||
func checkSettAmount(ctx context.Context, settAmount string) (bool, string) {
|
||
_, err := strconv.ParseFloat(settAmount, 64)
|
||
if err != nil {
|
||
otelTrace.Logger.WithContext(ctx).Error(fmt.Sprintf("代付金额有误,settAmount = %s", settAmount))
|
||
return false, "代付金额有误"
|
||
}
|
||
return true, ""
|
||
}
|