diff --git a/.github/workflows/scripts/codepath-notification b/.github/workflows/scripts/codepath-notification
index 8a3bae8daca..b917c6d2019 100644
--- a/.github/workflows/scripts/codepath-notification
+++ b/.github/workflows/scripts/codepath-notification
@@ -19,3 +19,4 @@ adapters/ix|imp_ix|ix.json|ix.yaml: pdu-supply-prebid@indexexchange.com
medianet: prebid@media.net
gumgum: prebid@gumgum.com
kargo: kraken@kargo.com
+proxistore: proxistore-technics@proxistore.com
diff --git a/adapters/proxistore/params_test.go b/adapters/proxistore/params_test.go
new file mode 100644
index 00000000000..bb1ddba43a2
--- /dev/null
+++ b/adapters/proxistore/params_test.go
@@ -0,0 +1,51 @@
+package proxistore
+
+import (
+ "encoding/json"
+ "testing"
+
+ "github.com/prebid/prebid-server/v3/openrtb_ext"
+)
+
+func TestValidParams(t *testing.T) {
+ validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+ if err != nil {
+ t.Fatalf("Failed to fetch the json schema. %v", err)
+ }
+
+ for _, p := range validParams {
+ if err := validator.Validate(openrtb_ext.BidderProxistore, json.RawMessage(p)); err != nil {
+ t.Errorf("Schema rejected valid params: %s", p)
+ }
+ }
+}
+
+func TestInvalidParams(t *testing.T) {
+ validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+ if err != nil {
+ t.Fatalf("Failed to fetch the json schema. %v", err)
+ }
+
+ for _, p := range invalidParams {
+ if err := validator.Validate(openrtb_ext.BidderProxistore, json.RawMessage(p)); err == nil {
+ t.Errorf("Schema allowed invalid params: %s", p)
+ }
+ }
+}
+
+var validParams = []string{
+ `{"website": "example.com", "language": "fr"}`,
+ `{"website": "test-site", "language": "en"}`,
+ `{"website": "publisher.be", "language": "nl"}`,
+}
+
+var invalidParams = []string{
+ `{}`,
+ `{"website": "example.com"}`,
+ `{"language": "fr"}`,
+ `{"website": "", "language": "fr"}`,
+ `{"website": "example.com", "language": ""}`,
+ `{"website": 123, "language": "fr"}`,
+ `{"website": "example.com", "language": 456}`,
+ `null`,
+}
diff --git a/adapters/proxistore/proxistore.go b/adapters/proxistore/proxistore.go
new file mode 100644
index 00000000000..3a93c70cd3d
--- /dev/null
+++ b/adapters/proxistore/proxistore.go
@@ -0,0 +1,139 @@
+package proxistore
+
+import (
+ "fmt"
+ "net/http"
+
+ "github.com/prebid/openrtb/v20/openrtb2"
+ "github.com/prebid/prebid-server/v3/adapters"
+ "github.com/prebid/prebid-server/v3/config"
+ "github.com/prebid/prebid-server/v3/errortypes"
+ "github.com/prebid/prebid-server/v3/openrtb_ext"
+ "github.com/prebid/prebid-server/v3/util/jsonutil"
+)
+
+type adapter struct {
+ endpoint string
+}
+
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
+ bidder := &adapter{
+ endpoint: config.Endpoint,
+ }
+ return bidder, nil
+}
+
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+ var errors []error
+
+ // Validate that all impressions have required bidder params
+ for _, imp := range request.Imp {
+ var extBidder adapters.ExtImpBidder
+ if err := jsonutil.Unmarshal(imp.Ext, &extBidder); err != nil {
+ errors = append(errors, &errortypes.BadInput{
+ Message: fmt.Sprintf("Error parsing imp[%s].ext: %s", imp.ID, err.Error()),
+ })
+ continue
+ }
+
+ var bidderExt openrtb_ext.ExtImpProxistore
+ if err := jsonutil.Unmarshal(extBidder.Bidder, &bidderExt); err != nil {
+ errors = append(errors, &errortypes.BadInput{
+ Message: fmt.Sprintf("Error parsing imp[%s].ext.bidder: %s", imp.ID, err.Error()),
+ })
+ continue
+ }
+
+ if bidderExt.Website == "" || bidderExt.Language == "" {
+ errors = append(errors, &errortypes.BadInput{
+ Message: fmt.Sprintf("imp[%s]: website and language are required bidder params", imp.ID),
+ })
+ }
+ }
+
+ if len(errors) > 0 {
+ return nil, errors
+ }
+
+ requestJSON, err := jsonutil.Marshal(request)
+ if err != nil {
+ return nil, []error{err}
+ }
+
+ headers := http.Header{}
+ headers.Add("Content-Type", "application/json")
+ headers.Add("Accept", "application/json")
+ headers.Add("X-Prebid-Source", "server")
+
+ requestData := &adapters.RequestData{
+ Method: "POST",
+ Uri: a.endpoint,
+ Body: requestJSON,
+ Headers: headers,
+ ImpIDs: openrtb_ext.GetImpIDs(request.Imp),
+ }
+
+ return []*adapters.RequestData{requestData}, nil
+}
+
+func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+ if responseData.StatusCode == http.StatusNoContent {
+ return nil, nil
+ }
+
+ if responseData.StatusCode == http.StatusBadRequest {
+ err := &errortypes.BadInput{
+ Message: "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.",
+ }
+ return nil, []error{err}
+ }
+
+ if responseData.StatusCode != http.StatusOK {
+ err := &errortypes.BadServerResponse{
+ Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info.", responseData.StatusCode),
+ }
+ return nil, []error{err}
+ }
+
+ var response openrtb2.BidResponse
+ if err := jsonutil.Unmarshal(responseData.Body, &response); err != nil {
+ return nil, []error{err}
+ }
+
+ bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
+ bidResponse.Currency = response.Cur
+
+ var errors []error
+ for _, seatBid := range response.SeatBid {
+ for i, bid := range seatBid.Bid {
+ bidType, err := getMediaTypeForBid(bid)
+ if err != nil {
+ errors = append(errors, err)
+ continue
+ }
+ bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
+ Bid: &seatBid.Bid[i],
+ BidType: bidType,
+ })
+ }
+ }
+
+ return bidResponse, errors
+}
+
+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.MarkupAudio:
+ return openrtb_ext.BidTypeAudio, nil
+ case openrtb2.MarkupNative:
+ return openrtb_ext.BidTypeNative, nil
+ default:
+ return "", &errortypes.BadServerResponse{
+ Message: fmt.Sprintf("unsupported MType %d for bid %s", bid.MType, bid.ID),
+ }
+ }
+}
diff --git a/adapters/proxistore/proxistore_test.go b/adapters/proxistore/proxistore_test.go
new file mode 100644
index 00000000000..b0a039d6e23
--- /dev/null
+++ b/adapters/proxistore/proxistore_test.go
@@ -0,0 +1,27 @@
+package proxistore
+
+import (
+ "testing"
+
+ "github.com/prebid/prebid-server/v3/adapters/adapterstest"
+ "github.com/prebid/prebid-server/v3/config"
+ "github.com/prebid/prebid-server/v3/openrtb_ext"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestJsonSamples(t *testing.T) {
+ bidder, buildErr := Builder(
+ openrtb_ext.BidderProxistore,
+ config.Adapter{
+ Endpoint: "https://api.proxistore.com/v3/rtb/openrtb",
+ },
+ config.Server{
+ ExternalUrl: "http://hosturl.com",
+ GvlID: 1,
+ DataCenter: "2",
+ },
+ )
+
+ assert.NoError(t, buildErr)
+ adapterstest.RunJSONBidderTest(t, "proxistoretest", bidder)
+}
diff --git a/adapters/proxistore/proxistoretest/exemplary/multi-imp-banner.json b/adapters/proxistore/proxistoretest/exemplary/multi-imp-banner.json
new file mode 100644
index 00000000000..cab78da49bb
--- /dev/null
+++ b/adapters/proxistore/proxistoretest/exemplary/multi-imp-banner.json
@@ -0,0 +1,151 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "Mozilla/5.0"
+ },
+ "site": {
+ "domain": "example.com",
+ "page": "https://example.com/page"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ },
+ {
+ "id": "imp-2",
+ "banner": {
+ "format": [{"w": 728, "h": 90}]
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://api.proxistore.com/v3/rtb/openrtb",
+ "body": {
+ "id": "test-request-id",
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "Mozilla/5.0"
+ },
+ "site": {
+ "domain": "example.com",
+ "page": "https://example.com/page"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ },
+ {
+ "id": "imp-2",
+ "banner": {
+ "format": [{"w": 728, "h": 90}]
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": ["imp-1", "imp-2"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 1.50,
+ "adm": "
Ad 1
",
+ "crid": "creative-1",
+ "w": 300,
+ "h": 250,
+ "mtype": 1
+ },
+ {
+ "id": "bid-2",
+ "impid": "imp-2",
+ "price": 0.80,
+ "adm": "Ad 2
",
+ "crid": "creative-2",
+ "w": 728,
+ "h": 90,
+ "mtype": 1
+ }
+ ],
+ "seat": "proxistore"
+ }
+ ],
+ "cur": "EUR"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "EUR",
+ "bids": [
+ {
+ "bid": {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 1.50,
+ "adm": "Ad 1
",
+ "crid": "creative-1",
+ "w": 300,
+ "h": 250,
+ "mtype": 1
+ },
+ "type": "banner"
+ },
+ {
+ "bid": {
+ "id": "bid-2",
+ "impid": "imp-2",
+ "price": 0.80,
+ "adm": "Ad 2
",
+ "crid": "creative-2",
+ "w": 728,
+ "h": 90,
+ "mtype": 1
+ },
+ "type": "banner"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/proxistore/proxistoretest/exemplary/simple-audio.json b/adapters/proxistore/proxistoretest/exemplary/simple-audio.json
new file mode 100644
index 00000000000..4bcea393646
--- /dev/null
+++ b/adapters/proxistore/proxistoretest/exemplary/simple-audio.json
@@ -0,0 +1,108 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
+ },
+ "site": {
+ "id": "site-id",
+ "domain": "example.com",
+ "page": "https://example.com/page"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "audio": {
+ "mimes": ["audio/mp3"],
+ "minduration": 5,
+ "maxduration": 30
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://api.proxistore.com/v3/rtb/openrtb",
+ "body": {
+ "id": "test-request-id",
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
+ },
+ "site": {
+ "id": "site-id",
+ "domain": "example.com",
+ "page": "https://example.com/page"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "audio": {
+ "mimes": ["audio/mp3"],
+ "minduration": 5,
+ "maxduration": 30
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": ["test-imp-id"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "test-bid-id",
+ "impid": "test-imp-id",
+ "price": 1.50,
+ "adm": "",
+ "adomain": ["advertiser.com"],
+ "crid": "creative-audio-123",
+ "mtype": 3
+ }
+ ],
+ "seat": "proxistore"
+ }
+ ],
+ "cur": "EUR"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "EUR",
+ "bids": [
+ {
+ "bid": {
+ "id": "test-bid-id",
+ "impid": "test-imp-id",
+ "price": 1.50,
+ "adm": "",
+ "adomain": ["advertiser.com"],
+ "crid": "creative-audio-123",
+ "mtype": 3
+ },
+ "type": "audio"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/proxistore/proxistoretest/exemplary/simple-banner-app.json b/adapters/proxistore/proxistoretest/exemplary/simple-banner-app.json
new file mode 100644
index 00000000000..bfc01eca1ca
--- /dev/null
+++ b/adapters/proxistore/proxistoretest/exemplary/simple-banner-app.json
@@ -0,0 +1,138 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36"
+ },
+ "app": {
+ "id": "app-id",
+ "bundle": "com.example.app",
+ "name": "Example App",
+ "domain": "example.com",
+ "publisher": {
+ "id": "pub-123"
+ }
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {"w": 320, "h": 50},
+ {"w": 300, "h": 250}
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ }
+ ],
+ "regs": {
+ "gdpr": 1
+ },
+ "user": {
+ "ext": {
+ "consent": "COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA"
+ }
+ }
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://api.proxistore.com/v3/rtb/openrtb",
+ "body": {
+ "id": "test-request-id",
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36"
+ },
+ "app": {
+ "id": "app-id",
+ "bundle": "com.example.app",
+ "name": "Example App",
+ "domain": "example.com",
+ "publisher": {
+ "id": "pub-123"
+ }
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {"w": 320, "h": 50},
+ {"w": 300, "h": 250}
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ }
+ ],
+ "regs": {
+ "gdpr": 1
+ },
+ "user": {
+ "ext": {
+ "consent": "COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA"
+ }
+ }
+ },
+ "impIDs": ["test-imp-id"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "test-bid-id",
+ "impid": "test-imp-id",
+ "price": 0.85,
+ "adm": "App banner creative
",
+ "adomain": ["advertiser.com"],
+ "crid": "creative-456",
+ "w": 320,
+ "h": 50,
+ "mtype": 1
+ }
+ ],
+ "seat": "proxistore"
+ }
+ ],
+ "cur": "EUR"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "EUR",
+ "bids": [
+ {
+ "bid": {
+ "id": "test-bid-id",
+ "impid": "test-imp-id",
+ "price": 0.85,
+ "adm": "App banner creative
",
+ "adomain": ["advertiser.com"],
+ "crid": "creative-456",
+ "w": 320,
+ "h": 50,
+ "mtype": 1
+ },
+ "type": "banner"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/proxistore/proxistoretest/exemplary/simple-banner.json b/adapters/proxistore/proxistoretest/exemplary/simple-banner.json
new file mode 100644
index 00000000000..a3006eed94d
--- /dev/null
+++ b/adapters/proxistore/proxistoretest/exemplary/simple-banner.json
@@ -0,0 +1,130 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
+ },
+ "site": {
+ "id": "site-id",
+ "domain": "example.com",
+ "page": "https://example.com/page"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {"w": 300, "h": 250},
+ {"w": 300, "h": 600}
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ }
+ ],
+ "regs": {
+ "gdpr": 1
+ },
+ "user": {
+ "ext": {
+ "consent": "COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA"
+ }
+ }
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://api.proxistore.com/v3/rtb/openrtb",
+ "body": {
+ "id": "test-request-id",
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
+ },
+ "site": {
+ "id": "site-id",
+ "domain": "example.com",
+ "page": "https://example.com/page"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {"w": 300, "h": 250},
+ {"w": 300, "h": 600}
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ }
+ ],
+ "regs": {
+ "gdpr": 1
+ },
+ "user": {
+ "ext": {
+ "consent": "COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA"
+ }
+ }
+ },
+ "impIDs": ["test-imp-id"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "test-bid-id",
+ "impid": "test-imp-id",
+ "price": 1.23,
+ "adm": "Ad creative
",
+ "adomain": ["advertiser.com"],
+ "crid": "creative-123",
+ "w": 300,
+ "h": 250,
+ "mtype": 1
+ }
+ ],
+ "seat": "proxistore"
+ }
+ ],
+ "cur": "EUR"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "EUR",
+ "bids": [
+ {
+ "bid": {
+ "id": "test-bid-id",
+ "impid": "test-imp-id",
+ "price": 1.23,
+ "adm": "Ad creative
",
+ "adomain": ["advertiser.com"],
+ "crid": "creative-123",
+ "w": 300,
+ "h": 250,
+ "mtype": 1
+ },
+ "type": "banner"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/proxistore/proxistoretest/exemplary/simple-native.json b/adapters/proxistore/proxistoretest/exemplary/simple-native.json
new file mode 100644
index 00000000000..b0146495cd5
--- /dev/null
+++ b/adapters/proxistore/proxistoretest/exemplary/simple-native.json
@@ -0,0 +1,104 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
+ },
+ "site": {
+ "id": "site-id",
+ "domain": "example.com",
+ "page": "https://example.com/page"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "native": {
+ "request": "{\"ver\":\"1.1\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":90}}]}"
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://api.proxistore.com/v3/rtb/openrtb",
+ "body": {
+ "id": "test-request-id",
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
+ },
+ "site": {
+ "id": "site-id",
+ "domain": "example.com",
+ "page": "https://example.com/page"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "native": {
+ "request": "{\"ver\":\"1.1\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":90}}]}"
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": ["test-imp-id"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "test-bid-id",
+ "impid": "test-imp-id",
+ "price": 1.80,
+ "adm": "{\"native\":{\"ver\":\"1.1\",\"link\":{\"url\":\"https://example.com\"},\"assets\":[{\"id\":1,\"title\":{\"text\":\"Ad Title\"}}]}}",
+ "adomain": ["advertiser.com"],
+ "crid": "creative-native-123",
+ "mtype": 4
+ }
+ ],
+ "seat": "proxistore"
+ }
+ ],
+ "cur": "EUR"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "EUR",
+ "bids": [
+ {
+ "bid": {
+ "id": "test-bid-id",
+ "impid": "test-imp-id",
+ "price": 1.80,
+ "adm": "{\"native\":{\"ver\":\"1.1\",\"link\":{\"url\":\"https://example.com\"},\"assets\":[{\"id\":1,\"title\":{\"text\":\"Ad Title\"}}]}}",
+ "adomain": ["advertiser.com"],
+ "crid": "creative-native-123",
+ "mtype": 4
+ },
+ "type": "native"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/proxistore/proxistoretest/exemplary/simple-video.json b/adapters/proxistore/proxistoretest/exemplary/simple-video.json
new file mode 100644
index 00000000000..192eb60295f
--- /dev/null
+++ b/adapters/proxistore/proxistoretest/exemplary/simple-video.json
@@ -0,0 +1,118 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
+ },
+ "site": {
+ "id": "site-id",
+ "domain": "example.com",
+ "page": "https://example.com/page"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "mimes": ["video/mp4"],
+ "protocols": [1, 2],
+ "w": 640,
+ "h": 480,
+ "minduration": 5,
+ "maxduration": 30
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://api.proxistore.com/v3/rtb/openrtb",
+ "body": {
+ "id": "test-request-id",
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
+ },
+ "site": {
+ "id": "site-id",
+ "domain": "example.com",
+ "page": "https://example.com/page"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "mimes": ["video/mp4"],
+ "protocols": [1, 2],
+ "w": 640,
+ "h": 480,
+ "minduration": 5,
+ "maxduration": 30
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": ["test-imp-id"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "test-bid-id",
+ "impid": "test-imp-id",
+ "price": 2.50,
+ "adm": "",
+ "adomain": ["advertiser.com"],
+ "crid": "creative-video-123",
+ "w": 640,
+ "h": 480,
+ "mtype": 2
+ }
+ ],
+ "seat": "proxistore"
+ }
+ ],
+ "cur": "EUR"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "EUR",
+ "bids": [
+ {
+ "bid": {
+ "id": "test-bid-id",
+ "impid": "test-imp-id",
+ "price": 2.50,
+ "adm": "",
+ "adomain": ["advertiser.com"],
+ "crid": "creative-video-123",
+ "w": 640,
+ "h": 480,
+ "mtype": 2
+ },
+ "type": "video"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/proxistore/proxistoretest/supplemental/bad-response.json b/adapters/proxistore/proxistoretest/supplemental/bad-response.json
new file mode 100644
index 00000000000..850d729e252
--- /dev/null
+++ b/adapters/proxistore/proxistoretest/supplemental/bad-response.json
@@ -0,0 +1,61 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "domain": "example.com"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://api.proxistore.com/v3/rtb/openrtb",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "domain": "example.com"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": ["test-imp-id"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": "not valid json"
+ }
+ }
+ ],
+ "expectedBidResponses": [],
+ "expectedMakeBidsErrors": [
+ {
+ "value": "expect { or n, but found \"",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/proxistore/proxistoretest/supplemental/missing-params.json b/adapters/proxistore/proxistoretest/supplemental/missing-params.json
new file mode 100644
index 00000000000..c03b071ad7b
--- /dev/null
+++ b/adapters/proxistore/proxistoretest/supplemental/missing-params.json
@@ -0,0 +1,27 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "domain": "example.com"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com"
+ }
+ }
+ }
+ ]
+ },
+ "expectedMakeRequestsErrors": [
+ {
+ "value": "imp[test-imp-id]: website and language are required bidder params",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/proxistore/proxistoretest/supplemental/status-204.json b/adapters/proxistore/proxistoretest/supplemental/status-204.json
new file mode 100644
index 00000000000..1375e9c65fa
--- /dev/null
+++ b/adapters/proxistore/proxistoretest/supplemental/status-204.json
@@ -0,0 +1,55 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "domain": "example.com"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://api.proxistore.com/v3/rtb/openrtb",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "domain": "example.com"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": ["test-imp-id"]
+ },
+ "mockResponse": {
+ "status": 204,
+ "body": {}
+ }
+ }
+ ],
+ "expectedBidResponses": []
+}
diff --git a/adapters/proxistore/proxistoretest/supplemental/status-400.json b/adapters/proxistore/proxistoretest/supplemental/status-400.json
new file mode 100644
index 00000000000..7ffd7aa8220
--- /dev/null
+++ b/adapters/proxistore/proxistoretest/supplemental/status-400.json
@@ -0,0 +1,61 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "domain": "example.com"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://api.proxistore.com/v3/rtb/openrtb",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "domain": "example.com"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": ["test-imp-id"]
+ },
+ "mockResponse": {
+ "status": 400,
+ "body": {}
+ }
+ }
+ ],
+ "expectedBidResponses": [],
+ "expectedMakeBidsErrors": [
+ {
+ "value": "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/proxistore/proxistoretest/supplemental/status-500.json b/adapters/proxistore/proxistoretest/supplemental/status-500.json
new file mode 100644
index 00000000000..597cad1caa7
--- /dev/null
+++ b/adapters/proxistore/proxistoretest/supplemental/status-500.json
@@ -0,0 +1,61 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "domain": "example.com"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://api.proxistore.com/v3/rtb/openrtb",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "domain": "example.com"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": ["test-imp-id"]
+ },
+ "mockResponse": {
+ "status": 500,
+ "body": {}
+ }
+ }
+ ],
+ "expectedBidResponses": [],
+ "expectedMakeBidsErrors": [
+ {
+ "value": "Unexpected status code: 500. Run with request.debug = 1 for more info.",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/proxistore/proxistoretest/supplemental/unsupported-mtype.json b/adapters/proxistore/proxistoretest/supplemental/unsupported-mtype.json
new file mode 100644
index 00000000000..5141e24f1de
--- /dev/null
+++ b/adapters/proxistore/proxistoretest/supplemental/unsupported-mtype.json
@@ -0,0 +1,86 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "domain": "example.com"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://api.proxistore.com/v3/rtb/openrtb",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "domain": "example.com"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "website": "example.com",
+ "language": "fr"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": ["test-imp-id"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "test-bid-id",
+ "impid": "test-imp-id",
+ "price": 1.23,
+ "adm": "Ad creative
",
+ "crid": "creative-123",
+ "w": 300,
+ "h": 250,
+ "mtype": 99
+ }
+ ],
+ "seat": "proxistore"
+ }
+ ],
+ "cur": "EUR"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "EUR",
+ "bids": []
+ }
+ ],
+ "expectedMakeBidsErrors": [
+ {
+ "value": "unsupported MType 99 for bid test-bid-id",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 9a2aaa0bd21..1d339f6b05d 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -183,6 +183,7 @@ import (
"github.com/prebid/prebid-server/v3/adapters/pangle"
"github.com/prebid/prebid-server/v3/adapters/pgamssp"
"github.com/prebid/prebid-server/v3/adapters/playdigo"
+ "github.com/prebid/prebid-server/v3/adapters/proxistore"
"github.com/prebid/prebid-server/v3/adapters/pubmatic"
"github.com/prebid/prebid-server/v3/adapters/pubnative"
"github.com/prebid/prebid-server/v3/adapters/pubrise"
@@ -452,6 +453,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
openrtb_ext.BidderPangle: pangle.Builder,
openrtb_ext.BidderPGAMSsp: pgamssp.Builder,
openrtb_ext.BidderPlaydigo: playdigo.Builder,
+ openrtb_ext.BidderProxistore: proxistore.Builder,
openrtb_ext.BidderPubmatic: pubmatic.Builder,
openrtb_ext.BidderPubnative: pubnative.Builder,
openrtb_ext.BidderPubrise: pubrise.Builder,
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index 8cd543c1f16..eff4582a9ba 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -201,6 +201,7 @@ var coreBidderNames []BidderName = []BidderName{
BidderPangle,
BidderPGAMSsp,
BidderPlaydigo,
+ BidderProxistore,
BidderPubmatic,
BidderPubrise,
BidderPubnative,
@@ -574,6 +575,7 @@ const (
BidderPangle BidderName = "pangle"
BidderPGAMSsp BidderName = "pgamssp"
BidderPlaydigo BidderName = "playdigo"
+ BidderProxistore BidderName = "proxistore"
BidderPubmatic BidderName = "pubmatic"
BidderPubrise BidderName = "pubrise"
BidderPubnative BidderName = "pubnative"
diff --git a/openrtb_ext/imp_proxistore.go b/openrtb_ext/imp_proxistore.go
new file mode 100644
index 00000000000..6c6b5bd5f03
--- /dev/null
+++ b/openrtb_ext/imp_proxistore.go
@@ -0,0 +1,6 @@
+package openrtb_ext
+
+type ExtImpProxistore struct {
+ Website string `json:"website"`
+ Language string `json:"language"`
+}
diff --git a/static/bidder-info/proxistore.yaml b/static/bidder-info/proxistore.yaml
new file mode 100644
index 00000000000..186e41ce551
--- /dev/null
+++ b/static/bidder-info/proxistore.yaml
@@ -0,0 +1,28 @@
+endpoint: "https://api.proxistore.com/v3/rtb/openrtb"
+geoscope:
+ - EEA
+maintainer:
+ email: support@proxistore.com
+gvlVendorID: 418
+openrtb:
+ version: 2.6
+capabilities:
+ app:
+ mediaTypes:
+ - banner
+ - video
+ - native
+ - audio
+ site:
+ mediaTypes:
+ - banner
+ - video
+ - native
+ - audio
+userSync:
+ redirect:
+ url: https://abs.proxistore.com/v3/rtb/sync/prebid-server/redirect?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redirect={{.RedirectURL}}
+ userMacro: $UID
+ iframe:
+ url: https://abs.proxistore.com/v3/rtb/sync/prebid-server/iframe?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redirect={{.RedirectURL}}
+ userMacro: $UID
\ No newline at end of file
diff --git a/static/bidder-params/proxistore.json b/static/bidder-params/proxistore.json
new file mode 100644
index 00000000000..d9360a49ced
--- /dev/null
+++ b/static/bidder-params/proxistore.json
@@ -0,0 +1,19 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "title": "Proxistore Adapter Params",
+ "description": "A schema which validates params accepted by the Proxistore adapter",
+ "type": "object",
+ "properties": {
+ "website": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Publisher website identifier"
+ },
+ "language": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Language code (e.g., 'fr', 'en')"
+ }
+ },
+ "required": ["website", "language"]
+}
\ No newline at end of file