@pagosrd/node 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,268 @@
1
+ // src/client.ts
2
+ import { randomUUID } from "crypto";
3
+ var DEFAULT_BASE_URL = "https://api.pagosrd.com";
4
+ var PagosRDError = class extends Error {
5
+ constructor(message, status, code, field, requestId, raw) {
6
+ super(message);
7
+ this.status = status;
8
+ this.code = code;
9
+ this.field = field;
10
+ this.requestId = requestId;
11
+ this.raw = raw;
12
+ }
13
+ status;
14
+ code;
15
+ field;
16
+ requestId;
17
+ raw;
18
+ name = "PagosRDError";
19
+ };
20
+ var PagosRDConnectionError = class extends Error {
21
+ name = "PagosRDConnectionError";
22
+ };
23
+ var PagosRD = class {
24
+ constructor(apiKey, options = {}) {
25
+ this.apiKey = apiKey;
26
+ if (!apiKey || !/^sk_(test|live)_/.test(apiKey)) {
27
+ throw new Error("Llave de API inv\xE1lida: debe empezar con sk_test_ o sk_live_.");
28
+ }
29
+ this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
30
+ this.maxRetries = options.maxRetries ?? 2;
31
+ this.timeoutMs = options.timeoutMs ?? 3e4;
32
+ this.fetchImpl = options.fetch ?? globalThis.fetch;
33
+ }
34
+ apiKey;
35
+ baseUrl;
36
+ maxRetries;
37
+ timeoutMs;
38
+ fetchImpl;
39
+ /** true si la llave es de modo de prueba. */
40
+ get testMode() {
41
+ return this.apiKey.startsWith("sk_test_");
42
+ }
43
+ // ---------------------------------------------------------------- recursos
44
+ paymentLinks = {
45
+ create: (params, opts) => this.post("/v1/payment_links", params, opts),
46
+ retrieve: (id) => this.get(`/v1/payment_links/${enc(id)}`),
47
+ list: (params = {}) => this.get("/v1/payment_links", params),
48
+ cancel: (id) => this.post(`/v1/payment_links/${enc(id)}/cancel`, void 0)
49
+ };
50
+ payments = {
51
+ /**
52
+ * Cobra con una tarjeta guardada, sin que el cliente vea ninguna página. La llave de
53
+ * idempotencia es obligatoria: usa algo propio de la operación (el id del pedido) para
54
+ * que reintentar nunca cobre dos veces. El pago vuelve succeeded, failed o processing.
55
+ */
56
+ create: (params, opts) => this.post("/v1/payments", params, opts),
57
+ retrieve: (id) => this.get(`/v1/payments/${enc(id)}`),
58
+ list: (params = {}) => this.get("/v1/payments", params)
59
+ };
60
+ paymentMethods = {
61
+ retrieve: (id) => this.get(`/v1/payment_methods/${enc(id)}`),
62
+ list: (params = {}) => this.get("/v1/payment_methods", params),
63
+ /** Deja de poder usarse de inmediato y se borra el token en el procesador. */
64
+ del: (id) => this.request("DELETE", `/v1/payment_methods/${enc(id)}`, void 0, randomUUID())
65
+ };
66
+ subscriptions = {
67
+ /**
68
+ * PagosRD cobra solo en cada fecha con la tarjeta guardada; reintenta los rechazos a 1, 3 y 5 días.
69
+ * La llave de idempotencia es obligatoria: usa el id del contrato para que reintentar no cree otra.
70
+ */
71
+ create: (params, opts) => this.post("/v1/subscriptions", params, opts),
72
+ retrieve: (id) => this.get(`/v1/subscriptions/${enc(id)}`),
73
+ list: (params = {}) => this.get("/v1/subscriptions", params),
74
+ update: (id, params) => this.request("PATCH", `/v1/subscriptions/${enc(id)}`, params),
75
+ /** Reintenta ya el cobro de una suscripción past_due o unpaid con la misma tarjeta. */
76
+ retry: (id) => this.post(`/v1/subscriptions/${enc(id)}/retry`, void 0),
77
+ cancel: (id) => this.post(`/v1/subscriptions/${enc(id)}/cancel`, void 0)
78
+ };
79
+ refunds = {
80
+ create: (paymentId, params = {}, opts) => this.post(`/v1/payments/${enc(paymentId)}/refunds`, params, opts),
81
+ list: (params = {}) => this.get("/v1/refunds", params)
82
+ };
83
+ merchants = {
84
+ create: (params) => this.post("/v1/merchants", params),
85
+ retrieve: (id) => this.get(`/v1/merchants/${enc(id)}`),
86
+ list: (params = {}) => this.get("/v1/merchants", params),
87
+ update: (id, params) => this.request("PATCH", `/v1/merchants/${enc(id)}`, params),
88
+ setProcessorConfig: (id, config) => this.request("PUT", `/v1/merchants/${enc(id)}/processor`, config),
89
+ balance: (id) => this.get(`/v1/merchants/${enc(id)}/balance`)
90
+ };
91
+ webhookEndpoints = {
92
+ create: (params) => this.post("/v1/webhook_endpoints", params),
93
+ list: () => this.get("/v1/webhook_endpoints"),
94
+ retrieve: (id) => this.get(`/v1/webhook_endpoints/${enc(id)}`),
95
+ update: (id, params) => this.request("PATCH", `/v1/webhook_endpoints/${enc(id)}`, params),
96
+ del: (id) => this.request("DELETE", `/v1/webhook_endpoints/${enc(id)}`),
97
+ rollSecret: (id) => this.post(`/v1/webhook_endpoints/${enc(id)}/roll_secret`, void 0),
98
+ deliveries: (params = {}) => this.get("/v1/webhook_deliveries", params)
99
+ };
100
+ events = {
101
+ retrieve: (id) => this.get(`/v1/events/${enc(id)}`),
102
+ list: (params = {}) => this.get("/v1/events", params),
103
+ resend: (id, endpointId) => this.post(`/v1/events/${enc(id)}/resend`, endpointId ? { endpoint_id: endpointId } : {})
104
+ };
105
+ summary(params = {}) {
106
+ return this.get("/v1/summary", params);
107
+ }
108
+ /**
109
+ * Recorre todas las páginas de una lista.
110
+ *
111
+ * for await (const pago of client.autoPaginate((p) => client.payments.list({ ...p, status: "succeeded" }))) { … }
112
+ */
113
+ async *autoPaginate(fetchPage, pageSize = 100) {
114
+ let startingAfter;
115
+ while (true) {
116
+ const page = await fetchPage({ limit: pageSize, starting_after: startingAfter });
117
+ for (const item of page.data) yield item;
118
+ if (!page.has_more || page.data.length === 0) return;
119
+ startingAfter = page.data[page.data.length - 1].id;
120
+ }
121
+ }
122
+ // ---------------------------------------------------------------- HTTP
123
+ get(path, query) {
124
+ return this.request("GET", path, void 0, void 0, query);
125
+ }
126
+ post(path, body, opts) {
127
+ return this.request("POST", path, body, opts?.idempotencyKey ?? randomUUID());
128
+ }
129
+ async request(method, path, body, idempotencyKey, query) {
130
+ const url = new URL(this.baseUrl + path);
131
+ for (const [k, v] of Object.entries(query ?? {})) if (v !== void 0 && v !== null) url.searchParams.set(k, String(v));
132
+ const headers = {
133
+ authorization: `Bearer ${this.apiKey}`,
134
+ "user-agent": "pagosrd-node/0.1.1",
135
+ accept: "application/json"
136
+ };
137
+ if (body !== void 0) headers["content-type"] = "application/json";
138
+ if (idempotencyKey) headers["idempotency-key"] = idempotencyKey;
139
+ const retryable = method === "GET" || Boolean(idempotencyKey);
140
+ for (let attempt = 0; ; attempt++) {
141
+ let res;
142
+ try {
143
+ res = await this.fetchImpl(url, {
144
+ method,
145
+ headers,
146
+ body: body === void 0 ? void 0 : JSON.stringify(body),
147
+ signal: AbortSignal.timeout(this.timeoutMs)
148
+ });
149
+ } catch (err) {
150
+ if (retryable && attempt < this.maxRetries) {
151
+ await sleep(backoff(attempt));
152
+ continue;
153
+ }
154
+ throw new PagosRDConnectionError(`No se pudo conectar con PagosRD: ${err.message}`);
155
+ }
156
+ if ((res.status === 429 || res.status >= 500) && retryable && attempt < this.maxRetries) {
157
+ const retryAfter = Number(res.headers.get("retry-after"));
158
+ await sleep(Number.isFinite(retryAfter) && retryAfter > 0 ? retryAfter * 1e3 : backoff(attempt));
159
+ continue;
160
+ }
161
+ const text = await res.text();
162
+ const data = text ? safeJson(text) : null;
163
+ if (!res.ok) {
164
+ const p = data ?? {};
165
+ throw new PagosRDError(p.detail ?? `Error HTTP ${res.status}`, res.status, p.code ?? "error_http", p.field, res.headers.get("x-request-id") ?? void 0, data);
166
+ }
167
+ return data;
168
+ }
169
+ }
170
+ };
171
+ var enc = encodeURIComponent;
172
+ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
173
+ var backoff = (attempt) => Math.min(8e3, 500 * 2 ** attempt) * (0.5 + Math.random() / 2);
174
+ function safeJson(text) {
175
+ try {
176
+ return JSON.parse(text);
177
+ } catch {
178
+ return { detail: text };
179
+ }
180
+ }
181
+
182
+ // src/internal/money.ts
183
+ var MoneyError = class extends Error {
184
+ name = "MoneyError";
185
+ };
186
+ function cents(value, field = "amount") {
187
+ if (!Number.isSafeInteger(value)) throw new MoneyError(`${field} debe ser un entero de centavos.`);
188
+ if (value < 0) throw new MoneyError(`${field} no puede ser negativo.`);
189
+ return value;
190
+ }
191
+ function toCents(amount) {
192
+ const text = typeof amount === "number" ? amount.toString() : amount;
193
+ const noSymbol = text.trim().replace(/^(RD\$|US\$|\$)\s*/i, "");
194
+ if (noSymbol.includes(",") && !/^\d{1,3}(,\d{3})+(\.\d{1,2})?$/.test(noSymbol)) {
195
+ throw new MoneyError(`Monto ambiguo: "${text}". Usa punto para los decimales y coma solo para los miles, por ejemplo "1,500.50".`);
196
+ }
197
+ const cleaned = noSymbol.replace(/,/g, "");
198
+ const match = /^(\d+)(?:\.(\d{1,2}))?$/.exec(cleaned);
199
+ if (!match) throw new MoneyError(`Monto inv\xE1lido: "${text}". Usa como m\xE1ximo 2 decimales.`);
200
+ const value = Number(match[1]) * 100 + Number((match[2] ?? "").padEnd(2, "0"));
201
+ return cents(value);
202
+ }
203
+ var formatters = /* @__PURE__ */ new Map();
204
+ function formatMoney(value, currency = "DOP") {
205
+ let f = formatters.get(currency);
206
+ if (!f) {
207
+ f = new Intl.NumberFormat("es-DO", { style: "currency", currency, minimumFractionDigits: 2 });
208
+ formatters.set(currency, f);
209
+ }
210
+ return f.format(value / 100);
211
+ }
212
+
213
+ // src/internal/crypto.ts
214
+ import { createHmac, randomBytes, timingSafeEqual } from "crypto";
215
+ function safeEqual(a, b) {
216
+ const ab = Buffer.from(a);
217
+ const bb = Buffer.from(b);
218
+ return ab.length === bb.length && timingSafeEqual(ab, bb);
219
+ }
220
+ function hmacSha256Hex(secret, data) {
221
+ return createHmac("sha256", secret).update(data).digest("hex");
222
+ }
223
+
224
+ // src/internal/webhooks.ts
225
+ var SIGNATURE_HEADER = "PagosRD-Signature";
226
+ var DEFAULT_TOLERANCE_SECONDS = 300;
227
+ var WebhookSignatureError = class extends Error {
228
+ name = "WebhookSignatureError";
229
+ };
230
+ function verifyWebhook(secret, body, header, options = {}) {
231
+ if (!header) throw new WebhookSignatureError(`Falta la cabecera ${SIGNATURE_HEADER}.`);
232
+ const parts = Object.fromEntries(
233
+ header.split(",").map((p) => {
234
+ const i = p.indexOf("=");
235
+ return [p.slice(0, i).trim(), p.slice(i + 1).trim()];
236
+ })
237
+ );
238
+ const t = Number(parts.t);
239
+ const v1 = parts.v1;
240
+ if (!Number.isInteger(t) || !v1) throw new WebhookSignatureError("Cabecera de firma con formato inv\xE1lido.");
241
+ const now = options.now ?? Math.floor(Date.now() / 1e3);
242
+ const tolerance = options.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;
243
+ if (Math.abs(now - t) > tolerance) {
244
+ throw new WebhookSignatureError("La firma es demasiado vieja o del futuro (posible reenv\xEDo malicioso).");
245
+ }
246
+ if (!safeEqual(hmacSha256Hex(secret, `${t}.${body}`), v1)) {
247
+ throw new WebhookSignatureError("La firma no coincide.");
248
+ }
249
+ }
250
+
251
+ // src/webhooks.ts
252
+ function constructEvent(rawBody, signatureHeader, secret, toleranceSeconds = 300) {
253
+ const body = typeof rawBody === "string" ? rawBody : new TextDecoder().decode(rawBody);
254
+ verifyWebhook(secret, body, signatureHeader, { toleranceSeconds });
255
+ return JSON.parse(body);
256
+ }
257
+ export {
258
+ DEFAULT_BASE_URL,
259
+ PagosRD,
260
+ PagosRDConnectionError,
261
+ PagosRDError,
262
+ SIGNATURE_HEADER,
263
+ WebhookSignatureError,
264
+ constructEvent,
265
+ formatMoney,
266
+ toCents
267
+ };
268
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts","../src/internal/money.ts","../src/internal/crypto.ts","../src/internal/webhooks.ts","../src/webhooks.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport type {\n AzulConfig,\n CardnetConfig,\n CreateMerchantParams,\n CustomerInfoParams,\n CreatePaymentLinkParams,\n CreatePaymentParams,\n CreateRefundParams,\n CreateSubscriptionParams,\n Event,\n List,\n Merchant,\n PageParams,\n Payment,\n PaymentLink,\n PaymentMethod,\n PaymentStatus,\n Refund,\n RequestOptions,\n Subscription,\n SubscriptionStatus,\n Summary,\n UpdateSubscriptionParams,\n WebhookDelivery,\n WebhookEndpoint,\n} from \"./types.js\";\n\nexport const DEFAULT_BASE_URL = \"https://api.pagosrd.com\";\n\n/** Error devuelto por la API (Problem Details). */\nexport class PagosRDError extends Error {\n override readonly name = \"PagosRDError\";\n constructor(\n message: string,\n readonly status: number,\n readonly code: string,\n readonly field: string | undefined,\n readonly requestId: string | undefined,\n readonly raw: unknown,\n ) {\n super(message);\n }\n}\n\n/** No se pudo hablar con la API (red caída, timeout). */\nexport class PagosRDConnectionError extends Error {\n override readonly name = \"PagosRDConnectionError\";\n}\n\nexport interface PagosRDOptions {\n /** Por defecto https://api.pagosrd.com */\n baseUrl?: string;\n /** Reintentos ante fallas de red, 429 y 5xx. Por defecto 2. Los POST llevan llave de idempotencia, así que reintentar es seguro. */\n maxRetries?: number;\n /** Tiempo máximo por solicitud en ms. Por defecto 30000. */\n timeoutMs?: number;\n fetch?: typeof fetch;\n}\n\ntype Query = object;\n\nexport class PagosRD {\n private readonly baseUrl: string;\n private readonly maxRetries: number;\n private readonly timeoutMs: number;\n private readonly fetchImpl: typeof fetch;\n\n constructor(\n private readonly apiKey: string,\n options: PagosRDOptions = {},\n ) {\n if (!apiKey || !/^sk_(test|live)_/.test(apiKey)) {\n throw new Error(\"Llave de API inválida: debe empezar con sk_test_ o sk_live_.\");\n }\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, \"\");\n this.maxRetries = options.maxRetries ?? 2;\n this.timeoutMs = options.timeoutMs ?? 30_000;\n this.fetchImpl = options.fetch ?? globalThis.fetch;\n }\n\n /** true si la llave es de modo de prueba. */\n get testMode(): boolean {\n return this.apiKey.startsWith(\"sk_test_\");\n }\n\n // ---------------------------------------------------------------- recursos\n\n readonly paymentLinks = {\n create: (params: CreatePaymentLinkParams, opts?: RequestOptions) => this.post<PaymentLink>(\"/v1/payment_links\", params, opts),\n retrieve: (id: string) => this.get<PaymentLink>(`/v1/payment_links/${enc(id)}`),\n list: (params: PageParams & { status?: PaymentLink[\"status\"]; merchant_id?: string; reference?: string } = {}) =>\n this.get<List<PaymentLink>>(\"/v1/payment_links\", params),\n cancel: (id: string) => this.post<PaymentLink>(`/v1/payment_links/${enc(id)}/cancel`, undefined),\n };\n\n readonly payments = {\n /**\n * Cobra con una tarjeta guardada, sin que el cliente vea ninguna página. La llave de\n * idempotencia es obligatoria: usa algo propio de la operación (el id del pedido) para\n * que reintentar nunca cobre dos veces. El pago vuelve succeeded, failed o processing.\n */\n create: (params: CreatePaymentParams, opts: RequestOptions & { idempotencyKey: string }) => this.post<Payment>(\"/v1/payments\", params, opts),\n retrieve: (id: string) => this.get<Payment>(`/v1/payments/${enc(id)}`),\n list: (\n params: PageParams & {\n status?: PaymentStatus;\n merchant_id?: string;\n payment_link_id?: string;\n payment_method_id?: string;\n subscription_id?: string;\n created_gte?: string;\n created_lte?: string;\n } = {},\n ) => this.get<List<Payment>>(\"/v1/payments\", params),\n };\n\n readonly paymentMethods = {\n retrieve: (id: string) => this.get<PaymentMethod>(`/v1/payment_methods/${enc(id)}`),\n list: (params: PageParams & { merchant_id?: string; customer_reference?: string; status?: PaymentMethod[\"status\"] } = {}) =>\n this.get<List<PaymentMethod>>(\"/v1/payment_methods\", params),\n /** Deja de poder usarse de inmediato y se borra el token en el procesador. */\n del: (id: string) => this.request<PaymentMethod>(\"DELETE\", `/v1/payment_methods/${enc(id)}`, undefined, randomUUID()),\n };\n\n readonly subscriptions = {\n /**\n * PagosRD cobra solo en cada fecha con la tarjeta guardada; reintenta los rechazos a 1, 3 y 5 días.\n * La llave de idempotencia es obligatoria: usa el id del contrato para que reintentar no cree otra.\n */\n create: (params: CreateSubscriptionParams, opts: RequestOptions & { idempotencyKey: string }) => this.post<Subscription>(\"/v1/subscriptions\", params, opts),\n retrieve: (id: string) => this.get<Subscription>(`/v1/subscriptions/${enc(id)}`),\n list: (params: PageParams & { status?: SubscriptionStatus; merchant_id?: string; customer_reference?: string; payment_method_id?: string } = {}) =>\n this.get<List<Subscription>>(\"/v1/subscriptions\", params),\n update: (id: string, params: UpdateSubscriptionParams) => this.request<Subscription>(\"PATCH\", `/v1/subscriptions/${enc(id)}`, params),\n /** Reintenta ya el cobro de una suscripción past_due o unpaid con la misma tarjeta. */\n retry: (id: string) => this.post<Subscription>(`/v1/subscriptions/${enc(id)}/retry`, undefined),\n cancel: (id: string) => this.post<Subscription>(`/v1/subscriptions/${enc(id)}/cancel`, undefined),\n };\n\n readonly refunds = {\n create: (paymentId: string, params: CreateRefundParams = {}, opts?: RequestOptions) => this.post<Refund>(`/v1/payments/${enc(paymentId)}/refunds`, params, opts),\n list: (params: PageParams & { payment_id?: string } = {}) => this.get<List<Refund>>(\"/v1/refunds\", params),\n };\n\n readonly merchants = {\n create: (params: CreateMerchantParams) => this.post<Merchant>(\"/v1/merchants\", params),\n retrieve: (id: string) => this.get<Merchant>(`/v1/merchants/${enc(id)}`),\n list: (params: PageParams = {}) => this.get<List<Merchant>>(\"/v1/merchants\", params),\n update: (id: string, params: { name?: string; rnc?: string | null; status?: \"active\" | \"disabled\"; metadata?: Record<string, string>; customer_info?: CustomerInfoParams }) =>\n this.request<Merchant>(\"PATCH\", `/v1/merchants/${enc(id)}`, params),\n setProcessorConfig: (id: string, config: AzulConfig | CardnetConfig) => this.request<Merchant>(\"PUT\", `/v1/merchants/${enc(id)}/processor`, config),\n balance: (id: string) => this.get<{ object: \"balance\"; merchant_id: string; processor_receivable: number }>(`/v1/merchants/${enc(id)}/balance`),\n };\n\n readonly webhookEndpoints = {\n create: (params: { url: string; events?: string[]; description?: string | null }) => this.post<WebhookEndpoint & { secret: string }>(\"/v1/webhook_endpoints\", params),\n list: () => this.get<List<WebhookEndpoint>>(\"/v1/webhook_endpoints\"),\n retrieve: (id: string) => this.get<WebhookEndpoint>(`/v1/webhook_endpoints/${enc(id)}`),\n update: (id: string, params: { url?: string; events?: string[]; description?: string | null; enabled?: boolean }) =>\n this.request<WebhookEndpoint>(\"PATCH\", `/v1/webhook_endpoints/${enc(id)}`, params),\n del: (id: string) => this.request<{ id: string; deleted: true }>(\"DELETE\", `/v1/webhook_endpoints/${enc(id)}`),\n rollSecret: (id: string) => this.post<{ secret: string }>(`/v1/webhook_endpoints/${enc(id)}/roll_secret`, undefined),\n deliveries: (params: PageParams & { endpoint_id?: string; event_id?: string; status?: WebhookDelivery[\"status\"] } = {}) =>\n this.get<List<WebhookDelivery>>(\"/v1/webhook_deliveries\", params),\n };\n\n readonly events = {\n retrieve: (id: string) => this.get<Event>(`/v1/events/${enc(id)}`),\n list: (params: PageParams & { type?: string } = {}) => this.get<List<Event>>(\"/v1/events\", params),\n resend: (id: string, endpointId?: string) => this.post<List<WebhookDelivery>>(`/v1/events/${enc(id)}/resend`, endpointId ? { endpoint_id: endpointId } : {}),\n };\n\n summary(params: { from?: string; to?: string } = {}) {\n return this.get<Summary>(\"/v1/summary\", params);\n }\n\n /**\n * Recorre todas las páginas de una lista.\n *\n * for await (const pago of client.autoPaginate((p) => client.payments.list({ ...p, status: \"succeeded\" }))) { … }\n */\n async *autoPaginate<T extends { id: string }>(fetchPage: (page: PageParams) => Promise<List<T>>, pageSize = 100): AsyncGenerator<T> {\n let startingAfter: string | undefined;\n while (true) {\n const page = await fetchPage({ limit: pageSize, starting_after: startingAfter });\n for (const item of page.data) yield item;\n if (!page.has_more || page.data.length === 0) return;\n startingAfter = page.data[page.data.length - 1]!.id;\n }\n }\n\n // ---------------------------------------------------------------- HTTP\n\n private get<T>(path: string, query?: Query) {\n return this.request<T>(\"GET\", path, undefined, undefined, query);\n }\n\n private post<T>(path: string, body: unknown, opts?: RequestOptions) {\n return this.request<T>(\"POST\", path, body, opts?.idempotencyKey ?? randomUUID());\n }\n\n private async request<T>(method: string, path: string, body?: unknown, idempotencyKey?: string, query?: Query): Promise<T> {\n const url = new URL(this.baseUrl + path);\n for (const [k, v] of Object.entries(query ?? {}) as [string, unknown][]) if (v !== undefined && v !== null) url.searchParams.set(k, String(v));\n const headers: Record<string, string> = {\n authorization: `Bearer ${this.apiKey}`,\n \"user-agent\": \"pagosrd-node/0.1.1\",\n accept: \"application/json\",\n };\n if (body !== undefined) headers[\"content-type\"] = \"application/json\";\n if (idempotencyKey) headers[\"idempotency-key\"] = idempotencyKey;\n // Solo se reintenta lo que es seguro: GET, o POST con llave de idempotencia.\n const retryable = method === \"GET\" || Boolean(idempotencyKey);\n\n for (let attempt = 0; ; attempt++) {\n let res: Response;\n try {\n res = await this.fetchImpl(url, {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n signal: AbortSignal.timeout(this.timeoutMs),\n });\n } catch (err) {\n if (retryable && attempt < this.maxRetries) {\n await sleep(backoff(attempt));\n continue;\n }\n throw new PagosRDConnectionError(`No se pudo conectar con PagosRD: ${(err as Error).message}`);\n }\n if ((res.status === 429 || res.status >= 500) && retryable && attempt < this.maxRetries) {\n const retryAfter = Number(res.headers.get(\"retry-after\"));\n await sleep(Number.isFinite(retryAfter) && retryAfter > 0 ? retryAfter * 1000 : backoff(attempt));\n continue;\n }\n const text = await res.text();\n const data: unknown = text ? safeJson(text) : null;\n if (!res.ok) {\n const p = (data ?? {}) as { detail?: string; code?: string; field?: string };\n throw new PagosRDError(p.detail ?? `Error HTTP ${res.status}`, res.status, p.code ?? \"error_http\", p.field, res.headers.get(\"x-request-id\") ?? undefined, data);\n }\n return data as T;\n }\n }\n}\n\nconst enc = encodeURIComponent;\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\nconst backoff = (attempt: number) => Math.min(8_000, 500 * 2 ** attempt) * (0.5 + Math.random() / 2);\nfunction safeJson(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return { detail: text };\n }\n}\n","/** Monto en centavos enteros. Solo se crea con `cents()`, que valida. */\nexport type Cents = number & { readonly __brand: \"Cents\" };\n\nexport const CURRENCIES = [\"DOP\", \"USD\"] as const;\nexport type Currency = (typeof CURRENCIES)[number];\n\nexport class MoneyError extends Error {\n override readonly name = \"MoneyError\";\n}\n\n/** Valida y marca un entero de centavos. */\nexport function cents(value: number, field = \"amount\"): Cents {\n if (!Number.isSafeInteger(value)) throw new MoneyError(`${field} debe ser un entero de centavos.`);\n if (value < 0) throw new MoneyError(`${field} no puede ser negativo.`);\n return value as Cents;\n}\n\n/** Convierte \"1,500.50\" o \"RD$1,500.50\" a 150050 sin pasar por punto flotante. */\nexport function toCents(amount: string | number): Cents {\n const text = typeof amount === \"number\" ? amount.toString() : amount;\n const noSymbol = text.trim().replace(/^(RD\\$|US\\$|\\$)\\s*/i, \"\");\n // La coma SOLO se acepta como separador de miles bien formado (\"1,500.50\").\n // \"1500,50\" o \"4,5\" se rechazan: podrían ser decimales y multiplicar el monto por 100.\n if (noSymbol.includes(\",\") && !/^\\d{1,3}(,\\d{3})+(\\.\\d{1,2})?$/.test(noSymbol)) {\n throw new MoneyError(`Monto ambiguo: \"${text}\". Usa punto para los decimales y coma solo para los miles, por ejemplo \"1,500.50\".`);\n }\n const cleaned = noSymbol.replace(/,/g, \"\");\n const match = /^(\\d+)(?:\\.(\\d{1,2}))?$/.exec(cleaned);\n if (!match) throw new MoneyError(`Monto inválido: \"${text}\". Usa como máximo 2 decimales.`);\n const value = Number(match[1]) * 100 + Number((match[2] ?? \"\").padEnd(2, \"0\"));\n return cents(value);\n}\n\nconst formatters = new Map<Currency, Intl.NumberFormat>();\n\n/** 150050 -> \"RD$1,500.50\" */\nexport function formatMoney(value: number, currency: Currency = \"DOP\"): string {\n let f = formatters.get(currency);\n if (!f) {\n f = new Intl.NumberFormat(\"es-DO\", { style: \"currency\", currency, minimumFractionDigits: 2 });\n formatters.set(currency, f);\n }\n return f.format(value / 100);\n}\n","// Copia de las primitivas de @pagosrd/core que usa la firma de webhooks.\nimport { createHmac, randomBytes, timingSafeEqual } from \"node:crypto\";\n\nconst BASE62 = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\n\nexport function randomToken(length: number): string {\n const bytes = randomBytes(length);\n let out = \"\";\n for (let i = 0; i < length; i++) out += BASE62[bytes[i]! % 62];\n return out;\n}\n\nexport function safeEqual(a: string, b: string): boolean {\n const ab = Buffer.from(a);\n const bb = Buffer.from(b);\n return ab.length === bb.length && timingSafeEqual(ab, bb);\n}\n\nexport function hmacSha256Hex(secret: string, data: string): string {\n return createHmac(\"sha256\", secret).update(data).digest(\"hex\");\n}\n","import { hmacSha256Hex, randomToken, safeEqual } from \"./crypto.js\";\n\n/**\n * Firma de los webhooks que PagosRD envía a las plataformas.\n *\n * Cabecera: `PagosRD-Signature: t=<unix>,v1=<hex>`\n * donde v1 = HMAC-SHA256(secreto, `${t}.${cuerpo}`).\n */\n\nexport const SIGNATURE_HEADER = \"PagosRD-Signature\";\nexport const DEFAULT_TOLERANCE_SECONDS = 300;\n\nexport class WebhookSignatureError extends Error {\n override readonly name = \"WebhookSignatureError\";\n}\n\nexport function generateWebhookSecret(): string {\n return `whsec_${randomToken(32)}`;\n}\n\nexport function signWebhook(secret: string, body: string, timestamp = Math.floor(Date.now() / 1000)): string {\n return `t=${timestamp},v1=${hmacSha256Hex(secret, `${timestamp}.${body}`)}`;\n}\n\n/**\n * Verifica la firma de un webhook. Lanza WebhookSignatureError si no es válida.\n * Usa el cuerpo CRUDO tal como llegó (no el JSON re-serializado).\n */\nexport function verifyWebhook(\n secret: string,\n body: string,\n header: string | null | undefined,\n options: { toleranceSeconds?: number; now?: number } = {},\n): void {\n if (!header) throw new WebhookSignatureError(`Falta la cabecera ${SIGNATURE_HEADER}.`);\n const parts = Object.fromEntries(\n header.split(\",\").map((p) => {\n const i = p.indexOf(\"=\");\n return [p.slice(0, i).trim(), p.slice(i + 1).trim()];\n }),\n );\n const t = Number(parts.t);\n const v1 = parts.v1;\n if (!Number.isInteger(t) || !v1) throw new WebhookSignatureError(\"Cabecera de firma con formato inválido.\");\n const now = options.now ?? Math.floor(Date.now() / 1000);\n const tolerance = options.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;\n if (Math.abs(now - t) > tolerance) {\n throw new WebhookSignatureError(\"La firma es demasiado vieja o del futuro (posible reenvío malicioso).\");\n }\n if (!safeEqual(hmacSha256Hex(secret, `${t}.${body}`), v1)) {\n throw new WebhookSignatureError(\"La firma no coincide.\");\n }\n}\n","import { SIGNATURE_HEADER, verifyWebhook, WebhookSignatureError } from \"./internal/index.js\";\nimport type { Event } from \"./types.js\";\n\nexport { SIGNATURE_HEADER, WebhookSignatureError };\n\n/**\n * Verifica la firma de un webhook y devuelve el evento tipado.\n *\n * IMPORTANTE: pasa el cuerpo CRUDO (string o Buffer), tal como llegó. Si tu\n * framework ya lo convirtió a JSON, la firma no va a coincidir.\n *\n * ```ts\n * app.post(\"/webhooks/pagosrd\", express.raw({ type: \"application/json\" }), (req, res) => {\n * const event = constructEvent(req.body, req.header(\"PagosRD-Signature\"), process.env.PAGOSRD_WEBHOOK_SECRET!);\n * if (event.type === \"payment.succeeded\") marcarCuotaPagada(event.data.payment_link_reference);\n * res.sendStatus(200);\n * });\n * ```\n */\nexport function constructEvent(rawBody: string | Uint8Array, signatureHeader: string | null | undefined, secret: string, toleranceSeconds = 300): Event {\n const body = typeof rawBody === \"string\" ? rawBody : new TextDecoder().decode(rawBody);\n verifyWebhook(secret, body, signatureHeader, { toleranceSeconds });\n return JSON.parse(body) as Event;\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,63 @@
1
1
  {
2
2
  "name": "@pagosrd/node",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "Cliente oficial de PagosRD para Node.js: links de cobro, pagos, reembolsos y verificación de webhooks.",
5
+ "license": "MIT",
6
+ "author": "Cristian Encarnación",
7
+ "homepage": "https://github.com/cristianEncarnacion/pagosrd-node#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/cristianEncarnacion/pagosrd-node.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/cristianEncarnacion/pagosrd-node/issues"
14
+ },
15
+ "keywords": [
16
+ "pagosrd",
17
+ "pagos",
18
+ "republica-dominicana",
19
+ "azul",
20
+ "cardnet",
21
+ "links-de-cobro",
22
+ "webhooks",
23
+ "suscripciones"
24
+ ],
25
+ "type": "module",
26
+ "main": "./dist/index.cjs",
27
+ "module": "./dist/index.js",
28
+ "types": "./dist/index.d.ts",
29
+ "exports": {
30
+ ".": {
31
+ "import": {
32
+ "types": "./dist/index.d.ts",
33
+ "default": "./dist/index.js"
34
+ },
35
+ "require": {
36
+ "types": "./dist/index.d.cts",
37
+ "default": "./dist/index.cjs"
38
+ }
39
+ }
40
+ },
41
+ "files": [
42
+ "dist",
43
+ "README.md",
44
+ "CHANGELOG.md",
45
+ "LICENSE"
46
+ ],
47
+ "engines": {
48
+ "node": ">=18"
49
+ },
50
+ "scripts": {
51
+ "build": "tsup",
52
+ "test": "vitest run",
53
+ "typecheck": "tsc --noEmit",
54
+ "prepublishOnly": "npm run build && npm test"
55
+ },
56
+ "sideEffects": false,
57
+ "devDependencies": {
58
+ "@types/node": "^22.20.5",
59
+ "tsup": "^8.5.1",
60
+ "typescript": "^5.9.3",
61
+ "vitest": "^5.0.5"
62
+ }
63
+ }