Seguridad
TAYPI implementa multiples capas de seguridad para proteger tus transacciones. Esta guia explica cada mecanismo y como implementarlo correctamente.
API Keys
Cada comercio tiene un par de claves:
| Clave | Formato | Proposito |
|---|---|---|
| Public Key | taypi_pk_test_ + 32 chars hex | Identifica tu comercio. Se envia en el header Authorization. Puede estar en el frontend (Checkout.js). |
| Secret Key | taypi_sk_test_ + 64 chars hex | Firma tus requests con HMAC-SHA256. Solo en tu backend. Nunca la expongas. |
En produccion, los prefijos cambian a taypi_pk_live_ y taypi_sk_live_.
Almacenamiento seguro
REGLAS DE SEGURIDAD
- Nunca incluyas la secret key en codigo frontend (HTML, JavaScript del navegador, apps moviles).
- Nunca la subas a repositorios (Git, SVN). Usa
.envo un gestor de secretos. - Nunca la envies por email, chat, o cualquier canal no cifrado.
- Nunca la registres en logs de aplicacion.
Almacena tus claves en variables de entorno:
# .env
TAYPI_PUBLIC_KEY=taypi_pk_test_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4
TAYPI_SECRET_KEY=taypi_sk_test_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4TIP
Si crees que tu secret key fue comprometida, revocala inmediatamente desde el panel y genera una nueva.
Firma HMAC-SHA256
Cada request a la API debe incluir una firma HMAC-SHA256 en el header Taypi-Signature. La firma garantiza que:
- El request fue enviado por alguien que posee la secret key.
- El contenido del request no fue modificado en transito.
- El request no es una repeticion de otro (proteccion anti-replay via timestamp).
¿Como se construye la firma?
La firma se calcula sobre un string de firma con el siguiente formato:
{timestamp}\n{method}\n{path}\n{body}Donde:
| Componente | Descripcion | Ejemplo |
|---|---|---|
timestamp | Timestamp UNIX en segundos (mismo valor del header Taypi-Timestamp) | 1710504600 |
method | Metodo HTTP en mayusculas | POST |
path | Ruta del endpoint (sin dominio, con query string si existe) | /api/v1/payments |
body | Body del request tal cual se envia (string JSON). Cadena vacia si no hay body (GET, DELETE). | {"amount":"50.00","currency":"PEN"} |
Los componentes se unen con el caracter de nueva linea \n (literal, no \\n).
Ejemplo paso a paso
Supongamos que quieres crear un pago:
1. Datos del request:
Metodo: POST
URL: https://sandbox.taypi.pe/api/v1/payments
Body: {"amount":"50.00","currency":"PEN","reference":"ORD-12345"}
Timestamp: 1710504600
Secret Key: taypi_sk_test_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d42. Construir el string de firma:
1710504600\nPOST\n/api/v1/payments\n{"amount":"50.00","currency":"PEN","reference":"ORD-12345"}3. Calcular HMAC-SHA256:
HMAC-SHA256(secret_key, string_de_firma) → hex_digest4. Enviar en el header:
Taypi-Signature: {hex_digest}Implementacion en PHP
$timestamp = time();
$method = 'POST';
$path = '/api/v1/payments';
$body = json_encode([
'amount' => '50.00',
'currency' => 'PEN',
'reference' => 'ORD-12345',
]);
$secretKey = getenv('TAYPI_SECRET_KEY');
// Construir string de firma
$signatureString = implode("\n", [$timestamp, $method, $path, $body]);
// Calcular HMAC-SHA256
$signature = hash_hmac('sha256', $signatureString, $secretKey);
// Enviar request
$ch = curl_init('https://sandbox.taypi.pe/api/v1/payments');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . getenv('TAYPI_PUBLIC_KEY'),
'Taypi-Signature: ' . $signature,
'Taypi-Timestamp: ' . $timestamp,
'Idempotency-Key: ' . bin2hex(random_bytes(16)),
],
]);
$response = curl_exec($ch);
curl_close($ch);Implementacion en JavaScript (Node.js)
import crypto from 'crypto';
const timestamp = Math.floor(Date.now() / 1000).toString();
const method = 'POST';
const path = '/api/v1/payments';
const body = JSON.stringify({
amount: '50.00',
currency: 'PEN',
reference: 'ORD-12345',
});
const secretKey = process.env.TAYPI_SECRET_KEY;
// Construir string de firma
const signatureString = [timestamp, method, path, body].join('\n');
// Calcular HMAC-SHA256
const signature = crypto
.createHmac('sha256', secretKey)
.update(signatureString)
.digest('hex');
// Enviar request
const response = await fetch('https://sandbox.taypi.pe/api/v1/payments', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.TAYPI_PUBLIC_KEY}`,
'Taypi-Signature': signature,
'Taypi-Timestamp': timestamp,
'Idempotency-Key': crypto.randomUUID(),
},
body,
});Implementacion en C# (.NET)
using System.Security.Cryptography;
using System.Text;
var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
var method = "POST";
var path = "/api/v1/payments";
var body = """{"amount":"50.00","currency":"PEN","reference":"ORD-12345"}""";
var secretKey = Environment.GetEnvironmentVariable("TAYPI_SECRET_KEY")!;
// Construir string de firma
var signatureString = $"{timestamp}\n{method}\n{path}\n{body}";
// Calcular HMAC-SHA256
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secretKey));
var hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(signatureString));
var signature = Convert.ToHexString(hashBytes).ToLowerInvariant();
// Enviar request
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.taypi.pe/api/v1/payments");
request.Content = new StringContent(body, Encoding.UTF8, "application/json");
request.Headers.Add("Authorization", $"Bearer {Environment.GetEnvironmentVariable("TAYPI_PUBLIC_KEY")}");
request.Headers.Add("Taypi-Signature", signature);
request.Headers.Add("Taypi-Timestamp", timestamp);
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
var response = await client.SendAsync(request);Implementacion con curl
TIMESTAMP=$(date +%s)
PUBLIC_KEY="taypi_pk_test_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4"
SECRET_KEY="taypi_sk_test_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4"
BODY='{"amount":"50.00","currency":"PEN","reference":"ORD-12345"}'
# Construir string de firma (printf interpreta \n como newline)
SIGNATURE_STRING=$(printf '%s\n%s\n%s\n%s' "$TIMESTAMP" "POST" "/api/v1/payments" "$BODY")
# Calcular HMAC-SHA256
SIGNATURE=$(echo -n "$SIGNATURE_STRING" | openssl dgst -sha256 -hmac "$SECRET_KEY" | cut -d' ' -f2)
# Enviar request
curl -X POST https://sandbox.taypi.pe/api/v1/payments \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PUBLIC_KEY" \
-H "Taypi-Signature: $SIGNATURE" \
-H "Taypi-Timestamp: $TIMESTAMP" \
-H "Idempotency-Key: $(uuidgen)" \
-d "$BODY"Validacion de timestamp
TAYPI rechaza requests con un Taypi-Timestamp que difiera mas de 5 minutos (300 segundos) del tiempo actual del servidor. Esto previene ataques de replay.
| Error | Codigo | HTTP |
|---|---|---|
Header Taypi-Timestamp ausente | AUTH_TIMESTAMP_MISSING | 400 |
| Timestamp no es un numero valido | AUTH_TIMESTAMP_INVALID | 400 |
| Timestamp fuera de la ventana de 5 minutos | AUTH_TIMESTAMP_EXPIRED | 401 |
SINCRONIZA TU RELOJ
Asegurate de que el reloj de tu servidor este sincronizado con NTP. Un reloj desincronizado causara rechazos constantes.
Idempotencia
El header Idempotency-Key evita que un request se procese dos veces, incluso si lo envias multiples veces (por ejemplo, por un timeout de red).
¿Como funciona?
- Envias un request
POSTconIdempotency-Key: {uuid_unico}. - TAYPI guarda la respuesta asociada a esa key (TTL de 24 horas).
- Si envias otro request con la misma
Idempotency-Key, TAYPI retorna la respuesta guardada sin re-procesar.
Reglas
| Regla | Detalle |
|---|---|
| Obligatorio en | Todos los requests POST |
| Formato | UUID v4 o cualquier string unico (max 255 chars) |
| TTL | 24 horas desde el primer uso |
| Conflicto | Si envias la misma key con body diferente, obtienes error IDEMPOTENCY_CONFLICT (409) |
# Primer envio — crea el pago
curl -X POST https://sandbox.taypi.pe/api/v1/payments \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-d '{"amount":"50.00","currency":"PEN","reference":"ORD-123"}'
# → 201 Created
# Segundo envio (misma key, mismo body) — retorna la misma respuesta
curl -X POST https://sandbox.taypi.pe/api/v1/payments \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-d '{"amount":"50.00","currency":"PEN","reference":"ORD-123"}'
# → 200 OK (respuesta cacheada, no se creo otro pago)
# Tercer envio (misma key, body diferente) — error
curl -X POST https://sandbox.taypi.pe/api/v1/payments \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-d '{"amount":"99.00","currency":"PEN","reference":"ORD-456"}'
# → 409 Conflict: IDEMPOTENCY_CONFLICTTIP
Genera un UUID nuevo para cada operacion distinta. No reutilices keys entre operaciones diferentes.
Rate limiting
TAYPI limita a 60 requests por minuto por comercio, usando una ventana deslizante (sliding window).
Headers de respuesta
Cada respuesta incluye headers que indican tu consumo actual:
| Header | Descripcion | Ejemplo |
|---|---|---|
X-RateLimit-Limit | Limite maximo por minuto | 60 |
X-RateLimit-Remaining | Requests restantes en la ventana actual | 45 |
X-RateLimit-Reset | Timestamp UNIX cuando se reinicia la ventana | 1710504660 |
Cuando se excede el limite
{
"code": "RATE_LIMIT_EXCEEDED",
"message": "Demasiadas solicitudes. Intenta en un momento.",
"http_status": 429
}El header Retry-After indica cuantos segundos esperar antes de reintentar.
Recomendaciones
- Implementa backoff exponencial en tu cliente HTTP.
- Si necesitas mas de 60 req/min, contacta al equipo de TAYPI para evaluar un aumento de limite.
HTTPS obligatorio
Toda comunicacion con la API de TAYPI debe usar HTTPS (TLS 1.2 o superior). TAYPI rechaza conexiones HTTP en produccion.
- Sandbox:
https://sandbox.taypi.pe - Produccion:
https://app.taypi.pe
SIN EXCEPCIONES
Requests via HTTP plano seran rechazados. Esto protege tus API keys y datos de pago contra interceptacion.
Resumen de headers requeridos
| Header | Requerido | Descripcion |
|---|---|---|
Authorization | Siempre | Bearer {public_key} |
Taypi-Signature | Siempre | Firma HMAC-SHA256 del request |
Taypi-Timestamp | Siempre | Timestamp UNIX en segundos |
Idempotency-Key | Solo en POST | UUID unico por operacion |
Content-Type | Solo con body | application/json |