UNPKG

@jackiemacklein/nettz-utils

Version:

Serviços de imagem, e-mail, códigos de barras, utilitários numéricos e componentes React para apps Node.js com TypeScript

322 lines (321 loc) 14 kB
"use strict"; /** * @author Jackiê Macklein * @company Onside tecnologia/Nettz * @copyright Todos direitos reservados. * @description Cliente HTTP para API do Asaas (clientes, cobranças e webhooks). */ Object.defineProperty(exports, "__esModule", { value: true }); exports.createAsaasClient = createAsaasClient; const errors_1 = require("./errors"); const normalize_1 = require("./normalize"); const normalizeBaseUrl_1 = require("./normalizeBaseUrl"); const validation_1 = require("./validation"); async function readResponseBody(res) { try { return await res.text(); } catch { return ""; } } const ASAAS_DEBUG_LOG_PREFIX = "[Asaas]"; function maskApiKey(key) { if (key.length <= 4) return `***(${key.length} chars)`; return `${key.slice(0, 4)}…(${key.length} chars)`; } function parseAsaasErrorMessage(body) { if (!body.trim()) return undefined; try { const parsed = JSON.parse(body); if (Array.isArray(parsed.errors) && parsed.errors.length > 0) { return parsed.errors.map(item => item.description).filter(Boolean).join(" | "); } if (typeof parsed.message === "string" && parsed.message.trim()) { return parsed.message.trim(); } } catch { /* corpo não é JSON */ } return undefined; } function describeAsaasHttpError(status, statusText, text, path) { const apiMessage = parseAsaasErrorMessage(text); if (apiMessage) { return `Asaas HTTP ${status}: ${apiMessage}`; } const trimmed = text.trim(); if (!trimmed) { return `Asaas HTTP ${status} ${statusText} em \`${path}\` sem corpo na resposta.`; } const preview = trimmed.length > 800 ? `${trimmed.slice(0, 800)}…` : trimmed; return `Asaas HTTP ${status} ${statusText}: ${preview}`; } function buildListChargesQuery(params) { const entries = { offset: params.offset, limit: params.limit, customer: params.customer, customerGroupName: params.customerGroupName, billingType: params.billingType, status: params.status, subscription: params.subscription, installment: params.installment, externalReference: params.externalReference, paymentDate: params.paymentDate, "dateCreated[ge]": params.dateCreatedGe, "dateCreated[le]": params.dateCreatedLe, "paymentDate[ge]": params.paymentDateGe, "paymentDate[le]": params.paymentDateLe, "dueDate[ge]": params.dueDateGe, "dueDate[le]": params.dueDateLe, user: params.user, checkoutSession: params.checkoutSession, anticipated: params.anticipated, anticipable: params.anticipable, pixQrCodeId: params.pixQrCodeId, }; const sp = new URLSearchParams(); for (const [key, value] of Object.entries(entries)) { if (value !== undefined && value !== null && value !== "") { sp.set(key, String(value)); } } const qs = sp.toString(); return qs ? `?${qs}` : ""; } function createAsaasClient(config) { var _a, _b, _c, _d; if (!((_a = config.apiKey) === null || _a === void 0 ? void 0 : _a.trim())) { throw new errors_1.AsaasApiError("Asaas: apiKey é obrigatório", 400, "", "CONFIG"); } const apiVersion = (_b = config.apiVersion) !== null && _b !== void 0 ? _b : "v3"; const rootBaseUrl = (0, normalizeBaseUrl_1.normalizeAsaasBaseUrl)(config.baseUrl); const baseUrl = `${rootBaseUrl}/${apiVersion}`; const timeoutMs = (_c = config.timeoutMs) !== null && _c !== void 0 ? _c : 30000; const fetchFn = (_d = config.fetchImpl) !== null && _d !== void 0 ? _d : fetch; const debug = Boolean(config.debug); const apiKey = config.apiKey.trim(); const normalizeToIntegration = new normalize_1.NormalizeToIntegration(); const normalizeToLocal = new normalize_1.NormalizeToLocal(); const log = (...args) => { if (debug) { console.log(ASAAS_DEBUG_LOG_PREFIX, new Date().toISOString(), ...args); } }; log("client init", { baseUrl, apiVersion, apiKey: maskApiKey(apiKey) }); async function request(path, init) { var _a, _b; const method = (init === null || init === void 0 ? void 0 : init.method) || "GET"; const url = `${baseUrl}${path.startsWith("/") ? path : `/${path}`}`; log("request", method, path, { hasJsonBody: Boolean(init === null || init === void 0 ? void 0 : init.body) }); const controller = new AbortController(); const t = setTimeout(() => controller.abort(), (_a = init === null || init === void 0 ? void 0 : init.timeoutMs) !== null && _a !== void 0 ? _a : timeoutMs); try { let res; try { res = await fetchFn(url, { ...init, signal: (_b = init === null || init === void 0 ? void 0 : init.signal) !== null && _b !== void 0 ? _b : controller.signal, headers: { Accept: "application/json", ...((init === null || init === void 0 ? void 0 : init.body) ? { "Content-Type": "application/json" } : {}), access_token: apiKey, ...init === null || init === void 0 ? void 0 : init.headers, }, }); } catch (e) { log("fetch exceção", path, e instanceof Error ? e.message : String(e)); throw e; } const text = await readResponseBody(res); log("response", path, res.status, res.ok, { bytes: text.length }); if (!res.ok) { const message = describeAsaasHttpError(res.status, res.statusText, text, path); log("HTTP erro", path, res.status, message); throw new errors_1.AsaasApiError(message, res.status, text); } if (!text || text.trim() === "") { return undefined; } try { return JSON.parse(text); } catch { throw new errors_1.AsaasApiError("Asaas resposta não é JSON válido", res.status, text); } } finally { clearTimeout(t); } } async function enrichChargeResult(paymentId, billingType, base) { var _a; let result = { ...base, id: paymentId }; if (billingType === "BOLETO") { const billet = await request(`/payments/${encodeURIComponent(paymentId)}/identificationField`); result = { ...result, billet: { ...billet, url: (_a = base.bankSlipUrl) !== null && _a !== void 0 ? _a : result.url, }, }; } if (billingType === "PIX") { const pix = await request(`/payments/${encodeURIComponent(paymentId)}/pixQrCode`); result = { ...result, pix }; } return result; } function rethrowAsaasOperationError(err, fallbackCode, fallbackPrefix) { if (err instanceof errors_1.AsaasApiError) { if (err.asaasErrorCode === "INVALID_DATA") throw err; const apiMessage = parseAsaasErrorMessage(err.body); throw new errors_1.AsaasApiError(apiMessage ? `${fallbackPrefix}: ${apiMessage}` : err.message, err.status, err.body, fallbackCode); } const message = err instanceof Error ? err.message : String(err); throw new errors_1.AsaasApiError(`${fallbackPrefix}: ${message}`, 500, "", fallbackCode); } return { baseUrl, debug, async createCustomer(data) { const payload = normalizeToIntegration.customer(data); (0, validation_1.assertValidAsaasPayload)("CREATE-CUSTOMER", payload); try { const response = await request("/customers", { method: "POST", body: JSON.stringify(payload), }); return normalizeToLocal.customer(response); } catch (err) { rethrowAsaasOperationError(err, "FAILED_CREATE_PAYMENT", "Erro ao criar cliente"); } }, async editCustomer(customerId, data) { const payload = normalizeToIntegration.customer(data); (0, validation_1.assertValidAsaasPayload)("EDIT-CUSTOMER", payload); try { const response = await request(`/customers/${encodeURIComponent(customerId)}`, { method: "POST", body: JSON.stringify(payload), }); return normalizeToLocal.customer(response); } catch (err) { rethrowAsaasOperationError(err, "FAILED_EDIT_PAYMENT", "Erro ao editar cliente"); } }, async deleteCustomer(customerId) { try { return await request(`/customers/${encodeURIComponent(customerId)}`, { method: "DELETE" }); } catch (err) { rethrowAsaasOperationError(err, "FAILED_DELETE_PAYMENT", "Erro ao excluir cliente"); } }, async createCharge(data) { const payload = normalizeToIntegration.charge(data); (0, validation_1.assertValidAsaasPayload)("CREATE-CHARGE", payload); try { const response = await request("/payments", { method: "POST", body: JSON.stringify(payload), }); const paymentId = String(response.id); const enriched = await enrichChargeResult(paymentId, String(payload.billingType), response); return normalizeToLocal.charge(enriched); } catch (err) { rethrowAsaasOperationError(err, "FAILED_CREATE_PAYMENT", "Erro ao criar cobrança"); } }, async getCharge(paymentId, options) { try { const response = await request(`/payments/${encodeURIComponent(paymentId)}`); const enrich = (options === null || options === void 0 ? void 0 : options.enrichPixBillet) !== false; const result = enrich ? await enrichChargeResult(paymentId, String(response.billingType), response) : response; return normalizeToLocal.charge(result); } catch (err) { rethrowAsaasOperationError(err, "FAILED_GET_PAYMENT", "Erro ao consultar cobrança"); } }, async listCharges(params = {}) { var _a, _b, _c, _d, _e, _f, _g; try { const response = await request(`/payments${buildListChargesQuery(params)}`); const items = (_a = response.data) !== null && _a !== void 0 ? _a : []; return { hasMore: (_b = response.hasMore) !== null && _b !== void 0 ? _b : false, totalCount: (_c = response.totalCount) !== null && _c !== void 0 ? _c : items.length, limit: (_e = (_d = response.limit) !== null && _d !== void 0 ? _d : params.limit) !== null && _e !== void 0 ? _e : items.length, offset: (_g = (_f = response.offset) !== null && _f !== void 0 ? _f : params.offset) !== null && _g !== void 0 ? _g : 0, charges: items.map(item => normalizeToLocal.charge(item)), }; } catch (err) { rethrowAsaasOperationError(err, "FAILED_LIST_PAYMENT", "Erro ao listar cobranças"); } }, async editCharge(paymentId, data) { const payload = normalizeToIntegration.charge(data); (0, validation_1.assertValidAsaasPayload)("EDIT-CHARGE", payload); try { const response = await request(`/payments/${encodeURIComponent(paymentId)}`, { method: "POST", body: JSON.stringify(payload), }); const enriched = await enrichChargeResult(paymentId, String(payload.billingType), response); return normalizeToLocal.charge(enriched); } catch (err) { rethrowAsaasOperationError(err, "FAILED_EDIT_PAYMENT", "Erro ao editar cobrança"); } }, async deleteCharge(paymentId) { try { return await request(`/payments/${encodeURIComponent(paymentId)}`, { method: "DELETE" }); } catch (err) { rethrowAsaasOperationError(err, "FAILED_DELETE_PAYMENT", "Erro ao deletar cobrança"); } }, webhook: { async save(data) { try { return await request("/webhook", { method: "POST", body: JSON.stringify(data), }); } catch (err) { if (err instanceof errors_1.AsaasApiError) throw err; const message = err instanceof Error ? err.message : String(err); throw new errors_1.AsaasApiError(`Erro ao criar webhook: ${message}`, 500, ""); } }, async get() { try { return await request("/webhook"); } catch (err) { if (err instanceof errors_1.AsaasApiError) throw err; const message = err instanceof Error ? err.message : String(err); throw new errors_1.AsaasApiError(`Erro ao obter webhook: ${message}`, 500, ""); } }, }, }; }