docs

Preços (ProductPrice)

Modelo de preço da Zhex — base único em centavos, conversão automática BRL/USD/EUR, descrição de fatura e melhores práticas.

ProductPrice é o valor que o cliente vê. Cada Product tem um único ProductPrice ativo por vez, com um valor base e uma lista de moedas habilitadas para conversão automática em tempo de checkout.

Por que valor em centavos

base_amount é sempre inteiro em centavos da base_currency — nunca decimal.

DisplayValor base_amount
R$ 497,0049700
US$ 49.994999
€ 9,90990

Por quê? Floats em JavaScript fazem 0.1 + 0.2 = 0.30000000000000004. Em pagamentos isso é um chargeback esperando para acontecer. Toda math da Zhex é em inteiros até o último step de display.

Multi-currency: como funciona

Quando você define base_amount: 49700 + base_currency: "BRL" + enabled_currencies: ["BRL", "USD", "EUR"], a Zhex:

A taxa de câmbio é lockada no momento que o PaymentIntent é criado, não no clique de "Pagar". Isso evita arbitragem de FX com sessões longas e dá previsibilidade ao seu reconciliation.

A base_currency continua sendo a moeda do seu payout: se você é PJ no Brasil cobrando em BRL, o cliente americano paga em USD via cross-border, mas você recebe em BRL no payout (descontando o spread informado).

Buscar preço atual

Endpoint: GET /v1/products/:productId/price

const res = await fetch(
  `https://prometheus.zhex.io/v1/products/${productId}/price`,
  {
    headers: { Authorization: `Bearer ${process.env.ZHEX_SECRET_KEY}` },
  },
);
const price = await res.json();
// {
//   id: "ppr_...",
//   base_amount: 49700,
//   base_currency: "BRL",
//   enabled_currencies: ["BRL", "USD", "EUR"],
//   payment_description: "Curso Node Avançado · Acesso 12m"
// }

Atualizar preço

Endpoint: POST /v1/products/:productId/price

curl -X POST https://prometheus.zhex.io/v1/products/prd_xyz/price \
  -H "Authorization: Bearer $ZHEX_SECRET_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "base_amount": 59700,
    "base_currency": "BRL",
    "enabled_currencies": ["BRL", "USD"],
    "payment_description": "Curso Node Avançado · Acesso 12m"
  }'

Campos

CampoTipoNotas
base_amountintCentavos da base_currency. Mín 100 (R$ 1,00).
base_currencystringBRL, USD ou EUR. Define moeda do seu payout.
enabled_currenciesstring[]Moedas exibidas no checkout. Sempre inclua a base_currency.
payment_descriptionstring?Aparece no statement do cartão e na fatura por email. Limite 22 chars (limite Visa/Master).

payment_description: o detalhe que evita chargeback

O statement do cartão é o que o cliente lê quando aparece a cobrança na fatura 30 dias depois. Se ele não reconhecer, o ticket vai pra dispute_chargeback.

Bom:

  • ZHEX*CursoNode12m
  • MEUSITE*Premium

Ruim:

  • ZHX BR PAYMNT 8392 ← bandeira recusa, gera friction
  • Pagamento ← sem branding, leva a "que cobrança é essa?"

A Zhex prefixa automaticamente com ZHEX* se o seu MCC exigir, mas sua descrição vai depois.

Atualizações in-place

ProductPrice é singleton por produtoPOST /v1/products/:productId/price muta o registro existente. Cobranças já registradas (PaymentIntent em curso, CustomerSubscription ativa) continuam com os valores que tinham na hora da criação — você sobe o preço sem afetar quem já está pagando.

// CustomerSubscription criada em janeiro com base_amount=49700
// Você sobe para 59700 em fevereiro
// → cobranças recorrentes da subscription antiga continuam em 49700
//   até você fazer upgrade explícito (configurado pelo dashboard)

Preço em produto recorrente

Quando o produto é RECURRING, o ProductPrice define o valor de cada ciclo. A configuração de intervalo (mensal, anual, lifetime) e trial vive no template de assinatura no dashboard — CustomerSubscription é a instância ativa que herda essas regras.

Melhores práticas

  • Lock o FX cedo. Se seu funil tem upsell/downsell, sempre crie PaymentIntents separados — não reutilize FX entre etapas distintas.
  • Não diminua enabled_currencies em produção sem um plano. Cliente que abriu checkout em USD e voltou 10 min depois pode receber MISMATCH_CURRENCY.
  • payment_descriptionProduct.name. O nome pode ter 60 chars, a descrição é mais curta porque vai pro statement.
Esta página foi útil?

Atualizado em

Nesta página