Integraciones
Webhooks
Transferfix envía eventos firmados a tu endpoint cada vez que una transferencia impacta el estado de una orden. Registrás la URL y el secret una sola vez al crear la cuenta y nosotros nos encargamos del resto.
Configuración — sin panel
No hay panel de configuración. Enviás webhook_url y webhook_secret la primera vez que creás una cuenta y queda registrado para toda tu empresa.
1. Generá un secret
2. Enviá los campos al crear
webhook_url + webhook_secret en POST /api/v2/accounts. Se registra una vez y se reutiliza.3. Manejá los eventos
curl -X POST "https://transferfix.com.ar/api/v2/accounts" \
-H "X-API-Key: ck_live_..." \
-H "Content-Type: application/json" \
-d '{
"customer_email": "buyer@example.com",
"total_amount": 12500,
"currency": "ARS",
"order_reference": "PIANTAO-ABC",
"webhook_url": "https://yourapp.com/api/webhooks/transferfix",
"webhook_secret": "at-least-32-chars-random-hex-string"
}'Rotación
webhook_url/webhook_secret en el próximo POST /api/v2/accounts. La rotación es instantánea — entregas en curso aún pueden firmar con el secret viejo, por eso conviene aceptar ambos durante la ventana de rollout.Eventos
Cada cambio relevante dispara un evento.
payment.paid
payment.overpaid
total_amount. Revisá o aceptá según tu lógica.payment.partially_paid
payment.paid.Headers y payload
POST {your_webhook_url}
Content-Type: application/json
User-Agent: Transferfix-Webhook/1.0
X-Transferfix-Event: payment.paid
X-Transferfix-Delivery-Id: 01J5Z...
X-Transferfix-Timestamp: 1745245680
X-Transferfix-Signature: sha256=4a9f...{
"event": "payment.paid",
"delivery_id": "01J5Z...",
"account_id": 1234,
"order_reference": "api_PIANTAO-ABC",
"client_order_reference": "PIANTAO-ABC",
"metadata": { "event_id": "rock-2024", "seat": "F12" },
"customer_email": "buyer@example.com",
"cucuru_alias": "pianta.entrada-0014",
"cucuru_collection_id": "bf3f72c0-9b8c-4018-8f0a-8a17c52b1cec",
"amount_paid": 12500,
"total_amount": 12500,
"payment_amount": 12500,
"currency": "ARS",
"status": "PAID",
"alias_payment_count": 1,
"is_first_payment": true,
"timestamp": "2026-04-21T18:48:00.000Z"
}Verificación de firma
La firma se calcula sobre `${timestamp}.${raw_body}` con HMAC-SHA256 y se envía en hex minúscula como `sha256=...`.
import { createHmac, timingSafeEqual } from 'crypto'
function verifyTransferfixSignature(
rawBody: string,
headers: Record<string, string>,
secret: string
): boolean {
const ts = headers['x-transferfix-timestamp']
const given = (headers['x-transferfix-signature'] || '').replace(/^sha256=/, '')
if (!ts || !given) return false
const age = Math.floor(Date.now() / 1000) - parseInt(ts, 10)
if (Math.abs(age) > 300) return false // reject > 5 min
const expected = createHmac('sha256', secret)
.update(`${ts}.${rawBody}`)
.digest('hex')
try {
return timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(given, 'hex'))
} catch {
return false
}
}<?php
function verify_transferfix_signature($raw_body, $headers, $secret) {
$ts = $headers['X-Transferfix-Timestamp'] ?? '';
$given = str_replace('sha256=', '', $headers['X-Transferfix-Signature'] ?? '');
if (!$ts || !$given) return false;
if (abs(time() - intval($ts)) > 300) return false;
$expected = hash_hmac('sha256', "$ts.$raw_body", $secret);
return hash_equals($expected, $given);
}Reglas
timingSafeEqual / hash_equals). Rechazá timestamps con más de 5 minutos de diferencia para prevenir replay. Leé el body crudo — no parsees JSON antes de firmar.Política de reintentos
6 intentos sobre ~20 horas. Reintentos sólo ante errores de red, 5xx, 408 y 429.
| Intento | Delay hasta siguiente |
|---|---|
| 1 | 1 min |
| 2 | 5 min |
| 3 | 30 min |
| 4 | 2 h |
| 5 | 6 h |
| 6 | 12 h (final) |
Requisitos de la respuesta
DEAD. Para operaciones lentas, respondé 2xx rápido y encolá el trabajo real en background.Idempotencia
Cada entrega trae X-Transferfix-Delivery-Id. En reintentos es el mismo.
Patrón del handler
delivery_id en una tabla con unique constraint y cortá si ya lo viste. También podés usar cucuru_collection_id para deduplicar por pago en el banco.Interno: banco → Transferfix
Contexto operacional. Esto NO es algo que tu backend consume — es cómo nos llega la notificación del banco.
/api/v1/webhooks/collection_receivedEndpoint interno. El banco nos llama, procesamos el pago y emitimos el webhook saliente a tu URL.
{
"collection_id": "bf3f72c0-9b8c-4018-8f0a-8a17c52b1cec",
"customer_id": "pianta.entrada-0014",
"amount": 12500,
"currency_id": "ARS"
}