作为卖家与 B&A 集成

出价和竞价 (B&A) 服务是一组面向广告买方和卖方的服务,可在可信执行环境 (TEE) 中运行,以促成 Protected Audience (PA) 竞价。本开发者指南介绍了卖家如何与 Chrome PA 竞价集成,以实现 B&A。

演示

卖家集成流程,其中 JavaScript 代码获取发送到 SAS 的 B&A 竞价载荷,而 SAS 将请求转发到卖家前端 (SFE)。SFE 返回 SAS 应转发给浏览器的结果,并且卖方 JavaScript 代码调用 runAdAuction

这些步骤可总结如下:

  1. 调用 getInterestGroupAdAuctionData() 从浏览器获取加密载荷
  2. 调用 fetch('https://your-ad-server.example') 并将包含加密载荷的统一竞价请求发送到您的 SAS
  3. 从 SAS 调用 SFE 的 SelectAd() 操作来运行 B&A 竞价
  4. 将 B&A 竞价结果以及响应的哈希值返回到网页
  5. 在浏览器中调用 runAdAuction() 以运行单卖家、混合模式或多卖家 PA 竞价,并将服务器端 B&A 竞价结果传递给该调用

获取加密的广告竞价数据

同一图解,但突出显示了第一步,即卖家 JavaScript 代码调用 getInterestGroupAdAuctionData 的时间

为了获取运行服务器端 B&A 竞价所需的数据,发布商网页上的卖方 JavaScript 代码会调用 navigator.getInterestGroupAdAuctionData()

const adAuctionData = await navigator.getInterestGroupAdAuctionData({
  seller: 'https://ssp.example', // Required
  requestSize: 51200,
  coordinatorOrigin: 'https://publickeyservice.pa.gcp.privacysandboxservices.com/',
  perBuyerConfig: {
    'https://dsp-x.example': { targetSize: 8192 },
    'https://dsp-y.example': { targetSize: 8192 }
  }
});

const { requestId, request } = adAuctionData;
字段 说明
seller 必需。运行竞价的卖家的来源。此值必须与后续 runAdAuction() 调用中的 seller 值一致。
requestSize 可选。设置所有买方数据的最大载荷大小。如需了解详情,请参阅说明中的请求大小部分。
perBuyerConfig 可选。为每个买方设置配置,并控制哪些买方参与 B&A 竞价。

如果买方来源列在 perBuyerConfig 中,则只有这些买方兴趣群体数据会包含在载荷中。如果 perBuyerConfig 中未列出任何买方,则相应用户的全部兴趣群体都会包含在载荷中。

targetSize 如果设置了 requestSize,则为可选。如果 perBuyerConfig 中设置了买方来源,但未设置 requestSize,则此字段为必需

设置相应买方数据的载荷大小上限。如需了解详情,请参阅说明中的请求大小部分。

coordinatorOrigin 可选,但最终会成为必需。如果未设置,则默认为 https://publickeyservice.pa.gcp.privacysandboxservices.com

设置用于获取密钥以加密载荷的协调器。如需了解详情,请参阅说明中的协调器部分。

发出调用时,浏览器会读取 perBuyerConfig 中列出的买家的兴趣群体,并对买家数据进行加密。此买方数据包含用于出价的跨网站信息,无法在 TEE 之外解密。对于载荷优化,载荷中仅包含兴趣组名称、可信竞价信号键和浏览器信号。

getInterestGroupAdAuctionData() 调用返回的广告竞价数据对象中,requestId 字符串和加密的 request 字节数组均可用。

Chrome 开发者工具屏幕截图,显示了广告竞价数据中提供的请求和请求 ID。

requestId 字符串稍后会在调用 runAdAuction() 以在浏览器中完成竞价时使用。加密后的 request 载荷会作为统一竞价请求的一部分发送给卖方广告服务。

如需查看此调用的示例,请参阅本地测试应用的卖方 JavaScript 代码

向 SAS 发送统一竞价请求

同一图解,但突出显示了第二步,即卖家 JavaScript 代码向 SAS 发送统一竞价请求

统一的竞价请求是指包含纯文本内容相关广告竞价载荷和 PA B&A 竞价载荷的请求。PA B&A 竞价载荷是浏览器在 getInterestGroupAdAuctionData() 调用中生成的加密 request 数据。此请求会发送到 SAS,在其中协调内容相关广告竞价和 PA B&A 竞价。

fetch('https://ssp.example/ad-auction', {
  method: 'POST',
  adAuctionHeaders: true,
  body: JSON.stringify({
    contextualAuctionPayload: { somePayload },
    protectedAudienceAuctionPayload: encodeBinaryData(request)
  }),
});

