Saltar al contenido principal

Tu primer cobro en sandbox

En sandbox el cobro pasa por un simulador de proveedores: recorres el flujo completo, con webhooks incluidos, sin mover dinero real.

Lo que necesitas​

QuéFormatoDe dónde sale
URL base de la APIhttps://api.pagos.example.comla que te entregamos al darte acceso
API key sandboxpay_sb_<prefijo>_<secreto>se entrega una vez al habilitar tu aplicación; después puedes crear más con POST /v1/admin/api-credentials
Id de tu comerciomrc_…viene junto con tu acceso
BASE=https://api.pagos.example.com
KEY=pay_sb_…
MERCHANT=mrc_…

Todas las llamadas llevan Authorization: Bearer $KEY. Las que crean o cambian algo (crear intent, pedir checkout, devolver) llevan además un Idempotency-Key único por operación: ver Idempotencia.

1. Crear el payment intent​

curl -s -X POST "$BASE/v1/payment-intents" \
-H "Authorization: Bearer $KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H 'Content-Type: application/json' \
-d '{
"merchant_account_id": "'"$MERCHANT"'",
"external_reference": "order-12345",
"amount": 25990,
"currency": "CLP",
"description": "Compra 12345",
"provider": "auto",
"return_url": "https://consumer.example.com/pagos/retorno",
"metadata": { "canal": "web" }
}'
{
"id": "payi_01…",
"status": "CREATED",
"amount": 25990,
"currency": "CLP",
"external_reference": "order-12345",
"created_at": "2026-09-26T18:00:00.000Z"
}

2. Pedir el checkout​

curl -s -X POST "$BASE/v1/payment-intents/payi_01…/checkout" \
-H "Authorization: Bearer $KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H 'Content-Type: application/json' \
-d '{ "provider": "auto" }'
{
"payment_intent_id": "payi_01…",
"status": "REQUIRES_ACTION",
"provider": "transbank_webpay_plus",
"checkout": {
"type": "REDIRECT",
"url": "https://…",
"method": "POST",
"fields": { "token_ws": "…" }
},
"expires_at": "2026-09-26T18:10:00.000Z"
}

3. Redirigir al comprador​

Lo que haces depende de checkout.method:

  • POST (Webpay Plus): arma un formulario con los fields y envíalo a checkout.url.
  • GET (Mercado Pago): redirige el navegador a checkout.url.
<form id="pago" method="POST" action="{{checkout.url}}">
<input type="hidden" name="token_ws" value="{{checkout.fields.token_ws}}" />
</form>
<script>document.getElementById('pago').submit();</script>

Al terminar, el comprador vuelve a tu return_url con ?payment_intent_id=payi_… (y, en Mercado Pago, también &status=…). No des el pago por bueno por el retorno: usa el webhook o consulta el estado. En sandbox eliges el resultado en la página del simulador: ver Sandbox.

4. Enterarte del resultado​

Registra una URL de tu sistema para recibir los eventos (una vez por aplicación). El secreto para verificar la firma se muestra solo en esta respuesta:

curl -s -X POST "$BASE/v1/admin/webhook-endpoints" \
-H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
-d '{ "url": "https://consumer.example.com/webhooks/pagos" }'
# → { "success": true, "data": { "id": "whe_…", "url": "…", "events": [], "secret": "a3f1…", … } }

Cada evento llega firmado. Verifícalo como se explica en Webhooks. También puedes consultar el estado en cualquier momento:

curl -s "$BASE/v1/payment-intents/payi_01…" -H "Authorization: Bearer $KEY"
{
"id": "payi_01…",
"status": "AUTHORIZED",
"provider": "transbank_webpay_plus",
"amount": 25990,
"currency": "CLP",
"payment": { "id": "pay_01…", "status": "AUTHORIZED", "authorization_code": "1213", "authorized_at": "…" },
"timeline": [
{ "type": "payment_intent.created", "at": "…" },
{ "type": "payment.checkout.started", "at": "…" },
{ "type": "payment.authorized", "at": "…" }
]
}

Lo mismo en tu lenguaje​

import { randomUUID } from 'node:crypto';

const BASE = 'https://api.pagos.example.com';
const KEY = process.env.PAGOS_API_KEY;

async function call(method, path, body) {
const res = await fetch(`${BASE}${path}`, {
method,
headers: {
Authorization: `Bearer ${KEY}`,
'Content-Type': 'application/json',
...(method === 'POST' ? { 'Idempotency-Key': randomUUID() } : {}),
},
body: body ? JSON.stringify(body) : undefined,
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error.code}: ${data.error.message} (${data.error.request_id})`);
return data;
}

const intent = await call('POST', '/v1/payment-intents', {
merchant_account_id: process.env.PAGOS_MERCHANT,
external_reference: 'order-12345',
amount: 25990,
currency: 'CLP',
provider: 'auto',
return_url: 'https://consumer.example.com/pagos/retorno',
});
const { checkout } = await call('POST', `/v1/payment-intents/${intent.id}/checkout`, { provider: 'auto' });
// checkout.method === 'POST' → formulario con checkout.fields; 'GET' → redirect a checkout.url
Reintentos

Si una llamada falla por red o timeout, reintenta con el mismo Idempotency-Key: obtendrás la misma respuesta sin crear nada dos veces. En los ejemplos se genera uno nuevo por llamada para simplificar; en producción guárdalo junto a tu orden.

Listar pagos​

curl -s "$BASE/v1/payments?status=AUTHORIZED&limit=20" -H "Authorization: Bearer $KEY"
# → { "items": [ … ], "next_cursor": "…" } — pasa next_cursor en ?cursor= para la página siguiente

Filtros: status, from y to (fechas ISO 8601), cursor y limit (entre 1 y 100, por defecto 20).

Siguientes pasos​