Tích hợp với B&A với tư cách là người bán

Dịch vụ Đặt giá thầu và Phiên đấu giá (B&A) là một nhóm dịch vụ dành cho người mua và người bán quảng cáo, chạy trong Môi trường thực thi đáng tin cậy (TEE) để hỗ trợ phiên đấu giá Protected Audience (PA). Hướng dẫn dành cho nhà phát triển này giải thích cách người bán có thể tích hợp với phiên đấu giá PA của Chrome cho B&A.

Hướng dẫn từng bước

Quy trình tích hợp người bán, trong đó mã JavaScript nhận tải trọng phiên đấu giá B&A được gửi đến SAS và SAS chuyển tiếp yêu cầu đến Seller Front-End (SFE). SFE trả về kết quả mà SAS sẽ chuyển tiếp đến trình duyệt và mã JavaScript của người bán sẽ gọi runAdAuction

Bạn có thể tóm tắt các bước như sau:

  1. Gọi getInterestGroupAdAuctionData() để lấy tải trọng đã mã hoá từ trình duyệt
  2. Gọi fetch('https://your-ad-server.example') và gửi yêu cầu đấu giá hợp nhất có tải trọng đã mã hoá đến SAS của bạn
  3. Gọi thao tác SelectAd() của SFE từ SAS để chạy phiên đấu giá B&A
  4. Trả về kết quả phiên đấu giá B&A cho trang cùng với hàm băm của phản hồi
  5. Gọi runAdAuction() trong trình duyệt để chạy phiên đấu giá PA một người bán, chế độ kết hợp hoặc nhiều người bán, đồng thời truyền kết quả đấu giá B&A phía máy chủ vào lệnh gọi

Nhận dữ liệu đã mã hoá về phiên đấu giá quảng cáo

Sơ đồ hướng dẫn tương tự với bước đầu tiên được đánh dấu, đó là khi mã JavaScript của người bán gọi getInterestGroupAdAuctionData

Để lấy dữ liệu cần thiết để chạy phiên đấu giá B&A phía máy chủ, mã JavaScript của người bán trên trang của nhà xuất bản sẽ gọi 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;
Trường Mô tả
seller Bắt buộc. Quốc gia của người bán đang chạy phiên đấu giá. Giá trị này phải khớp với giá trị seller trong lệnh gọi runAdAuction() sau đó.
requestSize Không bắt buộc. Đặt kích thước tải trọng tối đa của tất cả dữ liệu người mua. Hãy xem phần kích thước yêu cầu trong phần giải thích để tìm hiểu thêm.
perBuyerConfig Không bắt buộc. Đặt cấu hình cho từng người mua, đồng thời kiểm soát những người mua tham gia phiên đấu giá B&A.

Nếu nguồn gốc của người mua được liệt kê trong perBuyerConfig, thì chỉ những dữ liệu nhóm mối quan tâm của người mua đó mới được đưa vào tải trọng. Nếu không có người mua nào được liệt kê trong perBuyerConfig, thì tất cả các nhóm lợi ích của người dùng đều được đưa vào tải trọng.

targetSize Không bắt buộc nếu bạn đặt requestSize. Bắt buộc nếu nguồn gốc của người mua được đặt trong perBuyerConfig nhưng requestSize không được đặt.

Đặt kích thước tải trọng tối đa cho dữ liệu của người mua đó. Hãy xem phần kích thước yêu cầu trong phần giải thích để tìm hiểu thêm.

coordinatorOrigin Không bắt buộc, nhưng cuối cùng sẽ là thông tin bắt buộc. Mặc định là https://publickeyservice.pa.gcp.privacysandboxservices.com khi không được đặt.

Đặt trình điều phối để dùng cho việc tìm nạp khoá nhằm mã hoá tải trọng. Hãy xem phần điều phối viên trong phần giải thích để tìm hiểu thêm.

Khi lệnh gọi được thực hiện, trình duyệt sẽ đọc các nhóm mối quan tâm của người mua có trong perBuyerConfig và mã hoá dữ liệu của người mua. Dữ liệu người mua này chứa thông tin trên nhiều trang web để dùng cho hoạt động đặt giá thầu và không thể giải mã bên ngoài TEE. Để tối ưu hoá tải trọng, chỉ tên nhóm mối quan tâm, các khoá tín hiệu đặt giá thầu đáng tin cậy và tín hiệu của trình duyệt được đưa vào tải trọng.

