Get started
Payment link
One call, one URL: charge without building a checkout, without touching a card.
The shortest path between "I have a published product" and "somebody paid me". You create a link, send it, and that is it — there is no checkout to build: no payment page, no card input, no provider SDK in your app, no PCI scope.
First: two things the link requires
const link = await infi.links.create(productId, { slug: "your-tenant" });
link.url;
// https://app-sandbox.beinfi.com/pay/your-tenant/links/plink_… ← send this
// (with sk_live_ the host is app.beinfi.com)That line only works if both pieces below exist — both come from catalog:
| Piece | Where it comes from |
|---|---|
productId | products.create() (or products.list()). The productId from provisioning belongs to the seed product, which does not work |
| A published product version | products.versions.publish(...) — without it, links.create answers 422 product has no published version |
The slug is your tenant's and goes into the public URL, so it is an argument:
the SDK does not infer it from a secret key.
What happens when somebody opens it
The link has no payer. Whoever opens it fills in their own details and pays; the customer and the invoice are materialised on submit. That is what lets you send the same link to several people — or to a group — without registering anyone beforehand.
The money is received by your provider account. Which provider processes it is decided by Infi Routing at payment time, not when the link is created.
Pix and boleto require the payer's tax id
The provider refuses to create the payer without a document: the charge stops at
422 customer_tax_id_required ("A CPF/CNPJ is required to process this
payment"). This applies to pix and boleto. If you build the checkout in your
own app instead of using the link, pass taxId with the customer:
infi.checkout({ slug, productId, customer: { externalId, email, taxId } }).
Where the payer goes afterwards
By default they stay on our receipt. If you want the payer back on your site, pass the URLs when creating the link:
const link = await infi.links.create(productId, {
slug: "your-tenant",
successUrl: "https://your-app.com/thanks?order=42",
cancelUrl: "https://your-app.com/cart",
});After paying, the checkout takes the payer to successUrl with
?status=success&invoice=<id> appended — your own parameters are kept.
cancelUrl shows up as "Back to {your store}" while the checkout is open.
Both must be absolute http(s) URLs; a relative path or any other scheme
answers 422.
The same pair exists on infi.checkout({ successUrl, cancelUrl }) for invoices
created on your server, and in the embed (@beinfi/checkout) the equivalent is
the returnUrl prop.
A redirect is not a confirmation
status=success in the URL is an event in the payer's browser. Release the
product on the payment.confirmed webhook, never on the parameter.
Listing and revoking
await infi.links.list(productId, { slug: "your-tenant" });
await infi.links.revoke(productId, link.id);Revoking is final
The token stops resolving immediately. Invoices already created from that link stay payable — somebody mid-checkout does not lose the charge in their hands.
So how do I know they paid?
Not from what links.create returns: payment is asynchronous. In production, a
signed webhook (payment.confirmed); in sandbox, polling the invoice — both in
webhooks.
When to use the link, and when not to
Use the link
A one-off sale, charging over WhatsApp, a first sale before you have an app.
Use metering
Continuous usage billing (tokens, requests), where the amount only exists after the customer consumed it — see SDK.
If you want to control the screen and still use a link
The buyer's flow over HTTP — opening a session, charging, and where the invoice shows up — is in the thank-you page.