diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-03-26 00:37:41 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-03-26 00:37:41 +0000 |
| commit | e0dfb55c5457aec489fc084c4567e791b4c65eb1 (patch) | |
| tree | 68543a65d88f5afb3a0202925804103daa91bc6f /lib/docuSign/docuSignFns.ts | |
3/25 까지의 대표님 작업사항
Diffstat (limited to 'lib/docuSign/docuSignFns.ts')
| -rw-r--r-- | lib/docuSign/docuSignFns.ts | 383 |
1 files changed, 383 insertions, 0 deletions
diff --git a/lib/docuSign/docuSignFns.ts b/lib/docuSign/docuSignFns.ts new file mode 100644 index 00000000..87977a0b --- /dev/null +++ b/lib/docuSign/docuSignFns.ts @@ -0,0 +1,383 @@ +"use server"; + +import docusign from "docusign-esign"; +import fs from "fs"; +import path from "path"; +import jwtConfig from "./jwtConfig/jwtConfig.json"; +import dayjs from "dayjs"; +import { ContractInfo, ContractorInfo } from "./types"; + +const SCOPES = ["signature", "impersonation"]; + +//DocuSign 인증 정보 +async function authenticate(): Promise< + | undefined + | { + accessToken: string; + apiAccountId: string; + basePath: string; + } +> { + const jwtLifeSec = 10 * 60; + const dsApi = new docusign.ApiClient(); + dsApi.setOAuthBasePath(jwtConfig.dsOauthServer.replace("https://", "")); + const privateKeyPath = path.resolve( + process.cwd(), + jwtConfig.privateKeyLocation + ); + + let rsaKey: Buffer = fs.readFileSync(privateKeyPath); + + try { + const results = await dsApi.requestJWTUserToken( + jwtConfig.dsJWTClientId, + jwtConfig.impersonatedUserGuid, + SCOPES, + rsaKey, + jwtLifeSec + ); + const accessToken = results.body.access_token; + + const userInfoResults = await dsApi.getUserInfo(accessToken); + let userInfo = userInfoResults.accounts.find( + (account: Partial<{ isDefault: string }>) => account.isDefault === "true" + ); + + return { + accessToken: results.body.access_token, + apiAccountId: userInfo.accountId, + basePath: `${userInfo.baseUri}/restapi`, + }; + } catch (e) { + console.error("❌ 인증 실패:", e); + } +} + +async function getSignerId( + basePath: string, + accountId: string, + accessToken: string, + envelopeId: string, + roleName: string +): Promise<string | null> { + const apiClient = new docusign.ApiClient(); + apiClient.setBasePath(basePath); + apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken); + + const envelopesApi = new docusign.EnvelopesApi(apiClient); + + try { + const recipients = await envelopesApi.listRecipients(accountId, envelopeId); + + const singers = recipients?.signers ?? []; + + // 🔹 특정 서명자(Role Name 기준)의 Recipient ID 찾기 + const signer = singers.find((s) => s.roleName === roleName); + if (!signer) { + console.error("❌ 해당 Role Name을 가진 서명자를 찾을 수 없습니다."); + return null; + } + + return signer.recipientId as string; + } catch (error) { + console.error("❌ 서명자 ID 조회 실패:", error); + return null; + } +} + +//계약서 서명 요청 +export async function requestContractSign( + contractTemplateId: string, + contractInfo: ContractInfo[], + subcontractorinfo: ContractorInfo, + contractorInfo: ContractorInfo, + ccInfo: ContractorInfo[], + brandId: string | undefined = undefined +): Promise< + Partial<{ + result: boolean; + envelopeId: string; + error: any; + }> +> { + let accountInfo = await authenticate(); + if (accountInfo) { + const { accessToken, basePath, apiAccountId } = accountInfo; + const { + email: subEmail, + name: subConName, + roleName: subRoleName, + } = subcontractorinfo; + + const { + email: conEmail, + name: conName, + roleName: roleName, + } = contractorInfo; + + const apiClient = new docusign.ApiClient(); + apiClient.setBasePath(basePath); + apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken); + const envelopesApi = new docusign.EnvelopesApi(apiClient); + + const signer1: docusign.TemplateRole = { + email: subEmail, + name: subConName, + roleName: subRoleName, + }; + + const signer1Tabs: docusign.Tabs = { + textTabs: [ + ...contractInfo.map((c): docusign.Text => { + const textField: docusign.Text = { + tabLabel: c.tabLabel, + value: c.value, + locked: "true", + }; + return textField; + }), + ], + }; + + const signer2: docusign.TemplateRole = { + email: conEmail, + name: conName, + roleName: roleName, + }; + + const signer2Tabs: docusign.Tabs = { + dateSignedTabs: [ + { + tabLabel: "contract_complete_date", + }, + ], + }; + + signer1.tabs = signer1Tabs; + signer2.tabs = signer2Tabs; + + const envelopeDefinition: docusign.EnvelopeDefinition = { + templateId: contractTemplateId, + templateRoles: [signer1, signer2, ...ccInfo], // 두 명의 서명자 추가 + status: "sent", // 즉시 발송 + }; + + if (brandId) { + envelopeDefinition.brandId = brandId; + } + + try { + let envelopeSummary = await envelopesApi.createEnvelope(apiAccountId, { + envelopeDefinition, + }); + + // console.log("✅ 서명 요청 완료, Envelope ID:", envelopeSummary); + return { + result: true, + envelopeId: envelopeSummary.envelopeId, + }; + } catch (error) { + console.dir(error); + return { + result: false, + error, + }; + } + } else { + return { + result: false, + }; + } +} + +//서명된 계약서 다운로드 +export async function downloadContractFile(envelopeId: string): Promise< + Partial<{ + result: boolean; + fileName: string; + buffer: Buffer; + envelopeId: string; + error: any; + }> +> { + let accountInfo = await authenticate(); + + if (accountInfo) { + const { accessToken, apiAccountId, basePath } = accountInfo; + + const apiClient = new docusign.ApiClient(); + apiClient.setBasePath(basePath); + apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken); + + const envelopesApi = new docusign.EnvelopesApi(apiClient); + + try { + //Document ID 등 파일 정보를 호출 + const response = await envelopesApi.listDocuments( + apiAccountId, + envelopeId, + null + ); + + const { envelopeDocuments } = response || { envelopeDocuments: [] }; + + if (Array.isArray(envelopeDocuments) && envelopeDocuments.length > 0) { + const { documentId, name } = envelopeDocuments[0] as { + documentId: string; + name: string; + }; + + //Document Buffer 호출 + const downloadFile = await envelopesApi.getDocument( + apiAccountId, + envelopeId, + documentId, + {} + ); + + if (documentId && documentId !== "certificate") { + const bufferData: Buffer = downloadFile as unknown as Buffer; + return { + result: true, + fileName: name, + buffer: bufferData, + envelopeId, + }; + } + } + + return { + result: false, + }; + } catch (error) { + return { + result: false, + error, + }; + } + } else { + return { + result: false, + }; + } +} + +//최종 서명 날짜 찾기 +export async function findContractCompleteTime( + envelopeId: string, + lastSignerRoleName: string +): Promise<{ + completedDateTime: string; + year: string; + month: string; + day: string; + time: string; +} | null> { + let accountInfo = await authenticate(); + + if (!accountInfo) { + console.error("❌ 인증 실패: API 요청을 중단합니다."); + return null; + } + + const { accessToken, apiAccountId: accountId, basePath } = accountInfo; + + const apiClient = new docusign.ApiClient(); + apiClient.setBasePath(basePath); + apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken); + + const envelopesApi = new docusign.EnvelopesApi(apiClient); + + try { + const envelope = await envelopesApi.getEnvelope(accountId, envelopeId); + if (!envelope.completedDateTime) { + console.error("❌ 서명 완료 날짜가 없습니다."); + return null; + } + + // 🔹 `SIGNER_ID` 가져오기 + const signerId = await getSignerId( + basePath, + accountId, + accessToken, + envelopeId, + lastSignerRoleName + ); + if (!signerId) { + console.error("❌ 서명자 ID를 찾을 수 없습니다."); + return null; + } + + const completedDate = dayjs(envelope.completedDateTime); + const year = completedDate.format("YYYY").toString(); + const month = completedDate.format("MM").toString(); + const day = completedDate.format("DD").toString(); + const time = completedDate.format("HH:mm").toString(); + + return { + completedDateTime: envelope.completedDateTime, + year, + month, + day, + time, + }; + } catch (error) { + console.error("❌ 서명 완료 후 날짜 추가 실패:", error); + return null; + } +} + +export async function getRecipients( + envelopeId: string, + recipientId: string +): Promise<{ result: boolean; message?: string }> { + try { + let accountInfo = await authenticate(); + + if (!accountInfo) { + console.error("❌ 인증 실패: API 요청을 중단합니다."); + return { + result: false, + message: "인증 실패: API 요청을 중단합니다.", + }; + } + + const { accessToken, apiAccountId: accountId, basePath } = accountInfo; + + const apiClient = new docusign.ApiClient(); + apiClient.setBasePath(basePath); + apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken); + + const envelopesApi = new docusign.EnvelopesApi(apiClient); + + const response = await envelopesApi.listRecipients(accountId, envelopeId); + + const singers: { [key: string]: any }[] = response?.signers ?? []; + + // 🔹 특정 서명자(Role Name 기준)의 Recipient ID 찾기 + const signer = singers.find((s) => s.recipientId === recipientId); + if (!signer) { + console.error("❌ 해당 Role Name을 가진 서명자를 찾을 수 없습니다."); + return { + result: false, + message: "해당 Recipient id를 가진 서명자를 찾을 수 없습니다.", + }; + } + + const { autoRespondedReason, status } = signer; + + if (autoRespondedReason || status === "status") { + return { + result: false, + message: autoRespondedReason, + }; + } + + return { + result: true, + }; + } catch (error) { + console.error("Error retrieving recipients:", error); + return { result: false, message: (error as Error).message }; + } +} |
