Skip to main content
Variantes permitem oferecer diferentes niveis de um mesmo produto — por exemplo, planos Basic, Pro e Enterprise — cada um com seus proprios precos e intervalos de cobranca. Na Chargefy, variantes sao representadas como multiplos precos dentro de um unico produto ou como produtos distintos agrupados por metadados.

Estrategias de modelagem

EstrategiaQuando usarExemplo
Multiplos precos em um produtoMesmo recurso, intervalos diferentesPlano Pro mensal (R99,90)eanual(R 99,90) e anual (R 999,00)
Produtos separadosRecursos diferentes por nivelBasic, Pro e Enterprise com funcionalidades distintas

Passo 1: Criar produto com multiplos precos

O jeito mais simples de criar variantes e adicionar varios precos ao mesmo produto — cada preco representa um intervalo ou valor diferente.

Via SDK

const product = await chargefy.products.create({
  name: 'Plataforma SaaS',
  description: 'Ferramenta completa de gestao para empresas',
  prices: [
    // Basic - Mensal
    {
      type: 'recurring',
      amountType: 'fixed',
      priceAmount: 4990, // R$ 49,90/mes
      priceCurrency: 'brl',
      recurringInterval: 'month',
      metadata: { tier: 'basic' },
    },
    // Basic - Anual
    {
      type: 'recurring',
      amountType: 'fixed',
      priceAmount: 47900, // R$ 479,00/ano (~20% desconto)
      priceCurrency: 'brl',
      recurringInterval: 'year',
      metadata: { tier: 'basic' },
    },
    // Pro - Mensal
    {
      type: 'recurring',
      amountType: 'fixed',
      priceAmount: 9990, // R$ 99,90/mes
      priceCurrency: 'brl',
      recurringInterval: 'month',
      metadata: { tier: 'pro' },
    },
    // Pro - Anual
    {
      type: 'recurring',
      amountType: 'fixed',
      priceAmount: 95900, // R$ 959,00/ano (~20% desconto)
      priceCurrency: 'brl',
      recurringInterval: 'year',
      metadata: { tier: 'pro' },
    },
    // Enterprise - Mensal
    {
      type: 'recurring',
      amountType: 'fixed',
      priceAmount: 29990, // R$ 299,90/mes
      priceCurrency: 'brl',
      recurringInterval: 'month',
      metadata: { tier: 'enterprise' },
    },
    // Enterprise - Anual
    {
      type: 'recurring',
      amountType: 'fixed',
      priceAmount: 287900, // R$ 2.879,00/ano (~20% desconto)
      priceCurrency: 'brl',
      recurringInterval: 'year',
      metadata: { tier: 'enterprise' },
    },
  ],
})

console.log('Produto criado:', product.id)
console.log('Total de precos:', product.prices.length)

Via cURL

curl -X POST https://api.chargefy.io/v1/products \
  -H "Authorization: Bearer $CHARGEFY_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Plataforma SaaS",
    "description": "Ferramenta completa de gestao para empresas",
    "prices": [
      {
        "type": "recurring",
        "amountType": "fixed",
        "priceAmount": 4990,
        "priceCurrency": "brl",
        "recurringInterval": "month",
        "metadata": { "tier": "basic" }
      },
      {
        "type": "recurring",
        "amountType": "fixed",
        "priceAmount": 47900,
        "priceCurrency": "brl",
        "recurringInterval": "year",
        "metadata": { "tier": "basic" }
      },
      {
        "type": "recurring",
        "amountType": "fixed",
        "priceAmount": 9990,
        "priceCurrency": "brl",
        "recurringInterval": "month",
        "metadata": { "tier": "pro" }
      },
      {
        "type": "recurring",
        "amountType": "fixed",
        "priceAmount": 95900,
        "priceCurrency": "brl",
        "recurringInterval": "year",
        "metadata": { "tier": "pro" }
      },
      {
        "type": "recurring",
        "amountType": "fixed",
        "priceAmount": 29990,
        "priceCurrency": "brl",
        "recurringInterval": "month",
        "metadata": { "tier": "enterprise" }
      },
      {
        "type": "recurring",
        "amountType": "fixed",
        "priceAmount": 287900,
        "priceCurrency": "brl",
        "recurringInterval": "year",
        "metadata": { "tier": "enterprise" }
      }
    ]
  }'

