Notificações WhatsApp

Permite que responsáveis assinem um serviço de notificações via WhatsApp para receber alertas de entrada, saída e falta dos alunos. O faturamento recorrente é processado pelo gateway de pagamento Asaas; a ativação e a suspensão do serviço são automáticas conforme o ciclo financeiro.

Visão geral da arquitetura

A funcionalidade é composta por quatro camadas que trabalham de forma coordenada.

1

Habilitação por cliente (Admin)

O administrador da plataforma habilita a funcionalidade whatsapp-notifications para um cliente específico via painel administrativo. Sem essa habilitação, o responsável não vê a opção de assinatura no app.

2

Assinatura pelo responsável (App móvel)

O responsável escolhe um plano e uma forma de pagamento (PIX ou cartão de crédito). A plataforma cria o cliente e a assinatura recorrente no Asaas e retorna a URL de checkout hospedada pelo Asaas (invoice_url). Nenhum dado de cartão transita pela nossa infraestrutura — a cobrança é inteiramente PCI-compliant no lado do Asaas.

3

Ativação automática via webhook (Asaas → Plataforma)

O Asaas envia eventos de pagamento e assinatura para o endpoint POST /webhook/asaas. O evento PAYMENT_CONFIRMED ativa a assinatura; SUBSCRIPTION_DELETED a cancela. O estado da assinatura local é sempre espelho do estado financeiro no Asaas.

4

Envio condicionado ao status financeiro

No momento de enviar uma notificação WhatsApp (entrada, saída, falta), o NotificationService verifica se o responsável possui uma assinatura com status ACTIVE. Se não possuir, a mensagem não é enviada, independentemente dos toggles de notificação.

Fluxo de dados resumido

  1. → Admin habilita whatsapp-notifications para o cliente
  2. → Parceiro consulta GET /v1/partners/school/{cnpj}/responsible/{cpf}/whatsapp/subscription
  3. → Responsável escolhe plano → Parceiro chama POST /v1/partners/school/{cnpj}/responsible/{cpf}/whatsapp/subscribe
  4. → Plataforma cria customer + subscription no Asaas → retorna invoice_url
  5. → Responsável paga via checkout Asaas (PIX / cartão)
  6. → Asaas dispara PAYMENT_CONFIRMED → webhook ativa assinatura local
  7. → Parceiro faz polling em GET /v1/partners/school/{cnpj}/responsible/{cpf}/whatsapp/subscription/status até ACTIVE
  8. → WhatsApp começa a ser enviado nas notificações de entrada / saída / falta
Endpoints
GET/v1/partners/school/{cnpj}/responsible/{cpf}/whatsapp/subscription
POST/v1/partners/school/{cnpj}/responsible/{cpf}/whatsapp/subscribe
GET/v1/partners/school/{cnpj}/responsible/{cpf}/whatsapp/subscription/status
DELETE/v1/partners/school/{cnpj}/responsible/{cpf}/whatsapp/subscription
POST/webhook/asaas

Regras de negócio

Habilitação e acesso

  • •A funcionalidade deve ser habilitada individualmente para cada cliente pelo administrador da plataforma. Sem ela, a assinatura é bloqueada com HTTP 403.
  • •Cada responsável pode ter no máximo uma assinatura ativa ou pendente por vez. Tentativas de criar uma segunda assinatura enquanto existe uma bloqueante retornam HTTP 409.
  • •Statuses que bloqueiam nova assinatura: PENDING, ACTIVE, OVERDUE, PAST_DUE, PAUSED.

Ciclo de cobrança

  • •O ciclo padrão é mensal (MONTHLY), configurável por plano.
  • •A primeira cobrança é gerada imediatamente (data de vencimento = hoje). As cobranças subsequentes seguem o ciclo do plano.
  • •A assinatura é criada com status PENDING no banco de dados local. A ativação ocorre exclusivamente por webhook (PAYMENT_CONFIRMED).
  • •Formas de pagamento aceitas por plano são definidas no campo billing_types do SubscriptionPlan. O padrão é ["PIX", "CREDIT_CARD"].

Ativação e suspensão do serviço WhatsApp

  • •O envio de mensagens WhatsApp é condicionado a uma assinatura com status ACTIVE. Todos os demais statuses suspendem o envio.
  • •O sistema verifica o status no momento do disparo (entrada, saída, falta), não em cache. Uma assinatura que passe para OVERDUE para de receber mensagens imediatamente após o webhook.
  • •Os toggles de notificação (whatsapp_checkin, whatsapp_checkout, whatsapp_absence) são verificados adicionalmente — a mensagem só é enviada se o toggle estiver ativo e a assinatura estiver ativa.

