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:
- Open Preview Mode
- Trigger a refund from OpenCart
- Check the incoming request β
event_name
should berefund
πΉ 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:
- Create Google Ads Conversion Tag
- Use conversion_label, conversion_id
- Set value to
0
or refund amount (if partial refund logic is applied) - Add transaction_id and
event_id
- 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 hashestransaction_id
,currency
,value
: if availableaction_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]