Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions modules/.submodules.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"pubmaticIdSystem",
"rewardedInterestIdSystem",
"sharedIdSystem",
"startioIdSystem",
"taboolaIdSystem",
"tapadIdSystem",
"teadsIdSystem",
Expand Down
21 changes: 20 additions & 1 deletion modules/startioBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js';
import { logError, isFn, isPlainObject } from '../src/utils.js';
import { logError, isFn, isPlainObject, formatQS } from '../src/utils.js';
import { ortbConverter } from '../libraries/ortbConverter/converter.js'
import { ortb25Translator } from '../libraries/ortb2.5Translator/translator.js';
import { getUserSyncParams } from '../libraries/userSyncUtils/userSyncUtils.js';

const BIDDER_CODE = 'startio';
const METHOD = 'POST';
const GVLID = 1216;
const ENDPOINT_URL = `https://pbc-rtb.startappnetwork.com/1.3/2.5/getbid?account=pbc`;
const IFRAME_URL = 'https://cs.startappnetwork.com/sync?p=m4b8b3y4';

const converter = ortbConverter({
imp(buildImp, bidRequest, context) {
Expand Down Expand Up @@ -151,6 +153,23 @@ export const spec = {
},

onSetTargeting: (bid) => { },

getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) {
const syncs = [];

if (syncOptions.iframeEnabled) {
const consentParams = getUserSyncParams(gdprConsent, uspConsent, gppConsent);
const queryString = formatQS(consentParams);
const queryParam = queryString ? `&${queryString}` : '';

syncs.push({
type: 'iframe',
url: `${IFRAME_URL}${queryParam}`
Comment thread
IlliaMil marked this conversation as resolved.
});
}

return syncs;
}
};

registerBidder(spec);
32 changes: 28 additions & 4 deletions modules/startioBidAdapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ var adUnits = [
bidder: 'startio',
params: {
// REQUIRED - Publisher Account ID
accountId: 'your-account-id',
publisherId: 'your-account-id',

// OPTIONAL - Enable test ads
testAdsEnabled: true
Expand Down Expand Up @@ -58,7 +58,7 @@ var videoAdUnits = [
{
bidder: 'startio',
params: {
accountId: 'your-account-id',
publisherId: 'your-account-id',
testAdsEnabled: true
}
}
Expand All @@ -85,7 +85,7 @@ var nativeAdUnits = [
{
bidder: 'startio',
params: {
accountId: 'your-account-id',
publisherId: 'your-account-id',
testAdsEnabled: true
}
}
Expand All @@ -94,8 +94,32 @@ var nativeAdUnits = [
];
```

### Prebid Params Enabling User Sync

To enable iframe-based user syncing for Start.io, include the `filterSettings` configuration in your `userSync` setup:

```javascript
pbjs.setConfig({
userSync: {
userIds: [{
name: 'startioId',
storage: {
type: 'cookie&html5',
name: 'startioId'
}
}],
filterSettings: {
iframe: {
bidders: ['startio'],
filter: 'include'
}
}
}
});
```

# Additional Notes
- The adapter processes requests via OpenRTB 2.5 standards.
- Ensure that the `accountId` parameter is set correctly for your integration.
- Ensure that the `publisherId` parameter is set correctly for your integration.
- Test ads can be enabled using `testAdsEnabled: true` during development.
- The adapter supports multiple ad formats, allowing publishers to serve banners, native ads and instream video ads seamlessly.
74 changes: 74 additions & 0 deletions modules/startioIdSystem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* This module adds startio ID support to the User ID module
* The {@link module:modules/userId} module is required
* @module modules/startioIdSystem
* @requires module:modules/userId
*/
import { logError, formatQS } from '../src/utils.js';
import { submodule } from '../src/hook.js';
import { ajax } from '../src/ajax.js';
import { getUserSyncParams } from '../libraries/userSyncUtils/userSyncUtils.js';

const MODULE_NAME = 'startioId';
const GVLID = 1216;
const DEFAULT_ENDPOINT = 'https://cs.startappnetwork.com/get-uid-obj?p=m4b8b3y4';

function fetchIdFromServer(callback, consentData) {
const consentParams = getUserSyncParams(
consentData?.gdpr,
consentData?.usp,
consentData?.gpp
);
const queryString = formatQS(consentParams);
const url = queryString ? `${DEFAULT_ENDPOINT}&${queryString}` : DEFAULT_ENDPOINT;

const callbacks = {
success: response => {
let responseId;
try {
const responseObj = JSON.parse(response);
if (responseObj && responseObj.uid) {
responseId = responseObj.uid;
} else {
logError(`${MODULE_NAME}: Server response missing 'uid' field`);
}
} catch (error) {
logError(`${MODULE_NAME}: Error parsing server response`, error);
}
callback(responseId);
},
error: error => {
logError(`${MODULE_NAME}: ID fetch encountered an error`, error);
callback();
}
};
ajax(url, callbacks, undefined, { method: 'GET', withCredentials: true });
}

export const startioIdSubmodule = {
name: MODULE_NAME,
gvlid: GVLID,
decode(value) {
return value && typeof value === 'string'
? { 'startioId': value }
: undefined;
},
getId(config, consentData, storedId) {
if (storedId) {
return { id: storedId };
}
if (config.storage && config.storage.expires == null) {
config.storage.expires = 90;
}
return { callback: (cb) => fetchIdFromServer(cb, consentData) };
},
Comment thread
IlliaMil marked this conversation as resolved.

eids: {
'startioId': {
source: 'start.io',
atype: 1
},
}
};

submodule('userId', startioIdSubmodule);
44 changes: 44 additions & 0 deletions modules/startioIdSystem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
## Start.io User ID Submodule

The Start.io User ID submodule generates and persists a unique user identifier by fetching it from a Start.io-managed endpoint. This endpoint is fixed within the submodule implementation and is not configurable via Prebid.js parameters. The ID is stored in both cookies and local storage for subsequent page loads and is made available to other Prebid.js modules via the standard `eids` interface.

For integration support, contact prebid@start.io.

### Prebid Params Enabling User Sync

To enable iframe-based user syncing for Start.io, include the `filterSettings` configuration in your `userSync` setup:

```javascript
pbjs.setConfig({
userSync: {
userIds: [{
name: 'startioId',
storage: {
type: 'cookie&html5', // 'cookie', 'html5', or 'cookie&html5'
name: 'startioId',
expires: 90 // optional, 90 days by default
}
}],
filterSettings: {
iframe: {
bidders: ['startio'],
filter: 'include'
}
}
}
});
```

This configuration allows Start.io to sync user data via iframe, which is necessary for cross-domain user identification.

## Parameter Descriptions for the `userSync` Configuration Section

The below parameters apply only to the Start.io User ID integration.

| Param under userSync.userIds[] | Scope | Type | Description | Example |
| --- | --- | --- | --- | --- |
| name | Required | String | The name of this module. | `"startioId"` |
Comment thread
IlliaMil marked this conversation as resolved.
| storage | Required | Object | Storage configuration for the user ID. | |
| storage.type | Required | String | Type of storage: `"cookie"`, `"html5"`, or `"cookie&html5"`. | `"cookie&html5"` |
| storage.name | Required | String | The name used to store the user ID. | `"startioId"` |
| storage.expires | Optional | Number | Number of days before the stored ID expires. Defaults to `90`. | `365` |
7 changes: 7 additions & 0 deletions modules/userId/eids.md
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,13 @@ userIdAsEids = [
id: 'some-random-id-value',
atype: 1
}]
},
{
source: 'start.io',
uids: [{
id: 'some-random-id-value',
atype: 1
}]
}
]
```
3 changes: 3 additions & 0 deletions modules/userId/userId.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ pbjs.setConfig({
},
{
name: "mygaruId"
},
{
name: "startioId"
}
],
syncDelay: 5000,
Expand Down
94 changes: 94 additions & 0 deletions test/spec/modules/startioBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -370,4 +370,98 @@ describe('Prebid Adapter: Startio', function () {
});
}
});

describe('getUserSyncs', function () {
it('should return an iframe sync when iframeEnabled is true', function () {
const syncs = spec.getUserSyncs({ iframeEnabled: true }, []);

expect(syncs).to.have.lengthOf(1);
expect(syncs[0].type).to.equal('iframe');
expect(syncs[0].url).to.be.a('string');
});

it('should return an empty array when iframeEnabled is false', function () {
const syncs = spec.getUserSyncs({ iframeEnabled: false }, []);

expect(syncs).to.have.lengthOf(0);
});

it('should return an empty array when syncOptions is empty', function () {
const syncs = spec.getUserSyncs({}, []);

expect(syncs).to.have.lengthOf(0);
});

it('should append GDPR consent params to the sync URL', function () {
const gdprConsent = {
gdprApplies: true,
consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A=='
};

const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], gdprConsent);

expect(syncs).to.have.lengthOf(1);
expect(syncs[0].url).to.include('gdpr=1');
expect(syncs[0].url).to.include('gdpr_consent=BOJ/P2HOJ/P2HABABMAAAAAZ+A==');
});
Comment thread
IlliaMil marked this conversation as resolved.

it('should append gdpr=0 when gdprApplies is false', function () {
const gdprConsent = {
gdprApplies: false,
consentString: ''
};

const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], gdprConsent);

expect(syncs[0].url).to.include('gdpr=0');
});

