受保护的应用信号开发者指南

为帮助开发者开始试用 Protected App Signals API,本文档将介绍 API Surface 中的所有 API,详细说明如何设置测试环境,并提供配置和脚本的示例。

版本历史记录

2024 年 1 月

第一版开发者指南,支持 PAS MVP 版本

2024 年 3 月

为支持 M-2024-05 版的 Android API 和 2024 年 4 月发布了服务器端组件。最显著的变化:

  • 添加了有关设备端 API 所需权限的详细信息
  • 添加了有关设备端信号配额管理的详细信息
  • 更新了 generateBid 签名,添加了与上下文相关的更改 广告检索和出站流量支持
  • 更新了 reportWin 文档,包括出站流量支持
  • 更新了 Ad Retrieval API 文档,移除对 BYOS 广告检索的支持 并为广告检索 UDF 编写文档

API 概览

Protected Signals API Surface 在不同系统上包含不同 API 子集:

  • Android API:
    • Signal Curation API,其中包含:
    • Update Signals API
    • Signals Encoding API
    • Protected Auction Support API:供 SDK 使用,以便在使用 Protected App Signals 的出价和竞价 (B&A) 服务器上运行受保护竞价。
  • 服务器端 API:
    • Protected Auction API:在出价和竞价服务器中运行的一系列 JS 脚本。卖方和买方可以使用此 API 编写逻辑来实现受保护的竞价。
    • Ad Retrieval API:负责根据买方出价服务器可用的情境信息和用户信息提供候选广告列表。

Android 客户端

在客户端,Protected App Signals Surface 由三个不同的 API 组成:

  • Update Signals:一种 Android 系统 API,用于在设备上启用信号分类。
  • Signals Encoding:一种 JavaScript API,用于准备要在竞价期间发送至服务器的信号。
  • Protected Auction Support:支持在出价和竞价服务器上执行受保护的竞价的 API。此 API 并非特定于 Protected App Signals,也用于支持 Protected Audience API 竞价。

Update Signals API

Update Signals API 使广告技术平台能够代表买方注册用户和应用相关信号。该 API 基于委托模型。调用方会提供一个 URI,框架可从该 URI 中提取相应的信号以及对要在竞价中使用的那些信号进行编码的逻辑。

该 API 需要 android.permission.ACCESS_ADSERVICES_PROTECTED_SIGNALS 权限。

updateSignals() API 将检索 JSON 对象 说明要添加或移除哪些信号的 URI 以及如何准备 以便参与竞价

Executor executor = Executors.newCachedThreadPool();
ProtectedSignalsManager protectedSignalsManager
     =  ProtectedSignalsManager.get(context);

// Initialize a UpdateSignalsRequest
UpdateSignalsRequest updateSignalsRequest = new
  UpdateSignalsRequest.Builder(Uri.parse("https://example-adtech1.com/signals"))
      .build();

