@cuenca-mx/cep
v1.0.0
Published
TypeScript SDK for the Banxico CEP (Comprobante Electrónico de Pago) portal.
Readme
@cuenca-mx/cep
TypeScript SDK for the Banxico CEP portal — the public service that issues Comprobante Electrónico de Pago receipts for SPEI transfers.
This package is the JavaScript counterpart to
cep-python. The two share
the same protocol expectations and error semantics so consumers can
switch languages without surprises.
Install
npm install @cuenca-mx/cepRequires Node.js >= 20.
Usage
import Cep, {
CepError,
CepNotAvailableError,
FileFormat,
InvalidInstitutionError,
MaxRequestError,
TransferNotFoundError,
} from '@cuenca-mx/cep'
const cep = new Cep()
try {
const transfer = await cep.transfers.validate({
fecha: '08-11-2024', // dd-mm-yyyy
claveRastreo: 'BiB202411081016248360',
emisor: '37166', // 5-digit Banxico code
receptor: '90723', // 5-digit Banxico code
cuenta: '723969000011000077',
monto: 341495, // amount in cents (integer), same as cep-python
})
void transfer.ordenante.nombre // parsed from Banxico XML after validate
const pdf = await cep.transfers.download(FileFormat.Pdf)
// pdf is a Buffer with the %PDF magic bytes already verified.
} catch (err: unknown) {
if (err instanceof TransferNotFoundError) {
/* 404 */
} else if (err instanceof InvalidInstitutionError) {
/* 422 — emisor/receptor are not Banxico 5-digit codes */
} else if (err instanceof MaxRequestError) {
/* 429 */
} else if (err instanceof CepNotAvailableError) {
/* 202 / retry later */
} else if (err instanceof CepError) {
/* other SDK errors */
} else {
throw err
}
}For server contexts that pipe the PDF straight into an HTTP response
you can use downloadStream, which yields a Readable whose payload
has already been validated as PDF/XML/ZIP:
import Cep, { FileFormat } from '@cuenca-mx/cep'
import { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const cep = new Cep() // fresh instance per request — see "Concurrency" below
await cep.transfers.validate(req.query as any)
const stream = await cep.transfers.downloadStream(FileFormat.Pdf)
res.setHeader('Content-Type', 'application/pdf')
stream.pipe(res)
}Concurrency
Every new Cep() owns its own Client, which in turn owns its own
axios session and cookie jar. Two concurrent CEP lookups in the same
Node process never share a JSESSIONID. Always allocate a Cep
instance per inbound request — do not cache one in module scope.
Errors
All errors thrown by the SDK extend CepError, so consumers can
catch (err: unknown) and narrow with err instanceof CepError
(and specific subclasses):
| Error | When Banxico says… |
| ------------------------- | ------------------------------------------------------------------------------- |
| TransferNotFoundError | "No se encontró ningún pago…" / "El SPEI no ha recibido…" |
| CepNotAvailableError | "Con la información proporcionada se identificó el siguiente pago" (no CEP yet) |
| InvalidInstitutionError | "No existe un emisor válido" / "…receptor válido" |
| MaxRequestError | "Lo sentimos, pero ha excedido el número máximo de consultas" |
| InvalidDownloadError | descarga.do returned non-PDF/XML/ZIP bytes |
| CepRequestError | Network failure, 5xx, timeouts |
CepException and CepResponseException are kept as aliases for
backwards compatibility with the 0.x line.
Configuration
Pass options when constructing Cep (or Client). There is no global
base URL — each instance is isolated (better for tests and concurrent requests).
import Cep from '@cuenca-mx/cep'
new Cep({ beta: true }) // https://www.banxico.org.mx/cep-beta
new Cep() // production https://www.banxico.org.mx/cep
new Cep({ baseURL: 'https://internal-mock/cep' }) // tests / fixturesMigrating from 0.x
validatethrows a typed error if Banxico says the transfer does not exist / CEP is not available / emisor is invalid / rate limit was hit. On success it resolves to aTransferparsed from Banxico XML (fields such asordenante,beneficiario,concepto,sello, …).download(...)now returns a verifiedBuffer(with PDF magic bytes already checked). Callers that need the previous "Readable stream" behavior should switch todownloadStream(...).Client.getInstance()is gone — eachnew Cep()instantiates a fresh client. This fixes a session/cookie leak across concurrent requests in server runtimes (Next.js API, Vercel, Lambda).set-cookiehandling no longer joins entries with,— Banxico rejects the malformed header and the next call lost its session.- POST
/valida.donow sendsapplication/x-www-form-urlencodedbody instead of a query string.
Development
npm install
npm run lint
npm test
npm run buildLicense
MIT — see LICENSE.
