54 lines
2.4 KiB
TypeScript
54 lines
2.4 KiB
TypeScript
import type { ESignAdapter, SendParams, WebhookResult } from "./types"
|
|||
|
|
|
||
|
|
// Dropbox Sign (formerly HelloSign). API-key auth. Docs:
|
||
|
|
// https://developers.hellosign.com/api/reference/operation/signatureRequestSend/
|
||
|
|
const API_KEY = process.env.DROPBOX_SIGN_API_KEY ?? ""
|
||
|
|
const TEST_MODE = process.env.DROPBOX_SIGN_TEST_MODE === "true" ? "1" : "0"
|
||
|
|
const BASE = "https://api.hellosign.com/v3"
|
||
|
|
|
||
|
|
function auth() {
|
||
|
|
return "Basic " + Buffer.from(`${API_KEY}:`).toString("base64")
|
||
|
|
}
|
||
|
|
|
||
|
|
export const dropboxSign: ESignAdapter = {
|
||
|
|
id: "dropbox_sign",
|
||
|
|
label: "Dropbox Sign",
|
||
|
|
configured: () => Boolean(API_KEY),
|
||
|
|
|
||
|
|
async send({ document, documentName, signerEmail, signerName, subject, message }: SendParams) {
|
||
|
|
const fd = new FormData()
|
||
|
|
fd.append("subject", subject)
|
||
|
|
fd.append("message", message)
|
||
|
|
fd.append("test_mode", TEST_MODE)
|
||
|
|
fd.append("signers[0][email_address]", signerEmail)
|
||
|
|
fd.append("signers[0][name]", signerName)
|
||
|
|
fd.append("file[0]", new Blob([new Uint8Array(document)], { type: "application/pdf" }), documentName)
|
||
|
|
|
||
|
|
const res = await fetch(`${BASE}/signature_request/send`, { method: "POST", headers: { Authorization: auth() }, body: fd })
|
||
|
|
if (!res.ok) throw new Error(`Dropbox Sign ${res.status}: ${(await res.text()).slice(0, 300)}`)
|
||
|
|
const j = (await res.json()) as { signature_request?: { signature_request_id?: string } }
|
||
|
|
const id = j.signature_request?.signature_request_id
|
||
|
|
if (!id) throw new Error("Dropbox Sign did not return a signature_request_id")
|
||
|
|
return { externalId: id }
|
||
|
|
},
|
||
|
|
|
||
|
|
parseWebhook(body): WebhookResult | null {
|
||
|
|
// Dropbox Sign posts multipart form-data with a `json` field.
|
||
|
|
let event: { event?: { event_type?: string }; signature_request?: { signature_request_id?: string } }
|
||
|
|
try {
|
||
|
|
// Extract the JSON payload whether sent raw or as a form field.
|
||
|
|
const m = body.match(/name="json"\r?\n\r?\n([\s\S]*?)\r?\n--/) ?? body.match(/^(\{[\s\S]*\})\s*$/)
|
||
|
|
event = JSON.parse(m ? m[1] : body)
|
||
|
|
} catch {
|
||
|
|
return null
|
||
|
|
}
|
||
|
|
const type = event.event?.event_type
|
||
|
|
const externalId = event.signature_request?.signature_request_id
|
||
|
|
if (!externalId || !type) return null
|
||
|
|
if (type === "signature_request_all_signed") return { externalId, status: "signed" }
|
||
|
|
if (type === "signature_request_declined") return { externalId, status: "declined" }
|
||
|
|
if (type === "signature_request_canceled") return { externalId, status: "voided" }
|
||
|
|
return null
|
||
|
|
},
|
||
|
|
}
|