Cupons

Referência

Um cupom reduz o valor de uma cobrança (desconto) ou adia a data da próxima cobrança de uma assinatura (período extra). Este guia explica os três tipos de cupom, como controlar por quantos ciclos o benefício se repete e como validar um código antes de aplicá-lo.

Cupom de período extra não desconta valor. Com discountType=EXTRA_PERIOD, o campo discountValue passa a significar uma quantidade de dias ou meses (não reais nem porcentagem) somados à próxima data de cobrança. Nenhuma fatura é gerada no período pulado.

O que é um cupom

Um cupom é identificado por um code único por conta (ex.: BEMVINDO10), que o cliente digita no checkout. O comportamento do cupom quando aplicado depende de discountType: desconta valor (PERCENTAGE, FIXED) ou adia a data da próxima cobrança (EXTRA_PERIOD) — nunca os dois ao mesmo tempo.

O campo appliesTo restringe o cupom a cobranças RECURRING (assinatura), ONE_TIME (avulsa) ou ALL (default). Um cupom EXTRA_PERIOD só faz sentido numa assinatura, então a API exige appliesTo=RECURRING nesse tipo.

Tipos de desconto

Valores aceitos em discountType:

ValorO que faz
PERCENTAGEDesconta um percentual do valor da cobrança (discountValue de 0.01 a 100).
FIXEDDesconta um valor fixo em reais do valor da cobrança.
EXTRA_PERIODNão desconta valor: soma discountValue dias ou meses (extraPeriodUnit) à data da próxima cobrança da assinatura.
  • maxDiscount limita o teto do desconto em reais — útil para PERCENTAGE. Sem efeito em FIXED ou EXTRA_PERIOD.
  • minAmount exige um valor mínimo no pedido para o cupom ser aceito. Verificado apenas por POST /v1/coupons/validate, e só quando o corpo traz amount. Sem efeito em EXTRA_PERIOD.
  • productIds restringe o cupom a produtos específicos. Vazio aceita qualquer produto. Também é verificado apenas por POST /v1/coupons/validate, que confere só o primeiro item enviado.

Cupom de período extra

Com discountType=EXTRA_PERIOD, discountValue deixa de ser um valor monetário e passa a ser uma quantidade inteira de dias ou meses, definida por extraPeriodUnit. Exemplo: um cupom de “2 meses grátis” numa assinatura mensal, válido nos 3 próximos ciclos:

JSON
{
  "code": "DOISMESES",
  "discountType": "EXTRA_PERIOD",
  "discountValue": 2,
  "extraPeriodUnit": "MONTHS",
  "appliesTo": "RECURRING",
  "maxCycles": 3
}

O efeito acontece na data, não no valor: a próxima cobrança passa a ocorrer 2 meses depois da anterior em vez de 1, sem gerar nenhuma fatura no período pulado. O valor cobrado em cada cobrança continua o preço cheio do plano.

Quantos ciclos o cupom vale

maxCycles controla em quantos ciclos recorrentes consecutivos o benefício se repete — vale igualmente para desconto e para período extra:

1

maxCycles vazio: o benefício se aplica em todo ciclo, até a assinatura ser cancelada.

2

maxCycles=3: os 3 próximos ciclos recebem o benefício; a partir do 4º, a cobrança volta ao valor e à periodicidade normais do plano.

Num cupom EXTRA_PERIOD com maxCyclesvazio, a assinatura fica indefinidamente num intervalo maior que o do plano — por exemplo, uma assinatura mensal com “+1 mês grátis para sempre” cobra a cada 2 meses enquanto o cupom estiver ativo. Defina maxCycles quando o benefício deve ser temporário.

Validar antes de aplicar

POST /v1/coupons/validate é público (sem autenticação) e pensado para ser chamado direto do frontend do cliente final, antes de enviar o código no checkout. Sempre responde 200 — o campo valid indica se o cupom pode ser usado:

JSON
{
  "valid": true,
  "coupon": {
    "code": "DOISMESES",
    "discountType": "EXTRA_PERIOD",
    "discountValue": 2,
    "maxCycles": 3,
    "appliesTo": "RECURRING"
  }
}

Quando valid=false, reason explica o motivo:

reasonMotivo
COUPON_NOT_FOUNDNão existe cupom com este code na conta dona do primeiro price de productIds. Também é a resposta quando productIds não é enviado ou o price não existe.
COUPON_INACTIVECupom está com status PAUSED ou INACTIVE.
COUPON_NOT_STARTEDA data atual é anterior a validFrom.
COUPON_EXPIREDA data atual é posterior a validUntil.
COUPON_EXHAUSTEDcurrentRedemptions já atingiu maxRedemptions.
MIN_AMOUNT_NOT_METamount enviado é menor que minAmount do cupom. Sem amount no corpo, a restrição não é verificada.
PRODUCT_NOT_ELIGIBLEO cupom restringe produtos e o primeiro item de productIds não está na lista do cupom. Apenas o primeiro item enviado é verificado.
COUPON_NOT_APPLICABLEchargeType enviado não bate com o appliesTo do cupom. Sem chargeType no corpo, a API assume RECURRING.
COUPON_ALREADY_USEDCupom é firstTimeOnly e este customerDocument já usou este cupom antes.
A validação pública e a aplicação do cupom na cobrança não checam as mesmas coisas. Ao enviar couponCode numa cobrança, a API confere só status, janela de datas e limite de resgates, e trata a cobrança como RECURRING: minAmount e productIds não são reavaliados nesse momento. Um cupom firstTimeOnly já usado pelo mesmo documento é recusado aí com 400 e código COUPON_FIRST_TIME_ONLY, enquanto a rota pública responde 200 com reason: COUPON_ALREADY_USED.

O que a API de cupons permite

OperaçãoDescrição
Criar CupomCadastra um cupom de desconto (PERCENTAGE, FIXED) ou de período extra (EXTRA_PERIOD).
Listar CuponsLista paginada, com filtro por status e busca por nome/código.
Buscar CupomDetalhes de um cupom pelo couponId.
Atualizar CupomAltera os campos do cupom; o código (code) não pode ser alterado.
Alterar Status do CupomAtiva, pausa ou inativa; INACTIVE é um estado final.
Remover CupomSoft delete: o cupom vira status INACTIVE.
Validar CupomEndpoint público, sem autenticação, para checar um código antes de aplicá-lo no checkout.

Erros comuns

codeStatusQuando acontece
VALIDATION_ERROR400Corpo não passou na validação (Zod) em Criar ou Atualizar Cupom: discountType inválido, discountValue ausente, extraPeriodUnit faltando num cupom EXTRA_PERIOD, etc.
COUPON_CODE_DUPLICATE400Já existe um cupom com este code nesta conta (Criar Cupom).
COUPON_CODE_REQUIRED400Validar Cupom foi chamado sem o campo code no body.
COUPON_INACTIVE_FINAL400Tentativa de mudar o status de um cupom INACTIVE de volta para ACTIVE ou PAUSED. É um estado final; crie um cupom novo.
COUPON_NOT_FOUND404couponId não existe, ou pertence a outra conta (Buscar, Atualizar, Alterar Status, Remover Cupom).

Perguntas frequentes

Posso combinar desconto e período extra no mesmo cupom?
Não. discountType é um único valor por cupom — PERCENTAGE, FIXED ou EXTRA_PERIOD, nunca combinados. Para oferecer os dois efeitos, crie dois cupons.
Um cupom EXTRA_PERIOD pode ser usado numa venda avulsa (ONE_TIME)?
Não. Uma venda avulsa não tem “próximo ciclo” para adiar, então EXTRA_PERIOD exige appliesTo=RECURRING — a API rejeita a criação do cupom com qualquer outro valor de appliesTo.
Posso reativar um cupom que removi?
Não. DELETE /v1/coupons/:couponId é um soft delete: o cupom vira status=INACTIVE, que é um estado final — tentar voltar para ACTIVE ou PAUSED responde 400 COUPON_INACTIVE_FINAL. Crie um cupom novo se precisar da mesma oferta de volta.
Por que Validar Cupom não pede autenticação?
Porque é chamado direto do navegador do cliente final, no momento em que ele digita o cupom no checkout — antes de existir qualquer sessão autenticada com a sua API. Por isso a resposta nunca expõe dados sensíveis do cupom, só o necessário para calcular o efeito no checkout (tipo, valor, teto, validade e para quais cobranças ele vale).
Essa página foi útil?