OutcomeReceiver<Object, Exception> outcomeReceiver = new OutcomeReceiver<Object, Exception>() {
  @Override
  public void onResult(Object o) {
    //Post-success actions
  }

  @Override
  public void onError(Exception error) {
    //Post-failure actions
  };

// Call updateSignals
protectedSignalsManager.updateSignals(updateSignalsRequest,
    executor,
    outcomeReceiver);

平台会向请求中提供的 URI 发出 https 请求,以提取信号更新。除了信号更新之外,响应还可以包含一个端点,该端点托管用于将原始信号转换为编码载荷的编码逻辑。信号更新应采用 JSON 的形式,并且可以具有以下键:

JSON 对象的顶级键必须与以下五个命令之一相对应:

说明

put

添加新信号,使用同一键覆盖所有现有信号。此元素的值

是一个 JSON 对象,其中键是与要输入的键对应的 base 64 字符串,值是与要放入的值对应的 base 64 字符串。

append

将新信号附加到信号的时间序列。

如果时间序列的大小超出指定的上限,则会移除最早的信号来为新数据腾出空间。此元素的值是一个 JSON 对象,其中键是与要附加的键对应的 base 64 个字符串,值是包含以下两个字段的对象:“values”和“maxSignals”。

“values”:与要附加到时间序列的信号值相对应的 base 64 字符串列表。

“maxSignals”:此时间序列中允许的值的数量上限。如果

与键关联的信号的当前数量超出了 maxSignals,最早的信号将被移除。请注意,您可以附加到通过 put 添加的键。请注意,附加的值数量超过上限将导致失败。

put_if_not_present

仅当不存在具有相同键的现有信号时,会添加一个新信号。此元素的值是一个 JSON 对象,其中键是与要输入的键对应的 base 64 字符串,值是与要放入的值对应的 base 64 字符串。

remove

移除键的信号。此元素的值是一个 base 64 字符串的列表,与应删除的信号的键相对应。

update_encoder

提供用于更新端点的操作,以及可以用于

检索编码逻辑的 URI。用于提供更新操作的子键是“action”,

目前支持的值只有“REGISTER”,其会在首次提供编码器端点时注册该端点,或使用新提供的端点覆盖现有端点。若要执行“REGISTER”操作,必须提供端点。用于提供编码器端点的子键为“endpoint”,值为

该端点的 URI 字符串。

示例 JSON 请求如下所示:

{
    "put": {
        "AAAAAQ==": "AAAAZQ==",
        "AAAAAg==": "AAAAZg=="
    },
    "append": {
        "AAAAAw==": {
            "values": [
                "AAAAZw=="
            ],
            "max_signals": 3
        }
    },
    "put_if_not_present": {
        "AAAABA==": "AAAAaQ==",
        "AAAABQ==": "AAAAag=="
    },
    "update_encoder": {
        "action": "REGISTER",
        "endpoint": "https://adtech1.com/Protected App Signals_encode_script.js"
    }
}

信号的设备端配额约为 10-15 Kb。配额 超出 PPAPI 将使用 FIFO 策略逐出信号。逐出过程 将允许在短时间内稍微超出配额 以降低逐出频率。

Signals Encoding API

买方必须提供一个 Java 脚本函数,用于对存储在设备上的信号进行编码,以便在受保护的竞价期间发送到服务器。买方可通过在 UpdateSignal API 请求的任何响应中添加可使用键“update_encoder”提取相应脚本的网址,以提供此脚本。此脚本将具有以下签名:

function encodeSignals(signals, maxSize) {
  let result = new Uint8Array(maxSize);
  // first entry will contain the total size
  let size = 1;
  let keys = 0;
  
  for (const [key, values] of signals.entries()) {
    keys++;
    // In this encoding we only care about the first byte
    console.log("key " + keys + " is " + key)
    result[size++] = key[0];
    result[size++] = values.length;
    for(const value of values) {
      result[size++] = value.signal_value[0];
    }
  }
  result[0] = keys;
  
  return { 'status': 0, 'results': result.subarray(0, size)};
}

signals 参数是从大小为 4 的 UInt8Arrays 形式的键到 Protected App Signals 对象列表的映射。每个 Protected App Signals 对象都有三个字段:

  • signal_value:一个表示信号值的 UInt8Array。
  • creation_time:一个数字,表示信号的创建时间(以新纪元秒数表示)。
  • package_name:一个字符串,表示创建信号的软件包的名称。

maxSize 参数是一个数字,用于描述输出的最大允许数组大小。

该函数应输出包含以下两个字段的对象:

  • status:如果脚本成功运行,则应为 0。
  • results:应为长度小于或等于 maxSize 的 UInt8Array。 此数组将在竞价期间发送到服务器,并由 prepareDataForAdRetrieval 脚本进行准备。

编码为广告技术平台提供了特征工程的初始阶段,在该阶段,广告技术平台可以执行转换,例如根据自己的自定义逻辑将原始信号压缩为串联版本。请注意,在可信执行环境 (TEE) 中运行 Protected Auction 期间,广告技术平台自定义逻辑将拥有对编码生成的信号载荷的读取权限。在买方的 B&A TEE 中运行的自定义逻辑(称为用户定义的函数 (UDF))将拥有对编码信号和发布商应用提供的其他情境信号的读取权限,以便执行执行广告选择操作(广告检索和出价)。

信号编码

每个小时,已为其注册信号提供编码逻辑的买方都会将其信号编码为竞价载荷。竞价载荷的字节数组持久保存在设备上并经过加密,将作为广告选择数据的一部分由卖方收集,并作为 Protected Auction 的一部分包含在内。对于测试,您可以通过运行以下命令,在每小时的节奏之外触发此编码:

adb shell cmd jobscheduler run -f com.google.android.adservices.api 29
编码器逻辑版本控制

发出下载广告技术自定义编码器逻辑的请求时,广告技术平台端点可以在响应标头中使用版本号进行响应。此版本与设备上的编码器逻辑一起持久保留。对原始信号进行编码时,编码的载荷会与用于编码的版本一起保留。此版本也会在 Protected Auction 期间随同 B&A 服务器一起发送,以便广告技术平台可以根据版本调整其出价和编码逻辑。

Response header for providing encoder version : X_ENCODER_VERSION

Protected Auction Support API

在设备端,为 Protected App Signals 运行竞价的方式与为受保护的受众群体运行竞价相同

出价和竞价服务

服务器端 API 包括:

  • Protected Auction API:一系列 JS 函数或 UDF,买方和卖方可以在他们拥有的 B&A 组件上部署这些函数,以确定出价和竞价逻辑。
  • Ad Retrieval API:买方可通过实现 REST 端点来实现此 API,该端点负责为 Protected App Signal 竞价提供一组候选广告。

Protected Auction API

Protected Auction API 由 JS API 或 UDF 组成,买方和卖方可以使用它们来实现其竞价和出价逻辑。

买方广告技术平台 UDF
prepareDataForAdRetrieval UDF

在 Protected App Signals 可用于从 TEE 提取候选广告之前 Ad Retrieval Service,买方必须解码并准备 Protected App Signals 和其他卖方提供的数据。买方 prepareDataForAdRetrieval UDF 输出传递给广告检索服务,以检索前 k 个候选广告以进行出价

// Inputs
// ------
// encodedOnDeviceSignals: A Uint8Array of bytes from the device.
// encodedOnDeviceSignalsVersion: An integer representing the encoded
//   version of the signals.
// sellerAuctionSignals: Information about auction (ad format, size) derived
//                       contextually.
// contextualSignals: Additional contextual signals that could help in
//                    generating bids.
//
// Outputs
// -------
// Returns a JSON structure to be used for retrieval.
// The structure of this object is left to the adtech.
function prepareDataForAdRetrieval(encodedOnDeviceSignals,encodedOnDeviceSignalsVersion,sellerAuctionSignals,contextualSignals) {
   return {};
}
generateBid UDF

返回前 k 个候选广告后,系统会将候选广告传递给买方的自定义出价逻辑 generateBid UDF

// Inputs
// ------
// ads: Data string returned by the ads retrieval service. This can include Protected App Signals
//   ads and related ads metadata.
// sellerAuctionSignals: Information about the auction (ad format, size),
//                       derived contextually
// buyerSignals: Any additional contextual information provided by the buyer
// preprocessedDataForRetrieval: This is the output of this UDF.
function generateBid(ads, sellerAuctionSignals, buyerSignals,
                    preprocessedDataForRetrieval,
                    rawSignals, rawSignalsVersion) {
    return { "ad": <ad Value Object>,
             "bid": <float>,
             "render": <render URL string>,
             'adCost': <optional float ad cost>,
             "egressPayload": <limitedEgressPayload>,
             "temporaryUnlimitedEgressPayload": <temporaryUnlimitedEgressPayload>
    };
}

此函数的输出是对候选广告的单次出价,表示 表示为与 ProtectedAppSignalsAdWithBidMetadata。 该函数还可以返回两个数组,然后这两个数组将传递给 reportWin 启用模型训练(如需详细了解出站流量和模型训练), 请参阅 PAS 说明中的报告部分

reportWin UDF

竞价结束后,竞价服务会生成 买方和注册信标,并使用 reportWin UDF(这与 reportWin 函数)。 客户端呈现广告后,设备将对此执行 ping 操作。 此方法的签名与 Protected Audience 的签名几乎相同 version,除了两个额外参数 egressPayloadtemporaryUnlimitedEgressPayload,用于启用模型训练, 填充了来自 generateBid 的结果。

// Inputs / Outputs
// ----------------
// See detailed documentation here.
function reportWin(auctionSignals, perBuyerSignals, signalsForWinner,
                   buyerReportingSignals,
                   egressPayload, temporaryUnlimitedEgressPayload) {
  // ...
}
卖方广告技术平台 UDF
ScoreAd UDF

卖方会使用此 UDF 选择从买方收到的哪些广告将在竞价中胜出。

function scoreAd(adMetadata, bid, auctionConfig,
                 trustedScoringSignals, bid_metadata) {
  // ...
  return {desirability: desirabilityScoreForThisAd,
              allowComponentAuction: true_or_false};
}
reportResult UDF

借助此 UDF,卖方可以(最终)使用胜出广告的相关信息进行事件级报告。

function reportResult(auctionConfig, reporting_metadata) {
  // ...
  registerAdBeacon({"click", clickUrl,"view", viewUrl});
  sendReportTo(reportResultUrl);
  return signalsForWinner;
}

Ad Retrieval API

在 MVP 版本中,广告检索服务将由买方管理和托管 服务,出价服务会从此服务中检索候选广告。 自 2024 年 4 月起,广告检索服务器必须在可信任的环境中运行 执行环境 (TEE) 中,并且将公开 GRPC/proto 接口。广告技术平台 公司必须设置此服务器并在 B&A 中提供其网址 堆栈部署在 TEE 中运行的此服务的实现 Privacy Sandbox GitHub 以及 文档,我们假设这是部署中使用的代码。

从 2024 年 4 月开始,B&A 版本将支持内容相关路径广告 检索。在这种情况下,出价服务器会收到 实时出价服务器在竞价的内容相关部分发送的广告标识符。 系统会将标识符发送到 TEE KV 服务器来提取所有广告 在出价阶段使用的相关信息 (例如,要使用的广告呈现网址、元数据和广告嵌入) Top-k 选择)。第二种路径不需要任何特定逻辑 所以我们在这里只介绍如何配置基于 TEE 的 广告检索用例

