Files
kami_gateway/internal/service/pay_for/payfor_service.go
danial e88ff05a14 refactor(trace): 重命名 otel 包为 otelTrace并更新相关引用
- 将内部使用的 otel 包重命名为 otelTrace
- 更新了所有引用该包的文件中的导入路径
- 修改了部分函数和变量名称以适应新的包名
2025-02-23 21:56:29 +08:00

355 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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, ""
}