@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 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.0",
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
- // ../core/src/money.ts
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
- // ../core/src/crypto.ts
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
- // ../core/src/webhooks.ts
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 {
@@ -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.0",
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
- // ../core/src/money.ts
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
- // ../core/src/crypto.ts
214
- import { createCipheriv, createDecipheriv, createHash, createHmac, hkdfSync, randomBytes, timingSafeEqual } from "crypto";
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
- // ../core/src/webhooks.ts
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.0",
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
+ }