Confirmar pago
API v0.1.0En esta página
Confirma un payment_intent con el OTP que el banco envió al cliente. Hay dos modos: frontend con client_secret o backend con sk_.
Endpoint
POST/v1/payments/{intent_id}/confirm
Modo widget (frontend con client_secret)
Recomendado cuando el cliente final escribe el OTP en una UI servida en tu sitio.
bash
Content-Type: application/jsonSin Authorization header. El client_secret es la autenticación. CORS abierto.
Body:
json
{
"client_secret": "pi_L9limdfjZ6SHJjpkXueO1w_secret_tjxQABDZZWMokatGobBsZocUItksOJIZ",
"otp": "123456"
}Modo backend (con sk_)
Más seguro pero menos UX (recolectar OTP server-side requiere otro round-trip):
bash
curl -X POST https://rutiva-api.onrender.com/v1/payments/$INTENT_ID/confirm \
-H "Authorization: Bearer sk_live_xxxx" \
-H "Content-Type: application/json" \
-d '{"otp":"123456"}'Si envías sk_ y client_secret juntos, prevalece sk_.
Respuesta 200 OK
Éxito:
json
{
"id": "8d3a4b5c-2e74-41f6-a091-f7036f8d6a8c",
"external_id": "pi_L9limdfjZ6SHJjpkXueO1w",
"status": "succeeded",
"bank_reference": "MOCK-A1B2C3D4E5F6",
"succeeded_at": "2026-05-13T15:18:42",
"amount_cents": 50000,
"currency": "VES"
}Rechazo del banco:
json
{
"status": "failed",
"failure_code": "bank_declined",
"failure_message": "fondos_insuficientes",
"failed_at": "2026-05-13T15:18:42"
}Errores comunes
| HTTP | detail | Causa | Solución |
|---|---|---|---|
| 400 | payment_expired | Pasaron >15 min desde creación. Auto-cancelado. | Crear nuevo intent. |
| 401 | authentication_required | Ni sk_ ni client_secret en el request. | Adjuntar uno de los dos. |
| 403 | invalid_client_secret | client_secret no corresponde al intent del path. | Verificar pareo correcto. |
| 404 | payment_intent_not_found | intent_id no existe. | Verificar valor. |
| 409 | invalid_state:<estado> | Ya succeeded/failed/canceled. | No reconfirmar (estado terminal). |
Ejemplo React
javascript
async function confirmPayment(intentId, clientSecret, otp) {
const resp = await fetch(
`https://rutiva-api.onrender.com/v1/payments/${intentId}/confirm`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ client_secret: clientSecret, otp }),
}
);
if (!resp.ok) {
const err = await resp.json();
throw new Error(err.detail);
}
return resp.json();
}Modo test (MockBankAdapter)
Con sk_test_, el OTP no se valida. Pasa cualquier valor de 4-12 caracteres. El resultado es aleatorio:
- ~80%
succeeded - ~20%
failedconfailure_message=fondos_insuficientes
Latencia simulada: 1-3 segundos.