Trong đối tượng dữ liệu phiên đấu giá quảng cáo do lệnh gọi getInterestGroupAdAuctionData() trả về, chuỗi requestId và mảng byte request đã mã hoá sẽ có sẵn.

Ảnh chụp màn hình Chrome DevTools cho thấy yêu cầu và mã yêu cầu có trong dữ liệu phiên đấu giá quảng cáo.

Chuỗi requestId sẽ được dùng sau đó khi runAdAuction() được gọi để hoàn tất phiên đấu giá trong trình duyệt. Tải trọng request đã mã hoá được gửi đến Dịch vụ quảng cáo của người bán trong yêu cầu đấu giá thống nhất.

Để xem ví dụ về lệnh gọi này, hãy xem mã JavaScript của người bán trong ứng dụng kiểm thử cục bộ.

Gửi yêu cầu đấu giá thống nhất đến SAS

Cũng là sơ đồ quy trình từng bước này nhưng bước thứ hai được làm nổi bật, đó là khi mã JavaScript của người bán gửi một yêu cầu đấu giá hợp nhất đến SAS

Yêu cầu đấu giá hợp nhất là yêu cầu chứa tải trọng đấu giá theo bối cảnh ở dạng văn bản thuần tuý và tải trọng đấu giá B&A của PA. Tải trọng phiên đấu giá B&A của PA là dữ liệu request được mã hoá mà trình duyệt đã tạo trong lệnh gọi getInterestGroupAdAuctionData(). Yêu cầu này được gửi đến SAS, nơi phiên đấu giá theo bối cảnh và phiên đấu giá B&A của PA được điều phối.

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

Để gửi yêu cầu đến SAS, một lệnh gọi fetch() sẽ được thực hiện từ trang:

  • Lệnh gọi phải bao gồm lựa chọn adAuctionHeaders: true, lựa chọn này báo hiệu cho trình duyệt xác minh phản hồi của lệnh gọi này vào thời điểm sau đó khi runAdAuction() được gọi để hoàn tất phiên đấu giá trong trình duyệt.
  • Nguồn gốc của yêu cầu tìm nạp phải khớp với nguồn gốc seller được cung cấp cho các lệnh gọi getInterestGroupAdAuctionData()runAdAuction().

Nội dung của lệnh gọi chứa:

  1. Tải trọng đấu giá theo bối cảnh ở dạng văn bản thuần tuý mà SAS sẽ dùng để chạy phiên đấu giá theo bối cảnh.
  2. Tải trọng phiên đấu giá Protected Audience được mã hoá mà SAS sẽ gửi đến SFE để chạy phiên đấu giá B&A phía máy chủ.

Để xem ví dụ về lệnh gọi này, hãy xem mã JavaScript của người bán trong ứng dụng kiểm thử cục bộ.

Mã hoá và giải mã Base64

Tải trọng request được mã hoá do lệnh gọi getInterestGroupAdAuctionData() trả về là một phiên bản của Uint8Array, đây là một kiểu dữ liệu mà JSON không thể xử lý. Để gửi mảng byte ở định dạng JSON, bạn có thể áp dụng phương thức mã hoá base64 cho dữ liệu nhị phân để chuyển đổi dữ liệu đó thành một chuỗi.

API trình duyệt JavaScript cung cấp các hàm atob()btoa() trên window để chuyển đổi giữa dữ liệu nhị phân và chuỗi ASCII được mã hoá base64. (atob có nghĩa là ASCII sang nhị phân và btoa có nghĩa là nhị phân sang ASCII).

Gọi btoa() để mã hoá dữ liệu nhị phân thành một chuỗi được mã hoá base64 như sau:

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

Kết quả phiên đấu giá B&A được mã hoá do lệnh gọi fetch này trả về cũng ở dạng mã hoá base64, vì vậy, bạn cần giải mã kết quả đó về dữ liệu nhị phân. Gọi atob() để giải mã chuỗi ASCII được mã hoá base64 thành dữ liệu nhị phân:

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

Tuy nhiên, một chuỗi được mã hoá base64 thường lớn hơn khoảng 33% so với dữ liệu ban đầu. Nếu muốn cải thiện thêm độ trễ, hãy sử dụng một định dạng khác ngoài JSON để gửi dữ liệu nhị phân.

Gọi SelectAd của SFE để chạy phiên đấu giá B&A

Sơ đồ hướng dẫn tương tự với bước thứ ba được làm nổi bật, đó là khi SAS gửi yêu cầu SelectAd đến SFE và SFE chạy phiên đấu giá B&A

