Skip to content

Toku · Eventos (pagos / cobranza)

Webhooks de Toku (pagos/cobranza) → Cloud Run valida la firma HMAC y publica a Pub/Sub por tipo de evento → otro Cloud Run escribe directo a BigQuery solvento-data-prod.toku.* (sin Dataflow). Volumen bajo, streaming.

¿Llegaste hasta acá desde…?

Si ves… Eso sale de… La lógica está en…
Transacciones / pagos de Toku en análisis solvento-data-prod.toku.transaction · toku.payment · toku.payment_intent Ingesta directa (sin lógica de negocio); ver "Paso a paso"
Liquidaciones / payouts de Toku solvento-data-prod.toku.settlement (eventos payout_mx.*) toku-write-bigquery → cloud_run/app.py (remapea payout_mx → tabla settlement)
Clientes / suscripciones / facturas de Toku solvento-data-prod.toku.customer · toku.subscription · toku.invoice Ingesta directa por categoría de event_type

Resumen

Campo Valor
Origen Webhook de Toku (pagos / cobranza)
Patrón Event-driven (Cloud Run → Pub/Sub → Cloud Run → BigQuery directo, sin Dataflow)
Destino solvento-data-prod.toku.* (una tabla por categoría de evento)
Frecuencia Streaming (insert por evento)
Criticidad 🟢 Baja (volumen bajo)
Dueño Martin (data eng)
Repos webhook_toku · toku-write-bigquery

Diagrama de linaje

graph LR
    TOKU[["📨 Toku<br/>webhook pagos/cobranza"]]

    subgraph rcv["1 · Recepción + ruteo"]
        CR1["Cloud Run<br/>webhook-toku-inline-prod<br/><i>valida HMAC · enruta por event_type</i>"]
        T1(["Pub/Sub<br/>TOKU_PAYMENT_INTENT_TOPIC"])
        T2(["Pub/Sub<br/>TOKU_TRANSACTION_TOPIC"])
        T3(["Pub/Sub<br/>TOKU_PAYOUT_MX_TOPIC"])
        T4(["Pub/Sub<br/>otros TOKU_*_TOPIC"])
    end

    subgraph wr["2 · Escritura directa a BigQuery"]
        CR2["Cloud Run<br/>toku-write-bigquery-prod<br/>POST / (push) · insert_rows_json"]
        BQ[("solvento-data-prod.toku.*<br/>customer · payment · payment_intent<br/>transaction · subscription · invoice · settlement")]
    end

    TOKU -->|HTTPS| CR1
    CR1 --> T1 --> CR2
    CR1 --> T2 --> CR2
    CR1 --> T3 --> CR2
    CR1 --> T4 --> CR2
    CR2 --> BQ

    classDef src fill:#fde68a,stroke:#b45309,color:#000;
    classDef store fill:#bfdbfe,stroke:#1e40af,color:#000;
    class TOKU src;
    class BQ store;

Paso a paso

  1. Toku emite un webhook por cada evento (pagos / cobranza). El event_type viene en el payload (ej. payment_intent.succeeded, transaction.success, payout_mx.success, customer.created, subscription.updated, invoice.created).
  2. Cloud Run webhook-toku-inline-prod (webhook_toku, cloud_run/app.py) recibe el POST en /toku (y /webhook/toku):
    • Valida la firma HMAC-SHA256: lee el header Toku-Signature (t=<timestamp>,s=<firma>), verifica que el timestamp no tenga más de 300 s (anti-replay) y compara la firma con hmac.compare_digest usando el secreto TOKU_WEBHOOK_SECRET. Mismatch → 401.
    • Enruta por tipo: toma la categoría antes del primer punto del event_type (payment_intent.succeeded → payment_intent) y publica al topic cuyo nombre vive en la env var TOKU_<CATEGORÍA>_TOPIC (ej. TOKU_PAYMENT_INTENT_TOPIC, TOKU_TRANSACTION_TOPIC, TOKU_PAYOUT_MX_TOPIC, TOKU_CUSTOMER_TOPIC, TOKU_SUBSCRIPTION_TOPIC, TOKU_INVOICE_TOPIC, TOKU_PAYMENT_TOPIC). Sin categoría reconocida → TOKU_UNKNOWN_EVENTS_TOPIC. Los valores literales de los topics se inyectan al desplegar; ver env del servicio en GCP (confirmar nombres exactos).
  3. Pub/Sub desacopla: una suscripción push por topic entrega al siguiente servicio.
  4. Cloud Run toku-write-bigquery-prod (toku-write-bigquery, cloud_run/app.py) recibe el push en POST /, decodifica message.data (base64) y escribe directo a BigQuery con insert_rows_json (streaming insert; no hay Dataflow ni Beam). La tabla se elige por la categoría del evento vía TABLE_CONFIG; crea la tabla si no existe (CREATE TABLE IF NOT EXISTS, columna load_dts TIMESTAMP DEFAULT CURRENT_TIMESTAMP()).
    • Dataset por defecto BIGQUERY_DATASET_ID=toku, proyecto GOOGLE_CLOUD_PROJECT=solvento-data-prod.
    • Errores no recuperables (base64/JSON inválido) → 200 (ack, no reintenta). Errores transitorios de BigQuery → 500 (Pub/Sub reintenta).

Tablas que alimenta

Tabla Contenido Job que la carga
solvento-data-prod.toku.customer Eventos customer.* toku-write-bigquery-prod
solvento-data-prod.toku.payment_intent Eventos payment_intent.* toku-write-bigquery-prod
solvento-data-prod.toku.payment Eventos payment.* toku-write-bigquery-prod
solvento-data-prod.toku.transaction Eventos transaction.* (incluye payment_instrument, customer, payment_intents, wallet_movements) toku-write-bigquery-prod
solvento-data-prod.toku.subscription Eventos subscription.* toku-write-bigquery-prod
solvento-data-prod.toku.invoice Eventos invoice.* toku-write-bigquery-prod
solvento-data-prod.toku.settlement Eventos payout_mx.* (liquidaciones; el servicio remapea la categoría payout_mx → tabla settlement) toku-write-bigquery-prod

Cada fila lleva además id, event_type, webhook_uuid y load_dts.

Consumo aguas abajo

  • Analítica / Metabase sobre solvento-data-prod.toku.* para pagos y cobranza (confirmar uso concreto). Cualquier transformación dbt vive en dbt-etls; criticidad baja, no se rastrean modelos en esta ficha.

Si falla 🛠️

Impacto: bajo (volumen bajo). Si cae el webhook o el writer, dejan de llegar eventos de Toku a BigQuery. Mitigante: Pub/Sub reintenta los push fallidos.

Síntoma Dónde mirar Acción
No llegan eventos nuevos de Toku Logs Cloud Run webhook-toku-inline-prod Ver 401 (firma) o errores; si fue un deploy → rollback
Eventos clasificados como "unknown" Logs del webhook + env var TOKU_<CATEGORÍA>_TOPIC faltante Falta el topic de esa categoría; revisar config del servicio
Llega al topic pero la tabla no avanza Logs Cloud Run toku-write-bigquery-prod; backlog de la suscripción Errores BQ → 500 reintenta solo; persistente → escalar
Falla la firma HMAC (401 masivos) TOKU_WEBHOOK_SECRET en el servicio Verificar el secreto productivo

Escalación: SQL / Metabase → Diego · Airbyte → Gero · resto → Martin.

Enlaces


Última actualización: 2026-06-21 · Si cambia el flujo, los topics o el esquema de los eventos, actualizar esta ficha en el mismo MR.