Reenvío de Webhook
Descripción General
El endpoint de Reenvío de Webhook le permite solicitar el reenvío manual de notificaciones para transacciones específicas. Esto es útil en escenarios donde:
- Su servidor no estaba disponible cuando se envió el webhook original
- Necesita reprocesar una transacción específica
- Desea probar temporalmente la integración con una URL diferente
Este endpoint no cambia la configuración de webhook de su cuenta. La URL proporcionada se usa solo para el reenvío específico.
Cómo Funciona
Identificación de la Transacción
El endpoint acepta tres tipos de identificadores:
| Tipo | Descripción | Alcance |
|---|---|---|
| ID Numérico | ID interno de la transacción (campo transactionId en webhooks) | Global |
| ID Externo | Identificador proporcionado por usted al crear (campo externalId) | Único por cuenta |
| ID End-to-End | Identificador PIX del BACEN (campo endToEndId, formato: E/D + 32 caracteres) | Único por transacción |
El sistema busca simultáneamente en todos los tipos de identificador en su cuenta. En la práctica no hay ambigüedad: el id numérico es puramente dígitos; el e2eId comienza con 'E' o 'D' seguido de 32 caracteres alfanuméricos; el externalId es cualquier cadena proporcionada por usted.
¿Qué identificador usar? Use el transactionId numérico retornado por Avista, el externalId que proporcionó al crear la transacción, o el endToEndId del PIX recibido en webhooks. Todos son igualmente válidos.
Procesamiento Síncrono
El reenvío de webhook se procesa síncronamente. Esto significa:
- La solicitud espera a que se complete la entrega del webhook
- El resultado se comunica mediante código de estado HTTP (200, 502, 504)
- El tiempo de respuesta depende de la latencia de su servidor (timeout: 10s)
flowchart TD
A[Resend Request] --> B{URL provided?}
B -->|Yes| C[Use temporary URL]
B -->|No| D{Webhook configured?}
D -->|Yes| E[Use configured URL]
D -->|No| F[Error 400: No URL]
C --> G[Send Webhook HTTP]
E --> G
G --> H{Server response}
H -->|2xx| I[HTTP 200: Success]
H -->|4xx/5xx| J[HTTP 502: Bad Gateway]
H -->|Timeout| K[HTTP 504: Gateway Timeout]
I --> L[Record audit log]
J --> L
K --> L
L --> M[Return result]A diferencia de los webhooks automáticos (que usan colas con reintento), el reenvío manual se ejecuta inmediatamente y retorna el resultado en la misma solicitud.
Casos de Uso
1. Reenvío a URL Configurada
Si ya tiene un webhook configurado en su cuenta, simplemente llame al endpoint sin cuerpo:
curl -X POST https://api.safirapay.com/api/resend-webhook/external-teste-001 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json"2. Reenvío con URL Temporal
Para probar con una URL diferente o reenviar a un endpoint de contingencia:
curl -X POST https://api.safirapay.com/api/resend-webhook/external-teste-001 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://my-backup-server.com/webhooks/avista"
}'La URL temporal no se persiste. El próximo webhook automático se enviará a la URL configurada en la cuenta.
Respuesta
Éxito (200)
Webhook enviado exitosamente a la URL de destino.
{
"message": "Webhook resent successfully",
"webhookLogId": 12345,
"sentAt": "2024-01-15T10:30:00.000Z",
"statusCode": 200
}Error: Sin URL Configurada (400)
{
"statusCode": 400,
"message": "No webhook configured and no override URL provided",
"error": "Bad Request"
}Error: Transacción No Encontrada (404)
{
"statusCode": 404,
"message": "Transaction not found",
"error": "Not Found"
}Error: Destino Retornó Error (502)
El servidor de destino retornó un error (4xx o 5xx) o hubo un fallo de conexión.
{
"statusCode": 502,
"message": "Webhook failed with status 500",
"webhookLogId": 12345,
"sentAt": "2024-01-15T10:30:00.000Z"
}Incluso en caso de error, el webhook se registra en el log de auditoría. Use el webhookLogId para seguimiento.
Error: Timeout (504)
El servidor de destino no respondió dentro del límite de tiempo (10 segundos).
{
"statusCode": 504,
"message": "Timeout after 10000ms",
"webhookLogId": 12345,
"sentAt": "2024-01-15T10:30:00.000Z"
}Si experimenta timeouts frecuentes, verifique que su servidor responda en menos de 10 segundos.
Límite de Frecuencia
Este endpoint tiene un límite de frecuencia de 60 solicitudes por minuto por cuenta para prevenir abuso.
Si se excede el límite, recibirá un error 429 Too Many Requests:
{
"statusCode": 429,
"message": "Too Many Requests"
}Auditoría
Todos los reenvíos manuales se registran para fines de auditoría y trazabilidad:
| Información | Descripción |
|---|---|
| Tipo de envío | Marcado como reenvío manual |
| URL utilizada | Registra si se usó la URL temporal o configurada |
| Resultado | Estado HTTP y tiempo de respuesta |
| Identificador | ID de log único para seguimiento |
Use el webhookLogId retornado en la respuesta para correlacionar con los logs de soporte si es necesario.
Ejemplos de Integración
const axios = require('axios');
async function resendWebhook(transactionId, overrideUrl = null) {
const config = {
headers: {
'Authorization': `Bearer ${process.env.SAFIRAPAY_TOKEN}`,
'Content-Type': 'application/json'
}
};
const body = overrideUrl ? { url: overrideUrl } : {};
try {
const response = await axios.post(
`https://api.safirapay.com/api/resend-webhook/${transactionId}`,
body,
config
);
console.log('Webhook resent:', response.data);
return response.data;
} catch (error) {
console.error('Error resending webhook:', error.response?.data);
throw error;
}
}
// Usage
resendWebhook('external-teste-001');
resendWebhook('external-teste-001', 'https://backup.mysite.com/webhook'); // With temporary URLimport requests
import os
def resend_webhook(transaction_id: str, override_url: str = None):
headers = {
'Authorization': f'Bearer {os.environ["SAFIRAPAY_TOKEN"]}',
'Content-Type': 'application/json'
}
body = {'url': override_url} if override_url else {}
response = requests.post(
f'https://api.safirapay.com/api/resend-webhook/{transaction_id}',
json=body,
headers=headers
)
response.raise_for_status()
return response.json()
# Usage
result = resend_webhook('external-teste-001')
print(f"Webhook resent: {result}")
# With temporary URL
result = resend_webhook('external-teste-001', 'https://backup.mysite.com/webhook')using System.Net.Http;
using System.Text;
using System.Text.Json;
public class SafiraPayClient
{
private readonly HttpClient _client;
private readonly string _token;
public SafiraPayClient(string token)
{
_client = new HttpClient();
_token = token;
_client.DefaultRequestHeaders.Add("Authorization", $"Bearer {_token}");
}
public async Task<ResendWebhookResponse> ResendWebhookAsync(
string transactionId,
string overrideUrl = null)
{
var url = $"https://api.safirapay.com/api/resend-webhook/{transactionId}";
var body = overrideUrl != null
? JsonSerializer.Serialize(new { url = overrideUrl })
: "{}";
var content = new StringContent(body, Encoding.UTF8, "application/json");
var response = await _client.PostAsync(url, content);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<ResendWebhookResponse>(json);
}
}