Integração de pagamento com plataformas externas (LMS)

TLDR: quando o status de um pagamento muda, o PaymentListener chama o PlatformService, que lê checkout.integration e delega ao service da plataforma (Apolo, CBTRG ou Onion) para liberar ou bloquear o acesso do aluno.

Visão geral

O sistema usa um padrão event-driven (pub/sub via Wisper) para notificar plataformas externas. Cada plataforma recebe um webhook com os dados do pagamento e decide liberar ou bloquear o acesso.

A arquitetura tem seis camadas:

Camada Responsabilidade Arquivos
Evento Broadcast Wisper quando o pagamento muda de status app/listeners/payment_listener.rb
Roteamento Lê checkout.integration e direciona para o service correto app/services/platform_service.rb
Service Decide a ação (grant/remove) a partir do status app/services/checkout_integrations/lms/apolo/apolo_service.rb, app/services/cbtrg_service.rb, app/services/onion_service.rb
Módulo HTTP Faz o POST para a API externa com autenticação app/models/apolo.rb, app/models/cbtrg.rb, app/models/onion.rb
Config Define as plataformas disponíveis config/initializers/platform_integrations.rb
Log Registra request e response app/models/webhook_log.rb
Retry Job agendado para re-sync após 5 dias (overdue) app/jobs/platform_run_integration_sync_job.rb

Fluxo

mermaid graph TD A["Payments::Update<br/>broadcast payment_status_changed"] --> B["PaymentListener#payment_status_changed<br/>recebe payment + old_status"] B --> C["PlatformService#process<br/>lê checkout.integration"] C --> D["ApoloService"] C --> E["CbtrgService"] C --> F["OnionService"] C --> G["process_whatsapp"] C --> H["process_noop<br/>(compra avulsa)"] D --> I["create_or_update(execute_now)"] E --> I F --> I I --> J["Módulo HTTP<br/>POST na API externa"] J --> K["WebhookLog"] style C fill:#1f2937,color:#fff style I fill:#374151,color:#fff

Decisão dentro de create_or_update

Status do pagamento Ação Efeito colateral
:paid grant_user_access Envia payload com status enabled/paid
:overdue remove_user_access Agenda re-sync em 5 dias via PlatformRunIntegrationSyncJob.set(wait: 5.days)
:refunded remove_user_access Bloqueio imediato, sem retry

Resumido:

paid → grant_user_access → POST webhook (status: enabled/paid) overdue → remove_user_access → POST webhook (status: disabled/overdue) + retry em 5 dias refunded → remove_user_access → POST webhook (status: disabled/refunded)

Contratos

Apolo

POST {apolo_base_url}/checkout_notifications Auth: token obtido por email/senha e cacheado no Redis

json { "course_ids": ["..."], "status": "enabled", "email": "...", "name": "...", "doc_number": "...", "phone_number": "...", "token": "...", "transaction": "...", "expires_at": "2025-12-31 23:59:59" }

CBTRG

POST {cbtrg_base_url}/webhook?auth_token_verification={key} Auth: token fixo em query param

json { "payment": { "id": "...", "status": "paid", "billing_type": "BOLETO", "checkout_id": 123, "installment_count": 1, "customer": { "id": 1, "email": "...", "doc_number": "...", "name": "...", "phone_number": "..." } } }

Onion

Contrato próprio, detalhado em onion_integration.md.

Registro no WebhookLog

Cada chamada grava dois registros — payload enviado e resposta recebida:

  • APOLO_WEBHOOK_CREATOR / APOLO_WEBHOOK_RESPONSE
  • CBTRG_WEBHOOK_CREATOR

Junto com gateway_customer_id, customer_email e payload.

Diferenças entre as plataformas

Autenticação

  • Apolo — login com email/senha, token cacheado no Redis, refresh automático quando expira.
  • CBTRG — token fixo enviado como query param (auth_token_verification).
  • Onion — token fixo enviado no header Authorization: Bearer.

Payload

  • Apolo — envia course_ids (IDs dos cursos no Apolo) e expires_at, calculado a partir de activation_total_months.
  • CBTRG — envia os dados completos do pagamento, incluindo billing_type, installment_count e os dados do customer.

Tratamento de overdue

  • Apolo — remove o acesso imediatamente e agenda re-sync em 5 dias.
  • CBTRG — apenas agenda o re-sync em 5 dias; só remove o acesso quando execute_now = true, depois que o job roda.

Repagamento

Quando o pagamento é um repagamento (original_payment presente):

  • Usa a referência do pagamento original como ID da transação.
  • Calcula a expiração a partir da data do primeiro pagamento da parcela original.
  • Sincroniza com o mesmo curso/usuário na plataforma.

Referências