@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
JavaScript
;
/**
* @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, "");
}
},
},
};
}