Skip to content

Dépannage ​

Mon webhook n'arrive pas ​

Diagnostic ​

  1. Dashboard → Webhooks → Logs : la requête apparaît-elle ?
    • Non → l'événement n'a pas été déclenché. Vérifiez que la transaction a atteint success/failed/cancelled/expired. Forcez une vérification avec POST/v1/payments/{id}/verify.
    • Oui, avec un http_status non 2xx → votre serveur l'a refusée. Voir « Mon endpoint renvoie 4xx/5xx ».
    • Oui, avec error_message: "timeout" → votre serveur a mis plus de 15 secondes. Voir « Timeout ».

Mon endpoint renvoie 4xx/5xx ​

http_statusCause fréquente
400Parsing du corps : votre serveur lit le contenu avant de vérifier la signature
401 / 403Un middleware d'authentification bloque les requêtes externes — exemptez /webhooks/soamtar
404Mauvaise route — vérifiez l'URL exacte dans le dashboard
422Validation du framework — acceptez le contenu tel quel
500Erreur applicative — consultez vos logs
502 / 503Reverse proxy en panne ou application non démarrée

Correction : excluez votre route de webhook de la protection CSRF (ex. withoutMiddleware([VerifyCsrfToken::class]) sous Laravel) et de votre middleware d'authentification.

Timeout ​

Votre handler doit répondre 2xx en moins de 15 secondes. Pour un traitement long :

php
// Mauvais
public function webhook(Request $request) {
    $this->processOrder($request);     // 30 secondes
    $this->sendEmail($request);
    return response('ok');
}

// Bon
public function webhook(Request $request) {
    ProcessSoamtarWebhookJob::dispatch($request->all());
    return response('ok');             // immédiat
}

Mon serveur reçoit le webhook 5 fois ​

C'est un réessai, comportement attendu. Causes :

  1. Vous répondez 2xx mais après plus de 15 s → SOAMTAR a déjà enregistré un timeout
  2. Vous répondez autre chose que 2xx
  3. Vous répondez 2xx mais SOAMTAR ne reçoit jamais la réponse (réseau)

Correction : dédupliquez de votre côté sur le couple (event, data.id).

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

Signature de webhook invalide ​

Symptôme ​

Votre vérification HMAC échoue alors que vous utilisez le bon secret.

Cause 1 : corps modifié avant vérification ​

Express, Symfony et Laravel parsent le JSON automatiquement. Vous devez vérifier la signature sur le corps brut, avant tout parsing.

js
// Mauvais
app.use(express.json())   // parse le JSON
app.post('/webhook', (req, res) => {
  verify(req.body)        // req.body est déjà un objet, pas le corps brut
})

// Bon
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  verify(req.body.toString())   // brut
  const event = JSON.parse(req.body.toString())
})

Cause 2 : encodage du HMAC ​

Comparez de l'hexadécimal en minuscules des deux côtés (hash_hmac en PHP renvoie des minuscules ; vérifiez en Node). Utilisez hash_equals / crypto.timingSafeEqual, pas ===.

Cause 3 : horodatage expiré ​

|now - t| > 300s est rejeté. Vérifiez l'horloge de votre serveur (NTP). N'élargissez pas la tolérance pour traiter d'anciennes livraisons : renvoyez-les depuis Dashboard → Webhook logs → Resend (chaque tentative est re-signée avec un horodatage récent) ou utilisez la vérification verify.

Conflit d'Idempotency-Key ​

Symptôme ​

409 idempotency_conflict quand vous renvoyez la requête.

Diagnostic ​

bash
curl -X POST .../v1/payments \
  -H "Idempotency-Key: abc-123" \
  -d '{"merchant_ref":"ORDER-7","amount":"150.00","currency":"SLE"}'
# 201

curl -X POST .../v1/payments \
  -H "Idempotency-Key: abc-123" \
  -d '{"merchant_ref":"ORDER-7","amount":"150.00","currency":"SLE","description":"oops"}'
# 409 — corps différent

Correction :

  • Pour réessayer : même clé, exactement le même corps.
  • Pour une nouvelle requête : une nouvelle clé.

Bonne pratique ​

