Skip to content

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:

ClaveFormatoProposito
Public Keytaypi_pk_test_ + 32 chars hexIdentifica tu comercio. Se envia en el header Authorization. Puede estar en el frontend (Checkout.js).
Secret Keytaypi_sk_test_ + 64 chars hexFirma 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 .env o 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
# .env
TAYPI_PUBLIC_KEY=taypi_pk_test_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4
TAYPI_SECRET_KEY=taypi_sk_test_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4

TIP

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:

ComponenteDescripcionEjemplo
timestampTimestamp UNIX en segundos (mismo valor del header Taypi-Timestamp)1710504600
methodMetodo HTTP en mayusculasPOST
pathRuta del endpoint (sin dominio, con query string si existe)/api/v1/payments
bodyBody 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_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4

2. 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_digest

4. Enviar en el header:

Taypi-Signature: {hex_digest}

Implementacion en PHP

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)

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)

csharp
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

bash
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.

ErrorCodigoHTTP
Header Taypi-Timestamp ausenteAUTH_TIMESTAMP_MISSING400
Timestamp no es un numero validoAUTH_TIMESTAMP_INVALID400
Timestamp fuera de la ventana de 5 minutosAUTH_TIMESTAMP_EXPIRED401

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?

  1. Envias un request POST con Idempotency-Key: {uuid_unico}.
  2. TAYPI guarda la respuesta asociada a esa key (TTL de 24 horas).
  3. Si envias otro request con la misma Idempotency-Key, TAYPI retorna la respuesta guardada sin re-procesar.

Reglas

ReglaDetalle
Obligatorio enTodos los requests POST
FormatoUUID v4 o cualquier string unico (max 255 chars)
TTL24 horas desde el primer uso
ConflictoSi envias la misma key con body diferente, obtienes error IDEMPOTENCY_CONFLICT (409)
bash
# 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_CONFLICT

TIP

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:

HeaderDescripcionEjemplo
X-RateLimit-LimitLimite maximo por minuto60
X-RateLimit-RemainingRequests restantes en la ventana actual45
X-RateLimit-ResetTimestamp UNIX cuando se reinicia la ventana1710504660

Cuando se excede el limite

json
{
    "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

HeaderRequeridoDescripcion
AuthorizationSiempreBearer {public_key}
Taypi-SignatureSiempreFirma HMAC-SHA256 del request
Taypi-TimestampSiempreTimestamp UNIX en segundos
Idempotency-KeySolo en POSTUUID unico por operacion
Content-TypeSolo con bodyapplication/json

Plataforma de pagos QR interoperables para Perú