This commit is contained in:
2026-02-06 13:34:49 +08:00
parent 9648fcd1ec
commit dc4fcbf857
12 changed files with 1796 additions and 757 deletions

View File

@@ -20,12 +20,19 @@ VerifyCode:
Encrypt:
SecretKey: "ff83609b2b24fc73196aac3d3dfb874f"
Alipay:
AppID: "2021004161631930"
PrivateKey: "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCRrZNr8DNs4LhPSulTLEg4RLREWVSFGS+Nl5Q2FxQ8DgkUYV+p3kfi4XmB2W/Ruz4egPxEB0V/xj75OktVphVKY8rI6OaNnVoFVe5NqGa5MTj3wLwBIv/hMHA1VAru2KLIv9R1FR7LpWmreHSkpJ65CD2mZqYuMCekOfzMQZIGgSagEU4my0bLbFWw7M3qZz4vm2KUtm4Ew28OUJDkqygjPzXgS5l5niYQvqPjiQNdnTtoIcNcHo07tS8lmf/hdgq9EtVfY7Y0brESfgvOoVJeg1hTHEj0hyWnnWPeA4HD2izANP/5ObRX4ZVqpVju+7PSpbeFd71fxbR1blAVnrqTAgMBAAECggEASpkwHN3r9507xJ7/zG+oq+fCyB1WgrHbAA7W/rviyL4HOECE1F/XP/9mUXAfKq9PqB81D0EJ/dxu8wE/AqUB0g44EZnyNiKVrpXKakoKEFt8aKJxo8NgdNhxHV3kG1skQNi62xntoysZaY1NbeI+xVHLACMghhZytk5bfd02Ac3rMBz3X8Cl1R+3mgU0zFc5f476VRxywiRQM+QNJIaHDNB4vw1TKI0K92mEKD8lOuNZD7d5TCBZi3r08t8FFAkMjIMDiFvFRFmAqMg3NyaIGUkLVDU2zUP0Vlzmo9ghCV9hluqDqeP4RhxQydOw+rxGBk+crYQBhPyYOI/I9PFXAQKBgQDHSRRTPqYbCfztmwk3AIH7VN6izyU3FljEXAsdf+UVJpRa8429J3e+sB96jxhiwVlCzX4CDjsa/Pu0iQQx22a0AZs5GTE0MJ1FVydfGlyqF6/hRS4TswSkklW3be7/KDAjgj2+/wap+mN7rRmDkdvxgCJG6MiWuRAthhg/g16wIwKBgQC7Iu7D4yQXRKheL6p6pbMtE+oD58/EJ2vO8ZUz3LiPc9pZ6+bp4nkBP6JOuYiB5jkzWQifKe6hsXpv06kWzaBEzz4f4SUpWDmdBchNoct3pB/k66FaxHLO/pG4RV86hqscqTdutmdC62bbwM6yCtJ+3rS5rlCxDGQkGJbM+wM60QKBgH5nQyYeCbwC1NRdTzX883VYerLoEyHi4cEC5OX8NnD4/IbIDzJYc2KXUhAp7XzOSPDPaMqi/ih7KKh1dByvnnA0yKEp8oS5BThzNHzlOruEtMF9YOGL3jkIvKfRahOcCRSsyr94AWEVeb57qEBE5y5CaPtzMbAwiCtn779xc0DjAoGAZwEGXWokDm6rIhSoiJO2OQSyFW4+LSDptWHCF2bRa5yAPmiblHck1awaAa0b1yxKpdnG5hzljbirxOvDMZsDMXzFHDUICGbYZ3asVxbMcNE1AQM1sElbTFZRDRWaIhPIEaGOsnDSC8KYvjK1UsikLlMVNPMe1SUV5cxnDPLJR1ECgYEAw8M09uLylPtfGq7oyE2R6xC2kUA8EJ6aapJgUs/UZ6dtjtvudbYzUo0Cgnb12hpN3hfLc5O0/P4nRzZ72Hm43cMiRNLJi4BYCa0m/mCxq+RcoBWYQTIraHnR17yIQhxt5IBRVjgbvYCnryx5Jd5wjOvv7DdnGFJLepzSJwlGqeU="
AppID: "2021006121698606"
PrivateKey: "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCx3TqaCUgZIMfG8Lu8fqkKqKLzONB9NBp4qCM2CAgxkUK1fd48XZ3ANDzfzNoeIbLbR/VL3ZUgyX/F8jO7Cit71JS58X9b1ngeRyBdaiBhLZgHb7Ngg0f1zDJuq8H6/i3Tiu9PaH0iOFKeaFCCpkakFyXjp//rqkcj2bPfa6lX0bD4YcILz8DC3iPy/cXHrHwaBryhYouYA3bKFROHykfF5D+tS2PZdR13BiDVElwDuquZCMdRdISfTw8snp5HSxA0TyRZIiGrtU3WvSKxYi2s8smZl/uYN1uj4w4GOivnTrkBdLy6DBqvgqRsCeYDDV5UyjtbAhvksZH60EWS3HTfAgMBAAECggEAK4dv+xjAa13pZpet6nC5ICGrV4kVBT9GJzdG/scycicRw2cdh3qFy+884qy4yN0Ib8AJmVqOT6rguWoQHPtdLv4Us/kVaT1wwkA3/ISnjgDhjxhYNwuKBe7GfO1OGQYx4u7CqJVy4ngUSC5RXdghu7Dqle+co2lV5cE20zv/Ar2nYo4p9fxcl/XAttqdRyby3ge6zJZP46Ru4CHzFzyUsrJYC575R3Jq68Zrr/+v0BmpEm/wAmoQE8W0ElMMH+Jw0twj+4l6PaUcq9oUVIL9wzl7ay1B6dKyxEOyinGYrmd65NzsTu8HhEpFdvQ+1O27XyahLsNWpSamJlV23ns/EQKBgQDd2UiQQRrj+itfa1/Zehy8MstEICw73LMDk129yNG9cU8U4y5vaTJkAcDTfme08B3uVVhsIUhJsFdgnh4ayyYW3jsB5BBBgszhwYQciH7f/3nE3rcX/TIs6yQAQkPhQi2RNSdn3SrCIhxgu9TwrZIDyQYJ6kk/9qEmZd93RrhOBwKBgQDNPpS1jFt4l08kzCHzfTvTbIu5z/6NPxmRSpcrdkoOAUfzlKSkRnKJuzkFSJEmWpFXILfNiza1dBYncg60Lu58yXpaGlLbVmxeFem+DdQm55lgl28d8Ssg6nlCTqE/tjB5LZ6qn8KEkWSj6gXH37pU1XFZh0A3+EIytnL7NnrsaQKBgGMaGUw3iSemLZHmiV7BKez4U80PAjOLl3xVbF7HQsp5v3X5NlkWiSgbkGPp57HwQa6h+Wn0RDKGz8GdYJ1fephklb92fbyGDbgblkSYxPSTT3Yed3QD61IdiGuFLoWF5o0jTYMcTWmDi2G7BpitMLj4J/Zt7mLgbYSVpYnG0bYpAoGAS7Tfya/CNdMqQFqD03rITI5nY9zS+mriFXO8Gy4A1vWmArU7ndTWfvNubwJ7d/hEUC0jX1AQmBH/8gDiZ5hAJAt1dDLtiTZxtqrCk3YqYUdgjf6N4C+LRxL2M30pgYTEkI5BTpKrf5bZ1pSGGVnvM0egDfQTvhF26ZnfA8buxLECgYAssWTLQ2Ou2NJ8N3HJx54kpKLwf218ulINsH6opcot22hbVfj0rc7lAcMe9FTGgmSgrbZG9QqAC2BpSHDeSC4C57iSYhG86tq/jwvNsV1miPV9RRj3CvwZkbHzCwqhRvCPS/QUgTzG3Z9pljYLkZjuMkb9jp814GbwEGPeoucefw=="
AlipayPublicKey: ""
AppCertPath: "etc/merchant/appCertPublicKey_2021004161631930.crt"
AppCertPath: "etc/merchant/appCertPublicKey_2021006121698606.crt"
AlipayCertPath: "etc/merchant/alipayCertPublicKey_RSA2.crt"
AlipayRootCertPath: "etc/merchant/alipayRootCert.crt"
AppIDBak: "2021004161631930"
PrivateKeyBak: "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCRrZNr8DNs4LhPSulTLEg4RLREWVSFGS+Nl5Q2FxQ8DgkUYV+p3kfi4XmB2W/Ruz4egPxEB0V/xj75OktVphVKY8rI6OaNnVoFVe5NqGa5MTj3wLwBIv/hMHA1VAru2KLIv9R1FR7LpWmreHSkpJ65CD2mZqYuMCekOfzMQZIGgSagEU4my0bLbFWw7M3qZz4vm2KUtm4Ew28OUJDkqygjPzXgS5l5niYQvqPjiQNdnTtoIcNcHo07tS8lmf/hdgq9EtVfY7Y0brESfgvOoVJeg1hTHEj0hyWnnWPeA4HD2izANP/5ObRX4ZVqpVju+7PSpbeFd71fxbR1blAVnrqTAgMBAAECggEASpkwHN3r9507xJ7/zG+oq+fCyB1WgrHbAA7W/rviyL4HOECE1F/XP/9mUXAfKq9PqB81D0EJ/dxu8wE/AqUB0g44EZnyNiKVrpXKakoKEFt8aKJxo8NgdNhxHV3kG1skQNi62xntoysZaY1NbeI+xVHLACMghhZytk5bfd02Ac3rMBz3X8Cl1R+3mgU0zFc5f476VRxywiRQM+QNJIaHDNB4vw1TKI0K92mEKD8lOuNZD7d5TCBZi3r08t8FFAkMjIMDiFvFRFmAqMg3NyaIGUkLVDU2zUP0Vlzmo9ghCV9hluqDqeP4RhxQydOw+rxGBk+crYQBhPyYOI/I9PFXAQKBgQDHSRRTPqYbCfztmwk3AIH7VN6izyU3FljEXAsdf+UVJpRa8429J3e+sB96jxhiwVlCzX4CDjsa/Pu0iQQx22a0AZs5GTE0MJ1FVydfGlyqF6/hRS4TswSkklW3be7/KDAjgj2+/wap+mN7rRmDkdvxgCJG6MiWuRAthhg/g16wIwKBgQC7Iu7D4yQXRKheL6p6pbMtE+oD58/EJ2vO8ZUz3LiPc9pZ6+bp4nkBP6JOuYiB5jkzWQifKe6hsXpv06kWzaBEzz4f4SUpWDmdBchNoct3pB/k66FaxHLO/pG4RV86hqscqTdutmdC62bbwM6yCtJ+3rS5rlCxDGQkGJbM+wM60QKBgH5nQyYeCbwC1NRdTzX883VYerLoEyHi4cEC5OX8NnD4/IbIDzJYc2KXUhAp7XzOSPDPaMqi/ih7KKh1dByvnnA0yKEp8oS5BThzNHzlOruEtMF9YOGL3jkIvKfRahOcCRSsyr94AWEVeb57qEBE5y5CaPtzMbAwiCtn779xc0DjAoGAZwEGXWokDm6rIhSoiJO2OQSyFW4+LSDptWHCF2bRa5yAPmiblHck1awaAa0b1yxKpdnG5hzljbirxOvDMZsDMXzFHDUICGbYZ3asVxbMcNE1AQM1sElbTFZRDRWaIhPIEaGOsnDSC8KYvjK1UsikLlMVNPMe1SUV5cxnDPLJR1ECgYEAw8M09uLylPtfGq7oyE2R6xC2kUA8EJ6aapJgUs/UZ6dtjtvudbYzUo0Cgnb12hpN3hfLc5O0/P4nRzZ72Hm43cMiRNLJi4BYCa0m/mCxq+RcoBWYQTIraHnR17yIQhxt5IBRVjgbvYCnryx5Jd5wjOvv7DdnGFJLepzSJwlGqeU="
AlipayPublicKeyBak: ""
AppCertPathBak: "etc/merchant/bak/appCertPublicKey_2021004161631930.crt"
AlipayCertPathBak: "etc/merchant/bak/alipayCertPublicKey_RSA2.crt"
AlipayRootCertPathBak: "etc/merchant/bak/alipayRootCert.crt"
IsProduction: true
NotifyUrl: "https://www.tianyuancha.cn/api/v1/pay/alipay/callback"
ReturnURL: "https://www.tianyuancha.cn/payment/result"
@@ -52,7 +59,7 @@ Ali:
Code: "d55b58829efb41c8aa8e86769cba4844"
SystemConfig:
ThreeVerify: false
CommissionSafeMode: false # 佣金安全防御模式true-冻结模式false-直接结算模式
CommissionSafeMode: false # 佣金安全防御模式true-冻结模式false-直接结算模式
WechatH5:
AppID: "wxa581992dc74d860e"
AppSecret: "4de1fbf521712247542d49907fcd5dbf"

