Skip to content

Cupons Promocionais — Cupom com Filhos

O que e um cupom com filhos?

Um PromoCode pode conter outros PromoCode dentro dele (campo children_code_ids). Quando o Customer resgata o PromoCode principal, os filhos sao entregues ao Customer.

PromoCode "LANC2026" (code_type: "ticket")
├── Efeito proprio: cria 1 Ticket vinculado ao Event
└── children_code_ids:
    ├── PromoCode "ABC123" (ticket) → cria Ticket no Event
    └── PromoCode "XYZ789" (promo) → discount_first_month

Customer resgata "LANC2026":
  ✓ 1 Ticket criado (do PromoCode principal)
  ✓ PromoCode ABC123 e XYZ789 entregues ao Customer
    (adicionados em target_customer_ids de cada filho)

Regra principal: RECEBER ≠ RESGATAR

Quando o Customer resgata o PromoCode principal:

  1. O efeito do PromoCode principal e aplicado na hora

    • code_type "ticket": cria Ticket(s) no Event
    • code_type "promo": registra discount_first_month
  2. PromoCode filhos sao ENTREGUES ao Customer

    • $addToSet customer_id em target_customer_ids
    • Filhos ficam disponiveis para o Customer
  3. Cada PromoCode filho deve ser RESGATADO SEPARADAMENTE

    • PUT /customers/me/redeem-promo-code?code=CODIGO
    • Ticket do filho so e criado nesse momento
Conceito Significado
RECEBER Customer adicionado em target_customer_ids
RESGATAR Customer chama redeem-promo-code e ganha efeito

Quem pode resgatar um PromoCode filho?

target_customer_ids Comportamento
[] PromoCode PUBLICO — qualquer Customer pode resgatar
[customer_id_A, customer_id_B] PromoCode RESTRITO — so A e B podem resgatar
Endpoint Customer fora da lista Customer na lista
GET /validate-promo-code (com auth) valid: false valid: true
PUT /redeem-promo-code 403 FORBIDDEN Resgate normal

Janela de validade

Todo PromoCode pode ter um periodo de validade:

Campo Descricao
valid_from datetime a partir da qual pode ser resgatado (None = disponivel imediatamente)
valid_until datetime ate a qual pode ser resgatado (None = nunca expira)
valid_from valid_until Comportamento
None None Valido para sempre
2026-04-01 2026-04-30 Valido apenas em abril
None 2026-12-31 Valido ate fim do ano
2026-06-01 None Valido a partir de junho

Setup — Admin cria os PromoCode

Passo 1: Criar PromoCode filhos (independentes) Passo 2: Criar PromoCode principal com children_code_ids

Passo 1a: PromoCode tipo ticket (gera Ticket vinculado a Event)

POST /api/v1/admin/promo-codes
[{
  "title": "Ingresso Pre-Estreia",
  "code_type": "ticket",
  "bonus_tickets": 1,
  "event_id": "69c1dadce9c9cdd70fe6b58c"
}]

Resposta: { _id: "FILHO_TICKET_ID", code: "ABC123" }

Entidades envolvidas:

  • PromoCode (code_type: "ticket", event_id → Event)
  • Event (69c1dadce9c9cdd70fe6b58c)
  • Ticket (criado no resgate, nao na criacao do PromoCode)

Passo 1b: PromoCode tipo promo (desconto no 1o mes)

POST /api/v1/admin/promo-codes
[{
  "title": "Desconto Assinatura",
  "code_type": "promo",
  "discount_first_month": 5.0
}]

Resposta: { _id: "FILHO_PROMO_ID", code: "XYZ789" }

Entidades envolvidas:

  • PromoCode (code_type: "promo", discount_first_month: 5.0)
  • Subscription (desconto aplicado no subscribe via _check_promo_discount)

Passo 2: PromoCode principal com children_code_ids

POST /api/v1/admin/promo-codes
[{
  "title": "Pacote Lancamento",
  "code_type": "ticket",
  "bonus_tickets": 1,
  "event_id": "69c1dadce9c9cdd70fe6b58c",
  "children_code_ids": ["FILHO_TICKET_ID", "FILHO_PROMO_ID"]
}]

Resposta: { code: "LANC2026", children_code_ids: ["...", "..."] }

Entidades envolvidas:

  • PromoCode (code_type: "ticket", children_code_ids: [2 filhos])
  • Event (69c1dadce9c9cdd70fe6b58c)

Fluxo do Customer — passo a passo (com entidades)

