Skip to content

Codes d'erreur

Toute erreur API renvoie une enveloppe uniforme :

json
{
  "error": {
    "code": "amount_below_merchant_minimum",
    "message": "Le montant est inférieur au minimum autorisé (100.00 XOF).",
    "context": { "minimum": "100.00", "currency": "XOF" },
    "documentation_url": "https://docs.haybtech.sn/errors#amount_below_merchant_minimum"
  }
}

Le champ context est optionnel — présent uniquement quand l'erreur porte des données structurées exploitables côté SDK (montant min, currency, provider, etc.).

Code HTTPSens
400Requête malformée (header manquant, JSON invalide)
401Authentification (clé absente, invalide, révoquée)
403Autorisation (scope insuffisant, livemode mismatch)
404Ressource introuvable pour ce marchand
409Conflit (idempotency mismatch, in-flight)
422Validation métier (limite dépassée, état incohérent, KYC manquant)
429Rate limit
500Erreur interne (à signaler au support)
502Provider en aval indisponible
503Provider en aval temporairement injoignable

Authentification & autorisation

missing_credentials

HTTP 401. Header Authorization: Bearer <key> (ou X-API-Key) absent.

invalid_api_key

HTTP 401. Clé inconnue, révoquée, expirée, ou IP hors whitelist.

scope_insufficient

HTTP 403. Clé sans le scope requis pour cette route (ex: can_payout requis pour POST /v1/payouts).

livemode_mismatch

HTTP 403. Clé test sur un objet live ou inversement. Vérifier l'environnement avant de retry.

unauthorized

HTTP 401. Authentification générique requise.

forbidden

HTTP 403. Action refusée par les policies.

ip_not_allowed

HTTP 401. IP source absente de la liste autorisée (clé ou compte marchand).

test_key_in_production

HTTP 401. Une clé sk_test_* a été utilisée sur l'API live. Utiliser une clé sk_live_* ou continuer l'intégration en environnement non-production avec vos clés de test.

Idempotency

idempotency_key_required

HTTP 400. Header Idempotency-Key absent sur une requête mutante.

idempotency_key_too_long

HTTP 400. Clé > 255 caractères.

idempotency_conflict

HTTP 409. Même clé, body différent — OU requête déjà in-flight pour cette clé.

Validation de paiement / payout

Toutes ces erreurs sont HTTP 422 et sont réparables côté client sans contacter le support.

validation_error

Validation au niveau du FormRequest (champ manquant, type incorrect, format invalide). Le payload contient un sous-objet errors avec les détails par champ :

json
{ "error": { "code": "validation_error", "errors": { "amount": ["The amount field is required."] } } }

merchant_inactive

Le compte marchand est suspendu / pending / closed. Contacter le support pour réactivation. Context: { status: "suspended" }

merchant_kyc_required

Validation KYC manquante. Les paiements en mode live sont restreints jusqu'à approbation.

payouts_disabled

Le service de payout n'est pas activé sur ce compte. Contacter le support.

provider_unavailable

Provider inconnu, désactivé ou non configuré. Context: { provider: "free_money" }

provider_country_unsupported

Le provider ne sert pas le pays cible (ex: Wave hors UEMOA). Context: { provider: "wave", country: "TG" }

provider_payout_unsupported

Le provider supporte les paiements mais pas les payouts. Context: { provider: "free_money" }

merchant_provider_not_authorized

Le marchand a une whitelist allowed_providers qui exclut ce provider. Context: { provider: "wave" }

amount_must_be_positive

Montant ≤ 0. Aucun contexte.

amount_below_merchant_minimum

Montant inférieur au plancher configuré pour le marchand. Context: { minimum: "100.00", currency: "XOF" }

amount_below_provider_minimum

Montant inférieur au plancher du provider (ex: Orange Money min 10 XOF). Context: { minimum: "10.00", provider: "orange_money", currency: "XOF" }

amount_above_merchant_maximum

Montant > plafond par transaction du marchand. Context: { maximum: "5000000.00", currency: "XOF" }

daily_limit_exceeded

Plafond quotidien de transferts atteint pour ce marchand. Context: { limit: "10000000.00", currency: "XOF" }

merchant_limit_exceeded

