Skip to content

Instantly share code, notes, and snippets.

@it-can
Last active May 26, 2026 14:40
Show Gist options
  • Select an option

  • Save it-can/188acc89637160d21ab9a37e1bbf0b2c to your computer and use it in GitHub Desktop.

Select an option

Save it-can/188acc89637160d21ab9a37e1bbf0b2c to your computer and use it in GitHub Desktop.
Cloudflare Zaraz Google Ads conversion tracking with "enhanced conversions"
<script>
var user_data_info = {
sha256_email_address: '{{ normalizeAndHashEmailAddress($invoice->email) }}',
sha256_phone_number: '{{ normalizeAndHash(formatPhoneE164($invoice->billing_telephone)) }}',
address: {
sha256_first_name: '{{ normalizeAndHash($firstname ?? '') }}',
sha256_last_name: '{{ normalizeAndHash($lastname ?? '') }}',
sha256_street: '{{ normalizeAndHash($invoice->billing_address . ' ' . $invoice->billing_address_nr) }}',
postal_code: '{{ $invoice->billing_zipcode }}',
city: '{{ $invoice->billing_city }}',
country: '{{ $invoice->billing_country }}',
new_customer: {{ $new_customer ? 'true' : 'false' }}
}
};
// Google Ads conversion tracking
zaraz.track("conversion", {
transaction_id: "ORDER ID",
value: AMOUNT
currency: 'EUR',
user_data: JSON.stringify(user_data_info)
});
</script>
<?php
if (! function_exists('formatPhoneE164')) {
/**
* Format phonenumber to E164 (i use this library https://github.com/Propaganistas/Laravel-Phone)
*
*
* @return mixed|string
*/
function formatPhoneE164($value)
{
$value = Str::lower(trim($value));
try {
return (new PhoneNumber($value, 'NL'))
->formatE164();
} catch (Exception $e) {
}
return $value;
}
}
if (! function_exists('normalizeAndHash')) {
function normalizeAndHash(string $value, bool $trimIntermediateSpaces = false): string
{
// Normalizes by first converting all characters to lowercase, then trimming spaces.
$normalized = strtolower($value);
if ($trimIntermediateSpaces) {
// Removes leading, trailing, and intermediate spaces.
$normalized = str_replace(' ', '', $normalized);
} else {
// Removes only leading and trailing spaces.
$normalized = trim($normalized);
}
return hash('sha256', strtolower(trim($normalized)));
}
}
if (! function_exists('normalizeAndHashEmailAddress')) {
function normalizeAndHashEmailAddress(string $emailAddress): string
{
$normalizedEmail = strtolower($emailAddress);
$emailParts = explode('@', $normalizedEmail);
if (count($emailParts) > 1 && preg_match('/^(gmail|googlemail)\.com\s*/', $emailParts[1])) {
// Removes any '.' characters from the portion of the email address before the domain
// if the domain is gmail.com or googlemail.com.
$emailParts[0] = str_replace('.', '', $emailParts[0]);
$normalizedEmail = sprintf('%s@%s', $emailParts[0], $emailParts[1]);
}
return normalizeAndHash($normalizedEmail, true);
}
}
@kaka90

kaka90 commented Oct 4, 2025

Copy link
Copy Markdown
  1. trigger google ads on your conversion event, make sure you set in settings proper labels (conversion id and label and conversion linker)
  2. pass custom fields: currency, revenue, transaction_id, user_data.email, user_data.phone with event property
  3. make sure you send Consent Mode from event property _zarazGoogleConsentV2String

What's missing in steps above is passing these values from zaraz to gads, which is step 2

To confirm, see in zaraz debugger call to googleadservices.com/pagead/conversion/, as described here:
https://support.google.com/google-ads/answer/13258081?hl=en#zippy=%2Cvalidate-your-implementation-using-chrome-developer-tools

@JohJohan

JohJohan commented Apr 29, 2026

Copy link
Copy Markdown

@it-can i setup the same but dont see the enhanched data in zaraz debugger i see it still sents it as user_data.sha256_email_address=x while google docs state you need to see em=tv.1~em see: [docs](https://support.google.com/google-ads/answer/13258081?hl=en#zippy=%2Cfind-enhanced-conversions-fields-on-your-conversion-page%2Cidentify-enhanced-conversions-css-selectors-and-input-into-google-ads%2Cidentify-and-define-your-enhanced-conversions-fields%2Cvalidate-your-implementation-using-chrome-developer-tools%2Cafter-hours-review-the-diagnostics-report-to-confirm-your-implementation:~:text=click%20Save.-,Validate%20your%20implementation,-To%20verify%20if did ou also see this when testing?)

I also tried to sent email but this also doesn't show the em field i'm confused also think its strange Zaraz doesn't even document this while its pretty basic functionality in Google Ads .

Btw this is what i'm sending:

zaraz.track("registration", {"em":"e2554c4a1bd737bafb3cc7ce501d4e8042dfbfdf42d079defa12bf83d6acf959","user_data.sha256_email_address":"e2554c4a1bd737bafb3cc7ce501d4e8042dfbfdf42d079defa12bf83d6acf959"}); 

@JohJohan

Copy link
Copy Markdown

I was finally able to get it working by adding the hashed keys before my values see:

$userData = [
            'em'                             => 'tv.1~em.'.$this->hashedEmailAddress,
            'sha256_email_address'           => 'tv.1~em.'.$this->hashedEmailAddress,
            'user_data.sha256_email_address' => 'tv.1~em.'.$this->hashedEmailAddress,
        ];

        if (null !== $this->hashedFirstName) {
            $userData['fn']                                  = 'tv.1~fn.'.$this->hashedFirstName;
            $userData['address.sha256_first_name']           = 'tv.1~fn.'.$this->hashedFirstName;
            $userData['user_data.address.sha256_first_name'] = 'tv.1~fn.'.$this->hashedFirstName;
        }

        if (null !== $this->hashedLastName) {
            $userData['ln']                                 = 'tv.1~ln.'.$this->hashedLastName;
            $userData['address.sha256_last_name']           = 'tv.1~ln.'.$this->hashedLastName;
            $userData['user_data.address.sha256_last_name'] = 'tv.1~ln.'.$this->hashedLastName;
        }

        if (null !== $this->hashedPhoneNumber) {
            $userData['pn']                            = 'tv.1~pn.'.$this->hashedPhoneNumber;
            $userData['sha256_phone_number']           = 'tv.1~pn.'.$this->hashedPhoneNumber;
            $userData['user_data.sha256_phone_number'] = 'tv.1~pn.'.$this->hashedPhoneNumber;
        }

In the end i dont know which key makes it work found the info here: https://www.semetis.com/en/resources/articles/how-to-debug-google-enhanced-conversions-implementation#:~:text=If%20the%20em%20parameter%20is%20present%20but%20looks%20like%20%E2%80%9Ctv.1~em.%E2%80%9D%20then%20you%E2%80%99re%20sending%20the%20enhanced%20conversion%20parameter%20but%20it%E2%80%99s%20empty. goodluck!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment