Pular para o conteúdo principal

Assinatura do formulário HTML

O cliente pode alterar qualquer campo do formulário direto no navegador — por exemplo, definir um valor menor. Por isso os campos da cobrança viajam em um contêiner assinado: o serviço recalcula a assinatura e, se ela não corresponder, não cria a cobrança.

O servidor da loja calcula a assinatura. Somente o par pronto de data e signature vai ao navegador.

O que é assinado

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

O HMAC é calculado sobre a string data, não sobre os bytes do JSON. O receptor verifica a assinatura contra a string enviada como está, e só então a decodifica.

Disso decorre que não há canonicalização: a ordem das chaves, a indentação do JSON e o estilo de escape Unicode não afetam a assinatura. Qualquer codificador JSON padrão funciona.

Conjunto de referência

Coloque estes valores no seu código e compare a saída.

O segredo de exemplo:

dvc3khto5WpjEPojZfYJsbJzFEPPVnRa

O valor e o prazo de pagamento como números; strings também são aceitas:

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

A string data é o base64url desses bytes. O padding foi removido aqui; o receptor também a aceita com padding:

eyJwcm9qZWN0SWQiOiJhMWIyYzNkNC01ZTZmLTRhN2ItOGM5ZC0wZTFmMmEzYjRjNWQiLCJhbW91bnRGaWF0IjoxMC41LCJjdXJyZW5jeUZpYXQiOiJVU0QiLCJ0aW1lVG9QYXkiOjEsImRlc2NyaXB0aW9uIjoiT3JkZXIgIzcsIGRlbGl2ZXJ5Iiwic2VydmljZURhdGEiOiJvcmRlci03In0

A assinatura desta string com o segredo acima:

09628020c47400c9231420450af642adfdb3c246f33e3e12cb8e28f96c2a785e

Com estes valores, os exemplos abaixo imprimem exatamente estes data e signature. O mesmo JSON com a chave "output":"errors" no final produz uma string data diferente e uma assinatura diferente.

Se data não corresponder embora o JSON pareça o mesmo, a causa é o codificador JSON: outra ordem de chaves ou espaços depois dos separadores. O receptor aceitará esse contêiner, mas ele não corresponderá à referência ao pé da letra.

Exemplos

<?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";

Os valores de exemplo são ASCII, mas o código funciona com qualquer Unicode sem alterações. PHP e Python escapam não ASCII como \uXXXX por padrão, o JavaScript envia UTF-8 puro — o receptor aceita os dois.

Dois erros comuns

Assinar o JSON em vez do contêiner. O HMAC é calculado sobre a string data, isto é, sobre o base64url. O erro se manifesta como “tudo corresponde, mas a assinatura não”.

Remontar o contêiner depois de assinar. Se um único byte de data mudar, a assinatura fica inválida. Monte o par em um único lugar do seu código.

Se a assinatura não corresponder

Percorra a cadeia JSON → data → assinatura; o primeiro elo que divergir da referência é a causa. Verifique data primeiro: ela não depende do segredo.