HandleRequest UDF
function HandleRequest(requestMetadata, preparedDataForAdRetrieval,
                      deviceMetadata, contextualSignals) {
    return adsMetadataString;
}

其中:

  • requestMetadata:JSON。将每个请求的服务器元数据发送到 UDF。目前为空。
  • preparedDataForAdRetrieval:此字段的内容取决于广告 检索策略。对于内容相关广告检索,此参数为 将包含来自设备的原始信号,并从 出价服务。如果使用广告检索服务器进行 TEE 广告检索, 此参数将包含 prepareDataForAdRetrieval UDF 的结果。 注意:在此阶段,系统会对 Protected App Signals 进行解码和未加密。
  • deviceMetadata:包含由卖方广告服务转发的设备元数据的 JSON 对象。如需了解详情,请参阅 B&A 文档。
    • X-Accept-Language:设备上使用的语言。
    • X-User-Agent:设备上使用的用户代理。
    • X-BnA-Client-IP:设备 IP 地址。
  • contextualSignals:来自内容相关出价的任意字符串 由同一 DSP 运营的服务器。UDF 应该能够解码 并使用该字符串。情境信号可能包含机器学习等任何信息 使用 Protected App Signals。

UDF 应在成功时返回一个字符串。该字符串会返回 出价服务器随后将其传递给 generateBid UDF。虽然 字符串可以只是一个简单的字符串,该字符串很可能应为 序列化对象,其架构由每个广告技术平台自行定义。 只要广告技术平台的 generateBid,对架构就无限制 逻辑可以识别并使用该字符串。

