const PIXEL_ID = "lr_px_...";
const VALIDATE_ONLY = false; // true while testing: events are checked, not recorded
const ENDPOINT = "https://api.lemrock.com/v1/browser/events";
const COOKIE = "lemrock_click";
const LRCLID = /^lr_clk_[A-Za-z0-9_-]{22}$/;
const cents = (money) => {
if (!money || money.amount == null) return undefined;
return Math.round(Number(money.amount) * 100);
};
const item = (variant, quantity) => ({
id: String(variant?.product?.id ?? ""),
name: variant?.product?.title,
quantity,
amount: cents(variant?.price),
currency: variant?.price?.currencyCode,
});
const checkoutData = (checkout) => ({
amount: cents(checkout.subtotalPrice),
currency: checkout.currencyCode,
contents: checkout.lineItems.map((line) => item(line.variant, line.quantity)),
});
async function sha256(text) {
const bytes = new TextEncoder().encode(text);
const hash = await crypto.subtle.digest("SHA-256", bytes);
return Array.from(new Uint8Array(hash), (b) =>
b.toString(16).padStart(2, "0"),
).join("");
}
async function send(eventType, eventId, event, data, user) {
const lrclid = await browser.cookie.get(COOKIE);
if (!lrclid && !user) return;
fetch(ENDPOINT, {
method: "POST",
headers: { "content-type": "application/json" },
keepalive: true,
body: JSON.stringify({
event_type: eventType,
event_id: String(eventId),
event_time_ms: Date.parse(event.timestamp) || Date.now(),
lrclid: lrclid || undefined,
user,
source_url: event.context.document.location.href,
pixel_id: PIXEL_ID,
pixel_version: "shopify-1.0.0",
data: { type: "contents", ...data },
validate_only: VALIDATE_ONLY,
}),
});
}
analytics.subscribe("page_viewed", async (event) => {
const url = new URL(event.context.document.location.href);
const token = (url.searchParams.get("lrclid") || "").trim();
if (LRCLID.test(token)) {
await browser.cookie.set(
`${COOKIE}=${token}; path=/; max-age=${90 * 86400}; SameSite=Lax; Secure`,
);
}
});
analytics.subscribe("product_viewed", (event) => {
const variant = event.data.productVariant;
send("contents_viewed", event.id, event, { contents: [item(variant, 1)] });
});
analytics.subscribe("product_added_to_cart", (event) => {
const line = event.data.cartLine;
send("items_added", event.id, event, {
amount: cents(line.cost.totalAmount),
currency: line.cost.totalAmount.currencyCode,
contents: [item(line.merchandise, line.quantity)],
});
});
analytics.subscribe("checkout_started", (event) => {
send("checkout_started", event.id, event, checkoutData(event.data.checkout));
});
analytics.subscribe("checkout_completed", async (event) => {
const checkout = event.data.checkout;
const email = checkout.email?.trim().toLowerCase();
const user = email ? { email_sha256: await sha256(email) } : undefined;
send(
"order_created",
checkout.order?.id ?? event.id,
event,
checkoutData(checkout),
user,
);
});