Começar
Catálogo
Produto → versão publicada → link: a corrente que existe antes de você vender qualquer coisa.
Antes de cobrar por algo, esse algo precisa existir no catálogo e estar
publicado. São três chamadas. Sem elas, links.create responde 422.
De onde vem o productId
Toda página de cobrança pede um productId. Ele vem de um destes três lugares:
| Fonte | Quando |
|---|---|
productId do provisionamento | Vem no JSON de POST /public/v1/claimables — é o produto seed, e ele não está publicado |
await infi.products.list() | Você já criou o produto antes |
await infi.products.create({...}) | Criando agora — o retorno tem .id |
A corrente
// 1. produto
const product = await infi.products.create({
key: "guia-precificacao", // chave natural do tenant (upsert idempotente)
name: "Guia de precificação",
type: "item", // "item" (default) ou "agent"
pricingModel: "one_time", // subscription | one_time | usage | prepaid
currency: "BRL",
basePrice: "49.90",
});
// 2. a v1 já vem criada como draft — pegue ela
const [draft] = await infi.products.versions.list(product.id);
// 3. publique (é o passo que ninguém adivinha)
await infi.products.versions.publish(product.id, draft.id);
// 4. agora sim
const link = await infi.links.create(product.id, { slug: "seu-tenant" });products.create já devolve a versão 1 em draft — você não cria versão na mão,
só lista e publica.
Sem versão publicada, 422
Pular o passo 3 dá isto:
{"error_code":"validation_failed","message":"One or more fields are invalid.",
"errors":[{"field":"productId",
"description":"product has no published version; publish it before creating a payment link"}]}Desde o @beinfi/[email protected] esse detalhe chega até você: InfiError.errors[]
carrega { field, description }. Se estiver num SDK anterior, sobe só a mensagem
genérica "One or more fields are invalid" — e falta de publish é a primeira
suspeita.
Preço: você provavelmente não precisa de prices.add
Para produto avulso de valor fixo, o basePrice do produto é o preço — a
fatura e o link já saem com ele. products.prices.add existe para taxa por
meter (por token, por request), não para preço flat.
Meter só entra quando você cobra por uso:
await infi.products.meters.create(product.id, {
name: "tokens", // a chave que você manda em track()
displayName: "Tokens",
unit: "token", // token | request | unit
aggregation: "sum",
valueProperty: "value", // obrigatório salvo aggregation: "count"
});Vender da sua própria página: checkout()
Se você não quer mandar link e sim ter um botão "Comprar" no seu app, é uma chamada. Ela cria a fatura e devolve a URL hospedada onde a pessoa paga (Pix, boleto, cartão):
const { invoice, url } = await infi.checkout({
productId: product.id,
customer: { externalId: seuUserId, email: "[email protected]" },
slug: "seu-tenant",
successUrl: "https://seu-app.com/obrigado",
});
// redirecione a pessoa para `url`O valor sai do preço publicado do produto — passe amount só para sobrescrever.
A pessoa é inscrita no produto no processo, então você recebe uma fatura ligada
ao produto (e não uma cobrança solta).
Pix e boleto exigem CPF/CNPJ do pagador
Sem documento, a cobrança para em 422 customer_tax_id_required. Passe taxId
junto do cliente — a partir de @beinfi/[email protected] o checkout() repassa:
const { invoice, url } = await infi.checkout({
slug: "seu-tenant",
productId: product.id,
customer: { externalId: seuUserId, email: "[email protected]", taxId: "52998224725" },
});Use a url que volta — não monte o endereço à mão. Ela já sai no host certo do
seu modo (app-sandbox com sk_test_, app com sk_live_).
Chamando via curl
Todo POST/PUT/DELETE da API autenticada exige header Idempotency-Key — sem ele volta
400 idempotency_key_required. O SDK gera um por chamada; no curl você manda o
seu.