Passo 2: Listar variantes de um produto

Para exibir as opcoes ao cliente, busque o produto e agrupe os precos por tier e intervalo.

Via SDK

const product = await chargefy.products.get('prod_xxxx')

// Agrupar precos por tier
const tiers = product.prices.reduce((acc, price) => {
  const tier = price.metadata?.tier || 'default'
  if (!acc[tier]) acc[tier] = []
  acc[tier].push({
    id: price.id,
    amount: price.priceAmount,
    formatted: `R$ ${(price.priceAmount / 100).toFixed(2)}`,
    interval: price.recurringInterval,
  })
  return acc
}, {} as Record<string, any[]>)

console.log('Variantes:', JSON.stringify(tiers, null, 2))
// {
//   "basic": [
//     { "id": "price_xxx", "formatted": "R$ 49,90", "interval": "month" },
//     { "id": "price_yyy", "formatted": "R$ 479,00", "interval": "year" }
//   ],
//   "pro": [...],
//   "enterprise": [...]
// }

Passo 3: Criar checkout para uma variante

O checkout e criado usando o productPriceId da variante escolhida pelo cliente.
// Cliente escolheu: Pro Anual
const checkout = await chargefy.checkouts.create({
  productPriceId: 'price_pro_anual_xxxx',
  customerEmail: '[email protected]',
  successUrl: 'https://meuapp.com.br/assinatura/sucesso',
})

console.log('Checkout URL:', checkout.url)

Exemplo completo: Tabela de precos React

src/components/PricingTable.tsx
import { useState, useEffect } from 'react'

interface Price {
  id: string
  amount: number
  formatted: string
  interval: string
}

interface Tier {
  name: string
  key: string
  description: string
  features: string[]
  prices: Price[]
  highlighted?: boolean
}

const TIER_CONFIG: Record<string, Omit<Tier, 'prices'>> = {
  basic: {
    name: 'Basic',
    key: 'basic',
    description: 'Para profissionais autonomos',
    features: ['1 usuario', '1.000 transacoes/mes', 'Suporte por email', 'Relatorios basicos'],
  },
  pro: {
    name: 'Pro',
    key: 'pro',
    description: 'Para pequenas e medias empresas',
    features: ['Ate 10 usuarios', '10.000 transacoes/mes', 'Suporte prioritario', 'Relatorios avancados', 'API completa'],
    highlighted: true,
  },
  enterprise: {
    name: 'Enterprise',
    key: 'enterprise',
    description: 'Para grandes operacoes',
    features: ['Usuarios ilimitados', 'Transacoes ilimitadas', 'Suporte dedicado', 'SLA 99,9%', 'API completa', 'Webhooks customizados'],
  },
}

