Docs/Shopify
Shopify
What you can track on a Shopify store, what Shopify does not let you touch, and how orders get in anyway.
1. The storefront
In your admin: Online Store → Themes → … → Edit code, open layout/theme.liquid and paste the snippet just before </head>.
<script defer src="https://kipstats.com/tracker.js" data-site="kp_xxxxxxxx"></script>That covers every page the theme renders: home, collections, products, cart, blog, search. Route changes in themes that navigate without reloading are picked up automatically.
What Shopify does not let you do: checkout runs on Shopify's own pages, and custom scripts are restricted there. So the funnel you can see stops at the cart — the payment itself is reported by a webhook, below. Kipstats is not a Shopify app, and does not need one.
2. The steps you can see
<script>
function kp(n, d) {
try { if (window.kipstats && window.kipstats.event) window.kipstats.event(n, d || {}) } catch (e) {}
}
document.querySelector('[name="checkout"]')?.addEventListener('click', function () {
kp('checkout_started', {
price: {{ cart.total_price }}, // Shopify gives cents already
currency: '{{ cart.currency.iso_code }}',
items: {{ cart.item_count }}
})
})
</script>Combined with the rage clicks and dead clicks captured automatically, this is usually enough to find where a cart dies — a shipping table nobody can read, a discount field that swallows codes.
3. Orders, through the webhook
In Settings → Notifications → Webhooks, create an Order payment webhook pointing at a small endpoint of yours; that endpoint forwards the order to Kipstats with your ingest key. The key must never sit in the theme, where anyone can read it — that is the whole reason for the extra hop.
export default {
async fetch(request, env) {
const order = await request.json()
await fetch('https://kipstats.com/api/collect/server', {
method: 'POST',
headers: {
Authorization: `Bearer ${env.KIPSTATS_INGEST_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'purchase',
ref: `shopify_${order.id}`,
occurredAt: order.processed_at,
data: {
amount: Math.round(parseFloat(order.total_price) * 100),
currency: order.currency,
items: order.line_items?.length ?? 0,
},
}),
})
return new Response('ok')
},
}refis the Shopify order ID: a webhook Shopify retries records the sale once.- Amounts are converted to the smallest unit; Shopify sends a decimal string.
- Verify Shopify's HMAC header in production — the snippet above is deliberately minimal.
4. Check it
- Open your shop in a normal browser: the visit appears in Live activity.
- Place one real order: a
purchaseevent shows up in Events with the right amount. - Read tracking revenue for currencies, subscriptions and the refund limitation.