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:
-
O efeito do PromoCode principal e aplicado na hora
code_type "ticket": cria Ticket(s) no Eventcode_type "promo": registradiscount_first_month
-
PromoCode filhos sao ENTREGUES ao Customer
$addToSet customer_idemtarget_customer_ids- Filhos ficam disponiveis para o Customer
-
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 simpleschildren_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)childrencom itens → frontend deve exibir os beneficios de cada filho junto ao pai