The purchase confirmation page, the Thank You page a customer lands on after an order goes through, is the single most analytically important page on a retail site. It is where the purchase event fires and where Revenue, Orders, and Units are captured, which means it is where the numbers the business cares about most are born. Getting it right is non-negotiable, and getting it slightly wrong corrupts the headline figures everyone reports on. This article covers the full set of variables that belong on this page, the required, the recommended, and the optional, and shows how the same pattern adapts to conversion funnels that have no physical products at all.
When and Where the Purchase Event Fires
Set the purchase event only on the final confirmation page, after the order has been finalised. It must never fire on the checkout page or the review page, both of which use scCheckout instead. The minimal hit pairs the event with the products string.
s.events = "purchase";s.products = "Category;Product;Qty;TotalPrice";
That single combination drives three automatic metrics. Orders increments by one for every hit containing purchase. Units sums the quantity fields across all line items. Revenue sums the total-price fields across all line items. You never set these metrics by hand; they come entirely from the pairing of the purchase event with s.products. As a convenience, you can include quantity and price fields on non-purchase event pages like prodView and scAdd too, because Adobe simply ignores those fields on anything other than a purchase hit.
Multiple Products in One Order
When a customer buys several products, every line item goes into a single s.products string, separated by commas.
s.events = "purchase";s.products = ";100240;1;1849.99,;100233;1;650";
Here one purchase event covers both products. The first entry has no category, a product SKU of 100240, a quantity of one, and a price of 1849.99, and the comma then begins a second product, SKU 100233 at 650. One rule governs the price fields absolutely: always use a period as the decimal separator, regardless of locale. A value like 1849.99 is correct, while 1849,99 breaks parsing because the comma is read as a product separator, and a thousands separator like 1,849.99 breaks it the same way. The comma and semicolon are structural delimiters, so they can never appear inside a numeric value.
The Variables That Belong on a Purchase
When the purchase event fires, two variables are required and several others are worth setting. The required pair is s.events, which must contain purchase, and s.products, which carries the product details, quantity, and price. Highly recommended is s.purchaseID, which deduplicates the hit. Optionally you may add s.state and s.zip for visitor profile reporting, and s.transactionID to bridge to offline follow-up data. The next sections take the important ones in turn.
Deduplicating with s.purchaseID
If a visitor refreshes the confirmation page, bookmarks it, or hits the back button, the purchase event fires again and creates a duplicate order. s.purchaseID is what prevents that.
s.purchaseID = "11223344";s.purchaseID = "a8g784hjqlmnp3";
When it is populated, Adobe records the products only once per unique purchaseID value, discards subsequent hits carrying the same ID, and deduplicates all the conversion data associated with that hit, including campaigns and segments. Both numeric and alphanumeric order IDs are valid. The constraints to respect are a maximum of 20 bytes and standard ASCII characters only, and the right source is almost always the real order ID from the data layer or back-end system, because reusing the genuine ID avoids maintaining a second parallel system. If your order ID runs longer than 20 bytes, truncate it in a way that preserves uniqueness. Although s.purchaseID is technically optional, Adobe strongly recommends setting it every single time the purchase event fires, and you should treat that as a hard rule.
Stitching Offline Data with s.transactionID
Some conversions begin online but complete offline. A visitor submits a loan application on the site, but approval happens days later through a manual process, and s.transactionID is what bridges the online hit to that offline outcome. The flow has three stages: the visitor submits the application and the transaction ID is set, so Analytics records the hit with all its campaign and segment context; the loan is approved later and the offline approval data is uploaded to Analytics through Data Sources; and Data Sources matches on the transaction ID to merge the offline result back into the original online hit.
s.transactionID = "1234abcd";s.transactionID = s.purchaseID; // common pattern: reuse the order ID
Reusing the purchase ID as the transaction ID is a common and sensible pattern, since it avoids juggling two identifiers. One setup step is mandatory: Transaction ID Storage must be enabled before the variable does anything, which you check under Admin, then Data Sources, then Manage, and enable if needed under the report suite’s general account settings. The variable allows up to 100 bytes of alphanumeric ASCII, and in Launch it can be mapped directly from a data element in the Analytics extension.
Location with s.state and s.zip
These two are conversion variables, but they behave unlike ordinary eVars because they expire immediately, at page-view scope. Only events fired on the same page as s.state or s.zip are associated with those values. The practical consequence is important: if you want to compare conversion rates by state across the whole checkout, you must populate s.state on every checkout event page, not just the confirmation page.
s.state = "California";s.state = "Surrey,UK";s.zip = "84097";s.zip = "TW20 9AW";
Both accept freeform text, so US states, non-US regions, and postal codes in any national format all work, and both are capped at 50 bytes of alphanumeric ASCII, with multi-byte characters accepted when the character set is configured. Adobe recommends sourcing the postal code from the billing address, using the shipping address only when there is a single shipping address per order. Customer Care can also auto-populate s.zip from Adobe GeoSegmentation data, either always or only when the field is empty, and in Launch both variables are available as data-element-mapped fields in the Analytics extension.
Product-Level Amounts with the Incrementor
The fifth field of s.products is the incrementor, which assigns a custom numeric or currency value to a specific product for a custom event. Unlike revenue, which comes from field four and applies to the line item’s total, incrementor values are product-scoped and flow through custom events. Typical uses are tax per product, postage per product, or recording cart revenue so you can later calculate the revenue lost to abandonment.
s.events = "purchase,event5";s.products = ";Product;1;9.99;event5=1.86";
Here event5 carries 1.86 for this product, perhaps the tax on it, and crucially the event also appears in s.events. When one product needs values for several custom events, separate the incrementor entries with a pipe character.
s.events = "purchase,event5,event6";s.products = ";Product;1;9.99;event5=1.86|event6=2.50";
Two rules make or break this. The event type must be set to Numeric or Currency in the Admin console, never Counter, because counter events only tally occurrences and cannot hold a custom amount, so a value assigned to a counter event is silently ignored. And any event used in field five must also be declared in s.events, or Adobe disregards it entirely.
Repurposing the Pattern for Non-Retail Funnels
The same funnel logic works even when there is nothing physical to sell, because s.products can carry meaning for any conversion. You identify the conversion actions, assign events to each, and tag each page accordingly, exactly as in a retail build.
For a travel site, the hotel name occupies the product field, nights booked the quantity field, and the booking cost the price field.
// View destination pages.events = "prodView";s.products = ";Hotel Name";// Enter travel datess.events = "scAdd";s.products = ";Hotel Name";// Review orders.events = "scCheckout";s.products = ";Hotel Name";// Confirm bookings.events = "purchase";s.products = ";Hotel Name;2;249.95";
Viewing the hotel maps to a product view, entering dates behaves like adding to a cart, reviewing is checkout initiation, and the confirmation records two nights at a total of 249.95. For financial services, you mix predefined commerce events with custom ones and carry no revenue on the form pages.
// View banking productss.events = "prodView";s.products = ";Form Name";// Complete the online forms.events = "event1";s.products = ";Form Name";// Success confirmations.events = "event2";s.products = ";Form Name";
The form name plays the role of the product across all three steps, with custom events marking submission and successful confirmation. The pattern is identical; only the meaning of each field changes.
Rules Worth Keeping in View
A few principles apply to every products string. If a field is unused, keep its semicolon as a placeholder and never remove it, since positions are fixed. Strip commas, semicolons, and special characters out of category and product values, because those characters are the field delimiters. The category field has been deprecated, so omit it in new implementations. Route tax, postage, and other per-product amounts through the incrementor in field five. Always set s.purchaseID with the purchase event. Use s.transactionID when offline data will be merged in later, and enable Transaction ID Storage in Admin before relying on it.
The Pitfalls That Corrupt Orders
The errors here are costly because they distort the most-watched numbers on the site. Using a comma as a decimal separator makes Adobe read the value as a second product, so 1849,99 produces a nameless product with 99 in the wrong field. Not setting s.purchaseID means every revisit to the confirmation URL counts as a fresh order. Setting s.purchaseID to a non-unique value, such as always sending “1”, does the opposite and deduplicates every order down to the first one ever recorded. Exceeding the 20-byte limit on the purchase ID truncates it and can collide different orders into one. Using s.transactionID without enabling Transaction ID Storage populates the variable but stores nothing to match against, so later Data Sources uploads find no records. Forgetting that s.state and s.zip expire immediately means they only attach to events on the confirmation page unless you set them on every checkout step. Using a Counter event type with the incrementor field silently discards the value. And stacking incrementors without the pipe, writing event5=1.86event6=2.50, leaves Adobe unable to parse either value, where the correct form joins them with a pipe.
Conclusion
The confirmation page is where revenue is recorded, so precision matters more here than anywhere else. Fire the purchaseevent only on the final page, pair it with a correctly formatted s.products string using periods for decimals, and let Adobe compute Orders, Units, and Revenue automatically. Always set s.purchaseID from the real order ID to stop refreshes and back-buttons from inflating sales, reach for s.transactionID and Data Sources when offline fulfilment needs to be stitched in, and remember that s.state and s.zip expire on the page so they belong on every checkout step. Use the incrementor field for per-product tax and postage with Numeric or Currency events, and lean on the same funnel pattern for travel, finance, or any non-retail conversion. Treat every semicolon as load-bearing, and the most important page on the site will report exactly what the business earned.
[…] Implementing the Purchase Confirmation Page in Adobe Analytics […]