作为卖家与 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

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

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

将统一竞价请求发送到 SAS

相同的演示文稿图,其中突出显示了第二步,即卖方 JavaScript 代码向 SAS 发送统一竞价请求

统一竞价请求是指包含明文内容相关广告竞价载荷和 PA B&A 竞价载荷的请求。PA B&A 竞价载荷是浏览器在 getInterestGroupAdAuctionData() 调用中生成的加密 request 数据。此请求会发送到 SAS,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. 加密的 Protected Audience 竞价载荷,由 SAS 发送到 SFE 以运行服务器端 B&A 竞价。

如需查看此调用的示例,请参阅本地测试应用的卖方 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 请求中定义的其他字段,请参阅 SelectAdRequest 的 proto 定义

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

如果卖方正在运行混合模式竞价,或者作为组件卖方参与多卖家竞价,则需要在请求中定义 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

您可以使用 gRPC 或 HTTP 从 SAS 调用 SFE。

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

以下示例展示了如何在 Node 中使用 gRPC 客户端通过 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)
  });
})

服务器协调的多卖家竞价

如果您是运行服务器协调的多卖方竞价的顶级卖方,则系统会先向 SFE 发出 GetComponentAuctionCiphertexts 调用,然后再发出 SelectAd 调用。响应包含发送到组件卖方广告服务的重新加密的组件竞价载荷。返回的组件 B&A 广告竞价结果会提供给顶级卖方的 SFE 的 SelectAd 调用。

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

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

相同的演示文稿图,其中突出显示了第 4 步,即 SAS 将 SelectAd 的竞价结果发回给浏览器

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

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

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 代码

后续步骤

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

了解详情

有疑问?