Autenticación
La API de Pago46 firma cada petición con HMAC SHA-256. Tú calculas una firma a partir de tu llave secreta y del contenido de la petición; el servidor la recalcula y la compara. Si coincide, confirma dos cosas: que la petición es tuya y que nadie alteró el contenido en tránsito.
El esquema es idéntico para Comercios y Proveedores de Pago. Lo único que cambia es el nombre de la cabecera donde envías tu llave pública.
Cabeceras obligatorias
Cada petición HTTP debe incluir estas tres cabeceras:
| Cabecera | Descripción |
|---|---|
Merchant-Key o Provider-Key | Tu llave pública. Usa Merchant-Key si eres Comercio y Provider-Key si eres Proveedor de Pago. |
Message-Date | Marca de tiempo Unix en segundos (entero o decimal). |
Message-Hash | La firma HMAC SHA-256 en hexadecimal (ver abajo). |
El servidor rechaza la petición si Message-Date difiere de la hora del servidor en más de 5 minutos, para prevenir ataques de repetición (replay attacks). Usa siempre la hora actual y en segundos: enviar milisegundos la coloca fuera de la ventana y la petición se rechaza.
Construir la firma
1. Cadena a firmar
Concatena estos cinco valores separados por dos puntos (:), en este orden exacto:
LLAVE:MESSAGE_DATE:MÉTODO:PATH:BODY
- Llave pública — el mismo valor de tu cabecera
Merchant-KeyoProvider-Key. - Message-Date — el mismo valor que envías en la cabecera.
- Método HTTP —
POST,GET, etc. - Path — la ruta del recurso sin query string, por ejemplo
/api/v1/merchants/orders/pay-in/. - Body — el cuerpo crudo de la petición en UTF-8. Si la petición no tiene cuerpo (por ejemplo un
GET), usa una cadena vacía.
No se ordenan parámetros ni se normaliza el JSON. Firma exactamente los mismos bytes que envías en el cuerpo: cualquier espacio o cambio de orden produce una firma distinta.
2. Calcular el hash
Firma la cadena con HMAC SHA-256 usando tu secret como llave y representa el resultado en hexadecimal (hexdigest).
Ejemplos de implementación
El mismo firmante sirve para Comercios y Proveedores; solo cambia el nombre de la cabecera de la llave.
- Python
- Node.js
- Go
import hashlib
import hmac
import json
import time
import requests
HOST = "https://api.dev.pago46.io"
def signed_request(key, secret, key_header, method, path, body=None):
timestamp = str(time.time()) # Unix en segundos
body_str = json.dumps(body) if body else ""
string_to_sign = f"{key}:{timestamp}:{method}:{path}:{body_str}"
signature = hmac.new(
secret.encode("utf-8"),
string_to_sign.encode("utf-8"),
hashlib.sha256,
).hexdigest()
headers = {
key_header: key, # "Merchant-Key" o "Provider-Key"
"Message-Date": timestamp,
"Message-Hash": signature,
"Content-Type": "application/json",
}
return requests.request(method, f"{HOST}{path}", headers=headers, data=body_str)
# Ejemplo: un Comercio crea una orden de pay-in
response = signed_request(
key="TU_MERCHANT_KEY",
secret="TU_MERCHANT_SECRET",
key_header="Merchant-Key",
method="POST",
path="/api/v1/merchants/orders/pay-in/",
body={"order_type": "LocalCurrencyOrder", "price": "100.00", "price_currency": "CLP"},
)
print(response.status_code)
const crypto = require('crypto');
const axios = require('axios');
const HOST = 'https://api.dev.pago46.io';
async function signedRequest({ key, secret, keyHeader, method, path, body = null }) {
const timestamp = (Date.now() / 1000).toString(); // Unix en segundos
const bodyStr = body ? JSON.stringify(body) : '';
const stringToSign = [key, timestamp, method, path, bodyStr].join(':');
const signature = crypto
.createHmac('sha256', secret)
.update(stringToSign)
.digest('hex');
return axios({
method,
url: `${HOST}${path}`,
headers: {
[keyHeader]: key, // 'Merchant-Key' o 'Provider-Key'
'Message-Date': timestamp,
'Message-Hash': signature,
'Content-Type': 'application/json',
},
data: bodyStr,
});
}
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"strconv"
"strings"
"time"
)
// keyHeader es "Merchant-Key" o "Provider-Key".
func authHeaders(key, secret, keyHeader, method, path, bodyStr string) map[string]string {
timestamp := strconv.FormatInt(time.Now().Unix(), 10) // Unix en segundos
stringToSign := strings.Join([]string{key, timestamp, method, path, bodyStr}, ":")
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(stringToSign))
signature := hex.EncodeToString(h.Sum(nil))
return map[string]string{
keyHeader: key,
"Message-Date": timestamp,
"Message-Hash": signature,
}
}
Errores de autenticación
Cuando la autenticación falla, la API devuelve 403 Forbidden con el formato estándar de errores:
{
"type": "client_error",
"errors": [
{
"code": "authentication_failed",
"detail": "Incorrect authentication credentials.",
"attr": null
}
]
}
| Causa probable | Cómo resolverlo |
|---|---|
| Falta una cabecera obligatoria | Incluye Merchant-Key/Provider-Key, Message-Date y Message-Hash. |
Message-Date fuera de la ventana de 5 minutos | Usa la hora actual del sistema en segundos. |
| Llave pública desconocida | Verifica que usas la llave del ambiente correcto (sandbox vs. producción). |
| La firma no coincide | Revisa el orden de la cadena, el separador :, el path sin query string y que el body sea idéntico al enviado. |
Elige tu guía
Soy un Comercio
Quiero cobrar pagos o enviar dinero a mis usuarios.
Soy un Proveedor de Pago
Tengo puntos físicos y quiero procesar transacciones de Pago46.