오프라인 전환 관리

Google Ads API를 사용하여 오프라인 전환을 Google Ads로 가져와 전화 또는 영업사원을 통해 오프라인에서 판매로 이어진 광고를 추적할 수 있습니다.

전환 데이터 가져오기의 이점을 최대한 활용하려면 GCLID 및 사용자 제공 데이터를 활용하여 내구성과 실적을 극대화하는 리드 확보용 향상된 전환을 구현하는 것이 좋습니다.

향상된 전환

향상된 전환을 사용하면 이메일 주소, 이름, 집 주소, 전화번호와 같은 퍼스트 파티 전환 데이터로 전환을 보완하여 전환 측정의 정확성을 개선할 수 있습니다.

향상된 전환에는 두 가지 유형이 있습니다. 자세한 내용은 향상된 전환 정보 고객센터 도움말을 참고하세요.

다음 섹션에서는 리드 확보용 향상된 전환이라고도 하는 오프라인 전환을 개선하는 방법을 설명합니다.

리드 확보용 향상된 전환이란 무엇인가요?

리드 확보용 향상된 전환은 오프라인 전환 가져오기의 업그레이드 버전으로, 이메일 주소와 같은 사용자 제공 데이터를 사용하여 가져온 오프라인 전환 데이터를 보완하여 정확성과 입찰 실적을 개선합니다. 오프라인 전환을 가져올 때 제공된 해싱된 고객 데이터는 웹사이트에서 수집된 동일한 데이터(예: 리드 양식) 및 광고에 참여한 로그인한 고객과 매칭하여 Google Ads 캠페인의 기여도를 분석하는 데 다시 사용됩니다. 리드 확보용 향상된 전환의 작동 방식에 관한 자세한 내용은 리드 확보용 향상된 전환에 대한 정보 도움말을 참고하세요.

Google 태그를 사용하여 웹페이지에서 양식 제출 이벤트를 추적하는지에 따라 리드 확보용 향상된 전환을 구현하는 방법에는 두 가지가 있습니다. 최상의 성능과 내구성을 위해 리드 확보를 위한 향상된 전환에 Google 태그를 사용하는 것이 좋습니다.

  • 처음부터 시작하는 경우 요구사항 섹션부터 시작하세요.
  • 이미 오프라인 전환 가져오기를 설정했고 리드 확보용 향상된 전환으로 업그레이드하려면 태그 지정 구성 섹션부터 시작하는 것이 좋습니다.
  • 이미 Google 태그를 설정했거나 Google 태그를 사용하지 않을 계획이며 Google Ads API 통합 작업을 시작하는 경우 API 구현 섹션으로 이동하세요.
  • 사용자 제공 데이터를 가져올 수 없거나 전환에 외부 기여 분석을 사용해야 하는 경우 기존 오프라인 전환 가져오기 가이드를 참고하세요.

기본 요건

먼저 시작하기 섹션의 단계를 완료했는지 확인하세요.

리드 확보용 향상된 전환을 사용하려면 리드 확보용 향상된 전환을 선택하고 고객 데이터 약관에 동의해야 합니다. Google Ads 전환 고객에게 다음 쿼리를 실행하여 이러한 기본 요건이 이미 충족되었는지 확인할 수 있습니다.

SELECT
  customer.id,
  customer.conversion_tracking_setting.accepted_customer_data_terms,
  customer.conversion_tracking_setting.enhanced_conversions_for_leads_enabled
FROM customer

accepted_customer_data_terms 또는 enhanced_conversions_for_leads_enabledfalse인 경우 고객센터의 안내에 따라 기본 요건을 완료하세요.

태그 지정 구성

고객센터의 안내에 따라 리드 확보용 향상된 전환을 사용 설정하도록 Google 태그를 구성합니다. Google 태그 관리자로 리드 확보용 향상된 전환을 설정하려면 다음 안내를 따르세요.

API 구현

다음은 Google Ads API를 사용하여 리드 확보용 향상된 전환을 가져오는 전반적인 흐름입니다.

  1. 이메일 주소, 전화번호, 우편 주소와 같은 사용자 제공 데이터를 정규화하고 해싱합니다.

  2. 정규화되고 해싱된 사용자 제공 데이터로 ClickConversion 객체를 채웁니다.

  3. ConversionUploadService를 사용하여 Google Ads API에 ClickConversion 객체를 가져옵니다.

  4. 가져온 항목 검토하기

사용자 제공 데이터 정규화 및 해싱

개인 정보 보호를 위해 다음 데이터는 가져오기 전에 SHA-256을 사용하여 해싱해야 합니다.

  • 이메일 주소
  • 전화번호
  • 이름
  • 상세 주소

해싱 결과를 표준화하려면 이러한 값을 해싱하기 전에 다음을 실행해야 합니다.

  • 선행 및 후행 공백을 삭제합니다.
  • 텍스트를 소문자로 변환합니다.
  • 전화번호는 E164 표준에 따른 형식으로 작성합니다.
  • gmail.comgooglemail.com 이메일 주소의 도메인 이름 앞에 있는 모든 마침표 (.)를 삭제합니다.

다음 데이터는 해싱하지 마세요.

  • 국가
  • 도시
  • 우편번호

코드 예

이 예에서는 사용자 제공 데이터를 정규화하고 해싱하는 방법을 보여줍니다.

private String normalizeAndHash(MessageDigest digest, String s)
    throws UnsupportedEncodingException {
  // Normalizes by first converting all characters to lowercase, then trimming spaces.
  String normalized = s.toLowerCase();
  // Removes leading, trailing, and intermediate spaces.
  normalized = normalized.replaceAll("\\s+", "");
  // 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);
}
      
/// <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();
}
      
private static function normalizeAndHash(string $hashAlgorithm, string $value): string
{
    // Normalizes by first converting all characters to lowercase, then trimming spaces.
    $normalized = strtolower($value);
    // Removes leading, trailing, and intermediate spaces.
    $normalized = str_replace(' ', '', $normalized);
    return hash($hashAlgorithm, strtolower(trim($normalized)));
}

