Guía de integración · PRIVORA con Titanio Pay
Para el equipo de PRIVORA. Responde, punto por punto, al contrato que pide el ADR-006 de ~/privora/docs/DECISIONES.md, con los nombres reales que publica Titanio 2.0.0. Referencia completa: docs/API.md y https://ceoo.world/pago/docs.
1. Modelo: una plataforma, un comercio por fraccionamiento
- PRIVORA tiene una llave de plataforma (
tp_privora_…, rolplataforma). La entrega CEOO; está en la bóvedaBACKUP 2025/CLAVES_PAGOS/titanio_comercio_privora.json. Va cifrada en PRIVORA y nunca llega al navegador. - Cada fraccionamiento se da de alta como comercio hijo con
POST /api/v1/comercios, con su CLABE, su banco, su código de participante SPEI (banco_spei) y la comisión de plataforma pactada. Titanio valida el dígito verificador de la CLABE. - PRIVORA opera cada fraccionamiento con su llave de plataforma más
Titanio-Comercio: <id>. Otro fraccionamiento no ve nada del primero (probado: 404 y 403). - El dinero llega a la cuenta del fraccionamiento. Sin CLABE propia, Titanio no emite la ficha (409).
2. Lo que pidió el ADR-006 y cómo queda
| ADR-006 pide | Titanio 2.0.0 | Nota |
|---|---|---|
POST /api/cobro con x-integration-token por comercio | POST /api/v1/cobros con Authorization: Bearer <llave de plataforma> + Titanio-Comercio | La ruta heredada sigue viva para Capital Trading; la nueva es la de PRIVORA |
Idempotency-Key: prv-<fee_assessment_id>-<intento> | Igual, obligatoria | Misma llave + mismo cuerpo = mismo cobro; otro cuerpo = 409 |
monto_centavos entero, moneda: "MXN", concepto | Igual | Decimales = 400 |
referencia_externa: "PRV-<community_id>-<fee_assessment_id>" | Igual; viaja en cada evento y se puede buscar: GET /api/v1/cobros?referencia_externa=… | |
metodos (tarjeta, spei, oxxo) | Se acepta la lista; Titanio toma el primero que puede atender hoy (hoy: spei) | Tarjeta y OXXO para terceros requieren contrato con agregador |
notificar_url por cobro | Webhook por comercio (POST /api/v1/comercio/webhook) o el de la plataforma, que recibe los eventos de todos sus hijos | Una URL por comercio evita que un cobro desvíe notificaciones |
metadatos (comunidad, predio, periodo) | Igual, hasta 20 claves | |
Cabeceras X-Titanio-Evento (id único) y X-Titanio-Firma: t=…,v1=… | Se envían exactamente así (además de Titanio-Evento-Id y Titanio-Firma) | v1 = HMAC-SHA256(secreto, t + "." + cuerpo crudo) |
GET /api/cobro/<id> para releer | GET /api/v1/cobros/<id> | La verdad es la consulta |
Qué secreto firma: si el webhook es el del fraccionamiento, firma con el secreto de ese fraccionamiento (compuerta 1 del ADR tal cual). Si PRIVORA usa un solo webhook de plataforma, los eventos de todos los hijos llegan firmados con el secreto de la plataforma y el campo comercio del evento dice de qué fraccionamiento es.
3. Las cinco compuertas de PRIVORA, con Titanio
- Firma válida y
tdentro de ±300 s: Titanio firma cada envío en el momento del intento (los reintentos llevantnuevo). - Evento no visto:
X-Titanio-Eventoes único por evento y se repite idéntico en los reintentos. - Relectura:
GET /api/v1/cobros/<id>conTitanio-Comercio. - Cuadre: el cobro trae
comercio,referencia_externa,monto_centavos,monedaypago.monto_centavos(lo efectivamente pagado; puede ser mayor si hubo excedente, en cuyo casopago.excedente_centavos). - Transacción local en PRIVORA.
4. Cuotas recurrentes
Dos caminos, a elegir:
- PRIVORA genera las cuotas (ya tiene
fee_assessments) y crea un cobro por cuota conPOST /api/v1/cobros, usandotramossi quiere descuento por pronto pago o recargo. - Titanio genera las cuotas:
POST /api/v1/planes+POST /api/v1/suscripciones(una por predio). El trabajador de Titanio emite un cobro por periodo, sin duplicar, con tramos de pronto pago, normal y recargo; los avisos quedan enGET /api/v1/avisosy llegan comoaviso.creadopara que PRIVORA los envíe.
5. Conciliación
PRIVORA (o tesorería del fraccionamiento) sube el estado de cuenta del fraccionamiento en CSV a POST /api/v1/conciliacion/spei. Cada abono casado dispara cobro.pagado; lo que no cuadra dispara conciliacion.excepcion y queda en GET /api/v1/conciliacion/excepciones para la tabla payment_reconciliations.
6. Pruebas sin dinero
CEOO puede encender el sandbox (metodo: "sandbox"): el cobro queda pendiente y se paga con una notificación firmada al endpoint del proveedor sandbox. Sirve para probar todo el ciclo de PRIVORA (webhook, relectura, cuadre) sin mover un peso.
7. Modo real (23-oct-2026)
Ricardo Estrada liberó el esquema (cargo directo a la cuenta del fraccionamiento, comisión facturada aparte por CEOO) el 23-oct-2026. Ver docs/RIESGOS_REGULATORIOS.md.
- Riel de tarjeta y OXXO en modo real. Se enciende solo con las dos cosas a la vez: la llave viva de la cuenta de CEOO en el archivo de secretos y la bandera explícita (
POST /api/v1/admin/stripe {"modo":"real"}con la llave de administración). Una llave viva puesta en el campo de prueba bloquea el riel. Los eventos de modo de prueba se rechazan en real (compuertalivemode). - Dos avisos de Stripe, uno de la cuenta (ventas de CEOO) y otro de Connect (cuentas conectadas), cada uno con su secreto; Titanio acepta la firma de cualquiera de los dos.
- Comprobado sin cobrar: se abrió una sesión de Checkout viva de $10.00 que nadie pagó y se expiró por la API; Stripe envió
checkout.session.expiredfirmado, Titanio validó la firma, releyó la sesión y pasó el cobro aexpirado. - Falta, y solo lo puede dar el titular: activar Stripe Connect en la cuenta de CEOO. Sin eso, las cuotas con tarjeta u OXXO no se pueden cobrar a nombre de un fraccionamiento; SPEI a la CLABE de cada fraccionamiento sí funciona.