diff --git a/.env b/.env index 337daa7f..f3551dd3 100644 --- a/.env +++ b/.env @@ -84,12 +84,12 @@ SSP_B_DETAIL="Ad-Platform: SSP-B for publisher" SSP_X_HOST=privacy-sandbox-demos-ssp-x.dev SSP_X_URI=http://privacy-sandbox-demos-ssp-x.dev:8080 -SSP_X_TOKEN="" +SSP_X_HOST_INTERNAL=ssp-x SSP_X_DETAIL="Ad-Platform: SSP-X for publisher" SSP_Y_HOST=privacy-sandbox-demos-ssp-y.dev SSP_Y_URI=http://privacy-sandbox-demos-ssp-y.dev:8080 -SSP_Y_TOKEN="" +SSP_Y_HOST_INTERNAL=ssp-y SSP_Y_DETAIL="Ad-Platform: SSP-Y for publisher" ## Collector for Aggregation Service diff --git a/services/ad-tech/src/lib/arapi.ts b/services/ad-tech/src/lib/arapi.ts index 690ba81e..f9e13d38 100644 --- a/services/ad-tech/src/lib/arapi.ts +++ b/services/ad-tech/src/lib/arapi.ts @@ -228,3 +228,28 @@ function test() { } // test(); + +// tmp for event level report +// Retrieves trigger data and priority based on the provided conversion type. +// This function maps conversion types to corresponding trigger data and priority values +// * trigger data is the metadata value representing the conversion +// used for event-level reporting. The trigger data and priority are returned as an array. +// @param conversionType The conversion type string (e.g., 'click-cart-icon', 'add-to-cart', 'purchase'). +// @returns An array containing the trigger data (string) and priority (string), +// or `['0', '0']` for unknown conversion types. +// @example +// getTriggerData('purchase'); // Returns ['1', '100'] +// getTriggerData('unknown-event'); // Returns ['0', '0'] +export function getTriggerData(conversionType: string) { + console.log(`[arapi] getTriggerData: ${conversionType}`); + switch (conversionType) { + case `click-cart-icon`: + return [`5`, `60`]; + case `add-to-cart`: + return [`6`, `80`]; + case `purchase`: + return [`1`, `100`]; + default: + return ['0', '0']; + } +} diff --git a/services/ad-tech/src/lib/attribution-reporting-helper.ts b/services/ad-tech/src/lib/attribution-reporting-helper.ts index 3988b442..861d4669 100644 --- a/services/ad-tech/src/lib/attribution-reporting-helper.ts +++ b/services/ad-tech/src/lib/attribution-reporting-helper.ts @@ -19,6 +19,7 @@ import { sourceEventId, sourceKeyPiece, triggerKeyPiece, + getTriggerData, ADVERTISER, PUBLISHER, DIMENSION, @@ -84,6 +85,23 @@ export const getAttributionTriggerHeaders = (requestQuery: { debug_key: debugKey(), }; }; +/** Returns ARA trigger registration headers for event level. */ +export const getEventLevelAttributionTriggerHeaders = (requestQuery: { + [key: string]: string; +}): {[key: string]: any} => { + const conversionType: string = requestQuery['conversion-type'] as string; + const [_data, _priority] = getTriggerData(conversionType); + return { + event_trigger_data: [ + { + trigger_data: _data, + priority: _priority, + }, + ], + debug_key: debugKey(), + debug_reporting: true, + }; +}; /** Returns ARA source registration headers for the request context. */ export const getAttributionSourceHeaders = ( diff --git a/services/ad-tech/src/routes/common/attribution-reporting-router.ts b/services/ad-tech/src/routes/common/attribution-reporting-router.ts index be2cce78..fec05a33 100644 --- a/services/ad-tech/src/routes/common/attribution-reporting-router.ts +++ b/services/ad-tech/src/routes/common/attribution-reporting-router.ts @@ -16,6 +16,7 @@ import {decodeDict} from 'structured-field-values'; import { getAttributionSourceHeaders, getAttributionTriggerHeaders, + getEventLevelAttributionTriggerHeaders, getAttributionRedirectUrl, } from '../../lib/attribution-reporting-helper.js'; import {getStructuredObject} from '../../lib/common-utils.js'; @@ -94,3 +95,16 @@ AttributionReportingRouter.get( res.sendStatus(200); }, ); +/** Registers an attribution trigger (event) without aggr report. */ +AttributionReportingRouter.get( + '/register-event-level-trigger', + async (req: Request, res: Response) => { + const queryParams = getStructuredObject(req.query); + const triggerHeaders = getEventLevelAttributionTriggerHeaders(queryParams); + res.setHeader( + 'Attribution-Reporting-Register-Trigger', + JSON.stringify(triggerHeaders), + ); + res.sendStatus(200); + }, +); diff --git a/services/ad-tech/src/routes/well-known/well-known-attribution-reporting-router.ts b/services/ad-tech/src/routes/well-known/well-known-attribution-reporting-router.ts index 2b6bf797..cb4ce4e4 100644 --- a/services/ad-tech/src/routes/well-known/well-known-attribution-reporting-router.ts +++ b/services/ad-tech/src/routes/well-known/well-known-attribution-reporting-router.ts @@ -124,6 +124,17 @@ WellKnownAttributionReportingRouter.post( }, ); -// TODO: Implement verbose debug reports for ARA. -// WellKnownAttributionReportingRouter.post('/debug/verbose', -// async (req: Request, res: Response) => {}); +// temporarily console.logging verbose report +WellKnownAttributionReportingRouter.post( + '/debug/verbose', + async (req: Request, res: Response) => { + const debugReport = req.body; + console.log( + '\x1b[1;31m%s\x1b[0m', + `Received verbose debug reports from the browser`, + ); + console.log('VERBOSE REPORT(S) RECEIVED:\n=== \n', debugReport, '\n=== \n'); + + res.sendStatus(200); + }, +); diff --git a/services/home/docs/demos/img/single-touch-event-level-report-endpoint.png b/services/home/docs/demos/img/single-touch-event-level-report-endpoint.png new file mode 100644 index 00000000..5b5d5b9b Binary files /dev/null and b/services/home/docs/demos/img/single-touch-event-level-report-endpoint.png differ diff --git a/services/home/docs/demos/img/single-touch-event-level-report-journey.png b/services/home/docs/demos/img/single-touch-event-level-report-journey.png new file mode 100644 index 00000000..5aa1b0cb Binary files /dev/null and b/services/home/docs/demos/img/single-touch-event-level-report-journey.png differ diff --git a/services/home/docs/demos/img/single-touch-event-level-report-overview.png b/services/home/docs/demos/img/single-touch-event-level-report-overview.png new file mode 100644 index 00000000..9b82b82c Binary files /dev/null and b/services/home/docs/demos/img/single-touch-event-level-report-overview.png differ diff --git a/services/home/docs/demos/img/single-touch-event-level-report-request.png b/services/home/docs/demos/img/single-touch-event-level-report-request.png new file mode 100644 index 00000000..d059fe3f Binary files /dev/null and b/services/home/docs/demos/img/single-touch-event-level-report-request.png differ diff --git a/services/home/docs/demos/img/single-touch-event-level-report-request2.png b/services/home/docs/demos/img/single-touch-event-level-report-request2.png new file mode 100644 index 00000000..7510a1c0 Binary files /dev/null and b/services/home/docs/demos/img/single-touch-event-level-report-request2.png differ diff --git a/services/home/docs/demos/index.md b/services/home/docs/demos/index.md index c24e403a..4ec37b52 100644 --- a/services/home/docs/demos/index.md +++ b/services/home/docs/demos/index.md @@ -8,14 +8,15 @@ sidebar_position: 1 :::note Looking for running these demos on your local environment ? Check the [deployment guide](https://github.com/privacysandbox/privacy-sandbox-demos/blob/main/README.md) on the GitHub. ::: -| **Category** | **Use Case** | **Privacy Sandbox APIs** | **Relevant for** | -| :--------------------------------------: | :----------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------: | :----------------------------------------: | -| Show Relevant Video Ads | [Instream VAST video ad in a Protected Audience sequential auction setup](demos/instream-video-ad-multi-seller.md) | Protected Audience API | Publisher, Ad Server, SSP, Advertiser, DSP | -| Show Relevant Video Ads | [Instream VAST video ad in a Protected Audience single-seller auction](demos/vast-video-protected-audience.md) | Protected Audience API | Publisher, SSP, Advertiser, DSP | -| Show Relevant Content and Ads | [Retargeting / Remarketing](demos/retargeting-remarketing.md) | Protected Audience API | Publisher, SSP, Advertiser, DSP | -| Measure Digital Ads | [Single-touch conversion Attribution](demos/single-touch-conversion-attribution.md) | Attribution Reporting API, Aggregation Service | Publisher, SSP, Advertiser, DSP | -| Use SSP Key/Value service to exclude ads | [Enforcing publisher ad requirements in Protected Audience using K/V](demos/publisher-ad-quality-req.md) | Protected Audience API | Publisher, SSP | -| Measure Digital Ads | [Multi-touch conversion Attribution](demos/multi-touch-conversion-attribution.md) | Private Aggregation, Shared Storage, Aggregation Service | Publisher, Advertiser, DSP | +| **Category** | **Use Case** | **Privacy Sandbox APIs** | **Relevant for** | +| :---------------------------: | :----------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------: | :----------------------------------------: | +| Show Relevant Video Ads | [Instream VAST video ad in a Protected Audience sequential auction setup](demos/instream-video-ad-multi-seller.md) | Protected Audience API | Publisher, Ad Server, SSP, Advertiser, DSP | +| Show Relevant Video Ads | [Instream VAST video ad in a Protected Audience single-seller auction](demos/vast-video-protected-audience.md) | Protected Audience API | Publisher, SSP, Advertiser, DSP | +| Show Relevant Content and Ads | [Retargeting / Remarketing](demos/retargeting-remarketing.md) | Protected Audience API | Publisher, SSP, Advertiser, DSP | +| Measure Digital Ads | [Single-touch conversion Attribution](demos/single-touch-conversion-attribution.md)
(Summary Report) | Attribution Reporting API, Aggregation Service | Publisher, SSP, Advertiser, DSP | +| Measure Digital Ads | [Event Level Report](demos/single-touch-event-level-report.md) | Attribution Reporting API | Publisher, Advertiser, Ad Tech | +| Attribution Reporting API | [Enforcing publisher ad requirements in Protected Audience using K/V](demos/publisher-ad-quality-req.md) | Protected Audience API | Publisher, SSP | +| Measure Digital Ads | [Multi-touch conversion Attribution](demos/multi-touch-conversion-attribution.md) | Private Aggregation, Shared Storage, Aggregation Service | Publisher, Advertiser, DSP | :::info Looking for a use case not listed here ? Help us growing this repository by [contributing](https://github.com/privacysandbox/privacy-sandbox-demos/blob/main/CONTRIBUTING.md) or diff --git a/services/home/docs/demos/single-touch-event-level-report.md b/services/home/docs/demos/single-touch-event-level-report.md new file mode 100644 index 00000000..a070fb7a --- /dev/null +++ b/services/home/docs/demos/single-touch-event-level-report.md @@ -0,0 +1,266 @@ +--- +title: Event-level reports for single touch attribution +sidebar_position: 6 +more_data: + - apis: + - Attribution Reporting API + - parties: + - Publisher + - Advertiser + - Ad Tech +--- + +import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; + +# Event-level reports for single touch attribution + + + + +## Overview + +### Description + +Event-level reports help measure ad effectiveness while protecting user privacy. This demo shows how the Attribution Reporting API links ad +interactions to conversions without revealing user identities across websites. It focuses on single-touch attribution using the last-click model. +![overview](./img/single-touch-event-level-report-overview.png) + +### Privacy Sandbox APIs + +- [Attribution Reporting API](https://developer.chrome.com/en/docs/privacy-sandbox/attribution-reporting/) +- [Get started with Attribution Reporting](https://developers.google.com/privacy-sandbox/private-advertising/attribution-reporting/getting-started) + +### Related parties + +- Publisher: Hosts ads +- Advertiser: Hosts conversion pages, tracks conversions +- Ad Tech: Implements API, receives cross-site reports + + + + +## Scope + +### System Design + +We are exploring two distinct user journeys for ad attribution: + +- User Journey 1: Click-through conversion: The user visits a news site, clicks on an ad, then navigates to the advertiser's site where they convert. + The system tracks the ad click and the subsequent conversion. +- User Journey 2: View-through conversion: The user sees an ad on a news site without clicking it. Later, they independently visit the advertiser's + site and convert. The system connects this conversion to the earlier ad view. In both cases, the Attribution Reporting API links the ad interaction + (click or view) to a conversion without compromising user privacy across sites. + +### Attribution Reporting (Event-level) Flow + +We focus on two user journeys: click-through and view-through conversions. In the click-through journey, the user actively clicks an ad on a +publisher's site before converting on the advertiser's site. In the view-through journey, on the other hand, the user sees an ad without clicking, and +later independently visits and converts on the advertiser's site. Both journeys enable advertisers to measure ad effectiveness by linking ad +interactions (clicks or views) to conversions, all while preserving user privacy across different websites. +![journey](./img/single-touch-event-level-report-journey.png) + +The following sequence diagram illustrates these two main user journeys in digital advertising attribution: click-through and view-through. It shows +how the Attribution Reporting API tracks user interactions across publisher and advertiser websites, highlighting the key differences in timing and +user behavior between the two scenarios. + +```mermaid +sequenceDiagram +participant User/Browser +participant Publisher Website +participant Advertiser Website +participant AdTech + +rect rgb(200, 220, 240) +Note right of User/Browser: Click-through Journey +User/Browser->>Publisher Website: Visit website +Publisher Website->>AdTech: Register source (ad impression) +AdTech->>User/Browser: Set Attribution-Reporting-Register-Source header +Publisher Website->>Advertiser Website: Click ad +Advertiser Website->>AdTech: Register trigger +AdTech->>User/Browser: Set Attribution-Reporting-Register-Trigger header +User/Browser->>User/Browser: Generate report +User/Browser->>AdTech: Send report to /.well-known/attribution-reporting/report-event-attribution +end + +rect rgb(220, 240, 200) +Note right of User/Browser: View-through Journey +User/Browser->>Publisher Website: Visit website +Publisher Website->>AdTech: Register source (ad impression) +AdTech->>User/Browser: Set Attribution-Reporting-Register-Source header +User/Browser->>User/Browser: Time passes +User/Browser->>Advertiser Website: Directly visit advertiser website & convert +Advertiser Website->>AdTech: Register trigger +AdTech->>User/Browser: Set Attribution-Reporting-Register-Trigger header +User/Browser->>User/Browser: Generate report +User/Browser->>AdTech: Send report to /.well-known/attribution-reporting/report-event-attribution +end +``` + + + + +## Demo + +### Prerequisites + +- Chrome > v127 (Open chrome://version to look up your current version) +- Open chrome://attribution-internals/ and Click on “Clear all attribution data” + +### Click-Through Conversion Journey + +1. Clear attribution data in chrome://attribution-internals/ +2. [Navigate to news site](https://privacy-sandbox-demos-news.dev/iframe-static-img-ad) + +- ice skate shoes image will be displayed + +3. Click on the ad image + +- Demo shop site of skate shoes detail page will open + +4. Check “SourceTrigger Registration” tab in chrome://attribution-internals +5. Go back to the shop site and click the "ADD TO CART" button +6. Review “Trigger Registration” and "Event-Level Reports" tab in chrome://attribution-internals + +### View-Through Conversion Journey + +1. Clear attribution data in chrome://attribution-internals/ +2. [Navigate to news site](https://privacy-sandbox-demos-news.dev/iframe-static-img-ad) + +- ice skate shoes image will be displayed + +3. Click on "To the shop without ad click" below the image + +- Demo shop site will open + +4. Check “SourceTrigger Registration” tab in chrome://attribution-internals +5. Go back to the shop site and click an item +6. Click on the "ADD TO CART" button +7. Review “Trigger Registration” and "Event-Level Reports" tab in chrome://attribution-internals + +### Implementation details + +#### Register a source + +Sources are registered when a user views or clicks an ad. This is done on the publisher's website. + +To do so we need to: + +- Initiate the source registration with an HTML element or a Javascript call +- Complete the source registration, responding to the request with the header Attribution-Reporting-Register-Source. + ![request](./img/single-touch-event-level-report-request.png) + +### Initiatie the source registration + +For views: + +```html +ad image + +``` + +\*Here's the +[source code](https://github.com/privacysandbox/privacy-sandbox-demos/commit/cb581cb305b17d7442d0cd71eccfe851525a0cb7#diff-5bb02bedd9ceea45a3874a59caa25f4b9f80da3c3fe2098a88a55ea52a14dd52R4) + +For clicks: + +```js +function adClick() { + const encoded = encodeURIComponent("https://privacy-sandbox-demos-dsp.dev/attribution/register-source?advertiser=privacy-sandbox-demos-shop.dev&id=u26f8"); + const url = "https://privacy-sandbox-demos-shop.dev/items/26f8"; + window.open( + url, + "_blank", + `attributionsrc=${encoded}`); +} +``` + +\*Here's the +[source code](https://github.com/privacysandbox/privacy-sandbox-demos/commit/cb581cb305b17d7442d0cd71eccfe851525a0cb7#diff-27409a7640486ec4d969bbd02bd8c6e523e5ee586ac666d913a19dfe2c77837dR20) + +### Complete the source registration + +For both clicks and views is to respond with the Attribution-Reporting-Register-Source header. + +```js +res.set( + "Attribution-Reporting-Register-Source", + JSON.stringify({ + destination: "https://privacy-sandbox-demos-shop.dev"; + source_event_id: "1234", + expiry: "604800", + priority: "100", + debug_key: "1234", + debug_reporting: true, + }) +); +``` + +\*Here's the +[source code](https://github.com/privacysandbox/privacy-sandbox-demos/commit/cb581cb305b17d7442d0cd71eccfe851525a0cb7#diff-3e3c5e844647864b521c39ce06564f42b29325aa273ed61f4362ec498a39d6bdR99) + +### Register a trigger + +Triggers are registered when a user converts on the advertiser's website. Here we will use a pixel. +![request2](./img/single-touch-event-level-report-request2.png) + +### Initiate the trigger registration + +```html + +``` + +```js +function addToCart() { + const attributionReporting = { + eventSourceEligible: false, + triggerEligible: true, + }; + const url = "https://privacy-sandbox-demos-dsp.dev/attribution/register-event-level-trigger?conversion-type=add-to-cart" + window.fetch(url, { + mode: "no-cors", keepalive: true, attributionReporting + }); +} +``` + +\*Here's the +[source code](https://github.com/privacysandbox/privacy-sandbox-demos/commit/cb581cb305b17d7442d0cd71eccfe851525a0cb7#diff-fb8d83fec20a0b18888cbc05872559dbe6e79941c7e87ac2be6b251359801f3aR76) + +### Respond with a header + +Here, we set Attribution-Reporting-Register-Trigger on the request: + +```js +res.set( + "Attribution-Reporting-Register-Trigger", +JSON.stringify({ + event_trigger_data: [{ + trigger_data: "6", + priority: "80", + }], + debug_reporting: true, + debug_key: "1115698977" +}); +); +``` + +\*Here's the +[source code](https://github.com/privacysandbox/privacy-sandbox-demos/commit/cb581cb305b17d7442d0cd71eccfe851525a0cb7#diff-49482cc7257904ce6c46dbb276a02120f18bc5b9f659ebaf92f112b59de0e07fR89) + +### Set up an endpoint + +All we have to do now is to create an endpoint at `https://adtech.example/.well-known/attribution-reporting/report-event-attribution` to receive +reports. + +![endpoint](./img/single-touch-event-level-report-endpoint.png) + +### Related API documentation + +- [Attribution Reporting - Chrome Developers](https://developer.chrome.com/docs/privacy-sandbox/attribution-reporting/) +- [Attribution Reporting - Developer Guide](https://developer.chrome.com/docs/privacy-sandbox/attribution-reporting/developer-guide/) +- [Set up debug reports - Chrome Developers](https://developer.chrome.com/docs/privacy-sandbox/attribution-reporting-debugging/part-2/) + + + diff --git a/services/home/docs/intro.md b/services/home/docs/intro.md index 21b76086..e9016838 100644 --- a/services/home/docs/intro.md +++ b/services/home/docs/intro.md @@ -12,7 +12,9 @@ sidebar_position: 1 | Show Relevant Video Ads | [Instream VAST video ad in a Protected Audience sequential auction setup](demos/instream-video-ad-multi-seller.md) | Protected Audience API | Publisher, Ad Server, SSP, Advertiser, DSP | | Show Relevant Video Ads | [Instream VAST video ad in a Protected Audience single-seller auction](demos/vast-video-protected-audience.md) | Protected Audience API | Publisher, SSP, Advertiser, DSP | | Show Relevant Content and Ads | [Retargeting / Remarketing](demos/retargeting-remarketing.md) | Protected Audience API | Publisher, SSP, Advertiser, DSP | -| Measure Digital Ads | [Single-touch conversion Attribution](demos/single-touch-conversion-attribution.md) | Attribution Reporting API, Aggregation Service | Publisher, SSP, Advertiser, DSP | +| Measure Digital Ads | [Single-touch conversion Attribution](demos/single-touch-conversion-attribution.md)
(Summary Report) | Attribution Reporting API, Aggregation Service | Publisher, SSP, Advertiser, DSP | +| Measure Digital Ads | [Event Level Report](demos/single-touch-event-level-report.md) | Attribution Reporting API | Publisher, Advertiser, Ad Tech | +| Attribution Reporting API | [Enforcing publisher ad requirements in Protected Audience using K/V](demos/publisher-ad-quality-req.md) | Protected Audience API | Publisher, SSP | | Use SSP Key/Value service to exclude ads | [Enforcing publisher ad requirements in Protected Audience using K/V](demos/publisher-ad-quality-req.md) | Protected Audience API | Publisher, SSP | | Measure Digital Ads | [Multi-touch conversion Attribution](demos/multi-touch-conversion-attribution.md) | Private Aggregation, Shared Storage, Aggregation Service | Publisher, Advertiser, DSP | diff --git a/services/news/src/constants.ts b/services/news/src/constants.ts index c0994120..f86963b8 100644 --- a/services/news/src/constants.ts +++ b/services/news/src/constants.ts @@ -20,6 +20,9 @@ export const { EXTERNAL_PORT, HOSTNAME, + // Advertisers + SHOP_HOST, + // Publishers NEWS_HOST, NEWS_DETAIL, diff --git a/services/news/src/index.ts b/services/news/src/index.ts index 26efa94f..bfbcd0c4 100644 --- a/services/news/src/index.ts +++ b/services/news/src/index.ts @@ -30,6 +30,7 @@ import { SSP_Y_ORIGIN, SSP_HOST, SSP_ORIGIN, + SHOP_HOST, } from './constants.js'; const app: Application = express(); @@ -53,6 +54,7 @@ app.get('*', async (req: Request, res: Response) => { res.render(req.path.substring(1), { TITLE: NEWS_DETAIL, TEXT_LOREM, + SHOP_HOST, AD_SERVER_HOST, DSP_HOST, EXTERNAL_PORT, diff --git a/services/news/src/views/components/aside.ejs b/services/news/src/views/components/aside.ejs index cf5e36b2..e85e973c 100644 --- a/services/news/src/views/components/aside.ejs +++ b/services/news/src/views/components/aside.ejs @@ -22,6 +22,10 @@ multi-seller | single-seller +
  • + Display static ad in iframe: + event level report +
  • Video ad in iframe: multi-seller | diff --git a/services/news/src/views/components/static-img-ad.ejs b/services/news/src/views/components/static-img-ad.ejs new file mode 100644 index 00000000..8a52becd --- /dev/null +++ b/services/news/src/views/components/static-img-ad.ejs @@ -0,0 +1,9 @@ +

    Click the Image to Register Source

    +
    + + ad image +
    + diff --git a/services/news/src/views/iframe-static-img-ad.ejs b/services/news/src/views/iframe-static-img-ad.ejs new file mode 100644 index 00000000..d026e794 --- /dev/null +++ b/services/news/src/views/iframe-static-img-ad.ejs @@ -0,0 +1,54 @@ + + + + + + + <%= TITLE %> + + + + + + + + + + + <%- include('components/header') %> +
    +
    +

    <%= TEXT_LOREM %>

    + <%- include('components/static-img-ad') %> +

    <%= TEXT_LOREM %>

    +

    <%= TEXT_LOREM %>

    +

    <%= TEXT_LOREM %>

    +
    + <%- include('components/aside') %> +
    + <%- include('components/footer', {HOME_HOST, EXTERNAL_PORT}) %> + + + diff --git a/services/shop/src/index.ts b/services/shop/src/index.ts index 200dda9a..24dc6ed2 100644 --- a/services/shop/src/index.ts +++ b/services/shop/src/index.ts @@ -114,6 +114,12 @@ app.locals = { return triggerUrl.toString(); }); }, + getEventTriggerUrl: (conversionType: string) => { + const eventTriggerUrl = new URL(`https://${DSP_HOST}:${EXTERNAL_PORT}`); + eventTriggerUrl.pathname = '/attribution/register-event-level-trigger'; + eventTriggerUrl.searchParams.append('conversion-type', conversionType); + return eventTriggerUrl.toString(); + }, }; app.get('/', async (req: Request, res: Response) => { diff --git a/services/shop/src/views/item.ejs b/services/shop/src/views/item.ejs index f0b6ecf3..abcab8d6 100644 --- a/services/shop/src/views/item.ejs +++ b/services/shop/src/views/item.ejs @@ -73,7 +73,7 @@

    - +
    @@ -81,6 +81,16 @@