n1coePayDocs Referencia del API

Cobrar

Cobra con payment intents. Tu servidor crea el intent, tu página lo confirma con n1co.js y el webhook te dice el resultado.

  1. Tu servidor crea un payment intent con POST /v1/payment_intents y entrega su client_secret a la página.
  2. Tu página confirma el pago con confirmPayment() de n1co.js, que muestra 3D Secure si el banco lo pide.
  3. Tu servidor entrega el pedido cuando el webhook payment_intent.succeeded lo confirma.

Primero, tu servidor crea el intent:

Crear un payment intent
curl -s -X POST https://api-epay.dev.n1co.dev/v1/payment_intents \
  -H "Authorization: Bearer $N1CO_SECRET_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "amount": 1250,
    "currency": "USD",
    "customer": {
      "id": "cust-1001",
      "name": "Ana López",
      "email": "ana.lopez@example.com",
      "phone": "+503 7000-1234"
    },
    "description": "Order 8812",
    "reference": "order-8812",
    "metadata": { "cart": "c-1029" },
    "page_origins": ["https://shop.example.com"]
  }'
201 Created
{
  "id": "pi_UGF5bWVudEludGVudEV4YW1w",
  "object": "payment_intent",
  "client_secret": "pi_UGF5bWVudEludGVudEV4YW1w_secret_U2VjcmV0RXhhbXBsZTAwMDAwMDAwMDAw",
  "status": "requires_payment_method",
  "amount": 1250,
  "currency": "USD",
  "description": "Order 8812",
  "reference": "order-8812",
  "metadata": { "cart": "c-1029" },
  "billing": null,
  "page_origins": ["https://shop.example.com"],
  "card": null,
  "payment_method": null,
  "next_action": null,
  "last_error": null,
  "attempts_remaining": 3,
  "authorization_code": null,
  "order_id": null,
  "cancellation_reason": null,
  "refund": null,
  "created_at": "2026-10-17T15:04:05Z",
  "updated_at": "2026-10-17T15:04:05Z",
  "succeeded_at": null,
  "canceled_at": null,
  "expires_at": "2026-10-17T16:04:05Z"
}

Luego, la página. Pide el client_secret una vez y lo usa en cada intento:

pagar.html
<!doctype html>
<html lang="es">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Pagar</title>
  </head>
  <body>
    <form id="payment-form">
      <div id="card"></div>
      <p id="payment-message" role="alert"></p>
      <button id="pay" type="submit" disabled>Pagar $12.50</button>
    </form>

    <script src="https://elements.n1co.dev/v1/n1co.js"></script>
    <script>
      const n1 = n1co('pk_3x4mpl3pUbl1shabl3k3yAAAAAAAAAAA');
      const card = n1.elements().create('card', { locale: 'es' });
      const pay = document.querySelector('#pay');
      const message = document.querySelector('#payment-message');

      // Tu endpoint crea el intent con tu llave secreta y devuelve solo su client_secret.
      const clientSecret = fetch('/checkout/payment-intent', { method: 'POST' })
        .then((r) => r.json())
        .then((body) => body.client_secret);

      card
        .on('change', ({ complete, error }) => {
          pay.disabled = !complete;
          message.textContent = error ? error.message : '';
        })
        .mount('#card');

      document.querySelector('#payment-form').addEventListener('submit', async (event) => {
        event.preventDefault();
        pay.disabled = true;
        message.textContent = '';
        try {
          const intent = await n1.confirmPayment({ client_secret: await clientSecret, card });
          if (intent.status === 'succeeded') {
            // Tu servidor confirma con el webhook antes de entregar el pedido.
            window.location.assign('/checkout/gracias');
          } else if (intent.status === 'processing') {
            message.textContent = 'Estamos confirmando tu pago. No vuelvas a pagar; te avisaremos.';
          } else if (intent.status === 'canceled') {
            message.textContent = 'Este pago ya no se puede completar. Vuelve a empezar tu compra.';
          } else {
            message.textContent = intent.last_error?.message ?? 'No se pudo completar el pago.';
            pay.disabled = false;
          }
        } catch (error) {
          message.textContent = error.message;
          pay.disabled = false;
        }
      });
    </script>
  </body>