export function PricingTable() {
  const [tiers, setTiers] = useState<Tier[]>([])
  const [billingInterval, setBillingInterval] = useState<'month' | 'year'>('month')
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    fetch('/api/products/prod_xxxx')
      .then(r => r.json())
      .then(product => {
        // Agrupar precos por tier
        const grouped: Record<string, Price[]> = {}
        for (const price of product.prices) {
          const tier = price.metadata?.tier || 'default'
          if (!grouped[tier]) grouped[tier] = []
          grouped[tier].push({
            id: price.id,
            amount: price.priceAmount,
            formatted: `R$ ${(price.priceAmount / 100).toFixed(2)}`,
            interval: price.recurringInterval,
          })
        }

        const tierList = Object.entries(TIER_CONFIG).map(([key, config]) => ({
          ...config,
          prices: grouped[key] || [],
        }))

        setTiers(tierList)
        setLoading(false)
      })
  }, [])

  async function handleSubscribe(priceId: string) {
    const res = await fetch('/api/subscriptions/subscribe', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ priceId, customerEmail: '[email protected]' }),
    })
    const { checkoutUrl } = await res.json()
    window.location.href = checkoutUrl
  }

  if (loading) return <div>Carregando planos...</div>

  return (
    <div>
      <h2 style={{ textAlign: 'center' }}>Escolha seu plano</h2>

      {/* Toggle mensal/anual */}
      <div style={{ display: 'flex', justifyContent: 'center', gap: '0.5rem', marginBottom: '2rem' }}>
        <button
          onClick={() => setBillingInterval('month')}
          style={{
            padding: '0.5rem 1rem',
            borderRadius: '4px',
            border: '1px solid #d1d5db',
            background: billingInterval === 'month' ? '#2563eb' : 'white',
            color: billingInterval === 'month' ? 'white' : '#374151',
            cursor: 'pointer',
          }}
        >
          Mensal
        </button>
        <button
          onClick={() => setBillingInterval('year')}
          style={{
            padding: '0.5rem 1rem',
            borderRadius: '4px',
            border: '1px solid #d1d5db',
            background: billingInterval === 'year' ? '#2563eb' : 'white',
            color: billingInterval === 'year' ? 'white' : '#374151',
            cursor: 'pointer',
          }}
        >
          Anual <span style={{ fontSize: '0.75rem' }}>(-20%)</span>
        </button>
      </div>

      {/* Cards de planos */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '1.5rem', maxWidth: '900px', margin: '0 auto' }}>
        {tiers.map(tier => {
          const price = tier.prices.find(p => p.interval === billingInterval)
          if (!price) return null

          return (
            <div
              key={tier.key}
              style={{
                border: tier.highlighted ? '2px solid #2563eb' : '1px solid #e5e7eb',
                borderRadius: '12px',
                padding: '2rem',
                position: 'relative',
              }}
            >
              {tier.highlighted && (
                <span style={{
                  position: 'absolute', top: '-12px', left: '50%', transform: 'translateX(-50%)',
                  background: '#2563eb', color: 'white', padding: '2px 12px', borderRadius: '12px', fontSize: '0.75rem',
                }}>
                  Mais popular
                </span>
              )}

              <h3>{tier.name}</h3>
              <p style={{ color: '#6b7280', fontSize: '0.875rem' }}>{tier.description}</p>

              <p style={{ fontSize: '2rem', fontWeight: 'bold', margin: '1rem 0' }}>
                {price.formatted}
                <span style={{ fontSize: '1rem', fontWeight: 'normal', color: '#6b7280' }}>
                  /{billingInterval === 'month' ? 'mes' : 'ano'}
                </span>
              </p>

              <button
                onClick={() => handleSubscribe(price.id)}
                style={{
                  width: '100%', padding: '0.75rem',
                  background: tier.highlighted ? '#2563eb' : 'white',
                  color: tier.highlighted ? 'white' : '#2563eb',
                  border: '1px solid #2563eb', borderRadius: '6px', cursor: 'pointer',
                }}
              >
                Assinar {tier.name}
              </button>

              <ul style={{ marginTop: '1.5rem', listStyle: 'none', padding: 0 }}>
                {tier.features.map(feature => (
                  <li key={feature} style={{ padding: '0.25rem 0', fontSize: '0.875rem' }}>
                    {feature}
                  </li>
                ))}
              </ul>
            </div>
          )
        })}
      </div>
    </div>
  )
}

Adicionar novas variantes a um produto existente

// Adicionar um preco trimestral ao produto existente
const updatedProduct = await chargefy.products.update('prod_xxxx', {
  prices: [
    // Manter precos existentes (pelo ID)
    { id: 'price_basic_mensal_xxxx' },
    { id: 'price_basic_anual_xxxx' },
    // Adicionar novo preco
    {
      type: 'recurring',
      amountType: 'fixed',
      priceAmount: 12990, // R$ 129,90/trimestre
      priceCurrency: 'brl',
      recurringInterval: 'quarter',
      metadata: { tier: 'basic' },
    },
  ],
})
Ao atualizar precos de um produto, sempre inclua os IDs dos precos existentes que deseja manter. Precos nao listados serao arquivados.

Boas praticas

  • Use metadata para categorizar precos por tier, facilitando a filtragem no frontend
  • Ofereça desconto anual de 15-20% para incentivar compromissos mais longos
  • Destaque o plano mais popular visualmente para guiar a decisao do cliente
  • Mantenha no maximo 3-4 tiers para evitar paralisia de escolha

Proximos passos

Upgrade de Assinatura

Permita que clientes troquem entre variantes.

Precificacao por Assento

Combine variantes com cobranca por assento.