MOMENTO 1: Customer resgata PromoCode principal

PUT /api/v1/customers/me/redeem-promo-code?code=LANC2026

Customer                          API                         MongoDB
────────                     ─────────                    ────────────
    │                            │                            │
    │  redeem-promo-code         │                            │
    │  code=LANC2026             │                            │
    │───────────────────────────>│                            │
    │                            │                            │
    │                            │  1. Busca PromoCode        │
    │                            │     {code: "LANC2026"}     │
    │                            │                            │
    │                            │  2. PromoCode.code_type    │
    │                            │     == "ticket"            │
    │                            │     → Cria Ticket:         │
    │                            │       customer_id: Customer│
    │                            │       event_id: Event      │
    │                            │       promo_code: LANC2026 │
    │                            │       status: "available"  │
    │                            │                            │
    │                            │  3. Customer update:       │
    │                            │     $push promo_code_ids   │
    │                            │     $push redeemed_promo   │
    │                            │     _codes: "LANC2026"     │
    │                            │                            │
    │                            │  4. _deliver_promo_children│
    │                            │     Para cada filho:       │
    │                            │     $addToSet Customer._id │
    │                            │     em filho.target_       │
    │                            │     customer_ids           │
    │                            │     (SEM criar Ticket)     │
    │                            │     (SEM resgatar filho)   │
    │                            │                            │
    │  Response:                 │                            │
    │  { tickets_created: 1,     │                            │
    │    children_delivered: 2 } │                            │
    │<───────────────────────────│                            │

Estado das entidades apos este momento:

Entidade Estado
Ticket (do pai) Criado, status: "available"
PromoCode ABC123 target_customer_ids: [Customer._id] — NAO resgatado, Ticket NAO criado
PromoCode XYZ789 target_customer_ids: [Customer._id] — NAO resgatado, desconto NAO aplicado
Customer redeemed_promo_codes: ["LANC2026"], promo_code_ids: [PromoCode._id]
Event tickets_issued += 1

MOMENTO 2: Customer resgata PromoCode filho (ticket)

PUT /api/v1/customers/me/redeem-promo-code?code=ABC123

Customer                          API
────────                     ─────────
    │                            │
    │  redeem-promo-code         │
    │  code=ABC123               │
    │───────────────────────────>│
    │                            │
    │                            │  1. Busca PromoCode {code: "ABC123"}
    │                            │  2. Checa target_customer_ids
    │                            │     Customer._id na lista? SIM
    │                            │  3. code_type == "ticket"
    │                            │     → Cria Ticket:
    │                            │       customer_id: Customer
    │                            │       event_id: Event
    │                            │       promo_code: ABC123
    │                            │       status: "available"
    │                            │  4. Event.tickets_issued += 1
    │                            │
    │  { tickets_created: 1 }    │
    │<───────────────────────────│

AGORA o Ticket do PromoCode filho e criado.

MOMENTO 3: Customer resgata PromoCode filho (promo)

PUT /api/v1/customers/me/redeem-promo-code?code=XYZ789

Customer                          API
────────                     ─────────
    │                            │
    │  redeem-promo-code         │
    │  code=XYZ789               │
    │───────────────────────────>│
    │                            │
    │                            │  1. Busca PromoCode {code: "XYZ789"}
    │                            │  2. Checa target_customer_ids
    │                            │     Customer._id na lista? SIM
    │                            │  3. code_type == "promo"
    │                            │     → discount_first_month: 5.0
    │                            │       registrado no Customer
    │                            │
    │  { discount_first_month:   │
    │    5.0 }                   │
    │<───────────────────────────│

Desconto de R$5 aplicado na proxima Subscription via _check_promo_discount no POST /asaas/subscribe. Em modos PIX multi-mes (quarterly/semiannually/yearly), o desconto aplica-se a 1 dos N meses: ((N-1) × mensal) + discount_first_month.


Linha do tempo

Dia 0                         Dia 1                     Dia 2
─────                         ─────                     ─────
  │                             │                         │
  ▼                             ▼                         ▼
Customer resgata            Customer resgata          Customer resgata
PromoCode principal         PromoCode filho           PromoCode filho
code=LANC2026               code=ABC123               code=XYZ789
  │                             │                         │
  ├─ Ticket criado              └─ Ticket criado          └─ discount_first
  │  (Event + PromoCode)           (Event + PromoCode)       _month: 5.0
  │                                                          disponivel na
  └─ 2 PromoCode filhos                                      Subscription
     ENTREGUES ao Customer                                    (promo type)
     (target_customer_ids)

