Saltar al contenido

Buenas prácticas

API v0.1.0
En esta página

Checklist para integraciones de producción. Cubre seguridad, robustez, validación y UX.

Seguridad

  • sk_ solo en variables de entorno del servidor. Nunca en repos, bundles JS, o logs.
  • Verifica firma de webhooks siempre. Sin verificación, cualquiera puede simular eventos.
  • TLS obligatorio en URLs de webhook receptoras.
  • Rotación de keys periódica. Si expones una key, contacta soporte.
  • No loguees client_secret ni OTPs. Sensibles.

Robustez

  • Idempotency-Key en cada create, idealmente UUID v4 por intento lógico. Evita cobros dobles.
  • Reintentos con backoff exponencial ante 5xx. Nunca ante 4xx.
  • Manejo de expiración: si tu UI demora más de 15 min, crea un intent nuevo.
  • Procesamiento idempotente de webhooks: el mismo evento puede llegar más de una vez (en futuras versiones).
  • Loguea external_id (pi_xxx) de cada intent. Sirve para soporte y reconciliación.

Validación

  • Valida datos venezolanos en tu UI antes de mandar: teléfono ^04\d{9}$, cédula ^[VEJGP]\d{6,9}$, RIF ^[VEJGP]-\d{8}-\d$. Rutiva re-valida en el borde, pero te ahorra round-trips.
  • amount en céntimos, no decimales. Bs. 500,00 = 50000.
  • Currency case-sensitive: "VES" o "USD" en mayúsculas.

UX

  • Pobla el dropdown de bancos dinámicamente desde /v1/banks. No hardcodees.
  • Muestra bank_reference al cliente tras succeeded. Es la referencia bancaria, útil para soporte.
  • Muestra failure_message legible tras failed. Mensajes vienen del banco.
  • Loading state durante el confirm — el banco demora 1-3s en responder.

Variables de entorno típicas

bash
# Backend del comerciante
RUTIVA_SECRET_KEY=sk_live_xxxxxxxxxxxxxxxx
RUTIVA_BASE_URL=https://rutiva-api.onrender.com
RUTIVA_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxx

# Frontend
VITE_RUTIVA_PUBLISHABLE_KEY=pk_xxx        # widget usa como identificador
VITE_RUTIVA_BASE_URL=https://rutiva-api.onrender.com

Tipos de objeto (TypeScript)

PaymentIntent:

typescript
type PaymentIntent = {
  id: string;                    // UUID interno
  external_id: string;           // pi_xxx
  status: "created" | "succeeded" | "failed" | "canceled";
  amount_cents: number;
  currency: "VES" | "USD";
  customer_phone: string;
  customer_id_document: string;
  customer_bank_code: string;
  bank_reference: string | null;       // solo en succeeded
  failure_code: string | null;          // solo en failed
  failure_message: string | null;       // solo en failed
  expires_at: string;                   // ISO datetime, 15 min post-create
  created_at: string;
  updated_at: string;
  confirmed_at: string | null;
  succeeded_at: string | null;
  failed_at: string | null;
  canceled_at: string | null;
  // client_secret aparece SOLO en la response del create.
};

Bank:

typescript
type Bank = {
  code: string;   // "0114"
  name: string;   // "Banco habilitado"
};

WebhookEvent:

typescript
type WebhookEvent = {
  type: "payment_intent.created"
      | "payment_intent.succeeded"
      | "payment_intent.failed"
      | "payment_intent.canceled";
  data: PaymentIntent;
};

Recursos