cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

c# eBay Utility - going round in circles trying to handle multiple scopes / oAuthTokens etc

Having wasted around 12 hours so far I am dumping my brain here in the hope that someone can provide me that Eureka moment and tell me where I am going wrong. I develop using C# under VS2019, all I am trying to write is a simple utility to pull details from a particular order, items details, addressing etc. For background, I already have a C# application I have used for years that would convert an Order Details page into an MYOB Tax Invoice (MYOB is an accounting platform used in Australia). I would copy the entire HTML Page and my code would nibble through the HTML and extract useful information. Very unstable because eBay of course have to tweak their pages every 5 minutes. The last update has completely changed the layout and my scraping code of course failed. So I finally decided I'd spend a few hours getting under the hood of the RESTful API approach and pull the data out that way. I then discovered there's 20 years of code examples out there, some referring to the 'old SDK' method, some to the New API, and many somewhere inbetween. I have not found a single resource that can actually pin all this into one place and provide simple examples of how to employ the OAuth (New Security) method. 

 

I am NOT a 3rd party, I am only accessing my own ebay account using my own credentials, nothing here requires the porting off to web pages for customers to provide their credentials. 

 

I have added the eBay.Service.dll resource version 3.1131.0 from NuGet (eBay SDK .NET)

 

I have also tried to implement code examples that use the  eBay OAuth Client Library (https://github.com/ebay/ebay-oauth-csharp-client/) also available via Nuget

 

My key issues seems to be that I am unable to obtain a Token that covers more than one Scope. For example, I can get a token like this (AppID, CertID etc are all preset):

 

string endpoint = "https://api.ebay.com/identity/v1/oauth2/token";
string scope = "https://api.ebay.com/oauth/api_scope";
WebClient client = new WebClient();

client.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
string credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{appId}:{certId}"));
client.Headers.Add("Authorization", $"Basic {credentials}");
string body = $"grant_type=client_credentials&scope={scope }";
string response = client.UploadString(endpoint, body);
JObject json = JObject.Parse(response);
_oauthUserToken = (string)json["access_token"];

 

or with the OAuth Client Library I can use something like:

 

CredentialUtil.Load(AppDomain.CurrentDomain.BaseDirectory + @"\ebay-config.yaml");
// Get OAuth user token
var scopes = new List<string> { "https://api.ebay.com/oauth/api_scope" };
OAuth2Api oAuth2Api = new OAuth2Api();
var token = oAuth2Api.GetApplicationToken(OAuthEnvironment.PRODUCTION, scopes);

 

These both work. But if i want to actually get into some Order Details:

 

// Specify the order number for which you want to retrieve the details
string orderNumber = "10-01234-12345";

// Set the eBay fulfillment API endpoint for the getOrder command
string endpoint = $"https://api.ebay.com/sell/fulfillment/v1/order/{orderNumber}";

// Create a web client and set the headers for the API request
WebClient client = new WebClient();
client.Headers.Add("Authorization", $"Bearer {_oauthUserToken }");
client.Headers.Add("Accept", "application/json");
client.Headers.Add("Content-Type", "application/json");

// Make the API request to retrieve the details of the specified order
string response = client.DownloadString(endpoint);
JObject orderDetails = JObject.Parse(response);

 

I get errors relating to invalid scopes. I cannot seem to assign multiple scopes and get a Token that will allow me to do API calls I am enabled for, whether I am using OAuth2Api ...

 

// Define the required scopes for the API request
var scopes = new List<string> { "https://api.ebay.com/oauth/api_scope", "https://api.ebay.com/oauth/api_scope/sell.fulfillment", "https://api.ebay.com/oauth/api_scope/order.read.basic" }; 

// Get an OAuth access token for the defined scopes
OAuth2Api oAuth2Api = new OAuth2Api();
OAuthResponse token = oAuth2Api.GetApplicationToken(OAuthEnvironment.PRODUCTION, scopes);

 

... or passing a space delimited list to the ebay service... 

 

string oauthScope = "https://api.ebay.com/oauth/api_scope https://api.ebay.com/oauth/api_scope/order.read.basic";  

 

All I end up with is errors:

 

Unauthorized - invalid oauthUserToken
Bad Request - invalid order ID
Forbidden

 

Sorry I don't have some rock solid error codes here but I have // out so much code and tried so many combinations I'm now simply giving up and hoping someone has climbed this mountain before me. 

 

Can anyone please provide some working c# code to obtain a suitably OAuth scoped Token and follow this up with the required WebClient code to pull out an order. Shouldn't be this hard o_O

 

 

 

 

 

Message 1 of 9
latest reply
1 BEST ANSWER

Accepted Solutions

Re: c# eBay Utility - going round in circles trying to handle multiple scopes / oAuthTokens etc

from the docs on getOrder:

 

OAuth scope

This request requires an access token created with the authorization code grant flow, using one or more scopes from the following list (please check your Application Keys page for a list of OAuth scopes available to your application):

https://api.ebay.com/oauth/api_scope/sell.fulfillment

https://api.ebay.com/oauth/api_scope/sell.fulfillment.readonly

See OAuth access tokens for more information.

 

 

You are using the client credential (application token) flow rather than the authorization code (user token) flow.

grant_type=client_credentials

 

So there are 2 parts to a user token.

 

Authenticating and getting your first set of tokens, a refresh and an access token. The access token lasts about 2 hours and after that you can request with the refresh token a refresh of the access token, without logging back in.

 

Header generation looks ok.

 

You need to generate an authorization URL to use to log in with a browser, use the following params

 

"client_id": CLIENT_ID_HERE(appId)
"redirect_uri": REDIRECT_URI_HERE
"response_type": "code"
"prompt": "login"
"scope": "your scopes here feel free to use more than one separated by a space"

 

optional field
"state": ""

 

urlencode those params and attach them to https://auth.ebay.com/oauth2/authorize

Load the resulting URL in a browser, login.

Once logged in you will see code=STUFF& in the returned URL bar

copy the value of that code param. (manually, or if you have some creative way of automating that step)

 

then build the body of your request indicating the grant type, only the following fields

"grant_type": "authorization_code"
"redirect_uri": REDIRECT_URI_HERE
"code": authorization_code

 

authorization_code being what you got from the returned browser URL code param above.

submitting that to https://api.ebay.com/identity/v1/oauth2/token

should then get you the user token pair. The access token being the iaf token you use in calls.

 

From then on with that refresh token you can skip the login part and use a refresh token grant type body, use only the following fields

"grant_type": "refresh_token"
"refresh_token": THE_REFRESH_USER_TOKEN_YOU_GOT
"scope": "your scopes separated by spaces"

 

 

 

 

View Best Answer in original post

Message 2 of 9
latest reply
8 REPLIES 8

Re: c# eBay Utility - going round in circles trying to handle multiple scopes / oAuthTokens etc

from the docs on getOrder:

 

OAuth scope

This request requires an access token created with the authorization code grant flow, using one or more scopes from the following list (please check your Application Keys page for a list of OAuth scopes available to your application):

https://api.ebay.com/oauth/api_scope/sell.fulfillment

https://api.ebay.com/oauth/api_scope/sell.fulfillment.readonly

See OAuth access tokens for more information.

 

 

You are using the client credential (application token) flow rather than the authorization code (user token) flow.

grant_type=client_credentials

 

So there are 2 parts to a user token.

 

Authenticating and getting your first set of tokens, a refresh and an access token. The access token lasts about 2 hours and after that you can request with the refresh token a refresh of the access token, without logging back in.

 

Header generation looks ok.

 

You need to generate an authorization URL to use to log in with a browser, use the following params

 

"client_id": CLIENT_ID_HERE(appId)
"redirect_uri": REDIRECT_URI_HERE
"response_type": "code"
"prompt": "login"
"scope": "your scopes here feel free to use more than one separated by a space"

 

optional field
"state": ""

 

urlencode those params and attach them to https://auth.ebay.com/oauth2/authorize

Load the resulting URL in a browser, login.

Once logged in you will see code=STUFF& in the returned URL bar

copy the value of that code param. (manually, or if you have some creative way of automating that step)

 

then build the body of your request indicating the grant type, only the following fields

"grant_type": "authorization_code"
"redirect_uri": REDIRECT_URI_HERE
"code": authorization_code

 

authorization_code being what you got from the returned browser URL code param above.

submitting that to https://api.ebay.com/identity/v1/oauth2/token

should then get you the user token pair. The access token being the iaf token you use in calls.

 

From then on with that refresh token you can skip the login part and use a refresh token grant type body, use only the following fields

"grant_type": "refresh_token"
"refresh_token": THE_REFRESH_USER_TOKEN_YOU_GOT
"scope": "your scopes separated by spaces"

 

 

 

 

Message 2 of 9
latest reply

Re: c# eBay Utility - going round in circles trying to handle multiple scopes / oAuthTokens etc

Re: c# eBay Utility - going round in circles trying to handle multiple scopes / oAuthTokens etc

for clarity since the above portion of my response is possibly confusing:

 

then build the body of your request indicating the grant type, only the following fields

"grant_type": "authorization_code"
"redirect_uri": REDIRECT_URI_HERE
"code": THE_AUTHORIZATION_CODE_YOU_GOT_FROM_RETURNED_URL_PARAM

 

the stuff between "" are literal strings, so grant type is literally the string "authorization_code"

Message 4 of 9
latest reply

Re: c# eBay Utility - going round in circles trying to handle multiple scopes / oAuthTokens etc

Thanks so much for the above, can't study and implement immediately but I will provide feedback when I am next able to sit down and give this a go. Makes sense with the read-through so; very much appreciated. 

Message 5 of 9
latest reply

Re: c# eBay Utility - going round in circles trying to handle multiple scopes / oAuthTokens etc

Hi, just a note to say I am still implementing this, with some assistance from ChatGPT to implement it in my app. Will advise once it's all working

 

Message 6 of 9
latest reply

Re: c# eBay Utility - going round in circles trying to handle multiple scopes / oAuthTokens etc

Thanks to five_notch_trading_post for getting me on the path, took a lot of time but I can now click a button and I get a fully populated c# object mirroring every property of the eBay order. Reading everything that has accumulated over the years saw me downloading both the eBay SDK, several versions of it infact along with the https://github.com/eBay/ebay-oauth-csharp-client libraries which didn't provide me with anything beneficial as there's no documentation and unless you have the fundamental steps right; nothing will work anyway. Because I only needed the simplest of outcomes (order details) once I had the basics of oauth and REST functions, there was no need for the extensive benefits of ebay-oauth-csharp-client which I am sure the author put a lot of energy into. So apart from Json libraries (Newtonsoft.Json.13.0.2\lib\net45\Newtonsoft.Json.dll)  via NuGet, and a fair amount of brain massaging, everything required was already baked into my Visual Studio 2019 installation....

 

 

using System;
using System.Text;
using System.Windows.Forms;
using System.Net;
using Newtonsoft.Json.Linq;
using System.Web;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Threading;
using System.IO;
using System.Collections.Generic;

 

The cornerstone for me was understanding the Authorization code grant flow, needed to access any scopes outside of the public information you can get without logging into your account. The operational flow to achieve this is within my c# application:

 

-  Obtain an Authorisation Code - in theory this should only be required every 547 days

-  Use this Code and the Scopes you need to mint a User Token and a Refresh Token

-  Use User Token programatically to retrieve what you need, it lasts 2 hours 

- Error Trap within this to detect the error 1001 ("Invalid access token")...

// After 7200 seconds (2 hours)
// "errors" : [ {
// "errorId" : 1001,
// "domain" : "OAuth",
// "category" : "REQUEST",
// "message" : "Invalid access token",
// "longMessage" : "Invalid access token. Check the value of the Authorization HTTP request header."
//} ]

Exit function, use my Refresh Token to mint a new User Token, and run the function again. 

The top of my testing code holds the only variables used throughout my app...

/// <summary>
/// Holds last used, resave if renewed (lasts 2 hours)
/// </summary>
private string _oauthUserToken = "";
/// <summary>
/// Holds last used, resave if renewed (lasts 547.5 days)
/// </summary>
private string _oauthRefreshToken = "";
/// <summary>
/// Result of Login Process
/// </summary>
//private string _authorizationCode <- only called for within one function so kept within it;

string appId; // (Client ID)
//string devId was not called for at-all;
string certId; // (Client Secret)
string ruName; // (eBay Redirect URL name) e.g. Cottontech_Pty_-Xxxxxxxx-Xxxxxx-abcdeabcde

string scope = "https://api.ebay.com/oauth/api_scope " +
"https://api.ebay.com/oauth/api_scope/sell.inventory " +
"https://api.ebay.com/oauth/api_scope/sell.marketing " +
"https://api.ebay.com/oauth/api_scope/sell.account " +
"https://api.ebay.com/oauth/api_scope/sell.fulfillment";

 

The two eBay pages I visited most were:

 

1. you follow a consent request https://developer.ebay.com/api-docs/static/oauth-consent-request.html 
2. with an authorization code grant request https://developer.ebay.com/api-docs/static/oauth-auth-code-grant-request.html

3. And refresh the User Token when needed: https://developer.ebay.com/api-docs/static/oauth-auth-code-grant-request.html 

 

I hope this helps any others get a few steps into their own projects a little faster that I did. Happy to help further if some external resourcing is needed for your project. 

 

Finally, here's the contents of my OrderDetails class, this is what is extracted from the requried endpoint:

string apiEndpoint = "https://api.ebay.com/sell/fulfillment/v1/order/" + orderNumber;

 

OrderDetails.cs

 

using System;
using System.Collections.Generic;

namespace eBaySMD
{
public class OrderDetails
{
public string orderId { get; set; }
public string legacyOrderId { get; set; }
public DateTime creationDate { get; set; }
public DateTime lastModifiedDate { get; set; }
public string orderFulfillmentStatus { get; set; }
public string orderPaymentStatus { get; set; }
public string sellerId { get; set; }
public Buyer buyer { get; set; }
public PricingSummary pricingSummary { get; set; }
public CancelStatus cancelStatus { get; set; }
public PaymentSummary paymentSummary { get; set; }
public List<FulfillmentStartInstruction> fulfillmentStartInstructions { get; set; }
public List<string> fulfillmentHrefs { get; set; }
public List<LineItem> lineItems { get; set; }
public string salesRecordReference { get; set; }
public TotalFeeBasisAmount totalFeeBasisAmount { get; set; }
public TotalMarketplaceFee totalMarketplaceFee { get; set; }
}


public class Amount
{
public string value { get; set; }
public string currency { get; set; }
}

public class Buyer
{
public string username { get; set; }
public TaxAddress taxAddress { get; set; }
}

public class CancelStatus
{
public string cancelState { get; set; }
public List<object> cancelRequests { get; set; }
}

public class ContactAddress
{
public string addressLine1 { get; set; }
public string addressLine2 { get; set; }
public string city { get; set; }
public string stateOrProvince { get; set; }
public string postalCode { get; set; }
public string countryCode { get; set; }
}

public class DeliveryCost
{
public string value { get; set; }
public string currency { get; set; }
public ShippingCost shippingCost { get; set; }
}

public class FulfillmentStartInstruction
{
public string fulfillmentInstructionsType { get; set; }
public DateTime minEstimatedDeliveryDate { get; set; }
public DateTime maxEstimatedDeliveryDate { get; set; }
public bool ebaySupportedFulfillment { get; set; }
public ShippingStep shippingStep { get; set; }
}

public class ItemLocation
{
public string location { get; set; }
public string countryCode { get; set; }
public string postalCode { get; set; }
}

public class LineItem
{
public string lineItemId { get; set; }
public string legacyItemId { get; set; }
public string title { get; set; }
public LineItemCost lineItemCost { get; set; }
public int quantity { get; set; }
public string soldFormat { get; set; }
public string listingMarketplaceId { get; set; }
public string purchaseMarketplaceId { get; set; }
public string lineItemFulfillmentStatus { get; set; }
public Total total { get; set; }
public DeliveryCost deliveryCost { get; set; }
public List<object> appliedPromotions { get; set; }
public List<object> taxes { get; set; }
//public Properties properties { get; set; }
public LineItemFulfillmentInstructions lineItemFulfillmentInstructions { get; set; }
public ItemLocation itemLocation { get; set; }
}

public class LineItemCost
{
public string value { get; set; }
public string currency { get; set; }
}

public class LineItemFulfillmentInstructions
{
public DateTime minEstimatedDeliveryDate { get; set; }
public DateTime maxEstimatedDeliveryDate { get; set; }
public DateTime shipByDate { get; set; }
public bool guaranteedDelivery { get; set; }
}

public class Payment
{
public string paymentMethod { get; set; }
public string paymentReferenceId { get; set; }
public DateTime paymentDate { get; set; }
public Amount amount { get; set; }
public string paymentStatus { get; set; }
}

public class PaymentSummary
{
public TotalDueSeller totalDueSeller { get; set; }
public List<object> refunds { get; set; }
public List<Payment> payments { get; set; }
}

public class PriceSubtotal
{
public string value { get; set; }
public string currency { get; set; }
}

public class PricingSummary
{
public PriceSubtotal priceSubtotal { get; set; }
public DeliveryCost deliveryCost { get; set; }
public Total total { get; set; }
}

public class PrimaryPhone
{
public string phoneNumber { get; set; }
}

 


public class ShippingCost
{
public string value { get; set; }
public string currency { get; set; }
}

public class ShippingStep
{
public ShipTo shipTo { get; set; }
public string shippingCarrierCode { get; set; }
public string shippingServiceCode { get; set; }
}

public class ShipTo
{
public string fullName { get; set; }
public ContactAddress contactAddress { get; set; }
public PrimaryPhone primaryPhone { get; set; }
public string email { get; set; }
}

public class TaxAddress
{
public string stateOrProvince { get; set; }
public string postalCode { get; set; }
public string countryCode { get; set; }
}

public class Total
{
public string value { get; set; }
public string currency { get; set; }
}

public class TotalDueSeller
{
public string value { get; set; }
public string currency { get; set; }
}

public class TotalFeeBasisAmount
{
public string value { get; set; }
public string currency { get; set; }
}

public class TotalMarketplaceFee
{
public string value { get; set; }
public string currency { get; set; }
}
}

 

Thanks, 

Stewart Wood - Cottontech 

Message 7 of 9
latest reply

Re: c# eBay Utility - going round in circles trying to handle multiple scopes / oAuthTokens etc

Any chance you would be willing to share the code that covers the token sequence? I (a non dev) and my father (a very seasoned dev) are still struggling to put something together to get this to work...

 

Thanks in advance...

Message 8 of 9
latest reply

Re: c# eBay Utility - going round in circles trying to handle multiple scopes / oAuthTokens etc

Hi, 

I would be very willing to help as best I can. It's been a while since I got past this particular road-block but I have used the functionality almost daily now without a glitch. 

It may be easier to communicate via email (stewart@cottontech.com) as my mode as very bespoke with fun features like variable based usernames and passwords, and a bunch of MYOB (accounting) API control as well for sales invoices and GJ entries. The whole process from sale to invoice and labeling is practically automatic. 

So the code en-masse would be impossible here but drop me a line with your head-ache and I'll see if i can assist. 

 

Cheers : )

Message 9 of 9
latest reply