f
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user