שילוב עם B&A בתור מוכרים

שירותי בידינג ומכרזים (B&A) הם קבוצה של שירותים לקונים ולמוכרים של מודעות, שפועלים בסביבת מחשוב אמינה (TEE) כדי להקל על מכרז של קהל מוגן (PA). במדריך הזה למפתחים מוסבר איך מוֹכרים יכולים להשתלב במכרז של Chrome PA לקנייה ולמכירה של מודעות.

הדרכה מפורטת

תהליך השילוב של המוכר שבו קוד ה-JavaScript מקבל את מטען הנתונים של מכרז B&A שנשלח ל-SAS, ו-SAS מעביר את הבקשה ל-Seller Front-End ‏ (SFE). ‫SFE מחזיר את התוצאה ש-SAS צריך להעביר לדפדפן, וקוד ה-JavaScript של המוכר קורא ל-runAdAuction

אלה השלבים בתהליך:

  1. מתקשרים אל getInterestGroupAdAuctionData() כדי לקבל את מטען הנתונים המוצפן מהדפדפן
  2. מתקשרים אל fetch('https://your-ad-server.example') ושולחים את בקשת המכרז המאוחד עם מטען ייעודי מוצפן אל ה-SAS
  3. מתקשרים לפעולה של SelectAd() SFE מ-SAS כדי להפעיל את מכרז B&A
  4. החזרת תוצאת המכרז של B&A לדף יחד עם הגיבוב של התגובה
  5. מתקשרים אל runAdAuction() בדפדפן כדי להפעיל מכרז PA של אתר מכירה יחיד, מצב מעורב או כמה אתרי מכירה, ומעבירים את תוצאת מכרז ה-B&A בצד השרת אל הקריאה

קבלת נתונים מוצפנים של מכרזים על שטחי פרסום

אותו תרשים של תהליך העבודה עם השלב הראשון מודגש, שבו קוד ה-JavaScript של המוכר קורא ל-getInterestGroupAdAuctionData

כדי לקבל את הנתונים שנדרשים להפעלת מכרז הבידינג והניתוח בצד השרת, קוד ה-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 נדרש. מקור המוכר שמנהל את המכרז. הערך הזה צריך להיות זהה לערך seller בהפעלה של runAdAuction() בהמשך.
requestSize אופציונלי. הגדרה של הגודל המקסימלי של מטען הייעודי (payload) של כל נתוני הקונים. מידע נוסף זמין בקטע גודל הבקשה במאמר ההסבר.
perBuyerConfig אופציונלי. הוא מגדיר את ההגדרות לכל קונה, וגם קובע אילו קונים משתתפים במכרז של בידינג וניתוח.

אם מקורות הקונים מפורטים ב-perBuyerConfig, רק נתוני קבוצות האינטרסים של הקונים האלה נכללים במטען הייעודי (payload). אם לא מופיעים קונים ב-perBuyerConfig, כל קבוצות המשתמשים עם תחומי עניין משותפים נכללות במטען הייעודי (payload).

targetSize אופציונלי אם הערך של requestSize מוגדר. חובה אם מקור הקונה מוגדר ב-perBuyerConfig אבל requestSize לא מוגדר.

הגדרה של גודל המטען הייעודי המקסימלי של נתוני הקונה. מידע נוסף זמין בקטע גודל הבקשה במאמר ההסבר.

coordinatorOrigin אופציונלי, אבל בסופו של דבר יהיה חובה. אם לא מגדירים ערך, ברירת המחדל היא https://publickeyservice.pa.gcp.privacysandboxservices.com.

מגדיר את הרכיב המתאם שישמש לאחזור המפתח להצפנת המטען הייעודי. מידע נוסף זמין בקטע המתאם בהסבר.

כשמתבצעת הקריאה, הדפדפן קורא את קבוצות האינטרס של הקונים שמפורטים ב-perBuyerConfig, ומצפין את נתוני הקונים. נתוני הקונים האלה מכילים מידע חוצה אתרים שמשמש לבידינג, ואי אפשר לפענח אותם מחוץ לסביבת TEE. לצורך אופטימיזציה של מטען ייעודי (payload), המטען הייעודי כולל רק את השם של קבוצת המתעניינים, מפתחות של אותות בידינג מהימנים ואותות מהדפדפן.

באובייקט הנתונים של מכרז המודעות שמוחזר מהקריאה getInterestGroupAdAuctionData(), זמינים המחרוזת requestId ומערך הבייטים המוצפן request.

צילום מסך של כלי הפיתוח ל-Chrome שבו אפשר לראות שהבקשה ומזהה הבקשה זמינים בנתוני המכרז של המודעות.

מחרוזת requestId משמשת בהמשך כשקוראים ל-runAdAuction() כדי לסיים את המכרז בדפדפן. מטען הייעודי (payload) המוצפן request נשלח אל Seller Ad Service כחלק מהבקשה למכרז המאוחד.

דוגמה לקריאה הזו מופיעה בקוד JavaScript של המוכר באפליקציית הבדיקה המקומית.

שליחת הבקשה למכרז המאוחד אל SAS

אותו תרשים של תהליך העבודה עם השלב השני מודגש, שבו קוד ה-JavaScript של המוכר שולח בקשה מאוחדת למכרז אל SAS

בקשה מאוחדת למכרז היא בקשה שמכילה את המטען הייעודי (payload) של המכרז ההקשרי בטקסט פשוט ואת המטען הייעודי (payload) של המכרז של PA B&A. מטען הייעודי (payload) של המכרז ב-PA B&A הוא הנתונים המוצפנים request שהדפדפן יצר בקריאה getInterestGroupAdAuctionData(). הבקשה הזו נשלחת אל 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() כדי לסיים את המכרז בדפדפן.
  • המקור של בקשת האחזור חייב להיות זהה למקור seller שצוין בקריאות ל-getInterestGroupAdAuctionData() ול-runAdAuction().

גוף הקריאה מכיל:

  1. מטען הייעודי (payload) של המכרז ההקשרי בטקסט פשוט, שבו SAS ישתמש כדי להריץ את המכרז ההקשרי.
  2. מטען הייעודי (payload) של מכרז Protected Audience המוצפן שיישלח אל SFE על ידי SAS כדי להריץ את מכרז B&A מצד השרת.

דוגמה לקריאה הזו מופיעה בקוד JavaScript של המוכר באפליקציית הבדיקה המקומית.

קידוד ופענוח Base64

המטען הייעודי (payload) המוצפן request שמוחזר מהקריאה getInterestGroupAdAuctionData() הוא מופע של Uint8Array, שהוא סוג נתונים ש-JSON לא יכול לטפל בו. כדי לשלוח את מערך הבייטים בפורמט JSON, אפשר להחיל קידוד base64 על הנתונים הבינאריים כדי להמיר אותם למחרוזת.

‫JavaScript browser API מספק את הפונקציות atob() ו-btoa() ב-window שמבצעות המרה בין נתונים בינאריים לבין מחרוזת ASCII בקידוד Base64. ‫(atob פירושו ASCII לבינארי, ו-btoa פירושו בינארי ל-ASCII).

קריאה ל-btoa() כדי לקודד נתונים בינאריים למחרוזת בקידוד base64 נראית כך:

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

תוצאת המכרז המוצפנת של B&A שמוחזרת מהקריאה fetch הזו מקודדת גם היא בפורמט Base64, ולכן צריך לפענח אותה בחזרה לנתונים בינאריים. קוראים ל-atob() כדי לפענח את מחרוזת ה-ASCII בקידוד Base64 לנתונים בינאריים:

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

עם זאת, מחרוזת בקידוד Base64 בדרך כלל גדולה בכ-33% מהנתונים המקוריים. אם רוצים לשפר עוד יותר את זמן האחזור, צריך להשתמש בפורמט שאינו JSON כדי לשלוח את הנתונים הבינאריים.

מתקשרים ל-SFE בטלפון SelectAd כדי להפעיל את המכרז של B&A

אותו תרשים של תהליך העבודה עם השלב השלישי מודגש. בשלב הזה, SAS שולח בקשת SelectAd אל SFE, ו-SFE מריץ מכרז B&A.

אחרי ששירות המודעות לאתרי מכירה מקבל את בקשת המכירה הפומבית המאוחדת מהדף, המכירה הפומבית ההקשרית מופעלת קודם כדי לקבוע מי הזוכה במכירה הפומבית ההקשרית, וכדי לאסוף את האותות של הקונים שיועברו למכירה הפומבית של PA B&A. לאחר מכן, מכרז ה-B&A מופעל על ידי קריאה לפעולת SelectAd של SFE מ-SAS עם מטען הבקשה. שימו לב: חלק מהמטא-נתונים מהבקשה של הדף ל-SAS בשלב 2 מועברים ל-SFE.