如需向 SAS 发送请求,请从网页中调用 fetch()

  • 该调用必须包含 adAuctionHeaders: true 选项,该选项会向浏览器发出信号,要求其在稍后调用 runAdAuction() 以完成浏览器中的竞价时验证此调用的响应。
  • 提取请求的来源必须与提供给 getInterestGroupAdAuctionData()runAdAuction() 调用的 seller 来源相匹配。

调用的正文包含:

  1. 供 SAS 用于运行内容相关竞价的纯文本内容相关竞价载荷。
  2. 由 SAS 发送给 SFE 以运行服务器端 B&A 竞价的加密 Protected Audience 竞价载荷。

如需查看此调用的示例,请参阅本地测试应用的卖方 JavaScript 代码

Base64 编码和解码

getInterestGroupAdAuctionData() 调用返回的加密 request 载荷是 Uint8Array 的实例,而 JSON 无法处理这种数据类型。如需以 JSON 格式发送字节数组,您可以对二进制数据应用 base64 编码,将其转换为字符串。

JavaScript 浏览器 API 在 window 上提供了 atob()btoa() 函数,用于在二进制数据和 base64 编码的 ASCII 字符串之间进行转换。(atob 表示 ASCII 到二进制,btoa 表示二进制到 ASCII)。

调用 btoa() 将二进制数据编码为 base64 编码的字符串,如下所示:

function encodeBinaryData(data) {
  return btoa(String.fromCharCode.apply(null, data));
}

通过此 fetch 调用返回的加密 B&A 竞价结果也采用 base64 编码,因此您需要将其解码回二进制数据。调用 atob() 将 base64 编码的 ASCII 字符串解码为二进制数据:

function decodeBase64String(base64string) {
  return new Uint8Array(
    atob(base64string)
      .split('')
      .map((char) => char.charCodeAt(0))
  );
}

不过,采用 base64 编码的字符串通常比原始数据大 33% 左右。如果您想进一步缩短延迟时间,请使用 JSON 以外的格式发送二进制数据。

调用 SFE 的 SelectAd 来运行 B&A 竞价

同一图解流程图,但突出显示了第三步,即 SAS 向 SFE 发送 SelectAd 请求,而 SFE 运行 B&A 竞价

卖方广告服务从网页收到统一竞价请求后,会先运行内容相关竞价,以确定内容相关竞价的胜出者,并收集要传递到 PA B&A 竞价中的买方信号。然后,通过使用请求载荷从 SAS 调用 SFE 的 SelectAd 操作来启动 B&A 竞价。请注意,在第 2 步中,网页向 SAS 发出的请求中的某些元数据会转发给 SFE。

构建 SelectAdRequest 载荷

SelectAd 调用的请求载荷可以按如下方式构建:

const selectAdRequest = {
  auction_config: {
    seller: 'https://ssp.example',
    auction_signals: '{"testKey":"someValue"}',
    seller_signals: '{"testKey":"someValue"}',
    buyer_list: [
      'https://dsp-x.example',
      'https://dsp-y.example',
    ],
    per_buyer_config: {
      'https://dsp-x.example': { buyer_signals: '{"testKey": "someValue"}' },
      'https://dsp-y.example': { buyer_signals: '{"testKey": "someValue"}' },
    },
  },
  client_type: 'CLIENT_TYPE_BROWSER',
  protected_auction_ciphertext: decodeBase64string(request)
};

请注意,如果浏览器中的加密广告竞价数据采用的是 base64 编码,那么在通过 gRPC 向 SFE 发送请求时,需要将该数据解码回二进制数据。如果请求是使用 HTTP 发送的,则加密的广告竞价数据可以保持其 base64 编码形式。

如需查看 SelectAd 请求中定义的其他字段,请参阅 SelectAdRequestproto 定义

为混合模式和组件竞价设置顶级卖家字段

如果卖家正在进行混合模式竞价,或者以组件卖家的身份参与多卖家竞价,则需要在请求中定义 top_level_seller 字段。

如果您是混合模式的卖家,则 top_level_seller 值是您的来源:

const selectAdRequest = {
  auction_config: {
    seller: 'https://ssp-mix.example',
    top_level_seller: 'https://ssp-mix.example',
  }
}

如果您是组件卖家,则 top_level_seller 值是多卖家竞价的顶级卖家:

const selectAdRequest = {
  auction_config: {
    seller: 'https://ssp-mix.example',
    top_level_seller: 'https://ssp-top.example',
  }
}

调用 SFE 的 SelectAd

从 SAS 对 SFE 的调用可以通过 gRPC 或 HTTP 进行。

gRPC 调用

使用 Node 中的 ExpressgRPC 客户端向 SFE 发出的 gRPC 请求如下所示:

import grpc from '@grpc/grpc-js';