Estrutura MongoDB

ANTES do resgate

PromoCode (principal)              PromoCode (filho ticket)
┌──────────────────────┐          ┌──────────────────────┐
│ code: "LANC2026"     │          │ code: "ABC123"       │
│ code_type: "ticket"  │          │ code_type: "ticket"  │
│ bonus_tickets: 1     │     ┌──> │ bonus_tickets: 1     │
│ event_id: Event._id  │     │    │ event_id: Event._id  │
│ children_code_ids:   │─────┘    │ target_customer_ids: │
│   [ABC123_id,        │─────┐    │   []                 │
│    XYZ789_id]        │     │    └──────────────────────┘
└──────────────────────┘     │
                              │    PromoCode (filho promo)
                              │    ┌──────────────────────┐
                              └──> │ code: "XYZ789"       │
                                   │ code_type: "promo"   │
                                   │ discount_first_month:│
                                   │   5.0                │
                                   │ target_customer_ids: │
                                   │   []                 │
                                   └──────────────────────┘

DEPOIS do resgate do PromoCode principal

PromoCode (filho ticket)           PromoCode (filho promo)
┌──────────────────────┐          ┌──────────────────────┐
│ code: "ABC123"       │          │ code: "XYZ789"       │
│ target_customer_ids: │          │ target_customer_ids: │
│   [Customer._id]     │          │   [Customer._id]     │
│                      │          │                      │
│ Entregue ao Customer │          │ Entregue ao Customer │
│ NAO resgatado        │          │ NAO resgatado        │
│ Ticket NAO criado    │          │ Desconto NAO aplicado│
└──────────────────────┘          └──────────────────────┘

Customer:
┌──────────────────────────────────────────────────────────┐
│ redeemed_promo_codes: ["LANC2026"]                       │
│ promo_code_ids: [PromoCode_principal._id]                │
│ tickets: [Ticket._id]  (via $lookup ou /me/tickets)      │
│ current_subscription_id: Subscription._id                │
└──────────────────────────────────────────────────────────┘

Ticket (criado do principal):
┌──────────────────────────────────────────────────────────┐
│ customer_id: Customer._id                                │
│ event_id: Event._id                                      │
│ promo_code_id: PromoCode_principal._id                   │
│ promo_code: "LANC2026"                                   │
│ status: "available"                                      │
└──────────────────────────────────────────────────────────┘

Entidades envolvidas

Entidade Collection Papel no fluxo
PromoCode promo_codes Cupom. Pode ter filhos (children_code_ids)
Customer users Quem resgata. Campos: promo_code_ids, redeemed_promo_codes
Event events Evento vinculado ao PromoCode ticket. Campo tickets_issued
Ticket tickets Ingresso gerado no resgate. Vincula Customer + Event + PromoCode de origem
Subscription subscriptions Assinatura. Recebe desconto via discount_first_month no POST /subscribe

Regras (resumo)

Regra Detalhe
PromoCode filho e independente Nao referencia o pai. Pode estar em N pais
Entrega imediata $addToSet Customer._id em target_customer_ids do filho
Resgate individual Cada filho deve ser resgatado via PUT /me/redeem-promo-code
Ticket so no resgate Ticket criado quando o PromoCode e resgatado, nao quando e entregue
target_customer_ids restringe Lista vazia = publico. Lista com IDs = restrito
Validade independente Cada PromoCode tem valid_from e valid_until proprios
Resgate unico por Customer redeemed_promo_codes impede resgate duplicado (409)

Identificacao tecnica:

  • children_code_ids: [] → PromoCode simples
  • children_code_ids: [...] → PromoCode com filhos

Visualizacao dos filhos no validate

GET /customers/validate-promo-code?code=LANC2026 agora retorna o campo children com os dados de cada filho, permitindo ao frontend exibir todos os beneficios do bundle antes do resgate:

{
  "valid": true,
  "code": "LANC2026",
  "code_type": "ticket",
  "children": [
    {
      "code": "ABC123",
      "title": "Ingresso Cinema SP",
      "code_type": "ticket",
      "tickets": 1,
      "event_date": "2026-04-15T20:00:00Z"
    },
    {
      "code": "XYZ789",
      "title": "Desconto Assinatura",
      "code_type": "promo",
      "discount_first_month": 5.50
    }
  ]
}
  • children == null → cupom simples (sem filhos)
  • children com itens → frontend deve exibir os beneficios de cada filho junto ao pai