Overview
GA4 Enhanced Ecommerce provides comprehensive tracking of the customer journey from product discovery to purchase completion. This includes:
- Product impressions and clicks
- Add to cart and remove from cart
- Checkout steps and abandonment
- Transaction and revenue tracking
- Refunds (manual implementation)
Prerequisites
- GA4 base code installed (See Setup Guide)
- FTP/File access to OSCommerce files
- Complete site backup before modifications
- Understanding of PHP and OSCommerce file structure
Product Impression Tracking
Track when products are viewed in listings (category pages, search results).
Category Page Impressions
File: product_listing.php or index.php (for category pages)
Add after product query results:
<?php
// After products are loaded into $listing array
if (isset($listing) && is_array($listing)) {
?>
<script>
gtag('event', 'view_item_list', {
'item_list_id': 'category_<?php echo (int)$current_category_id; ?>',
'item_list_name': '<?php echo addslashes($category_name); ?>',
'items': [
<?php
$items = array();
foreach ($listing as $index => $product) {
$items[] = sprintf(
'{
"item_id": "%s",
"item_name": "%s",
"price": %s,
"index": %d,
"quantity": 1
}',
$product['products_id'],
addslashes($product['products_name']),
$product['products_price'],
$index
);
}
echo implode(',', $items);
?>
]
});
</script>
<?php
}
?>
Search Results Impressions
File: advanced_search_result.php
<?php
// After search results loaded
if (isset($listing_query)) {
?>
<script>
gtag('event', 'view_item_list', {
'item_list_id': 'search_results',
'item_list_name': 'Search: <?php echo addslashes($_GET['keywords']); ?>',
'items': [
<?php
$items = array();
while ($listing = tep_db_fetch_array($listing_query)) {
$items[] = sprintf(
'{
"item_id": "%s",
"item_name": "%s",
"price": %s
}',
$listing['products_id'],
addslashes($listing['products_name']),
$listing['products_price']
);
}
echo implode(',', $items);
?>
]
});
</script>
<?php
}
?>
Product Detail View Tracking
File: product_info.php
Add after product information is loaded:
<?php
// After $product_info is loaded
if (tep_not_null($product_info)) {
// Get category name
$categories_query = tep_db_query("SELECT cd.categories_name FROM " . TABLE_CATEGORIES_DESCRIPTION . " cd, " . TABLE_PRODUCTS_TO_CATEGORIES . " p2c WHERE p2c.products_id = '" . (int)$product_info['products_id'] . "' AND p2c.categories_id = cd.categories_id AND cd.language_id = '" . (int)$languages_id . "' LIMIT 1");
$categories = tep_db_fetch_array($categories_query);
$category_name = $categories ? $categories['categories_name'] : '';
// Get manufacturer/brand
$manufacturer_name = isset($product_info['manufacturers_name']) ? $product_info['manufacturers_name'] : '';
?>
<script>
gtag('event', 'view_item', {
'currency': '<?php echo $_SESSION['currency']; ?>',
'value': <?php echo $product_info['products_price']; ?>,
'items': [{
'item_id': '<?php echo $product_info['products_id']; ?>',
'item_name': '<?php echo addslashes($product_info['products_name']); ?>',
'item_category': '<?php echo addslashes($category_name); ?>',
'item_brand': '<?php echo addslashes($manufacturer_name); ?>',
'price': <?php echo $product_info['products_price']; ?>,
'quantity': 1
}]
});
</script>
<?php
}
?>
Add to Cart Tracking
OSCommerce uses different methods for add to cart depending on version and customization.
Method 1: Standard Form Submission
File: product_info.php
Add JavaScript to track form submission:
<!-- Find the add to cart form -->
<form name="cart_quantity" action="shopping_cart.php" method="post"
<!-- form fields -->
<input type="hidden" name="products_id" value="<?php echo $product_info['products_id']; ?>">
<input type="text" name="cart_quantity" value="1" id="cart_qty">
<button type="submit" name="add_to_cart">Add to Cart</button>
</form>
<script>
function trackAddToCart() {
var quantity = parseInt(document.getElementById('cart_qty').value) || 1;
gtag('event', 'add_to_cart', {
'currency': '<?php echo $_SESSION['currency']; ?>',
'value': <?php echo $product_info['products_price']; ?> * quantity,
'items': [{
'item_id': '<?php echo $product_info['products_id']; ?>',
'item_name': '<?php echo addslashes($product_info['products_name']); ?>',
'price': <?php echo $product_info['products_price']; ?>,
'quantity': quantity
}]
});
return true; // Allow form submission to continue
}
</script>
Method 2: AJAX Add to Cart
If your OSCommerce store uses AJAX for cart operations:
File: product_info.php or custom JavaScript file
<script>
// Override default add to cart function
function addToCart(productId, productName, productPrice) {
var quantity = parseInt($('input[name="cart_quantity"]').val()) || 1;
$.ajax({
url: 'shopping_cart.php',
method: 'POST',
data: {
action: 'add_product',
products_id: productId,
cart_quantity: quantity
},
success: function(response) {
// Track successful add to cart
gtag('event', 'add_to_cart', {
'currency': '<?php echo $_SESSION['currency']; ?>',
'value': productPrice * quantity,
'items': [{
'item_id': productId,
'item_name': productName,
'price': productPrice,
'quantity': quantity
}]
});
// Update cart display
updateCartDisplay(response);
}
});
}
</script>
Method 3: Quick Add from Listings
File: product_listing.php or category pages
<!-- For each product in listing -->
<button echo $product['products_id']; ?>', '<?php echo addslashes($product['products_name']); ?>', <?php echo $product['products_price']; ?>)">
Quick Add
</button>
<script>
function quickAddToCart(productId, productName, productPrice) {
// Send to cart
window.location.href = 'shopping_cart.php?action=add_product&products_id=' + productId;
// Track event
gtag('event', 'add_to_cart', {
'currency': '<?php echo $_SESSION['currency']; ?>',
'value': productPrice,
'items': [{
'item_id': productId,
'item_name': productName,
'price': productPrice,
'quantity': 1
}]
});
}
</script>
Remove from Cart Tracking
File: shopping_cart.php
Track cart removals:
<?php
// Detect when item is removed
if (isset($_GET['action']) && $_GET['action'] == 'remove_product' && isset($_GET['products_id'])) {
$products_id = tep_get_prid($_GET['products_id']);
// Get product info before removal
if (isset($_SESSION['cart']->contents[$products_id])) {
$quantity = $_SESSION['cart']->contents[$products_id]['qty'];
// Get product details for tracking
$product_query = tep_db_query("SELECT products_name, products_price FROM " . TABLE_PRODUCTS_DESCRIPTION . " pd, " . TABLE_PRODUCTS . " p WHERE pd.products_id = p.products_id AND pd.products_id = '" . (int)$products_id . "' AND pd.language_id = '" . (int)$languages_id . "'");
$product = tep_db_fetch_array($product_query);
?>
<script>
gtag('event', 'remove_from_cart', {
'currency': '<?php echo $_SESSION['currency']; ?>',
'value': <?php echo $product['products_price'] * $quantity; ?>,
'items': [{
'item_id': '<?php echo $products_id; ?>',
'item_name': '<?php echo addslashes($product['products_name']); ?>',
'price': <?php echo $product['products_price']; ?>,
'quantity': <?php echo $quantity; ?>
}]
});
</script>
<?php
}
}
?>
Cart View Tracking
File: shopping_cart.php
Track when customers view their cart:
<?php
// After cart contents are loaded
if (isset($_SESSION['cart']) && $_SESSION['cart']->count_contents() > 0) {
$cart_items = array();
$cart_value = 0;
$products = $_SESSION['cart']->get_products();
foreach ($products as $product) {
$cart_items[] = sprintf(
'{
"item_id": "%s",
"item_name": "%s",
"price": %s,
"quantity": %d
}',
$product['id'],
addslashes($product['name']),
$product['final_price'],
$product['quantity']
);
$cart_value += $product['final_price'] * $product['quantity'];
}
?>
<script>
gtag('event', 'view_cart', {
'currency': '<?php echo $_SESSION['currency']; ?>',
'value': <?php echo $cart_value; ?>,
'items': [<?php echo implode(',', $cart_items); ?>]
});
</script>
<?php
}
?>
Checkout Initiation Tracking
File: checkout_shipping.php (first checkout page)
<?php
// On checkout page load
if (isset($_SESSION['cart']) && $_SESSION['cart']->count_contents() > 0) {
$cart_items = array();
$cart_value = 0;
$products = $_SESSION['cart']->get_products();
foreach ($products as $product) {
$cart_items[] = sprintf(
'{
"item_id": "%s",
"item_name": "%s",
"price": %s,
"quantity": %d
}',
$product['id'],
addslashes($product['name']),
$product['final_price'],
$product['quantity']
);
$cart_value += $product['final_price'] * $product['quantity'];
}
?>
<script>
gtag('event', 'begin_checkout', {
'currency': '<?php echo $_SESSION['currency']; ?>',
'value': <?php echo $cart_value; ?>,
'items': [<?php echo implode(',', $cart_items); ?>]
});
</script>
<?php
}
?>
Shipping Information Tracking
File: checkout_shipping.php
Track when shipping method is selected:
<script>
// When shipping form submitted
document.querySelector('form[name="checkout_address"]').addEventListener('submit', function() {
var shippingMethod = document.querySelector('input[name="shipping"]:checked');
if (shippingMethod) {
gtag('event', 'add_shipping_info', {
'currency': '<?php echo $_SESSION['currency']; ?>',
'value': <?php echo $cart_value; ?>,
'shipping_tier': shippingMethod.value,
'items': [<?php echo implode(',', $cart_items); ?>]
});
}
});
</script>
Payment Information Tracking
File: checkout_payment.php
Track payment method selection:
<script>
// When payment form submitted
document.querySelector('form[name="checkout_payment"]').addEventListener('submit', function() {
var paymentMethod = document.querySelector('input[name="payment"]:checked');
if (paymentMethod) {
gtag('event', 'add_payment_info', {
'currency': '<?php echo $_SESSION['currency']; ?>',
'value': <?php echo $order_total; ?>,
'payment_type': paymentMethod.value,
'items': [<?php echo implode(',', $cart_items); ?>]
});
}
});
</script>
Purchase Tracking
This is the most critical ecommerce event.
File: checkout_process.php or checkout_success.php
Method 1: Track in checkout_process.php
Add after order is successfully created:
<?php
// After order insertion ($insert_id contains new order_id)
if ($insert_id) {
// Get order details
$order_query = tep_db_query("SELECT * FROM " . TABLE_ORDERS . " WHERE orders_id = '" . (int)$insert_id . "'");
$order = tep_db_fetch_array($order_query);
// Get order products
$products_query = tep_db_query("SELECT * FROM " . TABLE_ORDERS_PRODUCTS . " WHERE orders_id = '" . (int)$insert_id . "'");
$order_items = array();
while ($product = tep_db_fetch_array($products_query)) {
$order_items[] = sprintf(
'{
"item_id": "%s",
"item_name": "%s",
"price": %s,
"quantity": %d
}',
$product['products_id'],
addslashes($product['products_name']),
$product['final_price'],
$product['products_quantity']
);
}
// Get order totals
$totals_query = tep_db_query("SELECT * FROM " . TABLE_ORDERS_TOTAL . " WHERE orders_id = '" . (int)$insert_id . "'");
$tax = 0;
$shipping = 0;
while ($total = tep_db_fetch_array($totals_query)) {
if ($total['class'] == 'ot_tax') $tax = $total['value'];
if ($total['class'] == 'ot_shipping') $shipping = $total['value'];
}
// Store tracking data in session for checkout_success.php
$_SESSION['ga4_purchase'] = array(
'transaction_id' => $insert_id,
'value' => $order['order_total'],
'currency' => $order['currency'],
'tax' => $tax,
'shipping' => $shipping,
'items' => $order_items
);
}
?>
Method 2: Track in checkout_success.php
File: checkout_success.php
<?php
// Retrieve purchase data from session
if (isset($_SESSION['ga4_purchase'])) {
$purchase = $_SESSION['ga4_purchase'];
?>
<script>
gtag('event', 'purchase', {
'transaction_id': '<?php echo $purchase['transaction_id']; ?>',
'value': <?php echo $purchase['value']; ?>,
'currency': '<?php echo $purchase['currency']; ?>',
'tax': <?php echo $purchase['tax']; ?>,
'shipping': <?php echo $purchase['shipping']; ?>,
'items': [<?php echo implode(',', $purchase['items']); ?>]
});
</script>
<?php
// Clear from session to prevent duplicate tracking on refresh
unset($_SESSION['ga4_purchase']);
}
?>
Prevent Duplicate Purchase Events
Add duplicate prevention:
<?php
// Check if this order was already tracked
if (isset($_SESSION['ga4_tracked_orders'])) {
$tracked_orders = $_SESSION['ga4_tracked_orders'];
} else {
$tracked_orders = array();
}
if (!in_array($insert_id, $tracked_orders)) {
// Track purchase
// ... tracking code here ...
// Mark as tracked
$tracked_orders[] = $insert_id;
$_SESSION['ga4_tracked_orders'] = $tracked_orders;
}
?>
Coupon/Discount Tracking
File: checkout_payment.php or shopping_cart.php
<?php
// If coupon is applied
if (isset($_SESSION['cc_id'])) {
$coupon_code = $_SESSION['cc_id'];
$coupon_query = tep_db_query("SELECT coupon_amount FROM " . TABLE_COUPONS . " WHERE coupon_code = '" . tep_db_input($coupon_code) . "'");
$coupon = tep_db_fetch_array($coupon_query);
?>
<script>
gtag('event', 'coupon_applied', {
'coupon': '<?php echo addslashes($coupon_code); ?>',
'discount': <?php echo $coupon['coupon_amount']; ?>
});
</script>
<?php
}
?>
Refund Tracking (Manual)
OSCommerce doesn't have built-in refund processing, so this must be done manually.
File: admin/orders.php or custom admin page
<?php
// When processing a refund
function processRefund($order_id) {
// Get order details
$order_query = tep_db_query("SELECT * FROM " . TABLE_ORDERS . " WHERE orders_id = '" . (int)$order_id . "'");
$order = tep_db_fetch_array($order_query);
// Get order products
$products_query = tep_db_query("SELECT * FROM " . TABLE_ORDERS_PRODUCTS . " WHERE orders_id = '" . (int)$order_id . "'");
$items = array();
while ($product = tep_db_fetch_array($products_query)) {
$items[] = array(
'item_id' => $product['products_id'],
'item_name' => $product['products_name'],
'price' => $product['final_price'],
'quantity' => $product['products_quantity']
);
}
// Send refund via Measurement Protocol API
$payload = array(
'client_id' => $order['customers_id'] ?: 'guest_' . $order_id,
'events' => array(
array(
'name' => 'refund',
'params' => array(
'transaction_id' => $order_id,
'value' => $order['order_total'],
'currency' => $order['currency'],
'items' => $items
)
)
)
);
// Send to GA4 Measurement Protocol
$url = 'https://www.google-analytics.com/mp/collect?measurement_id=G-XXXXXXXXXX&api_secret=YOUR_API_SECRET';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
}
?>
Multi-Currency Support
OSCommerce supports multiple currencies. Track in user's selected currency:
<?php
// Always use the customer's currency
$currency_code = isset($_SESSION['currency']) ? $_SESSION['currency'] : DEFAULT_CURRENCY;
// When tracking events
?>
<script>
gtag('event', 'purchase', {
'currency': '<?php echo $currency_code; ?>',
'value': <?php echo $order_total; ?>,
// ... other parameters
});
</script>
Testing Ecommerce Tracking
1. Enable Debug Mode
gtag('config', 'G-XXXXXXXXXX', {
'debug_mode': true
});
2. Complete Test Purchase
- Add products to cart
- Proceed through checkout
- Complete order with test payment
- Verify each event in GA4 DebugView
3. Check Event Sequence
Expected flow:
view_item_list(category)view_item(product page)add_to_cartview_cartbegin_checkoutadd_shipping_infoadd_payment_infopurchase
4. Verify in GA4 Reports
After 24-48 hours:
- Reports > Monetization > Ecommerce purchases
- Reports > Monetization > Purchase journey
- Reports > Monetization > Product performance
Common Issues
Purchase Events Not Tracking
Problem: Order completes but no purchase event
Solutions:
- Check order_id is generated correctly
- Verify tracking code is in checkout_success.php
- Ensure gtag loads before event fires
- Check for JavaScript errors
- Verify numeric values (not strings)
Duplicate Purchases
Problem: Same order tracked multiple times
Solutions:
- Implement session-based duplicate prevention
- Clear tracking data after first load
- Use cookie to track already-sent orders
Incorrect Revenue
Problem: Revenue doesn't match order total
Solutions:
// Ensure proper numeric formatting
$value = (float)$order['order_total']; // Cast to float
$value = number_format($value, 2, '.', ''); // Format with period decimal
Currency Issues
Problem: Wrong currency reported
Solutions:
// Always verify currency code
$currency = isset($_SESSION['currency']) ? $_SESSION['currency'] : 'USD';
// Ensure 3-letter ISO code (USD, EUR, GBP, etc.)