πŸ”„ Tracking Refunds & Returns Server-Side for Clean ROAS Reporting in OpenCart

Standard

Tracking refunds and returns is just as important as tracking purchasesβ€”especially when you’re optimizing ad spend. Without proper refund tracking, your ROAS (Return on Ad Spend) metrics are inflated, leading to poor optimization in Google Ads, Meta, and GA4.


🎯 Why Refund Tracking Matters

Problem Impact
ROAS inflation Over-reports revenue in Google Ads, Meta, GA4
Misleading attribution Ads get credit even when purchase is reversed
Poor bid optimization Smart Bidding algorithms get incorrect feedback
No refund data in GA4 Incomplete funnel analysis & reporting

βœ… Solution: Trigger refund events from the backend and route them through Server-Side GTM to GA4, Google Ads, and Meta CAPI.


🧰 Prerequisites

  • OpenCart store (v3.x or v4.x)
  • Access to the order return system (Admin β†’ Returns)
  • Server-Side GTM Container deployed (gtm.yoursite.com)
  • Google Ads + GA4 + Meta CAPI setup
  • Consent handling logic (optional but recommended)


πŸš€ Step-by-Step Guide


πŸ”Ή Step 1: Detect Refund Trigger in OpenCart Admin

In OpenCart, refunds typically happen when a return is approved.

Modify the file:
admin/controller/sale/return.php

Find where the return status is updated, and hook into it:

if ($this->request->server['REQUEST_METHOD'] == 'POST' && isset($this->request->post['return_status_id'])) {
$return_id = (int)$this->request->get['return_id'];
$return_status_id = (int)$this->request->post['return_status_id'];

// When return is approved (adjust ID as per your config)
if ($return_status_id == APPROVED_STATUS_ID) {
$this->load->model('sale/return');
$return_info = $this->model_sale_return->getReturn($return_id);

$this->load->model('sale/order');
$order_info = $this->model_sale_order->getOrder($return_info['order_id']);
$products = $this->model_sale_order->getOrderProducts($return_info['order_id']);

$items = [];
foreach ($products as $product) {
$items[] = [
'item_id' => $product['model'],
'item_name' => $product['name'],
'quantity' => $product['quantity']
];
}

$refund_event = [
'event_name' => 'refund',
'transaction_id' => $order_info['order_id'],
'currency' => $order_info['currency_code'],
'items' => $items,
'event_id' => uniqid('refund_', true),
'user_data' => [
'email' => hash('sha256', strtolower(trim($order_info['email']))),
'phone' => hash('sha256', preg_replace('/\D/', '', $order_info['telephone']))
]
];

// Send to server-side GTM endpoint
$endpoint = 'https://gtm.yoursite.com/collect';
$ch = curl_init($endpoint);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($refund_event));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);
curl_close($ch);
}
}


πŸ”Ή Step 2: Receive Refund Data in Server-Side GTM

In your Server GTM container:

  1. Open Preview Mode
  2. Trigger a refund from OpenCart
  3. Check the incoming request β€” event_name should be refund


πŸ”Ή Step 3: Create Variables in Server GTM

  • Event Name: {{Event Data β†’ event_name}}
  • Transaction ID: {{Event Data β†’ transaction_id}}
  • Currency: {{Event Data β†’ currency}}
  • Items: {{Event Data β†’ items}}
  • Event ID: {{Event Data β†’ event_id}}
  • user_data.email, user_data.phone


πŸ”Ή Step 4: Send Refund to GA4 via Server Tag

Create a GA4 Event Tag:

  • Tag Type: Google Analytics: GA4
  • Event Name: refund
  • Transaction ID: {{transaction_id}}
  • Items: {{items}}
  • User Properties (optional): email/phone

Trigger: Event Name equals refund


πŸ”Ή Step 5: Send Refund to Google Ads

In Server GTM:

  1. Create Google Ads Conversion Tag
  2. Use conversion_label, conversion_id
  3. Set value to 0 or refund amount (if partial refund logic is applied)
  4. Add transaction_id and event_id
  5. Trigger: When event_name == refund

βœ… This updates your Google Ads account to reflect the refund, adjusting ROAS.


πŸ”Ή Step 6: Send Refund to Meta via CAPI

Use Meta CAPI HTTP Request Tag:

  • event_name: Refund
  • event_id: {{event_id}}
  • em, ph: email and phone hashes
  • transaction_id, currency, value: if available
  • action_source: website


πŸ”Ή Step 7: Validate and Test

Tool Use
GA4 DebugView Validate refund hits
Server GTM Preview Check payloads
Meta CAPI Test Events Confirm receipt
Google Ads Conversion Troubleshooter Check for adjusted ROAS


🧠 Pro Tips

Tip Why It Helps
Use event_id to deduplicate Prevents same refund from firing multiple times
Log refunds server-side Useful for audits and anomaly detection
Use server response logging Catch failed refund delivery to GTM
Consider partial refund logic Track only refunded item values if needed
Include consent signal if regulated Ensures privacy compliance


πŸ“¦ Summary Flow

[Admin Approves Refund in OpenCart]
↓
[Backend PHP Fires Event to ssGTM]
↓
[Server-Side GTM Container]
↓ ↓ ↓
[GA4] [Google Ads] [Meta CAPI]


Leave a Reply

Your email address will not be published. Required fields are marked *