Docs menu · Your own proxy container
Getting Started
Integrations
Server-side GTM
COD Order Protection
Advanced Features
Downloads
Tools & Support
Docs / COD Order Protection / Set up GTM
COD Order Protection
Your own proxy web container
Add COD Order Protection to a web container that sends events to PixelFly without sGTM.
Before you start
- COD Order Protection is on in PixelFly — Step 1.
- Have your API Key (
pf_live_…) ready.
Shortcut worth taking: import our proxy container into a spare workspace. Everything below is already built there, so you can open each piece and copy its settings.
What you are building, in plain terms
Everything here is one of two jobs:
- Hold — when a COD purchase happens, send it to PixelFly instead of your ad platforms.
- Block — stop every purchase tag firing for that order.
There is no third “return” step as there is with sGTM: with no server container to send it back through, the confirmed order leaves PixelFly through Receivers instead.
1. Import the tag template
Templates → Tag Templates → New → ⋮ → Import, choose the file, then Save.
Download pixelfly-cod-protection-web.tpl2. Create the hold tag
Tags → New, tag type PixelFly COD Protection (Web). Fill in:
| Field in the tag | Set it to |
|---|---|
Server hostname / base URL | https://track.pixelfly.io, or your own tracking domain. No path on the end. |
API Key | Your container's API Key (pf_live_…) |
How to read order fields | Auto from dataLayer (Recommended) |
Skip if payment_method is not COD | Ticked |
Worth adding: under Additional fields to store, add a row named ga_session_id holding the GA session ID. It lets GA4 place the confirmed purchase in the same visit the ad brought, rather than as a fresh session days later.
3. Build the COD hold flag
Your trigger needs a way to say “this order is COD and COD is switched on”. Build it once, as variables, and one switch then controls everything.
How to produce cod_hold
It is not a field your store sends — you build it in GTM from three small variables, so one switch controls the whole setup. Create them in this order (Variables → New).
a. Three Data Layer Variables
Type Data Layer Variable, one each, named exactly for the key they read:
| Variable name | Data Layer Variable Name |
|---|---|
dlv - ecommerce.payment_method | ecommerce.payment_method |
dlv - payment_method | payment_method |
dlv - ecommerce.payment_type | ecommerce.payment_type |
b. cJS - PixelFly payment method
Type Custom JavaScript. Different carts use different keys, so this takes whichever one is present:
function() {
return {{dlv - ecommerce.payment_method}} || {{dlv - payment_method}} || {{dlv - ecommerce.payment_type}} || '';
}
c. PixelFly - COD Protection
Type Constant, value off. This is your master switch — set it to on when the rest is built and published. Keeping it off while you work means a half-finished setup never holds a real order.
d. cJS - PixelFly COD hold
Type Custom JavaScript. This is the one the trigger and the cod_hold parameter read:
function() {
if ({{PixelFly - COD Protection}} !== 'on') return 'false';
var key = String({{PixelFly API Key}} || '');
if (key.indexOf('pf_live_') !== 0 || key === 'pf_live_REPLACE_ME') return 'false';
var method = String({{cJS - PixelFly payment method}} || '').toLowerCase();
if (!method) return 'true';
var cod = method === 'cod'
|| method.indexOf('cash_on_delivery') !== -1
|| method.indexOf('cash on delivery') !== -1;
return cod ? 'true' : 'false';
}
It returns the strings 'true' / 'false', not booleans, so trigger conditions compare cleanly. It also refuses to hold while the API Key is still a placeholder, so an unfinished container cannot swallow a real order. An order with no payment method at all is held — an unknown method is more often COD than not.
e. 📌 prevention_reload
Type Custom JavaScript. Our COD trigger also checks this is false, so refreshing the thank-you page does not hold the same order twice:
function(){
return window.performance.getEntriesByType('navigation')
.map(function(nav){ return nav.type })
.includes('reload');
}
Skip this only if your purchase event already fires once per order by some other means.
4. Create the hold trigger
Triggers → New → Custom Event, event name purchase, fires on Some Custom Events with:
| Variable | Operator | Value |
|---|---|---|
cJS - PixelFly COD hold | equals | true |
📌 prevention_reload | equals | false |
Set it as the hold tag’s firing trigger. This same trigger is what you add as an exception everywhere in the next step.
5. Block every purchase tag — the step to be thorough about
Add the trigger from step 3 under Exceptions on each of these:
- ☐ Meta Pixel
- ☐ GA4 purchase
- ☐ Google Ads conversion
- ☐ TikTok Pixel
- ☐ The PixelFly server-side tag
6. Add Receivers in PixelFly
One Receiver per platform the confirmed order should reach: Meta, GA4, TikTok, Google Ads. With no server container there are no server tags to run, so Receivers are the only way out.
Next
Publish, then place one prepaid and one COD test order. On a hand-built setup this test is not optional — a single missed exception in step 5 is invisible until you watch a COD order go through and see which tags fire.