הרכבת המטען הייעודי (payload) של SelectAdRequest

אפשר ליצור את מטען הייעודי (payload) של הבקשה של הקריאה 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, צריך לפענח אותם בחזרה לנתונים בינאריים אם הבקשה ל-SFE נשלחת באמצעות gRPC. אם הבקשה נשלחת באמצעות HTTP, נתוני המכרז המוצפן של המודעה יכולים להישאר בפורמט המקודד ב-Base64.

כדי לראות שדות אחרים שמוגדרים בבקשת SelectAd, אפשר לעיין בהגדרת הפרוטו של SelectAdRequest.

הגדרת שדה המוכר ברמה העליונה במכרזים במצב משולב ובמכרזים של רכיבים

אם המוכר מפעיל מכרז במצב מעורב או משתתף כמוכר רכיבים במכרז עם כמה מוכרים, צריך להגדיר את השדה 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',
  }
}

התקשרות אל SelectAd של SFE

אפשר להתקשר אל SFE מ-SAS באמצעות gRPC או HTTP.

קריאה ל-gRPC

בקשת gRPC אל SFE נראית כך באמצעות Express ב-Node עם לקוח 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 נמצאת ב מאגר של אפליקציית הבדיקה המקומית.

קריאת HTTP ל-Envoy proxy

בקשת ה-HTTP POST אל SFE נשלחת אל הנתיב /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

הדוגמה הבאה מראה איך אפשר להעביר את המטא-נתונים באמצעות Express ב-Node עם לקוח 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);
})

הדוגמה הבאה ממחישה איך אפשר להעביר את המטא-נתונים באמצעות קריאת 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)
  });
})

מכרז שמוגדרים בו כמה אתרי מכירה ומתנהל בשרת

אם אתם מוכרים ברמה העליונה שמפעילים מכרז עם כמה מוכרים שמתנהל בשרת, הקריאה GetComponentAuctionCiphertexts מתבצעת אל SFE לפני הקריאה SelectAd. התשובה מכילה את נתוני המכרז המוצפנים מחדש של הרכיבים שנשלחים לשירותי המודעות של מוכר הרכיבים. תוצאות המכרז של מודעות B&A שמוחזרות מסופקות לקריאה SelectAd של SFE של המוכר ברמה העליונה.

מידע נוסף זמין במאמר בנושא חשבונות עם מספר אתרי מכירה ב-GitHub.

החזרת תוצאת המכרז של B&A לדף

אותו תרשים עם הסבר מפורט, כשהשלב הרביעי מודגש. בשלב הזה, SAS שולח את תוצאת המכרז של SelectAd בחזרה לדפדפן

אחרי שמכרז B&A מסתיים, תוצאת המכרז המוצפנת מוחזרת ל-SAS, ו-SAS מגיב לבקשת המכרז המאוחד מהדף בשלב 2 עם תוצאת המכרז המוצפנת. בתגובת ה-SAS לדף, גיבוב SHA-256 של תוצאת המכרז המוצפנת בקידוד base64url מוגדר בכותרת התגובה Ad-Auction-Result. הגיבוב הזה משמש את הדפדפן לאימות מטען הייעודי (payload) בסיום המכרז בצד הלקוח.

כך נראית יצירת גיבוב SHA-256 עם קידוד Base64 ב-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, התשובה כוללת גם את תוצאת המכרז ההקשרי.

אפשר לכלול כמה גיבובים (hash) ב-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. המטען הייעודי (payload) הזה מועבר לקריאה runAdAuction() כדי להשלים את המכרז בדפדפן. הערך requestId מהקריאה getInterestGroupAdAuctionData() בשלב 1 מועבר גם למכרז.

// 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 מוזן הערך requestId שמוחזר מהקריאה getInterestGroupAdAuctionData(). בשדה serverResponse מזינים מערך בייטים של המכרז מסוג B&A שהופעל בשלב 3.

דוגמה לקריאה הזו מופיעה בקוד 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 של המוכר באפליקציית הבדיקה המקומית.

השלבים הבאים

אחרי שתקראו את המדריך הזה, תוכלו לבצע את השלבים הבאים:

מידע נוסף

יש לך שאלות?