O downgrade permite que clientes migrem para um plano inferior quando precisam reduzir custos. Diferente do upgrade, o downgrade normalmente entra em vigor apenas no proximo ciclo de cobranca, garantindo que o cliente aproveite o que ja pagou.
Downgrade vs cancelamento
Acao Resultado Downgrade Cliente continua assinante com menos recursos Cancelamento Cliente perde acesso ao final do periodo
Oferecer downgrade como alternativa ao cancelamento pode reduzir significativamente o churn. Considere apresentar a opcao de downgrade na pagina de cancelamento.
Opcoes de downgrade
A Chargefy suporta dois modos de downgrade:
1. Downgrade ao final do periodo (recomendado)
O cliente mantem acesso ao plano atual ate o fim do ciclo ja pago. O novo preco entra em vigor no proximo ciclo.
O novo preco e aplicado imediatamente. Pode gerar credito para o proximo ciclo.
Passo 1: Agendar downgrade ao final do periodo
Via SDK
// Downgrade de Pro (R$ 99,90/mes) para Basic (R$ 49,90/mes)
const downgraded = await chargefy . subscriptions . update ( 'sub_xxxx' , {
productPriceId: 'price_basic_mensal_xxxx' ,
proration: false , // aplicar no proximo ciclo
})
console . log ( 'Downgrade agendado!' )
console . log ( 'Plano atual:' , downgraded . product . name , '(ativo ate' , downgraded . currentPeriodEnd , ')' )
console . log ( 'Mudanca agendada:' , downgraded . scheduledChange ?. productPriceId )
console . log ( 'Novo valor a partir de:' , downgraded . scheduledChange ?. effectiveDate )
Via cURL
curl -X PATCH https://api.chargefy.io/v1/subscriptions/sub_xxxx \
-H "Authorization: Bearer $CHARGEFY_ACCESS_TOKEN " \
-H "Content-Type: application/json" \
-d '{
"productPriceId": "price_basic_mensal_xxxx",
"proration": false
}'
Com proration: false, nenhuma cobranca ou reembolso e gerado. O cliente simplesmente passa a pagar o valor do novo plano no proximo ciclo.
Se desejar aplicar o downgrade imediatamente, habilite a prorata. A Chargefy calcula o credito proporcional dos dias restantes do plano superior.
Via SDK
const downgraded = await chargefy . subscriptions . update ( 'sub_xxxx' , {
productPriceId: 'price_basic_mensal_xxxx' ,
proration: true , // aplicar imediatamente com credito
})
console . log ( 'Downgrade imediato!' )
console . log ( 'Credito gerado: R$' , ( downgraded . prorationCredit / 100 ). toFixed ( 2 ))
console . log ( 'Novo valor mensal: R$' , ( downgraded . amount / 100 ). toFixed ( 2 ))
Via cURL
curl -X PATCH https://api.chargefy.io/v1/subscriptions/sub_xxxx \
-H "Authorization: Bearer $CHARGEFY_ACCESS_TOKEN " \
-H "Content-Type: application/json" \
-d '{
"productPriceId": "price_basic_mensal_xxxx",
"proration": true
}'
Calculo do credito
Exemplo: Downgrade imediato no dia 16 de um ciclo mensal (31 dias)
Plano atual: Pro (R$ 99,90/mes)
Novo plano: Basic (R$ 49,90/mes)
Dias restantes: 15
Credito Pro: R$ 99,90 x (15/31) = R$ 48,34
Custo Basic: R$ 49,90 x (15/31) = R$ 24,15
Credito liquido: R$ 48,34 - R$ 24,15 = R$ 24,19
(aplicado como desconto no proximo ciclo)
Passo 3: Cancelar downgrade agendado
Se o cliente mudar de ideia antes do downgrade ser efetivado, cancele a mudanca agendada.
Via SDK
const restored = await chargefy . subscriptions . update ( 'sub_xxxx' , {
cancelScheduledChange: true ,
})
console . log ( 'Downgrade cancelado — plano Pro mantido' )
console . log ( 'Mudanca agendada:' , restored . scheduledChange ) // null
Via cURL
curl -X PATCH https://api.chargefy.io/v1/subscriptions/sub_xxxx \
-H "Authorization: Bearer $CHARGEFY_ACCESS_TOKEN " \
-H "Content-Type: application/json" \
-d '{
"cancelScheduledChange": true
}'
Passo 4: Revogar recursos no momento certo
A parte mais importante do downgrade e revogar o acesso aos recursos do plano superior no momento correto.
Para downgrade ao final do periodo
Revogue os recursos quando receber o webhook subscription.updated com o novo preco.
case 'subscription.updated' :
const { data } = event
// Verificar se foi downgrade
if ( data . previousAmount && data . amount < data . previousAmount ) {
console . log ( 'Downgrade efetivado!' )
console . log ( `De R$ ${ ( data . previousAmount / 100 ). toFixed ( 2 ) } para R$ ${ ( data . amount / 100 ). toFixed ( 2 ) } ` )
// Atualizar banco de dados
await db . subscriptions . update (
{ chargefy_id: data . id },
{
product_id: data . product . id ,
amount: data . amount ,
tier: data . product . metadata ?. tier ,
}
)
// Revogar recursos do plano superior
await downgradeFeatures ( data . customer . id , data . product . metadata ?. tier )
// Notificar cliente
await sendEmail ( data . customer . email , 'plan_downgraded' , {
newPlan: data . product . name ,
newAmount: `R$ ${ ( data . amount / 100 ). toFixed ( 2 ) } ` ,
})
}
break
Funcao de revogacao de recursos
async function downgradeFeatures ( customerId : string , newTier : string ) {
const featureLimits : Record < string , { users : number ; storage : number ; apiCalls : number }> = {
basic: { users: 1 , storage: 5 , apiCalls: 1000 },
pro: { users: 10 , storage: 50 , apiCalls: 10000 },
enterprise: { users: - 1 , storage: - 1 , apiCalls: - 1 }, // ilimitado
}
const limits = featureLimits [ newTier ]
if ( ! limits ) return
// Atualizar limites no banco
await db . organizations . update (
{ customer_id: customerId },
{
max_users: limits . users ,
max_storage_gb: limits . storage ,
max_api_calls: limits . apiCalls ,
}
)
// Verificar se organizacao excede novos limites
const org = await db . organizations . findOne ({ customer_id: customerId })
if ( limits . users > 0 && org . active_users > limits . users ) {
// Notificar admin que precisa reduzir usuarios
await sendEmail ( org . admin_email , 'reduce_users_required' , {
currentUsers: org . active_users ,
maxUsers: limits . users ,
deadline: '7 dias' ,
})
}
}
Para limites de usuarios, nao desative contas automaticamente. Envie um aviso ao admin e de um prazo (ex: 7 dias) para que ele escolha quais usuarios manter.
Webhooks do downgrade
Evento Quando ocorre Contexto subscription.updatedDowngrade efetivado (inicio do novo ciclo ou imediato) data.amount contem novo valorsubscription.activeProximo ciclo cobrado com valor reduzido Valor do plano inferior
Diferenciar upgrade de downgrade no webhook
case 'subscription.updated' :
const isUpgrade = event . data . amount > ( event . data . previousAmount || 0 )
const isDowngrade = event . data . amount < ( event . data . previousAmount || Infinity )
if ( isUpgrade ) {
await upgradeFeatures ( event . data . customer . id , event . data . product . metadata ?. tier )
} else if ( isDowngrade ) {
await downgradeFeatures ( event . data . customer . id , event . data . product . metadata ?. tier )
}
break
src/components/DowngradePlan.tsx
import { useState } from 'react'
interface DowngradeProps {
subscriptionId : string
currentPlan : string
currentAmount : number
targetPlan : string
targetAmount : number
currentPeriodEnd : string
}
export function DowngradePlan ({
subscriptionId , currentPlan , currentAmount ,
targetPlan , targetAmount , currentPeriodEnd ,
} : DowngradeProps ) {
const [ confirmed , setConfirmed ] = useState ( false )
const [ processing , setProcessing ] = useState ( false )
const [ mode , setMode ] = useState < 'end_of_period' | 'immediate' >( 'end_of_period' )
const formatCurrency = ( cents : number ) => `R$ ${ ( cents / 100 ). toFixed ( 2 ) } `
const savings = currentAmount - targetAmount
async function handleDowngrade () {
setProcessing ( true )
const res = await fetch ( `/api/subscriptions/ ${ subscriptionId } /change-plan` , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ({
newPriceId: `price_ ${ targetPlan } _mensal` ,
immediate: mode === 'immediate' ,
}),
})
if ( res . ok ) {
alert ( mode === 'end_of_period'
? `Downgrade agendado para ${ new Date ( currentPeriodEnd ). toLocaleDateString ( 'pt-BR' ) } `
: 'Downgrade aplicado imediatamente'
)
window . location . href = '/dashboard'
} else {
alert ( 'Erro ao processar downgrade.' )
setProcessing ( false )
}
}
return (
< div style = { { maxWidth: '500px' , margin: '0 auto' } } >
< h2 > Downgrade de plano </ h2 >
< div style = { { background: '#fef3c7' , padding: '1rem' , borderRadius: '8px' , marginBottom: '1.5rem' } } >
< p >< strong > Atencao: </ strong > Ao fazer downgrade, voce perdera acesso aos recursos do plano { currentPlan } . </ p >
</ div >
< div style = { { border: '1px solid #e5e7eb' , borderRadius: '8px' , padding: '1.5rem' , marginBottom: '1rem' } } >
< p >< strong > Plano atual: </ strong > { currentPlan } — { formatCurrency ( currentAmount ) } /mes </ p >
< p >< strong > Novo plano: </ strong > { targetPlan } — { formatCurrency ( targetAmount ) } /mes </ p >
< p style = { { color: '#16a34a' } } >< strong > Economia: </ strong > { formatCurrency ( savings ) } /mes </ p >
</ div >
< div style = { { marginBottom: '1.5rem' } } >
< p >< strong > Quando aplicar? </ strong ></ p >
< label style = { { display: 'block' , padding: '0.5rem 0' } } >
< input
type = "radio"
checked = { mode === 'end_of_period' }
onChange = { () => setMode ( 'end_of_period' ) }
/>
{ ' ' } Ao final do periodo atual ( {new Date ( currentPeriodEnd ). toLocaleDateString ( 'pt-BR' ) } )
</ label >
< label style = { { display: 'block' , padding: '0.5rem 0' } } >
< input
type = "radio"
checked = { mode === 'immediate' }
onChange = { () => setMode ( 'immediate' ) }
/>
{ ' ' } Imediatamente (credito proporcional aplicado)
</ label >
</ div >
< label style = { { display: 'flex' , alignItems: 'center' , gap: '0.5rem' , marginBottom: '1rem' } } >
< input type = "checkbox" checked = { confirmed } onChange = { e => setConfirmed ( e . target . checked ) } />
Entendo que perderei acesso aos recursos do plano { currentPlan }
</ label >
< button
onClick = { handleDowngrade }
disabled = { ! confirmed || processing }
style = { {
width: '100%' , padding: '0.75rem' ,
background: confirmed ? '#dc2626' : '#d1d5db' ,
color: 'white' , border: 'none' , borderRadius: '6px' ,
cursor: confirmed && ! processing ? 'pointer' : 'not-allowed' ,
} }
>
{ processing ? 'Processando...' : 'Confirmar downgrade' }
</ button >
</ div >
)
}
Boas praticas
Prefira downgrade ao final do periodo para evitar complexidade de reembolsos
Avise com antecedencia quais recursos serao perdidos antes de confirmar
De prazo para ajustes quando o cliente excede os limites do novo plano (ex: usuarios)
Ofereça alternativas — desconto temporario pode ser mais vantajoso que downgrade
Registre o motivo do downgrade para analise de churn
Proximos passos
Upgrade de Assinatura Implemente o fluxo reverso — upgrade de plano.
Multiplas Assinaturas Permita que clientes tenham mais de uma assinatura ativa.