3

I am trying to get a Product Review Rating through eBay's Browse API. Here is my code for this function:

sHeaders := TStringList.Create;
sHeaders.Add('X-EBAY-C-ENDUSERCTX=' + '"affiliateCampaignId=' + '533xxxxxxx"');
objIdHTTP.Request.CustomHeaders.AddStdValues(sHeaders);
sResponse := objIdHTTP.Get('https://api.sandbox.ebay.com/buy/browse/v1/item/v1|123456789|0');

But I get a Socket Error 10054 when I test this code.

It might be something wrong with the customer header part, but I can't figure out how to make it work.

Could anyone please help me with any mistakes?

Here is a link to eBay's API document that I am referring to:

Browse API | Use request headers

UPDATE: here is my updated code:

objIdHTTP.Request.CustomHeaders.AddValue('X-EBAY-C-ENDUSERCTX', 'affiliateCampaignId=' + '533xxxxxxx');
sResponse := objIdHTTP.Get('https://api.sandbox.ebay.com/buy/browse/v1/item/v1|123456789|0');

It still has the same error.

I tested the URL link in Postman, and I got an error message as below:

{
  "errors": [
      {
        "errorId": 1002,
        "domain": "OAuth",
        "category": "REQUEST",
        "message": "Missing access token",
        "longMessage": "Access token is missing in the Authorization HTTP request header."
      }
  ]
}

Many IDs exist, such as AppID, DevID, CerID, OAuthToken (9583 characters), Token for Store (3343 characters), EPN Campaign (already included in this URL header).

I tried with X-EBAY-API-IAF-TOKEN: OAuthToken, but it replied with the same error message.

UPDATE:

Thanks for all help in StackOverflow, specailly Remy Lebeau. Finally I got working codes as below:

with objHTTP.Request.CustomHeaders do begin
  Clear;
  FoldLines := False;
  Values['Content-Type'] := 'application/x-www-form-urlencoded';
  Values['Authorization'] := 'Basic ' + 'myAuthCode';
end;
xRequestBody := TStringStream.Create('{"Authorization":"Basic ' + 'myAuthCode' + '"' + ','
                                   + ' "Content-Type:' + 'application/x-www-form-urlencoded' + '"}',
                                     TEncoding.UTF8);

sResponse := objHTTP.Post(sURL, xRequestBody);

I still have no idea why indy HTTP needs to assign ContentType and Authorization twice but these codes work well for me now.

UPDATE: Refer to advice from Remy Lebeau, ContentType is assigned to idHTTP.Request directly and codes are updated again as below:

with objHTTP.Request do begin
  Clear;
  ContentType := 'application/x-www-form-urlencoded';
  CustomHeaders.FoldLines := False;
  CustomHeaders.Values['Authorization'] := 'Basic ' + myAuthCode;
end;
xRequestBody := TStringStream.Create('{"Authorization":"Basic ' + myAuthCode + '"}', TEncoding.UTF8);

sResponse := objHTTP.Post(sURL, xRequestBody);

Let's go back to my start point. Now I start to struggle with GET to have Authorization, code is modified from first part to those (code is updated again in order to capture http request):

objHTTP.Intercept := xIdLogDebug;
with objHTTP.Request.CustomHeaders do begin
  Clear;
  FoldLines := False;
  Values['X-EBAY-C-ENDUSERCTX'] := 'affiliateCampaignId=' + '533xxxxxxx';
  Values['Authorization'] := 'Bearer ' + myToken;
  Values['Postman-Token'] := '65546b71-aef8-422e-916d-93b75ddd9de2,a0687ba7-e142-4849-8288-a8f89b66f253';
  Accept := '*/*';
  UserAgent := 'PostmanRuntime/7.13.0';
  CacheControl := 'no-cache';
  AcceptEncoding := 'gzip, deflate';
  Connection := 'keep-alive';
end;

with objHTTP do begin
  AllowCookies := True;
  CookieManager  := objCookie;
end;

Result := objHTTP.Get('https://api.sandbox.ebay.com/buy/browse/v1/item/v1|123456789|0');

It still gets Socket Error 10054. I tested this URL in Postman and it works fine. The only different I can point out is that there is one TYPE option for Authorization inside Postman, and it is selected as "OAuth 2.0".

So, how to assign authorization to Indy HTTP for GET? Might be something like RequestBody in POST.

UPDATE:

Here is received data by POST call through IndyHTTP:

HTTP/1.1 200 OK
Content-Length: 1905
Date: Sat, 01 Jun 2019 11:09:08 GMT
RlogId: t6ldssk%28ciudbq%60anng%7Fu2h%3F%3Cwk%7Difvqn*14%3F0512%29pqtfwpu%29pdhcaj%7E%29fgg%7E%606%28dlh-16b12ba9d25-0x4fd7e
Set-Cookie: ebay=%5Esbf%3D%23%5E;Domain=.ebay.com;Path=/
Set-Cookie: dp1=bu1p/QEBfX0BAX19AQA**5ed39054^;Domain=.ebay.com;Expires=Mon, 31-May-2021 11:09:08 GMT;Path=/
X-Content-Type-Options: nosniff
X-EBAY-C-VERSION: 1.0.0
X-EBAY-REQUEST-ID: 16b12ba9d1f.a0962ac.25e7e.ff9d75b3!/identity/v1/oauth2/!10.9.98.172!esbnewesbngcos[]!token.clientcredentials!10.9.107.144!r1oauth-envadvcdhidzs5k[IdentityCDSClient[!ClientDetailResourceV1.getClientDetail!10.199.16.86!r1oauthclnt-envjiwv5gtvevlbi[]]]
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Content-Type: application/json
Connection: keep-alive