// Load proto definition
const packageDefinition = protoLoader.loadSync(protoPath, { keepCase: true, enums: String });

const {
  privacy_sandbox: {
    bidding_auction_servers: { SellerFrontEnd }
  }
} = grpc.loadPackageDefinition(packageDefinition);

// Instantiate the gRPC client
const sfeGrpcClient = new SellerFrontEnd('192.168.84.104:50067', grpc.credentials.createInsecure());

// Send SelectAd request
sfeGrpcClient.selectAd(selectAdRequest,(error, response) => {
  // Handle SFE response
});

SFE 客户端的 proto 定义可在 本地测试应用代码库中找到。

对 Envoy 代理的 HTTP 调用

发送到 SFE 的 HTTP POST 请求会发送到 /v1/selectAd 路径,如下所示:

fetch('https://ssp-ba.example/sfe/v1/selectAd', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(selectAdRequest),
});

转发元数据

应将网页对 SAS 的调用中的以下元数据添加到 SAS 对 SFE 的 SelectAd 调用中:

当元数据发送到 SFE 时,必须使用以下非标准标头,因为 gRPC 可能会更改 User-Agent 标头:

  • X-Accept-Language
  • X-User-Agent
  • X-BnA-Client-IP

以下示例展示了如何使用 gRPC 客户端Node 中通过 Express 转发元数据:

sellerAdService.post('/ad-auction', (req, res) => {
  // …
  const metadata = new grpc.Metadata();
  metadata.add('X-Accept-Language', req.header('Accept-Language'));
  metadata.add('X-User-Agent', req.header('User-Agent'));
  metadata.add('X-BnA-Client-IP', req.ip);

  const sfeGrpcClient = createSfeGrpcClient();
  sfeGrpcClient.selectAd(selectAdRequest, metadata, callbackFn);
})

以下示例展示了如何使用 HTTP 调用转发元数据:

sellerAdService.post('/ad-auction', (req, res) => {
  // …
  fetch('https://ssp-ba.example/sfe/v1/selectAd', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Accept-Language': req.header('Accept-Language'),
      'X-User-Agent': req.header('User-Agent'),
      'X-BnA-Client-IP': req.ip
    },
    body: JSON.stringify(selectAdRequest)
  });
})

服务器编排的多卖家竞价

如果您是运行服务器编排的多卖家竞价的顶级卖家,则会在发出 SelectAd 调用之前先向 SFE 发出 GetComponentAuctionCiphertexts 调用。响应包含重新加密的组件竞价载荷,这些载荷会发送给组件卖方广告服务。返回的组件 B&A 广告竞价结果会提供给顶级卖方 SFE 的 SelectAd 调用。

如需了解详情,请参阅 GitHub 上的多卖家说明

将 B&A 竞价结果返回到网页

同一图文解说图,其中突出显示了第四步,即 SAS 将 SelectAd 的竞价结果发送回浏览器

B&A 竞价结束后,加密的竞价结果会返回到 SAS,然后 SAS 会使用加密的竞价结果来响应网页在第 2 步中发出的统一竞价请求。在针对网页的 SAS 响应中,加密后的竞价结果的 base64url 编码 SHA-256 哈希设置在 Ad-Auction-Result 响应标头中。此哈希由浏览器用于在客户端完成竞价时验证载荷。

使用 base64 编码创建 SHA-256 哈希在 Node 中如下所示:

import { createHash } from 'crypto';

createHash('sha256')
  .update(binaryData, 'base64')
  .digest('base64url');

在响应标头中附加哈希并将竞价结果返回到网页,如下所示:

sellerAdService.post('/ad-auction', (req, res) => {
  // …
  sfeGrpcClient.selectAd(selectAdRequest, metadata, (error, response) => {
    const { auction_result_ciphertext } = response;

    const ciphertextShaHash = createHash('sha256')
      .update(auction_result_ciphertext, 'base64')
      .digest('base64url');

    res.set('Ad-Auction-Result', ciphertextShaHash);

    res.json({
      protectedAudienceAuctionResult: encodeBinaryData(auction_result_ciphertext),
      contextualAuctionResult: getContextualAuctionResult()
    });
  });
})

由于这是对在第 2 步中从网页发出的统一竞价请求的响应,因此响应中还包含内容相关竞价结果。

您可以在 Ad-Auction-Result 中添加多个哈希,方法是重复使用该标头或分隔哈希。以下两个响应标头是等效的:

Ad-Auction-Result: ungWv48Bz-pBQUDeXa4iI7ADYaOWF3qctBD_YfIAFa0=,9UTB-u-WshX66Xqz5DNCpEK9z-x5oCS5SXvgyeoRB1k=
Ad-Auction-Result: ungWv48Bz-pBQUDeXa4iI7ADYaOWF3qctBD_YfIAFa0=
Ad-Auction-Result: 9UTB-u-WshX66Xqz5DNCpEK9z-x5oCS5SXvgyeoRB1k=