View File

@@ -37,6 +37,13 @@ Alipay:
AppCertPath: "etc/merchant/appCertPublicKey_2021006121698606.crt"
AlipayCertPath: "etc/merchant/alipayCertPublicKey_RSA2.crt"
AlipayRootCertPath: "etc/merchant/alipayRootCert.crt"
AppIDBak: "2021004161631930"
PrivateKeyBak: "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCRrZNr8DNs4LhPSulTLEg4RLREWVSFGS+Nl5Q2FxQ8DgkUYV+p3kfi4XmB2W/Ruz4egPxEB0V/xj75OktVphVKY8rI6OaNnVoFVe5NqGa5MTj3wLwBIv/hMHA1VAru2KLIv9R1FR7LpWmreHSkpJ65CD2mZqYuMCekOfzMQZIGgSagEU4my0bLbFWw7M3qZz4vm2KUtm4Ew28OUJDkqygjPzXgS5l5niYQvqPjiQNdnTtoIcNcHo07tS8lmf/hdgq9EtVfY7Y0brESfgvOoVJeg1hTHEj0hyWnnWPeA4HD2izANP/5ObRX4ZVqpVju+7PSpbeFd71fxbR1blAVnrqTAgMBAAECggEASpkwHN3r9507xJ7/zG+oq+fCyB1WgrHbAA7W/rviyL4HOECE1F/XP/9mUXAfKq9PqB81D0EJ/dxu8wE/AqUB0g44EZnyNiKVrpXKakoKEFt8aKJxo8NgdNhxHV3kG1skQNi62xntoysZaY1NbeI+xVHLACMghhZytk5bfd02Ac3rMBz3X8Cl1R+3mgU0zFc5f476VRxywiRQM+QNJIaHDNB4vw1TKI0K92mEKD8lOuNZD7d5TCBZi3r08t8FFAkMjIMDiFvFRFmAqMg3NyaIGUkLVDU2zUP0Vlzmo9ghCV9hluqDqeP4RhxQydOw+rxGBk+crYQBhPyYOI/I9PFXAQKBgQDHSRRTPqYbCfztmwk3AIH7VN6izyU3FljEXAsdf+UVJpRa8429J3e+sB96jxhiwVlCzX4CDjsa/Pu0iQQx22a0AZs5GTE0MJ1FVydfGlyqF6/hRS4TswSkklW3be7/KDAjgj2+/wap+mN7rRmDkdvxgCJG6MiWuRAthhg/g16wIwKBgQC7Iu7D4yQXRKheL6p6pbMtE+oD58/EJ2vO8ZUz3LiPc9pZ6+bp4nkBP6JOuYiB5jkzWQifKe6hsXpv06kWzaBEzz4f4SUpWDmdBchNoct3pB/k66FaxHLO/pG4RV86hqscqTdutmdC62bbwM6yCtJ+3rS5rlCxDGQkGJbM+wM60QKBgH5nQyYeCbwC1NRdTzX883VYerLoEyHi4cEC5OX8NnD4/IbIDzJYc2KXUhAp7XzOSPDPaMqi/ih7KKh1dByvnnA0yKEp8oS5BThzNHzlOruEtMF9YOGL3jkIvKfRahOcCRSsyr94AWEVeb57qEBE5y5CaPtzMbAwiCtn779xc0DjAoGAZwEGXWokDm6rIhSoiJO2OQSyFW4+LSDptWHCF2bRa5yAPmiblHck1awaAa0b1yxKpdnG5hzljbirxOvDMZsDMXzFHDUICGbYZ3asVxbMcNE1AQM1sElbTFZRDRWaIhPIEaGOsnDSC8KYvjK1UsikLlMVNPMe1SUV5cxnDPLJR1ECgYEAw8M09uLylPtfGq7oyE2R6xC2kUA8EJ6aapJgUs/UZ6dtjtvudbYzUo0Cgnb12hpN3hfLc5O0/P4nRzZ72Hm43cMiRNLJi4BYCa0m/mCxq+RcoBWYQTIraHnR17yIQhxt5IBRVjgbvYCnryx5Jd5wjOvv7DdnGFJLepzSJwlGqeU="
AlipayPublicKeyBak: ""
AppCertPathBak: "etc/merchant/bak/appCertPublicKey_2021004161631930.crt"
AlipayCertPathBak: "etc/merchant/bak/alipayCertPublicKey_RSA2.crt"
AlipayRootCertPathBak: "etc/merchant/bak/alipayRootCert.crt"
IsProduction: true
NotifyUrl: "https://www.tianyuancha.cn/api/v1/pay/alipay/callback"
ReturnURL: "https://www.tianyuancha.cn/payment/result"
@@ -63,7 +70,7 @@ Ali:
Code: "d55b58829efb41c8aa8e86769cba4844"
SystemConfig:
ThreeVerify: true
CommissionSafeMode: false # 佣金安全防御模式true-冻结模式false-直接结算模式
CommissionSafeMode: false # 佣金安全防御模式true-冻结模式false-直接结算模式
WechatH5:
AppID: "wxa581992dc74d860e"
AppSecret: "4de1fbf521712247542d49907fcd5dbf"