/**
 * 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);
}
      
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.strip().lower()
    email_parts = normalized_email.split("@")

    # Check that there are at least two segments
    if len(email_parts) > 1:
        # 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.
        if re.match(r"^(gmail|googlemail)\.com$", email_parts[1]):
            # 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()
      
# 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 hashing.
  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
      
sub normalize_and_hash {
  my $value = shift;

  # Removes leading, trailing, and intermediate spaces.
  $value =~ 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);
}
      

ClickConversion 객체 채우기

UploadClickConversionRequestClickConversion 객체 모음은 가져오려는 전환 집합을 나타냅니다. ClickConversion 객체를 만들 때는 다음 세부정보에 유의하세요.

gclid

GCLID는 사용자가 광고를 클릭하고 웹사이트로 이동할 때 URL 매개변수에서 캡처되는 클릭 식별자입니다.

user_identifiers

리드 확보용 향상된 전환을 사용하는 경우 user_identifiers 필드를 정규화되고 해싱된 사용자 제공 데이터로 채워야 합니다. 사용 가능한 사용자 식별자가 여러 개인 경우 각 식별자(최대 5개)에 대해 별도의 UserIdentifier를 만듭니다.

conversion_date_time

전환 날짜 및 시간입니다.

값에는 시간대가 지정되어야 하며 형식은 yyyy-mm-dd HH:mm:ss+|-HH:mm여야 합니다(예: 2022-01-01 19:32:45-05:00(일광 절약 시간 무시)).

시간대는 유효한 값이면 무엇이든 될 수 있습니다. 계정의 시간대와 일치할 필요는 없습니다. 하지만 가져온 전환 데이터를 Google Ads UI의 전환 데이터와 비교하려는 경우 전환 수가 일치하도록 Google Ads 계정과 동일한 시간대를 사용하는 것이 좋습니다. 자세한 내용과 예시는 고객센터를 참고하고 유효한 시간대 ID 목록은 코드 및 형식을 확인하세요.

conversion_action

오프라인 전환의 ConversionAction 리소스 이름입니다.

전환 액션의 typeUPLOAD_CLICKS여야 하며 클릭과 연결된 Google Ads 계정의 Google Ads 전환 고객에 있어야 합니다.

conversion_value

전환의 가치입니다.

currency_code

conversion_value통화 코드입니다.

consent

ClickConversion 객체의 consent 필드를 채우는 것이 매우 좋습니다. 설정하지 않으면 전환 기여도를 부여할 수 없습니다.

order_id

전환의 거래 ID라고도 합니다. 이 입력란은 선택사항이지만 조정 시 가져온 전환을 더 쉽게 참조할 수 있으므로 적극 권장됩니다. 가져오는 동안 설정하는 경우 조정 시 이를 사용해야 합니다. 거래 ID를 사용하여 중복 전환을 최소화하는 방법을 자세히 알아보려면 이 고객센터 도움말을 참고하세요.

custom_variables

맞춤 전환 변수의 값입니다.

Google Ads에서는 wbraid 또는 gbraid와 함께 맞춤 전환 변수를 지원하지 않습니다.

conversion_environment

이 전환이 기록된 환경을 나타냅니다. 예를 들면 APP 또는 WEB입니다.

session_attributes_encodedsession_attributes_key_value_pairs

세션 속성은 전환 기여 분석에 사용되는 집계된 식별자를 나타냅니다. 이는 클릭 식별자(예: GCLID 및 GBRAID) 및 리드 확보용 향상된 전환의 핵심인 사용자 제공 데이터와 함께 작동합니다. 세션 속성을 가져오는 방법에는 두 가지가 있습니다. 브라우저에서 JavaScript 코드로 생성된 인코딩된 토큰을 제공하거나 각 식별자에 개별 키-값 쌍을 제공하는 것입니다.

캠페인 실적을 극대화하려면 가능하면 모든 전환과 함께 클릭 식별자, 사용자 제공 데이터, 세션 속성을 가져오는 것이 좋습니다.

This example is not yet available in Java; you can take a look at the other languages.
    
if (!string.IsNullOrEmpty(sessionAttributesEncoded))
{
    clickConversion.SessionAttributesEncoded =
        ByteString.CopyFrom(sessionAttributesEncoded, Encoding.Unicode);
}
else if (!string.IsNullOrEmpty(sessionAttributes))
{
    IEnumerable<SessionAttributeKeyValuePair> parsedSessionAttributes =
        sessionAttributes.Split(';').Select(pair => {
            string[] split = pair.Split('=');
            return new SessionAttributeKeyValuePair()
            {
                SessionAttributeKey = split[0],
                SessionAttributeValue = split[1]
            };
        });

    clickConversion.SessionAttributesKeyValuePairs =
        new SessionAttributesKeyValuePairs();
    clickConversion.SessionAttributesKeyValuePairs.KeyValuePairs
        .AddRange(parsedSessionAttributes);
}
      
This example is not yet available in PHP; you can take a look at the other languages.
    
# Set one of the session_attributes_encoded or
# session_attributes_key_value_pairs fields if either are provided.
if session_attributes_encoded:
    click_conversion.session_attributes_encoded = session_attributes_encoded
elif session_attributes_dict:
    for key, value in session_attributes_dict.items():
        pair = client.get_type("SessionAttributeKeyValuePair")
        pair.session_attribute_key = key
        pair.session_attribute_value = value
        click_conversion.session_attributes_key_value_pairs.key_value_pairs.append(
            pair
        )
      
This example is not yet available in Ruby; you can take a look at the other languages.
    
# Set one of the session_attributes_encoded or session_attributes_key_value_pairs
# fields if either are provided.
if (defined $session_attributes_encoded) {
  $click_conversion->{sessionAttributesEncoded} = $session_attributes_encoded;
} elsif (defined $session_attributes_hash) {
  while (my ($key, $value) = each %$session_attributes_hash) {
    my $pair =
      Google::Ads::GoogleAds::V19::Services::ConversionUploadService::SessionAttributeKeyValuePair
      ->new({sessionAttributeKey => $key, sessionAttributeValue => $value});
    push @{$click_conversion->{sessionAttributesKeyValuePairs}{keyValuePairs}
    }, $pair;
  }
}
      

코드 예

이 예에서는 정규화되고 해싱된 사용자 제공 데이터를 ClickConversion 객체에 설정하는 방법을 보여줍니다.

// Creates an empty builder for constructing the click conversion.
ClickConversion.Builder clickConversionBuilder = ClickConversion.newBuilder();

// Extracts user email and phone from the raw data, normalizes and hashes it, then wraps it in
// UserIdentifier objects.
// Creates a separate UserIdentifier object for each. The data in this example is hardcoded, but
// in your application you might read the raw data from an input file.

// IMPORTANT: Since the identifier attribute of UserIdentifier
// (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier) is a
// oneof
// (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set only ONE of
// hashedEmail, hashedPhoneNumber, mobileId, thirdPartyUserId, or addressInfo. Setting more
// than one of these attributes on the same UserIdentifier will clear all the other members
// of the oneof. For example, the following code is INCORRECT and will result in a
// UserIdentifier with ONLY a hashedPhoneNumber.
//
// UserIdentifier incorrectlyPopulatedUserIdentifier =
//     UserIdentifier.newBuilder()
//         .setHashedEmail("...")
//         .setHashedPhoneNumber("...")
//         .build();

ImmutableMap.Builder<String, String> rawRecordBuilder =
    ImmutableMap.<String, String>builder()
        .put("email", "alex.2@example.com")
        // Phone number to be converted to E.164 format, with a leading '+' as required.
        .put("phone", "+1 800 5550102")
        // This example lets you put conversion details as arguments, but in reality you might
        // store this data alongside other user data, so we include it in this sample user
        // record.
        .put("conversionActionId", Long.toString(conversionActionId))
        .put("conversionDateTime", conversionDateTime)
        .put("conversionValue", Double.toString(conversionValue))
        .put("currencyCode", "USD");

// Adds entries for the optional fields.
if (orderId != null) {
  rawRecordBuilder.put("orderId", orderId);
}
if (gclid != null) {
  rawRecordBuilder.put("gclid", gclid);
}
if (adUserDataConsent != null) {
  rawRecordBuilder.put("adUserDataConsent", adUserDataConsent.name());
}
if (sessionAttributesEncoded != null) {
  rawRecordBuilder.put("sessionAttributesEncoded", sessionAttributesEncoded);
}
if (sessionAttributesMap != null) {
  rawRecordBuilder.put("sessionAttributesMap", sessionAttributesMap);
}

// Builds the map representing the record.
Map<String, String> rawRecord = rawRecordBuilder.build();

// 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");

// Creates a list for the user identifiers.
List<UserIdentifier> userIdentifiers = new ArrayList<>();

// Creates a user identifier using the hashed email address, using the normalize and hash method
// specifically for email addresses.
UserIdentifier emailIdentifier =
    UserIdentifier.newBuilder()
        // Optional: specify the user identifier source.
        .setUserIdentifierSource(UserIdentifierSource.FIRST_PARTY)
        // Uses the normalize and hash method specifically for email addresses.
        .setHashedEmail(normalizeAndHashEmailAddress(sha256Digest, rawRecord.get("email")))
        .build();
userIdentifiers.add(emailIdentifier);

// Creates a user identifier using normalized and hashed phone info.
UserIdentifier hashedPhoneNumberIdentifier =
    UserIdentifier.newBuilder()
        .setHashedPhoneNumber(normalizeAndHash(sha256Digest, rawRecord.get("phone")))
        .build();
// Adds the hashed phone number identifier to the UserData object's list.
userIdentifiers.add(hashedPhoneNumberIdentifier);

// Adds the user identifiers to the conversion.
clickConversionBuilder.addAllUserIdentifiers(userIdentifiers);
      
// Adds a user identifier using the hashed email address, using the normalize
// and hash method specifically for email addresses.
clickConversion.UserIdentifiers.Add(new UserIdentifier()
{
    HashedEmail = NormalizeAndHashEmailAddress("alex.2@example.com"),
    // Optional: Specifies the user identifier source.
    UserIdentifierSource = UserIdentifierSource.FirstParty
});

// Adds a user identifier using normalized and hashed phone info.
clickConversion.UserIdentifiers.Add(new UserIdentifier()
{
    HashedPhoneNumber = NormalizeAndHash("+1 800 5550102"),
    // Optional: Specifies the user identifier source.
    UserIdentifierSource = UserIdentifierSource.FirstParty
});

// Adds a user identifier with all the required mailing address elements.
clickConversion.UserIdentifiers.Add(new UserIdentifier()
{
    AddressInfo = new OfflineUserAddressInfo()
    {
        // FirstName and LastName must be normalized and hashed.
        HashedFirstName = NormalizeAndHash("Alex"),
        HashedLastName = NormalizeAndHash("Quinn"),
        // CountryCode and PostalCode are sent in plain text.
        CountryCode = "US",
        PostalCode = "94045"
    }
});
      
// Creates a click conversion with the specified attributes.
$clickConversion = new ClickConversion();

// Extract user email and phone from the raw data, normalize and hash it, then wrap it in
// UserIdentifier objects. Creates a separate UserIdentifier object for each.
// The data in this example is hardcoded, but in your application you might read the raw
// data from an input file.

// IMPORTANT: Since the identifier attribute of UserIdentifier
// (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier) is a
// oneof
// (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set only ONE
// of hashedEmail, hashedPhoneNumber, mobileId, thirdPartyUserId, or addressInfo. Setting
// more than one of these attributes on the same UserIdentifier will clear all the other
// members of the oneof. For example, the following code is INCORRECT and will result in a
// UserIdentifier with ONLY a hashedPhoneNumber.
//
// $incorrectlyPopulatedUserIdentifier = new UserIdentifier([
//    'hashed_email' => '...',
//    'hashed_phone_number' => '...'
// ]);

$rawRecord = [
    // Email address that includes a period (.) before the Gmail domain.
    'email' => 'alex.2@example.com',
    // Phone number to be converted to E.164 format, with a leading '+' as required.
    'phone' => '+1 800 5550102',
    // This example lets you input conversion details as arguments, but in reality you might
    // store this data alongside other user data, so we include it in this sample user
    // record.
    'orderId' => $orderId,
    'gclid' => $gclid,
    'conversionActionId' => $conversionActionId,
    'conversionDateTime' => $conversionDateTime,
    'conversionValue' => $conversionValue,
    'currencyCode' => 'USD',
    'adUserDataConsent' => $adUserDataConsent
];

// Creates a list for the user identifiers.
$userIdentifiers = [];

// 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";

// Creates a user identifier using the hashed email address, using the normalize and hash
// method specifically for email addresses.
$emailIdentifier = new UserIdentifier([
    // Uses the normalize and hash method specifically for email addresses.
    'hashed_email' => self::normalizeAndHashEmailAddress(
        $hashAlgorithm,
        $rawRecord['email']
    ),
    // Optional: Specifies the user identifier source.
    'user_identifier_source' => UserIdentifierSource::FIRST_PARTY
]);
$userIdentifiers[] = $emailIdentifier;

// Checks if the record has a phone number, and if so, adds a UserIdentifier for it.
if (array_key_exists('phone', $rawRecord)) {
    $hashedPhoneNumberIdentifier = new UserIdentifier([
        'hashed_phone_number' => self::normalizeAndHash(
            $hashAlgorithm,
            $rawRecord['phone'],
            true
        )
    ]);
    // Adds the hashed email identifier to the user identifiers list.
    $userIdentifiers[] = $hashedPhoneNumberIdentifier;
}

// Adds the user identifiers to the conversion.
$clickConversion->setUserIdentifiers($userIdentifiers);
      
# Extract user email and phone from the raw data, normalize and hash it,
# then wrap it in UserIdentifier objects. Create a separate UserIdentifier
# object for each. The data in this example is hardcoded, but in your
# application you might read the raw data from an input file.

# IMPORTANT: Since the identifier attribute of UserIdentifier
# (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)
# is a oneof
# (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must
# set only ONE of hashed_email, hashed_phone_number, mobile_id,
# third_party_user_id, or address_info. Setting more than one of these
# attributes on the same UserIdentifier will clear all the other members of
# the oneof. For example, the following code is INCORRECT and will result in
# a UserIdentifier with ONLY a hashed_phone_number:
#
# incorrectly_populated_user_identifier = client.get_type("UserIdentifier")
# incorrectly_populated_user_identifier.hashed_email = "...""
# incorrectly_populated_user_identifier.hashed_phone_number = "...""

raw_record = {
    # Email address that includes a period (.) before the Gmail domain.
    "email": "alex.2@example.com",
    # Phone number to be converted to E.164 format, with a leading '+' as
    # required.
    "phone": "+1 800 5550102",
    # This example lets you input conversion details as arguments,
    # but in reality you might store this data alongside other user data,
    # so we include it in this sample user record.
    "order_id": order_id,
    "gclid": gclid,
    "conversion_action_id": conversion_action_id,
    "conversion_date_time": conversion_date_time,
    "conversion_value": conversion_value,
    "currency_code": "USD",
    "ad_user_data_consent": ad_user_data_consent,
}

# Constructs the click conversion.
click_conversion = client.get_type("ClickConversion")
# Creates a user identifier using the hashed email address, using the
# normalize and hash method specifically for email addresses.
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(
    raw_record["email"]
)
# Adds the user identifier to the conversion.
click_conversion.user_identifiers.append(email_identifier)

# Checks if the record has a phone number, and if so, adds a UserIdentifier
# for it.
if raw_record.get("phone") is not None:
    phone_identifier = client.get_type("UserIdentifier")
    phone_identifier.hashed_phone_number = normalize_and_hash(
        raw_record["phone"]
    )
    # Adds the phone identifier to the conversion adjustment.
    click_conversion.user_identifiers.append(phone_identifier)
      
# Extract user email and phone from the raw data, normalize and hash it,
# then wrap it in UserIdentifier objects. Create a separate UserIdentifier
# object for each. The data in this example is hardcoded, but in your
# application you might read the raw data from an input file.

# IMPORTANT: Since the identifier attribute of UserIdentifier
# (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)
# is a oneof
# (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must
# set only ONE of hashed_email, hashed_phone_number, mobile_id,
# third_party_user_id, or address_info. Setting more than one of these
# attributes on the same UserIdentifier will clear all the other members of
# the oneof. For example, the following code is INCORRECT and will result in
# a UserIdentifier with ONLY a hashed_phone_number:
#
# incorrectly_populated_user_identifier.hashed_email = "...""
# incorrectly_populated_user_identifier.hashed_phone_number = "...""

raw_record = {
  # Email address that includes a period (.) before the Gmail domain.
  "email" => "alex.2@example.com",
  # Phone number to be converted to E.164 format, with a leading '+' as
  # required.
  "phone" => "+1 800 5550102",
  # This example lets you input conversion details as arguments,
  # but in reality you might store this data alongside other user data,
  # so we include it in this sample user record.
  "order_id" => order_id,
  "gclid" => gclid,
  "conversion_action_id" => conversion_action_id,
  "conversion_date_time" => conversion_date_time,
  "conversion_value" => conversion_value,
  "currency_code" => "USD",
  "ad_user_data_consent" => ad_user_data_consent,
  "session_attributes_encoded" => session_attributes_encoded,
  "session_attributes_hash" => session_attributes_hash
}

click_conversion = client.resource.click_conversion do |cc|
  cc.conversion_action = client.path.conversion_action(customer_id, conversion_action_id)
  cc.conversion_date_time = conversion_date_time
  cc.conversion_value = conversion_value.to_f
  cc.currency_code = 'USD'

  unless order_id.nil?
    cc.order_id = order_id
  end

  unless raw_record["gclid"].nil?
    cc.gclid = gclid
  end

  # Specifies whether user consent was obtained for the data you are
  # uploading. For more details, see:
  # https://www.google.com/about/company/user-consent-policy
  unless raw_record["ad_user_data_consent"].nil?
    cc.consent = client.resource.consent do |c|
      c.ad_user_data = ad_user_data_consent
    end
  end

  # Set one of the session_attributes_encoded or
  # session_attributes_key_value_pairs fields if either are provided.
  if session_attributes_encoded != nil
    cc.class.module_eval { attr_accessor :session_attributes_encoded}
    cc.session_attributes_encoded = session_attributes_encoded
  elsif session_attributes_hash != nil
    # Add new attribute to click conversion object
    cc.class.module_eval { attr_accessor :session_attributes_key_value_pairs}
    cc.session_attributes_key_value_pairs = ::Google::Ads::GoogleAds::V19::Services::SessionAttributesKeyValuePairs.new

    # Loop thru inputted session_attributes_hash to populate session_attributes_key_value_pairs
    session_attributes_hash.each do |key, value|
      pair = ::Google::Ads::GoogleAds::V19::Services::SessionAttributeKeyValuePair.new
      pair.session_attribute_key = key
      pair.session_attribute_value = value
      cc.session_attributes_key_value_pairs.key_value_pairs << pair
    end
  end    

  # Creates a user identifier using the hashed email address, using the
  # normalize and hash method specifically for email addresses.
  # If using a phone number, use the normalize_and_hash method instead.
  cc.user_identifiers << client.resource.user_identifier do |ui|
    ui.hashed_email = normalize_and_hash_email(raw_record["email"])
    # Optional: Specifies the user identifier source.
    ui.user_identifier_source = :FIRST_PARTY
  end

  # Checks if the record has a phone number, and if so, adds a UserIdentifier
  # for it.
  unless raw_record["phone"].nil?
    cc.user_identifiers << client.resource.user_identifier do |ui|
      ui.hashed_phone_number = normalize_and_hash(raw_record["phone"])
    end
  end
end
      
# Create an empty click conversion.
my $click_conversion =
  Google::Ads::GoogleAds::V19::Services::ConversionUploadService::ClickConversion
  ->new({});

# Extract user email and phone from the raw data, normalize and hash it,
# then wrap it in UserIdentifier objects. Create a separate UserIdentifier
# object for each.
# The data in this example is hardcoded, but in your application
# you might read the raw data from an input file.
#
# IMPORTANT: Since the identifier attribute of UserIdentifier
# (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)
# is a oneof
# (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set
# only ONE of hashed_email, hashed_phone_number, mobile_id, third_party_user_id,
# or address-info. Setting more than one of these attributes on the same UserIdentifier
# will clear all the other members of the oneof. For example, the following code is
# INCORRECT and will result in a UserIdentifier with ONLY a hashed_phone_number:
#
# my $incorrect_user_identifier = Google::Ads::GoogleAds::V19::Common::UserIdentifier->new({
#   hashedEmail => '...',
#   hashedPhoneNumber => '...',
# });
my $raw_record = {
  # Email address that includes a period (.) before the Gmail domain.
  email => 'alex.2@example.com',
  # Phone number to be converted to E.164 format, with a leading '+' as
  # required.
  phone => '+1 800 5550102',
  # This example lets you input conversion details as arguments,
  # but in reality you might store this data alongside other user data,
  # so we include it in this sample user record.
  orderId            => $order_id,
  gclid              => $gclid,
  conversionActionId => $conversion_action_id,
  conversionDateTime => $conversion_date_time,
  conversionValue    => $conversion_value,
  currencyCode       => "USD",
  adUserDataConsent  => $ad_user_data_consent
};
my $user_identifiers = [];

# Create a user identifier using the hashed email address, using the normalize
# and hash method specifically for email addresses.
my $hashed_email = normalize_and_hash_email_address($raw_record->{email});
push(
  @$user_identifiers,
  Google::Ads::GoogleAds::V19::Common::UserIdentifier->new({
      hashedEmail => $hashed_email,
      # Optional: Specify the user identifier source.
      userIdentifierSource => FIRST_PARTY
    }));

# Create a user identifier using normalized and hashed phone info.
my $hashed_phone = normalize_and_hash($raw_record->{phone});
push(
  @$user_identifiers,
  Google::Ads::GoogleAds::V19::Common::UserIdentifier->new({
      hashedPhone => $hashed_phone,
      # Optional: Specify the user identifier source.
      userIdentifierSource => FIRST_PARTY
    }));

# Add the user identifiers to the conversion.
$click_conversion->{userIdentifiers} = $user_identifiers;
      

이 예에서는 ClickConversion 객체에 필요한 다른 필드를 설정하는 방법을 보여줍니다.

// Adds details of the conversion.
clickConversionBuilder.setConversionAction(
    ResourceNames.conversionAction(
        customerId, Long.parseLong(rawRecord.get("conversionActionId"))));
clickConversionBuilder.setConversionDateTime(rawRecord.get("conversionDateTime"));
clickConversionBuilder.setConversionValue(Double.parseDouble(rawRecord.get("conversionValue")));
clickConversionBuilder.setCurrencyCode(rawRecord.get("currencyCode"));

// Sets the order ID if provided.
if (rawRecord.containsKey("orderId")) {
  clickConversionBuilder.setOrderId(rawRecord.get("orderId"));
}

// Sets the Google click ID (gclid) if provided.
if (rawRecord.containsKey("gclid")) {
  clickConversionBuilder.setGclid(rawRecord.get("gclid"));
}

// Sets the consent information, if provided.
if (rawRecord.containsKey("adUserDataConsent")) {
  // Specifies whether user consent was obtained for the data you are uploading. See
  // https://www.google.com/about/company/user-consent-policy for details.
  clickConversionBuilder.setConsent(
      Consent.newBuilder()
          .setAdUserData(ConsentStatus.valueOf(rawRecord.get("adUserDataConsent"))));
}

// Sets one of the sessionAttributesEncoded or sessionAttributesKeyValuePairs if either is
// provided.
if (rawRecord.containsKey("sessionAttributesEncoded")) {
  clickConversionBuilder.setSessionAttributesEncoded(
      ByteString.copyFromUtf8(rawRecord.get("sessionAttributesEncoded")));
} else if (rawRecord.containsKey("sessionAttributesMap")) {
  List<String> pairings =
      Arrays.stream(rawRecord.get("sessionAttributesMap").split(" "))
          .map(String::trim)
          .collect(Collectors.toList());
  SessionAttributesKeyValuePairs.Builder sessionAttributePairs =
      SessionAttributesKeyValuePairs.newBuilder();
  for (String pair : pairings) {
    String[] parts = pair.split("=", 2);
    if (parts.length != 2) {
      throw new IllegalArgumentException(
          "Failed to read the sessionAttributesMap. SessionAttributesMap must use a "
              + "space-delimited list of session attribute key value pairs. Each pair should be"
              + " separated by an equal sign, for example: 'gad_campaignid=12345 gad_source=1'");
    }
    sessionAttributePairs.addKeyValuePairs(
        SessionAttributeKeyValuePair.newBuilder()
            .setSessionAttributeKey(parts[0])
            .setSessionAttributeValue(parts[1])
            .build());
  }
  clickConversionBuilder.setSessionAttributesKeyValuePairs(sessionAttributePairs.build());
}

// Calls build to build the conversion.
ClickConversion clickConversion = clickConversionBuilder.build();
      
// Adds details of the conversion.
clickConversion.ConversionAction =
    ResourceNames.ConversionAction(customerId, conversionActionId);
clickConversion.ConversionDateTime = conversionDateTime;
clickConversion.ConversionValue = conversionValue;
clickConversion.CurrencyCode = "USD";

// Sets the order ID if provided.
if (!string.IsNullOrEmpty(orderId))
{
    clickConversion.OrderId = orderId;
}

// Sets the Google click ID (gclid) if provided.
if (!string.IsNullOrEmpty(gclid))
{
    clickConversion.Gclid = gclid;
}

if (!string.IsNullOrEmpty(sessionAttributesEncoded))
{
    clickConversion.SessionAttributesEncoded =
        ByteString.CopyFrom(sessionAttributesEncoded, Encoding.Unicode);
}
else if (!string.IsNullOrEmpty(sessionAttributes))
{
    IEnumerable<SessionAttributeKeyValuePair> parsedSessionAttributes =
        sessionAttributes.Split(';').Select(pair => {
            string[] split = pair.Split('=');
            return new SessionAttributeKeyValuePair()
            {
                SessionAttributeKey = split[0],
                SessionAttributeValue = split[1]
            };
        });

    clickConversion.SessionAttributesKeyValuePairs =
        new SessionAttributesKeyValuePairs();
    clickConversion.SessionAttributesKeyValuePairs.KeyValuePairs
        .AddRange(parsedSessionAttributes);
}

      
// Adds details of the conversion.
$clickConversion->setConversionAction(
    ResourceNames::forConversionAction($customerId, $rawRecord['conversionActionId'])
);
$clickConversion->setConversionDateTime($rawRecord['conversionDateTime']);
$clickConversion->setConversionValue($rawRecord['conversionValue']);
$clickConversion->setCurrencyCode($rawRecord['currencyCode']);

// Sets the order ID if provided.
if (!empty($rawRecord['orderId'])) {
    $clickConversion->setOrderId($rawRecord['orderId']);
}

// Sets the Google click ID (gclid) if provided.
if (!empty($rawRecord['gclid'])) {
    $clickConversion->setGclid($rawRecord['gclid']);
}

// Sets the ad user data consent if provided.
if (!empty($rawRecord['adUserDataConsent'])) {
    // Specifies whether user consent was obtained for the data you are uploading. See
    // https://www.google.com/about/company/user-consent-policy for details.
    $clickConversion->setConsent(
        new Consent(['ad_user_data' => $rawRecord['adUserDataConsent']])
    );
}
      
# Add details of the conversion.
# Gets the conversion action resource name.
conversion_action_service = client.get_service("ConversionActionService")
click_conversion.conversion_action = (
    conversion_action_service.conversion_action_path(
        customer_id, raw_record["conversion_action_id"]
    )
)
click_conversion.conversion_date_time = raw_record["conversion_date_time"]
click_conversion.conversion_value = raw_record["conversion_value"]
click_conversion.currency_code = raw_record["currency_code"]

# Sets the order ID if provided.
if raw_record.get("order_id"):
    click_conversion.order_id = raw_record["order_id"]

# Sets the gclid if provided.
if raw_record.get("gclid"):
    click_conversion.gclid = raw_record["gclid"]

# Specifies whether user consent was obtained for the data you are
# uploading. For more details, see:
# https://www.google.com/about/company/user-consent-policy
if raw_record["ad_user_data_consent"]:
    click_conversion.consent.ad_user_data = client.enums.ConsentStatusEnum[
        raw_record["ad_user_data_consent"]
    ]

# Set one of the session_attributes_encoded or
# session_attributes_key_value_pairs fields if either are provided.
if session_attributes_encoded:
    click_conversion.session_attributes_encoded = session_attributes_encoded
elif session_attributes_dict:
    for key, value in session_attributes_dict.items():
        pair = client.get_type("SessionAttributeKeyValuePair")
        pair.session_attribute_key = key
        pair.session_attribute_value = value
        click_conversion.session_attributes_key_value_pairs.key_value_pairs.append(
            pair
        )
      
cc.conversion_action = client.path.conversion_action(customer_id, conversion_action_id)
cc.conversion_date_time = conversion_date_time
cc.conversion_value = conversion_value.to_f
cc.currency_code = 'USD'

unless order_id.nil?
  cc.order_id = order_id
end

unless raw_record["gclid"].nil?
  cc.gclid = gclid
end

# Specifies whether user consent was obtained for the data you are
# uploading. For more details, see:
# https://www.google.com/about/company/user-consent-policy
unless raw_record["ad_user_data_consent"].nil?
  cc.consent = client.resource.consent do |c|
    c.ad_user_data = ad_user_data_consent
  end
end

# Set one of the session_attributes_encoded or
# session_attributes_key_value_pairs fields if either are provided.
if session_attributes_encoded != nil
  cc.class.module_eval { attr_accessor :session_attributes_encoded}
  cc.session_attributes_encoded = session_attributes_encoded
elsif session_attributes_hash != nil
  # Add new attribute to click conversion object
  cc.class.module_eval { attr_accessor :session_attributes_key_value_pairs}
  cc.session_attributes_key_value_pairs = ::Google::Ads::GoogleAds::V19::Services::SessionAttributesKeyValuePairs.new

  # Loop thru inputted session_attributes_hash to populate session_attributes_key_value_pairs
  session_attributes_hash.each do |key, value|
    pair = ::Google::Ads::GoogleAds::V19::Services::SessionAttributeKeyValuePair.new
    pair.session_attribute_key = key
    pair.session_attribute_value = value
    cc.session_attributes_key_value_pairs.key_value_pairs << pair
  end
end    
      
# Add details of the conversion.
$click_conversion->{conversionAction} =
  Google::Ads::GoogleAds::V19::Utils::ResourceNames::conversion_action(
  $customer_id, $raw_record->{conversionActionId});
$click_conversion->{conversionDateTime} = $raw_record->{conversionDateTime};
$click_conversion->{conversionValue}    = $raw_record->{conversionValue};
$click_conversion->{currencyCode}       = $raw_record->{currencyCode};

# Set the order ID if provided.
if (defined $raw_record->{orderId}) {
  $click_conversion->{orderId} = $raw_record->{orderId};
}

# Set the Google click ID (gclid) if provided.
if (defined $raw_record->{gclid}) {
  $click_conversion->{gclid} = $raw_record->{gclid};
}

# Set the consent information, if provided.
if (defined $raw_record->{adUserDataConsent}) {
  $click_conversion->{consent} =
    Google::Ads::GoogleAds::V19::Common::Consent->new({
      adUserData => $raw_record->{adUserDataConsent}});
}

# Set one of the session_attributes_encoded or session_attributes_key_value_pairs
# fields if either are provided.
if (defined $session_attributes_encoded) {
  $click_conversion->{sessionAttributesEncoded} = $session_attributes_encoded;
} elsif (defined $session_attributes_hash) {
  while (my ($key, $value) = each %$session_attributes_hash) {
    my $pair =
      Google::Ads::GoogleAds::V19::Services::ConversionUploadService::SessionAttributeKeyValuePair
      ->new({sessionAttributeKey => $key, sessionAttributeValue => $value});
    push @{$click_conversion->{sessionAttributesKeyValuePairs}{keyValuePairs}
    }, $pair;
  }
}
      

요청 구성

ClickConversion 객체가 구성되고 UploadClickConversionRequest 객체의 conversions 필드에 추가되면 다음 필드를 설정하고 요청을 ConversionUploadServiceUploadClickConversions 메서드에 전달합니다.

customer_id
클릭 소스인 계정의 Google Ads 전환 고객으로 설정합니다. 어떤 계정이 올바른 계정인지 모르겠다면 시작하기 섹션의 샘플 쿼리에서 customer.conversion_tracking_setting.google_ads_conversion_customer 필드를 참고하세요.
job_id

가져오기 요청을 오프라인 데이터 진단의 작업별 정보와 연결하는 메커니즘을 제공합니다.

이 필드를 설정하지 않으면 Google Ads API는 각 요청에 [2^31, 2^63) 범위의 고유한 값을 할당합니다. 여러 요청을 단일 논리 작업으로 그룹화하려면 작업의 모든 요청에서 이 필드를 [0, 2^31) 범위의 동일한 값으로 설정하세요.

응답job_id에는 값을 지정했는지 또는 Google Ads API가 값을 할당했는지와 관계없이 요청의 작업 ID가 포함됩니다.

partial_failure

전환을 가져올 때 이 필드는 true로 설정해야 합니다. 응답을 처리할 때는 부분 실패 가이드라인을 따르세요.

요청 가져오기

ClickConversion 객체를 채우고 요청을 구성한 후 가져오기를 제출할 수 있습니다.

// Creates the conversion upload service client.
try (ConversionUploadServiceClient conversionUploadServiceClient =
    googleAdsClient.getLatestVersion().createConversionUploadServiceClient()) {
  // Uploads the click conversion. Partial failure should always be set to true.

  // NOTE: This request contains a single conversion as a demonstration.  However, if you have
  // multiple conversions to upload, it's best to upload multiple conversions per request
  // instead of sending a separate request per conversion. See the following for per-request
  // limits:
  // https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_service
  UploadClickConversionsResponse response =
      conversionUploadServiceClient.uploadClickConversions(
          UploadClickConversionsRequest.newBuilder()
              .setCustomerId(Long.toString(customerId))
              .addConversions(clickConversion)
              // Enables partial failure (must be true).
              .setPartialFailure(true)
              .build());
      
// Uploads the click conversion. Partial failure should always be set to true.
// NOTE: This request contains a single conversion as a demonstration.
// However, if you have multiple conversions to upload, it's best to upload multiple
// conversions per request instead of sending a separate request per conversion.
// See the following for per-request limits:
// https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload
UploadClickConversionsResponse response =
    conversionUploadService.UploadClickConversions(
        new UploadClickConversionsRequest()
        {
            CustomerId = customerId.ToString(),
            Conversions = { clickConversion },
            // Enables partial failure (must be true).
            PartialFailure = true
        });

      
// Issues a request to upload the click conversion.
$conversionUploadServiceClient = $googleAdsClient->getConversionUploadServiceClient();
// NOTE: This request contains a single conversion as a demonstration.  However, if you have
// multiple conversions to upload, it's best to upload multiple conversions per request
// instead of sending a separate request per conversion. See the following for per-request
// limits:
// https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_service
$response = $conversionUploadServiceClient->uploadClickConversions(
    // Enables partial failure (must be true).
    UploadClickConversionsRequest::build($customerId, [$clickConversion], true)
);
      
# Creates the conversion upload service client.
conversion_upload_service = client.get_service("ConversionUploadService")
# Uploads the click conversion. Partial failure should always be set to
# True.
# NOTE: This request only uploads a single conversion, but if you have
# multiple conversions to upload, it's most efficient to upload them in a
# single request. See the following for per-request limits for reference:
# https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_service
response = conversion_upload_service.upload_click_conversions(
    customer_id=customer_id,
    conversions=[click_conversion],
    # Enables partial failure (must be true).
    partial_failure=True,
)
      
response = client.service.conversion_upload.upload_click_conversions(
  customer_id: customer_id,
  conversions: [click_conversion],
  # Partial failure must be 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 click conversion that happened at #{result.conversion_date_time} " \
    "to #{result.conversion_action}."
end
      
# Upload the click conversion. Partial failure should always be set to true.
#
# NOTE: This request contains a single conversion as a demonstration.
# However, if you have multiple conversions to upload, it's best to
# upload multiple conversions per request instead of sending a separate
# request per conversion. See the following for per-request limits:
# https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_service
my $response =
  $api_client->ConversionUploadService()->upload_click_conversions({
    customerId  => $customer_id,
    conversions => [$click_conversion],
    # Enable partial failure (must be true).
    partialFailure => "true"
  });
      

가져온 항목 검토하기

리드 확보용 향상된 전환 진단 보고서를 사용하여 최근 가져온 데이터의 전반적인 상태를 검토합니다.

가져온 전환은 가져오기 요청 날짜 또는 ClickConversionconversion_date_time 날짜가 아니라 원래 클릭의 노출 날짜의 보고서에 반영됩니다.

가져온 전환 통계가 마지막 클릭 기여 분석을 위해 Google Ads 계정에 표시되기까지 최대 3시간이 걸립니다. 다른 검색 기여 분석 모델의 경우 3시간 넘게 걸릴 수 있습니다. 자세한 내용은 데이터 업데이트 가이드를 참고하세요.

캠페인의 전환 측정항목을 보고할 때는 사용자 인터페이스 측정항목 매핑을 참고하여 Google Ads UI 측정항목을 Google Ads API 보고 필드와 연결하세요. conversion_action 리소스를 쿼리하여 특정 전환 액션의 총 전환수 및 총 전환 가치를 확인할 수도 있습니다.

권장사항

리드 확보를 위한 향상된 전환을 구현할 때는 다음 권장사항을 염두에 두세요.

완전성 여부와 관계없이 모든 전환 데이터 전송

완전하고 정확한 전환 보고를 하려면 Google Ads에서 발생하지 않았을 수도 있는 전환 이벤트를 포함하여 사용 가능한 모든 오프라인 전환 이벤트를 가져오세요. 사용자 제공 데이터만 포함된 전환도 유용하며 Google Ads 캠페인 최적화에 긍정적인 영향을 줄 수 있습니다.

전환에 order_id를 할당하는 경우 포함하는 것이 좋습니다. 전환의 GCLID가 있는 경우 성능을 개선하기 위해 user_identifiers와 함께 전송하는 것이 좋습니다. 또한 전환에 UserIdentifier가 두 개 이상 있는 경우 일치 가능성을 높이기 위해 ClickConversion 객체에 모두 포함합니다.

단일 요청으로 여러 전환 일괄 처리

가져올 전환이 여러 개인 경우 전환별로 가져오기 요청을 전송하는 대신 전환을 하나의 UploadClickConversionsRequest로 일괄 처리합니다.

요청당 전환 수 한도는 할당량 가이드를 참고하세요.

오프라인 데이터 진단이 동일한 로직 작업 아래에 요청 집합을 그룹화하도록 하려면 모든 요청의 job_id를 동일한 값으로 설정하세요. 이는 여러 요청을 사용하여 대량의 전환을 가져오는 단일 작업 또는 프로세스가 있는 경우에 유용할 수 있습니다. 각 요청에서 job_id를 동일한 값으로 설정하면 job_summaries에서 작업의 단일 항목을 검색할 수 있습니다. 대신 Google Ads API가 각 요청의 job_id에 시스템 생성 값을 할당하도록 허용하면 job_summaries에 각 요청에 대한 별도의 항목이 포함되므로 작업의 전반적인 상태를 분석하기가 더 어려워질 수 있습니다.

외부 기여 분석 데이터를 사용하지 않음

리드 확보용 향상된 전환을 사용할 때는 ClickConversionexternal_attribution_data를 설정하거나 외부 기여 분석 모델을 사용하는 conversion_action를 지정하지 마세요. Google Ads에서는 사용자 제공 데이터를 사용하여 가져올 때 외부 기여 분석 전환을 지원하지 않습니다.

문제 해결

오프라인 데이터 진단은 지속적으로 가져오기의 전반적인 상태를 검토할 수 있는 단일 리소스를 제공합니다. 그러나 구현 중에 이 섹션의 정보를 사용하여 응답의 partial_failure_error 필드에 보고된 오류를 조사할 수 있습니다.

전환 액션을 가져올 때 가장 일반적인 오류는 USER_PERMISSION_DENIED와 같은 승인 오류입니다. 요청의 고객 ID가 전환 액션을 소유한 Google Ads 전환 고객으로 설정되어 있는지 다시 확인합니다. 자세한 내용은 승인 가이드를 참고하고 다양한 오류를 디버그하는 방법에 관한 도움말은 일반 오류 가이드를 참고하세요.

일반적인 오류 디버그

오류
NO_CONVERSION_ACTION_FOUND

지정된 전환 액션이 사용 설정되지 않았거나 요청의 `client_id` 필드에 지정된 고객 계정에서 액세스할 수 없습니다. 업로드의 전환 액션이 사용 설정되어 있고 업로드 요청을 전송하는 고객이 소유하고 있는지 확인합니다.

요청의 GCLID가 요청에 지정된 전환 액션에 액세스할 수 없는 고객 계정에 속하는 경우에도 이 오류가 발생할 수 있습니다. click_view 리소스를 사용하여 GCLID가 클라이언트 계정에 속하는지 확인할 수 있습니다. click_view.gclidsegments.date로 필터링하는 쿼리를 제출하면 됩니다. 여기서 날짜는 클릭이 발생한 날짜입니다.

INVALID_CONVERSION_ACTION_TYPE 지정된 전환 액션의 유형이 리드 확보용 향상된 전환에 유효하지 않습니다. 업로드 요청에 지정된 ConversionAction의 유형이 UPLOAD_CLICKS인지 확인합니다.
CUSTOMER_NOT_ENABLED_ENHANCED_CONVERSIONS_FOR_LEADS 전환 설정에서 리드 확보용 향상된 전환을 사용 설정했는지 확인합니다. 기본 요건 가이드에서 이에 관한 안내를 확인하세요.
DUPLICATE_ORDER_ID 가져온 이벤트에 주문 ID가 동일한 전환이 여러 개 포함되어 있어 처리되지 않았습니다. 주문 ID가 고유한지 확인한 후 다시 시도해 주세요.
CLICK_NOT_FOUND 제공된 사용자 식별자와 일치하는 클릭이 없습니다. Google Ads API는 UploadClickConversionsRequest에서 debug_enabledtrue인 경우에만 이 오류를 반환합니다.

전환에서 이 경고가 발생하면 Google Ads API는 오프라인 데이터 진단successful_event_count에 이를 포함합니다. Google Ads API에는 이 경고의 빈도를 모니터링할 수 있도록 alerts 컬렉션에 CLICK_NOT_FOUND 항목이 포함되어 있습니다.

클릭이 Google Ads 캠페인에서 발생하지 않은 경우 이 오류가 발생할 수 있습니다. 예를 들어 SA360 또는 DV360에서 가져올 수 있습니다. 그 밖의 가능한 원인은 다음과 같습니다.

업로드 고객이 Google Ads 전환 고객과 다른 드문 경우 이 오류는 업로드 고객이 고객 데이터 약관에 동의했지만 게재 고객이 동의하지 않았음을 의미할 수 있습니다.

customer 리소스를 쿼리하고 customer.offline_conversion_tracking_info.accepted_customer_data_terms 필드를 확인하여 계정이 고객 데이터 약관에 동의했는지 확인할 수 있습니다.