{"access_token":"v^1.1#i^............","expires_in":7200,"token_type":"Application Access Token"}

This is captured request by GET call (run after POST) from IndyHTTP (converted from TIdBytes to String):

GET /buy/browse/v1/item/v1|123456789|0 HTTP/1.1
Content-Type: application/json
X-EBAY-C-ENDUSERCTX: affiliateCampaignId=533xxxxxxx
Authorization: Bearer v^1.1#i^............
Postman-Token: 65546b71-aef8-422e-916d-93b75ddd9de2,a0687ba7-e142-4849-8288-a8f89b66f253
Host: api.sandbox.ebay.com
Accept: */*
Accept-Encoding: gzip, deflate, identity
User-Agent: PostmanRuntime/7.13.0
Cookie: dp1=bu1p/QEBfX0BAX19AQA**5ed3395b^; ebay=%5Esbf%3D%23%5E

And this is code copied from Postman:

GET /buy/browse/v1/item/v1|123456789|0 HTTP/1.1
Host: api.sandbox.ebay.com
X-EBAY-C-ENDUSERCTX: affiliateCampaignId=533xxxxxxx
Authorization: Bearer v^1.1#i^............
User-Agent: PostmanRuntime/7.13.0
Accept: */*
Cache-Control: no-cache
Postman-Token: 65546b71-aef8-422e-916d-93b75ddd9de2,a0687ba7-e142-4849-8288-a8f89b66f253
Host: api.sandbox.ebay.com
cookie: ebay=%5Esbf%3D%23%5E; dp1=bu1p/QEBfX0BAX19AQA**5ed2398a^bl/AU60b36d0a^; s=; nonsession=CgADKACBmVweKZWYwZThjOGQxNmEwYWMxZTQ2MDNlNGJhZmZkYjQzNTT+86sJ
accept-encoding: gzip, deflate
Connection: keep-alive
cache-control: no-cache

I am struggling which parameter makes IndyHTTP got #10054 error from server.

Thanks.

