Skip to main content

Webhook

In alternativa al polling degli endpoint entity/..., puoi ricevere una notifica HTTP POST quando lo stato di un pagamento, di una dilazione o di un abbonamento cambia. Il webhook evita di dover interrogare ripetutamente le API per sapere se il cliente ha completato il pagamento.

Configurazione​

Il webhook si imposta in due modi, equivalenti:

Il webhook_secret con cui vengono firmate le notifiche è generato da JataPay e si legge con GET /esercente/developer_options oppure dalla Dashboard.

Richiesta inviata da JataPay​

A ogni evento JataPay invia una richiesta con questa forma:

POST https://e-commerce.example.com/webhook
Content-Type: application/json; charset=utf-8
X-Webhook-Signature: t=1730000000,v1=5c1f9d2e...e7

Il corpo è il JSON dell'evento. Rispondi con un codice 2xx per confermare la ricezione: ogni altra risposta viene considerata un errore di consegna.

Calcolo della firma​

La firma è un HMAC-SHA256 calcolato sul corpo esatto della richiesta:

  1. il corpo viene serializzato una sola volta in JSON compatto (separatori , e :, chiavi ordinate alfabeticamente) e inviato così com'è, codificato in UTF-8;
  2. si firma la stringa timestamp + "." + body, dove timestamp sono i secondi Unix dell'invio e body sono i byte esatti del corpo;
  3. il digest esadecimale viene inviato nell'header X-Webhook-Signature nella forma t=<timestamp>,v1=<firma>.

Se il webhook non ha un secret, l'header X-Webhook-Signature non viene inviato.

Verificare la firma​

caution
  • Verifica la firma sul corpo grezzo della richiesta: non riserializzare il JSON, altrimenti la firma non corrisponde più.
  • Confronta le firme con una funzione a tempo costante (hash_equals, compare_digest, timingSafeEqual, hmac.Equal, FixedTimeEquals, MessageDigest.isEqual).
  • Rifiuta i timestamp troppo vecchi (ad esempio oltre 5 minuti) per limitare gli attacchi di replay.
$secret = 'IL_WEBHOOK_SECRET';

$rawBody = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';

if (!preg_match('/^t=(\d+),v1=([a-f0-9]{64})$/', $header, $matches)) {
http_response_code(400);
exit('Firma mancante');
}

$timestamp = $matches[1];
$firma = $matches[2];

// timestamp . "." . corpo grezzo
$atteso = hash_hmac('sha256', $timestamp . '.' . $rawBody, $secret);

if (!hash_equals($atteso, $firma)) {
http_response_code(401);
exit('Firma non valida');
}

http_response_code(200);

Esempio di verifica​

Supponendo che il webhook invii {"importo":2000,"stato":"paid"} con timestamp 1730000000, la stringa firmata è:

1730000000.{"importo":2000,"stato":"paid"}

e la firma da confrontare è l'header X-Webhook-Signature: t=1730000000,v1=<firma>.

Scarica questa pagina in Markdownfile .md da allegare a un assistente AI per integrare le API
Ambiente API
api.jatapay.it · authnew.jatapay.it