provider field comes back "infi", and everything below
describes Asaas behaviour, because Asaas is what sits underneath.
That is great for fidelity, and its annoying part is over: you close the loop
yourself, without anybody’s key. The QR we return in sandbox is a confirmation
page of ours — scan it with your phone, press a button, the invoice turns paid.
What comes back on a pix charge
pixPayload— render it as a QR, exactly as in production. In sandbox it is the URL of our confirmation page; in production it is the copy-and-paste EMV. Your rendering code is the same either way: it is a QR.pixQrImage— a ready base64 PNG, generated from that samepixPayload. When it comes, use it instead of generating the QR yourself.sandboxConfirmUrl— exists only in sandbox, and it is the explicit marker. If you want a “confirm as if I had paid” button on your test screen, check this field. Never by sniffing whetherpixPayloadlooks like a URL: in production it is EMV, and that kind of check is how a test-only thing leaks into the real checkout.providerPixPayload— Asaas’s EMV, so you can see what production would return. It is not payable in a banking app: it is a homologation account.- If
pixPayloadcomes back empty, the charge exists but cannot be paid. Do not invent a QR fromidorproviderId: the payer’s bank refuses it, and the screen lies to them. Show an error and offer another method.
Closing the loop: confirming the payment
Two ways, and neither needs a provider key. By scanning, which is the test that counts — it is your real buyer’s flow: render thepixPayload as a QR, point your phone’s camera at it, open it. You
land on a page of ours with the amount and a button.
Or over HTTP, for your CI:
$SANDBOX_CONFIRM_URL is the sandboxConfirmUrl the charge returned. In the
SDK:
200 and does nothing — it is a
page people reload.
Measured end to end: confirmation → paid in about 3s, with the download grant
issued and delivery triggered.
Underneath it is Asaas confirming, not us making it up. Our route does not write “paid” into the database. It calls the sandbox
confirm of the provider that created the charge, and that provider’s real
webhook comes back through the same path production uses. That is why you see
pixTransaction, a deducted fee and a credit date — the charge is real, only
the confirmation is yours.How to know they paid
Two ways, and in sandbox only one works today:
Registering a webhook in sandbox answers
503 secret_store_unavailable — the
secret store is not available to a sandbox tenant. So in sandbox, polling is
the way:
What does NOT exist in the sandbox
Things that answer with an error and are not your bug:providers.*→404. Connecting a PSP (BYOP) is a production-only surface. In sandbox the processor is Infi — a provider of ours, and what shows up inprovideron your charges. It runs on Asaas underneath, which explains the formats and errors you see, but the account is ours and there is nothing to connect.webhooks.create→503, as above.- Card may come disabled. The link’s public response carries
cardEnabled; when it isfalse, only pix and boleto are available on that tenant.