设置系统开发环境

Android

如需设置 Android 开发环境,您需要执行以下操作:

  1. 创建运行开发者预览版 10 映像的模拟器(首选)或实体设备
  2. 运行以下命令:
adb shell am start -n com.google.android.adservices.api/com.android.adservices.ui.settings.activities.AdServicesSettingsMainActivity

然后,选择显示的选项来同意使用应用建议的广告。

  1. 运行以下命令以启用相关 API。由于默认配置“已停用”会定期同步,因此您可能需要不时重新运行此命令。
adb shell device_config put adservices fledge_custom_audience_service_kill_switch false;  adb shell device_config put adservices fledge_select_ads_kill_switch false; adb shell device_config put adservices fledge_on_device_auction_kill_switch false; adb shell device_config put adservices fledge_auction_server_kill_switch false; adb shell "device_config put adservices disable_fledge_enrollment_check true";  adb shell device_config put adservices ppapi_app_allow_list '\*'; adb shell device_config put adservices fledge_auction_server_overall_timeout_ms 60000;
  1. 重启设备。
  2. 替换设备的竞价键,以指向您的竞价键服务器。在尝试运行竞价之前一定要运行此步骤,以防止不正确的键被缓存。

出价和竞价服务

如需设置 B&A 服务器,请参阅自助设置文档

