E-commerce events
AdPix accepts the GA4 commerce event set and expands each event's items array into one row per line item. The pre-purchase funnel belongs in the browser; a purchase the payment gateway confirmed belongs on your server.
Two collection paths, one report#
E-commerce in AdPix is fed from two places, and both land in the same report:
| Subject | Browser | Server |
|---|---|---|
| Viewing a product, adding to cart, starting checkout | Yes | Not needed |
| A purchase the payment gateway confirmed | Incomplete and fragile | Yes |
| Renewals, recurring invoices, refunds | No | Yes |
| An order an operator enters by hand | No | Yes |
The reason for the split is simple. Up to the moment of payment, the event happens on the page the customer has open, and the browser is the best place to see it. Payment itself happens off the page — the customer leaves for the gateway, may never come back, and an ad blocker may swallow the beacon. The truth about the purchase lives on your server, so send it from there.
What the browser command looks like#
The second argument is the event name and the third is the e-commerce object. The command does two separate things:
- It emits a full event under that name with
value,currencyandtransaction_idas event parameters — so the Events report and key-event revenue see it immediately. - It attaches the
itemsarray as a separate context alongside the event. Collection unpacks that context and writes one row per line item. The products table and AOV are built from those rows.
Like page and track, ecommerce is bound to the statistics consent category: until the visitor has accepted it, the command sends nothing and returns quietly.
Event names#
The same set GA4 uses is accepted. Send the name with exactly this spelling — Purchase and purchase are two different events.
| Event name | What reads it |
|---|---|
view_item |
step 1 of the commerce funnel |
add_to_cart |
step 2 of the funnel |
begin_checkout |
step 3 of the funnel |
purchase |
revenue, orders, AOV, units, top products, step 4 of the funnel |
refund |
refunds and net revenue |
view_item_list |
the Events report and Explore only |
remove_from_cart |
the Events report and Explore only |
add_payment_info |
the Events report and Explore only |
The four funnel steps are counted in distinct users, not events. Money and products are read only from purchase and refund; the other names still write line items, but no money report looks at them.
The fields on a line item#
| Field | Type | Note |
|---|---|---|
item_id |
string | the product key in the top-products table; rows are grouped by it |
item_name |
string | display name. Empty, and the table falls back to item_id. |
price |
number | unit price, in that event's currency |
quantity |
number | absent or zero is treated as 1 |
category |
string | the Category column in the products table |
brand |
string | stored |
variant |
string | stored |
discount |
number | subtracted from that line's revenue |
Line revenue is price × quantity − discount, and that is the figure summed into the products table's revenue column. Up to 200 items are accepted on one event.
coupon, shipping, tax and affiliation at order level, and coupon and index per item, are accepted and never cause a rejection — but no report reads them today. If you need one of them in a report, send it as an ordinary event parameter instead and build a custom dimension from it.
Three things that leave the report empty#
Order count, average order value and items per order are all computed by grouping on transaction_id. Omit it and every purchase in the range falls into a single group with an empty ID: orders becomes 1 and AOV equals total revenue. The number is not merely imprecise — it is meaningless. On the server path, order_id plays this role.
- No
items, no rows. A purchase event with no items array is visible in the Events report and in key-event revenue, but as far as the e-commerce report is concerned it does not exist. If you do not have the line detail, send at least one item carrying the order ID and the total. - No
transaction_id, no order — as above. - No
currencyand amounts pile up under "unknown". Send the code as three uppercase letters, such asUSDorIRR. The server path rejects an invalid code with a 422.
Send the purchase from your server#
The server-to-server path accepts the same items array and uses order_id as the transaction identity:
This path gives you three things the browser cannot:
- Re-sending does not create a second order. You choose the
event_id, and it must always identify that same order. A second send with the same ID and the same body returns the first response and writes no new event. - The order's attribution is frozen. The channel and campaign that produced the sale are written onto the order once, and the customer's later behaviour never rewrites them.
- Ad blockers and abandoned tabs are irrelevant.
Server keys, headers and error codes are covered in Server-side tracking.
If the same purchase goes out from the browser and from your server, two independent rows are written and revenue doubles. The two paths use different ID spaces and AdPix deliberately does not collapse them. Pick one — and if you pick the server, make sure a Create-event rule on the payment-success page is not quietly minting a second purchase at the same time.
Repeat purchases in the browser#
If you have no choice but to send the purchase from the browser, always include transaction_id. An event named exactly purchase that carries a transaction ID is given a stable ID derived from that transaction, so a reload of the success page, a back-navigation and a tracker retry all collapse onto one purchase.
That guard only applies to the name purchase. If your purchase event has a different name — because an event rule created it or renamed it — you need the approach described in Create and Modify event rules.
Currency#
Amounts are stored in the currency you send, with no conversion. The e-commerce report renders them with a $ sign; that sign does not change your unit, and the number next to it is the amount in your own currency.
The practical consequence: a property selling in two currencies adds them together in its revenue total. Create a separate property per currency.
Where each number comes from#
| Figure | Source |
|---|---|
| Revenue, orders, AOV, units | line items of purchase events, grouped by transaction_id |
| Refunds and net revenue | the same, for refund |
| Top products | line revenue summed by item_id |
| Commerce funnel | distinct users of the four funnel events in events_local |
| Key-event revenue | the value parameter on the event itself, not the line items |
Those last two sources are deliberately kept apart: key-event revenue comes from value and knows nothing about the items array. So a purchase sent without items costs you the e-commerce report, not the revenue.
Reading the report itself is covered in The E-commerce report.
Frequently asked questions#
Should I send the purchase from the browser or from my server?
The pre-purchase funnel from the browser, the purchase itself from the server. A browser purchase is exposed to ad blockers, to the customer closing the tab before the gateway redirects back, and to a bad connection; the same purchase sent from your server carries a stable ID, and re-sending it does not create a second order.
Why is the e-commerce report empty when I can see `purchase` in the Events report?
Because the e-commerce report is built on line-item rows. A purchase event with no items array writes no line items, so revenue, orders and top products never see it. The event's value still counts toward key-event revenue.
Why is every purchase counted as one order?
Because transaction_id is empty. Orders are counted by grouping on that field, so if every purchase carries an empty ID they all fall into one group and AOV becomes meaningless. On the server path, order_id plays the same role.
Does AdPix convert between currencies?
No. The amount is stored in the currency you send and no rate is applied. If one property sells in two currencies, its revenue total is a meaningless number; create a separate property per currency.
Thanks — your feedback helps us improve the docs.