Skip to content

Webhooks e IPN / APN ​

SOAMTAR notifica a su servidor en tiempo real mediante webhooks, también llamados IPN (Instant Payment Notification) o APN (Automated Payment Notification).

  • IPN: notificación inmediata en cuanto se confirma el pago (p. ej. payment.success).
  • APN: notificaciones automáticas para los cambios de estado secundarios y los eventos del ciclo de vida (p. ej. payment.expired).

Los envíos están firmados (HMAC-SHA256), son idempotentes por evento, se reintentan con espera exponencial y el secreto puede rotarse sin interrupción.

Eventos ​

EventoCuándo
payment.successOrange Money Sierra Leona confirmó el pago (el cliente introdujo su PIN)
payment.failedOrange Money rechazó el pago, devolvió un error o agotó el tiempo de espera
payment.cancelledEl pagador canceló
payment.expiredTerminó el plazo de pago sin que se completara
payment.refundedLos reembolsos cubren ahora la totalidad del pago (data.refund = el reembolso)
payment.partially_refundedUn reembolso parcial se ha completado (data.refund = el reembolso)
payout.success / payout.failed / payout.cancelledResultado de un pago saliente a un monedero Orange Money
refund.success / refund.failedResultado de un reembolso (data = el objeto refund + transaction_reference, merchant_reference, is_test)
payout_request.submitted / payout_request.approved / payout_request.rejected / payout_request.completed / payout_request.failedCiclo de vida de una solicitud de retiro de su saldo

Usted elige los eventos a los que se suscribe cada endpoint (Panel → Webhooks). Cada reembolso genera su propio juego de eventos: refund.success + payment.refunded / payment.partially_refunded, o refund.failed. Ver Reembolsos (en inglés).

El evento webhook.test (enviado por el botón de prueba del panel y por POST /v1/webhooks/test) se entrega a todos sus endpoints activos sin importar sus suscripciones; no se puede suscribir a él.

Configuración ​

Panel → Desarrolladores → Webhooks → Add endpoint

  • URL: debe ser HTTPS público.
  • Eventos: subconjunto de la lista anterior.
  • Secreto: generado aleatoriamente. Guárdelo en su servidor.

Un endpoint se suspende automáticamente tras varios fallos seguidos (umbral interno).

callback_url por transacción ​

Al crear un pago (POST /v1/payments) o un pago saliente (POST /v1/payouts) puede pasar un callback_url HTTPS opcional. SOAMTAR envía a esa URL todos los eventos de esa transacción, con el mismo payload, la misma firma X-Soamtar-Signature y los mismos reintentos que para sus endpoints.

  • La firma usa el secreto de su endpoint de webhook activo más antiguo del mismo entorno (sandbox o live). Necesita por tanto al menos un endpoint registrado; sin ninguno, el callback_url no se llama.
  • Si el callback_url es igual a la URL de un endpoint registrado, el evento no se envía dos veces.

Contenido del webhook ​

Cuerpo JSON enviado a su URL (formato por defecto):

json
{
  "event": "payment.success",
  "created": "2026-10-21T09:41:12+00:00",
  "data": {
    "id": "0e85b3c2-7c4f-4f1d-9b3a-1c8a2d4e5f6a",
    "reference": "TXN-1A2B3C4D-ABCDEF123456",
    "merchant_reference": "ORDER-001",
    "provider_reference": "CI190304.1139.A01071",
    "amount": "150.00",
    "fee_amount": "2.25",
    "net_amount": "147.75",
    "currency": "SLE",
    "status": "success",
    "is_test": false,
    "paid_at": "2026-10-21T09:41:10+00:00",
    "metadata": { "cart_id": "abc-123" }
  }
}

data.id es el identificador de la transacción SOAMTAR; data.merchant_reference es el merchant_ref que usted envió.

Firma ​

Cada solicitud lleva la cabecera:

X-Soamtar-Signature: t=<unix>,v1=<hex_hmac_sha256>

Durante una rotación de secreto pueden coexistir dos firmas:

X-Soamtar-Signature: t=<unix>,v1=<hmac_current_secret>,v2=<hmac_next_secret>

Verificación ​

  1. Extraiga la marca de tiempo t y la firma v1 (y v2 si existe).
  2. Compruebe que t es reciente (dentro de la ventana de tolerancia).
  3. Recalcule expected = HMAC-SHA256(t + "." + raw_body, secret).
  4. Compare en tiempo constante (hash_equals en PHP, crypto.timingSafeEqual en Node).
  5. Si hay una v2, acepte cuando coincida v1 O v2.
php
$header = $_SERVER['HTTP_X_SOAMTAR_SIGNATURE'] ?? '';
$body = file_get_contents('php://input');
$secrets = [getenv('SOAMTAR_WEBHOOK_SECRET'), getenv('SOAMTAR_WEBHOOK_SECRET_NEXT')];

if (! preg_match('/t=(\d+)/', $header, $tm)) throw new RuntimeException('bad sig');
$ts = (int) $tm[1];
if (abs(time() - $ts) > 300) throw new RuntimeException('expired');

$ok = false;
foreach (['v1', 'v2'] as $i => $version) {
    if (! preg_match("/{$version}=([a-f0-9]{64})/", $header, $vm)) continue;
    $secret = $secrets[$i] ?? null;
    if ($secret === null || $secret === '') continue;
    if (hash_equals(hash_hmac('sha256', "{$ts}.{$body}", $secret), $vm[1])) {
        $ok = true; break;
    }
}
if (! $ok) throw new RuntimeException('invalid');

Tolerancia

El ejemplo usa 300 segundos, un límite razonable que coincide con la tolerancia que aplica SOAMTAR.

Cabeceras adicionales ​

CabeceraSignificado
X-Soamtar-TimestampMarca de tiempo de la firma (mismo valor que t)
X-Soamtar-EventTipo de evento (payment.success, ...)
X-Soamtar-DeliveryIdentificador del intento de entrega, útil en los registros
User-AgentSoamtar-Webhooks/1.0 (+https://app.so-amtar.com/docs/webhooks)

Idempotencia en su servidor ​

El mismo evento puede llegar varias veces (reintento tras un tiempo de espera, reenvío tras un incidente). Elimine duplicados según el par (event, data.id) e ignore los repetidos.

sql
CREATE UNIQUE INDEX uq_inbound_event ON your_webhook_log(event, transaction_id);

Reintentos y entrega ​

  • 5 intentos como máximo: el envío inicial y luego reintentos tras 60 s, 5 min, 30 min y 2 h.
  • 2xx = éxito, SOAMTAR se detiene.
  • 3xx = se considera un fallo (su endpoint debe responder 2xx directamente).
  • 4xx, 5xx o tiempo agotado = fallo, se programa un reintento.
  • Tras varios fallos seguidos, el endpoint se suspende y recibe un correo.

Puede reenviar una entrega manualmente desde Panel → Webhook logs → Resend.

Rotación del secreto sin interrupción ​

  1. Panel → Webhooks → Rotate secret. SOAMTAR muestra el nuevo secreto (cópielo ahora: no se vuelve a mostrar).
  2. Durante la ventana de rotación, cada payload puede firmarse con los dos secretos.
  3. Actualice su verificador para aceptar v1 O v2.
  4. Haga clic en Promote: el nuevo secreto pasa a ser el actual y el anterior queda invalidado.

Probar un webhook ​

Use el panel para enviar un evento de prueba firmado con su secreto real (Panel → Webhooks → Test), o POST /v1/webhooks/test con su clave secreta. Ambos envían el evento webhook.test, entregado a todos sus endpoints activos sin importar sus suscripciones.

Si se pierde un webhook ​

  1. Consulta activa: POST /v1/payments/{id}/verify hace que SOAMTAR consulte a Orange Money y vuelva a emitir el evento si la transición se produjo.
  2. Una conciliación automática interna recupera las transacciones bloqueadas: normalmente no tiene que hacer nada.

SOAMTAR Payment API Documentation.