如需查看此调用的示例,请参阅本地测试应用的卖家服务器代码

调用 runAdAuction() 以完成竞价

与上图相同的演练图,但突出显示了第五步,即客户端 JavaScript 代码运行竞价并提供服务器响应。

从 SAS 返回的统一竞价响应包含加密的 B&A 竞价结果。此载荷会传递到 runAdAuction() 调用中,以在浏览器中完成竞价。第 1 步getInterestGroupAdAuctionData() 调用的 requestId 值也会传递到竞价中。

// Get the encrypted ad auction data (Step #1)
const { requestId, request } = navigator.getInterestGroupAdAuctionData(adAuctionDataConfig)

// Send unified auction request (Step #2)
const response = await fetch('https://ssp-ba.example/ad-auction', {
  method: 'POST',
  body: JSON.stringify({
    adAuctionRequest: encodeBinaryData(request),
  }),
});

const { protectedAudienceAuctionResult } = await response.json();

// Finish the auction in the browser
await navigator.runAdAuction({
  // pass in "requestId" and "protectedAudienceAuctionResult"
  // the config structure will differ based on the auction configuration
});

传递到 runAdAuction() 调用中的竞价配置的结构因卖家选择的竞价配置而异。

单卖家竞价

对于运行单卖方 B&A 竞价,runAdAuction() 调用的竞价配置的构建方式如下:

await navigator.runAdAuction({
  seller: 'https://ssp-ba.example',
  requestId,
  serverResponse: protectedAudienceAuctionResult,
});

requestId 字段接受 getInterestGroupAdAuctionData() 调用返回的 requestIdserverResponse 字段接受在第 3 步中运行的 B&A 竞价的字节数组。

如需查看此调用的示例,请参阅本地测试应用的卖方 JavaScript 代码

混合模式竞价

如需运行混合模式的 B&A 竞价(设备端买方和 B&A 买方均可参与),runAdAuction() 调用的竞价配置将按如下方式构建:

await navigator.runAdAuction({
  seller: 'https://ssp-mix.example',
  decisionLogicURL: 'https://ssp-mix.example/score-ad.js',
  componentAuctions: [
    // B&A auction result
    {
      seller: 'https://ssp-mix.example',
      requestId,
      serverResponse: protectedAudienceAuctionResult,
    },
    // On-device auction config
    {
      seller: 'https://ssp-mix.example',
      decisionLogicURL: 'https://ssp-mix.example/on-device-score-ad.js',
      interestGroupBuyers: [
        'https://dsp-a.example', // On-device buyer
        'https://dsp-a.example', // On-device buyer
      ],
    },
  ]
});

为了促成混合模式竞价,B&A 竞价结果和设备端竞价配置会传递到 componentAuctions 字段中。在混合模式竞价中,顶级配置和组件配置的 seller 值相同。

如需查看此调用的示例,请参阅本地测试应用的卖方 JavaScript 代码

多卖家竞价

如果您是运行设备编排的多卖家竞价的顶级卖家,则每个组件卖家都会提交其 B&A 竞价结果和设备端竞价配置。

await navigator.runAdAuction({
  seller: 'https://ssp-top.example',
  decisionLogicURL: 'https://ssp-top.example/score-ad.js',
  componentAuctions: [
    // SSP-BA's B&A-only auction result
    {
      seller: 'https://ssp-ba.example',
      requestId: 'g8312cb2-da2d-4e9b-80e6-e13dec2a581c',
      serverResponse: Uint8Array(560) [193, 120, 4, ] // Encrypted B&A auction result
    },
    // SSP-MIX's B&A auction result
    {
      seller: 'https://ssp-mix.example',
      requestId: 'f5135cb2-da2d-4e9b-80e6-e13dec2a581c',
      serverResponse: Uint8Array(560) [133, 20, 4, ] // Encrypted B&A auction result
    }.
    // SSP-MIX's on-device auction config
    {
      seller: 'https://ssp-mix.example',
      interestGroupBuyers: ['https://dsp-a.example', 'https://dsp-b.example'],
      decisionLogicURL: 'https://ssp-mix.example/score-ad.js',
    }
    // SSP-OD's on-device auction config
    {
      seller: 'https://ssp-od.example',
      interestGroupBuyers: ['https://dsp-a.example', 'https://dsp-b.example'],
      decisionLogicURL: 'https://ssp-od.example/score-ad.js',
    }
  ]
})

如需查看此调用的示例,请参阅本地测试应用的卖方 JavaScript 代码

后续步骤

阅读本指南后,您可以执行以下后续步骤:

了解详情

有疑问?