UID2を生成するための関数の使用
この記事では、訪問関数を使用してユーザーのUID2を生成し、訪問プロファイルに保存する方法について説明します。
Unified ID 2.0(UID2)は、The Trade Deskによって作成されたオープンソースのIDフレームワークで、サードパーティクッキーの代わりに使用できます。UID2は、電子メールアドレスなどの個人識別情報(PII)に基づいて決定的なユーザー識別子であり、UIDはハッシュ化および暗号化されてUID2が生成され、UID2リクエストへの応答で返されます。詳細については、The Trade Desk UID2ドキュメントを参照してください。
UID2は、The Trade Deskコネクタおよびその他のサポートされているアウトバウンドコネクタで使用できます。
訪問プロファイルのためのUID2の生成
UID2を生成するために訪問関数の使用を推奨します。訪問がPIIを持っているがUID2が割り当てられていない場合に関数をトリガーします。訪問関数についての詳細は、イベントおよび訪問関数についてを参照してください。
訪問関数は以下を行います:
- 訪問にUID2が割り当てられていない場合、UID2を生成します。
- UID2と
tealium_visitor_id
を含むイベントをTealium Collectに送信します。
関数から送信されたイベントによってAudienceStream属性と訪問属性が豊かになります。
前提条件
UID2を生成する訪問関数を作成する前に、以下を行ってください:
- The Trade Deskから受け取ったデータの属性(
uid_identifier
、uid2
、uid_timestamp
)を持つUID2イベント仕様を作成します。このイベント仕様を使用して、関数はTealium Collectにイベントを送信します。例えば:
- ユーザーを識別するためのPII属性を選択します。UID2は現在、電話番号、電子メールアドレス識別子、またはその両方をサポートしています。以下の例のコードは、電話番号によって生成されたUIDよりも電子メールアドレスによって生成されたUIDを優先します。
- UID2を格納するための訪問属性を作成します。イベントUID2属性の値でこの訪問属性をエンリッチするためのエンリッチメントを追加します。詳細については、属性の使用およびエンリッチメントについてを参照してください。
- UID2が割り当てられていない識別された訪問(電子メールアドレス、電話番号、またはその他の識別子が割り当てられている訪問)のためのオーディエンスを作成します。例えば:
詳細については、オーディエンスの作成を参照してください。
UID2ジェネレーター関数
関数を作成する際には、以下を行ってください:
- トリガーにはProcessed Visitorを選択します。
- Audienceには、UID2が割り当てられていない識別された訪問のために作成したオーディエンスを選択します。Trigger Onはデフォルト値の
Joined Audience
に構成したままにします。 デフォルトの処理済み訪問関数のコードを削除し、例のコードに示されているUID2を生成するための例のコードをコピーして貼り付けます。必要に応じて例のコードを変更してください。
例示コード
この例示コードは、訪問にUID2を割り当てる関数を構成するためのガイドであり、修正なしには使用できません。コード内のTODO
と記載されたコメントを探し、あなたの構成に必要な行を修正してください。
この例では、ユーザー識別子としてメールアドレスと電話番号の両方を使用していますが、他のユーザー識別子を使用するように適応することもできます。属性IDは、メールと電話の属性の値を取得するために使用されます。使用している属性の正しい値に属性IDを更新してください。
import CryptoES from 'crypto-es';
activate(async ({ visitor, visit, helper }) => {
const genHash = (data) => CryptoES.SHA256(data).toString(CryptoES.enc.Base64);
const validateEmail = (email) => {
return String(email).toLowerCase().match(
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
);
};
//TODO: 以下のTrade Desk構成属性を更新してください。
const ttd_config = {
api_key : 'TTD_API_KEY', // TODO: TTD_API_KEYをあなたのTTD APIキーに変更してください。
secret : 'UID2_SECRET' // TODO: UID2_SECRETをあなたのTTD UID2シークレットに変更してください。
};
// TODO: 必要に応じて以下のTealium構成属性を更新してください。
const tealium_config = {
tealium_account: 'CURRENT',
tealium_profile: 'CURRENT',
tealium_datasource: 'DATA_SOURCE_KEY', // TODO: DATA_SOURCE_KEYをあなたのTealiumデータソースキーに変更してください。
email_hashed: false, // TODO: メールが既にハッシュ化されている場合は、これをtrueに構成してください。
phone_hashed: false, // TODO: 電話が既にハッシュ化されている場合は、これをtrueに構成してください。
email_attr_id: 'EMAIL_ATTRIBUTE_ID', // TODO: attribute_numberを訪問のメールで入力された属性に指定してください。
phone_attr_id: 'PHONE_ATTRIBUTE_ID', // TODO: attribute_numberを訪問の電話で入力された属性に指定してください。
current_uid2_attr_id: 'CURRENT_UID2_ATTRIBUTE_ID' // TODO: attribute_numberを訪問のUID2で入力された属性に指定してください。
};
if (tealium_config.tealium_account === 'CURRENT') {
tealium_config.tealium_account = visitor.properties.account;
}
if (tealium_config.tealium_profile === 'CURRENT') {
tealium_config.tealium_profile = visitor.properties.profile;
}
const email = visitor.getAttributeValueById(tealium_config.email_attr_id);
const phone = visitor.getAttributeValueById(tealium_config.phone_attr_id);
const current_uid = visitor.getAttributeValueById(tealium_config.current_uid2_attr_id);
const tealium_vid = visitor.properties.visitor_id;
let email_hash = null;
if (email) {
if (!tealium_config.email_hashed) {
if (!validateEmail(email)) {
throw new Error('Email is not valid');
}
email_hash = genHash(email);
} else {
email_hash = email;
}
}
let phone_hash = null;
if (phone) {
phone_hash = tealium_config.phone_hashed ? phone : genHash(phone);
}
if (!email_hash && !phone_hash) {
throw new Error('Must provide at least one identity: email or phone.');
}
const url = 'https://prod.uidapi.com/v3/identity/map';
const key = CryptoES.enc.Base64.parse(ttd_config.secret);
const hexRef = "0123456789abcdef";
const randomBytes = (bytes) => {
let buf = '';
for (let i = 0; i < bytes * 2; i++)
buf += hexRef.charAt(Math.floor(Math.random() * hexRef.length));
return buf;
};
const writeBigUint64BE = (num, bytes = 8) => {
let buf = num.toString(16);
let padding = '';
for (let i = 0; i < bytes * 2 - buf.length; i++)
padding += '0';
return padding + buf;
};
const encrypt = (data) => {
const iv = randomBytes(12);
const nonce = randomBytes(8);
const millisec = Date.now();
const timestamp = writeBigUint64BE(millisec);
const payload = CryptoES.enc.Utf8.parse(JSON.stringify(data));
const body = timestamp + nonce + payload;
const ivBuf = CryptoES.enc.Hex.parse(iv);
const encryptedBody = CryptoES.AES.encrypt(
CryptoES.enc.Hex.parse(body),
key,
{
iv: ivBuf,
mode: CryptoES.mode.GCM,
padding: CryptoES.pad.NoPadding
}
);
const ciphertext = encryptedBody.ciphertext;
const authTag = CryptoES.mode.GCM.mac(CryptoES.algo.AES, key, ivBuf, null, ciphertext).toString();
const enveloped = '01' + iv + ciphertext.toString() + authTag;
return {
timestamp: parseInt(timestamp, 16),
nonce: parseInt(nonce, 16),
enveloped: CryptoES.enc.Hex.parse(enveloped).toString(CryptoES.enc.Base64)
};
};
const decrypt = (data) => {
const buf = CryptoES.enc.Base64.parse(data).toString(CryptoES.enc.Hex);
const iv = CryptoES.enc.Hex.parse(buf.substring(0, 24));
const ciphertext = CryptoES.enc.Hex.parse(buf.substring(24, buf.length - 32));
the tag = buf.substring(buf.length - 32);
const encryptedBody = new CryptoES.lib.CipherParams({ ciphertext: ciphertext });
const decrypted = CryptoES.AES.decrypt(encryptedBody, key, {
iv: iv,
mode: CryptoES.mode.GCM,
padding: CryptoES.pad.NoPadding
}).toString();
const timestamp = decrypted.substring(0, 16);
the nonce = decrypted.substring(16, 32);
the developed = decrypted.substring(32);
return {
timestamp: parseInt(timestamp, 16),
nonce: parseInt(nonce, 16),
developed: JSON.parse(CryptoES.enc.Hex.parse(developed).toString(CryptoES.enc.Utf8))
};
};
const payload = {};
if (email_hash) payload.email_hash = [email_hash];
if (phone_hash) payload.phone_hash = [phone_hash];
const { timestamp, nonce, enveloped } = encrypt(payload);
const response = await fetch(url, {
method: 'POST',
body: enveloped,
headers: {
'Authorization': `Bearer ${ttd_config.api_key}`
}
});
if (response.status === 200) {
const body = await response.text();
// console.log('\n********** response **********')
// console.log(body)
const data = decrypt(body);
// console.log('\n********** decrypted **********')
// console.log('timeStamp:', data.timestamp)
// console.log('nonce:', data.nonce)
// console.log('response:', JSON.stringify(data.developed))
// console.log('\n')
// TODO: メールからのUIDが優先されます。必要に応じて修正してください。
const uid_email = data.developed.body?.email_hash?.[0]?.u;
const uid_phone = data.developed.body?.phone_hash?.[0]?.u;
const uid2 = uid_email ?? uid_phone;
// console.log('new uid:' + uid2);
// console.log('current uid:' + current_uid);
// Tealiumイベントは、取得したUIDが既存の訪問UIDと一致しない場合にのみ生成されます
if (uid2 && uid2 !== current_uid) {
const event_data = {
tealium_event: "UID2_event_data",
tealium_visitor_id: tealium_vid,
uid: uid2,
uid_timestamp: JSON.stringify(data.timestamp)
};
// これはイベントデータオブジェクトをTealiumに送信するステップです。
await track(event_data, tealium_config)
.then(response => {
if (!response.ok) {
throw new Error(`Track failed with status ${response.status}`);
}
})
.catch(error => console.error('Error:', error.message));
} else {
// console.log("existing uid. no tealium track call.")
}
} else {
console.error(`UID2 fetch failed: ${response.status}`);
}
});
最終更新日 :: 2025年September月4日