אחרי הרכישה, השלב הבא במשפך הוא תחילת הצ'קאאוט. אירוע begin_checkout מראה מי הגיע לצ'קאאוט אבל לא סיים, בדיוק המקום שבו הרבה עסקאות אובדות. נוסיף אותו ל-WooCommerce עם קוד אמיתי להעתקה, והסבר בעברית לכל שלב.
woocommerce_before_checkout_form, בונה ב-PHP את אירוע begin_checkout מתוך WC()->cart (value דרך get_totals, קופון, item_variant ו-event_id), ודוחף אותו ל-dataLayer. הנתונים מוכנים בשרת, אבל האירוע עצמו נשלח מהדפדפן. ל-Checkout קלאסי בלבד.רוב העגלות שננטשות אף פעם לא מגיעות לשלב הרכישה. אירוע begin_checkout מאפשר לראות כמה משתמשים התחילו את תהליך התשלום, לחשב את שיעור הנטישה בין הצ'קאאוט לרכישה, ולבנות קהלי רימרקטינג של משתמשים שהיו קרובים מאוד לקנייה. בלי המדידה הזו רואים רק מי קנה, לא מי כמעט קנה.
זו לא באמת מדידה בצד השרת. הקוד בונה את הנתונים ב-PHP בזמן טעינת עמוד הצ'קאאוט, אבל האירוע עצמו נשלח מהדפדפן (דחיפה ל-dataLayer). מדידה server-side אמיתית שולחת את האירוע שרת-לשרת בלי תלות בדפדפן (Measurement Protocol, Server GTM, CAPI), ולזה נגיע במדריכים הבאים.
woocommerce_before_checkout_form אינו מופעל, והקוד לא ירוץ. ל-Blocks נדרש מימוש שונה.הקוד נתלה על woocommerce_before_checkout_form, קורא את תוכן העגלה דרך WC()->cart, ובונה את האירוע. הוא כולל שומרים דפנסיביים (function_exists('WC'), is_admin, is_checkout), חישוב value דרך WooCommerce, ו-event_id יציב לשימוש עתידי במטא:
add_action( 'woocommerce_before_checkout_form', 'doffice_ga4_begin_checkout', 20 );
/**
* Assembles a GA4 "begin_checkout" event in PHP at checkout page load,
* then prints an inline script that pushes it to the dataLayer.
* The DATA is built server-side, but the EVENT itself fires in the browser.
* This guide targets the CLASSIC checkout only (not Cart & Checkout Blocks).
*/
function doffice_ga4_begin_checkout() {
// Bail if WooCommerce isn't active, or we're in the admin.
if ( ! function_exists( 'WC' ) || is_admin() ) {
return;
}
// Run only on the front-end checkout page.
if ( ! function_exists( 'is_checkout' ) || ! is_checkout() ) {
return;
}
if ( ! WC()->cart || WC()->cart->is_empty() ) {
return;
}
$items = array();
foreach ( WC()->cart->get_cart() as $cart_item ) {
$product = $cart_item['data'];
$product_id = $cart_item['product_id'];
$qty = max( 1, (int) $cart_item['quantity'] );
$categories = wp_get_post_terms( $product_id, 'product_cat', array( 'fields' => 'names' ) );
// line_total is the price AFTER discounts (what the customer actually pays).
$line_total = (float) $cart_item['line_total'];
$item = array(
'item_id' => $product->get_sku() ? $product->get_sku() : (string) $product_id,
'item_name' => $product->get_name(),
'item_category' => ! empty( $categories ) ? $categories[0] : '',
// GA4 expects the PER-UNIT price (before multiplying by quantity).
'price' => round( $line_total / $qty, 2 ),
'quantity' => $qty,
);
// item_variant is sent ONLY for variable products.
if ( $product->is_type( 'variation' ) ) {
$item['item_variant'] = wc_get_formatted_variation( $product, true );
}
// item_brand (OPTIONAL). "product_brand" is NOT built into WooCommerce; it
// comes from a brands plugin (WooCommerce Brands / Perfect Brands / YITH...).
// Change the taxonomy name below if your store uses a different one.
$brands = wp_get_post_terms( $product_id, 'product_brand', array( 'fields' => 'names' ) );
if ( ! is_wp_error( $brands ) && ! empty( $brands ) ) {
$item['item_brand'] = $brands[0];
}
$items[] = $item;
}
// Let WooCommerce compute the total. It already handles tax, shipping, fees,
// coupons, compound tax and currency switchers, so we don't add them by hand.
$totals = WC()->cart->get_totals();
$ecommerce = array(
'currency' => get_woocommerce_currency(), // store currency, ISO 4217 (e.g. "ILS")
'value' => round( (float) $totals['total'], 2 ),
'items' => $items,
);
// Applied coupon(s), if any.
$coupons = WC()->cart->get_applied_coupons();
if ( ! empty( $coupons ) ) {
$ecommerce['coupon'] = implode( ', ', $coupons );
}
// Stable event_id for later Meta Pixel + CAPI deduplication (GA4 ignores it).
// Tied to the customer + cart state, so it stays stable while the cart is unchanged.
$event_id = 'checkout_' . md5( WC()->session->get_customer_id() . WC()->cart->get_cart_hash() );
$datalayer = array(
'event' => 'begin_checkout',
'event_id' => $event_id,
'ecommerce' => $ecommerce,
);
echo '<script>';
echo 'window.dataLayer = window.dataLayer || [];';
echo 'window.dataLayer.push({ ecommerce: null });';
echo 'window.dataLayer.push(' . wp_json_encode( $datalayer ) . ');';
echo '</script>';
}שימו לב לכמה דיוקים: value מגיע מ-get_totals()['total'], כלומר WooCommerce מחשב את הסכום הסופי (כולל מס, משלוח, עמלות, קופונים ומס מורכב) במקום חישוב ידני שעלול לסטות בשקל; price של כל פריט הוא המחיר ליחידה אחרי הנחות (line_total חלקי הכמות, מעוגל ל-2 ספרות); ו-item_variant נשלח רק עבור מוצרים משתנים, דרך הפונקציה הרשמית wc_get_formatted_variation.
product_brand בקוד היא דוגמה, היא אינה חלק מובנה של WooCommerce אלא מגיעה מתוסף מותגים (WooCommerce Brands, Perfect Brands, YITH ואחרים). אם החנות משתמשת בטקסונומיית מותג אחרת, התאימו את השם בקוד.למה יש event_id? GA4 אינו משתמש ב-event_id, ולכן הוא לא נדרש למדידת begin_checkout. אבל אם בהמשך תחברו את אותו אירוע ל-Meta Pixel ול-Conversions API, כדאי שיהיה מזהה יציב משותף בין שני הערוצים לצורך דדופליקציה. לכן אנחנו מייצרים אותו כבר עכשיו, מבוסס על מזהה הלקוח ומצב העגלה כך שהוא נשאר יציב כל עוד העגלה לא השתנתה. הפרטים המלאים יכוסו במדריך Meta CAPI.
אותה שיטה כמו ברכישה: טריגר Custom Event בשם begin_checkout, ותגית GA4 Event עם אותו שם, Send Ecommerce data מ-Data Layer, מחוברת לטריגר. אם כבר יש משתנה ecommerce ממדריך הרכישה, הוא משמש גם כאן. שומרים ו-Publish.
נכנסים לעמוד הצ'קאאוט, ובקונסול מסננים את האירוע:
window.dataLayer.filter(e => e.event === "begin_checkout")
// Expected on the checkout page:
[
{
event: "begin_checkout",
event_id: "checkout_9f8e7d6c5b4a...",
ecommerce: {
currency: "ILS",
value: 712.40,
coupon: "WELCOME10",
items: [
{ item_id: "SKU-1", item_name: "...", price: 349.90, quantity: 2, item_variant: "Color: Blue, Size: L" }
]
}
}
]ואז מוודאים ב-GA4 DebugView שהאירוע נכנס עם items, value ו-currency תקינים.
get_totals().begin_checkout הוא החוליה האמצעית במשפך המסחר:
add_to_cart יקבל מדריך נפרד ומעמיק, כי הוא האירוע שהכי משתנה בין חנויות: יש עשרות דרכים להוסיף מוצר לעגלה (עמוד קטגוריה, עמוד מוצר, Quick View, AJAX, Checkout Blocks, Elementor ובילדרים אחרים, תבנית מותאמת), וכל אחת דורשת מימוש אחר. הצגה של דרך אחת בלבד הייתה מטעה. בהמשך נוסיף גם view_item, ואת חיבור Meta Pixel + CAPI עם ה-event_id שכבר הכנו כאן.
נחבר את כל אירועי המסחר, מהוספה לעגלה ועד הרכישה, ל-GA4 ולמטא, בהתאמה לחנות שלכם (כולל Checkout Blocks), כדי שתראו בדיוק היכן הלקוחות נופלים.
בואו נדבר