Files
kami_gateway/internal/service/supplier/third_party/jinke.go
danial aca94de06f
All checks were successful
continuous-integration/drone/push Build is passing
fix(camel): 修正链路追踪名称及错误提示优化
- 调整Camel相关函数的链路追踪span名称,保持一致性
- 请求失败时若包含“不匹配”,错误信息追加“请按照正确金额重新提交”
- 修改Careless相关逻辑,更新URL匹配规则
- 修正测试用例中的IP固定值和请求地址
- 优化FavorableClouds提交订单HTTP请求,统一使用resty客户端并添加代理传递
- 修复Jinke支付通知链路追踪span名称及事件描述
- 调整ScanController中SubmitPool协程的context传递,避免丢失父context
2025-12-18 17:31:25 +08:00

285 lines
9.2 KiB
Go

package third_party
import (
"context"
"encoding/json"
"fmt"
"gateway/internal/otelTrace"
"gateway/internal/service/client"
"gateway/internal/models/merchant"
"gateway/internal/models/merchant_deploy"
"gateway/internal/models/order"
"gateway/internal/models/payfor"
"gateway/internal/models/road"
"gateway/internal/models/supply_model"
"gateway/internal/service"
"gateway/internal/service/supplier"
"gateway/internal/service/supplier/third_party/pool/card_sender"
"net/url"
"strconv"
"github.com/beego/beego/v2/server/web"
"github.com/duke-git/lancet/v2/convertor"
"github.com/duke-git/lancet/v2/maputil"
"github.com/duke-git/lancet/v2/netutil"
"github.com/duke-git/lancet/v2/pointer"
"github.com/widuu/gojson"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
)
type JinkeImpl struct {
web.Controller
}
func (f *JinkeImpl) sendCard(ctx context.Context, cardInfo supplier.RedeemCardInfo, orderInfo order.OrderInfo, roadInfo road.RoadInfo, merchantInfo merchant.MerchantInfo) (bool, string) {
returnIfAmountErr := gojson.Json(roadInfo.Params).Get("returnIfAmountErr").Tostring()
orderAmount, err := (&cardTypeQuery{
OrderNo: orderInfo.BankOrderId,
QueryType: gojson.Json(roadInfo.Params).Get("queryType").Tostring(),
CardNo: cardInfo.CardNo,
CardPwd: cardInfo.Data,
Balance: cardInfo.GetFaceTypeFloat(ctx),
}).GetBalance(ctx)
if returnIfAmountErr == "1" && err != nil {
return false, err.Error()
}
cardInfo.FaceType = strconv.FormatFloat(orderAmount, 'f', 2, 64)
merchantDeploy := merchant_deploy.GetMerchantDeployByUidAndRoadUid(ctx, merchantInfo.MerchantUid, roadInfo.RoadUid)
if merchantDeploy.MerchantUid == "" {
return false, "商户不存在"
}
if merchantDeploy.SubmitStrategy == merchant_deploy.SUBMIT_STRATEGY_POOL {
if err := orderPoolService.PushOrder(ctx, card_sender.SendCardTask{
CardInfo: cardInfo,
RoadUid: roadInfo.RoadUid,
LocalOrderID: orderInfo.BankOrderId,
SendCardTaskType: card_sender.SendCardTaskTypeEnumJinke,
NeedQuery: gojson.Json(roadInfo.Params).Get("needQuery").Tostring() == "1",
}); err != nil {
otelTrace.Logger.WithContext(ctx).Error("推送订单失败", zap.Error(err))
return false, err.Error()
}
return true, "订单已提交"
}
if err := orderPoolService.SubmitOrder(ctx, card_sender.SendCardTask{
CardInfo: cardInfo,
RoadUid: roadInfo.RoadUid,
LocalOrderID: orderInfo.BankOrderId,
SendCardTaskType: card_sender.SendCardTaskTypeEnumJinke,
NeedQuery: gojson.Json(roadInfo.Params).Get("needQuery").Tostring() == "1",
}); err != nil {
return false, err.Error()
}
return true, "订单已提交"
}
func (f *JinkeImpl) Scan(ctx context.Context, orderInfo order.OrderInfo, roadInfo road.RoadInfo, info3 merchant.MerchantInfo) supplier.ScanData {
ctx, span := otelTrace.Span(ctx, "JinkeImpl", "Scan", trace.WithAttributes(
attribute.String("BankOrderId", orderInfo.BankOrderId),
attribute.String("MerchantUid", orderInfo.MerchantUid),
attribute.String("ExValue", orderInfo.ExValue),
))
defer span.End()
cdata := supplier.RedeemCardInfo{}
err := json.Unmarshal([]byte(orderInfo.ExValue), &cdata)
if err != nil {
otelTrace.Logger.WithContext(ctx).Error("格式化数据失败", zap.String("ExValue", orderInfo.ExValue))
return supplier.ScanData{Status: "-1", Msg: "订单有误,请稍后再试"}
}
ok, response := f.sendCard(ctx, cdata, orderInfo, roadInfo, info3)
if !ok {
return supplier.ScanData{
Status: "-1",
Msg: "失败:" + response,
BankNo: orderInfo.MerchantOrderId,
OrderNo: orderInfo.BankOrderId,
ReturnData: response,
}
}
scanData := supplier.ScanData{
OrderNo: orderInfo.BankOrderId,
BankNo: orderInfo.MerchantOrderId,
OrderPrice: strconv.FormatFloat(orderInfo.OrderAmount, 'f', 2, 64),
Status: "00",
PayUrl: "",
Msg: "",
ReturnData: response,
UpStreamOrderNo: "",
}
return scanData
}
func (f *JinkeImpl) PayNotify() {
ctx := f.Ctx.Request.Context()
ctx, span := otelTrace.Span(ctx, "JinkeImpl", "JinkeImpl.PayNotify")
defer span.End()
resp := struct {
MchId int `json:"mchId"`
ProductId string `json:"productId"`
TradeNo string `json:"tradeNo"`
OutTradeNo string `json:"outTradeNo"`
Amount int `json:"amount"`
PayAmount int `json:"payAmount"`
State int `json:"state"`
CreateTime string `json:"createTime"`
PayTime string `json:"payTime"`
Sign string `json:"sign"`
}{}
err := f.BindJSON(&resp)
if err != nil {
otelTrace.Logger.WithContext(ctx).Error("解析参数失败", zap.Error(err))
f.Ctx.WriteString("FAIL")
return
}
span.AddEvent("解析参数成功", trace.WithAttributes(
attribute.String("mchId", strconv.Itoa(resp.MchId)),
attribute.String("productId", resp.ProductId),
attribute.String("tradeNo", resp.TradeNo),
attribute.String("outTradeNo", resp.OutTradeNo),
attribute.Int("amount", resp.Amount),
attribute.Int("payAmount", resp.PayAmount),
attribute.Int("state", resp.State),
))
otelTrace.Logger.WithContext(ctx).Info("金科回调", zap.Any("resp", resp))
localId, err := orderPoolService.GetLocalIdByOrderId(ctx, resp.OutTradeNo)
var orderInfo order.OrderInfo
if err != nil {
orderInfo = order.GetOrderByPoolOrderId(ctx, resp.OutTradeNo)
if orderInfo.Id == 0 {
otelTrace.Logger.WithContext(ctx).Error("【金科】获取本地订单ID失败", zap.Error(err))
f.Ctx.WriteString("FAIL")
return
}
} else {
orderInfo = order.GetOrderByBankOrderId(ctx, localId)
}
if orderInfo.Status != "wait" {
f.Ctx.WriteString("SUCCESS")
return
}
if resp.State == 1 {
money, err := convertor.ToFloat(resp.PayAmount / 100)
if err != nil {
otelTrace.Logger.WithContext(ctx).Error("金额转换失败", zap.Error(err))
f.Ctx.WriteString("FAIL")
}
if money != orderInfo.OrderAmount {
f.Ctx.WriteString("SUCCESS")
isOk := service.SolvePayFail(f.Ctx.Request.Context(), orderInfo.BankOrderId, resp.TradeNo, fmt.Sprintf("金额不一致 卡面金额:%.2f 实际金额:%.2f", orderInfo.OrderAmount, money))
if isOk {
f.Ctx.WriteString("SUCCESS")
} else {
f.Ctx.WriteString("FAIL")
}
return
}
isOk := service.SolvePaySuccess(ctx, orderInfo.BankOrderId, money, resp.TradeNo, "支付成功")
if isOk {
f.Ctx.WriteString("SUCCESS")
} else {
f.Ctx.WriteString("FAIL")
}
return
}
roadInfo := road.GetRoadInfoByRoadUid(ctx, orderInfo.RoadUid)
if roadInfo.RoadUid == "" || len(roadInfo.RoadUid) == 0 {
otelTrace.Logger.WithContext(ctx).Error("【祥云】支付通道已经关系或者删除,不进行回调")
f.Ctx.WriteString("fail")
return
}
cdata := supplier.RedeemCardInfo{}
_ = json.Unmarshal([]byte(orderInfo.ExValue), &cdata)
errMsg := "支付失败"
if gojson.Json(roadInfo.Params).Get("jvnkaQuery").Tostring() == "1" {
retMsg, _ := client.NewHeePayClient().ToString(ctx, &client.QueryCardInput{
OrderId: orderInfo.BankOrderId,
CardNumber: cdata.CardNo,
CardPassword: cdata.Data,
})
if retMsg != "" {
errMsg = retMsg
}
}
isOk := service.SolvePayFail(f.Ctx.Request.Context(), orderInfo.BankOrderId, resp.TradeNo, errMsg)
if isOk {
f.Ctx.WriteString("SUCCESS")
} else {
f.Ctx.WriteString("FAIL")
}
f.Ctx.WriteString("SUCCESS")
}
func (f *JinkeImpl) PayQuery(info order.OrderInfo, info2 road.RoadInfo) bool {
//TODO implement me
panic("implement me")
}
func (f *JinkeImpl) PayQueryV2(info order.OrderInfo, roadInfo road.RoadInfo) supply_model.MsgModel {
ctx := f.Ctx.Request.Context()
params := map[string]any{
"mid": "",
}
params["sign"] = sign(params, maputil.Keys(params), gojson.Json(roadInfo.Params).Get("paySecret").Tostring())
urlParams := url.Values{}
for k, v := range params {
urlParams.Add(k, v.(string))
}
request := &netutil.HttpRequest{
RawURL: "http://ti8lj4yj3am3mr4id0tl.itxitongsf.xyz/api/services/app/Api_PayOrder/QueryBalance",
Method: "POST",
FormData: urlParams,
}
httpClient := netutil.NewHttpClient()
resp, err := httpClient.SendRequest(request)
if err != nil || resp.StatusCode != 200 {
otelTrace.Logger.WithContext(ctx).Error("发送数据失败", zap.Error(err))
return "发送数据失败"
}
response := struct {
Status int `json:"status"`
ErrMsg interface{} `json:"errMsg"`
Result struct {
MerName string `json:"merName"`
Mid string `json:"mid"`
Balance string `json:"balance"`
} `json:"result"`
}{}
err = httpClient.DecodeResponse(resp, &response)
if err != nil || pointer.IsNil(response) {
otelTrace.Logger.WithContext(ctx).Error("解析分解会结构失败", zap.Error(err), zap.Any("response", response))
return "解析分解会结构失败"
}
return convertor.ToString(response.Result.Balance)
}
func (f *JinkeImpl) PayFor(info payfor.PayforInfo) string {
//TODO implement me
panic("implement me")
}
func (f *JinkeImpl) PayForNotify() string {
//TODO implement me
panic("implement me")
}
func (f *JinkeImpl) PayForQuery(info payfor.PayforInfo) (string, string) {
//TODO implement me
panic("implement me")
}
func (f *JinkeImpl) BalanceQuery(info road.RoadInfo) float64 {
//TODO implement me
panic("implement me")
}
func (f *JinkeImpl) HasDependencyHTML() bool {
return false
}