Java
// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.ads.googleads.examples.remarketing; import com.beust.jcommander.Parameter; import com.google.ads.googleads.examples.utils.ArgumentNames; import com.google.ads.googleads.examples.utils.CodeSampleParams; import com.google.ads.googleads.lib.GoogleAdsClient; import com.google.ads.googleads.v14.common.OfflineUserAddressInfo; import com.google.ads.googleads.v14.common.UserIdentifier; import com.google.ads.googleads.v14.enums.ConversionAdjustmentTypeEnum.ConversionAdjustmentType; import com.google.ads.googleads.v14.enums.UserIdentifierSourceEnum.UserIdentifierSource; import com.google.ads.googleads.v14.errors.GoogleAdsError; import com.google.ads.googleads.v14.errors.GoogleAdsException; import com.google.ads.googleads.v14.services.ConversionAdjustment; import com.google.ads.googleads.v14.services.ConversionAdjustmentResult; import com.google.ads.googleads.v14.services.ConversionAdjustmentUploadServiceClient; import com.google.ads.googleads.v14.services.GclidDateTimePair; import com.google.ads.googleads.v14.services.UploadConversionAdjustmentsRequest; import com.google.ads.googleads.v14.services.UploadConversionAdjustmentsResponse; import com.google.ads.googleads.v14.utils.ResourceNames; import java.io.FileNotFoundException; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * Adjusts an existing conversion by supplying user identifiers so Google can enhance the conversion * value. */ public class UploadConversionEnhancement { private static class UploadConversionEnhancementParams extends CodeSampleParams { @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true) private long customerId; @Parameter(names = ArgumentNames.CONVERSION_ACTION_ID, required = true) private long conversionActionId; @Parameter(names = ArgumentNames.ORDER_ID, required = true) private String orderId; @Parameter( names = ArgumentNames.CONVERSION_DATE_TIME, required = false, description = "The date time at which the conversion with the specified order ID occurred. " + "Must be after the click time, and must include the time zone offset. " + "The format is 'yyyy-mm-dd hh:mm:ss+|-hh:mm', e.g. '2019-01-01 12:32:45-08:00'. " + "Setting this field is optional, but recommended.") private String conversionDateTime; @Parameter(names = ArgumentNames.USER_AGENT, required = false) private String userAgent; } public static void main(String[] args) throws UnsupportedEncodingException, NoSuchAlgorithmException { UploadConversionEnhancementParams params = new UploadConversionEnhancementParams(); if (!params.parseArguments(args)) { // Either pass the required parameters for this example on the command line, or insert them // into the code here. See the parameter class definition above for descriptions. params.customerId = Long.parseLong("INSERT_CUSTOMER_ID_HERE"); params.conversionActionId = Long.parseLong("INSERT_CONVERSION_ACTION_ID_HERE"); params.orderId = "INSERT_ORDER_ID_HERE"; // Optional: Specify the conversion date/time and user agent. params.conversionDateTime = null; params.userAgent = null; } GoogleAdsClient googleAdsClient = null; try { googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build(); } catch (FileNotFoundException fnfe) { System.err.printf( "Failed to load GoogleAdsClient configuration from file. Exception: %s%n", fnfe); System.exit(1); } catch (IOException ioe) { System.err.printf("Failed to create GoogleAdsClient. Exception: %s%n", ioe); System.exit(1); } try { new UploadConversionEnhancement() .runExample( googleAdsClient, params.customerId, params.conversionActionId, params.orderId, params.conversionDateTime, params.userAgent); } catch (GoogleAdsException gae) { // GoogleAdsException is the base class for most exceptions thrown by an API request. // Instances of this exception have a message and a GoogleAdsFailure that contains a // collection of GoogleAdsErrors that indicate the underlying causes of the // GoogleAdsException. System.err.printf( "Request ID %s failed due to GoogleAdsException. Underlying errors:%n", gae.getRequestId()); int i = 0; for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) { System.err.printf(" Error %d: %s%n", i++, googleAdsError); } System.exit(1); } } /** * Runs the example. * * @param googleAdsClient the Google Ads API client. * @param customerId the client customer ID. * @param conversionActionId conversion action ID associated with this conversion. * @param orderId unique order ID (transaction ID) of the conversion. * @param conversionDateTime * @param userAgent the HTTP user agent of the conversion. */ private void runExample( GoogleAdsClient googleAdsClient, long customerId, long conversionActionId, String orderId, String conversionDateTime, String userAgent) throws NoSuchAlgorithmException, UnsupportedEncodingException { // Creates a builder for constructing the enhancement adjustment. ConversionAdjustment.Builder enhancementBuilder = ConversionAdjustment.newBuilder() .setConversionAction(ResourceNames.conversionAction(customerId, conversionActionId)) .setAdjustmentType(ConversionAdjustmentType.ENHANCEMENT) // Enhancements MUST use order ID instead of GCLID date/time pair. .setOrderId(orderId); // Sets the conversion date and time if provided. Providing this value is optional but // recommended. if (conversionDateTime != null) { enhancementBuilder.setGclidDateTimePair( GclidDateTimePair.newBuilder().setConversionDateTime(conversionDateTime)); } // Creates a SHA256 message digest for hashing user identifiers in a privacy-safe way, as // described at https://support.google.com/google-ads/answer/9888656. MessageDigest sha256Digest = MessageDigest.getInstance("SHA-256"); // Adds user identifiers, hashing where required. // Creates a user identifier using sample values for the user address. UserIdentifier addressIdentifier = UserIdentifier.newBuilder() .setAddressInfo( OfflineUserAddressInfo.newBuilder() .setHashedFirstName(normalizeAndHash(sha256Digest, "Dana")) .setHashedLastName(normalizeAndHash(sha256Digest, "Quinn")) .setHashedStreetAddress( normalizeAndHash(sha256Digest, "1600 Amphitheatre Pkwy")) .setCity("Mountain View") .setState("CA") .setPostalCode("94043") .setCountryCode("US")) // Optional: Specifies the user identifier source. .setUserIdentifierSource(UserIdentifierSource.FIRST_PARTY) .build(); // Creates a user identifier using the hashed email address. UserIdentifier emailIdentifier = UserIdentifier.newBuilder() .setUserIdentifierSource(UserIdentifierSource.FIRST_PARTY) // Uses the normalize and hash method specifically for email addresses. .setHashedEmail(normalizeAndHashEmailAddress(sha256Digest, "dana@example.com")) .build(); // Adds the user identifiers to the enhancement adjustment. enhancementBuilder.addUserIdentifiers(addressIdentifier).addUserIdentifiers(emailIdentifier); // Sets optional fields where a value was provided. if (userAgent != null) { // Sets the user agent. This should match the user agent of the request that sent the original // conversion so the conversion and its enhancement are either both attributed as same-device // or both attributed as cross-device. enhancementBuilder.setUserAgent(userAgent); } // Creates the conversion upload service client. try (ConversionAdjustmentUploadServiceClient conversionUploadServiceClient = googleAdsClient.getLatestVersion().createConversionAdjustmentUploadServiceClient()) { // Uploads the enhancement adjustment. Partial failure should always be set to true. UploadConversionAdjustmentsResponse response = conversionUploadServiceClient.uploadConversionAdjustments( UploadConversionAdjustmentsRequest.newBuilder() .setCustomerId(Long.toString(customerId)) .addConversionAdjustments(enhancementBuilder) // Enables partial failure (must be true). .setPartialFailure(true) .build()); // Prints any partial errors returned. if (response.hasPartialFailureError()) { System.out.printf( "Partial error encountered: '%s'.%n", response.getPartialFailureError().getMessage()); } else { // Prints the result. ConversionAdjustmentResult result = response.getResults(0); System.out.printf( "Uploaded conversion adjustment of '%s' for order ID '%s'.%n", result.getConversionAction(), result.getOrderId()); } } } /** * Returns the result of normalizing and then hashing the string using the provided digest. * Private customer data must be hashed during upload, as described at * https://support.google.com/google-ads/answer/7474263. * * @param digest the digest to use to hash the normalized string. * @param s the string to normalize and hash. */ private String normalizeAndHash(MessageDigest digest, String s) throws UnsupportedEncodingException { // Normalizes by removing leading and trailing whitespace and converting all characters to // lower case. String normalized = s.trim().toLowerCase(); // Hashes the normalized string using the hashing algorithm. byte[] hash = digest.digest(normalized.getBytes("UTF-8")); StringBuilder result = new StringBuilder(); for (byte b : hash) { result.append(String.format("%02x", b)); } return result.toString(); } /** * Returns the result of normalizing and hashing an email address. For this use case, Google Ads * requires removal of any '.' characters preceding {@code gmail.com} or {@code googlemail.com}. * * @param digest the digest to use to hash the normalized string. * @param emailAddress the email address to normalize and hash. */ private String normalizeAndHashEmailAddress(MessageDigest digest, String emailAddress) throws UnsupportedEncodingException { String normalizedEmail = emailAddress.toLowerCase(); String[] emailParts = normalizedEmail.split("@"); if (emailParts.length > 1 && emailParts[1].matches("^(gmail|googlemail)\\.com\\s*")) { // Removes any '.' characters from the portion of the email address before the domain if the // domain is gmail.com or googlemail.com. emailParts[0] = emailParts[0].replaceAll("\\.", ""); normalizedEmail = String.format("%s@%s", emailParts[0], emailParts[1]); } return normalizeAndHash(digest, normalizedEmail); } }
C#
// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. using CommandLine; using Google.Ads.Gax.Examples; using Google.Ads.GoogleAds.Lib; using Google.Ads.GoogleAds.V14.Common; using Google.Ads.GoogleAds.V14.Errors; using Google.Ads.GoogleAds.V14.Resources; using Google.Ads.GoogleAds.V14.Services; using System; using System.Collections.Generic; using System.Security.Cryptography; using System.Text; using static Google.Ads.GoogleAds.V14.Enums.ConversionAdjustmentTypeEnum.Types; using static Google.Ads.GoogleAds.V14.Enums.UserIdentifierSourceEnum.Types; namespace Google.Ads.GoogleAds.Examples.V14 { /// <summary> /// This code example adjusts an existing conversion by supplying user identifiers so /// Google can enhance the conversion value. /// </summary> public class UploadConversionEnhancement : ExampleBase { /// <summary> /// Command line options for running the <see cref="UploadConversionEnhancement"/> example. /// </summary> public class Options : OptionsBase { /// <summary> /// The Google Ads customer ID for which the conversion action is added. /// </summary> [Option("customerId", Required = true, HelpText = "The Google Ads customer ID for which the conversion action is added.")] public long CustomerId { get; set; } /// <summary> /// ID of the conversion action for which adjustments are uploaded. /// </summary> [Option("conversionActionId", Required = true, HelpText = "ID of the conversion action for which adjustments are uploaded.")] public long ConversionActionId { get; set; } /// <summary> /// The unique order ID (transaction ID) of the conversion. /// </summary> [Option("orderId", Required = true, HelpText = "The unique order ID (transaction ID) of the conversion.")] public string OrderId { get; set; } /// <summary> /// The date time at which the conversion with the specified order ID occurred. Must /// be after the click time, and must include the time zone offset. The format is /// 'yyyy-mm-dd hh:mm:ss+|-hh:mm', e.g. '2019-01-01 12:32:45-08:00'. Setting this /// field is optional, but recommended. /// </summary> [Option("conversionDateTime", Required = false, HelpText = "The date time at which the conversion with the specified order ID occurred. " + "Must be after the click time, and must include the time zone offset. The " + "format is 'yyyy-mm-dd hh:mm:ss+|-hh:mm', e.g. '2019-01-01 12:32:45-08:00'. " + "Setting this field is optional, but recommended.")] public string ConversionDateTime { get; set; } /// <summary> /// The HTTP user agent of the conversion. /// </summary> [Option("userAgent", Required = true, HelpText = "The HTTP user agent of the conversion.")] public string UserAgent { get; set; } } /// <summary> /// Main method, to run this code example as a standalone application. /// </summary> /// <param name="args">The command line arguments.</param> public static void Main(string[] args) { Options options = ExampleUtilities.ParseCommandLine<Options>(args); UploadConversionEnhancement codeExample = new UploadConversionEnhancement(); Console.WriteLine(codeExample.Description); codeExample.Run(new GoogleAdsClient(), options.CustomerId, options.ConversionActionId, options.OrderId, options.ConversionDateTime, options.UserAgent); } private static SHA256 digest = SHA256.Create(); /// <summary> /// Returns a description about the code example. /// </summary> public override string Description => "This code example adjusts an existing conversion by supplying user identifiers so " + "Google can enhance the conversion value."; /// <summary> /// Runs the code example. /// </summary> /// <param name="client">The Google Ads client.</param> /// <param name="customerId">The Google Ads customer ID for which the conversion /// enhancement is uploaded.</param> /// <param name="conversionActionId">ID of the conversion action for which adjustments are /// uploaded.</param> /// <param name="orderId">The unique order ID (transaction ID) of the conversion.</param> /// <param name="conversionDateTime">The date time at which the conversion with the /// specified order ID occurred.</param> /// <param name="userAgent">The HTTP user agent of the conversion.</param> public void Run(GoogleAdsClient client, long customerId, long conversionActionId, string orderId, string conversionDateTime, string userAgent) { // Get the ConversionAdjustmentUploadService. ConversionAdjustmentUploadServiceClient conversionAdjustmentUploadService = client.GetService(Services.V14.ConversionAdjustmentUploadService); // Creates the enhancement adjustment. ConversionAdjustment enhancement = new ConversionAdjustment() { ConversionAction = ResourceNames.ConversionAction(customerId, conversionActionId), AdjustmentType = ConversionAdjustmentType.Enhancement, // Enhancements MUST use order ID instead of GCLID date/time pair. OrderId = orderId }; // Sets the conversion date and time if provided. Providing this value is optional but // recommended. if (string.IsNullOrEmpty(conversionDateTime)) { enhancement.GclidDateTimePair = new GclidDateTimePair() { ConversionDateTime = conversionDateTime }; } // Adds user identifiers, hashing where required. // Creates a user identifier using sample values for the user address. UserIdentifier addressIdentifier = new UserIdentifier() { AddressInfo = new OfflineUserAddressInfo() { HashedFirstName = NormalizeAndHash("Dana"), HashedLastName = NormalizeAndHash("Quinn"), HashedStreetAddress = NormalizeAndHash("1600 Amphitheatre Pkwy"), City = "Mountain View", State = "CA", PostalCode = "94043", CountryCode = "US" }, // Optional: Specifies the user identifier source. UserIdentifierSource = UserIdentifierSource.FirstParty }; // Creates a user identifier using the hashed email address. UserIdentifier emailIdentifier = new UserIdentifier() { UserIdentifierSource = UserIdentifierSource.FirstParty, // Uses the normalize and hash method specifically for email addresses. HashedEmail = NormalizeAndHashEmailAddress("dana@example.com") }; // Adds the user identifiers to the enhancement adjustment. enhancement.UserIdentifiers.AddRange(new[] { addressIdentifier, emailIdentifier }); // Sets optional fields where a value was provided. if (!string.IsNullOrEmpty(userAgent)) { // Sets the user agent. This should match the user agent of the request that // sent the original conversion so the conversion and its enhancement are either // both attributed as same-device or both attributed as cross-device. enhancement.UserAgent = userAgent; } try { // Uploads the enhancement adjustment. Partial failure should always be set to true. UploadConversionAdjustmentsResponse response = conversionAdjustmentUploadService.UploadConversionAdjustments( new UploadConversionAdjustmentsRequest() { CustomerId = customerId.ToString(), ConversionAdjustments = { enhancement }, // Enables partial failure (must be true). PartialFailure = true, }); // Prints the status message if any partial failure error is returned. // Note: The details of each partial failure error are not printed here, // you can refer to the example HandlePartialFailure.cs to learn more. if (response.PartialFailureError != null) { // Extracts the partial failure from the response status. GoogleAdsFailure partialFailure = response.PartialFailure; Console.WriteLine($"{partialFailure.Errors.Count} partial failure error(s) " + $"occurred"); } else { // Prints the result. ConversionAdjustmentResult result = response.Results[0]; Console.WriteLine($"Uploaded conversion adjustment of " + $"'{result.ConversionAction}' for order ID '{result.OrderId}'."); } } catch (GoogleAdsException e) { Console.WriteLine("Failure:"); Console.WriteLine($"Message: {e.Message}"); Console.WriteLine($"Failure: {e.Failure}"); Console.WriteLine($"Request ID: {e.RequestId}"); throw; } } /// <summary> /// Normalizes the email address and hashes it. For this use case, Google Ads requires /// removal of any '.' characters preceding <code>gmail.com</code> or /// <code>googlemail.com</code>. /// </summary> /// <param name="emailAddress">The email address.</param> /// <returns>The hash code.</returns> private string NormalizeAndHashEmailAddress(string emailAddress) { string normalizedEmail = emailAddress.ToLower(); string[] emailParts = normalizedEmail.Split('@'); if (emailParts.Length > 1 && (emailParts[1] == "gmail.com" || emailParts[1] == "googlemail.com")) { // Removes any '.' characters from the portion of the email address before // the domain if the domain is gmail.com or googlemail.com. emailParts[0] = emailParts[0].Replace(".", ""); normalizedEmail = $"{emailParts[0]}@{emailParts[1]}"; } return NormalizeAndHash(normalizedEmail); } /// <summary> /// Normalizes and hashes a string value. /// </summary> /// <param name="value">The value to normalize and hash.</param> /// <returns>The normalized and hashed value.</returns> private static string NormalizeAndHash(string value) { return ToSha256String(digest, ToNormalizedValue(value)); } /// <summary> /// Hash a string value using SHA-256 hashing algorithm. /// </summary> /// <param name="digest">Provides the algorithm for SHA-256.</param> /// <param name="value">The string value (e.g. an email address) to hash.</param> /// <returns>The hashed value.</returns> private static string ToSha256String(SHA256 digest, string value) { byte[] digestBytes = digest.ComputeHash(Encoding.UTF8.GetBytes(value)); // Convert the byte array into an unhyphenated hexadecimal string. return BitConverter.ToString(digestBytes).Replace("-", string.Empty); } /// <summary> /// Removes leading and trailing whitespace and converts all characters to /// lower case. /// </summary> /// <param name="value">The value to normalize.</param> /// <returns>The normalized value.</returns> private static string ToNormalizedValue(string value) { return value.Trim().ToLower(); } } }
PHP
<?php /** * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ namespace Google\Ads\GoogleAds\Examples\Remarketing; require __DIR__ . '/../../vendor/autoload.php'; use GetOpt\GetOpt; use Google\Ads\GoogleAds\Examples\Utils\ArgumentNames; use Google\Ads\GoogleAds\Examples\Utils\ArgumentParser; use Google\Ads\GoogleAds\Lib\OAuth2TokenBuilder; use Google\Ads\GoogleAds\Lib\V14\GoogleAdsClient; use Google\Ads\GoogleAds\Lib\V14\GoogleAdsClientBuilder; use Google\Ads\GoogleAds\Lib\V14\GoogleAdsException; use Google\Ads\GoogleAds\Util\V14\ResourceNames; use Google\Ads\GoogleAds\V14\Common\OfflineUserAddressInfo; use Google\Ads\GoogleAds\V14\Common\UserIdentifier; use Google\Ads\GoogleAds\V14\Enums\ConversionAdjustmentTypeEnum\ConversionAdjustmentType; use Google\Ads\GoogleAds\V14\Enums\UserIdentifierSourceEnum\UserIdentifierSource; use Google\Ads\GoogleAds\V14\Errors\GoogleAdsError; use Google\Ads\GoogleAds\V14\Services\ConversionAdjustment; use Google\Ads\GoogleAds\V14\Services\ConversionAdjustmentResult; use Google\Ads\GoogleAds\V14\Services\GclidDateTimePair; use Google\Ads\GoogleAds\V14\Services\UploadConversionAdjustmentsRequest; use Google\ApiCore\ApiException; /** * This example adjusts an existing conversion by supplying user identifiers so Google can enhance * the conversion value. */ class UploadConversionEnhancement { private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE'; private const CONVERSION_ACTION_ID = 'INSERT_CONVERSION_ACTION_ID_HERE'; private const ORDER_ID = 'INSERT_ORDER_ID_HERE'; // Optional parameters. // The date time at which the conversion with the specified order ID occurred. // Must be after the click time, and must include the time zone offset. // The format is "yyyy-mm-dd hh:mm:ss+|-hh:mm", e.g. '2019-01-01 12:32:45-08:00'. // Setting this field is optional, but recommended. private const CONVERSION_DATE_TIME = null; private const USER_AGENT = null; public static function main() { // Either pass the required parameters for this example on the command line, or insert them // into the constants above. $options = (new ArgumentParser())->parseCommandArguments([ ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT, ArgumentNames::CONVERSION_ACTION_ID => GetOpt::REQUIRED_ARGUMENT, ArgumentNames::ORDER_ID => GetOpt::REQUIRED_ARGUMENT, ArgumentNames::CONVERSION_DATE_TIME => GetOpt::OPTIONAL_ARGUMENT, ArgumentNames::USER_AGENT => GetOpt::OPTIONAL_ARGUMENT ]); // Generate a refreshable OAuth2 credential for authentication. $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build(); // Construct a Google Ads client configured from a properties file and the // OAuth2 credentials above. $googleAdsClient = (new GoogleAdsClientBuilder()) ->fromFile() ->withOAuth2Credential($oAuth2Credential) // We set this value to true to show how to use GAPIC v2 source code. You can remove the // below line if you wish to use the old-style source code. Note that in that case, you // probably need to modify some parts of the code below to make it work. // For more information, see // https://developers.devsite.corp.google.com/google-ads/api/docs/client-libs/php/gapic. ->usingGapicV2Source(true) ->build(); try { self::runExample( $googleAdsClient, $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID, $options[ArgumentNames::CONVERSION_ACTION_ID] ?: self::CONVERSION_ACTION_ID, $options[ArgumentNames::ORDER_ID] ?: self::ORDER_ID, $options[ArgumentNames::CONVERSION_DATE_TIME] ?: self::CONVERSION_DATE_TIME, $options[ArgumentNames::USER_AGENT] ?: self::USER_AGENT ); } catch (GoogleAdsException $googleAdsException) { printf( "Request with ID '%s' has failed.%sGoogle Ads failure details:%s", $googleAdsException->getRequestId(), PHP_EOL, PHP_EOL ); foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) { /** @var GoogleAdsError $error */ printf( "\t%s: %s%s", $error->getErrorCode()->getErrorCode(), $error->getMessage(), PHP_EOL ); } exit(1); } catch (ApiException $apiException) { printf( "ApiException was thrown with message '%s'.%s", $apiException->getMessage(), PHP_EOL ); exit(1); } } /** * Runs the example. * * @param GoogleAdsClient $googleAdsClient the Google Ads API client * @param int $customerId the customer ID * @param int $conversionActionId the ID of the conversion action associated with this * conversion * @param string $orderId the unique order ID (transaction ID) of the conversion * @param string|null $conversionDateTime the date and time of the conversion. * The format is "yyyy-mm-dd hh:mm:ss+|-hh:mm", e.g. “2019-01-01 12:32:45-08:00” * @param string|null $userAgent the HTTP user agent of the conversion */ public static function runExample( GoogleAdsClient $googleAdsClient, int $customerId, int $conversionActionId, string $orderId, ?string $conversionDateTime, ?string $userAgent ) { // Creates the conversion enhancement. $conversionAdjustment = new ConversionAdjustment([ 'conversion_action' => ResourceNames::forConversionAction($customerId, $conversionActionId), 'adjustment_type' => ConversionAdjustmentType::ENHANCEMENT, // Enhancements must use order ID instead of GCLID date/time pair. 'order_id' => $orderId ]); // Uses the SHA-256 hash algorithm for hashing user identifiers in a privacy-safe way, as // described at https://support.google.com/google-ads/answer/9888656. $hashAlgorithm = "sha256"; // Adds user identifiers, hashing where required. // Creates a user identifier using sample values for the user address. $addressIdentifier = new UserIdentifier([ 'address_info' => new OfflineUserAddressInfo([ 'hashed_first_name' => self::normalizeAndHash($hashAlgorithm, 'Dana'), 'hashed_last_name' => self::normalizeAndHash($hashAlgorithm, 'Quinn'), 'hashed_street_address' => self::normalizeAndHash( $hashAlgorithm, '1600 Amphitheatre Pkwy' ), 'city' => 'Mountain View', 'state' => 'CA', 'postal_code' => '94043', 'country_code' => 'US' ]), // Optional: Specifies the user identifier source. 'user_identifier_source' => UserIdentifierSource::FIRST_PARTY ]); // Creates a user identifier using the hashed email address. $emailIdentifier = new UserIdentifier([ // Uses the normalize and hash method specifically for email addresses. 'hashed_email' => self::normalizeAndHashEmailAddress( $hashAlgorithm, 'dana@example.com' ), // Optional: Specifies the user identifier source. 'user_identifier_source' => UserIdentifierSource::FIRST_PARTY ]); // Adds the user identifiers to the enhancement adjustment. $conversionAdjustment->setUserIdentifiers([$addressIdentifier, $emailIdentifier]); // Sets optional fields where a value was provided. if ($conversionDateTime !== null) { // Sets the conversion date and time if provided. Providing this value is optional but // recommended. $conversionAdjustment->setGclidDateTimePair(new GclidDateTimePair([ 'conversion_date_time' => $conversionDateTime ])); } if ($userAgent !== null) { // Sets the user agent. This should match the user agent of the request that sent the // original conversion so the conversion and its enhancement are either both attributed // as same-device or both attributed as cross-device. $conversionAdjustment->setUserAgent($userAgent); } // Issues a request to upload the conversion enhancement. $conversionAdjustmentUploadServiceClient = $googleAdsClient->getConversionAdjustmentUploadServiceClient(); $response = $conversionAdjustmentUploadServiceClient->uploadConversionAdjustments( // Enables partial failure (must be true). UploadConversionAdjustmentsRequest::build($customerId, [$conversionAdjustment], true) ); // Prints the status message if any partial failure error is returned. // Note: The details of each partial failure error are not printed here, you can refer to // the example HandlePartialFailure.php to learn more. if ($response->hasPartialFailureError()) { printf( "Partial failures occurred: '%s'.%s", $response->getPartialFailureError()->getMessage(), PHP_EOL ); } else { // Prints the result if exists. /** @var ConversionAdjustmentResult $uploadedConversionAdjustment */ $uploadedConversionAdjustment = $response->getResults()[0]; printf( "Uploaded conversion adjustment of '%s' for order ID '%s'.%s", $uploadedConversionAdjustment->getConversionAction(), $uploadedConversionAdjustment->getOrderId(), PHP_EOL ); } } /** * Returns the result of normalizing and then hashing the string using the provided hash * algorithm. Private customer data must be hashed during upload, as described at * https://support.google.com/google-ads/answer/7474263. * * @param string $hashAlgorithm the hash algorithm to use * @param string $value the value to normalize and hash * @return string the normalized and hashed value */ private static function normalizeAndHash(string $hashAlgorithm, string $value): string { return hash($hashAlgorithm, strtolower(trim($value))); } /** * Returns the result of normalizing and hashing an email address. For this use case, Google * Ads requires removal of any '.' characters preceding "gmail.com" or "googlemail.com". * * @param string $hashAlgorithm the hash algorithm to use * @param string $emailAddress the email address to normalize and hash * @return string the normalized and hashed email address */ private static function normalizeAndHashEmailAddress( string $hashAlgorithm, 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 self::normalizeAndHash($hashAlgorithm, $normalizedEmail); } } UploadConversionEnhancement::main();
Python
#!/usr/bin/env python # Copyright 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Uploads a conversion using hashed email address instead of GCLID.""" import argparse import hashlib import re import sys from google.ads.googleads.client import GoogleAdsClient from google.ads.googleads.errors import GoogleAdsException def main( client, customer_id, conversion_action_id, order_id, conversion_date_time, user_agent, ): """The main method that creates all necessary entities for the example. Args: client: An initialized GoogleAdsClient instance. customer_id: The client customer ID string. conversion_action_id: The ID of the conversion action to upload to. order_id: The unique ID (transaction ID) of the conversion. conversion_date_time: The date and time of the conversion. user_agent: The HTTP user agent of the conversion. """ conversion_action_service = client.get_service("ConversionActionService") conversion_adjustment = client.get_type("ConversionAdjustment") conversion_adjustment.conversion_action = conversion_action_service.conversion_action_path( customer_id, conversion_action_id ) conversion_adjustment.adjustment_type = ( client.enums.ConversionAdjustmentTypeEnum.ENHANCEMENT ) # Enhancements MUST use order ID instead of GCLID date/time pair. conversion_adjustment.order_id = order_id # Sets the conversion date and time if provided. Providing this value is # optional but recommended. if conversion_date_time: conversion_adjustment.gclid_date_time_pair.conversion_date_time = ( conversion_date_time ) # Creates a user identifier using sample values for the user address, # hashing where required. address_identifier = client.get_type("UserIdentifier") address_identifier.address_info.hashed_first_name = normalize_and_hash( "Dana" ) address_identifier.address_info.hashed_last_name = normalize_and_hash( "Quinn" ) address_identifier.address_info.hashed_street_address = normalize_and_hash( "1600 Amphitheatre Pkwy" ) address_identifier.address_info.city = "Mountain View" address_identifier.address_info.state = "CA" address_identifier.address_info.postal_code = "94043" address_identifier.address_info.country_code = "US" # Optional: Specifies the user identifier source. address_identifier.user_identifier_source = ( client.enums.UserIdentifierSourceEnum.FIRST_PARTY ) # Creates a user identifier using the hashed email address. email_identifier = client.get_type("UserIdentifier") # Optional: Specifies the user identifier source. email_identifier.user_identifier_source = ( client.enums.UserIdentifierSourceEnum.FIRST_PARTY ) # Uses the normalize and hash method specifically for email addresses. email_identifier.hashed_email = normalize_and_hash_email_address( "dana@example.com" ) # Adds both user identifiers to the conversion adjustment. conversion_adjustment.user_identifiers.extend( [address_identifier, email_identifier] ) # Sets optional fields where a value was provided if user_agent: # Sets the user agent. This should match the user agent of the request # that sent the original conversion so the conversion and its # enhancement are either both attributed as same-device or both # attributed as cross-device. conversion_adjustment.user_agent = user_agent # Creates the conversion adjustment upload service client. conversion_adjustment_upload_service = client.get_service( "ConversionAdjustmentUploadService" ) # Uploads the enhancement adjustment. Partial failure should always be set # to true. response = conversion_adjustment_upload_service.upload_conversion_adjustments( customer_id=customer_id, conversion_adjustments=[conversion_adjustment], # Enables partial failure (must be true). partial_failure=True, ) # Prints any partial errors returned. if response.partial_failure_error: print( "Partial error encountered: " f"{response.partial_failure_error.message}" ) # Prints the result. result = response.results[0] # Only prints valid results. If the click conversion failed then this # result will be returned as an empty message and will be falsy. if result: print( f"Uploaded conversion adjustment of {result.conversion_action} for " f"order ID {result,order_id}." ) def normalize_and_hash_email_address(email_address): """Returns the result of normalizing and hashing an email address. For this use case, Google Ads requires removal of any '.' characters preceding "gmail.com" or "googlemail.com" Args: email_address: An email address to normalize. Returns: A normalized (lowercase, removed whitespace) and SHA-265 hashed string. """ normalized_email = email_address.lower() email_parts = normalized_email.split("@") # Checks whether the domain of the email address is either "gmail.com" # or "googlemail.com". If this regex does not match then this statement # will evaluate to None. is_gmail = re.match(r"^(gmail|googlemail)\.com$", email_parts[1]) # Check that there are at least two segments and the second segment # matches the above regex expression validating the email domain name. if len(email_parts) > 1 and is_gmail: # Removes any '.' characters from the portion of the email address # before the domain if the domain is gmail.com or googlemail.com. email_parts[0] = email_parts[0].replace(".", "") normalized_email = "@".join(email_parts) return normalize_and_hash(normalized_email) def normalize_and_hash(s): """Normalizes and hashes a string with SHA-256. Private customer data must be hashed during upload, as described at: https://support.google.com/google-ads/answer/7474263 Args: s: The string to perform this operation on. Returns: A normalized (lowercase, removed whitespace) and SHA-256 hashed string. """ return hashlib.sha256(s.strip().lower().encode()).hexdigest() if __name__ == "__main__": # GoogleAdsClient will read the google-ads.yaml configuration file in the # home directory if none is specified. googleads_client = GoogleAdsClient.load_from_storage(version="v14") parser = argparse.ArgumentParser( description="Imports offline call conversion values for calls related " "to your ads." ) # The following argument(s) should be provided to run the example. parser.add_argument( "-c", "--customer_id", type=str, required=True, help="The Google Ads customer ID.", ) parser.add_argument( "-a", "--conversion_action_id", type=str, required=True, help="The ID of the conversion action to upload to.", ) parser.add_argument( "-o", "--order_id", type=str, required=True, help="the unique ID (transaction ID) of the conversion.", ) parser.add_argument( "-d", "--conversion_date_time", type=str, help="The date time at which the conversion with the specified order " "ID occurred. Must be after the click time, and must include the time " "zone offset. The format is 'yyyy-mm-dd hh:mm:ss+|-hh:mm', " "e.g. '2019-01-01 12:32:45-08:00'. Setting this field is optional, " "but recommended", ) parser.add_argument( "-u", "--user_agent", type=str, help="The HTTP user agent of the conversion.", ) args = parser.parse_args() try: main( googleads_client, args.customer_id, args.conversion_action_id, args.order_id, args.conversion_date_time, args.user_agent, ) except GoogleAdsException as ex: print( f"Request with ID '{ex.request_id}'' failed with status " f"'{ex.error.code().name}' and includes the following errors:" ) for error in ex.failure.errors: print(f"\tError with message '{error.message}'.") if error.location: for field_path_element in error.location.field_path_elements: print(f"\t\tOn field: {field_path_element.field_name}") sys.exit(1)
Ruby
#!/usr/bin/env ruby # # Copyright 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Adjusts an existing conversion by supplying user identifiers so Google can # enhance the conversion value. require 'optparse' require 'google/ads/google_ads' require 'digest' def upload_conversion_enhancement( customer_id, conversion_action_id, order_id, conversion_date_time, user_agent ) # GoogleAdsClient will read a config file from # ENV['HOME']/google_ads_config.rb when called without parameters client = Google::Ads::GoogleAds::GoogleAdsClient.new enhancement = client.resource.conversion_adjustment do |ca| ca.conversion_action = client.path.conversion_action(customer_id, conversion_action_id) ca.adjustment_type = :ENHANCEMENT ca.order_id = order_id # Sets the conversion date and time if provided. Providing this value is # optional but recommended. unless conversion_date_time.nil? ca.gclid_date_time_pair = client.resource.gclid_date_time_pair do |pair| pair.conversion_date_time = conversion_date_time end end # Creates a user identifier using sample values for the user address. ca.user_identifiers << client.resource.user_identifier do |ui| ui.address_info = client.resource.offline_user_address_info do |info| # Certain fields must be hashed using SHA256 in order to handle # identifiers in a privacy-safe way, as described at # https://support.google.com/google-ads/answer/9888656. info.hashed_first_name = normalize_and_hash("Joanna") info.hashed_last_name = normalize_and_hash("Smith") info.hashed_street_address = normalize_and_hash("1600 Amphitheatre Pkwy") info.city = "Mountain View" info.state = "CA" info.postal_code = "94043" info.country_code = "US" end # Optional: Specifies the user identifier source. ui.user_identifier_source = :FIRST_PARTY end # Creates a user identifier using the hashed email address. ca.user_identifiers << client.resource.user_identifier do |ui| # Uses the normalize and hash method specifically for email addresses. ui.hashed_email = normalize_and_hash_email("dana@example.com") ui.user_identifier_source = :FIRST_PARTY end # Sets optional fields where a value was provided. unless user_agent.nil? # Sets the user agent. This should match the user agent of the request # that sent the original conversion so the conversion and its enhancement # are either both attributed as same-device or both attributed as # cross-device. ca.user_agent = user_agent end end response = client.service.conversion_adjustment_upload.upload_conversion_adjustments( customer_id: customer_id, conversion_adjustments: [enhancement], # Partial failure must be set to true. partial_failure: true, ) if response.partial_failure_error puts "Partial failure encountered: #{response.partial_failure_error.message}." else result = response.results.first puts "Uploaded conversion adjustment of #{result.conversion_action} for "\ "order ID #{result.order_id}." end end # Returns the result of normalizing and then hashing the string using the # provided digest. Private customer data must be hashed during upload, as # described at https://support.google.com/google-ads/answer/7474263. def normalize_and_hash(str) # Remove leading and trailing whitespace and ensure all letters are lowercase # before hasing. Digest::SHA256.hexdigest(str.strip.downcase) end # Returns the result of normalizing and hashing an email address. For this use # case, Google Ads requires removal of any '.' characters preceding 'gmail.com' # or 'googlemail.com'. def normalize_and_hash_email(email) email_parts = email.downcase.split("@") # Removes any '.' characters from the portion of the email address before the # domain if the domain is gmail.com or googlemail.com. if email_parts.last =~ /^(gmail|googlemail)\.com\s*/ email_parts[0] = email_parts[0].gsub('.', '') end normalize_and_hash(email_parts.join('@')) end if __FILE__ == $0 options = {} # The following parameter(s) should be provided to run the example. You can # either specify these by changing the INSERT_XXX_ID_HERE values below, or on # the command line. # # Parameters passed on the command line will override any parameters set in # code. # # Running the example with -h will print the command line usage. options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE' options[:conversion_action_id] = 'INSERT_CONVERSION_ACTION_ID_HERE' options[:order_id] = 'INSERT_ORDER_ID_HERE' OptionParser.new do |opts| opts.banner = format('Usage: %s [options]', File.basename(__FILE__)) opts.separator '' opts.separator 'Options:' opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v| options[:customer_id] = v end opts.on('-c', '--conversion-action-id CONVERSION-ACTION-ID', String, 'Conversion Action ID') do |v| options[:conversion_action_id] = v end opts.on('-o', '--order-id ORDER-ID', String, 'Order ID') do |v| options[:order_id] = v end opts.on('-d', '--conversion-date-time CONVERSION-DATE-TIME', String, 'The date and time of the conversion (should be after click time).'\ ' The format is "yyyy-mm-dd hh:mm:ss+|-hh:mm", '\ 'e.g. "2019-01-01 12:32:45-08:00".') do |v| options[:conversion_date_time] = v end opts.on('-u', '--user-agent USER-AGENT', String, 'User Agent') do |v| options[:user_agent] = v end opts.separator '' opts.separator 'Help:' opts.on_tail('-h', '--help', 'Show this message') do puts opts exit end end.parse! begin upload_conversion_enhancement( options.fetch(:customer_id).tr('-', ''), options.fetch(:conversion_action_id), options.fetch(:order_id), options[:conversion_date_time], options[:user_agent] ) rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e e.failure.errors.each do |error| STDERR.printf("Error with message: %s\n", error.message) error.location&.field_path_elements&.each do |field_path_element| STDERR.printf("\tOn field: %s\n", field_path_element.field_name) end error.error_code.to_h.each do |k, v| next if v == :UNSPECIFIED STDERR.printf("\tType: %s\n\tCode: %s\n", k, v) end end raise end end
Perl
#!/usr/bin/perl -w # # Copyright 2021, Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Adjusts an existing conversion by supplying user identifiers so Google can # enhance the conversion value. use strict; use warnings; use utf8; use FindBin qw($Bin); use lib "$Bin/../../lib"; use Google::Ads::GoogleAds::Client; use Google::Ads::GoogleAds::Utils::GoogleAdsHelper; use Google::Ads::GoogleAds::V14::Common::UserIdentifier; use Google::Ads::GoogleAds::V14::Common::OfflineUserAddressInfo; use Google::Ads::GoogleAds::V14::Enums::ConversionAdjustmentTypeEnum qw(ENHANCEMENT); use Google::Ads::GoogleAds::V14::Enums::UserIdentifierSourceEnum qw(FIRST_PARTY); use Google::Ads::GoogleAds::V14::Services::ConversionAdjustmentUploadService::ConversionAdjustment; use Google::Ads::GoogleAds::V14::Services::ConversionAdjustmentUploadService::GclidDateTimePair; use Google::Ads::GoogleAds::V14::Utils::ResourceNames; use Getopt::Long qw(:config auto_help); use Pod::Usage; use Cwd qw(abs_path); use Digest::SHA qw(sha256_hex); # The following parameter(s) should be provided to run the example. You can # either specify these by changing the INSERT_XXX_ID_HERE values below, or on # the command line. # # Parameters passed on the command line will override any parameters set in # code. # # Running the example with -h will print the command line usage. my $customer_id = "INSERT_CUSTOMER_ID_HERE"; my $conversion_action_id = "INSERT_CONVERSION_ACTION_ID_HERE"; my $order_id = "INSERT_ORDER_ID_HERE"; # Optional: Specify the conversion date/time and user agent. my $conversion_date_time = undef; my $user_agent = undef; sub upload_conversion_enhancement { my ( $api_client, $customer_id, $conversion_action_id, $order_id, $conversion_date_time, $user_agent ) = @_; # Construct the enhancement adjustment. my $enhancement = Google::Ads::GoogleAds::V14::Services::ConversionAdjustmentUploadService::ConversionAdjustment ->new({ conversionAction => Google::Ads::GoogleAds::V14::Utils::ResourceNames::conversion_action( $customer_id, $conversion_action_id ), adjustmentType => ENHANCEMENT, # Enhancements MUST use order ID instead of GCLID date/time pair. orderId => $order_id }); # Set the conversion date and time if provided. Providing this value is optional # but recommended. if (defined $conversion_date_time) { $enhancement->{gclidDateTimePair} = Google::Ads::GoogleAds::V14::Services::ConversionAdjustmentUploadService::GclidDateTimePair ->new({ conversionDateTime => $conversion_date_time }); } # Add user identifiers, hashing where required. # Create a user identifier using sample values for the user address. my $address_identifier = Google::Ads::GoogleAds::V14::Common::UserIdentifier->new({ addressInfo => Google::Ads::GoogleAds::V14::Common::OfflineUserAddressInfo->new({ hashedFirstName => normalize_and_hash("Dana"), hashedLastName => normalize_and_hash("Quinn"), hashedStreetAddress => normalize_and_hash("1600 Amphitheatre Pkwy"), city => "Mountain View", state => "CA", postalCode => "94043", countryCode => "US" } ), # Optional: Specify the user identifier source. userIdentifierSource => FIRST_PARTY }); # Create a user identifier using the hashed email address. my $email_identifier = Google::Ads::GoogleAds::V14::Common::UserIdentifier->new({ userIdentifierSource => FIRST_PARTY, # Use the normalize and hash method specifically for email addresses. hashedEmail => normalize_and_hash_email_address('dana@example.com')}); # Add the user identifiers to the enhancement adjustment. $enhancement->{userIdentifiers} = [$address_identifier, $email_identifier]; # Set optional fields where a value was provided. if (defined $user_agent) { # Set the user agent. This should match the user agent of the request that # sent the original conversion so the conversion and its enhancement are # either both attributed as same-device or both attributed as cross-device. $enhancement->{userAgent} = $user_agent; } # Upload the enhancement adjustment. Partial failure should always be set to true. my $response = $api_client->ConversionAdjustmentUploadService() ->upload_conversion_adjustments({ customerId => $customer_id, conversionAdjustments => [$enhancement], # Enable partial failure (must be true). partialFailure => "true" }); # Print any partial errors returned. if ($response->{partialFailureError}) { printf "Partial error encountered: '%s'.\n", $response->{partialFailureError}{message}; } else { # Print the result. my $result = $response->{results}[0]; printf "Uploaded conversion adjustment of '%s' for order ID '%s'.\n", $result->{conversionAction}, $result->{orderId}; } return 1; } # Normalizes and hashes a string value. # Private customer data must be hashed during upload, as described at # https://support.google.com/google-ads/answer/7474263. sub normalize_and_hash { my $value = shift; $value =~ s/^\s+|\s+$//g; return sha256_hex(lc $value); } # Returns the result of normalizing and hashing an email address. For this use # case, Google Ads requires removal of any '.' characters preceding 'gmail.com' # or 'googlemail.com'. sub normalize_and_hash_email_address { my $email_address = shift; my $normalized_email = lc $email_address; my @email_parts = split('@', $normalized_email); if (scalar @email_parts > 1 && $email_parts[1] =~ /^(gmail|googlemail)\.com\s*/) { # Remove any '.' characters from the portion of the email address before the # domain if the domain is 'gmail.com' or 'googlemail.com'. $email_parts[0] =~ s/\.//g; $normalized_email = sprintf '%s@%s', $email_parts[0], $email_parts[1]; } return normalize_and_hash($normalized_email); } # Don't run the example if the file is being included. if (abs_path($0) ne abs_path(__FILE__)) { return 1; } # Get Google Ads Client, credentials will be read from ~/googleads.properties. my $api_client = Google::Ads::GoogleAds::Client->new(); # By default examples are set to die on any server returned fault. $api_client->set_die_on_faults(1); # Parameters passed on the command line will override any parameters set in code. GetOptions( "customer_id=s" => \$customer_id, "conversion_action_id=i" => \$conversion_action_id, "order_id=s" => \$order_id, "conversion_date_time=s" => \$conversion_date_time, "user_agent=s" => \$user_agent ); # Print the help message if the parameters are not initialized in the code nor # in the command line. pod2usage(2) if not check_params($customer_id, $conversion_action_id, $order_id); # Call the example. upload_conversion_enhancement($api_client, $customer_id =~ s/-//gr, $conversion_action_id, $order_id, $conversion_date_time, $user_agent); =pod =head1 NAME upload_conversion_enhancement =head1 DESCRIPTION Adjusts an existing conversion by supplying user identifiers so Google can enhance the conversion value. =head1 SYNOPSIS upload_conversion_enhancement.pl [options] -help Show the help message. -customer_id The Google Ads customer ID. -conversion_action_id The conversion action ID associated with this conversion. -order_id The unique order ID (transaction ID) of the conversion. -conversion_date_time [optional] The date time at which the conversion with the specified order ID occurred. Must be after the click time, and must include the time zone offset. The format is "yyyy-mm-dd hh:mm:ss+|-hh:mm", e.g. "2019-01-01 12:32:45-08:00". Setting this field is optional, but recommended. -user_agent [optional] The HTTP user agent of the conversion. =cut