0

I'm trying to integrate my Company's online store (written in php/Laravel) with Xero such that I can create an invoice for each order. For this I have created a Private Xero application, generated the certificate necessary for Oauth authentication and added it to the Xero App such that I now have my consumer key and secret.

I had a look at the official php wrapper and have found it to be very outdated and unsuitable for integration into my app so, given the amount of Xero interaction is small I thought I'd try and call the API directly.

I am using Guzzle and the Guzzle Oauth subscriber but am struggling to send a properly authenticated request.

Given my Xero app is private the following paragraph should apply to my request (as mentioned in the Xero dev area):

Note, For Private applications, the consumer token and secret are also used as the access token and secret.

So I build up the request like so:

$stack = HandlerStack::create();

$middleware = new Oauth1([
    'consumer_key'    => config('services.xero.key'),
    'consumer_secret' => config('services.xero.secret'),
    'signature_method' => Oauth1::SIGNATURE_METHOD_RSA,
]);

$stack->push($middleware);

$client = new Client([
    'base_uri' => 'https://api.xero.com/api.xro/2.0/',
    'handler'  => $stack,
]);

$res = $client->request('GET', 'Contacts');

dd($res);

However I get the following exception thrown:

[GuzzleHttp\Exception\ClientException]
Client error: GET https://api.xero.com/api.xro/2.0/Contacts resulted in a 401 Unauthorized response:
oauth_problem=consumer_key_unknown&oauth_problem_advice=Consumer%20key%20was%20not%20recognised

As far as I can tell I have properly set up the Xero app and generated the consumer key and secret but I can't seem to debug this.

Any advice on how to make a proper request?

harryg
  • 23,311
  • 45
  • 125
  • 198
  • Your request looks good. Xero doc says "This error will be returned when a consumer key value, does not match the consumer key value of a registered application." Are you sure your consumer key is valid? If yes contact xero support. – clinical Nov 09 '16 at 11:16
  • I'm pretty sure my consumer key and secret are correct. I dump the client object to the console before I send the request and can see that the strings match those that are for my app. I will try Xero support. – harryg Nov 09 '16 at 11:45
  • Do I need to include the certificate file in the call? – harryg Nov 09 '16 at 12:00
  • Check this: https://github.com/XeroAPI/XeroOAuth-PHP – clinical Nov 09 '16 at 12:08
  • 1
    That wrapper is no good for modern PHP app. It's all global functions and general poor design. Doesn't even have proper versioning and it hasn't had a commit in 10 months suggesting a general lack of maintenance. There is another library (https://github.com/calcinai/xero-php) which looks better but given my limited interaction with the API I'd rather not add another dependency to my codebase. – harryg Nov 09 '16 at 13:02
  • I didn't post it for you to reuse. I thought maybe you can find out whether you missed something or not. – clinical Nov 09 '16 at 13:04
  • Oh, OK, well the other library seems to use the cert file as well to authenticate but adding this to my request makes to difference - it still says the consumer key is not recognised... – harryg Nov 09 '16 at 13:07
  • Check the post of the xero community manager maybe it is useful: https://community.xero.com/developer/question/35951 Never used xero so I can't help further sorry. – clinical Nov 09 '16 at 13:17

2 Answers2

0

I had the same issue as you did. This has worked for me:

$middleware = new Oauth1([
   'consumer_key' => config('services.xero.key'),
   'token' => config('services.xero.key'),
   'private_key_file' => config('services.xero.path_to_my_private_key.pem'),
   'private_key_passphrase' => config('services.xero.private_key_passphrase'),
   'signature_method' => Oauth1::SIGNATURE_METHOD_RSA,
]);

$stack = GuzzleHttp\HandlerStack::create();
$stack->push($middleware);

$options = [
    'base_uri' => 'https://api.xero.com/',
    'handler' => $stack
];

$this->apiClient = new GuzzleHttp\Client($options);

In https://developer.xero.com/documentation/auth-and-limits/private-applications it is stated that "The consumer key is also used as the access token. The consumer secret is not used for private apps." that is how I found out this config.

matks
  • 96
  • 7
0

For anyone struggling with this, like I have been for the past few days and the solution from matks wasn't working for me.

After examining the Guzzle Oauth1 documents I noticed that in the $options you need to set an 'auth' option like so

$options = [
        'base_uri' => 'https://api.xero.com/api.xro/2.0/',
        'handler' => $stack,
        'auth' => 'oauth'
    ];

After I added the option all worked well.

So full code would be like this

$middleware = new Oauth1([
       'consumer_key' => config('xero.oauth.consumer_key'),
       'token' => config('xero.oauth.consumer_key'),
       'private_key_file' => storage_path(config('xero.oauth.rsa_private_key')),
       'private_key_passphrase' => config('xero.oauth.rsa_private_key_passphrase'),
       'signature_method' => Oauth1::SIGNATURE_METHOD_RSA,
    ]);

    $stack = HandlerStack::create();
    $stack->push($middleware);

    $options = [
        'base_uri' => 'https://api.xero.com/api.xro/2.0/',
        'handler' => $stack,
        'auth' => 'oauth'
    ];

    $this->client = new Client($options);
ColinMD
  • 952
  • 6
  • 14