Sau khi Dịch vụ quảng cáo của người bán nhận được yêu cầu đấu giá thống nhất từ trang, phiên đấu giá theo bối cảnh sẽ chạy trước để xác định người chiến thắng trong phiên đấu giá theo bối cảnh và thu thập các tín hiệu của người mua để chuyển vào phiên đấu giá B&A của PA. Sau đó, phiên đấu giá B&A được bắt đầu bằng cách gọi thao tác SelectAd của SFE từ SAS bằng tải trọng yêu cầu. Xin lưu ý rằng một số siêu dữ liệu từ yêu cầu của trang đối với SAS trong bước 2 sẽ được chuyển tiếp đến SFE.

Tạo tải trọng SelectAdRequest

Bạn có thể tạo tải trọng yêu cầu của lệnh gọi SelectAd như sau:

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)
};

Xin lưu ý rằng nếu dữ liệu đấu giá quảng cáo đã mã hoá từ trình duyệt được mã hoá base64, thì dữ liệu đó cần được giải mã trở lại thành dữ liệu nhị phân nếu yêu cầu đến SFE được gửi bằng gRPC. Nếu yêu cầu được gửi bằng HTTP, thì dữ liệu đấu giá quảng cáo đã mã hoá có thể vẫn ở dạng được mã hoá base64.

Để xem các trường khác được xác định trong yêu cầu SelectAd, hãy xem định nghĩa proto của SelectAdRequest.

Đặt trường người bán cấp cao nhất cho phiên đấu giá ở chế độ kết hợp và phiên đấu giá thành phần

Nếu người bán đang chạy một phiên đấu giá ở chế độ kết hợp hoặc tham gia với tư cách là người bán thành phần trong một phiên đấu giá nhiều người bán, thì bạn cần xác định trường top_level_seller trong yêu cầu.

Nếu bạn là người bán ở chế độ kết hợp, giá trị top_level_seller là nguồn gốc của bạn:

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

Nếu bạn là người bán linh kiện, giá trị top_level_seller là người bán cấp cao nhất của phiên đấu giá nhiều người bán:

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

Gọi cho SelectAd của SFE

Bạn có thể thực hiện cuộc gọi đến SFE từ SAS bằng gRPC hoặc HTTP.

Cuộc gọi gRPC

Yêu cầu gRPC đến SFE có dạng như sau khi sử dụng Express trong Node với một gRPC client:

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
});

Bạn có thể tìm thấy định nghĩa proto cho ứng dụng SFE trong kho lưu trữ ứng dụng kiểm thử cục bộ.

Lệnh gọi HTTP đến proxy Envoy

Yêu cầu HTTP POST đến SFE được gửi đến đường dẫn /v1/selectAd và có dạng như sau:

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

Chuyển tiếp siêu dữ liệu

Bạn nên thêm siêu dữ liệu sau đây từ lệnh gọi của trang đến SAS vào lệnh gọi SelectAd của SAS đến SFE:

Khi gửi siêu dữ liệu đến SFE, siêu dữ liệu phải sử dụng các tiêu đề không chuẩn sau đây vì gRPC có thể thay đổi tiêu đề User-Agent:

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

Sau đây là ví dụ về cách chuyển tiếp siêu dữ liệu bằng Express trong Node bằng máy khách gRPC:

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);
})

Sau đây là ví dụ về cách chuyển tiếp siêu dữ liệu bằng lệnh gọi 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)
  });
})

Phiên đấu giá có nhiều người bán do máy chủ điều phối

Nếu bạn là người bán cấp cao nhất đang chạy một phiên đấu giá nhiều người bán do máy chủ điều phối, thì lệnh gọi GetComponentAuctionCiphertexts sẽ được thực hiện đến SFE trước khi lệnh gọi SelectAd được thực hiện. Phản hồi này chứa các tải trọng phiên đấu giá thành phần được mã hoá lại và gửi đến các dịch vụ quảng cáo của người bán thành phần. Kết quả đấu giá quảng cáo B&A của thành phần được trả về sẽ được cung cấp cho lệnh gọi SelectAd của SFE người bán cấp cao nhất.

Hãy xem tài liệu giải thích về nhiều người bán trên GitHub để tìm hiểu thêm.

Trả kết quả phiên đấu giá B&A về trang

Cũng là sơ đồ hướng dẫn có bước thứ tư được làm nổi bật, đó là khi SAS gửi kết quả đấu giá của SelectAd trở lại trình duyệt

