diff --git a/adapters/peak226/params_test.go b/adapters/peak226/params_test.go
new file mode 100644
index 00000000000..86a99437ce9
--- /dev/null
+++ b/adapters/peak226/params_test.go
@@ -0,0 +1,59 @@
+package peak226
+
+import (
+ "encoding/json"
+ "testing"
+
+ "github.com/prebid/prebid-server/v4/openrtb_ext"
+)
+
+var validParams = []string{
+ `{ "publisherId": "pub-123", "placementId": "plc-456" }`,
+ `{ "publisherId": "pub-123", "placementId": "plc-456", "region": "us" }`,
+ `{ "publisherId": "pub-123", "placementId": "plc-456", "region": "eu" }`,
+ `{ "publisherId": "pub-123", "placementId": "plc-456", "region": "jp" }`,
+}
+
+func TestValidParams(t *testing.T) {
+ validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+ if err != nil {
+ t.Fatalf("Failed to fetch the json-schemas. %v", err)
+ }
+
+ for _, validParam := range validParams {
+ if err := validator.Validate(openrtb_ext.BidderPeak226, json.RawMessage(validParam)); err != nil {
+ t.Errorf("Schema rejected Peak226 params: %s\n Error: %s", validParam, err)
+ }
+ }
+}
+
+var invalidParams = []string{
+ ``,
+ `null`,
+ `true`,
+ `5`,
+ `4.2`,
+ `[]`,
+ `{}`,
+ `{ "placementId": "plc-456" }`,
+ `{ "publisherId": "pub-123" }`,
+ `{ "publisherId": "", "placementId": "plc-456" }`,
+ `{ "publisherId": "pub-123", "placementId": "" }`,
+ `{ "publisherId": 123, "placementId": "plc-456" }`,
+ `{ "publisherId": "pub-123", "placementId": 456 }`,
+ `{ "publisherId": "pub-123", "placementId": "plc-456", "region": "asia" }`,
+ `{ "publisherId": "pub-123", "placementId": "plc-456", "region": 1 }`,
+}
+
+func TestInvalidParams(t *testing.T) {
+ validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+ if err != nil {
+ t.Fatalf("Failed to fetch the json-schemas. %v", err)
+ }
+
+ for _, invalidParam := range invalidParams {
+ if err := validator.Validate(openrtb_ext.BidderPeak226, json.RawMessage(invalidParam)); err == nil {
+ t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+ }
+ }
+}
diff --git a/adapters/peak226/peak226.go b/adapters/peak226/peak226.go
new file mode 100644
index 00000000000..5562ad9fc27
--- /dev/null
+++ b/adapters/peak226/peak226.go
@@ -0,0 +1,303 @@
+package peak226
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "strconv"
+ "strings"
+ "text/template"
+
+ "github.com/prebid/openrtb/v20/openrtb2"
+ "github.com/prebid/prebid-server/v4/adapters"
+ "github.com/prebid/prebid-server/v4/config"
+ "github.com/prebid/prebid-server/v4/errortypes"
+ "github.com/prebid/prebid-server/v4/macros"
+ "github.com/prebid/prebid-server/v4/openrtb_ext"
+ "github.com/prebid/prebid-server/v4/util/jsonutil"
+)
+
+const (
+ defaultRegion = "us"
+ currencyUSD = "USD"
+ // zeroIFA is the sentinel value the OS reports for device.ifa when the user has not
+ // granted app tracking permission (e.g. iOS ATT declined). It is not a real device ID.
+ zeroIFA = "00000000-0000-0000-0000-000000000000"
+)
+
+type adapter struct {
+ endpoint *template.Template
+}
+
+// Builder builds a new instance of the Peak226 adapter for the given bidder with the given config.
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
+ endpointTemplate, err := template.New("endpointTemplate").Parse(config.Endpoint)
+ if err != nil {
+ return nil, fmt.Errorf("unable to parse endpoint url template: %v", err)
+ }
+
+ bidder := &adapter{
+ endpoint: endpointTemplate,
+ }
+ return bidder, nil
+}
+
+// regionPublisher groups impressions by both region and publisher ID, since each is a
+// distinct request-level value (endpoint region, site/app.publisher.id) and impressions
+// with different publisherId values must not be merged into the same outgoing request.
+type regionPublisher struct {
+ region string
+ publisherID string
+}
+
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+ var errs []error
+
+ impGroups := make(map[regionPublisher][]openrtb2.Imp)
+ var groupOrder []regionPublisher
+
+ for _, imp := range request.Imp {
+ var bidderExt adapters.ExtImpBidder
+ if err := jsonutil.Unmarshal(imp.Ext, &bidderExt); err != nil {
+ errs = append(errs, &errortypes.BadInput{
+ Message: fmt.Sprintf("imp #%s: %s", imp.ID, err.Error()),
+ })
+ continue
+ }
+
+ var peak226Ext openrtb_ext.ImpExtPeak226
+ if err := jsonutil.Unmarshal(bidderExt.Bidder, &peak226Ext); err != nil {
+ errs = append(errs, &errortypes.BadInput{
+ Message: fmt.Sprintf("imp #%s: %s", imp.ID, err.Error()),
+ })
+ continue
+ }
+
+ imp.TagID = peak226Ext.PlacementID
+
+ if err := stripBidderExt(&imp); err != nil {
+ errs = append(errs, &errortypes.BadInput{
+ Message: fmt.Sprintf("imp #%s: %s", imp.ID, err.Error()),
+ })
+ continue
+ }
+
+ if imp.BidFloor > 0 && imp.BidFloorCur != "" && !strings.EqualFold(imp.BidFloorCur, currencyUSD) {
+ convertedValue, err := reqInfo.ConvertCurrency(imp.BidFloor, imp.BidFloorCur, currencyUSD)
+ if err != nil {
+ errs = append(errs, err)
+ continue
+ }
+ imp.BidFloor = convertedValue
+ imp.BidFloorCur = currencyUSD
+ }
+
+ region := peak226Ext.Region
+ if region == "" {
+ region = defaultRegion
+ }
+
+ key := regionPublisher{region: region, publisherID: peak226Ext.PublisherID}
+ if _, ok := impGroups[key]; !ok {
+ groupOrder = append(groupOrder, key)
+ }
+ impGroups[key] = append(impGroups[key], imp)
+ }
+
+ if len(groupOrder) == 0 {
+ return nil, errs
+ }
+
+ device := sanitizeDevice(request.Device)
+ requests := make([]*adapters.RequestData, 0, len(groupOrder))
+
+ for _, key := range groupOrder {
+ imps := impGroups[key]
+
+ requestCopy := *request
+ requestCopy.Imp = imps
+ requestCopy.Device = device
+ setPublisherID(&requestCopy, key.publisherID)
+
+ endpoint, err := macros.ResolveMacros(a.endpoint, macros.EndpointTemplateParams{Region: key.region})
+ if err != nil {
+ errs = append(errs, err)
+ continue
+ }
+
+ requestJSON, err := jsonutil.Marshal(&requestCopy)
+ if err != nil {
+ errs = append(errs, err)
+ continue
+ }
+
+ headers := http.Header{}
+ headers.Add("Content-Type", "application/json;charset=utf-8")
+ headers.Add("Accept", "application/json")
+
+ requests = append(requests, &adapters.RequestData{
+ Method: http.MethodPost,
+ Uri: endpoint,
+ Body: requestJSON,
+ Headers: headers,
+ ImpIDs: openrtb_ext.GetImpIDs(requestCopy.Imp),
+ })
+ }
+
+ if len(requests) == 0 {
+ return nil, errs
+ }
+
+ return requests, errs
+}
+
+// stripBidderExt removes only the "bidder" key from imp.ext, preserving non-bidder
+// signals such as gpid, data and tid that the Prebid.js adapter also forwards. When
+// nothing else remains, imp.Ext is cleared so the imp serializes without an empty "ext".
+func stripBidderExt(imp *openrtb2.Imp) error {
+ if len(imp.Ext) == 0 {
+ imp.Ext = nil
+ return nil
+ }
+
+ var ext map[string]json.RawMessage
+ if err := jsonutil.Unmarshal(imp.Ext, &ext); err != nil {
+ return err
+ }
+
+ delete(ext, "bidder")
+
+ if len(ext) == 0 {
+ imp.Ext = nil
+ return nil
+ }
+
+ updatedExt, err := jsonutil.Marshal(ext)
+ if err != nil {
+ return err
+ }
+ imp.Ext = updatedExt
+
+ return nil
+}
+
+// setPublisherID mirrors the Prebid.js adapter's behavior of writing the publisherId
+// param onto app.publisher.id when the request is an app request, or site.publisher.id otherwise.
+func setPublisherID(request *openrtb2.BidRequest, publisherID string) {
+ if publisherID == "" {
+ return
+ }
+
+ if request.App != nil {
+ appCopy := *request.App
+ appCopy.Publisher = clonePublisher(appCopy.Publisher, publisherID)
+ request.App = &appCopy
+ return
+ }
+
+ var siteCopy openrtb2.Site
+ if request.Site != nil {
+ siteCopy = *request.Site
+ }
+ siteCopy.Publisher = clonePublisher(siteCopy.Publisher, publisherID)
+ request.Site = &siteCopy
+}
+
+// sanitizeDevice clears device.ifa when it's the all-zero sentinel value reported by the
+// OS when app tracking permission was declined, so it's never forwarded as if it were a
+// real device ID. There is no cookie-sync equivalent for app traffic; device.ifa and
+// user.eids (from an in-app ID SDK, if the app integrates one) are the identity signals
+// available for app-originated requests, and both already pass through unmodified otherwise.
+func sanitizeDevice(device *openrtb2.Device) *openrtb2.Device {
+ if device == nil || device.IFA != zeroIFA {
+ return device
+ }
+ deviceCopy := *device
+ deviceCopy.IFA = ""
+ return &deviceCopy
+}
+
+func clonePublisher(publisher *openrtb2.Publisher, id string) *openrtb2.Publisher {
+ if publisher == nil {
+ return &openrtb2.Publisher{ID: id}
+ }
+ publisherCopy := *publisher
+ publisherCopy.ID = id
+ return &publisherCopy
+}
+
+func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+ if adapters.IsResponseStatusCodeNoContent(response) {
+ return nil, nil
+ }
+
+ if err := adapters.CheckResponseStatusCodeForErrors(response); err != nil {
+ return nil, []error{err}
+ }
+
+ var bidResp openrtb2.BidResponse
+ if err := jsonutil.Unmarshal(response.Body, &bidResp); err != nil {
+ return nil, []error{&errortypes.BadServerResponse{
+ Message: fmt.Sprintf("bad server response: %s", err.Error()),
+ }}
+ }
+
+ if len(bidResp.SeatBid) == 0 {
+ return adapters.NewBidderResponse(), nil
+ }
+
+ var errs []error
+ bidderResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid))
+ if bidResp.Cur != "" {
+ bidderResponse.Currency = bidResp.Cur
+ }
+
+ for _, seatBid := range bidResp.SeatBid {
+ for i := range seatBid.Bid {
+ bid := seatBid.Bid[i]
+
+ bidType, err := getMediaTypeForBid(bid)
+ if err != nil {
+ errs = append(errs, err)
+ continue
+ }
+
+ resolveMacros(&bid)
+
+ bidderResponse.Bids = append(bidderResponse.Bids, &adapters.TypedBid{
+ Bid: &bid,
+ BidType: bidType,
+ })
+ }
+ }
+
+ return bidderResponse, errs
+}
+
+// resolveMacros substitutes the OpenRTB ${AUCTION_PRICE} macro in adm and nurl with the
+// bid price. peak226 always returns the macro in adm and relies on the demand-side adapter
+// to expand it, so leaving it unresolved would render the literal macro text in the creative
+// and report the wrong price on the win notice.
+func resolveMacros(bid *openrtb2.Bid) {
+ if bid == nil {
+ return
+ }
+ price := strconv.FormatFloat(bid.Price, 'f', -1, 64)
+ bid.AdM = strings.Replace(bid.AdM, "${AUCTION_PRICE}", price, -1)
+ bid.NURL = strings.Replace(bid.NURL, "${AUCTION_PRICE}", price, -1)
+}
+
+func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) {
+ switch bid.MType {
+ case openrtb2.MarkupBanner:
+ return openrtb_ext.BidTypeBanner, nil
+ case openrtb2.MarkupVideo:
+ return openrtb_ext.BidTypeVideo, nil
+ case openrtb2.MarkupNative:
+ return openrtb_ext.BidTypeNative, nil
+ }
+
+ return "", &errortypes.BadServerResponse{
+ Message: fmt.Sprintf("unrecognized bid type for impression %s", bid.ImpID),
+ }
+}
diff --git a/adapters/peak226/peak226_test.go b/adapters/peak226/peak226_test.go
new file mode 100644
index 00000000000..0176879ec71
--- /dev/null
+++ b/adapters/peak226/peak226_test.go
@@ -0,0 +1,97 @@
+package peak226
+
+import (
+ "encoding/json"
+ "testing"
+
+ "github.com/prebid/openrtb/v20/openrtb2"
+
+ "github.com/prebid/prebid-server/v4/adapters/adapterstest"
+ "github.com/prebid/prebid-server/v4/config"
+ "github.com/prebid/prebid-server/v4/openrtb_ext"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestJsonSamples(t *testing.T) {
+ bidder, buildErr := Builder(openrtb_ext.BidderPeak226, config.Adapter{
+ Endpoint: "https://{{.Region}}.a.viddea.com/edge_direct"},
+ config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+
+ if buildErr != nil {
+ t.Fatalf("Builder returned unexpected error %v", buildErr)
+ }
+
+ adapterstest.RunJSONBidderTest(t, "peak226test", bidder)
+}
+
+func TestEndpointTemplateMalformed(t *testing.T) {
+ _, buildErr := Builder(openrtb_ext.BidderPeak226, config.Adapter{
+ Endpoint: "{{Malformed}}"},
+ config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+
+ assert.Error(t, buildErr)
+}
+
+func TestStripBidderExt(t *testing.T) {
+ testCases := []struct {
+ name string
+ impExt json.RawMessage
+ expectedExt json.RawMessage
+ expectError bool
+ }{
+ {
+ name: "nil ext is left nil",
+ impExt: nil,
+ expectedExt: nil,
+ },
+ {
+ name: "empty ext is cleared",
+ impExt: json.RawMessage(``),
+ expectedExt: nil,
+ },
+ {
+ name: "ext holding only bidder is cleared entirely",
+ impExt: json.RawMessage(`{"bidder":{"publisherId":"pub-1"}}`),
+ expectedExt: nil,
+ },
+ {
+ name: "non-bidder keys are preserved",
+ impExt: json.RawMessage(`{"bidder":{"publisherId":"pub-1"},"gpid":"/1234/home"}`),
+ expectedExt: json.RawMessage(`{"gpid":"/1234/home"}`),
+ },
+ {
+ name: "malformed json returns an error",
+ impExt: json.RawMessage(`{"bidder":`),
+ expectError: true,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ imp := openrtb2.Imp{ID: "imp-1", Ext: tc.impExt}
+ err := stripBidderExt(&imp)
+
+ if tc.expectError {
+ assert.Error(t, err)
+ return
+ }
+
+ assert.NoError(t, err)
+ if tc.expectedExt == nil {
+ assert.Nil(t, imp.Ext)
+ } else {
+ assert.JSONEq(t, string(tc.expectedExt), string(imp.Ext))
+ }
+ })
+ }
+}
+
+func TestSetPublisherIDEmpty(t *testing.T) {
+ // An empty publisher ID must leave the request untouched rather than creating an
+ // empty site/app publisher object.
+ request := openrtb2.BidRequest{ID: "req-1"}
+ setPublisherID(&request, "")
+
+ assert.Nil(t, request.Site)
+ assert.Nil(t, request.App)
+}
diff --git a/adapters/peak226/peak226test/exemplary/banner.json b/adapters/peak226/peak226test/exemplary/banner.json
new file mode 100644
index 00000000000..e2311343bca
--- /dev/null
+++ b/adapters/peak226/peak226test/exemplary/banner.json
@@ -0,0 +1,101 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page",
+ "publisher": {
+ "id": "existing-pub-id"
+ }
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "bidfloor": 1.5,
+ "bidfloorcur": "USD",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub-test",
+ "placementId": "plc-test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://us.a.viddea.com/edge_direct",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page",
+ "publisher": {
+ "id": "pub-test"
+ }
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "tagid": "plc-test",
+ "bidfloor": 1.5,
+ "bidfloorcur": "USD",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ }
+ }
+ ]
+ },
+ "impIDs": ["imp-1"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "resp-1",
+ "cur": "USD",
+ "seatbid": [
+ {
+ "seat": "peak226",
+ "bid": [
+ {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 2.5,
+ "adm": "
ad
",
+ "crid": "cr-1",
+ "adomain": ["advertiser.com"],
+ "w": 300,
+ "h": 250,
+ "mtype": 1
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 2.5,
+ "adm": "ad
",
+ "crid": "cr-1",
+ "adomain": ["advertiser.com"],
+ "w": 300,
+ "h": 250,
+ "mtype": 1
+ },
+ "type": "banner"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/peak226/peak226test/exemplary/multiformat.json b/adapters/peak226/peak226test/exemplary/multiformat.json
new file mode 100644
index 00000000000..d798f201b7c
--- /dev/null
+++ b/adapters/peak226/peak226test/exemplary/multiformat.json
@@ -0,0 +1,139 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "video": {
+ "mimes": ["video/mp4"],
+ "protocols": [2, 3, 5, 6],
+ "w": 640,
+ "h": 480
+ },
+ "native": {
+ "request": "{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}}]}",
+ "ver": "1.2"
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub-test",
+ "placementId": "plc-test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://us.a.viddea.com/edge_direct",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page",
+ "publisher": {
+ "id": "pub-test"
+ }
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "tagid": "plc-test",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "video": {
+ "mimes": ["video/mp4"],
+ "protocols": [2, 3, 5, 6],
+ "w": 640,
+ "h": 480
+ },
+ "native": {
+ "request": "{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}}]}",
+ "ver": "1.2"
+ }
+ }
+ ]
+ },
+ "impIDs": ["imp-1"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "resp-1",
+ "cur": "USD",
+ "seatbid": [
+ {
+ "seat": "peak226",
+ "bid": [
+ {
+ "id": "bid-banner",
+ "impid": "imp-1",
+ "price": 2.5,
+ "adm": "ad
",
+ "crid": "cr-1",
+ "adomain": ["advertiser.com"],
+ "w": 300,
+ "h": 250,
+ "mtype": 1
+ },
+ {
+ "id": "bid-video",
+ "impid": "imp-1",
+ "price": 4.75,
+ "adm": "",
+ "crid": "cr-2",
+ "adomain": ["advertiser.com"],
+ "w": 640,
+ "h": 480,
+ "mtype": 2
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "bid-banner",
+ "impid": "imp-1",
+ "price": 2.5,
+ "adm": "ad
",
+ "crid": "cr-1",
+ "adomain": ["advertiser.com"],
+ "w": 300,
+ "h": 250,
+ "mtype": 1
+ },
+ "type": "banner"
+ },
+ {
+ "bid": {
+ "id": "bid-video",
+ "impid": "imp-1",
+ "price": 4.75,
+ "adm": "",
+ "crid": "cr-2",
+ "adomain": ["advertiser.com"],
+ "w": 640,
+ "h": 480,
+ "mtype": 2
+ },
+ "type": "video"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/peak226/peak226test/exemplary/native.json b/adapters/peak226/peak226test/exemplary/native.json
new file mode 100644
index 00000000000..29b44b7c8a2
--- /dev/null
+++ b/adapters/peak226/peak226test/exemplary/native.json
@@ -0,0 +1,95 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page",
+ "publisher": {
+ "id": "existing-pub-id"
+ }
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "native": {
+ "request": "{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}}]}",
+ "ver": "1.2"
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub-test",
+ "placementId": "plc-test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://us.a.viddea.com/edge_direct",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page",
+ "publisher": {
+ "id": "pub-test"
+ }
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "tagid": "plc-test",
+ "native": {
+ "request": "{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}}]}",
+ "ver": "1.2"
+ }
+ }
+ ]
+ },
+ "impIDs": ["imp-1"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "resp-1",
+ "cur": "USD",
+ "seatbid": [
+ {
+ "seat": "peak226",
+ "bid": [
+ {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 1.25,
+ "adm": "{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"title\":{\"text\":\"Hello\"}}]}",
+ "crid": "cr-3",
+ "adomain": ["advertiser.com"],
+ "mtype": 4
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 1.25,
+ "adm": "{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"title\":{\"text\":\"Hello\"}}]}",
+ "crid": "cr-3",
+ "adomain": ["advertiser.com"],
+ "mtype": 4
+ },
+ "type": "native"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/peak226/peak226test/exemplary/video.json b/adapters/peak226/peak226test/exemplary/video.json
new file mode 100644
index 00000000000..75c2e8020c6
--- /dev/null
+++ b/adapters/peak226/peak226test/exemplary/video.json
@@ -0,0 +1,105 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "bidfloor": 2.0,
+ "bidfloorcur": "USD",
+ "video": {
+ "mimes": ["video/mp4"],
+ "protocols": [2, 3, 5, 6],
+ "w": 640,
+ "h": 480
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub-test",
+ "placementId": "plc-test",
+ "region": "eu"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://eu.a.viddea.com/edge_direct",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page",
+ "publisher": {
+ "id": "pub-test"
+ }
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "tagid": "plc-test",
+ "bidfloor": 2.0,
+ "bidfloorcur": "USD",
+ "video": {
+ "mimes": ["video/mp4"],
+ "protocols": [2, 3, 5, 6],
+ "w": 640,
+ "h": 480
+ }
+ }
+ ]
+ },
+ "impIDs": ["imp-1"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "resp-1",
+ "cur": "USD",
+ "seatbid": [
+ {
+ "seat": "peak226",
+ "bid": [
+ {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 4.75,
+ "adm": "",
+ "crid": "cr-2",
+ "adomain": ["advertiser.com"],
+ "w": 640,
+ "h": 480,
+ "mtype": 2
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 4.75,
+ "adm": "",
+ "crid": "cr-2",
+ "adomain": ["advertiser.com"],
+ "w": 640,
+ "h": 480,
+ "mtype": 2
+ },
+ "type": "video"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/peak226/peak226test/supplemental/app-device-eids-passthrough.json b/adapters/peak226/peak226test/supplemental/app-device-eids-passthrough.json
new file mode 100644
index 00000000000..c890cbd80d0
--- /dev/null
+++ b/adapters/peak226/peak226test/supplemental/app-device-eids-passthrough.json
@@ -0,0 +1,116 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.example.app"
+ },
+ "device": {
+ "ifa": "AEBE52E7-03EE-455A-B3C4-E57283966239",
+ "os": "iOS"
+ },
+ "user": {
+ "eids": [
+ {
+ "source": "uidapi.com",
+ "uids": [{"id": "UID2-TOKEN", "atype": 3}]
+ }
+ ]
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "banner": {
+ "format": [{"w": 320, "h": 50}]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub-test",
+ "placementId": "plc-test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://us.a.viddea.com/edge_direct",
+ "body": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.example.app",
+ "publisher": {
+ "id": "pub-test"
+ }
+ },
+ "device": {
+ "ifa": "AEBE52E7-03EE-455A-B3C4-E57283966239",
+ "os": "iOS"
+ },
+ "user": {
+ "eids": [
+ {
+ "source": "uidapi.com",
+ "uids": [{"id": "UID2-TOKEN", "atype": 3}]
+ }
+ ]
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "tagid": "plc-test",
+ "banner": {
+ "format": [{"w": 320, "h": 50}]
+ }
+ }
+ ]
+ },
+ "impIDs": ["imp-1"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "resp-1",
+ "cur": "USD",
+ "seatbid": [
+ {
+ "seat": "peak226",
+ "bid": [
+ {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 0.85,
+ "adm": "ad
",
+ "crid": "cr-1",
+ "w": 320,
+ "h": 50,
+ "mtype": 1
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 0.85,
+ "adm": "ad
",
+ "crid": "cr-1",
+ "w": 320,
+ "h": 50,
+ "mtype": 1
+ },
+ "type": "banner"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/peak226/peak226test/supplemental/app-zero-ifa-cleared.json b/adapters/peak226/peak226test/supplemental/app-zero-ifa-cleared.json
new file mode 100644
index 00000000000..bc24f38461c
--- /dev/null
+++ b/adapters/peak226/peak226test/supplemental/app-zero-ifa-cleared.json
@@ -0,0 +1,99 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.example.app"
+ },
+ "device": {
+ "ifa": "00000000-0000-0000-0000-000000000000",
+ "os": "iOS"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "banner": {
+ "format": [{"w": 320, "h": 50}]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub-test",
+ "placementId": "plc-test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://us.a.viddea.com/edge_direct",
+ "body": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.example.app",
+ "publisher": {
+ "id": "pub-test"
+ }
+ },
+ "device": {
+ "os": "iOS"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "tagid": "plc-test",
+ "banner": {
+ "format": [{"w": 320, "h": 50}]
+ }
+ }
+ ]
+ },
+ "impIDs": ["imp-1"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "resp-1",
+ "cur": "USD",
+ "seatbid": [
+ {
+ "seat": "peak226",
+ "bid": [
+ {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 0.85,
+ "adm": "ad
",
+ "crid": "cr-1",
+ "w": 320,
+ "h": 50,
+ "mtype": 1
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 0.85,
+ "adm": "ad
",
+ "crid": "cr-1",
+ "w": 320,
+ "h": 50,
+ "mtype": 1
+ },
+ "type": "banner"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/peak226/peak226test/supplemental/auction-price-macro.json b/adapters/peak226/peak226test/supplemental/auction-price-macro.json
new file mode 100644
index 00000000000..a23da8cb57d
--- /dev/null
+++ b/adapters/peak226/peak226test/supplemental/auction-price-macro.json
@@ -0,0 +1,94 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub-test",
+ "placementId": "plc-test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://us.a.viddea.com/edge_direct",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page",
+ "publisher": {
+ "id": "pub-test"
+ }
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "tagid": "plc-test",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ }
+ }
+ ]
+ },
+ "impIDs": ["imp-1"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "resp-1",
+ "cur": "USD",
+ "seatbid": [
+ {
+ "seat": "peak226",
+ "bid": [
+ {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 3.25,
+ "adm": "ad
",
+ "nurl": "https://count.viddea.com/nurl?p=${AUCTION_PRICE}",
+ "crid": "cr-1",
+ "w": 300,
+ "h": 250,
+ "mtype": 1
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 3.25,
+ "adm": "ad
",
+ "nurl": "https://count.viddea.com/nurl?p=3.25",
+ "crid": "cr-1",
+ "w": 300,
+ "h": 250,
+ "mtype": 1
+ },
+ "type": "banner"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/peak226/peak226test/supplemental/bad-server-response.json b/adapters/peak226/peak226test/supplemental/bad-server-response.json
new file mode 100644
index 00000000000..04b4d95330a
--- /dev/null
+++ b/adapters/peak226/peak226test/supplemental/bad-server-response.json
@@ -0,0 +1,58 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub-test",
+ "placementId": "plc-test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://us.a.viddea.com/edge_direct",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page",
+ "publisher": {
+ "id": "pub-test"
+ }
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "tagid": "plc-test",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ }
+ }
+ ]
+ },
+ "impIDs": ["imp-1"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": "invalid-response-body"
+ }
+ }
+ ],
+ "expectedMakeBidsErrors": [
+ {
+ "value": "bad server response: ",
+ "comparison": "startswith"
+ }
+ ]
+}
diff --git a/adapters/peak226/peak226test/supplemental/bidfloor-currency-conversion-error.json b/adapters/peak226/peak226test/supplemental/bidfloor-currency-conversion-error.json
new file mode 100644
index 00000000000..579b676c40f
--- /dev/null
+++ b/adapters/peak226/peak226test/supplemental/bidfloor-currency-conversion-error.json
@@ -0,0 +1,44 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page"
+ },
+ "ext": {
+ "prebid": {
+ "currency": {
+ "rates": {
+ "EUR": {
+ "GBP": 0.85
+ }
+ },
+ "usepbsrates": false
+ }
+ }
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "bidfloor": 10,
+ "bidfloorcur": "EUR",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub-test",
+ "placementId": "plc-test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [],
+ "expectedBidResponses": [],
+ "expectedMakeRequestsErrors": [
+ {
+ "value": "Currency conversion rate not found: 'EUR' => 'USD'",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/peak226/peak226test/supplemental/bidfloor-currency-conversion.json b/adapters/peak226/peak226test/supplemental/bidfloor-currency-conversion.json
new file mode 100644
index 00000000000..7ece3789507
--- /dev/null
+++ b/adapters/peak226/peak226test/supplemental/bidfloor-currency-conversion.json
@@ -0,0 +1,118 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page"
+ },
+ "ext": {
+ "prebid": {
+ "currency": {
+ "rates": {
+ "EUR": {
+ "USD": 1.1
+ }
+ }
+ }
+ }
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "bidfloor": 10,
+ "bidfloorcur": "EUR",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub-test",
+ "placementId": "plc-test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://us.a.viddea.com/edge_direct",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page",
+ "publisher": {
+ "id": "pub-test"
+ }
+ },
+ "ext": {
+ "prebid": {
+ "currency": {
+ "rates": {
+ "EUR": {
+ "USD": 1.1
+ }
+ }
+ }
+ }
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "tagid": "plc-test",
+ "bidfloor": 11,
+ "bidfloorcur": "USD",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ }
+ }
+ ]
+ },
+ "impIDs": ["imp-1"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "resp-1",
+ "cur": "USD",
+ "seatbid": [
+ {
+ "seat": "peak226",
+ "bid": [
+ {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 12.0,
+ "adm": "ad
",
+ "crid": "cr-1",
+ "w": 300,
+ "h": 250,
+ "mtype": 1
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 12.0,
+ "adm": "ad
",
+ "crid": "cr-1",
+ "w": 300,
+ "h": 250,
+ "mtype": 1
+ },
+ "type": "banner"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/peak226/peak226test/supplemental/empty-seatbid.json b/adapters/peak226/peak226test/supplemental/empty-seatbid.json
new file mode 100644
index 00000000000..4f720e48d9d
--- /dev/null
+++ b/adapters/peak226/peak226test/supplemental/empty-seatbid.json
@@ -0,0 +1,61 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub-test",
+ "placementId": "plc-test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://us.a.viddea.com/edge_direct",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page",
+ "publisher": {
+ "id": "pub-test"
+ }
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "tagid": "plc-test",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ }
+ }
+ ]
+ },
+ "impIDs": ["imp-1"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "resp-1",
+ "cur": "USD",
+ "seatbid": []
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "bids": []
+ }
+ ]
+}
diff --git a/adapters/peak226/peak226test/supplemental/imp-ext-passthrough.json b/adapters/peak226/peak226test/supplemental/imp-ext-passthrough.json
new file mode 100644
index 00000000000..fdff6b7c3b3
--- /dev/null
+++ b/adapters/peak226/peak226test/supplemental/imp-ext-passthrough.json
@@ -0,0 +1,106 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub-test",
+ "placementId": "plc-test"
+ },
+ "gpid": "/1234/home#div-banner",
+ "tid": "tx-1",
+ "data": {
+ "pbadslot": "/1234/home#div-banner"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://us.a.viddea.com/edge_direct",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page",
+ "publisher": {
+ "id": "pub-test"
+ }
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "tagid": "plc-test",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "gpid": "/1234/home#div-banner",
+ "tid": "tx-1",
+ "data": {
+ "pbadslot": "/1234/home#div-banner"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": ["imp-1"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "resp-1",
+ "cur": "USD",
+ "seatbid": [
+ {
+ "seat": "peak226",
+ "bid": [
+ {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 2.5,
+ "adm": "ad
",
+ "crid": "cr-1",
+ "adomain": ["advertiser.com"],
+ "w": 300,
+ "h": 250,
+ "mtype": 1
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 2.5,
+ "adm": "ad
",
+ "crid": "cr-1",
+ "adomain": ["advertiser.com"],
+ "w": 300,
+ "h": 250,
+ "mtype": 1
+ },
+ "type": "banner"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/peak226/peak226test/supplemental/invalid-bidder-ext.json b/adapters/peak226/peak226test/supplemental/invalid-bidder-ext.json
new file mode 100644
index 00000000000..edf197065be
--- /dev/null
+++ b/adapters/peak226/peak226test/supplemental/invalid-bidder-ext.json
@@ -0,0 +1,26 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": "not-an-object"
+ }
+ }
+ ]
+ },
+ "httpCalls": [],
+ "expectedMakeRequestsErrors": [
+ {
+ "value": "imp #imp-1: ",
+ "comparison": "startswith"
+ }
+ ]
+}
diff --git a/adapters/peak226/peak226test/supplemental/invalid-imp-ext.json b/adapters/peak226/peak226test/supplemental/invalid-imp-ext.json
new file mode 100644
index 00000000000..2dfad1d9b89
--- /dev/null
+++ b/adapters/peak226/peak226test/supplemental/invalid-imp-ext.json
@@ -0,0 +1,24 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": "not-an-object"
+ }
+ ]
+ },
+ "httpCalls": [],
+ "expectedMakeRequestsErrors": [
+ {
+ "value": "imp #imp-1: ",
+ "comparison": "startswith"
+ }
+ ]
+}
diff --git a/adapters/peak226/peak226test/supplemental/multi-publisher-same-region-split.json b/adapters/peak226/peak226test/supplemental/multi-publisher-same-region-split.json
new file mode 100644
index 00000000000..1627cb8b7a3
--- /dev/null
+++ b/adapters/peak226/peak226test/supplemental/multi-publisher-same-region-split.json
@@ -0,0 +1,160 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page"
+ },
+ "imp": [
+ {
+ "id": "imp-pub-a",
+ "bidfloor": 1.0,
+ "bidfloorcur": "USD",
+ "banner": { "format": [{"w": 300, "h": 250}] },
+ "ext": {
+ "bidder": { "publisherId": "pub-a", "placementId": "plc-a" }
+ }
+ },
+ {
+ "id": "imp-pub-b",
+ "bidfloor": 2.0,
+ "bidfloorcur": "USD",
+ "banner": { "format": [{"w": 320, "h": 50}] },
+ "ext": {
+ "bidder": { "publisherId": "pub-b", "placementId": "plc-b" }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://us.a.viddea.com/edge_direct",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page",
+ "publisher": { "id": "pub-a" }
+ },
+ "imp": [
+ {
+ "id": "imp-pub-a",
+ "tagid": "plc-a",
+ "bidfloor": 1.0,
+ "bidfloorcur": "USD",
+ "banner": { "format": [{"w": 300, "h": 250}] }
+ }
+ ]
+ },
+ "impIDs": ["imp-pub-a"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "resp-pub-a",
+ "cur": "USD",
+ "seatbid": [
+ {
+ "seat": "peak226",
+ "bid": [
+ {
+ "id": "bid-pub-a-1",
+ "impid": "imp-pub-a",
+ "price": 1.5,
+ "adm": "pub-a-ad
",
+ "crid": "cr-pub-a-1",
+ "w": 300,
+ "h": 250,
+ "mtype": 1
+ }
+ ]
+ }
+ ]
+ }
+ }
+ },
+ {
+ "expectedRequest": {
+ "uri": "https://us.a.viddea.com/edge_direct",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page",
+ "publisher": { "id": "pub-b" }
+ },
+ "imp": [
+ {
+ "id": "imp-pub-b",
+ "tagid": "plc-b",
+ "bidfloor": 2.0,
+ "bidfloorcur": "USD",
+ "banner": { "format": [{"w": 320, "h": 50}] }
+ }
+ ]
+ },
+ "impIDs": ["imp-pub-b"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "resp-pub-b",
+ "cur": "USD",
+ "seatbid": [
+ {
+ "seat": "peak226",
+ "bid": [
+ {
+ "id": "bid-pub-b-1",
+ "impid": "imp-pub-b",
+ "price": 2.5,
+ "adm": "pub-b-ad
",
+ "crid": "cr-pub-b-1",
+ "w": 320,
+ "h": 50,
+ "mtype": 1
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "bid-pub-a-1",
+ "impid": "imp-pub-a",
+ "price": 1.5,
+ "adm": "pub-a-ad
",
+ "crid": "cr-pub-a-1",
+ "w": 300,
+ "h": 250,
+ "mtype": 1
+ },
+ "type": "banner"
+ }
+ ]
+ },
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "bid-pub-b-1",
+ "impid": "imp-pub-b",
+ "price": 2.5,
+ "adm": "pub-b-ad
",
+ "crid": "cr-pub-b-1",
+ "w": 320,
+ "h": 50,
+ "mtype": 1
+ },
+ "type": "banner"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/peak226/peak226test/supplemental/multi-region-split.json b/adapters/peak226/peak226test/supplemental/multi-region-split.json
new file mode 100644
index 00000000000..4ce83bc4109
--- /dev/null
+++ b/adapters/peak226/peak226test/supplemental/multi-region-split.json
@@ -0,0 +1,160 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page"
+ },
+ "imp": [
+ {
+ "id": "imp-us",
+ "bidfloor": 1.0,
+ "bidfloorcur": "USD",
+ "banner": { "format": [{"w": 300, "h": 250}] },
+ "ext": {
+ "bidder": { "publisherId": "pub-us-1", "placementId": "plc-us-1" }
+ }
+ },
+ {
+ "id": "imp-jp",
+ "bidfloor": 2.0,
+ "bidfloorcur": "USD",
+ "banner": { "format": [{"w": 320, "h": 50}] },
+ "ext": {
+ "bidder": { "publisherId": "pub-jp-1", "placementId": "plc-jp-1", "region": "jp" }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://us.a.viddea.com/edge_direct",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page",
+ "publisher": { "id": "pub-us-1" }
+ },
+ "imp": [
+ {
+ "id": "imp-us",
+ "tagid": "plc-us-1",
+ "bidfloor": 1.0,
+ "bidfloorcur": "USD",
+ "banner": { "format": [{"w": 300, "h": 250}] }
+ }
+ ]
+ },
+ "impIDs": ["imp-us"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "resp-us",
+ "cur": "USD",
+ "seatbid": [
+ {
+ "seat": "peak226",
+ "bid": [
+ {
+ "id": "bid-us-1",
+ "impid": "imp-us",
+ "price": 1.5,
+ "adm": "us-ad
",
+ "crid": "cr-us-1",
+ "w": 300,
+ "h": 250,
+ "mtype": 1
+ }
+ ]
+ }
+ ]
+ }
+ }
+ },
+ {
+ "expectedRequest": {
+ "uri": "https://jp.a.viddea.com/edge_direct",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page",
+ "publisher": { "id": "pub-jp-1" }
+ },
+ "imp": [
+ {
+ "id": "imp-jp",
+ "tagid": "plc-jp-1",
+ "bidfloor": 2.0,
+ "bidfloorcur": "USD",
+ "banner": { "format": [{"w": 320, "h": 50}] }
+ }
+ ]
+ },
+ "impIDs": ["imp-jp"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "resp-jp",
+ "cur": "USD",
+ "seatbid": [
+ {
+ "seat": "peak226",
+ "bid": [
+ {
+ "id": "bid-jp-1",
+ "impid": "imp-jp",
+ "price": 2.5,
+ "adm": "jp-ad
",
+ "crid": "cr-jp-1",
+ "w": 320,
+ "h": 50,
+ "mtype": 1
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "bid-us-1",
+ "impid": "imp-us",
+ "price": 1.5,
+ "adm": "us-ad
",
+ "crid": "cr-us-1",
+ "w": 300,
+ "h": 250,
+ "mtype": 1
+ },
+ "type": "banner"
+ }
+ ]
+ },
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "bid-jp-1",
+ "impid": "imp-jp",
+ "price": 2.5,
+ "adm": "jp-ad
",
+ "crid": "cr-jp-1",
+ "w": 320,
+ "h": 50,
+ "mtype": 1
+ },
+ "type": "banner"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/peak226/peak226test/supplemental/region-jp-app-publisher.json b/adapters/peak226/peak226test/supplemental/region-jp-app-publisher.json
new file mode 100644
index 00000000000..2aa46e3afb0
--- /dev/null
+++ b/adapters/peak226/peak226test/supplemental/region-jp-app-publisher.json
@@ -0,0 +1,93 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.example.app"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "banner": {
+ "format": [{"w": 320, "h": 50}]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub-test",
+ "placementId": "plc-test",
+ "region": "jp"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://jp.a.viddea.com/edge_direct",
+ "body": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.example.app",
+ "publisher": {
+ "id": "pub-test"
+ }
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "tagid": "plc-test",
+ "banner": {
+ "format": [{"w": 320, "h": 50}]
+ }
+ }
+ ]
+ },
+ "impIDs": ["imp-1"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "resp-1",
+ "cur": "USD",
+ "seatbid": [
+ {
+ "seat": "peak226",
+ "bid": [
+ {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 0.85,
+ "adm": "ad
",
+ "crid": "cr-1",
+ "w": 320,
+ "h": 50,
+ "mtype": 1
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 0.85,
+ "adm": "ad
",
+ "crid": "cr-1",
+ "w": 320,
+ "h": 50,
+ "mtype": 1
+ },
+ "type": "banner"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/peak226/peak226test/supplemental/status-code-bad-request.json b/adapters/peak226/peak226test/supplemental/status-code-bad-request.json
new file mode 100644
index 00000000000..ff0e5b639e8
--- /dev/null
+++ b/adapters/peak226/peak226test/supplemental/status-code-bad-request.json
@@ -0,0 +1,58 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub-test",
+ "placementId": "plc-test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://us.a.viddea.com/edge_direct",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page",
+ "publisher": {
+ "id": "pub-test"
+ }
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "tagid": "plc-test",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ }
+ }
+ ]
+ },
+ "impIDs": ["imp-1"]
+ },
+ "mockResponse": {
+ "status": 400,
+ "body": {}
+ }
+ }
+ ],
+ "expectedMakeBidsErrors": [
+ {
+ "value": "Unexpected status code: 400. Run with request.debug = 1 for more info",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/peak226/peak226test/supplemental/status-code-no-content.json b/adapters/peak226/peak226test/supplemental/status-code-no-content.json
new file mode 100644
index 00000000000..20f2df88d55
--- /dev/null
+++ b/adapters/peak226/peak226test/supplemental/status-code-no-content.json
@@ -0,0 +1,53 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub-test",
+ "placementId": "plc-test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://us.a.viddea.com/edge_direct",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page",
+ "publisher": {
+ "id": "pub-test"
+ }
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "tagid": "plc-test",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ }
+ }
+ ]
+ },
+ "impIDs": ["imp-1"]
+ },
+ "mockResponse": {
+ "status": 204,
+ "body": {}
+ }
+ }
+ ],
+ "expectedBidResponses": []
+}
diff --git a/adapters/peak226/peak226test/supplemental/status-code-server-error.json b/adapters/peak226/peak226test/supplemental/status-code-server-error.json
new file mode 100644
index 00000000000..ab2df6c0c4a
--- /dev/null
+++ b/adapters/peak226/peak226test/supplemental/status-code-server-error.json
@@ -0,0 +1,58 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub-test",
+ "placementId": "plc-test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://us.a.viddea.com/edge_direct",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page",
+ "publisher": {
+ "id": "pub-test"
+ }
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "tagid": "plc-test",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ }
+ }
+ ]
+ },
+ "impIDs": ["imp-1"]
+ },
+ "mockResponse": {
+ "status": 500,
+ "body": {}
+ }
+ }
+ ],
+ "expectedMakeBidsErrors": [
+ {
+ "value": "Unexpected status code: 500. Run with request.debug = 1 for more info",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/peak226/peak226test/supplemental/unrecognized-mtype.json b/adapters/peak226/peak226test/supplemental/unrecognized-mtype.json
new file mode 100644
index 00000000000..365ba4f7070
--- /dev/null
+++ b/adapters/peak226/peak226test/supplemental/unrecognized-mtype.json
@@ -0,0 +1,106 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub-test",
+ "placementId": "plc-test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://us.a.viddea.com/edge_direct",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "https://publisher.example.com/page",
+ "publisher": {
+ "id": "pub-test"
+ }
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "tagid": "plc-test",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ }
+ }
+ ]
+ },
+ "impIDs": ["imp-1"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "resp-1",
+ "cur": "USD",
+ "seatbid": [
+ {
+ "seat": "peak226",
+ "bid": [
+ {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 2.5,
+ "adm": "ad
",
+ "crid": "cr-1",
+ "w": 300,
+ "h": 250,
+ "mtype": 1
+ },
+ {
+ "id": "bid-2",
+ "impid": "imp-2",
+ "price": 1.0,
+ "adm": "audio-not-supported",
+ "crid": "cr-2",
+ "mtype": 3
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "expectedMakeBidsErrors": [
+ {
+ "value": "unrecognized bid type for impression imp-2",
+ "comparison": "literal"
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 2.5,
+ "adm": "ad
",
+ "crid": "cr-1",
+ "w": 300,
+ "h": 250,
+ "mtype": 1
+ },
+ "type": "banner"
+ }
+ ]
+ }
+ ]
+}
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index c8da847f761..f889228445f 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -184,6 +184,7 @@ import (
"github.com/prebid/prebid-server/v4/adapters/outbrain"
"github.com/prebid/prebid-server/v4/adapters/ownadx"
"github.com/prebid/prebid-server/v4/adapters/pangle"
+ "github.com/prebid/prebid-server/v4/adapters/peak226"
"github.com/prebid/prebid-server/v4/adapters/pgamssp"
"github.com/prebid/prebid-server/v4/adapters/pixfuture"
"github.com/prebid/prebid-server/v4/adapters/playdigo"
@@ -465,6 +466,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
openrtb_ext.BidderOutbrain: outbrain.Builder,
openrtb_ext.BidderOwnAdx: ownadx.Builder,
openrtb_ext.BidderPangle: pangle.Builder,
+ openrtb_ext.BidderPeak226: peak226.Builder,
openrtb_ext.BidderPGAMSsp: pgamssp.Builder,
openrtb_ext.BidderPixfuture: pixfuture.Builder,
openrtb_ext.BidderPlaydigo: playdigo.Builder,
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index 4858f69ef8f..d23f5f70326 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -202,6 +202,7 @@ var coreBidderNames []BidderName = []BidderName{
BidderOutbrain,
BidderOwnAdx,
BidderPangle,
+ BidderPeak226,
BidderPGAMSsp,
BidderPixfuture,
BidderPlaydigo,
@@ -587,6 +588,7 @@ const (
BidderOutbrain BidderName = "outbrain"
BidderOwnAdx BidderName = "ownadx"
BidderPangle BidderName = "pangle"
+ BidderPeak226 BidderName = "peak226"
BidderPGAMSsp BidderName = "pgamssp"
BidderPixfuture BidderName = "pixfuture"
BidderPlaydigo BidderName = "playdigo"
diff --git a/openrtb_ext/imp_peak226.go b/openrtb_ext/imp_peak226.go
new file mode 100644
index 00000000000..74f01412166
--- /dev/null
+++ b/openrtb_ext/imp_peak226.go
@@ -0,0 +1,8 @@
+package openrtb_ext
+
+// ImpExtPeak226 defines the contract for bidrequest.imp[i].ext.prebid.bidder.peak226
+type ImpExtPeak226 struct {
+ PublisherID string `json:"publisherId"`
+ PlacementID string `json:"placementId"`
+ Region string `json:"region,omitempty"`
+}
diff --git a/static/bidder-info/peak226.yaml b/static/bidder-info/peak226.yaml
new file mode 100644
index 00000000000..c5c0ecc8b3e
--- /dev/null
+++ b/static/bidder-info/peak226.yaml
@@ -0,0 +1,22 @@
+endpoint: "https://{{.Region}}.a.viddea.com/edge_direct"
+geoscope:
+ - global
+maintainer:
+ email: "support@edge226.com"
+gvlVendorID: 1202
+modifyingVastXmlAllowed: false
+openrtb:
+ version: 2.6
+ gpp-supported: true
+ multiformat-supported: true
+capabilities:
+ app:
+ mediaTypes:
+ - banner
+ - video
+ - native
+ site:
+ mediaTypes:
+ - banner
+ - video
+ - native
diff --git a/static/bidder-params/peak226.json b/static/bidder-params/peak226.json
new file mode 100644
index 00000000000..f08d0cfd5e4
--- /dev/null
+++ b/static/bidder-params/peak226.json
@@ -0,0 +1,24 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "title": "Peak226 Adapter Params",
+ "description": "A schema which validates params accepted by the Peak226 adapter",
+ "type": "object",
+ "properties": {
+ "publisherId": {
+ "type": "string",
+ "description": "Publisher/account ID",
+ "minLength": 1
+ },
+ "placementId": {
+ "type": "string",
+ "description": "Placement ID for this ad unit",
+ "minLength": 1
+ },
+ "region": {
+ "type": "string",
+ "description": "Data center to send the request to. Defaults to us.",
+ "enum": ["us", "eu", "jp"]
+ }
+ },
+ "required": ["publisherId", "placementId"]
+}