Merchant Generic Callback
El Merchant Generic Callback es la solicitud HTTP saliente que Migo envía a un endpoint del comercio cuando una transacción alcanza un estado terminal. Es el segundo contrato que tu backend implementa para integrar Payment Link: tu backend crea el link (vía POST /transactions o POST /transactions-hook) y luego recibe el resultado de la transacción en la URL que registras con Migo al momento de integrar.
Para la mayoría de integraciones Migo envía un payload JSON estándar documentado abajo. El despacho está dirigido por configuración por cliente, así que el método HTTP, URL, headers, body y timeout pueden personalizarse cuando es necesario; el payload estándar cubre el caso común y es lo que deberías diseñar como base de tu handler salvo que hayas acordado un shape personalizado con Migo.
El Merchant Generic Callback es un flujo separado: es la notificación saliente del producto Payment Link. No firma el body con HMAC y no sigue semánticas de reintento estandarizadas. Léelo por su cuenta.
Cómo configurar tu callback
Hay dos formas de configurar el callback:
- Configuración asistida por Migo. Envía la información a Migo vía ticket de soporte o tu contacto de integración para que Migo registre tu endpoint.
- Configuración self-service. Si tu comercio tiene acceso habilitado a Client Config Management, puedes actualizar
config.callbackdesde el flujo de Update Client Config.
Para la configuración asistida, envía:
- URL del endpoint. La URL de tu lado que recibirá las notificaciones
POST(por ejemplohttps://api.yourdomain.com/webhooks/transactions). - Headers de seguridad (opcional). Cualquier header HTTP personalizado que Migo deba incluir para que puedas autenticar la solicitud — típicamente un
x-api-keyestático o unAuthorization: Bearer <token>. Por defecto Migo no envía headers extra; debes proveerlos explícitamente si los necesitas.
Configuración self-service vía Client Config Management
Cuando el acceso está habilitado para tu comercio, el flujo de Update Client Config de Client Config Management (PUT /properties en esa superficie) puede persistir la configuración del callback en la estructura existente de Client Config. Envía callback dentro de config; si no envías callback, Update Client Config mantiene el comportamiento anterior y solo actualiza las propiedades que incluiste.
status define los estados transaccionales configurados para el callback. La aplicación efectiva del filtro depende del mecanismo de callback habilitado para el cliente. No lo pongas dentro de callback.data.status: se persiste como callback.status.
Ejemplo de request:
{
"client": "merchant-slug",
"source": "merchant-portal",
"config": {
"callback": {
"enabled": true,
"type": "generic",
"functionName": "generic",
"status": ["approved", "denied"],
"data": {
"url": "https://merchant.example.com/callback",
"method": "POST",
"headers": {
"x-api-key": "<merchant-secret>"
},
"data": {
"reference": "{{reference}}",
"uid": "{{uid}}",
"country": "{{country}}",
"currency": "{{currency}}",
"channel": "{{channel}}",
"status": "{{status}}",
"createdAt": "{{createdAt}}",
"amount": "{{total}}",
"externalId": "{{externalId}}"
},
"extraData": {
"source": "client-config"
}
}
}
}
}
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
config.callback.enabled | boolean | Sí | Habilita la configuración de callback para el cliente. |
config.callback.type | string | Sí | Tipo de callback configurado. Para este contrato usa generic. |
config.callback.functionName | string | Condicional | Nombre de función o mecanismo configurado para el callback. Para callback genérico puede usarse generic; otros mecanismos pueden requerir un valor acordado con Migo. |
config.callback.status | array de strings | Sí | Estados transaccionales configurados para el callback. Debe ser un array no vacío con estados soportados, por ejemplo approved y denied. Se persiste como callback.status. |
config.callback.data.url | string | Sí | URL absoluta de tu receiver. Debe ser válida y no vacía. |
config.callback.data.method | string | Sí | Método HTTP que Migo usará para llamar tu endpoint. Debe ser un método soportado por la configuración de callback. |
config.callback.data.headers | object | Sí | Headers que Migo enviará al receiver. Debe ser un objeto; usa placeholders o valores estáticos solo cuando estén acordados para tu integración. |
config.callback.data.data | object | Sí | Template del body que Migo resolverá contra la transacción antes de enviar el callback. Debe ser un objeto. |
config.callback.data.extraData | object | No | Datos adicionales de configuración para el mecanismo de callback. |
Validaciones de configuración:
urldebe ser una URL válida y no vacía.methoddebe ser un método HTTP soportado.headersdebe ser un objeto.datadebe ser un objeto.extraDataes opcional.statusdebe ser un array no vacío con estados soportados.- Si
callbackes inválido, la actualización no se persiste.
Los headers como Authorization, x-api-key, token, secret, password o apiKey pueden contener credenciales. No deben exponerse completos en logs, tickets, capturas o ejemplos compartidos; usa valores enmascarados o placeholders cuando los documentes.
Cuándo se dispara el callback
Migo emite el callback al final de la transacción, solo para estados terminales:
| Origen del trigger | Estados que disparan el callback |
|---|---|
Flujos de pago (finalUpdate) | approved, denied, refunded, reversed |
| Flujos de servicios / créditos (despacho directo) | approved, denied |
Puedes recibir el callback por cualquiera de los dos caminos dependiendo de qué flujo de Migo manejó la transacción. Tu handler no debe asumir una sola fuente; diseñalo para ser idempotente sobre (uid, status).
Payload estándar
Migo envía un POST con Content-Type: application/json a la URL que registraste. El body es:
{
"reference": "ORDER-98765",
"uid": "ak_D3b0ETlw3HwPmQ3MNK",
"country": "GT",
"currency": "GTQ",
"channel": "WhatsApp",
"status": "approved",
"amount": 150,
"externalId": "ext_auth_123",
"createdAt": "05/03/2026 07:22:32",
"transactionId": "txn_1029384756",
"paymentMethodType": "credit_card"
}
| Campo | Tipo | Largo máx. | Descripción | Ejemplo |
|---|---|---|---|---|
reference | string | 100 | Referencia emitida por Migo para la transacción. Devuelta en la respuesta de POST /transactions; para POST /transactions-hook se entrega aquí. | ORDER-98765 |
uid | string | 100 | Identificador único de la transacción emitido por Migo. Mismo valor que devuelve la llamada de creación de link. | ak_D3b0ETlw3Hw... |
country | string | 2 | Código ISO 3166-1 alpha-2 del país donde se realizó la operación. | GT |
currency | string | 3 | Código de moneda ISO 4217. | GTQ |
channel | string | 100 | Canal o plataforma donde originó la transacción. | WhatsApp |
status | string | 20 | Estado terminal de la transacción. Uno de approved, denied, reversed, refunded. | approved |
amount | integer | 12 | Monto total de la transacción en la unidad mínima de la moneda. | 150 |
externalId | string | 100 | Código de autorización o identificador externo devuelto por el procesador de pago. | ext_auth_123 |
createdAt | string | 19 | Marca temporal de cuándo se creó originalmente la transacción, formato DD/MM/YYYY HH:MM:SS. | 05/03/2026 07:22:32 |
transactionId | string | 100 | Id interno de la transacción que usa el procesador. | txn_1029384756 |
paymentMethodType | string | 100 | Tipo de método de pago usado para la transacción (por ejemplo credit_card, bank_transfer). | credit_card |
Autenticando la solicitud en tu lado
El Merchant Generic Callback no firma el body con HMAC. La autenticidad se delega al comercio mediante los headers que registraste con Migo — típicamente un API key estático en Authorization o un header personalizado (por ejemplo x-api-key).
Verifica el header en tu handler antes de procesar el body. Si necesitas garantías más fuertes (HMAC, JWT firmado, mTLS), coordínalo con Migo para extender la configuración de tu cliente; no es parte del contrato estándar.
Esqueleto de receiver (Node.js / Express)
import express from 'express';
const app = express();
const MIGO_API_KEY = process.env.MIGO_WEBHOOK_API_KEY;
app.post(
'/webhooks/transactions',
express.json({ limit: '1mb' }),
async (req, res) => {
// 1. Authenticate the request via the header you registered with Migo.
if (req.header('x-api-key') !== MIGO_API_KEY) {
return res.status(401).send('invalid credentials');
}
const { uid, status, reference, amount, currency } = req.body;
// 2. Idempotency: dedupe on (uid, status). Migo may deliver the same
// notification more than once if a queued message is replayed.
if (await alreadyProcessed(uid, status)) {
return res.status(200).json({ ack: 'duplicate' });
}
// 3. Process based on req.body.status:
// - "approved" -> mark order as paid, fulfill, etc.
// - "denied" -> mark order as failed, notify the customer.
// - "refunded" -> reverse fulfillment, update accounting.
// - "reversed" -> idem.
await processCallback(req.body);
// 4. Respond 2xx so Migo records a successful delivery.
res.status(200).json({ ack: 'received' });
},
);
Recomendaciones:
- Responde siempre
2xxuna vez tu procesamiento sea durable; fallar la respuesta hace que Migo registre un error de entrega. - Haz tu handler idempotente sobre
(uid, status). Trata las entregas repetidas como un caso normal. - Valida el schema contra el payload estándar de arriba; extiéndelo solo si tienes acordado un payload personalizado con Migo.
- Mantén el procesamiento rápido o muévelo a una cola dentro de tu sistema. Migo aplica un timeout por defecto de 25 segundos.
Comportamiento ante error
Migo registra los fallos de entrega y persiste los pasos request / response para que el operador pueda reprocesar manualmente si es necesario. Las semánticas de reintento dependen del camino de despacho que Migo seleccionó para tu cliente (algunos flujos pasan la solicitud a un mecanismo de despacho interno con su propia política de reintentos; otros hacen una sola llamada directa). Trata cada entrega como best-effort y apóyate en la idempotencia de tu lado.
Avanzado: templates de payload personalizado
La mayoría de los comercios no necesita esta sección. Documenta la configuración por cliente que dirige el despacho y permite personalizar la solicitud cuando el payload estándar no es suficiente.
El callback se configura por cliente en el documento ClientConfig.callback. El shape es:
interface GenericCallback {
// HTTP method Migo uses to call your endpoint. Typically "POST".
method: string;
// Merchant endpoint URL. Supports template placeholders resolved with
// transaction values via getValues().
url: string;
// Optional template headers. Each value can include placeholders resolved
// with transaction values via getDataCallback().
headers?: { [key: string]: string };
// Optional template body. Recursively resolved with transaction values
// via getDataCallback().
data?: { [key: string]: any };
// Optional extra configuration for the callback mechanism.
extraData?: { [key: string]: any };
// Optional timeout in milliseconds. Defaults to 25000.
timeout?: number;
}
El wrapper que vive en la configuración de cliente añade estos campos:
| Campo | Tipo | Descripción |
|---|---|---|
enabled | boolean | Debe ser true para que el callback se dispare. |
type | string | Uno de "generic", "queue", "lambda". Selecciona el mecanismo de despacho — ver Variantes de type. |
functionName | string | Requerido cuando type es "lambda". Nombre de la función que Migo invoca para entregar el callback. El literal %env% se reemplaza por el ambiente de despliegue. |
status | string[] | Estados transaccionales configurados para el callback. La aplicación efectiva del filtro depende del mecanismo de callback habilitado para el cliente. |
notifyExpiration | boolean | Opcional. Controla si las transacciones expiradas también notifican; el comportamiento depende del servicio originario. |
data | object | El payload GenericCallback de arriba (method, url, headers, body, timeout). |
Variantes de type
type | Implementación | Mecanismo |
|---|---|---|
"generic" | V1 | Migo entrega la solicitud del callback a un mecanismo de despacho interno que hace la llamada HTTP a tu endpoint. |
"queue" | V1 | Mismo camino de despacho que "generic" — usa el mecanismo de despacho interno. |
"lambda" | V1 | Migo invoca una función nombrada específica (functionName) responsable de entregar el callback a tu endpoint. Úsalo solo si acordaste con Migo una función de despacho personalizada. |
(sin campo type) | V2 | Migo llama tu endpoint directamente con un solo request axios desde el servicio originario. |
Para el comercio, el request que aterriza en tu endpoint se ve igual en V1 (generic / queue) y V2: la URL, método, headers y body resueltos desde tu configuración. Las diferencias son operativas (cola, reintentos, manejo de errores — ver Comportamiento ante error).
Reglas de templating
Los valores de configuración son templates. Migo sustituye placeholders contra el objeto transaction antes de enviar la solicitud.
getValues(template, transaction)se aplica al campourl. Sustituye placeholders como{{uid}},{{reference}},{{status}}con sus valores en la transacción.getDataCallback(template, transaction)se aplica aheadersydata(el body). Recorre el objeto recursivamente y sustituye placeholders dentro de cada valor string.
Los placeholders disponibles en runtime corresponden a campos del objeto transaction que Migo persiste para el pago, incluyendo (no exhaustivo): uid, reference, status, total, currency, customKeys, userId, clientCode, y los timestamps emitidos por Migo. Solo los campos que existen en la transacción se sustituyen; los faltantes se dejan como el placeholder literal, así que diseña tus templates alrededor de los campos que acordaste con Migo.
Ejemplo de configuración
{
"enabled": true,
"type": "generic",
"functionName": "generic",
"status": ["approved", "denied"],
"data": {
"method": "POST",
"url": "https://api.merchant.com/migo/callback?trx={{uid}}",
"headers": {
"Authorization": "Bearer <merchant-static-api-key>",
"Content-Type": "application/json"
},
"data": {
"transactionUid": "{{uid}}",
"reference": "{{reference}}",
"status": "{{status}}",
"amount": "{{total}}",
"currency": "{{currency}}",
"metadata": "{{customKeys}}"
},
"extraData": {
"source": "client-config"
},
"timeout": 30000
}
}
Cuando una transacción con uid = "abcd1234efgh" alcanza approved, Migo envía un POST a https://api.merchant.com/migo/callback?trx=abcd1234efgh con el header Authorization de arriba y un body JSON donde cada placeholder se resuelve contra la transacción.
Detalles de despacho
| Origen del trigger | Mecanismo |
|---|---|
| Flujos de pago (V1) | Migo entrega la solicitud a un mecanismo de despacho interno con su propia política de reintentos a nivel de infraestructura. |
| Flujos de servicios / créditos (V2) | Migo hace una sola llamada directa desde el servicio originario. Ante error HTTP o timeout, el fallo se registra y la solicitud / respuesta se persisten como pasos de la transacción; no hay reintento dentro del proceso. |
Las semánticas exactas de reintento son operativas y pueden evolucionar. Coordina con Migo si tu negocio requiere garantías de entrega específicas.
Relacionado
- Crear un Payment Link — el contrato entrante que llamas para obtener la URL del webview.
- Checkout hospedado (webview) — la experiencia de pago del cliente para Payment Link.