Sau khi phiên đấu giá B&A kết thúc, kết quả phiên đấu giá được mã hoá sẽ được trả về SAS và SAS sẽ phản hồi yêu cầu phiên đấu giá thống nhất từ trang ở bước 2 bằng kết quả phiên đấu giá được mã hoá. Trong phản hồi SAS đối với trang, hàm băm SHA-256 được mã hoá base64url của kết quả phiên đấu giá được mã hoá sẽ được đặt trong tiêu đề phản hồi Ad-Auction-Result. Trình duyệt dùng hàm băm này để xác minh tải trọng khi hoàn tất phiên đấu giá trong ứng dụng.

Việc tạo hàm băm SHA-256 bằng phương thức mã hoá base64 sẽ có dạng như sau trong Node:

import { createHash } from 'crypto';

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

Việc đính kèm hàm băm trong tiêu đề phản hồi và trả về kết quả phiên đấu giá cho trang sẽ có dạng như sau:

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()
    });
  });
})

Vì đây là phản hồi cho yêu cầu phiên đấu giá hợp nhất được thực hiện từ trang ở bước 2, nên kết quả phiên đấu giá theo bối cảnh cũng được đưa vào phản hồi.

Bạn có thể thêm nhiều hàm băm vào Ad-Auction-Result bằng cách lặp lại tiêu đề hoặc phân tách các hàm băm. Hai tiêu đề phản hồi sau đây tương đương nhau:

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

Để xem ví dụ về lệnh gọi này, hãy xem mã máy chủ của người bán trong ứng dụng kiểm thử cục bộ.

Gọi runAdAuction() để hoàn tất phiên đấu giá

Sơ đồ hướng dẫn tương tự với bước thứ năm được làm nổi bật, đó là khi mã JavaScript phía máy khách chạy phiên đấu giá và cung cấp phản hồi của máy chủ.

Phản hồi phiên đấu giá hợp nhất do SAS trả về bao gồm kết quả phiên đấu giá B&A đã mã hoá. Tải trọng này được truyền vào lệnh gọi runAdAuction() để hoàn tất phiên đấu giá trong trình duyệt. Giá trị requestId từ lệnh gọi getInterestGroupAdAuctionData() trong bước 1 cũng được truyền vào phiên đấu giá.

// 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
});

Cấu trúc của cấu hình đấu giá được truyền vào lệnh gọi runAdAuction() sẽ khác nhau tuỳ thuộc vào cấu hình đấu giá mà người bán chọn.

Phiên đấu giá của một người bán

Để chạy một phiên đấu giá B&A của một người bán, cấu hình đấu giá của lệnh gọi runAdAuction() được tạo như sau:

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

Trường requestId chấp nhận requestId do lệnh gọi getInterestGroupAdAuctionData() trả về. Trường serverResponse chấp nhận một mảng byte của phiên đấu giá B&A đã chạy trong bước 3.

Để xem ví dụ về lệnh gọi này, hãy xem mã JavaScript của người bán trong ứng dụng kiểm thử cục bộ.

Phiên đấu giá ở chế độ kết hợp

Để chạy phiên đấu giá B&A ở chế độ kết hợp, trong đó cả người mua trên thiết bị và người mua B&A đều có thể tham gia, cấu hình đấu giá của lệnh gọi runAdAuction() được tạo như sau:

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
      ],
    },
  ]
});

Để tạo điều kiện cho phiên đấu giá ở chế độ kết hợp, kết quả đấu giá B&A và cấu hình đấu giá trên thiết bị sẽ được truyền vào trường componentAuctions. Trong phiên đấu giá ở chế độ kết hợp, giá trị seller sẽ giống nhau cho cả cấu hình cấp cao nhất và cấu hình thành phần.

Để xem ví dụ về lệnh gọi này, hãy xem mã JavaScript của người bán trong ứng dụng kiểm thử cục bộ.

Phiên đấu giá nhiều người bán

Nếu bạn là người bán cấp cao nhất đang chạy phiên đấu giá nhiều người bán do thiết bị điều phối, thì mỗi người bán thành phần sẽ gửi kết quả đấu giá B&A và cấu hình đấu giá trên thiết bị.

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',
    }
  ]
})

Để xem ví dụ về lệnh gọi này, hãy xem mã JavaScript của người bán trong ứng dụng kiểm thử cục bộ.

Các bước tiếp theo

Sau khi đọc hướng dẫn này, bạn có thể thực hiện các bước tiếp theo sau đây:

Tìm hiểu thêm

Bạn có thắc mắc?