Atribu
Integrations

Webpay (Transbank)

Carry the ad click through Webpay Plus checkout so every Chilean card payment attributes to the campaign that produced it.

Webpay (Transbank)

Webpay Plus is Chile's dominant card gateway. Unlike Stripe and MercadoPago it has no metadata field and no webhook, so Atribu cannot read your payments on its own — and without a passthrough every Webpay sale arrives with no ad signal at all.

Two lines of code fix that.


What you get

  • Every authorized Webpay transaction recorded as a payment_received conversion in CLP
  • Revenue attributed to the exact ad click, with no email or phone match required
  • Idempotent by construction — a retried commit, and a later upload of your monthly webpay.cl report, land on the same conversion instead of doubling your revenue

How it works

Transbank echoes exactly two merchant-supplied values back to you when the payment completes: buy_order (max 26 characters) and session_id (max 61). session_id is the carrier — Transbank documents it as "uso interno de comercio", it has no uniqueness requirement to collide with, and it comes back on Transaction.commit(), on Transaction.status(), and as TBK_ID_SESION on the abort and timeout returns.

The full atb1. attribution token used for Stripe and MercadoPago is 80–904 characters, so it cannot ride either field. getAttributionRef() packs the visitor's identity into 48 characters instead, and Atribu recovers the campaign, ad set, ad and click IDs from that visitor's own tracked session.

The reference is base64url — letters, digits, - and _ — all of which are inside the character set Transbank documents as legal for buy_order, so it is safe in either field.


Step 1 — carry the reference into checkout

Where your page starts the payment, read the reference and send it as session_id:

<script>
  // 48 characters. Always keep a fallback: getAttributionRef() returns ""
  // when the visitor's identity cannot be packed, and Webpay needs a value.
  const atribuRef =
    window.atribuTracker.getAttributionRef() || ("sess-" + orderId);
</script>

Then create the transaction with it, using Transbank's SDK or a raw request:

import { WebpayPlus } from "transbank-sdk";

const response = await new WebpayPlus.Transaction().create(
  orderId,     // buy_order — yours, unchanged
  atribuRef,   // session_id — the attribution carrier
  amount,
  `${origin}/checkout/webpay-return`
);
// → redirect the browser to response.url with token_ws

Your buy_order stays entirely yours. If you cannot use session_id, pass 26 to getAttributionRef(26) and put the result in buy_order instead — it fits exactly, but then it consumes the whole field.


Step 2 — report the commit

Your server is what calls Transaction.commit(), so your server is what tells Atribu the sale happened. Forward the response verbatim — Atribu does the field mapping:

import { Atribu } from "@atribu/node";

const atribu = new Atribu({ apiKey: process.env.ATRIBU_API_KEY });

const commit = await new WebpayPlus.Transaction().commit(token_ws);

const result = await atribu.payments.webpay({
  commit,                                   // exactly as Transbank returned it
  customer: { email: buyerEmail },          // optional, if you have it
});

// result.attribution_carrier === "session_id" → the passthrough is wired

…or with plain HTTP:

curl -X POST https://api.atribu.app/api/v1/payments/webpay \
  -H "Authorization: Bearer $ATRIBU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"commit": {"vci":"TSY","amount":18990,"status":"AUTHORIZED",
       "buy_order":"orden-8891","session_id":"atbww0s5oEf2Sb-HN2fVLo-a8Q_4fhPV4qQF-jvv0gij7NvA",
       "card_detail":{"card_number":"6623"},"accounting_date":"0902",
       "transaction_date":"2026-09-02T15:55:33.841Z","authorization_code":"1213",
       "payment_type_code":"VN","response_code":0,"installments_number":0}}'

The endpoint requires the events:write scope.


What Atribu does with it

Webpay fieldbecomes
amountthe conversion value (CLP unless you pass currency)
transaction_datethe event time — what the attribution window is measured against
session_idthe visitor: anonymous_id + session_id
buy_order + authorization_codethe idempotency key
status + response_codewhether this is revenue at all

Everything else on the commit response — vci, payment_type_code, installments_number, the last four card digits — is stored verbatim for later reconciliation.


Verify the passthrough

The response tells you whether the reference actually arrived:

{
  "data": {
    "status": "accepted",
    "attribution_carrier": "session_id",
    "anonymous_id": "anon_c34b39a047f649bf873767d52e8f9af1",
    "conversion": { "matched": true, "revenue_type": "cash" }
  }
}
  • attribution_carrier: null — the reference never made it. Check that Transaction.create() really sent getAttributionRef() as session_id.
  • conversion.matched: false — the payment was stored but no conversion goal claims payment_received, so it will never reach ROAS.

Things worth knowing

Only authorized sales become revenue. A commit whose status is not AUTHORIZED, or whose response_code is not 0, comes back as {"status": "ignored", "reason": "…"} with nothing written. Post every commit you receive; Atribu decides which are sales.

Retries are free. The event is filed under <buy_order>_<authorization_code>, so a repeated commit — or a Transaction.status() poll you forward instead — collapses onto the same conversion.

Your monthly report still works, and will not double-count. The webpay.cl PDF/CSV importer builds that same key from the report's OC and "Código de autorización" columns, so a payment already reported live is recognised as a duplicate.

The return_url query string is not a carrier. Transbank documents nothing about whether parameters appended to it survive, and the aborted-payment return in the integration environment is a POST. Use session_id.

Oneclick Mall has no session_id. Its only round-trip field is buy_order (26 characters, and it must be unique per transaction). Use getAttributionRef(26) there, or keep the atb1. token in your own session keyed on tbk_user and send it as attribution_token.


Sources

On this page