本文档将重点介绍如何配置买方专用服务器,因为卖方无需进行任何更改。

前提条件

在部署 B&A 服务堆栈之前,买方广告技术平台需要确保以下事项:

  • 确保已部署自己的 TEE 广告检索服务(请参阅 相关部分)。
  • 确保广告技术平台具有所有必要的 UDF (prepareDataForAdRetrievalgenerateBidreportWinHandleRequest) 定义和托管

了解如何将 B&A 与通过 Protected Audience 开展 Protected Auction 竞价搭配使用也会有所帮助,但不是强制性要求。

Terraform 配置

如需使用 Protected App Signals,广告技术平台必须:

  • 在 B&A 中启用 Protected App Signals 支持。
  • 提供可从中提取 prepareDataForAdRetrieval, generateBidreportWin 的新 UDF 的网址端点。

此外,本指南假定希望使用 B&A 进行再营销的广告技术平台会继续照常为再营销竞价设置所有现有配置标志。

买方广告技术平台配置

以此演示文件为例,买方需要设置以下标志:

  • 启用 Protected App Signals:启用以收集 Protected App Signals 数据。
  • Protected App Signals 网址:设置为 Protected App Signals 服务器的网址。

广告技术平台必须将以下字段的占位符替换为正确的网址:

module "buyer" {
  # ... More config here.

  runtime_flags = {
    # ... More config here.

    ENABLE_PROTECTED_APP_SIGNALS                  = "true"
    PROTECTED_APP_SIGNALS_GENERATE_BID_TIMEOUT_MS = "60000"
    TEE_AD_RETRIEVAL_KV_SERVER_ADDR               = "<service mesh address of the instance>"
    AD_RETRIEVAL_TIMEOUT_MS                       = "60000"
    BUYER_CODE_FETCH_CONFIG                       = <<EOF
    {
        "protectedAppSignalsBiddingJsUrl": "<URL to Protected App Signals generateBid UDF>",
        "urlFetchTimeoutMs": 60001, # This has to be > 1 minute.
        "urlFetchPeriodMs": 13000000,
        "prepareDataForAdsRetrievalJsUrl": "<URL to the UDF>"
    }
    EOF

  }  # runtime_flags

}  # Module "buyer"

卖方广告技术平台配置

以此 demo 文件为例,卖方必须设置以下标志。(注意:此处仅突出显示与 Protected App Signals 相关的配置)。广告技术平台需要确保将占位符替换为正确的网址:

module "seller" {
  # ... More config here.

  runtime_flags = {
    # ... More config here.

    ENABLE_PROTECTED_APP_SIGNALS                  = "true"

    SELLER_CODE_FETCH_CONFIG                           = <<EOF
  {
    "urlFetchTimeoutMs": 60001, # This has to be > 1 minute.
    "urlFetchPeriodMs": 13000000,
    "protectedAppSignalsBuyerReportWinJsUrls": {"<Buyer Domain>": "URL to reportWin UDF"}
  }
  EOF

  }  # runtime_flags

}  # Module "seller"

键值和广告检索服务

根据选择用于支持广告检索的策略,系统会 需要部署一个或两个 KV 服务实例。我们会参考 用于基于 TEE 的广告检索的 KV 实例实例, Ad Retrieval Server 添加到实例中,以支持基于上下文路径 作为 KV Lookup Server 进行检索。

在这两种情况下,服务器部署都遵循 KV 服务器 GitHub 上,则这两种情况之间的区别在于,查询 外壳可以直接使用,无需任何额外配置, 检索一个需要部署 HandleRequest UDF 来实现 检索逻辑有关详情,请查看 KV 服务器 新手入门指南。请注意,B&A 要求 部署在与出价服务相同的服务网格中。

