Saltar al contenido

Confirmar pago

API v0.1.0
En 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/json

Sin 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

HTTPdetailCausaSolución
400payment_expiredPasaron >15 min desde creación. Auto-cancelado.Crear nuevo intent.
401authentication_requiredNi sk_ ni client_secret en el request.Adjuntar uno de los dos.
403invalid_client_secretclient_secret no corresponde al intent del path.Verificar pareo correcto.
404payment_intent_not_foundintent_id no existe.Verificar valor.
409invalid_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% failed con failure_message=fondos_insuficientes

Latencia simulada: 1-3 segundos.