</html>

Crear el payment intent#

  • amount va en unidades menores: 1250 son 12.50.
  • currency es un código ISO 4217 con dos decimales. Tu tienda cobra en una sola moneda, la de su primer pago exitoso.
  • customer lleva tu id del comprador, su nombre, su correo y su teléfono, como en Guardar una tarjeta.
  • billing es la dirección de facturación. n1co la pide para tarjetas de Estados Unidos y Canadá.
  • page_origins limita el intent a tus páginas, igual que en las sesiones de tarjeta.

El client_secret se muestra solo aquí. Un intent vence en una hora y tiene 3 intentos. Las reglas de cada campo están en la referencia del API.

Cobrar una tarjeta guardada#

Tu servidor adjunta la tarjeta con su id en payment_method. La tarjeta debe ser del cliente con ese customer.id. El navegador nunca elige ni ve las tarjetas guardadas.

Con una tarjeta guardada
curl -s -X POST https://api-epay.dev.n1co.dev/v1/payment_intents \
  -H "Authorization: Bearer $N1CO_SECRET_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "amount": 1250,
    "currency": "USD",
    "customer": { "id": "cust-1001", "name": "Ana López", "email": "ana.lopez@example.com", "phone": "+503 7000-1234" },
    "payment_method": "card_Q2FyZEV4YW1wbGUwMDAwMDAx"
  }'

La página confirma solo con el client_secret, sin card element:

const intent = await n1co('pk_3x4mpl3pUbl1shabl3k3yAAAAAAAAAAA').confirmPayment({ client_secret });
Error Qué pasó Qué hacer
404 card_not_found La tarjeta no es de ese cliente. Revisa los dos id.
409 card_unavailable La tarjeta ya no se puede usar. Cobra con el card element.

Una tarjeta nueva nunca se guarda al cobrar. Para guardarla, usa una sesión de tarjeta.

Lo que devuelve confirmPayment()#

n1co.js resuelve 3D Secure en su propia ventana y devuelve el intent. Revisa intent.status:

status Qué hacer en tu página
succeeded Muestra la confirmación.
processing Muestra que se está confirmando. No crees otro intent.
requires_payment_method Muestra last_error.message y deja reintentar.
canceled Crea un intent nuevo si el comprador quiere pagar.

Estos casos piden atención:

  • billing_required: pide país, estado y código postal, y confirma otra vez con billing.
  • card_unavailable: la tarjeta guardada dejó de servir; el comprador paga con el card element.
  • outcome_unknown: el intent queda en processing y n1co nunca cobra dos veces. Espera el webhook.

Los códigos de last_error están en Errores y los rechazos de la promesa en la referencia de n1co.js.

Confirmar en tu servidor#

Entrega el pedido solo cuando tu servidor recibe payment_intent.succeeded con firma válida, o lee el intent con GET /v1/payment_intents/{id} y está succeeded.

curl -s https://api-epay.dev.n1co.dev/v1/payment_intents/pi_UGF5bWVudEludGVudEV4YW1w \
  -H "Authorization: Bearer $N1CO_SECRET_KEY"
Evento Cuándo
payment_intent.succeeded Una vez, al aceptarse el cargo.
payment_intent.payment_failed Por cada intento fallido.
payment_intent.canceled Al cancelarse o vencer.

Estados, cancelación y reembolsos#

Estados de un payment intent Un intent nace en requires_payment_method. Al confirmar pasa a processing, que termina en succeeded, vuelve a requires_payment_method si el intento falla, o pasa a requires_action cuando el banco pide 3D Secure. Desde requires_action, una autenticación completada vuelve a processing y una fallida o vencida vuelve a requires_payment_method. Cancelar o vencer desde requires_payment_method o requires_action lleva a canceled. requires_payment_method processing succeeded requires_action canceled crear confirmar intento fallido aceptado 3D Secure autenticado falló o venció cancelar o vencer cancelar o vencer
succeeded y canceled son finales. Sin intentos restantes, el intent también termina en canceled.

Cancela un intent que ya no necesitas con POST /v1/payment_intents/{id}/cancel. Solo se cancela en requires_payment_method o requires_action.

Para devolver un pago succeeded, crea un reembolso.