We’re upgrading our email infrastructure — for immediate response, email andrewjgaber@gmail.com meanwhile.
Skip to main content
Part of Digital Empire
Product analytics / CDP

PostHog posthog.capture('purchase') + $set at Shopify checkout

PostHog · Web Pixels API replacement · confidence: high

Dies 2026-08-26

Paste-ready snippet
// Wave4-Hotel7 (2026-08-20): PostHog purchase event + user identify
// replacement. PostHog is the open-source product-analytics + session-
// replay + feature-flags platform PixelProof itself runs on (see
// lib/pixelproof/posthog.ts) and is one of the fastest-growing DTC
// analytics installs -- especially for stores that want session replay
// without paying FullStory / LogRocket pricing. The legacy install pastes
// posthog.init() + posthog.identify() + posthog.capture('purchase', {...})
// into Shopify Additional Scripts. All three stop firing 2026-08-26.
//
// Two variants: pick ONE.
//
// ============================================================
// VARIANT A -- BROWSER-SIDE (Shopify Web Pixels sandbox).
// Loads the PostHog JS snippet inside the pixel sandbox. Uses the
// project's Public API Key (POSTHOG_PROJECT_API_KEY) -- documented
// browser-safe (write-only, per PostHog docs). If you self-host PostHog,
// replace https://us.i.posthog.com with your instance host.
// ============================================================
(function loadPosthog() {
  if (window.posthog && window.posthog.__loaded) return;
  !function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="init capture register register_once register_for_session unregister unregister_for_session getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey getNextSurveyStep identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty createPersonProfile opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing debug getPageViewId".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
  window.posthog.init('POSTHOG_PROJECT_API_KEY', { api_host: 'https://us.i.posthog.com', capture_pageview: false });
})();

analytics.subscribe('checkout_completed', (event) => {
  const { checkout } = event.data;
  if (!checkout || !checkout.email || !window.posthog) return;

  const distinctId = checkout.order
    ? String(checkout.order.customerId || checkout.order.id)
    : checkout.email;

  window.posthog.identify(distinctId, {
    email: checkout.email,
    last_purchase_at: new Date().toISOString(),
  });

  window.posthog.capture('purchase', {
    order_id: checkout.order ? String(checkout.order.id) : checkout.token,
    revenue: Number(checkout.totalPrice.amount),
    currency: checkout.totalPrice.currencyCode,
    item_count: (checkout.lineItems || []).length,
    products: (checkout.lineItems || []).map((li) => ({
      product_id: li.variant && li.variant.product ? String(li.variant.product.id) : null,
      sku: li.variant ? li.variant.sku : null,
      name: li.title,
      price: li.variant && li.variant.price ? Number(li.variant.price.amount) : null,
      quantity: li.quantity,
    })),
  });
});

// ============================================================
// VARIANT B -- SERVER-SIDE (PostHog Capture API + Shopify webhook).
// Recommended for revenue-critical events. PostHog's Capture API
// endpoint accepts the same shape from any backend; use a Personal API
// Key or Project API Key (Project key is fine for pure capture).
// Docs: https://posthog.com/docs/api/capture
// ============================================================
//
// // Shopify webhook: orders/paid
// export async function handleShopifyOrderPaidForPosthog(order) {
//   await fetch('https://us.i.posthog.com/capture/', {
//     method: 'POST',
//     headers: { 'Content-Type': 'application/json' },
//     body: JSON.stringify({
//       api_key: process.env.POSTHOG_PROJECT_API_KEY,
//       event: 'purchase',
//       distinct_id: String(order.customer ? order.customer.id : order.id),
//       timestamp: order.created_at,
//       properties: {
//         $insert_id: 'shopify-order-' + order.id,  // dedupe key
//         order_id: String(order.id),
//         revenue: Number(order.total_price),
//         currency: order.currency,
//         item_count: order.line_items.length,
//       },
//     }),
//   });
// }

Legacy pattern detected via: posthog\.capture\(\s*['"]purchase['"]|posthog\.capture\(\s*['"]order[ _]completed['"]|posthog\.identify\(\s*['"]?.+['"]?\s*,

What this does NOT cover

What this does NOT cover: PostHog Feature Flag evaluations (fire independently via posthog.getFeatureFlag), PostHog Surveys, group analytics (B2B account rollups), or PostHog Experiments exposure logging. It also does not merge a pre-purchase anonymous session -- if the buyer was anonymous before checkout, use posthog.alias() at the moment they logged in, not here.

Test-event verification checklist

  • Use `revenue` (lowercase) as the property name -- PostHog's Web Analytics + Revenue insights auto-detect this property name. Naming it `total` or `Revenue` works for querying but does not populate the built-in Revenue trend chart. `$insert_id` on the server-side variant is required for retries/dedupe.
  • Verify a live purchase in PostHog Activity feed (Activity > Live events) within 30 seconds. Confirm the person profile updated with the new distinct_id and email set. If session replays are enabled, the replay for this session should now be linked to the identified user (previously anonymous replays get retroactively re-associated within 5-10 minutes).
  • Duplicate-check: if you already send Shopify orders to PostHog via its Shopify data-warehouse source (in beta as of 2026) OR via a Segment forwarder, do NOT also ship this snippet. PostHog does not dedupe events across sources without $insert_id, so Purchase will double-count. Choose exactly one ingestion path.
  • Verify live against the platform's own test-event tool: https://posthog.com/docs/data/events