Troubleshooting de firma
Si una request falla con 401 (firma inválida), casi siempre es por una diferencia entre el
canonical string que armás vos y el que reconstruye el servidor. Estas son las causas más
comunes, en orden de frecuencia.
1. Orden de claves del body
El servidor hashea el body con las claves ordenadas alfabéticamente de forma recursiva. Si tu cliente firma el JSON en el orden en que escribiste las claves, los hashes difieren.
- Síntoma:
GETsin body funciona, peroPOST/PUTfallan con401. - Fix: serializá el body con claves ordenadas recursivamente antes del SHA-256
(
deterministicJsonStringifyen el script).
2. Valor del host
El servidor firma el header Host real que recibe. El cliente firma el host derivado de la
base_url. Si hay un proxy o gateway en el medio que reescribe el Host, divergen.
- Fix: asegurate de que el
hostque firmás sea el mismo host público del entorno, sin esquema (api.psp.stg.stickbank.net, nohttps://api.psp.stg.stickbank.net).
3. Headers extra firmados
Solo content-type, host y x-idempotency entran en la firma (allowlist). Si tu
implementación incluye otros headers en el canonical (por ejemplo accept), no va a coincidir.
- Fix: firmá únicamente el allowlist.
4. Timestamp fuera de ventana
La firma vale por 5 minutos y no se aceptan timestamps futuros.
- Síntoma: requests intermitentes con
401, sobre todo si el reloj del servidor está desincronizado. - Fix: timestamp en milisegundos (
Date.now()) y reloj sincronizado por NTP.
5. Content-Type inconsistente
content-type está firmado. Si mandás body JSON pero el header dice otra cosa (o falta y el
servidor asume uno distinto), el canonical difiere.
- Fix: para body JSON,
Content-Type: application/json.
6. Path con barra final o barras repetidas
El path se normaliza colapsando // → / y quitando la barra final. /api/v1/account/cvu/ se firma
como /api/v1/account/cvu.
- Fix: normalizá el path igual antes de firmar.
Cómo diagnosticar
El script de Postman imprime en la consola (View → Show Postman Console):
[bodyString] el JSON con claves ordenadas que se hasheó
[bodyHash] SHA-256 del bodyString
[canonical] el canonical string completo (con timestamp)
[signature] la firma enviada
Compará ese canonical línea por línea con lo que esperás. La causa del 401 casi siempre salta
comparando el bodyString (orden de claves) y la línea del host.
Si implementás la firma en tu backend, agregá los mismos logs. Poder ver el canonical exacto que
firmás es lo que hace diagnosticable un 401.