n1coePayDocs Referencia del API

SDK de Node.js

@n1co/epay llama al API de ePay desde tu servidor Node.js, con tipos de TypeScript, errores con código y verificación de webhooks.

  1. Instala @n1co/epay en tu servidor (Node.js 22.12+, 24 o 26+).
  2. Crea el cliente con tu llave secreta y el host de desarrollo.
  3. Crea intents con paymentIntents.create() y verifica webhooks con constructEvent().
npm install @n1co/epay

Un servidor Express completo: crea el intent y recibe el webhook.

server.mjs
import express from 'express';
import { N1co, WebhookSignatureError } from '@n1co/epay';

const n1co = new N1co(process.env.N1CO_SECRET_KEY, {
  baseUrl: 'https://api-epay.dev.n1co.dev', // desarrollo
});
const app = express();

// El webhook va antes de express.json(): necesita el cuerpo crudo.
app.post('/webhooks/n1co', express.raw({ type: 'application/json' }), (req, res) => {
  let event;
  try {
    event = N1co.webhooks.constructEvent(req.body, req.headers, process.env.N1CO_WEBHOOK_SECRET);
  } catch (err) {
    if (err instanceof WebhookSignatureError) return res.status(400).send('invalid signature');
    throw err;
  }
  if (event.type === 'payment_intent.succeeded') {
    // Entrega el pedido de event.data.object.reference; descarta los event.id repetidos.
  }
  res.sendStatus(204);
});

app.use(express.json());

app.post('/checkout/payment-intent', async (req, res) => {
  // El monto sale de tu pedido, nunca del navegador.
  const intent = await n1co.paymentIntents.create({
    amount: 1250,
    currency: 'USD',
    customer: { id: 'cust-1001', name: 'Ana López', email: 'ana.lopez@example.com', phone: '+503 7000-1234' },
    reference: 'order-8812',
  });
  res.json({ client_secret: intent.client_secret });
});

app.listen(3000);

El paquete trae ES modules y CommonJS, sin dependencias: const { N1co } = require('@n1co/epay');. La página confirma el pago como en Cobrar.

Webhooks con NestJS#

Activa rawBody al crear la app y verifica req.rawBody:

webhooks.controller.ts
// main.ts: const app = await NestFactory.create(AppModule, { rawBody: true });
import { BadRequestException, Controller, Headers, HttpCode, Post, Req } from '@nestjs/common';
import type { RawBodyRequest } from '@nestjs/common';
import type { Request } from 'express';
import { N1co, WebhookSignatureError } from '@n1co/epay';

@Controller('webhooks')
export class WebhooksController {
  @Post('n1co')
  @HttpCode(204)
  handle(@Req() req: RawBodyRequest<Request>, @Headers() headers: Record<string, string>) {
    try {
      const event = N1co.webhooks.constructEvent(req.rawBody!, headers, process.env.N1CO_WEBHOOK_SECRET!);
      // Procesa event.type…
    } catch (err) {
      if (err instanceof WebhookSignatureError) throw new BadRequestException();
      throw err;
    }
  }
}

Errores#

Cada error del SDK tiene code (el código estable del API), status, details y requestId. Decide con code, nunca con el mensaje.

import { isN1coError } from '@n1co/epay';

try {
  await n1co.paymentIntents.create(params);
} catch (err) {
  if (!isN1coError(err)) throw err;
  if (err.status === 400) console.log(err.details?.field, err.details?.code);
  else console.log(err.code, err.status, err.requestId);
}

Los códigos están en Errores. Incluye el requestId al contactar a n1co.

Lo que suele fallar#

  • Parsear el cuerpo antes de verificar. Con express.json() en la ruta del webhook, la firma no coincide. Usa express.raw() o rawBody: true.
  • Reintentar un POST a ciegas. El SDK nunca reintenta un POST: el API no tiene claves de idempotencia. Tras un ConnectionError, lee el intent o espera su webhook antes de crear otro.
  • instanceof con ESM y CommonJS a la vez. Si tu app carga las dos copias, usa isN1coError(err) con code y status.
  • Omitir baseUrl. Sin él, el cliente usa el host de producción, que llega con el lanzamiento a producción.

Las opciones del cliente, los métodos, los tipos y los reintentos de GET están en el README del paquete.