Aller au contenu principal

Signature du formulaire HTML

Le client peut modifier n'importe quel champ du formulaire directement dans le navigateur — par exemple, mettre un montant plus bas. C'est pourquoi les champs de la facture voyagent dans un conteneur signé : le service recalcule la signature et, si elle ne correspond pas, ne crée pas la facture.

Le serveur de la boutique calcule la signature. Seule la paire finie data et signature va au navigateur.

Ce qui est signé

data = base64url(json)
signature = HMAC-SHA256(data, formSecret)

Le HMAC est calculé sur la chaîne data, pas sur les octets du JSON. Le destinataire vérifie la signature sur la chaîne soumise telle quelle, et seulement ensuite la décode.

Il en découle qu'il n'y a pas de canonicalisation : l'ordre des clés, l'indentation du JSON et le style d'échappement Unicode n'affectent pas la signature. N'importe quel encodeur JSON standard convient.

Jeu de référence

Insérez ces valeurs dans votre code et comparez le résultat.

Le secret d'exemple :

dvc3khto5WpjEPojZfYJsbJzFEPPVnRa

Le montant et le délai de paiement comme des nombres ; les chaînes sont acceptées aussi :

{"projectId":"a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d","amountFiat":10.5,"currencyFiat":"USD","timeToPay":1,"description":"Order #7, delivery","serviceData":"order-7"}

La chaîne data est le base64url de ces octets. Le padding est retiré ici ; le destinataire l'accepte aussi avec padding :

eyJwcm9qZWN0SWQiOiJhMWIyYzNkNC01ZTZmLTRhN2ItOGM5ZC0wZTFmMmEzYjRjNWQiLCJhbW91bnRGaWF0IjoxMC41LCJjdXJyZW5jeUZpYXQiOiJVU0QiLCJ0aW1lVG9QYXkiOjEsImRlc2NyaXB0aW9uIjoiT3JkZXIgIzcsIGRlbGl2ZXJ5Iiwic2VydmljZURhdGEiOiJvcmRlci03In0

La signature de cette chaîne avec le secret ci-dessus :

09628020c47400c9231420450af642adfdb3c246f33e3e12cb8e28f96c2a785e

Avec ces valeurs, les exemples ci-dessous impriment exactement ces data et signature. Le même JSON avec la clé "output":"errors" à la fin produit une chaîne data différente et une signature différente.

Si data ne correspond pas alors que le JSON semble identique, l'encodeur JSON est en cause : un ordre de clés différent ou des espaces après les séparateurs. Le destinataire acceptera un tel conteneur, mais il ne correspondra pas mot pour mot à la référence.

Exemples

<?php

function signedContainer(array $fields, string $formSecret): array {
$data = rtrim(strtr(base64_encode(json_encode($fields)), '+/', '-_'), '=');

return [
'data' => $data,
'signature' => hash_hmac('sha256', $data, $formSecret),
];
}

// Test values from the reference set. Before going live,
// replace the secret with the form secret from your project settings
$container = signedContainer([
'projectId' => 'a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d',
'amountFiat' => 10.5,
'currencyFiat' => 'USD',
'timeToPay' => 1,
'description' => 'Order #7, delivery',
'serviceData' => 'order-7',
], 'dvc3khto5WpjEPojZfYJsbJzFEPPVnRa');

echo $container['data'], "\n";
echo $container['signature'], "\n";

Les valeurs d'exemple sont en ASCII, mais le code fonctionne tel quel avec tout Unicode. PHP et Python échappent le non-ASCII en \uXXXX par défaut, JavaScript envoie l'UTF-8 brut — le destinataire accepte les deux.

Deux erreurs courantes

Le JSON est signé au lieu du conteneur. Le HMAC est calculé sur la chaîne data, c'est-à-dire sur le base64url. L'erreur ressemble à « tout correspond, mais pas la signature ».

Le conteneur est reconstruit après la signature. Si un seul octet de data change, la signature est invalide. Construisez la paire à un seul endroit de votre code.

Si la signature ne correspond pas

Parcourez la chaîne JSON → data → signature ; le premier maillon qui diverge de la référence est la cause. Vérifiez data d'abord : elle ne dépend pas du secret.