Dados necessários para criar cliente no Asaas

  • •O responsável deve ter o campo cpf preenchido.
  • •O responsável deve ter um nome associado via perfil de usuário.
  • •Se CPF ou nome estiverem ausentes, a assinatura é bloqueada com HTTP 422.
  • •O cliente no Asaas é criado uma única vez e reutilizado em assinaturas subsequentes (busca por CPF antes de criar).
Ciclo de vida da assinatura
PENDING

Assinatura criada, aguardando primeiro pagamento.

suspenso
ACTIVE

Pagamento confirmado. WhatsApp ativo.

WhatsApp ✓
OVERDUE

Pagamento em atraso. Serviço suspenso.

suspenso
PAST_DUE

Período de tolerância expirado.

suspenso
PAUSED

Pausada temporariamente pelo Asaas.

suspenso
CANCELED

Cancelada pelo responsável ou plataforma.

suspenso
EXPIRED

Assinatura encerrada por prazo.

suspenso
FAILED

Falha no pagamento / chargeback.

suspenso
Eventos Asaas → status local
PAYMENT_CONFIRMED→ ACTIVE
PAYMENT_RECEIVED→ ACTIVE
PAYMENT_OVERDUE→ OVERDUE
PAYMENT_REFUNDED→ CANCELED
PAYMENT_DELETED→ CANCELED
PAYMENT_CHARGEBACK_*→ FAILED
SUBSCRIPTION_UPDATED→ sincroniza dados
SUBSCRIPTION_DELETED→ CANCELED
SUBSCRIPTION_INACTIVATED→ CANCELED

O modelo assinatura

Representa uma assinatura recorrente de um responsável a um plano de notificações WhatsApp. O modelo é criado localmente no momento da requisição e sincronizado com o Asaas via webhooks.

id string (uuid)

Identificador local da assinatura. É enviado ao Asaas como externalReference.

status string

Status financeiro: PENDING, ACTIVE, OVERDUE, PAST_DUE, PAUSED, CANCELED, EXPIRED, FAILED.

billing_type string

Forma de pagamento: PIX ou CREDIT_CARD.

cycle string

Ciclo de cobrança: MONTHLY, QUARTERLY, SEMIANNUALLY, YEARLY.

value number

Valor cobrado por ciclo, em reais.

next_due_date string (Y-m-d)

Data de vencimento da próxima cobrança.

asaas_subscription_id string

ID da assinatura no Asaas (sub_...). Null até a confirmação do Asaas.

asaas_customer_id string

ID do cliente no Asaas (cus_...).

activated_at string (ISO 8601)

Data/hora em que a assinatura foi ativada pela primeira vez.

canceled_at string (ISO 8601)

Data/hora do cancelamento. Null se ainda ativa.

plan object

Plano associado: { id, name }.

created_at string (ISO 8601)

Data/hora de criação do registro.

Modelo assinatura
{
  "id": "019e9326-6dad-705e-afe2-34d494ee0694",
  "status": "ACTIVE",
  "billing_type": "PIX",
  "cycle": "MONTHLY",
  "value": 49.9,
  "next_due_date": "2026-07-04",
  "asaas_subscription_id": "sub_a1B2c3D4e5F6",
  "asaas_customer_id": "cus_X9y8Z7w6",
  "activated_at": "2026-06-04T12:05:00+00:00",
  "canceled_at": null,
  "plan": {
    "id": "9b1f4a72-0000-0000-0000-000000000001",
    "name": "WhatsApp Essencial"
  },
  "created_at": "2026-06-04T12:00:00+00:00"
}
GET /v1/partners/school/{cnpj}/responsible/{cpf}/whatsapp/subscription

Estado + planos disponíveis

Retorna se a funcionalidade está habilitada para o cliente do responsável autenticado, a assinatura atual (se houver) e o catálogo de planos disponíveis. Deve ser chamado ao carregar a tela de configurações de notificações.

Autenticação

Requer os três cabeçalhos de parceiro:

  • X-Partner — token do parceiro
  • X-Client — identificador do cliente (escola)
  • X-Authorization — token de API do cliente

Campos da resposta

feature_enabled boolean

Se true, a funcionalidade está habilitada para o cliente. Se false, esconder a seção no app.

has_active_subscription boolean

Se true, o responsável já possui uma assinatura ACTIVE.

current object|null

Objeto assinatura atual. Null se não houver nenhuma.

plans array

Lista de planos disponíveis com id, name, description, features, price, cycle, billing_types.

