Pular para o conteúdo principal
Antes da primeira venda: hoje a venda é no Brasil (você e o comprador no Brasil), por Pix ou cartão à vista, os dois em ambiente de teste. A cobrança cai na sua conta de recebimento no provedor de pagamento. Venda internacional, com a Infi UK Ltd como vendedora oficial, está em breve. Veja colocar em produção.

Qual caminho é o seu

Os dois terminam no mesmo lugar: o comprador paga na página de checkout da Infi (/pay/...), que mostra o QR do Pix ou o formulário de cartão. Os dois terminam na mesma fatura e no mesmo webhook. A diferença é quanto da experiência é sua.
Na dúvida, comece pelo link. É o único que não exige nada do seu app. Você troca depois: o produto e o catálogo são os mesmos.

O caminho do meio, ponta a ponta

Assumindo que você já tem um produto publicado (veja catálogo), uma chave e a conta pronta para receber:
Você precisa mapear invoiceId → seu usuário. Nós não sabemos quem é o seu usuário: você passa um externalId e nós devolvemos um invoiceId. Quando o pagamento confirmar, o webhook traz o invoiceId, não o seu usuário. Se você não guardou o par, recebeu dinheiro e não sabe de quem.É a decisão de arquitetura mais importante desta página e ela cabe em uma linha de tabela no seu banco.

Cobrar: a resposta do charge

A resposta traz o que o comprador precisa para concluir, como o QR Code do Pix, e o fluxo fica na página de checkout da Infi. O status volta pending.
Não confie na volta do comprador. Ele pode voltar sem ter pago, ou nem voltar. Quem confirma é o webhook; a query string do retorno nunca prova nada.

Saber que pagou

Não confie no retorno do charge: ele volta pending. Pagamento é assíncrono, e a confirmação chega por webhook (payment.confirmed, invoice.paid).
Quando confirmar, use o invoiceId do evento pra achar o pedido que você guardou no passo 2. Detalhes em webhooks.

O comprador clicou duas vezes em “Comprar”

Todo método que não é GET na API autenticada exige Idempotency-Key (as rotas públicas de /pay/*, que o navegador do comprador chama, não exigem). É o que impede que dois cliques virem duas faturas.
  • Mesma chave, mesmo corpo → você recebe a resposta original de volta. Uma fatura só.
  • Mesma chave, corpo diferente → 409 idempotency_key_reused. É proteção: quer dizer que você reusou a chave pra outra coisa.
  • Sem chave → 400 idempotency_key_required.
Gerar uma chave nova a cada chamada protege contra retry de rede, não contra clique duplo. Para o clique duplo, a chave tem que vir de algo estável na sua intenção, como no exemplo acima.
O mais simples é não deixar clicar duas vezes. Desabilite o botão no primeiro clique e trate a Idempotency-Key como a rede de segurança, não como a primeira linha de defesa.

Antes de vender: o nome que o comprador vê

Seu tenant nasce com um nome de placeholder. Se você não trocar, o checkout e o link de pagamento dizem literalmente “New app”, e ninguém compra de uma loja chamada New app.
Vale em seguida, sem republicar produto e sem gerar link novo: o mesmo link passa a mostrar o nome novo. infi.account.get() lê de volta. (A partir do @beinfi/sdk@0.10.7; antes disso é PATCH /account/tenant.)

Vendeu. Agora entrega

Se o que você vende é um arquivo ou um acesso, não monte isso à mão: anexe o entregável ao produto e a Infi manda o link pessoal pro comprador quando o pagamento confirma, e te devolve o mesmo link pra você mostrar na sua página de obrigado. Está em entregar o produto. O lado do comprador (descobrir que pagou, entregar, e os dois polling que ninguém adivinha) está em a página de obrigado.