프로스펙팅(prospecting) 캠페인(신규 고객 유치)을 최적화하기 위해, AppLovin은 귀사의 주문 내역 데이터를 AppLovin에 제공할 것을 강력히 권장합니다. 이를 통해 AppLovin은 기존 고객의 정확한 모델을 구축하여 가치가 높은 새로운 사용자를 찾을 수 있습니다.
Shopify 사용자로서 AppLovin Shopify 앱을 설치하고 이를 Ads Manager 계정에 연결하셨다면, 모든 준비가 완료된 것입니다!
그렇지 않은 경우, 아래의 요구 사항에 따라 이 데이터를 CSV 형식으로 업로드하십시오.
AppLovin에 고객 데이터를 제공하는 것은 필요한 고지 및 동의를 포함하여 귀사 자체의 개인정보 보호 준수 요구 사항을 따릅니다.
transaction_id를 기준으로 주문을 자동으로 중복 제거합니다.
AppLovin은 시스템에 이미 존재하는 transaction_id를 가진 데이터 행은 무시합니다.
단일 파일에 중복된 transaction_id를 가진 행이 존재하는 경우, AppLovin은 event_timestamp가 가장 빠른 행을 유지하고 나머지는 버립니다.CSV 파일에는 아래에 나열된 필수 헤더가 모두 포함되어야 하지만, 특정 필드는 선택적으로 채울 수 있습니다.
열 순서는 상관없지만, 헤더 이름은 정확히 일치해야 합니다.
이벤트 기간: AppLovin은 사용 가능한 모든 주문 내역을 업로드할 것을 강력히 권장합니다.
| 필드 | 값 필수 여부? | 유형 | 설명 | 값 |
|---|---|---|---|---|
country_code | 예 | String | ISO 3166 국가 코드 | US |
currency | 예 | String | ISO 4217 형식의 거래 통화 (3자리 코드) | USD |
email | 예 | String | 고객의 이메일 주소. 평문 이메일을 제공하십시오. AppLovin이 자체적으로 해싱합니다. | customer@example.com |
event_name | 예 | String | 반드시 purchase여야 합니다. | purchase |
event_timestamp | 예 | String | 구매가 발생한 시간
|
|
transaction_id | 예 | String | 주문의 고유 식별자 (예: 주문 ID, 체크아웃 ID). 이는 중복 제거에 매우 중요합니다. | txn_12345 |
user_id | 예 | String | 귀사의 내부 고객 ID. | user_abc123 |
value | 예 | Double | 거래의 총 가치. 0보다 크거나 같은 숫자여야 합니다. 통화 기호를 포함하지 마십시오. | 99.99 |
zip | 아니요 | String | 고객의 우편번호. 이는 해당 거래의 청구 우편번호여야 합니다. 미국 우편번호의 경우 처음 5자리만 포함하십시오. | 12345 |
idfv | 아니요 | String | 사용자의 IDFV. | f325g3gb-12fc-352f-c6c3-dz52f0f690d8 |
ifa | 아니요 | String | 사용자의 광고 식별자. IDFA 또는 GAID 중 하나입니다. | 918f1d4f-d195-4a8b-af47-44683fe11db9 |
phone | 아니요 | String | 고객의 전화번호. 국가 코드를 포함해야 합니다 (예: +1). 평문 번호를 제공하십시오. AppLovin이 자체적으로 해싱합니다. | +14155551234 |
AppLovin은 귀사가 제공하는 평문 이메일 주소와 전화번호를 해싱합니다.
직접 해싱을 수행하려는 경우, 다음 가이드라인을 주의 깊게 따르십시오:
해시에 솔트(salt)를 추가하지 마십시오. 정규화된 값에만 일반 SHA-256을 사용하십시오. 솔트, 페퍼(pepper) 또는 이중 해싱된 값은 AppLovin 측에서 매칭할 수 없으며 값이 없는 것으로 처리됩니다.
emailgmail.com 및 googlemail.com에 한해, 먼저 @ 앞의 모든 마침표를 제거하고,
첫 번째 플러스 기호(+)와 그 뒤의 모든 문자를 제거합니다. 도메인은
변경하지 않은 상태로 둡니다. @ 앞에 아무것도 남지 않으면 빈 값을 생성합니다.
phone1).
그 다음 SHA256을 통해 해싱합니다.
예시 (이메일 주소의 경우):
| 입력 | 정규화됨 | SHA256 |
|---|---|---|
Alice@Example.COM | alice@example.com | ff8d9819fc0e12bf0d24892e45987e249a28dce836a85cad60e28eaaa8c6d976 |
J..A.NE+work.more+extra@GMAIL.COM | jane@gmail.com | 988b074286b20c503e3015c2076533f3bf4ce5ca6f8a507ab52c2e0f98d620b7 |
Jane.Doe+Work@GoogleMail.com | janedoe@googlemail.com | 338abf9ef1c8793cadc7bcf51ed595338eb727ed9e06ce3d91d566d60b975937 |
참조 구현은 다음과 같습니다. 각 구현은 위의 두 번째 예시에 대한 해시를 출력합니다. 코드를 실행하여 동일한 값이 나오는지 확인하십시오.
#include <algorithm>
#include <cctype>
#include <iostream>
#include <regex>
#include <string>
#include <openssl/sha.h>
std::string normalizeAndHash(std::string email)
{
email.erase(0, email.find_first_not_of(" \t\n\r\f\v"));
email.erase(email.find_last_not_of(" \t\n\r\f\v") + 1);
std::transform(email.begin(), email.end(), email.begin(),
[](unsigned char c) { return std::tolower(c); });
static const std::regex valid("^[!-?A-~]+@[!-?A-~]+$");
if (!std::regex_match(email, valid))
return "";
const auto at = email.find('@');
std::string local = email.substr(0, at);
const std::string domain = email.substr(at + 1);
if (domain == "gmail.com" || domain == "googlemail.com")
{
local = local.substr(0, local.find('+'));
local.erase(std::remove(local.begin(), local.end(), '.'), local.end());
if (local.empty())
return "";
}
const std::string normalized = local + "@" + domain;
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256(reinterpret_cast<const unsigned char*>(normalized.data()),
normalized.size(), hash);
static constexpr char digits[] = "0123456789abcdef";
std::string out;
for (const unsigned char value : hash)
{
out += digits[value >> 4];
out += digits[value & 15];
}
return out;
}
int main()
{
std::cout << normalizeAndHash(" J..A.NE+work.more+extra@GMAIL.COM ") << "\n";
}
using System;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
class Program
{
static readonly Regex Valid = new Regex("^[!-?A-~]+@[!-?A-~]+$");
static string NormalizeAndHash(string email)
{
string trimmed = email.Trim(' ', '\t', '\n', '\r', '\f', '\v').ToLowerInvariant();
if (!Valid.IsMatch(trimmed))
return "";
int at = trimmed.IndexOf('@');
string local = trimmed.Substring(0, at);
string domain = trimmed.Substring(at + 1);
if (domain == "gmail.com" || domain == "googlemail.com")
local = local.Split('+')[0].Replace(".", "");
if (local.Length == 0)
return "";
using (SHA256 sha256 = SHA256.Create())
{
byte[] hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(local + "@" + domain));
StringBuilder output = new StringBuilder(hash.Length * 2);
foreach (byte value in hash)
output.Append(value.ToString("x2"));
return output.ToString();
}
}
static void Main()
{
Console.WriteLine(NormalizeAndHash(" J..A.NE+work.more+extra@GMAIL.COM "));
}
}
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HexFormat;
import java.util.regex.Pattern;
public final class EmailHash
{
private static final Pattern VALID = Pattern.compile( "^[!-?A-~]+@[!-?A-~]+$" );
static String normalizeAndHash( final String email ) throws NoSuchAlgorithmException
{
final String trimmed = email.trim().toLowerCase();
if ( !VALID.matcher( trimmed ).matches() )
{
return "";
}
final int at = trimmed.indexOf( '@' );
final String domain = trimmed.substring( at + 1 );
String local = trimmed.substring( 0, at );
if ( domain.equals( "gmail.com" ) || domain.equals( "googlemail.com" ) )
{
local = local.split( "\\+", 2 )[0].replace( ".", "" );
if ( local.isEmpty() )
{
return "";
}
}
return HexFormat.of().formatHex( MessageDigest.getInstance( "SHA-256" )
.digest( ( local + "@" + domain ).getBytes( StandardCharsets.UTF_8 ) ) );
}
public static void main( final String[] args ) throws NoSuchAlgorithmException
{
System.out.println( normalizeAndHash( " J..A.NE+work.more+extra@GMAIL.COM " ) );
}
}
import { createHash } from "crypto";
function normalizeAndHash(email) {
const trimmed = email.replace(/^[ \t\n\r\f\v]+|[ \t\n\r\f\v]+$/g, "").toLowerCase();
if (!/^[!-?A-~]+@[!-?A-~]+$/.test(trimmed)) {
return "";
}
const at = trimmed.indexOf("@");
let local = trimmed.slice(0, at);
const domain = trimmed.slice(at + 1);
if (domain === "gmail.com" || domain === "googlemail.com") {
local = local.split("+")[0].replaceAll(".", "");
}
return local ? createHash("sha256").update(`${local}@${domain}`).digest("hex") : "";
}
console.log(normalizeAndHash(" J..A.NE+work.more+extra@GMAIL.COM "));
import java.security.MessageDigest
private val VALID = Regex("^[!-?A-~]+@[!-?A-~]+$")
fun normalizeAndHash(email: String): String {
val trimmed = email.trim(' ', '\t', '\n', '\r', '\u000B', '\u000C').lowercase()
if (!VALID.matches(trimmed)) return ""
val (rawLocal, domain) = trimmed.split('@')
val local = if (domain == "gmail.com" || domain == "googlemail.com") {
rawLocal.substringBefore('+').replace(".", "").ifEmpty { return "" }
} else rawLocal
return MessageDigest.getInstance("SHA-256")
.digest("$local@$domain".toByteArray())
.joinToString("") { "%02x".format(it) }
}
fun main() {
println(normalizeAndHash(" J..A.NE+work.more+extra@GMAIL.COM "))
}
import hashlib
import re
VALID = re.compile(r"^[!-?A-~]+@[!-?A-~]+$")
def normalize_and_hash(email):
email = email.strip(" \t\n\r\f\v").lower()
if not VALID.match(email):
return ""
local, _, domain = email.partition("@")
if domain in ("gmail.com", "googlemail.com"):
local = local.split("+", 1)[0].replace(".", "")
return hashlib.sha256(f"{local}@{domain}".encode()).hexdigest() if local else ""
print(normalize_and_hash(" J..A.NE+work.more+extra@GMAIL.COM "))
import CryptoKit
import Foundation
func normalizeAndHash(_ email: String) -> String {
let trimmed = email.trimmingCharacters(
in: CharacterSet(charactersIn: " \t\n\r\u{0b}\u{0c}")).lowercased()
guard trimmed.range(of: "^[!-?A-~]+@[!-?A-~]+$", options: .regularExpression) != nil
else { return "" }
let parts = trimmed.split(separator: "@")
let domain = String(parts[1])
var local = String(parts[0])
if domain == "gmail.com" || domain == "googlemail.com" {
local = local.prefix { $0 != "+" }.filter { $0 != "." }
}
guard !local.isEmpty else { return "" }
return SHA256.hash(data: Data("\(local)@\(domain)".utf8))
.map { String(format: "%02x", $0) }.joined()
}
print(normalizeAndHash(" J..A.NE+work.more+extra@GMAIL.COM "))
988b074286b20c503e3015c2076533f3bf4ce5ca6f8a507ab52c2e0f98d620b7
user_id와 email이 포함되어야 합니다.
AppLovin은 해당 열의 값이 누락된 행을 거부합니다.event_name 열이 존재해야 하며 그 값은 purchase여야 합니다.value 열에는 음수가 아닌 수(≥0)가 포함되어야 합니다.AX — 올란드 제도AD — 안도라AT — 오스트리아BE — 벨기에DK — 덴마크FO — 페로 제도FI — 핀란드FR — 프랑스DE — 독일GI — 지브롤터GR — 그리스GG — 건지IS — 아이슬란드IE — 아일랜드IM — 맨섬IT — 이탈리아JE — 저지LI — 리히텐슈타인LU — 룩셈부르크MT — 몰타MC — 모나코NL — 네덜란드NO — 노르웨이PT — 포르투갈SM — 산마리노ES — 스페인SJ — 스발바르 및 얀마옌SE — 스웨덴CH — 스위스GB — 영국다음은 유효한 CSV 파일의 예시입니다.
event_name,user_id,phone,email,event_timestamp,value,currency,transaction_id,country_code,zip,idfv,ifa
purchase,user_abc123,+14155551234,user@example.com,2025-11-10T16:45:00Z,99.99,USD,txn_12345,US,12345,f325g3gb-12fc-352f-c6c3-dz52f0f690d8,918f1d4f-d195-4a8b-af47-44683fe11db9
purchase,user_def456,+14155555678,customer@example.com,2025-11-10T16:45:00Z,149.5,USD,txn_67890,US,12345,f325g3gb-12fc-352f-c6c3-dz52f0f690d8,918f1d4f-d195-4a8b-af47-44683fe11db9
purchase,user_ghi789,+14155559012,shopper@example.com,2025-11-10T17:20:00Z,75,EUR,txn_11223,US,12345,f325g3gb-12fc-352f-c6c3-dz52f0f690d8,918f1d4f-d195-4a8b-af47-44683fe11db9