מדריך מעשי עם קוד אמיתי להעתקה, והסבר בעברית לכל שלב. נלמד לדחוף אירוע רכישה (purchase) מ-WooCommerce ל-dataLayer, לחבר אותו ב-GTM, ולאמת שהוא עובד, אחרי שנוודא שהאתר בכלל מתאים לזה.
purchase ל-dataLayer דרך ה-hook woocommerce_thankyou (עמוד אישור ההזמנה). GTM קורא אותו ושולח ל-GA4. ה-dataLayer הוא שכבת נתונים משותפת לכל מערכות המדידה. הקוד תואם HPOS, מונע ספירה כפולה, כולל event_id ניטרלי, ומהווה את שכבת ה-client-side שעליה בונים server-side ו-CAPI.קל להעתיק קוד ולהניח שהכול עובד, אבל אתר שנראה תקין לא בהכרח מודד נכון. לפני שנוגעים בקוד, כדאי לוודא שהבסיס בכלל קיים. שלוש שאלות שחובה לבדוק:
window.dataLayer ורואים שהאירוע קיים.purchase מופיע ב-dataLayer.אפשר לתהות למה לא לשלוח את הרכישה ישר ל-GA4. הסיבה: ברגע שהמידע נמצא ב-dataLayer, אפשר להשתמש בו עבור כל מערכת מדידה, בלי לכתוב את הקוד מחדש:
ה-dataLayer הוא שכבת הנתונים המשותפת, ולא תלוי בפלטפורמת מדידה אחת. זו בדיוק הסיבה שמתחילים ממנו.
יש היום המון תוספים, ותוסף טוב שמוגדר נכון יכול לחסוך זמן. חשוב רק להבין מה הוא עושה. הקוד במדריך הזה נותן dataLayer נקי במבנה שאתם שולטים בו, שליטה מלאה בלי קוד מיותר, קל יותר לדבג, וקל יותר לעבור ל-server-side בהמשך.
שתי דרכים בטוחות, בחרו אחת:
functions.php של תבנית הבת.woocommerce_thankyou כי הוא פשוט ומתאים לרוב החנויות. אבל הוא מסמן שהלקוח הגיע לעמוד אישור ההזמנה, וזה לא בדיוק כמו "התשלום אושר". Payment Success ≠ woocommerce_thankyou. כשנדרשת מדידה מלאה, מודדים את אישור התשלום דרך Webhook או Server-side, שאינם תלויים בכך שהלקוח יחזור. כאן מתחילים מהבסיס.הקוד נתלה על ה-hook, בונה את אובייקט ה-purchase בפורמט GA4, כולל מניעת כפילות ברענון, שמירה על ריצה רק בעמוד אישור ההזמנה, ו-event_id ניטרלי מבוסס-הזמנה (לא קשור לסוג האירוע, כדי שישרת בעתיד גם refund/renewal, ולדדופליקציה של Pixel ו-CAPI).
add_action( 'woocommerce_thankyou', 'doffice_ga4_purchase_datalayer', 20, 1 );
/**
* Pushes a GA4 "purchase" event to the dataLayer on the Order Received page.
* Note: this fires when the customer REACHES the Order Received page,
* which is not always the same as "payment confirmed" (see the guide).
*/
function doffice_ga4_purchase_datalayer( $order_id ) {
if ( ! $order_id ) {
return;
}
// Extra guard: run only on the Order Received page, in case the
// hook is ever triggered in another context.
if ( function_exists( 'is_order_received_page' ) && ! is_order_received_page() ) {
return;
}
$order = wc_get_order( $order_id );
if ( ! $order ) {
return;
}
// Prevent a second push if the customer refreshes the page.
// This is ONE simple option; larger setups may instead rely on GA4's
// transaction_id deduplication, or keep this state outside the order.
if ( 'yes' === $order->get_meta( '_doffice_ga4_tracked' ) ) {
return;
}
$order->update_meta_data( '_doffice_ga4_tracked', 'yes' );
$order->save();
// Build the items array in GA4 format.
$items = array();
foreach ( $order->get_items() as $item ) {
$product = $item->get_product();
$product_id = $item->get_product_id();
$categories = wp_get_post_terms( $product_id, 'product_cat', array( 'fields' => 'names' ) );
$items[] = array(
'item_id' => ( $product && $product->get_sku() ) ? $product->get_sku() : (string) $product_id,
'item_name' => $item->get_name(),
'item_category' => ! empty( $categories ) ? $categories[0] : '',
'price' => $product ? (float) $product->get_price() : 0,
'quantity' => (int) $item->get_quantity(),
);
}
$datalayer = array(
'event' => 'purchase',
// Neutral, order-based ID (NOT tied to the event type), so the same
// order can back future refund / renewal events too. Reused later
// for Meta Pixel + CAPI deduplication.
'event_id' => 'order_' . $order->get_id(),
'ecommerce' => array(
'transaction_id' => $order->get_order_number(),
'value' => (float) $order->get_total(),
'tax' => (float) $order->get_total_tax(),
'shipping' => (float) $order->get_shipping_total(),
'currency' => $order->get_currency(),
'items' => $items,
),
);
// Reset the previous ecommerce object, then push the purchase.
echo '<script>';
echo 'window.dataLayer = window.dataLayer || [];';
echo 'window.dataLayer.push({ ecommerce: null });';
echo 'window.dataLayer.push(' . wp_json_encode( $datalayer ) . ');';
echo '</script>';
}תאימות HPOS: הקוד משתמש ב-API הרשמי של WooCommerce (wc_get_order() ומתודות האובייקט), בלי גישה ישירה לטבלאות או ל-post meta, ולכן תואם גם לאחסון ההזמנות החדש (HPOS).
למה מטא-שדה למניעת כפילות? רק כדי שהאירוע לא יירה שוב ברענון עמוד. זו אפשרות אחת ופשוטה, במערכות גדולות אפשר להישען על דדופליקציית transaction_id של GA4, או לשמור את המצב מחוץ להזמנה.
למה מאפסים את ecommerce ל-null? לפני כל אירוע Ecommerce ב-GA4 מאפסים את האובייקט, כדי שנתונים מאירוע קודם לא "ייגררו" לאירוע הבא. זה עיקרון, לא רק שורת קוד.
עכשיו מגדירים ב-GTM שיקרא את האירוע וישלח אותו ל-GA4 (הכל בממשק, בלי קוד):
ecommerce.purchase.purchase, מסמנים Send Ecommerce data עם Source = Data Layer, ומחברים לטריגר.שומרים ולוחצים Submit / Publish.
איך יודעים שזה באמת עובד? שלוש דרכים: GTM Preview (משלימים רכישת בדיקה ובודקים שהאירוע נורה), GA4 DebugView (Admin ← DebugView), ו-Console. בקונסול, במקום להסתכל על כל ה-dataLayer, סננו רק את אירוע הרכישה:
window.dataLayer.filter(e => e.event === "purchase")
// Expected output on the Order Received page:
[
{
event: "purchase",
event_id: "order_1234",
ecommerce: {
transaction_id: "1234",
value: 349.9,
currency: "ILS",
items: [
{ item_id: "SKU-1", item_name: "...", price: 349.9, quantity: 1 }
]
}
}
]האירוע לא מופיע? עברו על הרשימה:
האם הקוד מכביד על האתר? לא. הוא רץ פעם אחת בלבד, אחרי השלמת ההזמנה, ומוסיף רק Script קטן ל-HTML של עמוד אחד. אין לו שום השפעה על מהירות הגלישה הרגילה.
המדריך הזה הוא רק השלב הראשון בשרשרת מדידה מלאה:
כרגע סיימנו עד GA4 (client-side). הקוד רץ בדפדפן, ולכן חשוף לחוסמי פרסום, iOS ומחיקת קוקיז, וגם תלוי בכך שהלקוח יחזור לעמוד אישור ההזמנה. במדריכים הבאים נעבור לאירועים שמבוססים על אישור התשלום ועל תהליכים בצד השרת (Server GTM, CAPI), שאינם תלויים בחזרת הלקוח. וזה גם ההבדל המהותי בין מדידה בצד הדפדפן (client-side) למדידה בצד השרת (server-side). הרחבנו על התפיסה במדריך המדידה server-side ובמדריך CAPI.
אחרי ה-purchase, מרחיבים באותה שיטה. כל אחד מאלה יקבל מדריך נפרד:
event_id שכבר הכנו, לדדופליקציה.נחבר את WooCommerce ל-GTM, ל-GA4 ולמטא, עם דדופליקציה ומדידה בצד השרת, כך שהנתונים יהיו מלאים ומדויקים גם כשהלקוח לא חוזר לאתר, ומערכות הפרסום ילמדו ממכירות אמיתיות.
בואו נדבר