Passer au contenu principal

1. Pourquoi une clé publique

Aucun secret à garder

Vous ne détenez qu’une clé publique : personne ne peut signer un rappel à la place d’Umeme, ni pour vous, ni pour un autre intégrateur.

Umeme change de clé sans vous

Chaque signature nomme sa clé (keyid), et deux clés restent actives pendant un changement.

Le corps est couvert

La signature porte sur le condensat du corps : un rappel modifié en route échoue à la vérification.
Umeme ne promet aucune adresse IP de départ : ses serveurs sortent par plusieurs liens. La signature est votre seule garantie d’origine.

2. Ce que vous recevez

Quand une vente atteint son statut final, Umeme envoie un POST en HTTPS à l’URL de rappel de votre fiche. Le corps est celui de GET /v1/ventes/{uuid}.
Rappel reçu par votre serveur
Répondez 200. Sinon, Umeme réessaie huit fois en 25 minutes :
0:001ʳᵉ
1:002ᵉ
2:003ᵉ
5:004ᵉ
10:005ᵉ
15:006ᵉ
20:007ᵉ
25:008ᵉ
Chaque tentative attend 10 s la connexion et 30 s la réponse. Après la huitième, le rappel se renvoie à la demande pendant 90 jours.

3. Vérifier un rappel, étape par étape

Huit étapes, toutes avant de toucher à la vente.
1

Garder le corps brut

Lisez les octets reçus, avant tout décodage JSON. Un JSON relu puis réécrit ne donne plus le même condensat.
2

Vérifier le condensat

Calculez le SHA-256 du corps brut, encodez-le en base64, et comparez sha-256=:…: à Content-Digest. Différent : le corps a changé en route, refusez.
3

Lire Signature-Input

Il dit quels éléments sont signés, quand (created, en secondes Unix) et avec quelle clé (keyid). Refusez une signature de plus de 5 minutes.
4

Reconstruire la base de signature

C’est le texte qu’Umeme a signé : une ligne par élément, dans l’ordre de Signature-Input, séparées par un retour à la ligne, sans retour final.
Base de signature
@target-uri est votre URL de rappel, telle qu’enregistrée dans votre fiche. @signature-params se recopie tel quel de Signature-Input, après umeme=.
5

Prendre la clé du keyid

Cherchez le keyid dans la réponse de GET /v1/cle-publique, gardée en cache (section 5).
6

Vérifier la signature

ECDSA sur la courbe P-256, avec SHA-256. La signature est brute : r puis s, 64 octets. Certaines bibliothèques attendent du DER : le code ci-dessous convertit quand il le faut.
7

Écarter les doublons

Un Umeme-Evenement-Id déjà traité : répondez 200 sans rien refaire.
8

Répondre, puis traiter

Répondez 200 en moins de 30 secondes, puis traitez le statut de la vente. Signature fausse : répondez 401 et ne touchez à rien.

Et voilà !

Vous ne croyez plus que les rappels d’Umeme. Voici le même vérificateur, prêt à copier.

4. Le code, prêt à copier

Le corps doit rester brut jusqu’à la vérification. Avec Express, prenez express.raw() sur la route du rappel ; avec Laravel, $request->getContent().
La version Node.js est vérifiée par les tests de ce site contre de vraies signatures ECDSA P-256 : rappel valide, corps modifié, signature trop ancienne, clé inconnue, autre URL de rappel.

5. Rotation des clés

GET /v1/cle-publique renvoie toutes les clés actives. Pendant un changement, il y en a deux : la nouvelle signe, l’ancienne reste valable jusqu’à sa date de fin.
GET /v1/cle-publique
  • Gardez la liste en cache 24 heures.
  • Un keyid inconnu ? Rechargez la liste une fois, puis refusez le rappel s’il manque encore.
  • N’écrivez jamais la clé en dur dans votre code : vous rateriez le prochain changement.

6. Répondre et écarter les doublons

Un rappel peut arriver deux fois, si la réponse à une tentative s’est perdue. Rendez son traitement idempotent :

7. Renvoyer un rappel

Votre serveur était en panne ? Redemandez le dernier rappel d’une vente, signé à nouveau, pendant 90 jours :

8. Tester dans la sandbox

  • La sandbox signe avec sa propre clé : prenez celle de https://sandbox.umeme.app/v1/cle-publique, jamais celle de la production.
  • Le compteur 09900000002 envoie son rappel deux fois, avec le même Umeme-Evenement-Id : votre dédoublonnage doit écarter le second.
  • Modifiez un octet du corps reçu avant de vérifier : la vérification doit échouer à l’étape 2.