Générez la clé quand le client appuie sur « Payer » et conservez-la (stockage local / base de données) jusqu'au résultat final. Ne la régénérez pas à chaque réessai HTTP.

Transaction bloquée en pending ​

Diagnostic ​

bash
curl -X POST https://app.so-amtar.com/v1/payments/{id}/verify \
  -H "Authorization: Bearer sk_test_xxx"

La vérification verify interroge Orange Money et synchronise le statut. Si Orange Money confirme, la transaction avance et un webhook vous est envoyé.

Si rien ne change ​

  • Le client n'a pas confirmé la demande Orange Money avec son code PIN (la demande reste ouverte environ 5 minutes) : demandez-lui de vérifier son téléphone, ou lancez un nouveau paiement.
  • Orange Money subit un incident : renseignez-vous auprès du support.
  • La transaction expire automatiquement à expires_at (2 heures par défaut).
  • Toujours bloquée : écrivez à support@so-amtar.com avec la référence SOAMTAR (TXN-...).

Rembourser un paiement Orange Money ​

Utilisez POST/v1/refunds avec le transaction_id du paiement (voir Remboursements). Si l'appel échoue :

Code d'erreurCauseSolution
scope_insufficient (403)La clé API n'a pas can_refundActivez le scope sur la clé dans le dashboard
refunds_disabledLes remboursements ne sont pas activés sur votre compteContactez le support
transaction_not_refundableLe paiement n'est pas success (ou déjà remboursé)Ne remboursez que des paiements réussis
refund_environment_mismatchClé de test sur un paiement live, ou l'inverseUtilisez la clé de l'environnement du paiement
refund_payer_phone_missingLe paiement n'a pas de téléphone payeur à créditerRemboursez le client hors SOAMTAR
refund_exceeds_refundable / transaction_already_fully_refundedMontant supérieur au restant remboursableBaissez amount ou arrêtez

Un remboursement en processing attend la confirmation du crédit par Orange Money ; le statut final arrive par refund.success ou refund.failed (voir failure_reason). Vous pouvez aussi interroger GET/v1/refunds/{id}.

Le solde affiché ne correspond pas à mes reversements ​

ComposantInclus dans le solde affiché ?
success pas encore reverséOui
pendingNon
success déjà reverséNon
Remboursement ou paiement sortant émisOui (déduit)
Litige ouvertRéservé sur un solde distinct

Le solde « disponible » diffère du solde « comptable » ; le dashboard affiche les deux.

Une clé live ne trouve pas mon objet ​

Vous utilisez une clé live sur un objet créé en sandbox (ou l'inverse). Utilisez la clé du mode correspondant : les identifiants sont strictement cloisonnés par mode.

Problèmes de connexion à l'API (SSL, timeout…) ​

Erreur SSL : SSL certificate problem: unable to get local issuer certificate ​

Survient en général sur des serveurs Windows ou d'anciennes installations PHP/cURL sans bundle de certificats à jour.

Correction :

  1. Téléchargez le cacert.pem officiel depuis curl.se/ca/cacert.pem.
  2. Pointez php.ini dessus :
    ini
    curl.cainfo = "C:/path/to/cacert.pem"
    openssl.cafile = "C:/path/to/cacert.pem"
  3. Redémarrez votre serveur web.

Erreur : Connection Timeout ​

Si votre serveur ne joint pas app.so-amtar.com :

  1. Pare-feu : votre serveur doit être autorisé à sortir sur le port 443 (HTTPS).
  2. DNS : vérifiez que app.so-amtar.com se résout depuis votre serveur.
  3. Proxy : si votre serveur passe par un proxy, configurez le SDK en conséquence (ou les variables d'environnement HTTP_PROXY).

Erreur : 401 Unauthorized (clé invalide) ​

  1. Utilisez la clé secrète (sk_...) pour les appels backend, pas la clé publique (pk_...).
  2. Vérifiez le mode : une clé sk_test_ ne fonctionne que pour les objets sandbox.
  3. Assurez-vous qu'aucun espace ni retour à la ligne n'a été copié avec la clé.

Aller plus loin ​

  • Support : support@so-amtar.com

SOAMTAR Payment API Documentation.