设置示例

请考虑以下情形:广告技术平台借助 Protected App Signals API 根据用户应用使用情况存储相关信号。在我们的示例中,存储的信号表示多个应用中的应用内购买交易。在竞价期间,系统会收集加密信号,并将其传递到在 B&A 中运行的 Protected Auction 竞价。在 B&A 中运行的买方 UDF 会根据这些信号来提取候选广告并计算出价。

[买方] 信号示例

添加一个键为 0、值为 1 的信号。

{
  "put": {
    "AA==": "AQ=="
  },
  "update_encoder": {
    "action": "REGISTER",
    "endpoint": "https://example.com/example_script"
  }
}

添加一个键为 1、值为 2 的信号。

{
  "put": {
    "AQ==": "Ag=="
  },
  "update_encoder": {
    "action": "REGISTER",
    "endpoint": "https://example.com/example_script"
  }
}

[买方] encodeSignals 示例

将每个信号编码为两个字节,第一个字节是信号键的第一个字节,第二个字节是信号值的第一个字节。

function encodeSignals(signals, maxSize) {
  // if there are no signals don't write a payload
  if (signals.size === 0) {
      return {};
  }

  let result = new Uint8Array(signals.size * 2);
  let index = 0;
  
  for (const [key, values] of signals.entries()) {
    result[index++] = key[0];
    result[index++] = values[0].signal_value[0];
  }
  
  return { 'status': 0, 'results': result};
}

[买方] prepareDataForAdRetrieval 示例

/**
 * `encodedOnDeviceSignals` is a Uint8Array and would contain
 * the app signals emanating from device. For purpose of the
 * demo, in our sample example, we assume that device is sending
 * the signals with pair of bytes formatted as following:
 * "<id><In app spending>". Where id corresponds to an ad category
 * that user uses on device, and the in app spending is a measure
 * of how much money the user has spent in this app category
 * previously. In our example, id of 0 will correspond to a
 * fitness ad category and a non-zero id will correspond to
 * food app category -- though this info will be useful
 * later in the B&A pipeline.
 *
 * Returns a JSON object indicating what type of ad(s) may be
 * most relevant to the user. In a real setup ad techs might
 * want to decode the signals as part of this script.
 *
 * Note: This example script makes use of only encoded device signals
 * but adtech can take other signals into account as well to prepare
 * the data that will be useful down stream for ad retrieval and
 * bid generation. The max length of the app signals used in this
 * sample example is arbitrarily limited to 4 bytes.
 */
function prepareDataForAdRetrieval(encodedOnDeviceSignals,
                                   encodedOnDeviceSignalsVersion,
                                   sellerAuctionSignals,
                                   contextualSignals) {
  if (encodedOnDeviceSignals.length === 0 || encodedOnDeviceSignals.length > 4 ||
      encodedOnDeviceSignals.length % 2 !== 0) {
     throw "Expected encoded signals length to be an even number in (0, 4]";
  }

  var preparedDataForAdRetrieval = {};
  for (var i = 0; i < encodedOnDeviceSignals.length; i += 2) {
    preparedDataForAdRetrieval[encodedOnDeviceSignals[i]] = encodedOnDeviceSignals[i + 1];
  }
  return preparedDataForAdRetrieval;
}

[买方] 广告检索 UDF 示例

在我们的示例中,广告检索服务器为前 k 个候选广告中的每一个发送元数据(即本示例中每个广告的 ID,但可以包含每个广告的其他数据,这些数据有助于稍后生成出价)。

