Produtos e Ofertas
Produtos e Ofertas
Product é o catálogo do seu negócio: nome, descrição, imagem. Offer é a variação de preço e ciclo de cobrança desse produto. Um Product tem várias Offers — você pode vender o mesmo curso por R$ 497 à vista (oferta one-time) ou por 12x de R$ 49 (oferta recorrente mensal) sem duplicar o catálogo.
Product "Curso de Marketing"
├── Offer "Pagamento único" R$ 497,00 one-time
├── Offer "Mensal 12x" R$ 49,00 MONTHLY × 12 ciclos
└── Offer "Anual" R$ 397,00 YEARLY × infinito
PaymentLinks, Subscriptions e Invoices ficam vinculados a uma Offer, não diretamente ao Product. Você consulta o Product a partir da Offer.
Editar Offer ativa
Quando seu cliente assina uma Offer, a Pagnovo congela os valores da Offer (amount, billingCycle, trialDays, maxCycles, billingCycleCount) dentro da Subscription. Cada assinatura carrega o preço e o ciclo que existia no momento em que o cliente assinou.
Isso garante que:
- Aumentar o preço de uma Offer não cobra mais dos assinantes existentes. Eles continuam pagando o valor que assinaram.
- Mudar o ciclo de
MONTHLYpraYEARLYnuma Offer ativa não converte as Subscriptions antigas. - Histórico de faturamento sempre reflete o preço que o cliente concordou em pagar.
A consequência: editar esses campos de uma Offer com assinantes ou PaymentLinks ativos é confuso — você muda a Offer mas ninguém é afetado, exceto novos clientes. Pra evitar essa pegadinha, a API bloqueia:
| Campo | Editável sempre | Bloqueia se há ativos |
|---|---|---|
name, description | ✓ | — |
deliveryPdfUrl | ✓ | — |
active (desativar Offer) | ✓ | — |
externalId, metadata | ✓ | — |
amount | — | ✗ |
billingCycle | — | ✗ |
billingCycleCount | — | ✗ |
maxCycles | — | ✗ |
trialDays | — | ✗ |
Onde "há ativos" significa: Subscriptions em status ACTIVE/TRIAL/PAST_DUE/PAUSED, ou PaymentLinks em status ACTIVE/PAUSED.
Padrão recomendado para mudar preço:
- Crie uma Offer nova com o preço novo.
- Marque a Offer antiga como
active: false(some das listagens de venda, mas Subscriptions existentes continuam funcionando). - Aponte seus novos PaymentLinks pra Offer nova.
stats.canEditSnapshotFields em GET /v2/offers/:id te diz se a Offer está livre pra editar todos os campos.
One-time vs recurring
O que define o tipo é o campo billingCycle na criação:
One-time (billingCycle: null ou omitido):
- Pagamento único. Cliente paga uma vez e recebe o produto.
maxCycles,trialDaysebillingCycleCountsão ignorados se enviados.- Faz sentido pra cursos, e-books, produtos digitais "fechados".
Recurring (billingCycle preenchido com um enum):
- Cobrança recorrente. Gera nova Invoice a cada ciclo.
- Valores válidos:
DAILY,WEEKLY,MONTHLY,QUARTERLY,SEMIANNUAL,YEARLY. billingCycleCount(default 1): permite intervalos custom.MONTHLY+billingCycleCount: 3= a cada 3 meses. Em Stripe seriainterval: "month", interval_count: 3.maxCycles(default null): número total de cobranças antes da Subscription expirar automaticamente.null= infinito.trialDays(default 0): dias grátis antes da primeira cobrança. Útil pra trial de SaaS.
Combinações úteis:
| Cenário | Config |
|---|---|
| Mensal infinito | MONTHLY, count=1, max=null |
| Mensal 12x (anual parcelado) | MONTHLY, count=1, max=12 |
| Bimestral | MONTHLY, count=2, max=null |
| Anual com 14 dias de trial | YEARLY, count=1, max=null, trialDays=14 |
| Trimestral por 1 ano | QUARTERLY, count=1, max=4 |
Unidades — centavos
Offers usam centavos no campo amount, igual ao resto da API:
| Valor real | Centavos |
|---|---|
| R$ 1,00 | 100 |
| R$ 49,00 | 4900 |
| R$ 497,00 | 49700 |
| R$ 1.000,00 | 100000 |
Range válido para amount: 100 (R$ 1,00) a 5.000.000 (R$ 50.000,00).