summaryrefslogtreecommitdiff
path: root/lib/docuSign/docuSignFns.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/docuSign/docuSignFns.ts')
-rw-r--r--lib/docuSign/docuSignFns.ts383
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 };
+ }
+}