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.
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:
| Valor | O que faz |
|---|---|
PERCENTAGE | Desconta um percentual do valor da cobrança (discountValue de 0.01 a 100). |
FIXED | Desconta um valor fixo em reais do valor da cobrança. |
EXTRA_PERIOD | Não desconta valor: soma discountValue dias ou meses (extraPeriodUnit) à data da próxima cobrança da assinatura. |
- •
maxDiscountlimita o teto do desconto em reais — útil paraPERCENTAGE. Sem efeito emFIXEDouEXTRA_PERIOD. - •
minAmountexige um valor mínimo no pedido para o cupom ser aceito. Verificado apenas porPOST /v1/coupons/validate, e só quando o corpo trazamount. Sem efeito emEXTRA_PERIOD. - •
productIdsrestringe o cupom a produtos específicos. Vazio aceita qualquer produto. Também é verificado apenas porPOST /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:
{
"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:
maxCycles vazio: o benefício se aplica em todo ciclo, até a assinatura ser cancelada.
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.
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:
{
"valid": true,
"coupon": {
"code": "DOISMESES",
"discountType": "EXTRA_PERIOD",
"discountValue": 2,
"maxCycles": 3,
"appliesTo": "RECURRING"
}
}Quando valid=false, reason explica o motivo:
| reason | Motivo |
|---|---|
COUPON_NOT_FOUND | Nã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_INACTIVE | Cupom está com status PAUSED ou INACTIVE. |
COUPON_NOT_STARTED | A data atual é anterior a validFrom. |
COUPON_EXPIRED | A data atual é posterior a validUntil. |
COUPON_EXHAUSTED | currentRedemptions já atingiu maxRedemptions. |
MIN_AMOUNT_NOT_MET | amount enviado é menor que minAmount do cupom. Sem amount no corpo, a restrição não é verificada. |
PRODUCT_NOT_ELIGIBLE | O cupom restringe produtos e o primeiro item de productIds não está na lista do cupom. Apenas o primeiro item enviado é verificado. |
COUPON_NOT_APPLICABLE | chargeType enviado não bate com o appliesTo do cupom. Sem chargeType no corpo, a API assume RECURRING. |
COUPON_ALREADY_USED | Cupom é firstTimeOnly e este customerDocument já usou este cupom antes. |
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ção | Descrição |
|---|---|
| Criar Cupom | Cadastra um cupom de desconto (PERCENTAGE, FIXED) ou de período extra (EXTRA_PERIOD). |
| Listar Cupons | Lista paginada, com filtro por status e busca por nome/código. |
| Buscar Cupom | Detalhes de um cupom pelo couponId. |
| Atualizar Cupom | Altera os campos do cupom; o código (code) não pode ser alterado. |
| Alterar Status do Cupom | Ativa, pausa ou inativa; INACTIVE é um estado final. |
| Remover Cupom | Soft delete: o cupom vira status INACTIVE. |
| Validar Cupom | Endpoint público, sem autenticação, para checar um código antes de aplicá-lo no checkout. |
Erros comuns
| code | Status | Quando acontece |
|---|---|---|
VALIDATION_ERROR | 400 | Corpo 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_DUPLICATE | 400 | Já existe um cupom com este code nesta conta (Criar Cupom). |
COUPON_CODE_REQUIRED | 400 | Validar Cupom foi chamado sem o campo code no body. |
COUPON_INACTIVE_FINAL | 400 | Tentativa de mudar o status de um cupom INACTIVE de volta para ACTIVE ou PAUSED. É um estado final; crie um cupom novo. |
COUPON_NOT_FOUND | 404 | couponId 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?▾
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)?▾
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?▾
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.