Skip to main content
Before your first sale: today a sale happens in Brazil (you and the buyer in Brazil), by Pix or card (single payment), both in test environment. The charge lands on your receiving account at the payment provider. International sales, with Infi UK Ltd as the merchant of record, are coming soon. See going to production.

Which path is yours

Both end in the same place: the buyer pays on Infi’s checkout page (/pay/...), which shows the Pix QR or the card form. Both end in the same invoice and the same webhook. The difference is how much of the experience is yours.
When in doubt, start with the link. It is the only one that asks nothing of your app. You can switch later: the product and the catalog are the same.

The middle path, end to end

Assuming you already have a published product (see the catalog), a key and an account ready to receive:
You must map invoiceId → your user. We do not know who your user is: you pass an externalId and we hand back an invoiceId. When the payment confirms, the webhook carries the invoiceId, not your user. If you did not store the pair, you received money and do not know from whom.It is the most important architecture decision on this page and it fits in one row of a table in your database.

Charging: what charge returns

The response carries what the buyer needs to finish, such as the Pix QR code, and the flow stays on Infi’s checkout page. status comes back pending.
Do not trust the buyer’s return. They may come back without having paid, or never come back. The webhook is what confirms; the return query string never proves anything.

Knowing it was paid

Do not trust the return of charge: it comes back pending. Payment is asynchronous, and the confirmation arrives by webhook (payment.confirmed, invoice.paid).
When it confirms, use the event’s invoiceId to find the order you stored in step 2. Details in webhooks.

The buyer clicked “Buy” twice

Every non-GET method on the authenticated API requires an Idempotency-Key (the public /pay/* routes, which the buyer’s browser calls, do not). It is what stops two clicks from becoming two invoices.
  • Same key, same body → you get the original response back. One invoice.
  • Same key, different body → 409 idempotency_key_reused. It is protection: it means you reused the key for something else.
  • No key → 400 idempotency_key_required.
A fresh key on every call protects against a network retry, not against a double click. For the double click, the key must come from something stable in your intent, as in the example above.
The simplest thing is not to let them click twice. Disable the button on the first click and treat the Idempotency-Key as the safety net, not the first line of defense.

Before selling: the name the buyer sees

Your tenant is born with a placeholder name. If you do not change it, the checkout and the payment link literally say “New app”, and nobody buys from a store called New app.
It takes effect at once, with no product republish and no new link: the same link shows the new name. infi.account.get() reads it back. (From @beinfi/sdk@0.10.7; before that it is PATCH /account/tenant.)

Sold. Now deliver

If what you sell is a file or access, do not build that by hand: attach the deliverable to the product and Infi sends the buyer a personal link when the payment confirms, and hands you the same link to show on your thank-you page. It is in delivering the product. The buyer’s side (finding out they paid, delivering, and the two polls nobody guesses) is in the thank-you page.