@veriko-mx/sdk 0.4.3

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.
Files changed (86) hide show
  1. package/CHANGELOG.md +186 -0
  2. package/LICENSE +21 -0
  3. package/README.md +566 -0
  4. package/dist/cjs/client.d.ts +128 -0
  5. package/dist/cjs/client.d.ts.map +1 -0
  6. package/dist/cjs/client.js +145 -0
  7. package/dist/cjs/client.js.map +1 -0
  8. package/dist/cjs/errors.d.ts +110 -0
  9. package/dist/cjs/errors.d.ts.map +1 -0
  10. package/dist/cjs/errors.js +133 -0
  11. package/dist/cjs/errors.js.map +1 -0
  12. package/dist/cjs/generated/openapi.d.ts +8404 -0
  13. package/dist/cjs/generated/openapi.d.ts.map +1 -0
  14. package/dist/cjs/generated/openapi.js +7 -0
  15. package/dist/cjs/generated/openapi.js.map +1 -0
  16. package/dist/cjs/http.d.ts +75 -0
  17. package/dist/cjs/http.d.ts.map +1 -0
  18. package/dist/cjs/http.js +237 -0
  19. package/dist/cjs/http.js.map +1 -0
  20. package/dist/cjs/index.d.ts +35 -0
  21. package/dist/cjs/index.d.ts.map +1 -0
  22. package/dist/cjs/index.js +91 -0
  23. package/dist/cjs/index.js.map +1 -0
  24. package/dist/cjs/internal.d.ts +40 -0
  25. package/dist/cjs/internal.d.ts.map +1 -0
  26. package/dist/cjs/internal.js +92 -0
  27. package/dist/cjs/internal.js.map +1 -0
  28. package/dist/cjs/package.json +3 -0
  29. package/dist/cjs/pagination.d.ts +41 -0
  30. package/dist/cjs/pagination.d.ts.map +1 -0
  31. package/dist/cjs/pagination.js +66 -0
  32. package/dist/cjs/pagination.js.map +1 -0
  33. package/dist/cjs/resources.d.ts +522 -0
  34. package/dist/cjs/resources.d.ts.map +1 -0
  35. package/dist/cjs/resources.js +1361 -0
  36. package/dist/cjs/resources.js.map +1 -0
  37. package/dist/cjs/types.d.ts +559 -0
  38. package/dist/cjs/types.d.ts.map +1 -0
  39. package/dist/cjs/types.js +117 -0
  40. package/dist/cjs/types.js.map +1 -0
  41. package/dist/cjs/webhooks.d.ts +54 -0
  42. package/dist/cjs/webhooks.d.ts.map +1 -0
  43. package/dist/cjs/webhooks.js +116 -0
  44. package/dist/cjs/webhooks.js.map +1 -0
  45. package/dist/esm/client.d.ts +128 -0
  46. package/dist/esm/client.d.ts.map +1 -0
  47. package/dist/esm/client.js +141 -0
  48. package/dist/esm/client.js.map +1 -0
  49. package/dist/esm/errors.d.ts +110 -0
  50. package/dist/esm/errors.d.ts.map +1 -0
  51. package/dist/esm/errors.js +117 -0
  52. package/dist/esm/errors.js.map +1 -0
  53. package/dist/esm/generated/openapi.d.ts +8404 -0
  54. package/dist/esm/generated/openapi.d.ts.map +1 -0
  55. package/dist/esm/generated/openapi.js +6 -0
  56. package/dist/esm/generated/openapi.js.map +1 -0
  57. package/dist/esm/http.d.ts +75 -0
  58. package/dist/esm/http.d.ts.map +1 -0
  59. package/dist/esm/http.js +229 -0
  60. package/dist/esm/http.js.map +1 -0
  61. package/dist/esm/index.d.ts +35 -0
  62. package/dist/esm/index.d.ts.map +1 -0
  63. package/dist/esm/index.js +33 -0
  64. package/dist/esm/index.js.map +1 -0
  65. package/dist/esm/internal.d.ts +40 -0
  66. package/dist/esm/internal.d.ts.map +1 -0
  67. package/dist/esm/internal.js +82 -0
  68. package/dist/esm/internal.js.map +1 -0
  69. package/dist/esm/package.json +3 -0
  70. package/dist/esm/pagination.d.ts +41 -0
  71. package/dist/esm/pagination.d.ts.map +1 -0
  72. package/dist/esm/pagination.js +62 -0
  73. package/dist/esm/pagination.js.map +1 -0
  74. package/dist/esm/resources.d.ts +522 -0
  75. package/dist/esm/resources.d.ts.map +1 -0
  76. package/dist/esm/resources.js +1346 -0
  77. package/dist/esm/resources.js.map +1 -0
  78. package/dist/esm/types.d.ts +559 -0
  79. package/dist/esm/types.d.ts.map +1 -0
  80. package/dist/esm/types.js +109 -0
  81. package/dist/esm/types.js.map +1 -0
  82. package/dist/esm/webhooks.d.ts +54 -0
  83. package/dist/esm/webhooks.d.ts.map +1 -0
  84. package/dist/esm/webhooks.js +109 -0
  85. package/dist/esm/webhooks.js.map +1 -0
  86. package/package.json +78 -0
