package certification import ( "context" "crypto/hmac" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "sort" "strings" "go.uber.org/zap" "tyapi-server/internal/domains/certification/entities" "tyapi-server/internal/domains/certification/enums" "tyapi-server/internal/domains/certification/services" user_services "tyapi-server/internal/domains/user/services" "tyapi-server/internal/shared/database" esign_service "tyapi-server/internal/shared/esign" ) // EsignCallbackData e签宝回调数据结构 type EsignCallbackData struct { Action string `json:"action"` Timestamp int64 `json:"timestamp"` AuthFlowId string `json:"authFlowId,omitempty"` SignFlowId string `json:"signFlowId,omitempty"` CustomBizNum string `json:"customBizNum,omitempty"` SignOrder int `json:"signOrder,omitempty"` OperateTime int64 `json:"operateTime,omitempty"` SignResult int `json:"signResult,omitempty"` ResultDescription string `json:"resultDescription,omitempty"` AuthType string `json:"authType,omitempty"` SignFlowStatus string `json:"signFlowStatus,omitempty"` Operator *EsignOperator `json:"operator,omitempty"` PsnInfo *EsignPsnInfo `json:"psnInfo,omitempty"` Organization *EsignOrganization `json:"organization,omitempty"` } // EsignOperator 签署人信息 type EsignOperator struct { PsnId string `json:"psnId"` PsnAccount *EsignPsnAccount `json:"psnAccount"` } // EsignPsnInfo 个人认证信息 type EsignPsnInfo struct { PsnId string `json:"psnId"` PsnAccount *EsignPsnAccount `json:"psnAccount"` } // EsignPsnAccount 个人账户信息 type EsignPsnAccount struct { AccountMobile string `json:"accountMobile"` AccountEmail string `json:"accountEmail"` } // EsignOrganization 企业信息 type EsignOrganization struct { OrgName string `json:"orgName"` // 可以根据需要添加更多企业信息字段 } // EsignCallbackApplicationServiceImpl e签宝回调应用服务实现 type EsignCallbackApplicationServiceImpl struct { certManagementService *services.CertificationManagementService certWorkflowService *services.CertificationWorkflowService certificationEsignService *services.CertificationEsignService enterpriseService *user_services.EnterpriseService esignService *esign_service.Client enterpriseRecordService *services.EnterpriseInfoSubmitRecordService txManager *database.TransactionManager logger *zap.Logger } // NewEsignCallbackApplicationService 创建e签宝回调应用服务 func NewEsignCallbackApplicationService( certManagementService *services.CertificationManagementService, certWorkflowService *services.CertificationWorkflowService, certificationEsignService *services.CertificationEsignService, enterpriseService *user_services.EnterpriseService, esignService *esign_service.Client, enterpriseRecordService *services.EnterpriseInfoSubmitRecordService, txManager *database.TransactionManager, logger *zap.Logger, ) EsignCallbackApplicationService { return &EsignCallbackApplicationServiceImpl{ certManagementService: certManagementService, certWorkflowService: certWorkflowService, certificationEsignService: certificationEsignService, enterpriseService: enterpriseService, esignService: esignService, enterpriseRecordService: enterpriseRecordService, txManager: txManager, logger: logger, } } // HandleCallback 处理e签宝回调 func (s *EsignCallbackApplicationServiceImpl) HandleCallback(ctx context.Context, callbackData map[string]interface{}, headers map[string]string, queryParams map[string]string) error { s.logger.Info("开始处理e签宝回调", zap.Any("callback_data", callbackData)) // 1. 验签 if err := s.verifySignature(callbackData, headers, queryParams); err != nil { s.logger.Error("e签宝回调验签失败", zap.Error(err)) return fmt.Errorf("验签失败: %w", err) } // 2. 解析回调数据为结构体 var callback EsignCallbackData jsonBytes, err := json.Marshal(callbackData) if err != nil { return fmt.Errorf("序列化回调数据失败: %w", err) } if err := json.Unmarshal(jsonBytes, &callback); err != nil { return fmt.Errorf("解析回调数据失败: %w", err) } // 3. 记录回调信息 s.logger.Info("e签宝回调信息解析", zap.String("action", callback.Action), zap.String("auth_flow_id", callback.AuthFlowId), zap.String("sign_flow_id", callback.SignFlowId), zap.String("auth_type", callback.AuthType), zap.String("sign_flow_status", callback.SignFlowStatus), zap.Int64("timestamp", callback.Timestamp), ) // 4. 根据回调类型处理业务逻辑 switch callback.Action { case "AUTH_PASS": // 只处理企业认证通过 if callback.AuthType == "ORG" { return s.handleEnterpriseAuthPass(ctx, &callback) } s.logger.Info("忽略非企业认证通过回调", zap.String("auth_type", callback.AuthType)) return nil case "AUTH_FAIL": // 只处理企业认证失败 if callback.AuthType == "ORG" { return s.handleEnterpriseAuthFail(ctx, &callback) } s.logger.Info("忽略非企业认证失败回调", zap.String("auth_type", callback.AuthType)) return nil case "SIGN_FLOW_COMPLETE": // 合同签署流程完成 return s.handleContractSignFlowComplete(ctx, &callback) default: s.logger.Info("忽略未知的回调动作", zap.String("action", callback.Action)) return nil } } // verifySignature 验证e签宝回调签名 func (s *EsignCallbackApplicationServiceImpl) verifySignature(callbackData map[string]interface{}, headers map[string]string, queryParams map[string]string) error { // 1. 获取签名相关参数 signature, ok := headers["X-Tsign-Open-Signature"] if !ok { return fmt.Errorf("缺少签名头: X-Tsign-Open-Signature") } timestamp, ok := headers["X-Tsign-Open-Timestamp"] if !ok { return fmt.Errorf("缺少时间戳头: X-Tsign-Open-Timestamp") } // 2. 构建查询参数字符串 var queryKeys []string for key := range queryParams { queryKeys = append(queryKeys, key) } sort.Strings(queryKeys) // 按ASCII码升序排序 var queryValues []string for _, key := range queryKeys { queryValues = append(queryValues, queryParams[key]) } queryString := strings.Join(queryValues, "") // 3. 获取请求体数据 bodyData, err := s.getRequestBodyString(callbackData) if err != nil { return fmt.Errorf("获取请求体数据失败: %w", err) } // 4. 构建验签数据 data := timestamp + queryString + bodyData // 5. 计算签名 expectedSignature := s.calculateSignature(data, s.esignService.GetConfig().AppSecret) // 6. 比较签名 if strings.ToLower(expectedSignature) != strings.ToLower(signature) { s.logger.Error("签名验证失败", zap.String("expected", strings.ToLower(expectedSignature)), zap.String("received", strings.ToLower(signature)), zap.String("data", data), ) return fmt.Errorf("签名验证失败") } s.logger.Info("e签宝回调验签成功") return nil } // calculateSignature 计算HMAC-SHA256签名 func (s *EsignCallbackApplicationServiceImpl) calculateSignature(data, secret string) string { h := hmac.New(sha256.New, []byte(secret)) h.Write([]byte(data)) return strings.ToUpper(hex.EncodeToString(h.Sum(nil))) } // getRequestBodyString 获取请求体字符串 func (s *EsignCallbackApplicationServiceImpl) getRequestBodyString(callbackData map[string]interface{}) (string, error) { // 将map转换为JSON字符串 jsonBytes, err := json.Marshal(callbackData) if err != nil { return "", fmt.Errorf("JSON序列化失败: %w", err) } return string(jsonBytes), nil } // handleEnterpriseAuthPass 处理企业认证通过回调 func (s *EsignCallbackApplicationServiceImpl) handleEnterpriseAuthPass(ctx context.Context, callback *EsignCallbackData) error { s.logger.Info("处理企业认证通过回调") if callback.Organization == nil { return fmt.Errorf("回调数据中缺少organization字段") } if callback.AuthFlowId == "" { return fmt.Errorf("回调数据中缺少authFlowId字段") } // 查找对应的认证申请 certification, err := s.certManagementService.GetCertificationByAuthFlowID(ctx, callback.AuthFlowId) if err != nil { return fmt.Errorf("查找认证申请失败: %w", err) } if certification.Status != enums.StatusInfoSubmitted { s.logger.Warn("当前状态不允许完成企业认证", zap.String("status", string(certification.Status))) return nil } if err := s.completeEnterpriseAuth(ctx, certification); err != nil { return fmt.Errorf("完成企业认证失败: %w", err) } s.logger.Info("企业认证通过处理完成", zap.String("user_id", certification.UserID), zap.String("certification_id", certification.ID), zap.String("org_name", callback.Organization.OrgName), ) return nil } // handleEnterpriseAuthFail 处理企业认证失败回调 func (s *EsignCallbackApplicationServiceImpl) handleEnterpriseAuthFail(ctx context.Context, callback *EsignCallbackData) error { s.logger.Info("处理企业认证失败回调") if callback.Organization == nil { return fmt.Errorf("回调数据中缺少organization字段") } // 暂时忽略 return nil } // handleContractSignFlowComplete 处理合同签署流程完成回调 func (s *EsignCallbackApplicationServiceImpl) handleContractSignFlowComplete(ctx context.Context, callback *EsignCallbackData) error { s.logger.Info("处理合同签署流程完成回调") if callback.SignFlowId == "" { return fmt.Errorf("回调数据中缺少signFlowId字段") } if callback.SignFlowStatus == "" { return fmt.Errorf("回调数据中缺少signFlowStatus字段") } // 查找对应的认证申请 certification, err := s.certManagementService.GetCertificationByEsignFlowID(ctx, callback.SignFlowId) if err != nil { return fmt.Errorf("查找认证申请失败: %w", err) } // 根据签署流程状态处理 switch callback.SignFlowStatus { case "2": // 已完成(所有签署方完成签署) s.logger.Info("合同签署流程已完成,所有签署方完成签署") // 完成合同签署 if err := s.certWorkflowService.CompleteContractSign(ctx, certification.ID, "所有签署方完成签署"); err != nil { return fmt.Errorf("完成合同签署失败: %w", err) } // 自动完成认证 if err := s.certWorkflowService.CompleteCertification(ctx, certification.ID); err != nil { return fmt.Errorf("完成认证失败: %w", err) } s.logger.Info("合同签署流程完成处理成功", zap.String("user_id", certification.UserID), zap.String("certification_id", certification.ID), zap.String("sign_flow_id", callback.SignFlowId), zap.String("sign_flow_status", callback.SignFlowStatus), ) case "3": // 已撤销(发起方撤销签署任务) s.logger.Info("合同签署流程已撤销") // 可以在这里添加撤销处理逻辑 s.logger.Info("合同签署流程撤销处理完成", zap.String("user_id", certification.UserID), zap.String("certification_id", certification.ID), zap.String("sign_flow_id", callback.SignFlowId), zap.String("sign_flow_status", callback.SignFlowStatus), ) // 暂无撤销业务逻辑 case "5": // 已过期(签署截止日到期后触发) s.logger.Info("合同签署流程已过期") // 可以在这里添加过期处理逻辑 s.logger.Info("合同签署流程过期处理完成", zap.String("user_id", certification.UserID), zap.String("certification_id", certification.ID), zap.String("sign_flow_id", callback.SignFlowId), zap.String("sign_flow_status", callback.SignFlowStatus), ) // 暂无过期业务逻辑 case "7": // 已拒签(签署方拒绝签署) s.logger.Info("合同签署流程已拒签") // 可以在这里添加拒签处理逻辑 s.logger.Info("合同签署流程拒签处理完成", zap.String("user_id", certification.UserID), zap.String("certification_id", certification.ID), zap.String("sign_flow_id", callback.SignFlowId), zap.String("sign_flow_status", callback.SignFlowStatus), ) default: s.logger.Warn("未知的签署流程状态", zap.String("sign_flow_status", callback.SignFlowStatus), zap.String("sign_flow_id", callback.SignFlowId), ) // 暂无拒签业务逻辑 } return nil } // 企业认证成功后操作 func (s *EsignCallbackApplicationServiceImpl) completeEnterpriseAuth(ctx context.Context, certification *entities.Certification) error { err := s.txManager.ExecuteInTx(ctx, func(txCtx context.Context) error { // 1. 获取企业信息提交记录 enterpriseRecord, err := s.enterpriseRecordService.GetLatestByUserID(txCtx, certification.UserID) if err != nil { return fmt.Errorf("获取企业信息失败: %w", err) } // 2. 转换状态 if err := s.certWorkflowService.CompleteEnterpriseVerification(txCtx, certification.ID); err != nil { return err } // 3. 创建企业信息 _, err = s.enterpriseService.CreateEnterpriseInfo(txCtx, certification.UserID, enterpriseRecord.CompanyName, enterpriseRecord.UnifiedSocialCode, enterpriseRecord.LegalPersonName, enterpriseRecord.LegalPersonID) if err != nil { s.logger.Warn("创建用户企业信息失败", zap.Error(err)) return err } return nil }) if err != nil { return fmt.Errorf("完成企业认证失败: %w", err) } return nil }