it('should append USP consent param to the sync URL', function () {
const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], undefined, '1YNN');

expect(syncs).to.have.lengthOf(1);
expect(syncs[0].url).to.include('us_privacy=1YNN');
});

it('should append GPP consent params to the sync URL', function () {
const gppConsent = {
gppString: 'DBABMA~BAAAAAAAAgA.QA',
applicableSections: [7, 8]
};

const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], undefined, undefined, gppConsent);

expect(syncs).to.have.lengthOf(1);
expect(syncs[0].url).to.include('gpp=DBABMA~BAAAAAAAAgA.QA');
expect(syncs[0].url).to.include('gpp_sid=7,8');
});

it('should append all consent params together when all are provided', function () {
const gdprConsent = {
gdprApplies: true,
consentString: 'testConsent'
};
const uspConsent = '1YNN';
const gppConsent = {
gppString: 'testGpp',
applicableSections: [2]
};

const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], gdprConsent, uspConsent, gppConsent);

expect(syncs).to.have.lengthOf(1);
expect(syncs[0].url).to.include('gdpr=1');
expect(syncs[0].url).to.include('gdpr_consent=testConsent');
expect(syncs[0].url).to.include('us_privacy=1YNN');
expect(syncs[0].url).to.include('gpp=testGpp');
expect(syncs[0].url).to.include('gpp_sid=2');
});

it('should not append query string when no consent params are provided', function () {
const syncs = spec.getUserSyncs({ iframeEnabled: true }, []);

expect(syncs).to.have.lengthOf(1);
expect(syncs[0].url).to.equal('https://cs.startappnetwork.com/sync?p=m4b8b3y4');
});
});
});
Loading
Loading