Planos
O plano é a oferta comercial, montada sem cliente nenhum: período, método de pagamento padrão, parcelamento máximo, fidelidade e os produtos com suas réguas de desconto. Modelo completo em Conceitos.
| Rota | O que faz |
|---|---|
POST /v1/plans |
Cria a oferta completa num POST |
GET /v1/plans |
Lista as ofertas, paginadas |
GET /v1/plans/{planId} |
O plano com itens e réguas de desconto |
PUT /v1/plans/{planId} |
Publica uma nova versão da oferta |
POST /v1/plans/{planId}/archive |
Bloqueia assinaturas novas |
O objeto Plano
Seção intitulada “O objeto Plano”| Campo | Tipo | Descrição |
|---|---|---|
id |
string (uuid) | Identificador do plano |
name |
string | Nome do plano |
version |
integer | Versão vigente da oferta — toda edição cria uma versão nova; assinantes ficam pinados na deles |
periodMonths |
integer | Meses por ciclo — ver valores |
defaultPaymentMethod |
enum | Método padrão de quem assinar — ver valores |
maxInstallments |
integer | Parcelamento máximo no cartão (1 = à vista) |
commitmentCycles |
integer | null | Fidelidade em ciclos — só registro; a tratativa de quebra é sua. null = sem fidelidade |
archivedAt |
datetime | null | Quando foi arquivado — arquivado não aceita assinatura nova. null = ativo |
createdAt |
datetime | Quando foi criado |
items[] |
array | Produtos e réguas de desconto — presente no detalhe, criação e nova versão |
Cada elemento de items[]:
| Campo | Tipo | Descrição |
|---|---|---|
productId |
string (uuid) | Produto do catálogo |
position |
integer | Ordem do item no plano |
discounts[] |
array | Régua de desconto do produto, na ordem de prioridade |
Cada regra de discounts[]:
| Campo | Tipo | Descrição |
|---|---|---|
percent |
integer | Percentual de desconto, 1 a 100 |
fromCycle |
integer | Primeiro ciclo em que a regra vale, ≥ 1 (ciclo = fatura) |
toCycle |
integer | null | Último ciclo em que a regra vale — null = desconto sem fim |
O campo periodMonths
Seção intitulada “O campo periodMonths”Aceita apenas 1, 3, 6 ou 12. É o multiplicador do preço: cada ciclo gera uma fatura de periodMonths × preço mensal de cada produto. Um plano semestral com produto de R$ 249,90/mês fatura R$ 1.499,40 a cada 6 meses.
O enum defaultPaymentMethod
Seção intitulada “O enum defaultPaymentMethod”| Valor | Significado |
|---|---|
card |
Cartão de crédito — o único método que aceita parcelamento |
pix |
Pix — sempre à vista |
boleto |
Boleto — sempre à vista |
É o padrão de quem assinar; cada assinatura pode sobrescrever. O plano é agnóstico de gateway: na criação basta que algum gateway ativo da conta suporte o método e o parcelamento pedidos — senão 422 (engine.payment_method_unsupported / engine.installments_unsupported); nada é convertido por conta própria. Quem valida contra um gateway específico é a assinatura, na conexão pinada dela.
Como a régua de desconto resolve
Seção intitulada “Como a régua de desconto resolve”- A cada ciclo, o motor percorre
discounts[]do item na ordem da lista e aplica a primeira regra cujo intervalofromCycle–toCyclecontém o ciclo. Nada empilha. - Ciclo = fatura, não mês. Num plano semestral, “2 ciclos com desconto” = 1 ano de desconto.
- Desconto só existe em produto de preço fixo — produto variável já chega com o valor calculado do seu endpoint (
422se tentar).
Criar plano
Seção intitulada “Criar plano”POST /v1/plansHeaders:
| Header | Obrigatório | Descrição |
|---|---|---|
Idempotency-Key |
Sim | Chave única sua — repetir devolve a mesma resposta |
Corpo:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
name |
string | Sim | 1–120 caracteres |
periodMonths |
integer | Sim | 1, 3, 6 ou 12 — meses por ciclo |
defaultPaymentMethod |
enum | Sim | card, pix ou boleto |
maxInstallments |
integer | Não | 1–12, padrão 1. Independente do período: semestral à vista é válido |
commitmentCycles |
integer | Não | ≥ 1. Fidelidade em ciclos — só registro |
items[] |
array | Sim | 1 a 20 produtos |
items[].productId |
uuid | Sim | Produto do catálogo — produto arquivado é recusado com 409 |
items[].discounts[] |
array | Não | Até 20 regras, na ordem de prioridade |
items[].discounts[].percent |
integer | Sim | 1–100 |
items[].discounts[].fromCycle |
integer | Sim | ≥ 1 |
items[].discounts[].toCycle |
integer | Não | ≥ fromCycle — omita para desconto sem fim |
curl -X POST $MOTOR/plans -H "$AUTH" -H "Idempotency-Key: plano-semestral-1" \ -H "Content-Type: application/json" -d '{ "name": "Clube Semestral", "periodMonths": 6, "defaultPaymentMethod": "card", "maxInstallments": 6, "commitmentCycles": 2, "items": [{ "productId": "0199c1a2-…", "discounts": [ { "percent": 20, "fromCycle": 1, "toCycle": 2 }, { "percent": 10, "fromCycle": 3, "toCycle": 5 } ] }] }'Leitura: uma fatura a cada 6 meses de 6 × R$ 249,90, com 20% off nos 2 primeiros ciclos, 10% do 3º ao 5º, cheia depois. Cartão em até 6x. Fidelidade de 2 ciclos (= 1 ano).
Resposta 201: o objeto Plano com version: 1 e items[] completos.
Erros: 400 · 409 (produto arquivado) · 422 (desconto em produto variável; método ou parcelamento não suportado pelo gateway).
Listar planos
Seção intitulada “Listar planos”GET /v1/plansPaginação padrão (page, pageSize). Resposta 200: { data: [Plano…], pagination }, mais recentes primeiro, sem items[] — use o detalhe para ver as réguas.
Detalhar plano
Seção intitulada “Detalhar plano”GET /v1/plans/{planId}Resposta 200: o objeto Plano com items[] e réguas. Erros: 404.
Publicar uma nova versão
Seção intitulada “Publicar uma nova versão”PUT /v1/plans/{planId}O corpo é a oferta completa de novo — mesmos campos e obrigatoriedades do criar (sem Idempotency-Key). Não é um patch: o que não vier, não existe na versão nova.
O que acontece:
versionincrementa; a resposta é o plano na versão nova.- Quem já assinou segue pinado na versão que assinou — só assinaturas novas pegam a nova. Propagar condições para quem já assina é sempre uma operação explícita, nunca efeito colateral.
- Para mudar as condições de um assinante só, use
POST /v1/subscriptions/{id}/customize.
Resposta 200: o objeto Plano. Erros: 404, e os mesmos 409/422 do criar.
Arquivar plano
Seção intitulada “Arquivar plano”POST /v1/plans/{planId}/archiveSem assinaturas novas (POST /v1/subscriptions com este plano passa a devolver 409); as existentes seguem faturando. Resposta 200: o objeto Plano com archivedAt. Erros: 404.