Autenticación
La nueva API Core de Pago46 utiliza un esquema de seguridad basado en HMAC SHA-256 para garantizar la integridad y autenticidad de cada transacción.
Este mecanismo asegura que quien envía la petición posee las credenciales correctas y que el mensaje no ha sido alterado en el camino ni es una repetición de una petición antigua.
Cabeceras Obligatorias
Cada petición HTTP que realices a nuestra API debe incluir las siguientes cabeceras (headers):
| Cabecera | Tipo | Descripción |
|---|---|---|
Provider-Key | String | Tu identificador único de proveedor (API Key). |
Message-Date | Timestamp | Timestamp Unix (segundos o milisegundos flotantes). |
Message-Hash | String | El resultado hexadecimal de la firma HMAC calculada. |
El servidor validará que la diferencia entre el Message-Date enviado y la hora actual del servidor no sea mayor a 24 horas. Si el tiempo excede este rango, la petición será rechazada para prevenir ataques de repetición (replay attacks).
Algoritmo de Firma
Para generar el Message-Hash válido, debes seguir estrictamente el siguiente algoritmo de construcción de la cadena de firma.
1. Construcción de la Cadena (String to Sign)
A diferencia de versiones anteriores, este flujo no requiere ordenar parámetros alfabéticamente. La cadena se construye concatenando los siguientes valores separados por dos puntos (:):
- Provider Key
- Message Date (El mismo valor enviado en el header)
- Método HTTP (Ej:
POST,GET) - Path (La ruta del recurso, ej:
/api/v1/payments) - Body (El cuerpo crudo de la petición en UTF-8)
Formato:
PROVIDER_KEY:MESSAGE_DATE:METHOD:PATH:BODY
Si la petición es GET y no tiene cuerpo, el valor de BODY debe ser una cadena vacía. Si es un POST con JSON, asegúrate de usar el string JSON exacto que enviarás en el request, sin espacios adicionales ni modificaciones.
2. Generación del Hash
Una vez construida la cadena, debes firmarla utilizando:
- Algoritmo: HMAC SHA-256
- Secret: Tu
Provider Secret(proporcionado por Pago46) - Mensaje: La cadena construida en el paso 1.
- Output: Hexdigest (cadena hexadecimal).
Ejemplos de Implementación
A continuación, presentamos cómo generar esta firma en diferentes lenguajes.
- Python
- Node.js
- Go
import hmac
import hashlib
import time
import requests
import json
def send_authenticated_request(provider_key, provider_secret, method, path, body_dict=None):
# 1. Preparar datos
host = "[https://api.pago46.com](https://api.pago46.com)"
timestamp = str(time.time()) # Timestamp actual (float en string)
# Si hay body, lo convertimos a string JSON, si no, es vacío
body_str = json.dumps(body_dict) if body_dict else ""
# 2. Construir la cadena de firma (IMPORTANTE: Separador es ":")
# Formato: KEY:DATE:METHOD:PATH:BODY
string_to_sign = f"{provider_key}:{timestamp}:{method}:{path}:{body_str}"
# 3. Calcular HMAC SHA-256
calculated_hmac = hmac.new(
provider_secret.encode("utf-8"),
string_to_sign.encode("utf-8"),
hashlib.sha256
).hexdigest()
# 4. Enviar Petición
headers = {
"Provider-Key": provider_key,
"Message-Date": timestamp,
"Message-Hash": calculated_hmac,
"Content-Type": "application/json"
}
response = requests.request(
method=method,
url=f"{host}{path}",
headers=headers,
data=body_str
)
return response
# Uso
response = send_authenticated_request(
provider_key="PK_12345",
provider_secret="SECRET_XYZ",
method="POST",
path="/api/v1/payments/",
body_dict={"amount": 100, "currency": "CLP"}
)
print(response.status_code)
const crypto = require('crypto');
const axios = require('axios');
async function sendAuthenticatedRequest(providerKey, providerSecret, method, path, body = null) {
// 1. Timestamp actual
const timestamp = (Date.now() / 1000).toString(); // Python usa segundos float, JS usa ms. Ajustar según backend.
// Nota: El backend acepta float timestamp. Date.now() / 1000 es compatible.
// 2. Body string
const bodyStr = body ? JSON.stringify(body) : '';
// 3. Construir cadena (Separador ":")
const stringToSign = [
providerKey,
timestamp,
method,
path,
bodyStr
].join(':');
// 4. Calcular HMAC
const signature = crypto
.createHmac('sha256', providerSecret)
.update(stringToSign)
.digest('hex');
// 5. Enviar
try {
const response = await axios({
method: method,
url: `https://api.pago46.com${path}`,
headers: {
'Provider-Key': providerKey,
'Message-Date': timestamp,
'Message-Hash': signature,
'Content-Type': 'application/json'
},
data: bodyStr
});
return response.data;
} catch (error) {
console.error("Auth Error", error.response?.status);
}
}
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"strings"
"time"
)
func GenerateAuthHeaders(apiKey, apiSecret, method, path, bodyStr string) map[string]string {
// 1. Timestamp
timestamp := fmt.Sprintf("%f", float64(time.Now().Unix()))
// 2. Construir cadena
parts := []string{apiKey, timestamp, method, path, bodyStr}
stringToSign := strings.Join(parts, ":")
// 3. HMAC
h := hmac.New(sha256.New, []byte(apiSecret))
h.Write([]byte(stringToSign))
signature := hex.EncodeToString(h.Sum(nil))
return map[string]string{
"Provider-Key": apiKey,
"Message-Date": timestamp,
"Message-Hash": signature,
}
}
Errores Comunes
| Código HTTP | Mensaje | Causa Probable |
|---|---|---|
403 Forbidden | Invalid authentication credentials | El header Provider-Key no existe o no se encuentra en base de datos. |
403 Forbidden | Possible replay attack | El Message-Date tiene más de 24 horas de diferencia con el servidor. |
403 Forbidden | Hash mismatch | La firma no coincide. Verifica que el separador sea :, que el body sea exacto y que el path sea correcto. |