All checks were successful
continuous-integration/drone/push Build is passing
- 调整Camel相关函数的链路追踪span名称,保持一致性 - 请求失败时若包含“不匹配”,错误信息追加“请按照正确金额重新提交” - 修改Careless相关逻辑,更新URL匹配规则 - 修正测试用例中的IP固定值和请求地址 - 优化FavorableClouds提交订单HTTP请求,统一使用resty客户端并添加代理传递 - 修复Jinke支付通知链路追踪span名称及事件描述 - 调整ScanController中SubmitPool协程的context传递,避免丢失父context
285 lines
9.2 KiB
Go
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
|
|
}
|