Skip to content

Troubleshooting

Mon webhook n'arrive pas

Diagnostic

  1. Dashboard Webhooks Logs : la requête est-elle listée ?
    • Non l'événement n'a pas été déclenché. Vérifiez que la transaction est passée en success/failed/cancelled/expired. Forcez un pull avec POST /v1/payments/{id}/verify.
    • Oui, avec http_status non-2xx votre serveur a refusé. Allez à « Mon endpoint répond 4xx/5xx ».
    • Oui, avec error_message: "timeout" votre serveur a dépassé 15 secondes. Allez à « Timeout ».

Mon endpoint répond 4xx/5xx

http_statusCause typique
400Body parsing : votre serveur lit le payload avant la vérification de signature
401 / 403Auth middleware bloque les requêtes externes exempter /webhooks/haybtech
404Route mal configurée vérifier l'URL exacte dans le dashboard
422Validation Laravel/middleware accepter le payload tel quel
500Erreur applicative voir vos logs
502 / 503Reverse-proxy down ou app non démarrée

Solution : ajoutez route('webhooks.haybtech') à withoutMiddleware([VerifyCsrfToken::class]) (Laravel) ou exemptez du middleware d'auth.

Timeout

Votre handler doit répondre 2xx en moins de 15 secondes. Si vous avez du 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) {
    ProcessHaybtechWebhookJob::dispatch($request->all());
    return response('ok');             // immédiat
}

Mon serveur reçoit le webhook 5 fois

Comportement normal c'est un retry. Causes :

  1. Vous répondez 2xx mais après plus de 15s HayBTech a déjà déclaré timeout
  2. Vous répondez non-2xx
  3. Vous répondez 2xx mais HayBTech ne reçoit pas la réponse (réseau)

Solution : implémentez l'idempotency côté votre serveur en stockant event_id (champ data.id du payload).

sql
CREATE UNIQUE INDEX uq_inbound_event ON your_webhook_log(event_id);

Signature webhook invalide

Symptôme

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

Cause #1 : body modifié avant vérification

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

js
//  Mauvais
app.use(express.json())   // parse JSON
app.post('/webhook', (req, res) => {
  verify(req.body)        // req.body est déjà un objet, pas le 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 : encoding HMAC

Comparez les hex en lowercase des deux côtés (hash_hmac PHP retourne lowercase, vérifiez côté Node). Et utilisez hash_equals / crypto.timingSafeEqual pas ===.

Cause #3 : timestamp expiré

|now - t| > 300s est rejeté. Vérifiez l'heure de votre serveur (NTP). Si vous travaillez sur du replay manuel d'anciens webhooks, augmentez la tolérance ou repassez par le pull verify.

Idempotency-Key conflict

Symptôme

409 idempotency_conflict quand vous renvoyez la requête.

Diagnostic

bash
curl -X POST .../v1/payments \
  -H "Idempotency-Key: abc-123" \
  -d '{"amount":5000}'
# 201

curl -X POST .../v1/payments \
  -H "Idempotency-Key: abc-123" \
  -d '{"amount":5000,"description":"oops"}'
# 409  body différent

Solution :

  • Pour retry : même clé, exactement même body.
  • Pour une nouvelle requête : nouvelle clé.

Bonne pratique

Générez la clé au moment où l'utilisateur clique "Payer", persistez-la côté client (localStorage / DB) jusqu'à la confirmation finale. Ne la regénérez pas à chaque retry HTTP.

Transaction bloquée en pending

Diagnostic

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

Le pull verify va interroger le provider en aval et synchroniser le statut. Si le provider confirme, la transaction transite et un webhook part vers vous.

Si rien ne change

  • Le canal de paiement est en incident : consultez la page de statut communiquée au support
  • Le payeur n'a jamais finalisé l'OTP : la transaction expirera à expires_at
  • La référence fournisseur est manquante côté HayBTech : contactez support@haybtech.com avec la référence interne

Refund refusé : refund_not_supported

Certains canaux mobile money ne supportent pas le refund automatique. Procédure manuelle dans ce cas :

  1. Créer un payout vers le numéro du payeur d'origine
  2. Marquer la transaction d'origine comme remboursée via le dashboard

Les cartes supportent le refund automatique.

Le solde affiché ne correspond pas à mes settlements

ComposantInclus dans le solde affiché ?
success non-settled
pending (en attente)
success déjà settled
Refund émis(déduit)
Chargeback en cours(réservé sur un solde séparé)

Le solde "disponible" diffère du solde "comptable". Le dashboard expose les deux.

Signature pk_live_ qui répond livemode_mismatch

Vous utilisez une clé live sur un objet créé avec une clé test (ou inversement). Solution : utilisez la clé du même mode (pk_test_ ↔ objet test, pk_live_ ↔ objet live). Les identifiants sont strictement scopés par livemode.

Problèmes de connexion API (SSL, Timeout, etc.)

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

Cette erreur survient généralement sur les serveurs Windows ou les installations PHP/CURL anciennes qui n'ont pas de certificat CA à jour.

Solution :

  1. Téléchargez le fichier cacert.pem officiel sur curl.se/ca/cacert.pem.
  2. Dans votre php.ini, configurez le chemin :
    ini
    curl.cainfo = "C:/chemin/vers/votre/cacert.pem"
    openssl.cafile = "C:/chemin/vers/votre/cacert.pem"
  3. Redémarrez votre serveur web.

Erreur : Connection Timeout

Si votre serveur n'arrive pas à joindre app.haybtech.com :

  1. Vérifiez votre pare-feu : Votre serveur doit pouvoir faire des requêtes sortantes sur le port 443 (HTTPS).
  2. DNS : Vérifiez que app.haybtech.com est correctement résolu depuis votre serveur (ping app.haybtech.com).
  3. Proxy : Si votre serveur utilise un proxy, assurez-vous de configurer les options du SDK correspondantes (ou utilisez les variables d'environnement HTTP_PROXY).

Erreur : 401 Unauthorized (Clé invalide)

Si vous recevez une erreur 401 alors que vous avez copié votre clé :

  1. Vérifiez que vous utilisez bien la Clé Secrète (sk_...) pour les appels backend, et non la Clé Publique (pk_...).
  2. Vérifiez le Mode : une clé sk_test_ ne peut accéder qu'aux objets de test (et inversement pour sk_live_).
  3. Assurez-vous qu'aucun espace ou retour à la ligne n'a été copié par mégarde avec la clé.

Aller plus loin

  • Support : support@haybtech.com

Documentation de l'API de paiement HayBTech.