Webhooks

Recevoir les événements, et pourquoi on les relit.

Pourquoi vous en avez besoin

Un encaissement ne se dénoue pas pendant votre requête. Le client doit valider sur son téléphone, ce qui prend de quelques secondes à plusieurs minutes — ou n'arrive jamais. Le webhook vous prévient quand l'issue est connue, au lieu de vous faire interroger l'API en boucle.

Déclarer une adresse

Depuis la console, section Développeurs. L'adresse doit être en HTTPS et joignable depuis Internet.

Un secret, préfixé whsec_, est généré avec elle et s'affiche sous l'adresse : c'est lui qui signe les envois vers cette adresse, et lui seul. Chaque adresse a le sien, donc en révoquer une ne touche pas les autres. Il n'est pas votre clé API et ne s'utilise jamais à sa place.

Vérifier la signature

Chaque requête porte un en-tête MaticPay-Signature :

MaticPay-Signature: t=1793996400,v1=4f2a...e91c

v1 est le HMAC-SHA256 de t.corps — l'horodatage, un point, le corps brut — calculé avec votre secret.

<?php
$brut = file_get_contents('php://input');
$entete = $_SERVER['HTTP_MATICPAY_SIGNATURE'] ?? '';
parse_str(str_replace(',', '&', $entete), $parts);

$attendu = hash_hmac('sha256', $parts['t'] . '.' . $brut, VOTRE_SECRET);

// Comparaison a temps constant : un == classique fuit la signature.
if (!hash_equals($attendu, $parts['v1'] ?? '')) {
    http_response_code(400);
    exit;
}
// Rejeter au-dela de quelques minutes : sinon un envoi capte reste rejouable.
if (abs(time() - (int) $parts['t']) > 300) {
    http_response_code(400);
    exit;
}

Signez sur le corps brut, avant tout décodage JSON : ré-encoder change les espaces et invalide la signature.

Les événements

Répondez vite, traitez après

Renvoyez 200 dès que vous avez enregistré l'événement, et faites le travail ensuite. Un traitement long dans la requête finit en délai dépassé, et nous réessayons alors que vous aviez déjà reçu l'information.

Un même événement peut arriver deux fois

C'est normal, et nous ne pouvons pas le garantir autrement : un envoi dont la réponse se perd sera renvoyé. Rendez votre traitement idempotent — retenez l'identifiant de l'événement et ignorez ce que vous avez déjà vu.

Et ne vous fiez pas au corps pour décider : s'il porte un enjeu d'argent, relisez l'objet avec GET /api/v1/payments/{id}. C'est la règle que nous appliquons nous-mêmes avec l'opérateur.