Une limite quotidienne ou mensuelle configurée sur le compte marchand a été dépassée. Le champ message détaille la cause précise (volume, montant cumulé, etc.).

bceao_limit_exceeded

Plafond BCEAO du payeur (calculé sur 30 jours glissants par MSISDN). Context: { tier: 1, ceiling: 200000 }

invalid_phone_number

Numéro de téléphone non-parsable au format E.164 pour le pays cible. Context: { reason: "..." }

insufficient_balance

Solde marchand insuffisant pour couvrir montant + frais (payout uniquement). Context: { currency: "XOF", available: "1234.50", required: "10148.50" }

fees_unavailable

Aucune grille de frais configurée pour cette combinaison (provider, pays). À signaler au support.

Remboursements

transaction_not_found

HTTP 404. Aucune transaction avec cet id/internal_ref pour ce marchand. Context: { transaction_id: "..." }

transaction_not_refundable

La transaction n'est pas en status success. Seuls les paiements aboutis sont remboursables. Context: { status: "pending" }

non_payment_transaction_refund

Tentative de remboursement d'un payout / refund / autre. Context: { type: "payout" }

transaction_already_fully_refunded

Tous les fonds ont déjà été remboursés.

refund_exceeds_refundable

Le montant demandé dépasse le solde remboursable restant. Context: { requested: "5000", refundable: "3000.0000", already_refunded: "7000.00" }

Splits (marketplace)

split_items_required

La liste items est vide.

non_payment_transaction_split

Tentative de split sur un type ≠ payment.

transaction_not_splittable

Transaction en status terminal (refunded, cancelled, expired, disputed). Context: { status: "refunded" }

split_total_exceeds_transaction

Somme des splits > montant de la transaction parent. Context: { total_split: "11000.00", transaction_amount: "10000.00" }

splits_exceed_transaction_amount

Variant TOCTOU détecté inside-transaction : la somme des splits existants + nouveaux dépasse le montant. Refaire la requête après reload côté client.

beneficiary_not_active

Marchand bénéficiaire suspendu / fermé. Context: { beneficiary_merchant_id: "..." }

Souscriptions

subscription_plan_not_available

Le plan n'existe pas, est inactif, ou n'appartient pas au marchand authentifié. Context: { plan_id: "..." }

subscription_customer_mismatch

Le customer_id n'appartient pas au marchand authentifié (cross-tenant). Context: { customer_id: "..." }

subscription_already_in_status

Tentative de cancel/cancel-at-period-end sur un abonnement déjà en cancelled ou expired. Context: { status: "cancelled" }

Providers & infrastructure

gateway_unavailable

HTTP 503. Provider en aval temporairement injoignable. Voir header Retry-After.

gateway_error

HTTP 502. Le provider a refusé l'init avec une erreur métier.

not_found

HTTP 404. Ressource générique introuvable.

method_not_allowed

HTTP 405. Mauvais verbe HTTP sur l'endpoint.

too_many_requests

HTTP 429. Rate limit atteint. Respecter Retry-After.

api_error

HTTP 500. Erreur serveur non classifiée. À signaler au support avec le request_id de la réponse.

Webhooks providers entrants (côté HayBTech)

provider_unavailable_webhook

HTTP 404. Provider désactivé sur ce slug/pays.

invalid_signature

HTTP 401. Signature ou timestamp rejeté.

duplicate

HTTP 200, no-op. Événement déjà traité (idempotent côté HayBTech).

Que faire selon le code

CodeAction recommandée
4xx (validation)Corriger la requête, utiliser une nouvelle Idempotency-Key
401Vérifier la clé. Ne PAS retry.
409Vérifier votre logique d'idempotency
422Lire error.code + error.context ; corriger le champ identifié
429Attendre le Retry-After, puis retry
5xxRetry avec la même Idempotency-Key, backoff exponentiel
502/503Provider en aval, retry après quelques secondes

Reporter un bug

support@haybtech.com avec :

  • Le code d'erreur (error.code)
  • L'error.context complet (s'il existe)
  • Le documentation_url
  • L'horodatage exact
  • Votre merchant_ref ou l'id HayBTech
  • Le header X-Request-Id de la réponse (toujours présent)

Documentation de l'API de paiement HayBTech.