View File

@@ -53,9 +53,16 @@ type AlipayConfig struct {
AppCertPath string // 应用公钥证书路径
AlipayCertPath string // 支付宝公钥证书路径
AlipayRootCertPath string // 根证书路径
IsProduction bool
NotifyUrl string
ReturnURL string
// Bak 仅用于 2026-02-02 18:26 之前的订单退款
AppIDBak string
PrivateKeyBak string
AlipayPublicKeyBak string
AppCertPathBak string
AlipayCertPathBak string
AlipayRootCertPathBak string
IsProduction bool
NotifyUrl string
ReturnURL string
}
type WxpayConfig struct {
AppID string

View File

@@ -75,10 +75,13 @@ func (l *AdminRefundOrderLogic) getAndValidateOrder(orderId int64, refundAmount
return order, nil
}
// handleAlipayRefund 处理支付宝退款
// handleAlipayRefund 处理支付宝退款2026-02-02 18:26 前的订单走 bak 商户号)
func (l *AdminRefundOrderLogic) handleAlipayRefund(order *model.Order, req *types.AdminRefundOrderReq) (*types.AdminRefundOrderResp, error) {
// 调用支付宝退款接口
refundResp, err := l.svcCtx.AlipayService.AliRefund(l.ctx, order.OrderNo, req.RefundAmount)
orderPayTime := &order.CreateTime
if order.PayTime.Valid {
orderPayTime = &order.PayTime.Time
}
refundResp, err := l.svcCtx.AlipayService.AliRefund(l.ctx, order.OrderNo, req.RefundAmount, orderPayTime)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "AdminRefundOrder, 支付宝退款失败 err: %v", err)
}

