Payment integrity and Pix
How the SDK validates Pix, boleto, crypto and card payments on your checkout, and how to register trusted recipients.
Besides watching scripts, the SDK checks that the payment details shown to the customer are your store's: the Pix copy-and-paste code, the boleto digit line, the crypto address and the amount. This page explains how it finds this data, what it validates and how to pin your trusted recipients.
How the SDK finds the payment
No special markup is needed. Once it starts, the SDK looks for payment codes on the page:
- in known selectors, such as
[data-pix-code],#pix-code,.pix-copia-cola,[data-pix-qr],[data-crypto-address],#crypto-address,.wallet-address,[data-payment-value]and[data-qr-value]; - in all visible text in the
<body>between 20 and 1,000 characters long; - in page changes (for example, when the Pix code is inserted after the customer chooses Pix);
- every 2 seconds during the first minute, as a fallback.
Codes are recognized by format: Pix BRCode (EMV standard), boleto digit line (47 or 48 digits), Bitcoin and Ethereum addresses, UPI VPAs and EMV QR codes from other countries (PayNow, PromptPay, DuitNow, QR Ph, HK FPS, Transferencias 3.0, CoDi and QR wallets).
The code must be in the page's text
The SDK reads text, not form field values. A Pix copy-and-paste code shown only in
<input readonly value="000201…"> is not detected, and the same goes for a <textarea> filled in by
JavaScript. Show the code as text, for example <div id="pix-code">000201…</div>, or provide the recipient with
Proteside.registerPaymentPayload().
<div class="pix">
<img src="/qrcode/order-1234.png" alt="Pix QR code" />
<div id="pix-code">00020126580014br.gov.bcb.pix0136…6304ABCD</div>
<button type="button">Copy code</button>
</div>Payment validation
The first valid reading of each method on the page becomes the reference for that page load. From then on:
- if the displayed text changes to a different code that doesn't match the reference, the SDK emits the method's
tampering event (
PIX_TAMPERED,BOLETO_TAMPERED,CRYPTO_ADDRESS_SWAP,UPI_TAMPEREDorQR_TAMPERED); - if the content copied to the clipboard is a code that differs from the reference, the SDK emits
CLIPBOARD_HIJACK; - when the reading is valid, the SDK emits
PAYMENT_VALIDATED, which feeds the dashboard's Payment Integrity page.
PAYMENT_VALIDATED is sent on the first detection, when the code changes to the same recipient (for example, a
regenerated QR) and, at most, every 60 seconds while the customer is on the page. The event never carries the code
or the key: only the method, whether the recipient was verified and the number of fields.
Card
The SDK doesn't read card data. It considers a card payment validated when:
- it finds a known payment provider's iframe on the page (Stripe, Adyen, Braintree, Mercado Pago, Pagar.me, PagBank, Iugu, Cielo and others) and observes a successful response to a charge request; or
- it found the iframe and, within 10 minutes, the customer reaches a confirmation page with
obrigado,sucesso,thank-you,success,confirmacaoororder-completein the path.
For the second case to work, the snippet must also be on the confirmation page.
Trusted recipients (Pix Key Pinning)
Without a recipient list, a payment code tampered with from the moment it's first shown would become the reference, and nothing would look wrong. The trusted recipient list solves this: the SDK checks each code's recipient against the keys that belong to your store.
Open Payment protection
In the dashboard, go to Settings → Payment protection, Trusted recipients section. Only owners and admins can add recipients.
Enter the recipient
Choose the Method (Pix, Bitcoin, Ethereum, UPI or Boleto (bank)), fill in the Key and, optionally, a Label.
- Pix: CPF, CNPJ, email, phone number (
+55…) or random key. - Boleto: the issuing bank's code (3 digits) or the full digit line.
Click Add
The recipient is saved immediately and reaches the SDK through the remote configuration. The dashboard stores only the SHA-256 hash and a masked version of the key.
The SDK only receives the hashes. To compare, it normalizes the recipient it reads on the checkout (email in lowercase, phone number digits only, boleto by its first 3 digits) and computes the same hash.
| Situation | Result |
|---|---|
| The code's recipient is on the list | The code becomes the reference and PAYMENT_VALIDATED is sent with the recipient verified. The dashboard shows Recipient matches the trusted list. |
| The recipient is not on the list | The SDK emits the method's tampering event with the reason "untrusted recipient" and doesn't accept the code as the reference. In Block mode, the code's text is replaced with ⚠︎. |
| No recipient registered for the method | No recipient verification. Only the comparison with the first reading applies. |
Limits of neutralization in Block mode
The SDK only neutralizes the text it read. The QR code image, <input value> fields and other copies of the code on
the page stay as they are, and a re-render of the checkout may restore the text. Treat the alert as an incident even
with Block mode on.
Limitations
- Dynamic Pix. A dynamic BRCode carries a charge URL instead of the recipient's key. With no key in the code, there's no recipient verification; the comparison with the first reading still applies.
- Collection boletos (48-digit line, from utilities and taxes) have no bank code and get no recipient verification.
- Pix in a form field isn't read (see the warning at the top of this page).
- QR wallets and instant payments from other countries are recognized and validated, but don't accept a registered recipient.
Expected amount and session recipient
If your checkout knows the order's amount and key, pass them to the SDK with
Proteside.expectPayment():
// Before showing the order's Pix code
Proteside.expectPayment({ method: 'pix', amount: '19,90', key: 'pix@mystore.com' })amountenablesAMOUNT_TAMPERED: if the code's amount differs from the expected one, the SDK alerts. It accepts19.9,'19,90'or'R$ 19,90', compared to two decimal places.keyadds a trusted recipient for this page only. Only the hash is kept in memory.
Call expectPayment before the code appears on the page and only after the SDK has finished starting up. See
how to wait for the SDK.
Payment methods
In Payment protection → Payment methods you check the methods your store offers. In the browser, this list only
defines what shows up in getStatus().paymentMethodsMonitored and when the SDK stops looking for codes.
Unchecking a method doesn't turn detection off
The screen says unchecking methods reduces noise and that, with no method checked, verification will be disabled. In the code, the SDK recognizes and validates every method, checked or not, and an empty list is treated as Pix and card.
Payment pages
Payment pages (URL patterns, with mode and methods per page) are registered through the API, not the dashboard. They're used to count payment page pageviews in your plan usage and for compliance reports.
The mode and methods set per page don't reach the SDK. The SDK behaves the same on every page where the snippet is installed, using the store's mode and methods.
In the dashboard
The Payment Integrity page shows one card per method with the status NOT CONFIGURED, WAITING (no payment observed yet), INTACT or TAMPERED, plus the recipient status. See Payment integrity.