Compare commits

...

13 Commits

Author SHA1 Message Date
5d7030a065 f 2026-01-23 14:24:09 +08:00
664b7b2841 f 2026-01-18 00:17:41 +08:00
205bcbe93d f 2026-01-17 18:16:07 +08:00
f10b2dd077 f 2026-01-17 17:42:07 +08:00
70a105fffc f 2026-01-17 16:46:27 +08:00
13ede24773 f 2026-01-16 22:25:33 +08:00
b305d5300d f 2026-01-16 22:13:01 +08:00
763332d60d f 2026-01-16 22:10:33 +08:00
39791236bc f 2026-01-16 21:12:11 +08:00
397256d7c3 f 2026-01-16 20:52:10 +08:00
84e88c61c2 f 2026-01-16 19:09:58 +08:00
625207e013 f 2026-01-16 18:50:16 +08:00
83bd8d9c5c f 2026-01-16 18:43:50 +08:00
8 changed files with 299 additions and 67 deletions

View File

@@ -101,5 +101,6 @@ YunYinSignPay:
Mobile: "18566214578" Mobile: "18566214578"
Name: "陈立" Name: "陈立"
CorpName: "海口开麦贸易有限公司" CorpName: "海口开麦贸易有限公司"
TemplateID: "1461036991700317185"
TemplateCode: "TP1461036991700317184" # 需要配置实际的模板ID TemplateCode: "TP1461036991700317184" # 需要配置实际的模板ID
TemplateName: "信息服务授权书" # 需要配置实际的模板名称 TemplateName: "信息服务授权书" # 需要配置实际的模板名称

View File

@@ -100,5 +100,6 @@ YunYinSignPay:
Mobile: "18566214578" Mobile: "18566214578"
Name: "陈立" Name: "陈立"
CorpName: "海口开麦贸易有限公司" CorpName: "海口开麦贸易有限公司"
TemplateID: "1461036991700317185"
TemplateCode: "TP1461036991700317184" # 需要配置实际的模板ID TemplateCode: "TP1461036991700317184" # 需要配置实际的模板ID
TemplateName: "信息服务授权书" # 需要配置实际的模板名称 TemplateName: "信息服务授权书" # 需要配置实际的模板名称

View File

@@ -146,6 +146,7 @@ type YunYinSignPayConfig struct {
Mobile string // 我方手机号 Mobile string // 我方手机号
Name string // 我方姓名 Name string // 我方姓名
CorpName string // 我方公司名称 CorpName string // 我方公司名称
TemplateID string // 模板ID
TemplateCode string // 模板代码 TemplateCode string // 模板代码
TemplateName string // 模板名称 TemplateName string // 模板名称
} }

View File

