Skip to content

Recuperação de Carrinho Abandonado

O módulo de recuperação de carrinho (backend/src/cart-recovery/) detecta carrinhos abandonados pelo cliente e dispara até três mensagens de recuperação via WhatsApp, automaticamente, sem nenhuma ação manual do admin.

Visão Geral

CaracterísticaDetalhe
Módulo backendbackend/src/cart-recovery/
SchemasAbandonedCart, RecoveryAttempt
SchedulerCartRecoveryScheduler — tick a cada 5 min
CanalWhatsApp (Evolution API via WhatsAppService)
Endpoint públicoPOST /t/:slug/cart-recovery/heartbeat
Permissão adminNenhuma — totalmente automático

Schemas

AbandonedCart (schemas/abandoned-cart.schema.ts)

CampoTipoDescrição
tenantObjectIdIsolamento multi-tenant
phonestringE.164 (+5511999990000) — chave de upsert
customerNamestring?Nome detectado no carrinho
itemsCartItemSnapshot[]Snapshot dos itens no momento do último heartbeat
cartValueBrlnumberValor total do carrinho (R$)
nudgesSentnumberContador de nudges enviados (0–3)
lastNudgeAtDate?Timestamp do último nudge
recoveredbooleantrue quando cliente finaliza pedido
optedOutbooleantrue quando cliente responde STOP/PARAR
deepLinkstringURL de retorno ao carrinho (/t/:slug/b/:pdvSlug/cart)
tenantSlugstringDesnormalizado para evitar join no scheduler

Índices:

  • { tenant, phone } único — upsert target
  • TTL 7 dias em updatedAt — purga automática

RecoveryAttempt (schemas/recovery-attempt.schema.ts)

CampoTipoDescrição
cartObjectId → AbandonedCart
attemptnumber1 = 30 min · 2 = +1 h · 3 = +24 h
sentAtDate
channelstring'whatsapp' (padrão)
messageIdstring?ID retornado pela Evolution API
errorstring?Mensagem de erro em caso de falha

Sequência de Nudges

Heartbeat recebido com itens + phone

        ▼ (updatedAt resetado)
  ┌─ 30 minutos ──►  Nudge 1  ── "Esqueceu alguma coisa? Seu pedido está te esperando"

  ├─ +60 minutos ──►  Nudge 2  ── "Últimas peças disponíveis — garanta o seu"

  └─ +24 horas ───►  Nudge 3  ── "Uma última chance: volte e ganhe [desconto]"

Stop conditions:

  • recovered = true (pedido criado no mesmo (tenant, phone))
  • optedOut = true (cliente respondeu STOP)
  • nudgesSent >= 3

Heartbeat — Frontend Integration

O frontend envia um heartbeat a cada 30 segundos quando o carrinho tem itens e o cliente informou telefone:

POST /t/:slug/cart-recovery/heartbeat
Content-Type: application/json

{
  "phone": "+5511999990000",
  "customerName": "Ana Silva",
  "items": [
    { "menuItemId": "...", "name": "X-Burguer", "qty": 1, "unitPrice": 25.90 }
  ],
  "deepLink": "/t/demo/b/balcao/cart"
}

Resposta: 204 No Content (sem corpo). Endpoint público — sem autenticação.

Opt-out

Quando o NotificationsService recebe a resposta do cliente com texto STOP, PARAR, CANCELAR ou equivalente, chama CartRecoveryService.markOptedOut(tenantId, phone), setando optedOut = true. Nenhum nudge adicional é enviado.

Integração com Pedido

Quando um pedido é criado (OrdersService.create), a sequência é interrompida via CartRecoveryService.markRecovered(tenantId, phone). O documento permanece no banco (para analytics) mas recovered = true previne novos nudges.

Configurações

Intervalos dos nudges são constantes em cart-recovery.service.ts:

typescript
const NUDGE_1_DELAY_MS = 30 * 60_000;      // 30 min
const NUDGE_2_DELAY_MS = 60 * 60_000;      // 1 h
const NUDGE_3_DELAY_MS = 24 * 60 * 60_000; // 24 h

O scheduler roda a cada 5 minutos (CartRecoveryScheduler).

Relacionados

Lançado sob a licença MIT.