James
  • 71
  • 1
  • 8
  • Try `objIdHTTP.Request.CustomHeaders.AddValue('X-EBAY-C-ENDUSERCTX', '"affiliateCampaignId=533xxxxxxx"');` (note that the linked API uses a colon as separator, which is the key-value separator for HTTP headers, while your code uses an equal sign) – mjn May 17 '19 at 19:13
  • @mjn `AddStdValues()` takes a `TStrings` in standard `name=value` format and converts the strings into `name: value` header format – Remy Lebeau May 17 '19 at 20:46
  • @James you should not be including `"` chars in your header string, as they are not shown in the documentation. Use `sHeaders.Add('X-EBAY-C-ENDUSERCTX=' + 'affiliateCampaignId=' + '533xxxxxxx')` or `objIdHTTP.Request.CustomHeaders.AddValue('X-EBAY-C-ENDUSERCTX', 'affiliateCampaignId=' + '533xxxxxxx')` or `objIdHTTP.Request.CustomHeaders.Values['X-EBAY-C-ENDUSERCTX'] := 'affiliateCampaignId=' + '533xxxxxxx'` – Remy Lebeau May 17 '19 at 20:54
  • @James 10054 is `WSAECONNRESET`, which means the server is forcibly costing the connection on its end. Really hard to know why it does that, could be for any reason. It may not even be related to this header at all, it could be anything else in the request that the server does not like. Are you able to invoke the browse API through any other apps/browsers without errors? Can you sniff those requests to compare them to Indy's? – Remy Lebeau May 17 '19 at 20:59
  • Thanks for everyone. I will update my code now and have a test. @RemyLebeau I guess my code has mistake inside header, not related IndyHTTP. Currently, it works well for Trading API calls. – James May 18 '19 at 00:56
  • You will likely have to contact eBay to diagnose the problem, let them review the requests you are sending and see what is wrong/missing with them. However, have you read eBay's [Request components](https://developer.ebay.com/api-docs/static/rest-request-components.html) documentation yet? It explains the minimum HTTP headers that are required for REST calls. As for your Postman test, the error message is complaining about a missing OAuth token in the HTTP `Authorization` request header, see [Using OAuth to access eBay APIs](https://developer.ebay.com/api-docs/static/oauth-tokens.html) – Remy Lebeau May 18 '19 at 05:43
  • @RemyLebeau Thanks for your comment. I read the eBay document before I posted my question here. But I didn't sort it out for this issue. I will do some more research in eBay Documents. – James May 18 '19 at 13:58
  • @RemyLebeau After digging in eBay docuemtns, I do finally get Browse API works properly in Postman. Now I have a question about authorization (OAuth 2.0 with "Bear XXXXX") for idHTTP.GET. There is a similar question (https://stackoverflow.com/questions/37316786/delphi-indy-idhttp-authorization) you answered before but I can't fully understand. Could you please show me how to set OAuth 2.0 method and assign token to idHTTP? Thanks. – James May 25 '19 at 14:43
  • @James see [How to add a "Authorization=Bearer" header with Indy in Delphi?](https://stackoverflow.com/questions/38272627/) – Remy Lebeau May 25 '19 at 16:41
  • @RemyLebeau Thank you for all your help and finally I got working codes for POST methond. Now I am going back to GET and still stuck at Authorization parameter. It is not like standard Username/Parssword for indyHTTP.Request.Authentication.Username/Password. I am not sure how to get it activated like I did in POST. – James May 28 '19 at 11:57
  • 1
    @James don't use `Request.CustomHeaders` to set the `Content-Type`, use `Request.ContentType` instead. `application/x-www-form-urlencoded` is the wrong media type to use for JSON, use `application/json` instead. There is no need to duplicate HTTP headers inside of JSON, no server requires that. `'myAuthCode'` is not a valid value for `Basic` authentication. But you need `Bearer` authentication instead. If Postman works, you need to look at its actual HTTP request and compare it to Indy's HTTP request, something is still different between them that you are not accounting for. – Remy Lebeau May 28 '19 at 16:12
  • @RemyLebeau Thank you for your advice and I updated my code inside my question. It works fine with twice assignment for Authorization (one time assignment will get error). About comparing Indy's HTTP and Postman, I tried both Postman (proxy agent) and Wireshark (network traffice), but can't get request from my application. I need to do some more research. Is there a simple way to check it inside Delphi? Thanks. – James May 29 '19 at 12:17
  • 1
    @James you are requesting an HTTPS url, so the request is encrypted. To capture the request before it is encrypted, you can assign one of Indy's own `TIdLog...` components to the `TIdHTTP.Intercept` property – Remy Lebeau May 29 '19 at 15:09
  • @RemyLebeau Thank you for your advice. I will try it asap and leave feedback for detail. – James May 30 '19 at 13:53
  • @RemyLebeau Tried with IdLogDebug and IndyHTTP request is captured sucessfully (shown in my question). I compared it with Postman and major codes are same between two Apps. I don't know if other parameters included in Postman are important for server. In eBay document, they are not mentioned at all. Thanks for your further help. – James May 31 '19 at 11:43
  • 1
    @James the `TIdHTTP` request is missing a `Postman-Token` header. And the fact that Postman's request has a `cookie` header implies that there was a previous request made before the `GET` (probably to start the authentication process), are you making the same request with `TIdHTTP` too? After making sure `TIdHTTP` matches all of Postman's requests, if you are still getting the error, try tweaking the `TIdHTTP.Request.UserAgent` to mimic Postman, or even a web browser, or at least your app's name. A lot of webservers tend to dislike Indy's default `UserAgent`, maybe eBay is one of them. – Remy Lebeau May 31 '19 at 14:51
  • @RemyLebeau Yes, I run Postman for two calls continously to eBay. First one (POST) to get Authorization token and the second one is this GET. I will try to run both calls together through Indy. Also it is good idea to put Postman as agent for Indy. Thanks. – James Jun 01 '19 at 04:05
  • For eBay browse API, it asks for two steps. First one as POST to get Authorization token and second one as GET to do real function like GetItem detail information. I run my code (POST) to get new Token and pass it to Postman to run second call (GET), it works fine in Postman. But for reversed test, it fails. I tested modified codes (GET part) with Postman Token and enabled cookie for IndyHTTP, error is still present. Code is updated above with new HTTP request sent by Indy. – James Jun 01 '19 at 05:19
  • @James the Postman `GET` request has more cookies than the Indy `GET` request has. Don't do the `POST` with Postman and then the `GET` with Indy. Do both requests with Indy using the same `CookieManager` so cookies can carry through from one request to the next. Other than that, the only other thing I see is that you should not be specifying a `Content-Type` on a `GET` request. – Remy Lebeau Jun 01 '19 at 05:56
  • @RemyLebeau Thank you for so quick response. Tested again with three new codes added to GET call (Cache-Control/accept-encoding/Connection) refer to difference request between Indy HTTP and Postman. The captured request from Indy just has one new line for Accept-Encoding. Error #10054 is still there. POST response by Indy HTTP is posted in my question just above GET request. – James Jun 01 '19 at 12:03
  • With your clue mentioned in previous message, I found every time after Postman run POST call, GET call will have a new Cookies value like "s=; nonsession=CgADKACBmWGfDZWYwZThjOGQxNmEwYWMxZTQ2MDNlNGJhZmZkYjQzNTRhkiEd". And this is something missed in Indy HTTP GET call. I checked received cookies by Indy HTTP POST call, there is not similar data like this. By the way, inside GET call, Content-Type value is not assign in my code. It should be assigned by Indy HTTP itself. – James Jun 01 '19 at 12:03

0 Answers0