@pagosrd/node 0.1.0 → 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/CHANGELOG.md +10 -0
- package/dist/index.cjs +4 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -3
- package/dist/index.d.ts +32 -3
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/package.json +31 -10
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Cambios
|
|
2
|
+
|
|
3
|
+
## 0.1.1 (13-nov-2026)
|
|
4
|
+
|
|
5
|
+
- Tipos nuevos: `Merchant.saved_cards_enabled`, `Merchant.processor_account_shared_with` y `Refund.recorded_manually` (reembolso hecho en el portal del procesador y registrado en PagosRD).
|
|
6
|
+
- `package.json` enlaza el repositorio público.
|
|
7
|
+
|
|
8
|
+
## 0.1.0 (12-nov-2026)
|
|
9
|
+
|
|
10
|
+
- Primera versión: links de cobro, pagos, reembolsos, tarjetas guardadas, suscripciones, eventos y verificación de webhooks.
|
package/dist/index.cjs
CHANGED
|
@@ -165,7 +165,7 @@ var PagosRD = class {
|
|
|
165
165
|
for (const [k, v] of Object.entries(query ?? {})) if (v !== void 0 && v !== null) url.searchParams.set(k, String(v));
|
|
166
166
|
const headers = {
|
|
167
167
|
authorization: `Bearer ${this.apiKey}`,
|
|
168
|
-
"user-agent": "pagosrd-node/0.1.
|
|
168
|
+
"user-agent": "pagosrd-node/0.1.1",
|
|
169
169
|
accept: "application/json"
|
|
170
170
|
};
|
|
171
171
|
if (body !== void 0) headers["content-type"] = "application/json";
|
|
@@ -213,7 +213,7 @@ function safeJson(text) {
|
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
//
|
|
216
|
+
// src/internal/money.ts
|
|
217
217
|
var MoneyError = class extends Error {
|
|
218
218
|
name = "MoneyError";
|
|
219
219
|
};
|
|
@@ -244,7 +244,7 @@ function formatMoney(value, currency = "DOP") {
|
|
|
244
244
|
return f.format(value / 100);
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
-
//
|
|
247
|
+
// src/internal/crypto.ts
|
|
248
248
|
var import_node_crypto2 = require("crypto");
|
|
249
249
|
function safeEqual(a, b) {
|
|
250
250
|
const ab = Buffer.from(a);
|
|
@@ -255,7 +255,7 @@ function hmacSha256Hex(secret, data) {
|
|
|
255
255
|
return (0, import_node_crypto2.createHmac)("sha256", secret).update(data).digest("hex");
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
//
|
|
258
|
+
// src/internal/webhooks.ts
|
|
259
259
|
var SIGNATURE_HEADER = "PagosRD-Signature";
|
|
260
260
|
var DEFAULT_TOLERANCE_SECONDS = 300;
|
|
261
261
|
var WebhookSignatureError = class extends Error {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/client.ts","../../core/src/money.ts","../../core/src/crypto.ts","../../core/src/webhooks.ts","../src/webhooks.ts"],"sourcesContent":["export { PagosRD, PagosRDError, PagosRDConnectionError, DEFAULT_BASE_URL } from \"./client.js\";\nexport type { PagosRDOptions } from \"./client.js\";\nexport { constructEvent, SIGNATURE_HEADER, WebhookSignatureError } from \"./webhooks.js\";\nexport { toCents, formatMoney } from \"@pagosrd/core\";\nexport type * from \"./types.js\";\n","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.0\",\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","import { createCipheriv, createDecipheriv, createHash, createHmac, hkdfSync, randomBytes, timingSafeEqual } from \"node:crypto\";\n\n/**\n * Cifrado de secretos en reposo (credenciales de AZUL, secretos de webhooks).\n *\n * AES-256-GCM con una llave derivada (HKDF-SHA256) de la llave maestra y de un\n * \"contexto\" (por ejemplo el id del comercio). Así cada comercio tiene su propia\n * llave efectiva y un texto cifrado no se puede mover a otro registro.\n *\n * Formato: \"v1:<iv b64>:<tag b64>:<cifrado b64>\". La versión permite rotar la\n * llave maestra en el futuro.\n */\n\nexport class CryptoError extends Error {\n override readonly name = \"CryptoError\";\n}\n\nexport function parseMasterKey(base64: string): Buffer {\n const key = Buffer.from(base64, \"base64\");\n if (key.length !== 32) {\n throw new CryptoError(\"La llave maestra debe ser de 32 bytes en base64. Genera una con: openssl rand -base64 32\");\n }\n return key;\n}\n\nfunction deriveKey(masterKey: Buffer, context: string): Buffer {\n return Buffer.from(hkdfSync(\"sha256\", masterKey, Buffer.alloc(0), `pagosrd:${context}`, 32));\n}\n\nexport function encryptSecret(masterKey: Buffer, context: string, plaintext: string): string {\n const iv = randomBytes(12);\n const cipher = createCipheriv(\"aes-256-gcm\", deriveKey(masterKey, context), iv);\n cipher.setAAD(Buffer.from(context));\n const encrypted = Buffer.concat([cipher.update(plaintext, \"utf8\"), cipher.final()]);\n return `v1:${iv.toString(\"base64\")}:${cipher.getAuthTag().toString(\"base64\")}:${encrypted.toString(\"base64\")}`;\n}\n\nexport function decryptSecret(masterKey: Buffer, context: string, payload: string): string {\n const [version, iv, tag, data] = payload.split(\":\");\n if (version !== \"v1\" || !iv || !tag || !data) throw new CryptoError(\"Formato de secreto cifrado desconocido.\");\n try {\n const decipher = createDecipheriv(\"aes-256-gcm\", deriveKey(masterKey, context), Buffer.from(iv, \"base64\"));\n decipher.setAAD(Buffer.from(context));\n decipher.setAuthTag(Buffer.from(tag, \"base64\"));\n return Buffer.concat([decipher.update(Buffer.from(data, \"base64\")), decipher.final()]).toString(\"utf8\");\n } catch (err) {\n throw new CryptoError(\"No se pudo descifrar el secreto: llave maestra o contexto incorrectos.\", { cause: err });\n }\n}\n\nexport function encryptJson(masterKey: Buffer, context: string, value: unknown): string {\n return encryptSecret(masterKey, context, JSON.stringify(value));\n}\n\nexport function decryptJson<T = unknown>(masterKey: Buffer, context: string, payload: string): T {\n return JSON.parse(decryptSecret(masterKey, context, payload)) as T;\n}\n\n// ------------------------------------------------------------------ llaves de API\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 type ApiKeyMode = \"test\" | \"live\";\n\n/** Genera una llave de API. El valor completo solo se muestra una vez. */\nexport function generateApiKey(mode: ApiKeyMode): { key: string; prefix: string; hash: string } {\n const key = `sk_${mode}_${randomToken(32)}`;\n return { key, prefix: key.slice(0, 12), hash: hashApiKey(key) };\n}\n\nexport function hashApiKey(key: string): string {\n return createHash(\"sha256\").update(key).digest(\"hex\");\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 \"@pagosrd/core\";\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":["import_node_crypto"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/internal/money.ts","../src/internal/crypto.ts","../src/internal/webhooks.ts","../src/webhooks.ts"],"sourcesContent":["export { PagosRD, PagosRDError, PagosRDConnectionError, DEFAULT_BASE_URL } from \"./client.js\";\nexport type { PagosRDOptions } from \"./client.js\";\nexport { constructEvent, SIGNATURE_HEADER, WebhookSignatureError } from \"./webhooks.js\";\nexport { toCents, formatMoney } from \"./internal/index.js\";\nexport type * from \"./types.js\";\n","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":["import_node_crypto"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export { SIGNATURE_HEADER, WebhookSignatureError, formatMoney, toCents } from '@pagosrd/core';
|
|
2
|
-
|
|
3
1
|
/** Tipos de los objetos de la API de PagosRD. Montos en centavos. */
|
|
4
2
|
type Livemode = boolean;
|
|
5
3
|
interface List<T> {
|
|
@@ -32,11 +30,18 @@ interface Merchant {
|
|
|
32
30
|
processor: "simulador" | "azul" | "cardnet";
|
|
33
31
|
processor_configured: boolean;
|
|
34
32
|
processor_summary: Record<string, string>;
|
|
33
|
+
/** true si los links de este comercio pueden guardar la tarjeta para cobros automáticos y suscripciones. */
|
|
34
|
+
saved_cards_enabled: boolean;
|
|
35
35
|
status: "active" | "disabled";
|
|
36
36
|
metadata: Record<string, string>;
|
|
37
37
|
customer_info: CustomerInfo;
|
|
38
38
|
/** true si ya tiene contacto, dirección y políticas: lo necesario para cobrar en vivo. */
|
|
39
39
|
customer_info_complete: boolean;
|
|
40
|
+
/** Otros comercios de la plataforma que cobran en vivo con la misma cuenta del procesador. Solo al consultar, crear o modificar un comercio. */
|
|
41
|
+
processor_account_shared_with?: {
|
|
42
|
+
id: string;
|
|
43
|
+
name: string;
|
|
44
|
+
}[];
|
|
40
45
|
created_at: string;
|
|
41
46
|
updated_at: string;
|
|
42
47
|
}
|
|
@@ -227,6 +232,8 @@ interface Refund {
|
|
|
227
232
|
reason: string | null;
|
|
228
233
|
status: "pending" | "succeeded" | "failed";
|
|
229
234
|
failure_message: string | null;
|
|
235
|
+
/** true si el reembolso se hizo en el portal del procesador y aquí solo se registró. */
|
|
236
|
+
recorded_manually: boolean;
|
|
230
237
|
created_at: string;
|
|
231
238
|
}
|
|
232
239
|
type EventType = "payment.succeeded" | "payment.failed" | "payment.requires_review" | "payment_link.paid" | "payment_link.expired" | "payment_link.canceled" | "refund.succeeded" | "refund.failed" | "payment_method.created" | "payment_method.deleted" | "subscription.created" | "subscription.updated" | "subscription.past_due" | "subscription.period_unpaid" | "subscription.unpaid" | "subscription.canceled" | "subscription.completed";
|
|
@@ -539,6 +546,28 @@ declare class PagosRD {
|
|
|
539
546
|
private request;
|
|
540
547
|
}
|
|
541
548
|
|
|
549
|
+
/** Monto en centavos enteros. Solo se crea con `cents()`, que valida. */
|
|
550
|
+
type Cents = number & {
|
|
551
|
+
readonly __brand: "Cents";
|
|
552
|
+
};
|
|
553
|
+
declare const CURRENCIES: readonly ["DOP", "USD"];
|
|
554
|
+
type Currency = (typeof CURRENCIES)[number];
|
|
555
|
+
/** Convierte "1,500.50" o "RD$1,500.50" a 150050 sin pasar por punto flotante. */
|
|
556
|
+
declare function toCents(amount: string | number): Cents;
|
|
557
|
+
/** 150050 -> "RD$1,500.50" */
|
|
558
|
+
declare function formatMoney(value: number, currency?: Currency): string;
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Firma de los webhooks que PagosRD envía a las plataformas.
|
|
562
|
+
*
|
|
563
|
+
* Cabecera: `PagosRD-Signature: t=<unix>,v1=<hex>`
|
|
564
|
+
* donde v1 = HMAC-SHA256(secreto, `${t}.${cuerpo}`).
|
|
565
|
+
*/
|
|
566
|
+
declare const SIGNATURE_HEADER = "PagosRD-Signature";
|
|
567
|
+
declare class WebhookSignatureError extends Error {
|
|
568
|
+
readonly name = "WebhookSignatureError";
|
|
569
|
+
}
|
|
570
|
+
|
|
542
571
|
/**
|
|
543
572
|
* Verifica la firma de un webhook y devuelve el evento tipado.
|
|
544
573
|
*
|
|
@@ -555,4 +584,4 @@ declare class PagosRD {
|
|
|
555
584
|
*/
|
|
556
585
|
declare function constructEvent(rawBody: string | Uint8Array, signatureHeader: string | null | undefined, secret: string, toleranceSeconds?: number): Event;
|
|
557
586
|
|
|
558
|
-
export { type AzulConfig, type CardnetConfig, type CreateMerchantParams, type CreatePaymentLinkParams, type CreatePaymentParams, type CreateRefundParams, type CreateSubscriptionParams, type CustomerInfo, type CustomerInfoParams, DEFAULT_BASE_URL, type Event, type EventType, type List, type Livemode, type Merchant, type PageParams, PagosRD, PagosRDConnectionError, PagosRDError, type PagosRDOptions, type Payment, type PaymentLink, type PaymentMethod, type PaymentStatus, type RecurringType, type Refund, type RequestOptions, type Subscription, type SubscriptionStatus, type Summary, type UpdateSubscriptionParams, type WebhookDelivery, type WebhookEndpoint, constructEvent };
|
|
587
|
+
export { type AzulConfig, type CardnetConfig, type CreateMerchantParams, type CreatePaymentLinkParams, type CreatePaymentParams, type CreateRefundParams, type CreateSubscriptionParams, type CustomerInfo, type CustomerInfoParams, DEFAULT_BASE_URL, type Event, type EventType, type List, type Livemode, type Merchant, type PageParams, PagosRD, PagosRDConnectionError, PagosRDError, type PagosRDOptions, type Payment, type PaymentLink, type PaymentMethod, type PaymentStatus, type RecurringType, type Refund, type RequestOptions, SIGNATURE_HEADER, type Subscription, type SubscriptionStatus, type Summary, type UpdateSubscriptionParams, type WebhookDelivery, type WebhookEndpoint, WebhookSignatureError, constructEvent, formatMoney, toCents };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export { SIGNATURE_HEADER, WebhookSignatureError, formatMoney, toCents } from '@pagosrd/core';
|
|
2
|
-
|
|
3
1
|
/** Tipos de los objetos de la API de PagosRD. Montos en centavos. */
|
|
4
2
|
type Livemode = boolean;
|
|
5
3
|
interface List<T> {
|
|
@@ -32,11 +30,18 @@ interface Merchant {
|
|
|
32
30
|
processor: "simulador" | "azul" | "cardnet";
|
|
33
31
|
processor_configured: boolean;
|
|
34
32
|
processor_summary: Record<string, string>;
|
|
33
|
+
/** true si los links de este comercio pueden guardar la tarjeta para cobros automáticos y suscripciones. */
|
|
34
|
+
saved_cards_enabled: boolean;
|
|
35
35
|
status: "active" | "disabled";
|
|
36
36
|
metadata: Record<string, string>;
|
|
37
37
|
customer_info: CustomerInfo;
|
|
38
38
|
/** true si ya tiene contacto, dirección y políticas: lo necesario para cobrar en vivo. */
|
|
39
39
|
customer_info_complete: boolean;
|
|
40
|
+
/** Otros comercios de la plataforma que cobran en vivo con la misma cuenta del procesador. Solo al consultar, crear o modificar un comercio. */
|
|
41
|
+
processor_account_shared_with?: {
|
|
42
|
+
id: string;
|
|
43
|
+
name: string;
|
|
44
|
+
}[];
|
|
40
45
|
created_at: string;
|
|
41
46
|
updated_at: string;
|
|
42
47
|
}
|
|
@@ -227,6 +232,8 @@ interface Refund {
|
|
|
227
232
|
reason: string | null;
|
|
228
233
|
status: "pending" | "succeeded" | "failed";
|
|
229
234
|
failure_message: string | null;
|
|
235
|
+
/** true si el reembolso se hizo en el portal del procesador y aquí solo se registró. */
|
|
236
|
+
recorded_manually: boolean;
|
|
230
237
|
created_at: string;
|
|
231
238
|
}
|
|
232
239
|
type EventType = "payment.succeeded" | "payment.failed" | "payment.requires_review" | "payment_link.paid" | "payment_link.expired" | "payment_link.canceled" | "refund.succeeded" | "refund.failed" | "payment_method.created" | "payment_method.deleted" | "subscription.created" | "subscription.updated" | "subscription.past_due" | "subscription.period_unpaid" | "subscription.unpaid" | "subscription.canceled" | "subscription.completed";
|
|
@@ -539,6 +546,28 @@ declare class PagosRD {
|
|
|
539
546
|
private request;
|
|
540
547
|
}
|
|
541
548
|
|
|
549
|
+
/** Monto en centavos enteros. Solo se crea con `cents()`, que valida. */
|
|
550
|
+
type Cents = number & {
|
|
551
|
+
readonly __brand: "Cents";
|
|
552
|
+
};
|
|
553
|
+
declare const CURRENCIES: readonly ["DOP", "USD"];
|
|
554
|
+
type Currency = (typeof CURRENCIES)[number];
|
|
555
|
+
/** Convierte "1,500.50" o "RD$1,500.50" a 150050 sin pasar por punto flotante. */
|
|
556
|
+
declare function toCents(amount: string | number): Cents;
|
|
557
|
+
/** 150050 -> "RD$1,500.50" */
|
|
558
|
+
declare function formatMoney(value: number, currency?: Currency): string;
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Firma de los webhooks que PagosRD envía a las plataformas.
|
|
562
|
+
*
|
|
563
|
+
* Cabecera: `PagosRD-Signature: t=<unix>,v1=<hex>`
|
|
564
|
+
* donde v1 = HMAC-SHA256(secreto, `${t}.${cuerpo}`).
|
|
565
|
+
*/
|
|
566
|
+
declare const SIGNATURE_HEADER = "PagosRD-Signature";
|
|
567
|
+
declare class WebhookSignatureError extends Error {
|
|
568
|
+
readonly name = "WebhookSignatureError";
|
|
569
|
+
}
|
|
570
|
+
|
|
542
571
|
/**
|
|
543
572
|
* Verifica la firma de un webhook y devuelve el evento tipado.
|
|
544
573
|
*
|
|
@@ -555,4 +584,4 @@ declare class PagosRD {
|
|
|
555
584
|
*/
|
|
556
585
|
declare function constructEvent(rawBody: string | Uint8Array, signatureHeader: string | null | undefined, secret: string, toleranceSeconds?: number): Event;
|
|
557
586
|
|
|
558
|
-
export { type AzulConfig, type CardnetConfig, type CreateMerchantParams, type CreatePaymentLinkParams, type CreatePaymentParams, type CreateRefundParams, type CreateSubscriptionParams, type CustomerInfo, type CustomerInfoParams, DEFAULT_BASE_URL, type Event, type EventType, type List, type Livemode, type Merchant, type PageParams, PagosRD, PagosRDConnectionError, PagosRDError, type PagosRDOptions, type Payment, type PaymentLink, type PaymentMethod, type PaymentStatus, type RecurringType, type Refund, type RequestOptions, type Subscription, type SubscriptionStatus, type Summary, type UpdateSubscriptionParams, type WebhookDelivery, type WebhookEndpoint, constructEvent };
|
|
587
|
+
export { type AzulConfig, type CardnetConfig, type CreateMerchantParams, type CreatePaymentLinkParams, type CreatePaymentParams, type CreateRefundParams, type CreateSubscriptionParams, type CustomerInfo, type CustomerInfoParams, DEFAULT_BASE_URL, type Event, type EventType, type List, type Livemode, type Merchant, type PageParams, PagosRD, PagosRDConnectionError, PagosRDError, type PagosRDOptions, type Payment, type PaymentLink, type PaymentMethod, type PaymentStatus, type RecurringType, type Refund, type RequestOptions, SIGNATURE_HEADER, type Subscription, type SubscriptionStatus, type Summary, type UpdateSubscriptionParams, type WebhookDelivery, type WebhookEndpoint, WebhookSignatureError, constructEvent, formatMoney, toCents };
|
package/dist/index.js
CHANGED
|
@@ -131,7 +131,7 @@ var PagosRD = class {
|
|
|
131
131
|
for (const [k, v] of Object.entries(query ?? {})) if (v !== void 0 && v !== null) url.searchParams.set(k, String(v));
|
|
132
132
|
const headers = {
|
|
133
133
|
authorization: `Bearer ${this.apiKey}`,
|
|
134
|
-
"user-agent": "pagosrd-node/0.1.
|
|
134
|
+
"user-agent": "pagosrd-node/0.1.1",
|
|
135
135
|
accept: "application/json"
|
|
136
136
|
};
|
|
137
137
|
if (body !== void 0) headers["content-type"] = "application/json";
|
|
@@ -179,7 +179,7 @@ function safeJson(text) {
|
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
-
//
|
|
182
|
+
// src/internal/money.ts
|
|
183
183
|
var MoneyError = class extends Error {
|
|
184
184
|
name = "MoneyError";
|
|
185
185
|
};
|
|
@@ -210,8 +210,8 @@ function formatMoney(value, currency = "DOP") {
|
|
|
210
210
|
return f.format(value / 100);
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
-
//
|
|
214
|
-
import {
|
|
213
|
+
// src/internal/crypto.ts
|
|
214
|
+
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
215
215
|
function safeEqual(a, b) {
|
|
216
216
|
const ab = Buffer.from(a);
|
|
217
217
|
const bb = Buffer.from(b);
|
|
@@ -221,7 +221,7 @@ function hmacSha256Hex(secret, data) {
|
|
|
221
221
|
return createHmac("sha256", secret).update(data).digest("hex");
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
//
|
|
224
|
+
// src/internal/webhooks.ts
|
|
225
225
|
var SIGNATURE_HEADER = "PagosRD-Signature";
|
|
226
226
|
var DEFAULT_TOLERANCE_SECONDS = 300;
|
|
227
227
|
var WebhookSignatureError = class extends Error {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../../core/src/money.ts","../../core/src/crypto.ts","../../core/src/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.0\",\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","import { createCipheriv, createDecipheriv, createHash, createHmac, hkdfSync, randomBytes, timingSafeEqual } from \"node:crypto\";\n\n/**\n * Cifrado de secretos en reposo (credenciales de AZUL, secretos de webhooks).\n *\n * AES-256-GCM con una llave derivada (HKDF-SHA256) de la llave maestra y de un\n * \"contexto\" (por ejemplo el id del comercio). Así cada comercio tiene su propia\n * llave efectiva y un texto cifrado no se puede mover a otro registro.\n *\n * Formato: \"v1:<iv b64>:<tag b64>:<cifrado b64>\". La versión permite rotar la\n * llave maestra en el futuro.\n */\n\nexport class CryptoError extends Error {\n override readonly name = \"CryptoError\";\n}\n\nexport function parseMasterKey(base64: string): Buffer {\n const key = Buffer.from(base64, \"base64\");\n if (key.length !== 32) {\n throw new CryptoError(\"La llave maestra debe ser de 32 bytes en base64. Genera una con: openssl rand -base64 32\");\n }\n return key;\n}\n\nfunction deriveKey(masterKey: Buffer, context: string): Buffer {\n return Buffer.from(hkdfSync(\"sha256\", masterKey, Buffer.alloc(0), `pagosrd:${context}`, 32));\n}\n\nexport function encryptSecret(masterKey: Buffer, context: string, plaintext: string): string {\n const iv = randomBytes(12);\n const cipher = createCipheriv(\"aes-256-gcm\", deriveKey(masterKey, context), iv);\n cipher.setAAD(Buffer.from(context));\n const encrypted = Buffer.concat([cipher.update(plaintext, \"utf8\"), cipher.final()]);\n return `v1:${iv.toString(\"base64\")}:${cipher.getAuthTag().toString(\"base64\")}:${encrypted.toString(\"base64\")}`;\n}\n\nexport function decryptSecret(masterKey: Buffer, context: string, payload: string): string {\n const [version, iv, tag, data] = payload.split(\":\");\n if (version !== \"v1\" || !iv || !tag || !data) throw new CryptoError(\"Formato de secreto cifrado desconocido.\");\n try {\n const decipher = createDecipheriv(\"aes-256-gcm\", deriveKey(masterKey, context), Buffer.from(iv, \"base64\"));\n decipher.setAAD(Buffer.from(context));\n decipher.setAuthTag(Buffer.from(tag, \"base64\"));\n return Buffer.concat([decipher.update(Buffer.from(data, \"base64\")), decipher.final()]).toString(\"utf8\");\n } catch (err) {\n throw new CryptoError(\"No se pudo descifrar el secreto: llave maestra o contexto incorrectos.\", { cause: err });\n }\n}\n\nexport function encryptJson(masterKey: Buffer, context: string, value: unknown): string {\n return encryptSecret(masterKey, context, JSON.stringify(value));\n}\n\nexport function decryptJson<T = unknown>(masterKey: Buffer, context: string, payload: string): T {\n return JSON.parse(decryptSecret(masterKey, context, payload)) as T;\n}\n\n// ------------------------------------------------------------------ llaves de API\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 type ApiKeyMode = \"test\" | \"live\";\n\n/** Genera una llave de API. El valor completo solo se muestra una vez. */\nexport function generateApiKey(mode: ApiKeyMode): { key: string; prefix: string; hash: string } {\n const key = `sk_${mode}_${randomToken(32)}`;\n return { key, prefix: key.slice(0, 12), hash: hashApiKey(key) };\n}\n\nexport function hashApiKey(key: string): string {\n return createHash(\"sha256\").update(key).digest(\"hex\");\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 \"@pagosrd/core\";\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":[]}
|
|
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,8 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pagosrd/node",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Cliente oficial de PagosRD para Node.js: links de cobro, pagos, reembolsos y verificación de webhooks.",
|
|
5
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
|
+
],
|
|
6
25
|
"type": "module",
|
|
7
26
|
"main": "./dist/index.cjs",
|
|
8
27
|
"module": "./dist/index.js",
|
|
@@ -21,22 +40,24 @@
|
|
|
21
40
|
},
|
|
22
41
|
"files": [
|
|
23
42
|
"dist",
|
|
24
|
-
"README.md"
|
|
43
|
+
"README.md",
|
|
44
|
+
"CHANGELOG.md",
|
|
45
|
+
"LICENSE"
|
|
25
46
|
],
|
|
26
47
|
"engines": {
|
|
27
48
|
"node": ">=18"
|
|
28
49
|
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsup",
|
|
52
|
+
"test": "vitest run",
|
|
53
|
+
"typecheck": "tsc --noEmit",
|
|
54
|
+
"prepublishOnly": "npm run build && npm test"
|
|
55
|
+
},
|
|
29
56
|
"sideEffects": false,
|
|
30
57
|
"devDependencies": {
|
|
31
58
|
"@types/node": "^22.20.5",
|
|
32
59
|
"tsup": "^8.5.1",
|
|
33
60
|
"typescript": "^5.9.3",
|
|
34
|
-
"vitest": "^5.0.5"
|
|
35
|
-
"@pagosrd/core": "0.1.0"
|
|
36
|
-
},
|
|
37
|
-
"scripts": {
|
|
38
|
-
"build": "tsup",
|
|
39
|
-
"test": "vitest run",
|
|
40
|
-
"typecheck": "tsc --noEmit"
|
|
61
|
+
"vitest": "^5.0.5"
|
|
41
62
|
}
|
|
42
|
-
}
|
|
63
|
+
}
|