Skip to content

Webhooks e IPN / APN ​

A SOAMTAR notifica o seu servidor em tempo real através de webhooks, também chamados IPN (Instant Payment Notification) ou APN (Automated Payment Notification).

  • IPN: notificação imediata assim que o pagamento é confirmado (por ex. payment.success).
  • APN: notificações automáticas para as alterações de estado secundárias e os eventos do ciclo de vida (por ex. payment.expired).

Os envios são assinados (HMAC-SHA256), idempotentes por evento, repetidos com espera exponencial, e o segredo pode ser rodado sem interrupção.

Eventos ​

EventoQuando
payment.successO Orange Money Serra Leoa confirmou o pagamento (o cliente introduziu o seu PIN)
payment.failedO Orange Money recusou o pagamento, devolveu um erro ou esgotou o tempo de espera
payment.cancelledO pagador cancelou
payment.expiredO prazo de pagamento terminou sem conclusão
payment.refundedOs reembolsos cobrem agora a totalidade do pagamento (data.refund = o reembolso)
payment.partially_refundedUm reembolso parcial foi concluído (data.refund = o reembolso)
payout.success / payout.failed / payout.cancelledResultado de um pagamento de saída para uma carteira Orange Money
refund.success / refund.failedResultado de um reembolso (data = o 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 um pedido de levantamento do seu saldo

Escolhe os eventos a que cada endpoint está subscrito (Painel → Webhooks).

Cada reembolso gera o seu próprio conjunto de eventos: refund.success + payment.refunded / payment.partially_refunded, ou refund.failed. Ver Reembolsos (em inglês).

O evento webhook.test (enviado pelo botão de teste do painel e por POST /v1/webhooks/test) é entregue a todos os seus endpoints ativos independentemente das subscrições; não é possível subscrevê-lo.

Configuração ​

Painel → Programadores → Webhooks → Add endpoint

  • URL: deve ser HTTPS público.
  • Eventos: subconjunto da lista acima.
  • Segredo: gerado aleatoriamente. Guarde-o no seu servidor.

Um endpoint é suspenso automaticamente após várias falhas seguidas (limiar interno).

callback_url por transação ​

Ao criar um pagamento (POST /v1/payments) ou um pagamento de saída (POST /v1/payouts) pode passar um callback_url HTTPS opcional. A SOAMTAR envia para esse URL todos os eventos dessa transação, com o mesmo payload, a mesma assinatura X-Soamtar-Signature e as mesmas repetições que para os seus endpoints.

  • A assinatura usa o segredo do seu endpoint de webhook ativo mais antigo do mesmo ambiente (sandbox ou live). Precisa portanto de pelo menos um endpoint registado; sem nenhum, o callback_url não é chamado.
  • Se o callback_url for igual ao URL de um endpoint registado, o evento não é enviado duas vezes.

Conteúdo do webhook ​

Corpo JSON enviado para o seu URL (formato por defeito):

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 é o identificador da transação SOAMTAR; data.merchant_reference é o merchant_ref que enviou.

Assinatura ​

Cada pedido inclui o cabeçalho:

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

Durante uma rotação do segredo, podem coexistir duas assinaturas:

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

Verificação ​

  1. Extraia o carimbo temporal t e a assinatura v1 (e v2, se existir).
  2. Verifique que t é recente (dentro da janela de tolerância).
  3. Recalcule expected = HMAC-SHA256(t + "." + raw_body, secret).
  4. Compare em tempo constante (hash_equals em PHP, crypto.timingSafeEqual em Node).
  5. Se existir uma v2, aceite quando v1 OU v2 corresponder.
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');

Tolerância

O exemplo usa 300 segundos, um limite razoável que corresponde à tolerância aplicada pela SOAMTAR.

Cabeçalhos adicionais ​

CabeçalhoSignificado
X-Soamtar-TimestampCarimbo temporal da assinatura (mesmo valor que t)
X-Soamtar-EventTipo de evento (payment.success, ...)
X-Soamtar-DeliveryIdentificador da tentativa de entrega, útil nos registos
User-AgentSoamtar-Webhooks/1.0 (+https://app.so-amtar.com/docs/webhooks)

Idempotência no seu servidor ​

O mesmo evento pode chegar várias vezes (repetição após um tempo de espera esgotado, reenvio após um incidente). Elimine duplicados segundo o par (event, data.id) e ignore os repetidos.

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

Repetições e entrega ​

  • 5 tentativas no máximo: o envio inicial e depois repetições após 60 s, 5 min, 30 min e 2 h.
  • 2xx = sucesso, a SOAMTAR para.
  • 3xx = considerado falha (o seu endpoint deve responder 2xx diretamente).
  • 4xx, 5xx ou tempo esgotado = falha, é agendada uma repetição.
  • Após várias falhas seguidas, o endpoint é suspenso e recebe um email.

Pode reenviar uma entrega manualmente em Painel → Webhook logs → Resend.

Rotação do segredo sem interrupção ​

  1. Painel → Webhooks → Rotate secret. A SOAMTAR mostra o novo segredo (copie-o já: não volta a ser mostrado).
  2. Durante a janela de rotação, cada payload pode ser assinado com os dois segredos.
  3. Atualize o seu verificador para aceitar v1 OU v2.
  4. Clique em Promote: o novo segredo passa a ser o atual e o anterior é invalidado.

Testar um webhook ​

Use o painel para enviar um evento de teste assinado com o seu segredo real (Painel → Webhooks → Test), ou POST /v1/webhooks/test com a sua chave secreta. Ambos enviam o evento webhook.test, entregue a todos os seus endpoints ativos independentemente das subscrições.

Se perder um webhook ​

  1. Consulta ativa: POST /v1/payments/{id}/verify faz a SOAMTAR consultar o Orange Money e voltar a emitir o evento se a transição tiver ocorrido.
  2. Uma reconciliação automática interna recupera as transações bloqueadas: normalmente não tem de fazer nada.

SOAMTAR Payment API Documentation.