248 lines
7.1 KiB
Go
248 lines
7.1 KiB
Go
|
|
package fadada
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"context"
|
|||
|
|
"fmt"
|
|||
|
|
"net/url"
|
|||
|
|
"strings"
|
|||
|
|
|
|||
|
|
"hyapi-server/internal/domains/certification/enums"
|
|||
|
|
"hyapi-server/internal/domains/certification/ports"
|
|||
|
|
sharedfadada "hyapi-server/internal/shared/fadada"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
type Provider struct {
|
|||
|
|
client *sharedfadada.Client
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func NewProvider(client *sharedfadada.Client) *Provider {
|
|||
|
|
return &Provider{client: client}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (p *Provider) Platform() enums.SignPlatform {
|
|||
|
|
return enums.SignPlatformFadada
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (p *Provider) GenerateEnterpriseAuth(ctx context.Context, req *ports.EnterpriseAuthRequest) (*ports.AuthLinkResult, error) {
|
|||
|
|
_ = ctx
|
|||
|
|
res, err := p.client.GenerateEnterpriseAuth(&sharedfadada.EnterpriseAuthRequest{
|
|||
|
|
ClientCorpID: req.ClientCorpID,
|
|||
|
|
ClientUserID: req.ClientUserID,
|
|||
|
|
CompanyName: req.CompanyName,
|
|||
|
|
UnifiedSocialCode: req.UnifiedSocialCode,
|
|||
|
|
LegalPersonName: req.LegalPersonName,
|
|||
|
|
LegalPersonID: req.LegalPersonID,
|
|||
|
|
TransactorName: req.TransactorName,
|
|||
|
|
TransactorMobile: req.TransactorMobile,
|
|||
|
|
TransactorID: req.TransactorID,
|
|||
|
|
})
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
return &ports.AuthLinkResult{
|
|||
|
|
AuthFlowID: res.AuthFlowID,
|
|||
|
|
AuthURL: res.AuthURL,
|
|||
|
|
AuthShortURL: firstNonEmpty(res.AuthShortURL, res.AuthURL),
|
|||
|
|
}, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (p *Provider) QueryOrgVerified(ctx context.Context, req *ports.OrgIdentityQuery) (bool, error) {
|
|||
|
|
_ = ctx
|
|||
|
|
return p.client.QueryOrgVerified(&sharedfadada.QueryOrgIdentityRequest{
|
|||
|
|
CorpName: req.OrgName,
|
|||
|
|
CorpIdentNo: req.OrgIdentNo,
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GenerateContractFile 法大大走签署任务模板(/sign-task/create-with-template),
|
|||
|
|
// 此处返回空结果,实际填单由 CreateSignFlow 通过 /sign-task/field/fill-values 完成。
|
|||
|
|
func (p *Provider) GenerateContractFile(ctx context.Context, req *ports.ContractGenerateRequest) (*ports.ContractFileResult, error) {
|
|||
|
|
_ = ctx
|
|||
|
|
_ = req
|
|||
|
|
return &ports.ContractFileResult{FileID: "", FileDownloadURL: ""}, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (p *Provider) CreateSignFlow(ctx context.Context, req *ports.SignFlowCreateRequest) (*ports.SignFlowResult, error) {
|
|||
|
|
_ = ctx
|
|||
|
|
createReq := &sharedfadada.CreateSignFlowRequest{
|
|||
|
|
Subject: req.Subject,
|
|||
|
|
PartyAOpenCorpID: req.PartyAOpenCorpID,
|
|||
|
|
PartyAName: req.PartyAName,
|
|||
|
|
PartyAUSCC: req.PartyAUSCC,
|
|||
|
|
TransactorName: req.TransactorName,
|
|||
|
|
TransactorMobile: req.TransactorMobile,
|
|||
|
|
TransactorID: req.TransactorID,
|
|||
|
|
TransReferenceID: req.TransReferenceID,
|
|||
|
|
}
|
|||
|
|
if req.Fill != nil {
|
|||
|
|
createReq.Fill = &sharedfadada.ContractFillRequest{
|
|||
|
|
AgreementNo: req.Fill.AgreementNo,
|
|||
|
|
CompanyName: req.Fill.CompanyName,
|
|||
|
|
UnifiedSocialCode: req.Fill.UnifiedSocialCode,
|
|||
|
|
EnterpriseAddress: req.Fill.EnterpriseAddress,
|
|||
|
|
AuthorizedRepName: req.Fill.AuthorizedRepName,
|
|||
|
|
SignDate: req.Fill.SignDate,
|
|||
|
|
FileName: req.Fill.FileName,
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 签署模板创建必须填控件
|
|||
|
|
createReq.Fill = &sharedfadada.ContractFillRequest{
|
|||
|
|
CompanyName: req.PartyAName,
|
|||
|
|
UnifiedSocialCode: req.PartyAUSCC,
|
|||
|
|
AuthorizedRepName: req.TransactorName,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
createRes, err := p.client.CreateSignFlow(createReq)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
if req.SkipActorURL {
|
|||
|
|
return &ports.SignFlowResult{
|
|||
|
|
SignFlowID: createRes.SignTaskID,
|
|||
|
|
}, nil
|
|||
|
|
}
|
|||
|
|
urlRes, err := p.client.GetSignURL(createRes.SignTaskID, sharedfadada.ActorIDPartyA)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
embedURL := strings.TrimSpace(urlRes.EmbedURL)
|
|||
|
|
if embedURL == "" {
|
|||
|
|
return nil, fmt.Errorf("法大大未返回可嵌入签署链接 actorSignTaskEmbedUrl")
|
|||
|
|
}
|
|||
|
|
return &ports.SignFlowResult{
|
|||
|
|
SignFlowID: createRes.SignTaskID,
|
|||
|
|
SignURL: embedURL,
|
|||
|
|
ShortURL: strings.TrimSpace(urlRes.SignURL),
|
|||
|
|
}, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (p *Provider) GetActorSignURL(ctx context.Context, req *ports.GetActorSignURLRequest) (*ports.ActorSignURLResult, error) {
|
|||
|
|
_ = ctx
|
|||
|
|
if req == nil || strings.TrimSpace(req.SignFlowID) == "" {
|
|||
|
|
return nil, fmt.Errorf("signFlowId 不能为空")
|
|||
|
|
}
|
|||
|
|
urlRes, err := p.client.GetSignURL(strings.TrimSpace(req.SignFlowID), sharedfadada.ActorIDPartyA)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
embedURL := strings.TrimSpace(urlRes.EmbedURL)
|
|||
|
|
if embedURL == "" {
|
|||
|
|
return nil, fmt.Errorf("法大大未返回可嵌入签署链接 actorSignTaskEmbedUrl")
|
|||
|
|
}
|
|||
|
|
return &ports.ActorSignURLResult{
|
|||
|
|
SignFlowID: req.SignFlowID,
|
|||
|
|
EmbedURL: embedURL,
|
|||
|
|
ShortURL: strings.TrimSpace(urlRes.SignURL),
|
|||
|
|
}, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (p *Provider) GetSignTaskPreviewURL(ctx context.Context, signFlowID string) (*ports.SignTaskPreviewURLResult, error) {
|
|||
|
|
_ = ctx
|
|||
|
|
signFlowID = strings.TrimSpace(signFlowID)
|
|||
|
|
if signFlowID == "" {
|
|||
|
|
return nil, fmt.Errorf("signFlowId 不能为空")
|
|||
|
|
}
|
|||
|
|
res, err := p.client.GetSignTaskPreviewURL(signFlowID)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
return &ports.SignTaskPreviewURLResult{
|
|||
|
|
SignFlowID: signFlowID,
|
|||
|
|
PreviewURL: res.PreviewURL,
|
|||
|
|
}, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (p *Provider) QuerySignStatus(ctx context.Context, flowID string) (*ports.SignStatusResult, error) {
|
|||
|
|
_ = ctx
|
|||
|
|
st, err := p.client.QuerySignStatus(flowID)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
return &ports.SignStatusResult{
|
|||
|
|
SignFlowID: flowID,
|
|||
|
|
Status: st.SignTaskStatus,
|
|||
|
|
Completed: st.Completed,
|
|||
|
|
Rejected: st.Terminated,
|
|||
|
|
Expired: false,
|
|||
|
|
Message: firstNonEmpty(st.TerminationNote, st.RevokeReason),
|
|||
|
|
}, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (p *Provider) DownloadSignedFiles(ctx context.Context, flowID string) ([]*ports.SignedFile, error) {
|
|||
|
|
_ = ctx
|
|||
|
|
res, err := p.client.DownloadSignedFiles(flowID)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
files := make([]*ports.SignedFile, 0, len(res.Files))
|
|||
|
|
for _, f := range res.Files {
|
|||
|
|
files = append(files, &ports.SignedFile{
|
|||
|
|
DownloadURL: f.DownloadURL,
|
|||
|
|
DownloadID: f.DownloadID,
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
return files, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (p *Provider) VerifyCallback(ctx context.Context, headers map[string]string, body []byte) error {
|
|||
|
|
_ = ctx
|
|||
|
|
bizContent := extractBizContent(body)
|
|||
|
|
return p.client.VerifyCallback(headers, bizContent)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (p *Provider) ParseCallback(ctx context.Context, headers map[string]string, body []byte) (*ports.CallbackEvent, error) {
|
|||
|
|
_ = ctx
|
|||
|
|
bizContent := extractBizContent(body)
|
|||
|
|
ev, err := p.client.ParseCallback(headers, bizContent)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
out := &ports.CallbackEvent{
|
|||
|
|
Event: ev.Event,
|
|||
|
|
SignFlowID: ev.SignTaskID,
|
|||
|
|
AuthFlowID: firstNonEmpty(ev.ClientCorpID, ev.OpenCorpID),
|
|||
|
|
Raw: ev.Raw,
|
|||
|
|
}
|
|||
|
|
if ev.IsSignCompletedCallback() {
|
|||
|
|
out.SignCompleted = true
|
|||
|
|
}
|
|||
|
|
// 企业授权/认证成功事件
|
|||
|
|
switch strings.ToLower(ev.Event) {
|
|||
|
|
case "corp-authorize", "corp-authorize-success", "corporation_authorize", "auth-pass":
|
|||
|
|
out.AuthPassed = true
|
|||
|
|
}
|
|||
|
|
if strings.EqualFold(ev.AuthResult, "success") || strings.EqualFold(ev.AuthResult, "pass") {
|
|||
|
|
out.AuthPassed = true
|
|||
|
|
}
|
|||
|
|
return out, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func extractBizContent(body []byte) string {
|
|||
|
|
raw := strings.TrimSpace(string(body))
|
|||
|
|
if raw == "" {
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
if strings.HasPrefix(raw, "{") {
|
|||
|
|
return raw
|
|||
|
|
}
|
|||
|
|
values, err := url.ParseQuery(raw)
|
|||
|
|
if err == nil {
|
|||
|
|
if v := values.Get("bizContent"); v != "" {
|
|||
|
|
return v
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return raw
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func firstNonEmpty(values ...string) string {
|
|||
|
|
for _, v := range values {
|
|||
|
|
if strings.TrimSpace(v) != "" {
|
|||
|
|
return v
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var _ ports.SignPlatformProvider = (*Provider)(nil)
|