package/README.md ADDED
@@ -0,0 +1,566 @@
1
+ # @veriko-mx/sdk · SDK de JavaScript y TypeScript para la API del CEP
2
+
3
+ Cliente oficial de [Veriko](https://veriko.mx) para Node. Valida transferencias SPEI mexicanas
4
+ contra el CEP de Banco de México, descarga el comprobante oficial y verifica la firma de los
5
+ webhooks.
6
+
7
+ Sin dependencias de runtime. Tipos generados del spec de OpenAPI. Node 18 o superior, en ESM y en
8
+ CommonJS.
9
+
10
+ ```ts
11
+ import { Veriko } from '@veriko-mx/sdk';
12
+
13
+ const client = new Veriko(); // lee VERIKO_API_KEY del entorno
14
+
15
+ const validation = await client.validateTransfer({
16
+ fecha: '2025-03-15',
17
+ monto: 15000.5,
18
+ claveRastreo: 'MXBA20250315001234',
19
+ cuentaBeneficiaria: '012180004412345678',
20
+ });
21
+
22
+ console.log(validation.attributes.status); // 'valid'
23
+ ```
24
+
25
+ ## Qué es el CEP
26
+
27
+ El **Comprobante Electrónico de Pago (CEP)** es el documento que expide el **Banco de México
28
+ (Banxico)** por cada transferencia que pasa por el SPEI, el sistema de pagos interbancarios
29
+ mexicano. Contiene el sello digital y la cadena original de la institución receptora, de modo que
30
+ acredita que una transferencia ocurrió y por qué importe.
31
+
32
+ El CEP no tiene efectos fiscales: no sustituye a una factura.
33
+
34
+ Su consulta manual se hace en el portal de Banxico, un comprobante a la vez.
35
+
36
+ ## Qué hace la API
37
+
38
+ La API consulta el CEP y devuelve un veredicto en el campo `status`:
39
+
40
+ | veredicto | significado |
41
+ | ----------------- | -------------------------------------------------------------------------------------- |
42
+ | `valid` | Banxico devolvió un CEP que coincide con los datos enviados |
43
+ | `not_found` | Banxico no tiene un CEP con esos datos. Puede ser temporal: un CEP tarda en publicarse |
44
+ | `cep_unavailable` | Banxico no pudo responder. No dice nada sobre la transferencia |
45
+ | `returned` | El pago se liquidó y la institución beneficiaria lo devolvió después |
46
+ | `invalid` | Los datos enviados no forman una consulta válida |
47
+ | `error` | Fallo durante el procesamiento; el motivo viaja en `error_code` |
48
+
49
+ La distinción entre `not_found` y `cep_unavailable` es la que importa al integrar: el primero es
50
+ una respuesta de Banxico sobre la transferencia, el segundo es la ausencia de respuesta.
51
+
52
+ Con veredicto `valid`, el comprobante queda disponible en XML y en PDF.
53
+
54
+ ## Las familias de operaciones
55
+
56
+ El cliente agrupa las 66 operaciones M2M de la API en once familias:
57
+
58
+ | familia | qué cubre |
59
+ | ---------------------- | --------------------------------------------------------------------------------------------------------------------- |
60
+ | `client.validations` | Validar por campos o por imagen, consultar, listar, exportar, la política de reintentos y la descarga del comprobante |
61
+ | `client.webhooks` | Registrar endpoints, rotar su secreto, enviar un evento de prueba y leer el historial de entregas |
62
+ | `client.catalog` | Catálogo de bancos SPEI, banco emisor de una tarjeta y estado del servicio de Banxico |
63
+ | `client.beneficiaries` | Cuentas beneficiarias guardadas y la importación masiva, como ciclo completo |
64
+ | `client.usage` | Cuota de validaciones, límites de tasa y registro de actividad de la API |
65
+ | `client.account` | Perfil y política de reintentos predeterminada de la cuenta |
66
+ | `client.dashboard` | Resumen del panel |
67
+ | `client.plans` | Catálogo y comparación de planes públicos, sin clave de API |
68
+ | `client.insights` | Resumen, tendencias, bancos y beneficiarios principales |
69
+ | `client.finance` | Resumen, estado de cuenta, vistas previas y descargas financieras |
70
+ | `client.billing` | Suscripción activa |
71
+
72
+ Las tres operaciones de uso más frecuente están también en la raíz del cliente, como atajo:
73
+ `validateTransfer()`, `getValidation()` y `getCep()`.
74
+
75
+ ## Instalación
76
+
77
+ ```bash
78
+ npm install @veriko-mx/sdk
79
+ ```
80
+
81
+ Mientras la versión sea `0.x`, una versión menor puede ajustar la superficie pública del SDK.
82
+ La versión `1.0.0` se reservará para una integración estable en producción.
83
+
84
+ ## Autenticación
85
+
86
+ La clave de API se obtiene en el panel ([app.veriko.mx](https://app.veriko.mx)) y empieza con
87
+ `veriko_`. El cliente la toma de la variable de entorno `VERIKO_API_KEY`:
88
+
89
+ ```bash
90
+ export VERIKO_API_KEY=veriko_tu_clave_aqui
91
+ ```
92
+
93
+ Sólo `client.plans.listPublic()` y `client.plans.getPublicPlanComparison()` se pueden usar sin
94
+ clave y no envían `Authorization`. El resto conserva la autenticación de la API: una petición sin
95
+ clave falla localmente antes de salir a la red.
96
+
97
+ La opción `apiKey` la recibe directamente cuando la gestiona otro mecanismo:
98
+
99
+ ```ts
100
+ const client = new Veriko({ apiKey: 'veriko_tu_clave_aqui' });
101
+ ```
102
+
103
+ ## Validar una transferencia
104
+
105
+ La operación exige la fecha de envío, el importe y **la clave de rastreo o la referencia
106
+ numérica**. Enviar las dos precisa la búsqueda. El banco emisor, el receptor y la cuenta
107
+ beneficiaria son opcionales y mejoran la identificación.
108
+
109
+ ```ts
110
+ import { Veriko } from '@veriko-mx/sdk';
111
+
112
+ const client = new Veriko();
113
+
114
+ const validation = await client.validateTransfer({
115
+ fecha: '2025-03-15', // YYYY-MM-DD, la fecha de envío
116
+ monto: 15000.5, // en pesos, con centavos
117
+ claveRastreo: 'MXBA20250315001234',
118
+ referenciaNumerica: '1234567',
119
+ emisor: 'BANCO NACIONAL DE MEXICO',
120
+ receptor: 'BBVA MEXICO',
121
+ cuentaBeneficiaria: '012180004412345678', // CLABE, tarjeta o celular DiMo
122
+ });
123
+
124
+ if (validation.attributes.status === 'valid') {
125
+ console.log('Pago confirmado en', validation.attributes.processing_time_ms, 'ms');
126
+ }
127
+ ```
128
+
129
+ Los argumentos van en `camelCase` y viajan con el nombre que la API espera. Lo que la API devuelve
130
+ conserva el suyo (`banxico_status`, `processing_time_ms`), porque es el tipo generado del spec.
131
+
132
+ Cada llamada consume cuota del plan, y se descuenta al aceptar la petición.
133
+
134
+ ### Desde la imagen del comprobante
135
+
136
+ ```ts
137
+ const validation = await client.validations.validateOcr({
138
+ image: 'comprobante.png', // ruta, Buffer o Uint8Array
139
+ cuentaBeneficiaria: '012180004412345678', // obligatoria para celular DiMo
140
+ });
141
+ ```
142
+
143
+ El SDK lee el archivo y lo codifica en base64. `imageUrl` recibe una imagen ya publicada en HTTPS, y
144
+ si se envían `image` e `imageUrl`, la API sólo considera `image`. Formatos: JPEG, PNG o WebP, de
145
+ hasta 12 MB.
146
+
147
+ La imagen de una validación por OCR se descarga con `client.validations.image(id)`.
148
+
149
+ ### Sin esperar al veredicto
150
+
151
+ Para volumen, la API acepta la petición y responde con el identificador:
152
+
153
+ ```ts
154
+ const queued = await client.validations.enqueue({
155
+ fecha: '2025-03-15',
156
+ monto: 15000.5,
157
+ claveRastreo: 'MXBA20250315001234',
158
+ });
159
+
160
+ const validation = await client.validations.waitFor(queued.id); // sondea hasta el veredicto
161
+ ```
162
+
163
+ `enqueue()` y `enqueueOcr()` son métodos aparte y no una opción de `validate()`, de modo que el
164
+ tipo de retorno de cada uno es fijo.
165
+
166
+ `waitFor()` manda el `ETag` de la respuesta anterior en cada vuelta, así que un sondeo que no
167
+ encuentra cambios no descarga otra vez el mismo cuerpo. Espera a que el veredicto quede firme, no
168
+ sólo a que el estado sea terminal: una validación con reintentos en marcha llega a `not_found` y
169
+ sigue cambiando después. Esa distinción es `isSettled()`, junto a `isTerminal()` y `hasCep()`.
170
+
171
+ El tiempo de espera se fija con `timeoutMs` (cinco minutos por omisión) y la pausa entre sondeos con
172
+ `pollIntervalMs` (cinco segundos). Al agotarse el primero, `waitFor()` lanza `TimeoutError`.
173
+
174
+ La alternativa a sondear es suscribirse al webhook `validation.completed`.
175
+
176
+ ### Listar y recorrer el historial
177
+
178
+ ```ts
179
+ const page = await client.validations.list({ status: 'valid', perPage: 50 });
180
+ console.log(page.total, 'validaciones,', page.totalPages, 'páginas');
181
+
182
+ for await (const item of client.validations.iter({ from: '2025-03-01', to: '2025-03-31' })) {
183
+ console.log(item.id, item.attributes.status);
184
+ }
185
+ ```
186
+
187
+ `iter()` es un `AsyncIterable` y pide la página siguiente sólo cuando la anterior se agota. Con
188
+ `maxPages` se acota el recorrido. Cada `Page` trae `items`, `page`, `perPage`, `total`,
189
+ `totalPages` y `hasNext`.
190
+
191
+ Los filtros (`status`, `type`, `from`, `to`, `search`, `playground`, `withDeleted`, `batchId`,
192
+ `bank`, `amountMin`, `amountMax` y `retryState`) van en `camelCase`. `status` acepta un estado o una
193
+ lista, y `withDeleted: true` devuelve sólo las validaciones retiradas, mientras que `false` devuelve
194
+ sólo las activas.
195
+
196
+ Un listado trae menos campos que `get()`: no incluye los datos enviados, el resultado de Banxico ni
197
+ los enlaces al comprobante.
198
+
199
+ El historial se exporta con `client.validations.export({ format: 'csv' })`, que admite también
200
+ `xlsx` y los mismos filtros. `client.validations.stats()` devuelve los totales con esos filtros.
201
+
202
+ ## Descargar el CEP
203
+
204
+ ```ts
205
+ import { writeFile } from 'node:fs/promises';
206
+ import { hasCep } from '@veriko-mx/sdk';
207
+
208
+ if (hasCep(validation)) {
209
+ const cep = await client.getCep(validation.id, { format: 'pdf' }); // o 'xml'
210
+ await writeFile(cep.filename, cep.content); // CEP-<id>.pdf
211
+ }
212
+ ```
213
+
214
+ `getCep()` devuelve el archivo: el XML que emitió Banxico, con su sello digital y su cadena
215
+ original, o el PDF equivalente. Cuando la validación no tiene comprobante, la API responde `404`
216
+ con `cep_not_available` y el SDK lanza `NotFoundError`.
217
+
218
+ Las descargas y las exportaciones devuelven el archivo y no un enlace: `content`, `contentType` y
219
+ el `filename` que propone la API en `Content-Disposition`.
220
+
221
+ ## Registrar un webhook
222
+
223
+ ```ts
224
+ const endpoint = await client.webhooks.create({
225
+ url: 'https://miapp.example.com/hooks/pagos',
226
+ events: ['validation.completed'],
227
+ });
228
+
229
+ console.log(endpoint.attributes.secret); // whsec_… La API no lo vuelve a entregar
230
+ ```
231
+
232
+ El secreto de firma viaja **una sola vez**, en esta respuesta. Si se pierde,
233
+ `client.webhooks.regenerateSecret(endpoint.id)` devuelve uno nuevo, y el anterior deja de valer.
234
+
235
+ `client.webhooks.test(endpoint.id)` manda un evento de prueba y dice si el receptor lo aceptó. Las
236
+ entregas de prueba no cuentan para el contador de fallos consecutivos que apaga un endpoint a los
237
+ tres seguidos. Cuando eso pasa, el estado queda en `auto_disabled` y se reactiva con
238
+ `client.webhooks.update(id, { status: 'active' })`.
239
+
240
+ El historial de intentos está en `client.webhooks.deliveries()`, con o sin identificador de
241
+ endpoint, y se recorre con `iterDeliveries()`. `exportDeliveries()` lo descarga en CSV o en XLSX.
242
+ Los filtros `status` y `eventType` sólo existen en el listado global: con un endpoint y un filtro,
243
+ el SDK consulta ese listado con `endpoint_id`.
244
+
245
+ ## Catálogo y estado de Banxico
246
+
247
+ ```ts
248
+ const banks = await client.catalog.banks(); // instituciones SPEI con su código
249
+ const card = await client.catalog.binLookup('455632'); // banco emisor de una tarjeta
250
+ const status = await client.catalog.banxicoStatus();
251
+
252
+ console.log(status.attributes.status); // 'operational'
253
+ ```
254
+
255
+ `banxicoStatus()` sirve para distinguir un `cep_unavailable` propio de la transferencia de una caída
256
+ del servicio, y `banxicoTimeseries()` devuelve la serie de latencia o de veredictos por ventana.
257
+
258
+ ## Beneficiarios
259
+
260
+ `client.beneficiaries` guarda las cuentas a las que se paga: CLABE, tarjeta o celular DiMo. El tipo
261
+ se detecta por la longitud del número, y un celular exige `bankCode`.
262
+
263
+ ```ts
264
+ const beneficiary = await client.beneficiaries.create({
265
+ accountNumber: '012180004412345678',
266
+ label: 'Proveedor ABC',
267
+ });
268
+
269
+ const found = await client.beneficiaries.lookup('012180004412345678'); // la cuenta ya guardada
270
+ console.log(found.attributes.bank_name);
271
+ ```
272
+
273
+ La lista completa se recorre con `client.beneficiaries.list()`, que no pagina, y `delete()` archiva una cuenta sin
274
+ borrarla: `list({ withArchived: true })` devuelve sólo las archivadas. `export()` la baja en CSV o en
275
+ XLSX.
276
+
277
+ ### Importación masiva
278
+
279
+ La importación es un ciclo: descargar la plantilla, subir el archivo, revisar las filas y confirmar.
280
+
281
+ ```ts
282
+ const template = await client.beneficiaries.importTemplate({ format: 'csv' }); // 1. descargar
283
+ const started = await client.beneficiaries.importStart('beneficiarios.csv'); // 2. subir
284
+ await client.beneficiaries.importWait(started.id); // 3. esperar la vista previa
285
+
286
+ for await (const row of client.beneficiaries.iterImportPreview(started.id)) {
287
+ // 4. revisar
288
+ if (row.id !== undefined && row.attributes?.status === 'correctable') {
289
+ await client.beneficiaries.importEditRow(started.id, row.id, { parsedBankCode: '40012' });
290
+ }
291
+ }
292
+
293
+ await client.beneficiaries.importCommit(started.id); // 5. confirmar
294
+ ```
295
+
296
+ `importStart()` recibe los bytes del archivo o su ruta, y lo sube como `multipart/form-data`. Nada
297
+ se persiste hasta `importCommit()`. `importWait()` espera a `preview_ready` o a un estado final,
298
+ que es lo que distingue `isImportSettled()`; el endpoint de estado no expone `ETag`, así que cada
299
+ vuelta descarga el cuerpo. Una importación que todavía no se confirmó se cancela con
300
+ `importCancel()`.
301
+
302
+ ## Consumo
303
+
304
+ ```ts
305
+ const summary = await client.usage.summary();
306
+ console.log(summary.attributes?.used, 'de', summary.attributes?.limit);
307
+
308
+ const history = await client.usage.history({ months: 6 });
309
+ const limits = await client.usage.limits();
310
+ ```
311
+
312
+ `summary()` trae la cuota del plan en curso y `limits()` los límites de tasa, que son ajenos a esa
313
+ cuota. `breakdown()`, `heatmap()` y `apiUsage()` desglosan el consumo, y
314
+ `client.usage.export({ format: 'csv' })` baja el registro de actividad.
315
+
316
+ ## Lecturas condicionales
317
+
318
+ `client.validations.get()` y `client.catalog.banks()` admiten `ifNoneMatch`. La respuesta trae el
319
+ `ETag` en `etag`, y cuando nada cambió la API responde `304`, que el SDK lanza como `ApiError` con
320
+ `status` 304.
321
+
322
+ ```ts
323
+ import { ApiError } from '@veriko-mx/sdk';
324
+
325
+ const validation = await client.validations.get(id);
326
+
327
+ try {
328
+ await client.validations.get(id, { ifNoneMatch: validation.etag });
329
+ } catch (error) {
330
+ if (!(error instanceof ApiError && error.status === 304)) throw error;
331
+ }
332
+ ```
333
+
334
+ ## Verificar la firma de un webhook
335
+
336
+ Cada entrega llega firmada con HMAC-SHA256 del cuerpo, usando el secreto que devolvió
337
+ `POST /v1/webhooks` al registrar el endpoint:
338
+
339
+ ```
340
+ X-Webhook-Signature: sha256=<hex>
341
+ ```
342
+
343
+ Lo que se firma es el cuerpo **tal como llegó**. Interpretar el JSON y volver a serializarlo
344
+ cambia los bytes y la firma deja de cuadrar. En Express, eso significa `express.raw()` en la ruta
345
+ del webhook, no `express.json()`.
346
+
347
+ ```ts
348
+ import express from 'express';
349
+ import { SignatureVerificationError, parseWebhook } from '@veriko-mx/sdk';
350
+
351
+ const app = express();
352
+ const SECRET = process.env.VERIKO_WEBHOOK_SECRET;
353
+
354
+ app.post('/hooks/veriko', express.raw({ type: 'application/json' }), (request, response) => {
355
+ let evento;
356
+ try {
357
+ evento = parseWebhook(request.body, request.get('X-Webhook-Signature'), SECRET);
358
+ } catch (error) {
359
+ if (error instanceof SignatureVerificationError) return response.sendStatus(400);
360
+ throw error;
361
+ }
362
+
363
+ response.sendStatus(200); // responder 2xx primero, procesar después
364
+
365
+ if (evento.event === 'validation.completed') {
366
+ console.log(evento.data.id, evento.data.attributes.status);
367
+ }
368
+ });
369
+ ```
370
+
371
+ `verifyWebhook(payload, signature, secret)` hace la misma comprobación y devuelve `true` o `false`,
372
+ para decidir el código de respuesta aparte. La comparación es en tiempo constante.
373
+
374
+ El receptor completo está en [`examples/express-webhook.mjs`](examples/express-webhook.mjs).
375
+
376
+ ### Las cuatro cabeceras de una entrega
377
+
378
+ | cabecera | contenido |
379
+ | ---------------------- | ----------------------------------------------------- |
380
+ | `X-Webhook-Signature` | `sha256=` seguido del HMAC en hexadecimal |
381
+ | `X-Veriko-Event` | Tipo de evento, por ejemplo `validation.completed` |
382
+ | `X-Veriko-Delivery-Id` | Identificador de la entrega, estable entre reintentos |
383
+ | `X-Veriko-Timestamp` | Momento del envío, ISO 8601 con sufijo `Z` |
384
+
385
+ El `Delivery-Id` es el valor que permite descartar entregas repetidas.
386
+
387
+ ## Reintentos
388
+
389
+ Hay dos mecanismos distintos con el mismo nombre.
390
+
391
+ **Los del cliente** repiten una petición que falló por causas pasajeras. El SDK reintenta los
392
+ `5xx`, el `408` y el `429`, y respeta el `Retry-After` de la respuesta cuando lo trae. El resto de
393
+ los `4xx` no se reintenta, porque la petición hay que corregirla antes de repetirla.
394
+
395
+ ```ts
396
+ const client = new Veriko({ maxRetries: 3 }); // 0 los desactiva; por omisión son 2
397
+ ```
398
+
399
+ **Los de la API** siguen consultando a Banxico durante horas, porque un CEP tarda en publicarse, y
400
+ avisan por webhook cuando el veredicto cambia.
401
+
402
+ ```ts
403
+ const validation = await client.validateTransfer({
404
+ fecha: '2025-03-15',
405
+ monto: 15000.5,
406
+ claveRastreo: 'MXBA20250315001234',
407
+ retryPolicy: {
408
+ enabled: true,
409
+ max_retries: 3,
410
+ interval_seconds: 600, // de 300 a 86 400
411
+ outcomes: ['not_found', 'cep_unavailable'], // qué veredictos reintentar
412
+ },
413
+ });
414
+ ```
415
+
416
+ El avance del ciclo se lee en `(await client.getValidation(id)).attributes.retry_state`, o se
417
+ espera al webhook `validation.retry.resolved`. Los intentos ya hechos están en
418
+ `client.validations.retryAttempts(id)`.
419
+
420
+ La política de una validación ya creada se cambia con `client.validations.setRetryPolicy(id, policy)`
421
+ y el ciclo se detiene con `client.validations.cancelRetries(id)`. Las dos devuelven el estado del
422
+ ciclo, no la validación completa.
423
+
424
+ ## Idempotencia
425
+
426
+ Reintentar un `POST` sin clave de idempotencia puede duplicar la validación, y su cargo, cuando la
427
+ respuesta se perdió pero la petición llegó. Con clave, la repetición devuelve la respuesta original
428
+ durante 24 horas.
429
+
430
+ ```ts
431
+ const validation = await client.validateTransfer({
432
+ fecha: '2025-03-15',
433
+ monto: 15000.5,
434
+ claveRastreo: 'MXBA20250315001234',
435
+ idempotencyKey: 'pedido-4f3a2b1c', // el identificador del intento de negocio
436
+ });
437
+ ```
438
+
439
+ La clave se deriva del intento de negocio (el número de pedido, de lote o de transacción), no se
440
+ genera al azar en cada envío: una clave aleatoria por reintento anula la protección.
441
+
442
+ Sin `idempotencyKey`, el SDK genera una por llamada y la repite en sus propios reintentos. Esa
443
+ clave no sobrevive al proceso que la generó, así que un reenvío posterior sí se ejecuta dos veces.
444
+ Esto vale para `validate()`, `validateOcr()`, `enqueue()` y `enqueueOcr()`. `setRetryPolicy()` y
445
+ `cancelRetries()` aceptan la clave, pero el SDK no genera una por su cuenta.
446
+
447
+ ## Errores
448
+
449
+ Todas las excepciones heredan de `VerikoError`. Las de la API traen `code`, que es el contrato
450
+ estable, y `detail`, que se traduce y puede reformularse entre versiones.
451
+
452
+ ```ts
453
+ import { InvalidRequestError, NotFoundError, RateLimitError } from '@veriko-mx/sdk';
454
+
455
+ try {
456
+ const validation = await client.validateTransfer(params);
457
+ } catch (error) {
458
+ if (error instanceof InvalidRequestError) {
459
+ // 400, 413, 422 — un 422 trae una entrada por campo inválido
460
+ for (const entrada of error.errors) {
461
+ console.error(entrada.code, entrada.source?.pointer);
462
+ }
463
+ } else if (error instanceof RateLimitError) {
464
+ console.error('esperar', error.retryAfter, 'segundos');
465
+ } else if (error instanceof NotFoundError) {
466
+ // 404
467
+ }
468
+ }
469
+ ```
470
+
471
+ | excepción | estado |
472
+ | ---------------------------- | ----------------------------------------------- |
473
+ | `AuthenticationError` | `401` |
474
+ | `ForbiddenError` | `403` |
475
+ | `NotFoundError` | `404` |
476
+ | `ConflictError` | `409` |
477
+ | `InvalidRequestError` | `400`, `413`, `422` |
478
+ | `RateLimitError` | `429` |
479
+ | `ServerError` | `5xx` |
480
+ | `ConnectionError` | Sin respuesta, con los reintentos agotados |
481
+ | `TimeoutError` | `waitFor()` o `importWait()` agotaron su tiempo |
482
+ | `SignatureVerificationError` | La firma de un webhook no cuadra |
483
+
484
+ Cada error de la API trae `requestId`, que identifica la petición en los registros del sistema.
485
+
486
+ ## Los tipos
487
+
488
+ Los tipos de lo que viaja por el cable se generan del spec de OpenAPI y se versionan en
489
+ [`src/generated/openapi.ts`](src/generated/openapi.ts):
490
+
491
+ ```bash
492
+ npm run gen:types # openapi-typescript sobre spec/openapi.yaml
493
+ ```
494
+
495
+ La copia de `spec/openapi.yaml` sale de
496
+ [docs.veriko.mx/openapi.yaml](https://docs.veriko.mx/openapi.yaml), que es la versión **pública**
497
+ del spec, filtrada por visibilidad. El bundle interno de la aplicación no se usa aquí, y
498
+ `npm run check:spec-public` lo comprueba en cada ejecución del CI.
499
+
500
+ Ese spec es neutral de marca por diseño de la plataforma, así que sus ejemplos citan un host
501
+ genérico. La raíz real de la API la fija el SDK en `DEFAULT_BASE_URL`.
502
+
503
+ Cada método devuelve el recurso `data` de la respuesta, tipado por el spec: `validation.attributes`,
504
+ `endpoint.attributes.secret`, `bank.attributes.name`.
505
+
506
+ Los cuerpos de `POST /v1/validate` y de `POST /v1/validate-ocr` son la única excepción y se escriben
507
+ a mano: sus schemas llevan un `anyOf` que el generador traduce a `unknown`. `test/spec.test.ts`
508
+ falla si el spec añade, quita o renombra alguno de sus campos.
509
+
510
+ Además de los tipos con nombre corto, el paquete reexporta `components`, `operations` y `paths`
511
+ completos, por si hace falta una operación que el SDK todavía no envuelve.
512
+
513
+ ## Superficie M2M
514
+
515
+ El SDK cubre exactamente las 66 operaciones que el spec público clasifica como M2M: `security: []`
516
+ para las públicas, o una alternativa con `ApiKeyAuth` para las autenticadas. No se infiere de
517
+ `x-auth`, etiquetas ni familias. Cualquier operación que sólo admita `CookieAuth` queda fuera.
518
+
519
+ Las 18 incorporadas en esta alineación son el perfil y su política de reintentos, el resumen del
520
+ panel, los dos endpoints públicos de planes, las cuatro vistas de insights, las siete operaciones
521
+ financieras y la suscripción. Por ejemplo:
522
+
523
+ ```ts
524
+ const profile = await client.account.myProfile();
525
+ const trends = await client.insights.getTrends({ range: '30d', metric: 'latency' });
526
+ const statement = await client.finance.getStatement({ month: '2026-04', format: 'pdf' });
527
+ await writeFile(statement.filename, statement.content);
528
+ ```
529
+
530
+ Los reportes mensual, por contraparte, por banco y contable conservan el default de la API:
531
+ `format: 'csv'`. Usa `format: 'preview'` explícitamente cuando necesites la respuesta JSON para
532
+ procesarla en memoria.
533
+
534
+ La importación masiva de validaciones, las sesiones de usuario y el playground siguen fuera: sólo
535
+ aceptan cookie de sesión. La corrección también retira el antiguo `validateAccount()`; ya no forma
536
+ parte del contrato M2M público.
537
+
538
+ ## Desarrollo
539
+
540
+ ```bash
541
+ npm install
542
+ npm test # compila a dist-test/ y corre node --test
543
+ npm run lint # eslint
544
+ npm run typecheck # tsc --noEmit
545
+ npm run build # dist/esm y dist/cjs
546
+ ```
547
+
548
+ `test/operations.test.ts` compara el conjunto completo de métodos con el spec filtrado, ejerce cada
549
+ ruta contra un servidor HTTP local y falla si aparece una operación M2M sin método, un método ajeno
550
+ al contrato o una excepción temporal que no se eliminó.
551
+
552
+ Ninguna prueba llama a la API. El arnés levanta un servidor HTTP local que sirve las respuestas
553
+ guardadas en [`test/recordings/`](test/recordings), recortadas de los ejemplos del spec público.
554
+
555
+ ## Enlaces
556
+
557
+ - Documentación de la API: [docs.veriko.mx](https://docs.veriko.mx)
558
+ - Spec de OpenAPI: [docs.veriko.mx/openapi.yaml](https://docs.veriko.mx/openapi.yaml)
559
+ - SDK de Python: [veriko-mx-labs/veriko-python](https://github.com/veriko-mx-labs/veriko-python)
560
+ - Ejemplos en otros lenguajes: [veriko-mx-labs/examples](https://github.com/veriko-mx-labs/examples)
561
+ - El CEP, en detalle: [docs.veriko.mx/es/concepts/cep-concept](https://docs.veriko.mx/es/concepts/cep-concept)
562
+ - Registro de cambios: [CHANGELOG.md](CHANGELOG.md)
563
+
564
+ ## Licencia
565
+
566
+ MIT — ver [`LICENSE`](LICENSE).
@@ -0,0 +1,128 @@
1
+ /** El cliente: la entrada al SDK y las familias de operaciones. */
2
+ import { Transport, type RetryConfig } from './http.js';
3
+ import { Account, Beneficiaries, Billing, Catalog, Dashboard, Finance, Insights, Plans, Usage, Validations, Webhooks } from './resources.js';
4
+ import type { CepDocument, CepFormat, GetValidationOptions, ValidateTransferParams, Validation, ValidationWithEtag } from './types.js';
5
+ export declare const DEFAULT_BASE_URL = "https://api.veriko.mx/v1";
6
+ export declare const DEFAULT_TIMEOUT_MS = 30000;
7
+ export declare const API_KEY_ENV_VAR = "VERIKO_API_KEY";
8
+ export declare const BASE_URL_ENV_VAR = "VERIKO_BASE_URL";
9
+ /** La versión que viaja en el `User-Agent`. `test/client.test.ts` la compara con `package.json`. */
10
+ export declare const VERSION = "0.4.3";
11
+ export interface VerikoOptions {
12
+ /** La clave de API. Por omisión, `VERIKO_API_KEY`. */
13
+ apiKey?: string;
14
+ /** La raíz de la API. Por omisión, `VERIKO_BASE_URL` o producción. */
15
+ baseUrl?: string;
16
+ /** Milisegundos de espera por intento. */
17
+ timeoutMs?: number;
18
+ /**
19
+ * Reintentos automáticos además del primer intento. `0` los desactiva. Sólo
20
+ * se reintenta lo que admite reintento: los `5xx`, el `408` y el `429`.
21
+ */
22
+ maxRetries?: number;
23
+ /** `es` o `en`, para el idioma de `error.detail`. */
24
+ acceptLanguage?: string;
25
+ /** Configuración de reintentos completa. Gana sobre `maxRetries`. */
26
+ retry?: Partial<RetryConfig>;
27
+ userAgentSuffix?: string;
28
+ /** Transporte ya construido. Las pruebas lo usan para no salir a la red. */
29
+ transport?: Transport;
30
+ }
31
+ /**
32
+ * Cliente de la API de Veriko.
33
+ *
34
+ * La clave de API se lee de `options.apiKey` o, si no se pasa, de la variable de
35
+ * entorno `VERIKO_API_KEY`. Empieza con `veriko_` y se obtiene en el panel:
36
+ * https://app.veriko.mx
37
+ *
38
+ * Las operaciones se agrupan por familia:
39
+ *
40
+ * - `client.validations`: validar, consultar, reintentar y descargar.
41
+ * - `client.webhooks`: endpoints y su historial de entregas.
42
+ * - `client.catalog`: bancos y estado del servicio de Banxico.
43
+ * - `client.beneficiaries`: cuentas guardadas y su importación masiva.
44
+ * - `client.usage`: cuota, límites y registro de actividad.
45
+ * - `client.account`: perfil y política de reintentos predeterminada.
46
+ * - `client.dashboard`: resumen del panel.
47
+ * - `client.plans`: catálogo y comparación de planes públicos.
48
+ * - `client.insights`: métricas agregadas de la cuenta.
49
+ * - `client.finance`: resúmenes y descargas financieras.
50
+ * - `client.billing`: suscripción activa.
51
+ *
52
+ * Las tres de uso más frecuente están también en la raíz, como atajo:
53
+ *
54
+ * ```ts
55
+ * const client = new Veriko();
56
+ *
57
+ * const validation = await client.validateTransfer({
58
+ * fecha: '2025-03-15',
59
+ * monto: 15000.5,
60
+ * claveRastreo: 'MXBA20250315001234',
61
+ * cuentaBeneficiaria: '012180004412345678',
62
+ * });
63
+ * ```
64
+ */
65
+ export declare class Veriko {
66
+ private readonly transport;
67
+ /** Validar, consultar, reintentar y descargar. */
68
+ readonly validations: Validations;
69
+ /** Endpoints de webhook y su historial de entregas. */
70
+ readonly webhooks: Webhooks;
71
+ /** Catálogo de bancos y estado del servicio de Banxico. */
72
+ readonly catalog: Catalog;
73
+ /** Cuentas beneficiarias guardadas y su importación masiva. */
74
+ readonly beneficiaries: Beneficiaries;
75
+ /** Cuota, límites y registro de actividad. */
76
+ readonly usage: Usage;
77
+ /** Perfil y preferencias de la cuenta autenticada. */
78
+ readonly account: Account;
79
+ /** Resumen del panel. */
80
+ readonly dashboard: Dashboard;
81
+ /** Catálogo público de planes. */
82
+ readonly plans: Plans;
83
+ /** Métricas agregadas de la cuenta. */
84
+ readonly insights: Insights;
85
+ /** Resúmenes y descargas financieras. */
86
+ readonly finance: Finance;
87
+ /** Suscripción activa de la cuenta. */
88
+ readonly billing: Billing;
89
+ constructor(options?: VerikoOptions);
90
+ get baseUrl(): string;
91
+ /**
92
+ * Atajo de `client.validations.validate()`.
93
+ *
94
+ * Valida una transferencia SPEI contra el CEP de Banxico. `POST /v1/validate`.
95
+ * Devuelve el veredicto en `attributes.status`: `valid`, `not_found`,
96
+ * `cep_unavailable`, `returned` o `error`.
97
+ *
98
+ * La operación exige `claveRastreo` o `referenciaNumerica`. Enviar las dos
99
+ * precisa la búsqueda. La fecha es la de envío, en `YYYY-MM-DD`.
100
+ *
101
+ * Un `not_found` inmediato no equivale a una transferencia inexistente: un
102
+ * CEP tarda en publicarse. `retryPolicy` deja a la API consultando de nuevo y
103
+ * avisando por webhook cuando el veredicto cambia.
104
+ *
105
+ * Cada llamada consume cuota del plan, y se descuenta al aceptar la petición.
106
+ */
107
+ validateTransfer(params: ValidateTransferParams): Promise<Validation>;
108
+ /**
109
+ * Atajo de `client.validations.get()`.
110
+ *
111
+ * Lee una validación por su identificador. `GET /v1/validations/{id}`. Es la
112
+ * operación con la que se sigue una validación que quedó reintentando: el
113
+ * veredicto final aparece cuando `attributes.status` alcanza un estado
114
+ * terminal.
115
+ */
116
+ getValidation(validationId: string, options?: GetValidationOptions): Promise<ValidationWithEtag>;
117
+ /**
118
+ * Atajo de `client.validations.cep()`.
119
+ *
120
+ * Descarga el CEP oficial de una validación. `GET /v1/validations/{id}/cep`.
121
+ * Devuelve el archivo: el XML que emitió Banxico, con su sello digital y su
122
+ * cadena original, o el PDF equivalente.
123
+ */
124
+ getCep(validationId: string, options?: {
125
+ format?: CepFormat;
126
+ }): Promise<CepDocument>;
127
+ }
128
+ //# sourceMappingURL=client.d.ts.map