Lógica de apresentação no app

  • feature_enabled = false → ocultar toda a seção WhatsApp
  • has_active_subscription = false → exibir botão "Assinar WhatsApp"
  • has_active_subscription = true → mostrar toggles + "Cancelar assinatura"
Resposta 200
{
  "feature_enabled": true,
  "has_active_subscription": false,
  "current": null,
  "plans": [
    {
      "id": "9b1f4a72-0000-0000-0000-000000000001",
      "name": "WhatsApp Essencial",
      "description": "Notificações de entrada, saída e falta.",
      "features": ["Entrada", "Saída", "Falta"],
      "price": 49.9,
      "cycle": "MONTHLY",
      "billing_types": ["PIX", "CREDIT_CARD"]
    }
  ]
}
POST /v1/partners/school/{cnpj}/responsible/{cpf}/whatsapp/subscribe

Assinar um plano

Cria uma assinatura recorrente no Asaas para o responsável autenticado e retorna os dados de checkout. A assinatura é criada localmente com status PENDING e ativada automaticamente quando o Asaas confirmar o pagamento via webhook.

Corpo da requisição

subscription_plan_id string (uuid) — obrigatório

ID do plano escolhido. Deve ser um plano ativo com feature_key = whatsapp-notifications.

billing_type string — obrigatório

Forma de pagamento: PIX ou CREDIT_CARD. Deve estar no array billing_types do plano.

Campos da resposta (201)

subscriptionobject

O modelo assinatura criado com status PENDING.

invoice_urlstring|null

URL de checkout hospedada pelo Asaas. Abra em WebView ou browser externo. Nenhum dado de cartão passa pela plataforma.

pix_qr_codeobject|null

QR code PIX: encodedImage (PNG base64), payload (copia-e-cola), expirationDate. Null para cartão de crédito.

Erros possíveis

403

Funcionalidade não habilitada para o cliente.

409

Responsável já possui assinatura ativa ou pendente.

422

billing_type não permitido pelo plano ou dados de perfil (CPF/nome) ausentes.

502

Erro no gateway Asaas. O erro é registrado em integration_logs.

Requisição
POST /api/v1/partners/school/{cnpj}/responsible/{cpf}/whatsapp/subscribe
X-Partner: <partner-token>
X-Client: <client-name>
X-Authorization: <client-token>
Content-Type: application/json

{
  "subscription_plan_id": "9b1f4a72-0000-0000-0000-000000000001",
  "billing_type": "PIX"
}
Resposta 201
{
  "message": "Assinatura iniciada. Conclua o pagamento...",
  "subscription": { /* objeto assinatura */ },
  "invoice_url": "https://www.asaas.com/i/abc123",
  "pix_qr_code": {
    "encodedImage": "iVBORw0KGgo...",
    "payload": "00020126...",
    "expirationDate": "2026-06-05 23:59:59"
  }
}
GET /v1/partners/school/{cnpj}/responsible/{cpf}/whatsapp/subscription/status

Consultar status da assinatura

Retorna o status atual da assinatura do responsável autenticado. Use este endpoint para polling após redirecionar o responsável ao checkout do Asaas, detectando a ativação.

Estratégia de polling recomendada

Faça polling a cada 3–5 segundos por no máximo 2 minutos enquanto o responsável estiver na tela de aguardo. Pare quando has_active_subscription for true ou o responsável sair da tela.

Campos da resposta

has_active_subscriptionboolean

True quando status = ACTIVE. Use este campo para habilitar os toggles de notificação no app.

currentobject|null

O modelo assinatura mais recente do responsável. Null se não houver nenhuma.

Resposta 200 — assinatura ativa
{
  "has_active_subscription": true,
  "current": {
    "id": "019e9326-6dad-...",
    "status": "ACTIVE",
    "billing_type": "PIX",
    "cycle": "MONTHLY",
    "value": 49.9,
    "next_due_date": "2026-07-04",
    "activated_at": "2026-06-04T12:05:00+00:00",
    "canceled_at": null,
    "plan": { "id": "...", "name": "WhatsApp Essencial" },
    "created_at": "2026-06-04T12:00:00+00:00"
  }
}
DELETE /v1/partners/school/{cnpj}/responsible/{cpf}/whatsapp/subscription

Cancelar assinatura

Cancela a assinatura ativa do responsável identificado pelo CPF. O cancelamento é propagado ao Asaas (encerrando cobranças futuras) e o status local é atualizado para CANCELED. O registro histórico é preservado.

Erros possíveis

404

Não existe assinatura ativa para cancelar.

502

Erro ao cancelar no Asaas. O erro é registrado em integration_logs.

Resposta 200
{
  "message": "Assinatura cancelada com sucesso."
}