function HandleRequest(requestMetadata, protectedSignals, deviceMetadata,
                      contextualSignals) {
 return "[{\"adId\":\"0\"},{\"adId\":\"1\"}]"

[买方] generateBid 示例

/**
 * This script receives the data returned by the ad retrieval service
 * in the `ads` argument. This argument is supposed to contain all
 * the Protected App Signals related ads and the metadata obtained from the retrieval
 * service.
 *
 * `preparedDataForAdRetrieval` argument contains the data returned
 * from the `prepareDataForAdRetrieval` UDF.
 *
 * This script is responsible for generating bids for the ads
 * collected from the retrieval service and ad techs can decide to
 * run a small inference model as part of this script in order to
 * decide the best bid given all the signals available to them.
 *
 * For the purpose of the demo, this sample script assumes
 * that ad retrieval service has sent us most relevant ads for the
 * user and this scripts decides on the ad render URL as well as
 * what value to bid for each ad based on the previously decoded
 * device signals. For simplicity sake, this script only considers
 * 2 types of app categories i.e. fitness and food.
 *
 * Note: Only one bid is returned among all the
 * input ad candidates.
 */
function generateBid(ads, sellerAuctionSignals, buyerSignals, preparedDataForAdRetrieval) {
  if (ads === null) {
    console.log("No ads obtained from the ad retrieval service")
    return {};
  }     
        
  const kFitnessAd = "0";
  const kFoodAd = "1";
  const kBuyerDomain = "https://buyer-domain.com";
        
  let resultingBid = 0;
  let resultingRender = kBuyerDomain + "/no-ad";
  for (let i = 0 ; i < ads.length; ++i) {
    let render = "";
    let bid = 0;
    switch (ads[i].adId) {
      case kFitnessAd:
        render = kBuyerDomain + "/get-fitness-app";
        bid = preparedDataForAdRetrieval[kFitnessAd];
        break;
      case kFoodAd:
        render = kBuyerDomain + "/get-fastfood-app";
        bid = preparedDataForAdRetrieval[kFoodAd];
        break;
      default:
        console.log("Unknown ad category");
        render = kBuyerDomain + "/no-ad";
        break;
    }
    console.log("Existing bid: " + resultingBid + ", incoming candidate bid: " + bid);
    if (bid > resultingBid) {
      resultingBid = bid;
      resultingRender = render;
    }
  }
  return {"render": resultingRender, "bid": resultingBid};
}

[买方] reportWin 示例

reportWin UDF 会向买方报告他们在竞价中胜出。

function reportWin(auctionSignals, perBuyerSignals, signalsForWinner,
                                       buyerReportingSignals, directFromSellerSignals,
                                       egressPayload,
                                       temporaryUnlimitedEgressPayload) {
  sendReportTo("https://buyer-controlled-domain.com/");
  registerAdBeacon({"clickEvent":"https://buyer-controlled-domain.com/clickEvent"});
  return;
}

[卖方] KV 服务器设置

卖方必须设置得分信号 KV 服务器,以便提供从广告呈现网址到相应得分信号的映射。例如:如果 https:/buyer-domain.com/get-fitness-apphttps:/buyer-domain.com/get-fastfood-app 由买方返回,那么当 SFE 使用 GEThttps://key-value-server-endpoint.com?client_type=1&renderUrls=<render-url-returned-by-the-buyer> 上进行查询时,卖方可以得到以下示例得分信号响应:

{
   "renderUrls" : {
      "https:/buyer-domain.com/get-fitness-app" : [
         "1",
         "2"
      ],
      "https:/buyer-domain.com/get-fastfood-app" : [
         "3",
         "4"
      ]
   }
}

[Seller] scoreAd 示例

/**
 * This module generates a random desirability score for the Protected App
 * Signals ad in this example. In a production deployment,
 * however, the sellers would want to use all the available signals to generate
 * a score for the ad.
 */
function getRandomInt(max) {
  return Math.floor(Math.random() * max);
}

function scoreAd(adMetadata, bid, auctionConfig,
                                   trustedScoringSignals, deviceSignals,
                                   directFromSellerSignals) {
  return {
    "desirability": getRandomInt(10000),
    "allowComponentAuction": false
  };
}

[卖方] reportResult 示例

function reportResult(auctionConfig, sellerReportingSignals, directFromSellerSignals){
  let signalsForWinner = {};
    sendReportTo("https://seller-controlled-domain.com");
    registerAdBeacon({"clickEvent":
                    "https://seller-controlled-domain.com/clickEvent"});
    return signalsForWinner;
}

示例应用

为了说明如何使用 API 创建采用上述简单流程的应用,我们创建了一个 Protected App Signals 示例应用,该应用可在此示例代码库中找到。