View File

@@ -218,7 +218,9 @@ func (l *AlipayCallbackLogic) handleRefund(order *model.AgentMembershipRechargeO
return refundErr
}
} else {
refund, refundErr := l.svcCtx.AlipayService.AliRefund(ctx, order.OrderNo, order.Amount)
// 代理会员订单以创建时间为准2026-02-02 18:26 前的订单走 bak 商户号
orderPayTime := order.CreateTime
refund, refundErr := l.svcCtx.AlipayService.AliRefund(ctx, order.OrderNo, order.Amount, &orderPayTime)
if refundErr != nil {
return refundErr
}

View File

@@ -221,7 +221,9 @@ func (l *WechatPayCallbackLogic) handleRefund(order *model.AgentMembershipRechar
return refundErr
}
} else {
refund, refundErr := l.svcCtx.AlipayService.AliRefund(ctx, order.OrderNo, order.Amount)
// 代理会员订单以创建时间为准2026-02-02 18:26 前的订单走 bak 商户号
orderPayTime := order.CreateTime
refund, refundErr := l.svcCtx.AlipayService.AliRefund(ctx, order.OrderNo, order.Amount, &orderPayTime)
if refundErr != nil {
return refundErr
}

File diff suppressed because it is too large Load Diff

View File

@@ -266,8 +266,12 @@ func (l *PaySuccessNotifyUserHandler) handleError(ctx context.Context, err error
logx.Infof("已发起微信退款申请, orderID: %d, amount: %f", order.Id, order.Amount)
return asynq.SkipRetry
} else {
// 支付宝退款为同步结果,这里直接根据返回结果更新订单和佣金/钱包
refund, refundErr := l.svcCtx.AlipayService.AliRefund(ctx, order.OrderNo, order.Amount)
// 支付宝退款为同步结果,2026-02-02 18:26 前的订单走 bak 商户号
orderPayTime := &order.CreateTime
if order.PayTime.Valid {
orderPayTime = &order.PayTime.Time
}
refund, refundErr := l.svcCtx.AlipayService.AliRefund(ctx, order.OrderNo, order.Amount, orderPayTime)
if refundErr != nil {
logx.Error(refundErr)
return asynq.SkipRetry

View File

@@ -16,9 +16,13 @@ import (
"github.com/smartwalle/alipay/v3"
)
// AlipayBakRefundCutoff bak 支付宝仅用于该时间点之前的订单退款2026年2月2日 18:26 下午CST
var AlipayBakRefundCutoff = time.Date(2026, 2, 2, 18, 26, 0, 0, time.FixedZone("CST", 8*3600))
type AliPayService struct {
config config.AlipayConfig
AlipayClient *alipay.Client
config config.AlipayConfig
AlipayClient *alipay.Client
AlipayClientBak *alipay.Client // 仅用于 2026-02-02 18:26 前订单的退款
}
// NewAliPayService 是一个构造函数,用于初始化 AliPayService
@@ -44,10 +48,30 @@ func NewAliPayService(c config.Config) *AliPayService {
panic(fmt.Sprintf("加载根证书失败: %v", err))
}
return &AliPayService{
svc := &AliPayService{
config: c.Alipay,
AlipayClient: client,
}
// 初始化 bak 支付宝客户端(仅用于 2026-02-02 18:26 前订单的退款)
if c.Alipay.AppIDBak != "" && c.Alipay.PrivateKeyBak != "" {
bakClient, err := alipay.New(c.Alipay.AppIDBak, c.Alipay.PrivateKeyBak, c.Alipay.IsProduction)
if err != nil {
panic(fmt.Sprintf("创建支付宝 bak 客户端失败: %v", err))
}
if err = bakClient.LoadAppCertPublicKeyFromFile(c.Alipay.AppCertPathBak); err != nil {
panic(fmt.Sprintf("加载 bak 应用公钥证书失败: %v", err))
}
if err = bakClient.LoadAlipayCertPublicKeyFromFile(c.Alipay.AlipayCertPathBak); err != nil {
panic(fmt.Sprintf("加载 bak 支付宝公钥证书失败: %v", err))
}
if err = bakClient.LoadAliPayRootCertFromFile(c.Alipay.AlipayRootCertPathBak); err != nil {
panic(fmt.Sprintf("加载 bak 根证书失败: %v", err))
}
svc.AlipayClientBak = bakClient
}
return svc
}
func (a *AliPayService) CreateAlipayAppOrder(amount float64, subject string, outTradeNo string) (string, error) {
@@ -116,16 +140,20 @@ func (a *AliPayService) CreateAlipayOrder(ctx context.Context, amount float64, s
}
}
// AliRefund 发起支付宝退款
func (a *AliPayService) AliRefund(ctx context.Context, outTradeNo string, refundAmount float64) (*alipay.TradeRefundRsp, error) {
// AliRefund 发起支付宝退款。orderPayTime 为订单支付时间(或创建时间),若在 2026-02-02 18:26 之前则使用 bak 商户号退款;传 nil 则使用主商户号。
func (a *AliPayService) AliRefund(ctx context.Context, outTradeNo string, refundAmount float64, orderPayTime *time.Time) (*alipay.TradeRefundRsp, error) {
client := a.AlipayClient
if orderPayTime != nil && !orderPayTime.After(AlipayBakRefundCutoff) && a.AlipayClientBak != nil {
client = a.AlipayClientBak
}
refund := alipay.TradeRefund{
OutTradeNo: outTradeNo,
RefundAmount: lzUtils.ToAlipayAmount(refundAmount),
OutRequestNo: fmt.Sprintf("refund-%s", outTradeNo),
}
// 发起退款请求
refundResp, err := a.AlipayClient.TradeRefund(ctx, refund)
refundResp, err := client.TradeRefund(ctx, refund)
if err != nil {
return nil, fmt.Errorf("支付宝退款请求错误:%v", err)
}

View File

@@ -2,6 +2,7 @@ package service
import (
"context"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
@@ -13,6 +14,7 @@ import (
"tyc-server/app/main/api/internal/config"
tianyuanapi "tyc-server/app/main/api/internal/service/tianyuanapi_sdk"
"tyc-server/app/main/model"
"tyc-server/pkg/lzkit/crypto"
"github.com/Masterminds/squirrel"
"github.com/tidwall/gjson"
@@ -175,6 +177,9 @@ var requestProcessors = map[string]func(*ApiRequestService, []byte) ([]byte, err
"BehaviorRiskScan": (*ApiRequestService).ProcessBehaviorRiskScanRequest,
"YYSYBE08": (*ApiRequestService).ProcessYYSYBE08Request,
"YYSY09CD": (*ApiRequestService).ProcessYYSY09CDRequest,
"QCXGGB2Q": (*ApiRequestService).ProcessQCXGGB2QRequest,
"QCXGYTS2": (*ApiRequestService).ProcessQCXGYTS2Request,
"QCXG5F3A": (*ApiRequestService).ProcessQCXG5F3ARequest,
"FLXG0687": (*ApiRequestService).ProcessFLXG0687Request,
"FLXG3D56": (*ApiRequestService).ProcessFLXG3D56Request,
"FLXG0V4B": (*ApiRequestService).ProcesFLXG0V4BRequest,
@@ -197,6 +202,17 @@ var requestProcessors = map[string]func(*ApiRequestService, []byte) ([]byte, err
"IVYZ3P9M": (*ApiRequestService).ProcessIVYZ3P9MRequest,
"FLXG7E8F": (*ApiRequestService).ProcessFLXG7E8FRequest,
"QCXG9P1C": (*ApiRequestService).ProcessQCXG9P1CFRequest,
// 车辆类接口(传参后续配置,先透传缓存 params
"QCXG4D2E": (*ApiRequestService).ProcessQCXG4D2ERequest,
"QCXG5U0Z": (*ApiRequestService).ProcessQCXG5U0ZRequest,
"QCXG1U4U": (*ApiRequestService).ProcessQCXG1U4URequest,
"QCXGY7F2": (*ApiRequestService).ProcessQCXGY7F2Request,
"QCXG1H7Y": (*ApiRequestService).ProcessQCXG1H7YRequest,
"QCXG4I1Z": (*ApiRequestService).ProcessQCXG4I1ZRequest,
"QCXG3Y6B": (*ApiRequestService).ProcessQCXG3Y6BRequest,
"QCXG3Z3L": (*ApiRequestService).ProcessQCXG3Z3LRequest,
"QCXGP00W": (*ApiRequestService).ProcessQCXGP00WRequest,
"QCXG6B4E": (*ApiRequestService).ProcessQCXG6B4ERequest,
}
// PreprocessRequestApi 调用指定的请求处理函数
@@ -1104,6 +1120,253 @@ func (a *ApiRequestService) ProcessQYGL6F2DRequest(params []byte) ([]byte, error
return nil, fmt.Errorf("响应code错误%s", code.String())
}
// ProcessQCXGGB2QRequest 人车核验简版
func (a *ApiRequestService) ProcessQCXGGB2QRequest(params []byte) ([]byte, error) {
plateNo := gjson.GetBytes(params, "plate_no")
carplateType := gjson.GetBytes(params, "carplate_type")
name := gjson.GetBytes(params, "name")
if !plateNo.Exists() || !carplateType.Exists() || !name.Exists() {
return nil, errors.New("api请求, QCXGGB2Q, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("QCXGGB2Q", map[string]interface{}{
"plate_no": plateNo.String(),
"carplate_type": carplateType.String(),
"name": name.String(),
})
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
// ProcessQCXGYTS2Request 人车核验详版
func (a *ApiRequestService) ProcessQCXGYTS2Request(params []byte) ([]byte, error) {
plateNo := gjson.GetBytes(params, "plate_no")
carplateType := gjson.GetBytes(params, "carplate_type")
name := gjson.GetBytes(params, "name")
if !plateNo.Exists() || !carplateType.Exists() || !name.Exists() {
return nil, errors.New("api请求, QCXGYTS2, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("QCXGYTS2", map[string]interface{}{
"plate_no": plateNo.String(),
"carplate_type": carplateType.String(),
"name": name.String(),
})
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
// ProcessQCXG5F3ARequest 名下车辆(车牌)
func (a *ApiRequestService) ProcessQCXG5F3ARequest(params []byte) ([]byte, error) {
idCard := gjson.GetBytes(params, "id_card")
name := gjson.GetBytes(params, "name")
if !idCard.Exists() || !name.Exists() {
return nil, errors.New("api请求, QCXG5F3A, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("QCXG5F3A", map[string]interface{}{
"id_card": idCard.String(),
"name": name.String(),
})
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
// processVehicleApiPassThrough 车辆类接口通用透传:将缓存 params 原样传给天远(传参后续按接口文档再细化)
func (a *ApiRequestService) processVehicleApiPassThrough(params []byte, apiID string) ([]byte, error) {
var m map[string]interface{}
if err := json.Unmarshal(params, &m); err != nil {
return nil, fmt.Errorf("api请求, %s, 解析参数失败: %w", apiID, err)
}
resp, err := a.tianyuanapi.CallInterface(apiID, m)
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
// 车辆 API 按 md 从缓存 params 提取字段并调用天远
func (a *ApiRequestService) ProcessQCXG4D2ERequest(params []byte) ([]byte, error) {
m := map[string]interface{}{}
if err := json.Unmarshal(params, &m); err != nil {
return nil, fmt.Errorf("api请求, QCXG4D2E, 解析参数失败: %w", err)
}
body := map[string]interface{}{}
if v, ok := m["user_type"].(string); ok && v != "" {
body["user_type"] = v
} else {
body["user_type"] = "1"
}
if v, ok := m["id_card"].(string); ok {
body["id_card"] = v
} else {
return nil, errors.New("api请求, QCXG4D2E, 缺少 id_card")
}
resp, err := a.tianyuanapi.CallInterface("QCXG4D2E", body)
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
func (a *ApiRequestService) ProcessQCXG5U0ZRequest(params []byte) ([]byte, error) {
vin := gjson.GetBytes(params, "vin_code")
if !vin.Exists() || vin.String() == "" {
return nil, errors.New("api请求, QCXG5U0Z, 缺少 vin_code")
}
resp, err := a.tianyuanapi.CallInterface("QCXG5U0Z", map[string]interface{}{"vin_code": vin.String()})
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
func (a *ApiRequestService) ProcessQCXG1U4URequest(params []byte) ([]byte, error) {
body := buildVehicleBody(params, []string{"vin_code", "return_url", "image_url"}, nil)
if body["vin_code"] == nil || body["return_url"] == nil || body["image_url"] == nil {
return nil, errors.New("api请求, QCXG1U4U, 缺少必填参数 vin_code/return_url/image_url")
}
resp, err := a.tianyuanapi.CallInterface("QCXG1U4U", body)
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
func (a *ApiRequestService) ProcessQCXGY7F2Request(params []byte) ([]byte, error) {
body := buildVehicleBody(params, []string{"vin_code", "vehicle_location", "first_registrationdate"}, nil)
if body["vin_code"] == nil || body["vehicle_location"] == nil || body["first_registrationdate"] == nil {
return nil, errors.New("api请求, QCXGY7F2, 缺少必填参数 vin_code/vehicle_location/first_registrationdate")
}
resp, err := a.tianyuanapi.CallInterface("QCXGY7F2", body)
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
func (a *ApiRequestService) ProcessQCXG1H7YRequest(params []byte) ([]byte, error) {
vin := gjson.GetBytes(params, "vin_code")
if !vin.Exists() || vin.String() == "" {
return nil, errors.New("api请求, QCXG1H7Y, 缺少 vin_code")
}
resp, err := a.tianyuanapi.CallInterface("QCXG1H7Y", map[string]interface{}{"vin_code": vin.String()})
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
func (a *ApiRequestService) ProcessQCXG4I1ZRequest(params []byte) ([]byte, error) {
vin := gjson.GetBytes(params, "vin_code")
if !vin.Exists() || vin.String() == "" {
return nil, errors.New("api请求, QCXG4I1Z, 缺少 vin_code")
}
resp, err := a.tianyuanapi.CallInterface("QCXG4I1Z", map[string]interface{}{"vin_code": vin.String()})
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
func (a *ApiRequestService) ProcessQCXG3Y6BRequest(params []byte) ([]byte, error) {
body := buildVehicleBody(params, []string{"vin_code", "return_url"}, nil)
if body["vin_code"] == nil || body["return_url"] == nil {
return nil, errors.New("api请求, QCXG3Y6B, 缺少必填参数 vin_code/return_url")
}
resp, err := a.tianyuanapi.CallInterface("QCXG3Y6B", body)
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
func (a *ApiRequestService) ProcessQCXG3Z3LRequest(params []byte) ([]byte, error) {
body := buildVehicleBody(params, []string{"vin_code", "return_url"}, nil)
if body["vin_code"] == nil || body["return_url"] == nil {
return nil, errors.New("api请求, QCXG3Z3L, 缺少必填参数 vin_code/return_url")
}
resp, err := a.tianyuanapi.CallInterface("QCXG3Z3L", body)
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
func (a *ApiRequestService) ProcessQCXGP00WRequest(params []byte) ([]byte, error) {
vin := gjson.GetBytes(params, "vin_code")
returnURL := gjson.GetBytes(params, "return_url")
vlphoto := gjson.GetBytes(params, "vlphoto_data")
if !vin.Exists() || vin.String() == "" || !returnURL.Exists() || returnURL.String() == "" || !vlphoto.Exists() || vlphoto.String() == "" {
return nil, errors.New("api请求, QCXGP00W, 缺少必填参数 vin_code/return_url/vlphoto_data")
}
key, err := hex.DecodeString(a.config.Encrypt.SecretKey)
if err != nil {
return nil, fmt.Errorf("api请求, QCXGP00W, 密钥解析失败: %w", err)
}
encData, err := crypto.AesEncrypt([]byte(vlphoto.String()), key)
if err != nil {
return nil, fmt.Errorf("api请求, QCXGP00W, 加密行驶证数据失败: %w", err)
}
resp, err := a.tianyuanapi.CallInterface("QCXGP00W", map[string]interface{}{
"vin_code": vin.String(),
"return_url": returnURL.String(),
"data": encData,
})
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
func (a *ApiRequestService) ProcessQCXG6B4ERequest(params []byte) ([]byte, error) {
vin := gjson.GetBytes(params, "vin_code")
auth := gjson.GetBytes(params, "authorized")
if !vin.Exists() || vin.String() == "" || !auth.Exists() {
return nil, errors.New("api请求, QCXG6B4E, 缺少 vin_code 或 authorized")
}
// 天远文档字段名为 VINCode、Authorized
resp, err := a.tianyuanapi.CallInterface("QCXG6B4E", map[string]interface{}{
"VINCode": vin.String(),
"Authorized": auth.String(),
})
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
// buildVehicleBody 从 params 中取 required 与 optional 键,仅非空才写入 body
func buildVehicleBody(params []byte, required, optional []string) map[string]interface{} {
body := make(map[string]interface{})
for _, k := range required {
v := gjson.GetBytes(params, k)
if v.Exists() {
body[k] = v.String()
}
}
for _, k := range optional {
v := gjson.GetBytes(params, k)
if v.Exists() && v.String() != "" {
body[k] = v.String()
}
}
return body
}
// ProcessQCXG7A2BRequest 名下车辆
func (a *ApiRequestService) ProcessQCXG7A2BRequest(params []byte) ([]byte, error) {
idCard := gjson.GetBytes(params, "id_card")

View File

@@ -122,6 +122,91 @@ type AgentQueryData struct {
Mobile string `json:"mobile"`
Code string `json:"code"`
}
// 名下车辆(数量) QCXG4D2E仅 user_type(1-ETC开户人 2-车辆所有人 3-ETC经办人)+id_card无验证码
type TocVehiclesUnderNameCountReq struct {
UserType string `json:"user_type"` // 必填,默认 1
IDCard string `json:"id_card" validate:"required,idCard"`
}
// 车辆静态信息/过户详版等 仅 vin_code
type TocVehicleVinCodeReq struct {
VinCode string `json:"vin_code" validate:"required"`
Mobile string `json:"mobile" validate:"required,mobile"`
Code string `json:"code" validate:"required"`
}
// 车辆里程记录(混合) QCXG1U4U
type TocVehicleMileageMixedReq struct {
VinCode string `json:"vin_code" validate:"required"`
PlateNo string `json:"plate_no"`
ReturnURL string `json:"return_url" validate:"required"`
ImageURL string `json:"image_url" validate:"required"`
RegURL string `json:"reg_url"`
EngineNumber string `json:"engine_number"`
Mobile string `json:"mobile" validate:"required,mobile"`
Code string `json:"code" validate:"required"`
}
// 二手车VIN估值 QCXGY7F2
type TocVehicleVinValuationReq struct {
VinCode string `json:"vin_code" validate:"required"`
VehicleName string `json:"vehicle_name"`
VehicleLocation string `json:"vehicle_location" validate:"required"`
FirstRegistrationDate string `json:"first_registrationdate" validate:"required"` // yyyy-MM
Color string `json:"color"`
Mobile string `json:"mobile" validate:"required,mobile"`
Code string `json:"code" validate:"required"`
}
// 车辆过户简版 QCXG1H7Y
type TocVehicleTransferSimpleReq struct {
VinCode string `json:"vin_code" validate:"required"`
PlateNo string `json:"plate_no"`
Mobile string `json:"mobile" validate:"required,mobile"`
Code string `json:"code" validate:"required"`
}
// 车辆维保简版 QCXG3Y6B
type TocVehicleMaintenanceSimpleReq struct {
VinCode string `json:"vin_code" validate:"required"`
PlateNo string `json:"plate_no"`
ReturnURL string `json:"return_url" validate:"required"`
ImageURL string `json:"image_url"`
RegURL string `json:"reg_url"`
EngineNumber string `json:"engine_number"`
Mobile string `json:"mobile" validate:"required,mobile"`
Code string `json:"code" validate:"required"`
}
// 车辆维保详细版 QCXG3Z3L
type TocVehicleMaintenanceDetailReq struct {
VinCode string `json:"vin_code" validate:"required"`
PlateNo string `json:"plate_no"`
ReturnURL string `json:"return_url" validate:"required"`
ImageURL string `json:"image_url"`
EngineNumber string `json:"engine_number"`
Mobile string `json:"mobile" validate:"required,mobile"`
Code string `json:"code" validate:"required"`
}
// 车辆出险详版 QCXGP00Wvlphoto_data 加密后以 data 字段提交
type TocVehicleClaimDetailReq struct {
VinCode string `json:"vin_code" validate:"required"`
PlateNo string `json:"plate_no"`
ReturnURL string `json:"return_url" validate:"required"`
VlphotoData string `json:"vlphoto_data" validate:"required"` // 行驶证图片 base64加密后传 API 的 data
Mobile string `json:"mobile" validate:"required,mobile"`
Code string `json:"code" validate:"required"`
}
// 车辆出险记录核验 QCXG6B4EVINCode + Authorized(0/1)
type TocVehicleClaimVerifyReq struct {
VINCode string `json:"vin_code" validate:"required"`
Authorized string `json:"authorized" validate:"required"` // 0:否 1:是
Mobile string `json:"mobile" validate:"required,mobile"`
Code string `json:"code" validate:"required"`
}
type AgentIdentifier struct {
Product string `json:"product"`
AgentID int64 `json:"agent_id"`

File diff suppressed because it is too large Load Diff