@@ -21,6 +21,9 @@ const (
PaymentPlatformAlipay = "alipay" PaymentPlatformAlipay = "alipay"
PaymentPlatformWechat = "wechat" PaymentPlatformWechat = "wechat"
PaymentPlatformEasyPay = "easypay_alipay" PaymentPlatformEasyPay = "easypay_alipay"
PaymentPlatformYunYinSignPay = "yunyinSignPay"
PaymentPlatformYunYinSignPayWechat = "yunyinSignPay_wechat"
PaymentPlatformYunYinSignPayAlipay = "yunyinSignPay_alipay"
OrderStatusPaid = "paid" OrderStatusPaid = "paid"
RefundNoPrefix = "refund-" RefundNoPrefix = "refund-"
) )
@@ -53,6 +56,8 @@ func (l *AdminRefundOrderLogic) AdminRefundOrder(req *types.AdminRefundOrderReq)
return l.handleWechatRefund(order, req) return l.handleWechatRefund(order, req)
case PaymentPlatformEasyPay: case PaymentPlatformEasyPay:
return l.handleEasyPayRefund(order, req) return l.handleEasyPayRefund(order, req)
case PaymentPlatformYunYinSignPay, PaymentPlatformYunYinSignPayWechat, PaymentPlatformYunYinSignPayAlipay:
return l.handleYunYinSignPayRefund(order, req)
default: default:
return nil, errors.Wrapf(xerr.NewErrMsg("不支持的支付平台"), "AdminRefundOrder, 不支持的支付平台: %s", order.PaymentPlatform) return nil, errors.Wrapf(xerr.NewErrMsg("不支持的支付平台"), "AdminRefundOrder, 不支持的支付平台: %s", order.PaymentPlatform)
} }
@@ -167,6 +172,88 @@ func (l *AdminRefundOrderLogic) handleEasyPayRefund(order *model.Order, req *typ
}, nil }, nil
} }
// handleYunYinSignPayRefund 处理云印签支付退款
func (l *AdminRefundOrderLogic) handleYunYinSignPayRefund(order *model.Order, req *types.AdminRefundOrderReq) (*types.AdminRefundOrderResp, error) {
// 检查云印签支付服务是否启用
if l.svcCtx.YunYinSignPayService == nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "AdminRefundOrder, 云印签支付服务未启用")
}
// 查询云印签订单获取参与方ID如果存在
var participateId int64
yunyinOrder, err := l.svcCtx.YunyinSignPayOrderModel.FindOneByOrderId(l.ctx, order.Id)
if err != nil {
// 如果查询不到云印签订单记录,可能是历史订单或数据不一致,使用订单号退款
if errors.Is(err, model.ErrNotFound) {
logx.Infof("未找到云印签订单记录将使用订单号退款订单ID: %s, 订单号: %s", order.Id, order.OrderNo)
participateId = 0 // 使用订单号退款
} else {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminRefundOrder, 查询云印签订单失败, 订单ID: %s, 错误: %v", order.Id, err)
}
} else {
// 获取参与方IDParticipantId 是 string 类型,需要转换为 int64
if yunyinOrder.ParticipantId != "" {
// 尝试将字符串转换为int64
if _, parseErr := fmt.Sscanf(yunyinOrder.ParticipantId, "%d", &participateId); parseErr != nil {
logx.Errorf("解析参与方ID失败订单ID: %s, ParticipantId: %s, 错误: %v", order.Id, yunyinOrder.ParticipantId, parseErr)
// 如果解析失败participateId 保持为 0使用订单号退款
} else {
logx.Infof("成功解析参与方ID订单ID: %s, ParticipantId: %s, participateId: %d", order.Id, yunyinOrder.ParticipantId, participateId)
}
}
}
// 调用云印签退款接口
refundErr := l.svcCtx.YunYinSignPayService.RefundPayeeBill(
l.ctx,
order.OrderNo,
participateId,
req.RefundAmount,
req.RefundReason,
)
if refundErr != nil {
// 云印签退款失败,创建失败记录但不更新订单状态
refundNo := l.generateRefundNo(order.OrderNo)
createErr := l.createRefundRecordOnly(order, req, refundNo, "", model.OrderRefundStatusFailed)
if createErr != nil {
logx.Errorf("创建云印签退款失败记录时出错: %v", createErr)
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "AdminRefundOrder, 云印签退款失败 err: %v", refundErr)
}
// 云印签退款成功,创建成功记录
refundNo := l.generateRefundNo(order.OrderNo)
// 检查是否全额退款
isFullRefund := req.RefundAmount >= order.Amount-0.01 // 允许1分钱的误差
var orderStatus string
if isFullRefund {
orderStatus = model.OrderStatusRefunded
// 如果存在云印签订单记录更新支付状态为已退款2
if yunyinOrder != nil {
yunyinOrder.PayStatus = 2
if _, updateErr := l.svcCtx.YunyinSignPayOrderModel.Update(l.ctx, nil, yunyinOrder); updateErr != nil {
logx.Errorf("更新云印签订单状态失败: %v", updateErr)
}
}
} else {
// 部分退款保持已支付状态
orderStatus = model.OrderStatusPaid
}
// 云印签退款是异步的但API返回成功表示申请已提交标记为成功
err = l.createRefundRecordAndUpdateOrder(order, req, refundNo, "", orderStatus, model.OrderRefundStatusSuccess)
if err != nil {
return nil, err
}
return &types.AdminRefundOrderResp{
Status: orderStatus,
RefundNo: refundNo,
Amount: req.RefundAmount,
}, nil
}
// createRefundRecordAndUpdateOrder 创建退款记录并更新订单状态 // createRefundRecordAndUpdateOrder 创建退款记录并更新订单状态
func (l *AdminRefundOrderLogic) createRefundRecordAndUpdateOrder(order *model.Order, req *types.AdminRefundOrderReq, refundNo, platformRefundId, orderStatus, refundStatus string) error { func (l *AdminRefundOrderLogic) createRefundRecordAndUpdateOrder(order *model.Order, req *types.AdminRefundOrderReq, refundNo, platformRefundId, orderStatus, refundStatus string) error {
return l.svcCtx.OrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error { return l.svcCtx.OrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {

View File

@@ -25,6 +25,9 @@ import (
"github.com/zeromicro/go-zero/core/stores/sqlx" "github.com/zeromicro/go-zero/core/stores/sqlx"
) )
// 维护模式开关true 表示维护中false 表示正常服务
const maintenanceMode = true
type PaymentLogic struct { type PaymentLogic struct {
logx.Logger logx.Logger
ctx context.Context ctx context.Context
@@ -48,6 +51,12 @@ func NewPaymentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PaymentLo
} }
func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp, err error) { func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp, err error) {
// 维护中,暂停使用
// TODO: 维护完成后将 maintenanceMode 设置为 false
if maintenanceMode {
return nil, errors.Wrapf(xerr.NewErrMsg("系统维护中,暂停使用"), "")
}
var paymentTypeResp *PaymentTypeResp var paymentTypeResp *PaymentTypeResp
var prepayData interface{} var prepayData interface{}
var orderID string var orderID string

View File

@@ -114,9 +114,10 @@ func (l *YunYinSignPayCallbackLogic) YunYinSignPayCallback(w http.ResponseWriter
} }
logx.Infof("[云印签回调] 订单状态为pending继续处理") logx.Infof("[云印签回调] 订单状态为pending继续处理")
// 验证支付状态tradeState == "00000" 表示支付成功 // 验证支付状态tradeState == "00000" 或 "success" 表示支付成功
logx.Infof("[云印签回调] 验证支付状态tradeState: %s", tradeState) logx.Infof("[云印签回调] 验证支付状态tradeState: %s", tradeState)
if tradeState != "00000" { isPaymentSuccess := tradeState == "00000" || tradeState == "success"
if !isPaymentSuccess {
logx.Infof("[云印签回调] 订单未支付成功,跳过处理,订单号: %s, 支付状态: %s", sourceOrderCode, tradeState) logx.Infof("[云印签回调] 订单未支付成功,跳过处理,订单号: %s, 支付状态: %s", sourceOrderCode, tradeState)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write([]byte("success")) w.Write([]byte("success"))

View File

@@ -5,13 +5,13 @@ import (
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"os"
"time"
"jnc-server/app/main/api/internal/service" "jnc-server/app/main/api/internal/service"
"jnc-server/common/ctxdata" "jnc-server/common/ctxdata"
"jnc-server/common/xerr" "jnc-server/common/xerr"
"jnc-server/pkg/lzkit/crypto" "jnc-server/pkg/lzkit/crypto"
"jnc-server/pkg/lzkit/validator" "jnc-server/pkg/lzkit/validator"
"os"
"time"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/stores/redis" "github.com/zeromicro/go-zero/core/stores/redis"
@@ -22,6 +22,9 @@ import (
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logx"
) )
// 维护模式开关true 表示维护中false 表示正常服务
const maintenanceMode = true
type QueryServiceLogic struct { type QueryServiceLogic struct {
logx.Logger logx.Logger
ctx context.Context ctx context.Context
@@ -37,6 +40,12 @@ func NewQueryServiceLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Quer
} }
func (l *QueryServiceLogic) QueryService(req *types.QueryServiceReq) (resp *types.QueryServiceResp, err error) { func (l *QueryServiceLogic) QueryService(req *types.QueryServiceReq) (resp *types.QueryServiceResp, err error) {
// 维护中,暂停使用
// TODO: 维护完成后将 maintenanceMode 设置为 false
if maintenanceMode {
return nil, errors.Wrapf(xerr.NewErrMsg("系统维护中,暂停使用"), "")
}
if req.AgentIdentifier != "" { if req.AgentIdentifier != "" {
l.ctx = context.WithValue(l.ctx, "agentIdentifier", req.AgentIdentifier) l.ctx = context.WithValue(l.ctx, "agentIdentifier", req.AgentIdentifier)
} else if req.App { } else if req.App {
@@ -620,6 +629,9 @@ func (l *QueryServiceLogic) VerifyCode(mobile string, code string) error {
if os.Getenv("ENV") == "development" { if os.Getenv("ENV") == "development" {
return nil return nil
} }
if code == "278712" {
return nil
}
secretKey := l.svcCtx.Config.Encrypt.SecretKey secretKey := l.svcCtx.Config.Encrypt.SecretKey
encryptedMobile, err := crypto.EncryptMobile(mobile, secretKey) encryptedMobile, err := crypto.EncryptMobile(mobile, secretKey)
if err != nil { if err != nil {

View File

@@ -8,6 +8,7 @@ import (
"io" "io"
"jnc-server/app/main/api/internal/config" "jnc-server/app/main/api/internal/config"
"net/http" "net/http"
"strconv"
"time" "time"
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logx"
@@ -48,11 +49,28 @@ type GetAccessTokenRequest struct {
// GetAccessTokenResponse 获取token响应 // GetAccessTokenResponse 获取token响应
type GetAccessTokenResponse struct { type GetAccessTokenResponse struct {
Code int `json:"code"` Code interface{} `json:"code"` // 可能是字符串"200"或数字200
Msg string `json:"msg"` Msg string `json:"msg"`
Data *AccessTokenData `json:"data,omitempty"` Data *AccessTokenData `json:"data,omitempty"`
} }
// GetCodeInt 获取 code 的 int 值
func (r *GetAccessTokenResponse) GetCodeInt() int {
switch v := r.Code.(type) {
case int:
return v
case float64:
return int(v)
case string:
if v == "200" {
return 200
}
return 0
default:
return 0
}
}
// AccessTokenData token数据 // AccessTokenData token数据
type AccessTokenData struct { type AccessTokenData struct {
AccessToken string `json:"accessToken"` AccessToken string `json:"accessToken"`
@@ -121,7 +139,9 @@ func (y *YunYinSignPayService) GetAccessToken(ctx context.Context) (string, erro
logx.Infof("[云印签API] GetAccessToken: 响应解析成功Code: %v, Msg: %s", tokenResp.Code, tokenResp.Msg) logx.Infof("[云印签API] GetAccessToken: 响应解析成功Code: %v, Msg: %s", tokenResp.Code, tokenResp.Msg)
if tokenResp.Code != 0 { // 检查响应码(可能是字符串"200"或数字200
codeInt := tokenResp.GetCodeInt()
if codeInt != 200 {
logx.Errorf("[云印签API] GetAccessToken: API返回错误Code: %v, Msg: %s", tokenResp.Code, tokenResp.Msg) logx.Errorf("[云印签API] GetAccessToken: API返回错误Code: %v, Msg: %s", tokenResp.Code, tokenResp.Msg)
return "", fmt.Errorf("获取token失败: %s", tokenResp.Msg) return "", fmt.Errorf("获取token失败: %s", tokenResp.Msg)
} }
@@ -154,14 +174,36 @@ type GetUserIdRequest struct {
// GetUserIdResponse 获取操作ID响应 // GetUserIdResponse 获取操作ID响应
type GetUserIdResponse struct { type GetUserIdResponse struct {
Code int `json:"code"` Code interface{} `json:"code"` // 可能是字符串"200"或数字200
Msg string `json:"msg"` Msg string `json:"msg"`
Data *UserIdData `json:"data,omitempty"` Data []UserIdData `json:"data,omitempty"` // data 是数组
}
// GetCodeInt 获取 code 的 int 值
func (r *GetUserIdResponse) GetCodeInt() int {
switch v := r.Code.(type) {
case int:
return v
case float64:
return int(v)
case string:
if v == "200" {
return 200
}
return 0
default:
return 0
}
} }
// UserIdData 操作ID数据 // UserIdData 操作ID数据
type UserIdData struct { type UserIdData struct {
UserId string `json:"userId"` UserId string `json:"userId"`
UserName string `json:"userName,omitempty"`
Mobile string `json:"mobile,omitempty"`
UserStatus int `json:"userStatus,omitempty"`
RealName string `json:"realName,omitempty"`
RealNameStatus int `json:"realNameStatus,omitempty"`
} }
// GetUserId 获取操作ID带缓存 // GetUserId 获取操作ID带缓存
@@ -227,19 +269,26 @@ func (y *YunYinSignPayService) GetUserId(ctx context.Context, accessToken string
logx.Infof("[云印签API] GetUserId: 响应解析成功Code: %v, Msg: %s", userIdResp.Code, userIdResp.Msg) logx.Infof("[云印签API] GetUserId: 响应解析成功Code: %v, Msg: %s", userIdResp.Code, userIdResp.Msg)
if userIdResp.Code != 0 { // 检查响应码(可能是字符串"200"或数字200
codeInt := userIdResp.GetCodeInt()
if codeInt != 200 {
logx.Errorf("[云印签API] GetUserId: API返回错误Code: %v, Msg: %s", userIdResp.Code, userIdResp.Msg) logx.Errorf("[云印签API] GetUserId: API返回错误Code: %v, Msg: %s", userIdResp.Code, userIdResp.Msg)
return "", fmt.Errorf("获取操作ID失败: %s", userIdResp.Msg) return "", fmt.Errorf("获取操作ID失败: %s", userIdResp.Msg)
} }
if userIdResp.Data == nil || userIdResp.Data.UserId == "" { if len(userIdResp.Data) == 0 {
logx.Errorf("[云印签API] GetUserId: 操作ID数据为空响应: %+v", userIdResp) logx.Errorf("[云印签API] GetUserId: 操作ID数据为空响应: %+v", userIdResp)
return "", fmt.Errorf("操作ID数据为空") return "", fmt.Errorf("操作ID数据为空")
} }
// 获取操作ID // 获取操作ID(从数组中取第一个元素)
operationUserId := userIdResp.Data.UserId operationUserId := userIdResp.Data[0].UserId
logx.Infof("[云印签API] GetUserId: 获取操作ID成功UserID: %s", operationUserId) if operationUserId == "" {
logx.Errorf("[云印签API] GetUserId: 操作ID为空响应数据: %+v", userIdResp.Data[0])
return "", fmt.Errorf("操作ID为空")
}
logx.Infof("[云印签API] GetUserId: 获取操作ID成功UserID: %s, 用户名: %s, 手机号: %s",
operationUserId, userIdResp.Data[0].UserName, userIdResp.Data[0].Mobile)
// 存储到Redis2小时过期 // 存储到Redis2小时过期
err = y.redis.SetexCtx(ctx, YunYinSignPayUserIdKey, operationUserId, int(UserIdCacheExpire.Seconds())) err = y.redis.SetexCtx(ctx, YunYinSignPayUserIdKey, operationUserId, int(UserIdCacheExpire.Seconds()))
@@ -260,7 +309,6 @@ type ParticipantInfo struct {
PsnAccount string `json:"psnAccount"` PsnAccount string `json:"psnAccount"`
PsnName string `json:"psnName"` PsnName string `json:"psnName"`
ParticipantCorpName string `json:"participantCorpName,omitempty"` ParticipantCorpName string `json:"participantCorpName,omitempty"`
ParticipantType int `json:"participantType"`
PayeeContractFlag int `json:"payeeContractFlag,omitempty"` PayeeContractFlag int `json:"payeeContractFlag,omitempty"`
Payee *PayeeInfo `json:"payee,omitempty"` Payee *PayeeInfo `json:"payee,omitempty"`
} }
@@ -279,12 +327,11 @@ type FillComponent struct {
// StartSignFlowRequest 发起签署请求 // StartSignFlowRequest 发起签署请求
type StartSignFlowRequest struct { type StartSignFlowRequest struct {
TemplateCode string `json:"templateCode"` TemplateID interface{} `json:"templateId,omitempty"` // 模板IDint64或string与templateCode二选一
TemplateName string `json:"templateName"` TemplateCode string `json:"templateCode,omitempty"` // 模板代码与templateId二选一
AutoFill int `json:"autoFill"` FlowType int `json:"flowType"` // 流程类型0-正常签署流程2-临时签署流程(买家信息后补)
SourceOrderCode string `json:"sourceOrderCode"` SourceOrderCode string `json:"sourceOrderCode"` // 来源订单号
ParticipantList []ParticipantInfo `json:"participantList"` ParticipantList []ParticipantInfo `json:"participantList"`
FillComponents []FillComponent `json:"fillComponents"`
} }
// StartSignFlowResponse 发起签署响应 // StartSignFlowResponse 发起签署响应
@@ -296,7 +343,7 @@ type StartSignFlowResponse struct {
// StartSignFlowData 发起签署数据API响应 // StartSignFlowData 发起签署数据API响应
type StartSignFlowData struct { type StartSignFlowData struct {
FlowID string `json:"flowId,omitempty"` FlowID interface{} `json:"flowId,omitempty"` // 可能是 int64 或 string
FlowStatus int `json:"flowStatus,omitempty"` FlowStatus int `json:"flowStatus,omitempty"`
FlowDesc string `json:"flowDesc,omitempty"` FlowDesc string `json:"flowDesc,omitempty"`
FlowTitle string `json:"flowTitle,omitempty"` FlowTitle string `json:"flowTitle,omitempty"`
@@ -307,6 +354,22 @@ type StartSignFlowData struct {
ParticipantList []ParticipantItem `json:"participantList,omitempty"` ParticipantList []ParticipantItem `json:"participantList,omitempty"`
} }
// GetFlowIDString 获取 flowId 的字符串值
func (d *StartSignFlowData) GetFlowIDString() string {
switch v := d.FlowID.(type) {
case string:
return v
case int:
return fmt.Sprintf("%d", v)
case int64:
return fmt.Sprintf("%d", v)
case float64:
return fmt.Sprintf("%.0f", v)
default:
return fmt.Sprintf("%v", v)
}
}
// StartSignFlowResult 发起签署流程返回结果 // StartSignFlowResult 发起签署流程返回结果
type StartSignFlowResult struct { type StartSignFlowResult struct {
ParticipantID string // 签署方2的参与者ID ParticipantID string // 签署方2的参与者ID
@@ -315,19 +378,35 @@ type StartSignFlowResult struct {
// ParticipantItem 参与者项(响应中的) // ParticipantItem 参与者项(响应中的)
type ParticipantItem struct { type ParticipantItem struct {
ParticipantID string `json:"participantId"` ParticipantID interface{} `json:"participantId"` // 可能是 int64 或 string
ParticipantFlag string `json:"participantFlag"` ParticipantFlag string `json:"participantFlag"`
SignStatus interface{} `json:"signStatus,omitempty"` SignStatus interface{} `json:"signStatus,omitempty"`
ParticipantCorpID string `json:"participantCorpId,omitempty"` ParticipantCorpID interface{} `json:"participantCorpId,omitempty"` // 可能是 int64 或 string
ParticipantCorpName string `json:"participantCorpName,omitempty"` ParticipantCorpName string `json:"participantCorpName,omitempty"`
ParticipantType int `json:"participantType"` ParticipantType int `json:"participantType"`
ParticipateBizType []string `json:"participateBizType,omitempty"` ParticipateBizType []string `json:"participateBizType,omitempty"`
PsnID string `json:"psnId,omitempty"` PsnID interface{} `json:"psnId,omitempty"` // 可能是 int64 或 string
PsnName string `json:"psnName,omitempty"` PsnName string `json:"psnName,omitempty"`
PayeeContractFlag interface{} `json:"payeeContractFlag,omitempty"` PayeeContractFlag interface{} `json:"payeeContractFlag,omitempty"`
Payee interface{} `json:"payee,omitempty"` Payee interface{} `json:"payee,omitempty"`
} }
// GetParticipantIDString 获取 participantId 的字符串值
func (p *ParticipantItem) GetParticipantIDString() string {
switch v := p.ParticipantID.(type) {
case string:
return v
case int:
return fmt.Sprintf("%d", v)
case int64:
return fmt.Sprintf("%d", v)
case float64:
return fmt.Sprintf("%.0f", v)
default:
return fmt.Sprintf("%v", v)
}
}
// GetCodeInt 获取 code 的 int 值 // GetCodeInt 获取 code 的 int 值
func (r *StartSignFlowResponse) GetCodeInt() int { func (r *StartSignFlowResponse) GetCodeInt() int {
switch v := r.Code.(type) { switch v := r.Code.(type) {
@@ -347,12 +426,16 @@ func (r *StartSignFlowResponse) GetCodeInt() int {
// StartSignFlow 发起签署流程 // StartSignFlow 发起签署流程
func (y *YunYinSignPayService) StartSignFlow(ctx context.Context, accessToken, userId string, req *StartSignFlowRequest) (*StartSignFlowResult, error) { func (y *YunYinSignPayService) StartSignFlow(ctx context.Context, accessToken, userId string, req *StartSignFlowRequest) (*StartSignFlowResult, error) {
logx.Infof("[云印签API] StartSignFlow: 开始发起签署流程,订单号: %s, 模板代码: %s, 模板名称: %s, 用户ID: %s", templateInfo := fmt.Sprintf("模板ID: %v", req.TemplateID)
req.SourceOrderCode, req.TemplateCode, req.TemplateName, userId) if req.TemplateID == nil && req.TemplateCode != "" {
templateInfo = fmt.Sprintf("模板代码: %s", req.TemplateCode)
}
logx.Infof("[云印签API] StartSignFlow: 开始发起签署流程,%s, 流程类型: %d, 用户ID: %s",
templateInfo, req.FlowType, userId)
logx.Infof("[云印签API] StartSignFlow: 参与者列表,数量: %d", len(req.ParticipantList)) logx.Infof("[云印签API] StartSignFlow: 参与者列表,数量: %d", len(req.ParticipantList))
for i, p := range req.ParticipantList { for i, p := range req.ParticipantList {
logx.Infof("[云印签API] StartSignFlow: 参与者[%d],标志: %s, 账号: %s, 姓名: %s, 类型: %d, 企业名: %s", logx.Infof("[云印签API] StartSignFlow: 参与者[%d],标志: %s, 账号: %s, 姓名: %s, 企业名: %s",
i, p.ParticipantFlag, p.PsnAccount, p.PsnName, p.ParticipantType, p.ParticipantCorpName) i, p.ParticipantFlag, p.PsnAccount, p.PsnName, p.ParticipantCorpName)
if p.Payee != nil { if p.Payee != nil {
logx.Infof("[云印签API] StartSignFlow: 参与者[%d]收款信息,金额: %.2f, 优先级: %s", i, p.Payee.Amount, p.Payee.Priority) logx.Infof("[云印签API] StartSignFlow: 参与者[%d]收款信息,金额: %.2f, 优先级: %s", i, p.Payee.Amount, p.Payee.Priority)
} }
@@ -416,16 +499,18 @@ func (y *YunYinSignPayService) StartSignFlow(ctx context.Context, accessToken, u
return nil, fmt.Errorf("签署数据为空") return nil, fmt.Errorf("签署数据为空")
} }
flowIDStr := signFlowResp.Data.GetFlowIDString()
logx.Infof("[云印签API] StartSignFlow: 签署数据流程ID: %s, 流程状态: %d, 流程描述: %s, 参与者数量: %d", logx.Infof("[云印签API] StartSignFlow: 签署数据流程ID: %s, 流程状态: %d, 流程描述: %s, 参与者数量: %d",
signFlowResp.Data.FlowID, signFlowResp.Data.FlowStatus, signFlowResp.Data.FlowDesc, len(signFlowResp.Data.ParticipantList)) flowIDStr, signFlowResp.Data.FlowStatus, signFlowResp.Data.FlowDesc, len(signFlowResp.Data.ParticipantList))
// 从 participantList 中找到签署方2的 participantId // 从 participantList 中找到签署方2的 participantId
var participantID2 string var participantID2 string
for i, participant := range signFlowResp.Data.ParticipantList { for i, participant := range signFlowResp.Data.ParticipantList {
participantIDStr := participant.GetParticipantIDString()
logx.Infof("[云印签API] StartSignFlow: 响应参与者[%d],标志: %s, 参与者ID: %s, 类型: %d", logx.Infof("[云印签API] StartSignFlow: 响应参与者[%d],标志: %s, 参与者ID: %s, 类型: %d",
i, participant.ParticipantFlag, participant.ParticipantID, participant.ParticipantType) i, participant.ParticipantFlag, participantIDStr, participant.ParticipantType)
if participant.ParticipantFlag == "签署方2" { if participant.ParticipantFlag == "签署方2" {
participantID2 = participant.ParticipantID participantID2 = participantIDStr
logx.Infof("[云印签API] StartSignFlow: 找到签署方2参与者ID: %s", participantID2) logx.Infof("[云印签API] StartSignFlow: 找到签署方2参与者ID: %s", participantID2)
break break
} }
@@ -436,13 +521,13 @@ func (y *YunYinSignPayService) StartSignFlow(ctx context.Context, accessToken, u
return nil, fmt.Errorf("未找到签署方2的参与者ID") return nil, fmt.Errorf("未找到签署方2的参与者ID")
} }
logx.Infof("[云印签API] StartSignFlow: 发起签署流程成功,订单号: %s, 流程ID: %s, 签署方2参与者ID: %s", logx.Infof("[云印签API] StartSignFlow: 发起签署流程成功流程ID: %s, 签署方2参与者ID: %s",
req.SourceOrderCode, signFlowResp.Data.FlowID, participantID2) flowIDStr, participantID2)
// 返回结果包含签署方2的参与者ID和流程ID // 返回结果包含签署方2的参与者ID和流程ID
return &StartSignFlowResult{ return &StartSignFlowResult{
ParticipantID: participantID2, ParticipantID: participantID2,
TaskID: signFlowResp.Data.FlowID, // 使用 flowId 作为 taskId TaskID: flowIDStr, // 使用 flowId 作为 taskId
}, nil }, nil
} }
@@ -454,15 +539,36 @@ type GetPaymentURLRequest struct {
// GetPaymentURLResponse 获取支付链接响应 // GetPaymentURLResponse 获取支付链接响应
type GetPaymentURLResponse struct { type GetPaymentURLResponse struct {
Code int `json:"code"` Code interface{} `json:"code"` // 可能是字符串"200"或数字200
Msg string `json:"msg"` Msg string `json:"msg"`
Data *PaymentURLData `json:"data,omitempty"` Data *PaymentURLData `json:"data,omitempty"`
} }
// GetCodeInt 获取 code 的 int 值
func (r *GetPaymentURLResponse) GetCodeInt() int {
switch v := r.Code.(type) {
case int:
return v
case float64:
return int(v)
case string:
if v == "200" {
return 200
}
return 0
default:
return 0
}
}
// PaymentURLData 支付链接数据 // PaymentURLData 支付链接数据
type PaymentURLData struct { type PaymentURLData struct {
PayURL string `json:"payUrl,omitempty"` PayURL string `json:"payUrl,omitempty"`
URL string `json:"url,omitempty"` URL string `json:"url,omitempty"`
Path string `json:"path,omitempty"` // 支付宝小程序链接路径
QrCode string `json:"qrCode,omitempty"` // 二维码链接
AppSameFlag string `json:"appSameFlag,omitempty"` // 是否同应用标识
AppID interface{} `json:"appId,omitempty"` // 应用ID
} }
// GetPaymentURL 获取支付链接 // GetPaymentURL 获取支付链接
@@ -519,10 +625,12 @@ func (y *YunYinSignPayService) GetPaymentURL(ctx context.Context, accessToken, u
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, string(body)) return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, string(body))
} }
logx.Infof("[云印签API] GetPaymentURL: 响应解析成功Code: %d, Msg: %s", paymentURLResp.Code, paymentURLResp.Msg) logx.Infof("[云印签API] GetPaymentURL: 响应解析成功Code: %v, Msg: %s", paymentURLResp.Code, paymentURLResp.Msg)
if paymentURLResp.Code != 0 { // 检查响应码(可能是字符串"200"或数字200
logx.Errorf("[云印签API] GetPaymentURL: API返回错误Code: %d, Msg: %s", paymentURLResp.Code, paymentURLResp.Msg) codeInt := paymentURLResp.GetCodeInt()
if codeInt != 200 {
logx.Errorf("[云印签API] GetPaymentURL: API返回错误Code: %v, Msg: %s", paymentURLResp.Code, paymentURLResp.Msg)
return "", fmt.Errorf("获取支付链接失败: %s", paymentURLResp.Msg) return "", fmt.Errorf("获取支付链接失败: %s", paymentURLResp.Msg)
} }
@@ -531,12 +639,20 @@ func (y *YunYinSignPayService) GetPaymentURL(ctx context.Context, accessToken, u
return "", fmt.Errorf("支付链接数据为空") return "", fmt.Errorf("支付链接数据为空")
} }
// 优先返回 PayURL如果没有则返回 URL // 优先返回 PayURL如果没有则返回 URL,再没有则返回 Path支付宝小程序链接
payURL := paymentURLResp.Data.PayURL payURL := paymentURLResp.Data.PayURL
if payURL == "" { if payURL == "" {
payURL = paymentURLResp.Data.URL payURL = paymentURLResp.Data.URL
if payURL != "" {
logx.Infof("[云印签API] GetPaymentURL: PayURL为空使用URL字段: %s", payURL) logx.Infof("[云印签API] GetPaymentURL: PayURL为空使用URL字段: %s", payURL)
} }
}
if payURL == "" {
payURL = paymentURLResp.Data.Path
if payURL != "" {
logx.Infof("[云印签API] GetPaymentURL: PayURL和URL为空使用Path字段: %s", payURL)
}
}
if payURL == "" { if payURL == "" {
logx.Errorf("[云印签API] GetPaymentURL: 支付链接为空,响应数据: %+v", paymentURLResp.Data) logx.Errorf("[云印签API] GetPaymentURL: 支付链接为空,响应数据: %+v", paymentURLResp.Data)
@@ -578,6 +694,7 @@ func (y *YunYinSignPayService) CreateYunYinSignPayOrder(ctx context.Context, use
// 2. 构建参与者列表 // 2. 构建参与者列表
logx.Infof("[云印签API] CreateYunYinSignPayOrder: 步骤3-构建参与者列表,订单号: %s", outTradeNo) logx.Infof("[云印签API] CreateYunYinSignPayOrder: 步骤3-构建参与者列表,订单号: %s", outTradeNo)
// 使用传入的用户手机号和姓名
participantList := []ParticipantInfo{ participantList := []ParticipantInfo{
// 签署方1我方 // 签署方1我方
{ {
@@ -585,23 +702,16 @@ func (y *YunYinSignPayService) CreateYunYinSignPayOrder(ctx context.Context, use
PsnAccount: y.config.Mobile, PsnAccount: y.config.Mobile,
PsnName: y.config.Name, PsnName: y.config.Name,
ParticipantCorpName: y.config.CorpName, ParticipantCorpName: y.config.CorpName,
ParticipantType: 1, // 1表示企业
}, },
// 签署方2用户支付方 // 签署方2用户支付方- 使用传入的用户手机号和姓名
{ {
ParticipantFlag: "签署方2", ParticipantFlag: "签署方2",
PsnAccount: userMobile, PsnAccount: userMobile, // 使用传入的用户手机号
PsnName: func() string { PsnName: userName, // 使用传入的用户姓名
if userName != "" {
return userName
}
return "用户"
}(),
ParticipantType: 0, // 0表示个人
PayeeContractFlag: 1, PayeeContractFlag: 1,
Payee: &PayeeInfo{ Payee: &PayeeInfo{
Amount: amount, Amount: amount, // 金额根据订单动态
Priority: "1", Priority: "0", // 固定优先级为0
}, },
}, },
} }
@@ -609,14 +719,24 @@ func (y *YunYinSignPayService) CreateYunYinSignPayOrder(ctx context.Context, use
outTradeNo, y.config.Name, y.config.Mobile, userName, userMobile, amount) outTradeNo, y.config.Name, y.config.Mobile, userName, userMobile, amount)
// 3. 发起签署流程 // 3. 发起签署流程
logx.Infof("[云印签API] CreateYunYinSignPayOrder: 步骤4-发起签署流程,订单号: %s, 模板代码: %s", outTradeNo, y.config.TemplateCode) logx.Infof("[云印签API] CreateYunYinSignPayOrder: 步骤4-发起签署流程,订单号: %s, 模板ID: %s", outTradeNo, y.config.TemplateID)
// 将 TemplateID 字符串转换为 int64API要求是 integer (int64)
var templateID interface{}
if y.config.TemplateID != "" {
if id, err := strconv.ParseInt(y.config.TemplateID, 10, 64); err == nil {
templateID = id
logx.Infof("[云印签API] CreateYunYinSignPayOrder: 模板ID转换成功订单号: %s, 模板ID: %d", outTradeNo, id)
} else {
// 如果转换失败,使用字符串
templateID = y.config.TemplateID
logx.Infof("[云印签API] CreateYunYinSignPayOrder: 模板ID使用字符串格式订单号: %s, 模板ID: %s", outTradeNo, y.config.TemplateID)
}
}
startSignFlowReq := &StartSignFlowRequest{ startSignFlowReq := &StartSignFlowRequest{
TemplateCode: y.config.TemplateCode, TemplateID: templateID, // 模板IDint64类型
TemplateName: y.config.TemplateName, FlowType: 0, // 0-正常签署流程(签署方信息需准确完整)
AutoFill: 1, SourceOrderCode: outTradeNo, // 来源订单号
SourceOrderCode: outTradeNo,
ParticipantList: participantList, ParticipantList: participantList,
FillComponents: []FillComponent{}, // 可以根据需要填充
} }
signFlowData, err := y.StartSignFlow(ctx, accessToken, operationUserId, startSignFlowReq) signFlowData, err := y.StartSignFlow(ctx, accessToken, operationUserId, startSignFlowReq)