I’ve recently completed a project that required integration with the online accounting software, Xero.
Xero provide a great PHP SDK, however when I was first developing this there were very few examples available of how to use this SDK. The documentation consisted of nothing beyond a simple definition for each class. I note this is improving now, however I thought I’d detail the way I approached the project and hopefully it will help others in the future.
All of this code assumes you have the Xero PHP OAuth 2 SDK installed in your CakePHP project using Composer. This can be installed in that same way you would install any package using Composer. Much of my code is based on the examples found in the Xero PHP OAuth 2 SDK repo.
You will also need to setup a Xero developer account and create a new app (where you can setup a return URL and generate a client ID and app secret). Instructions for this can be found on the GitHub repo.
Note that the component only performs the functions I needed it to do, so only a very limited number of the possible API calls have been implemented. However, it should be a good starting point for anyone else who needs to extend it, as it covers all the access and authorisation calls in a reusable way.
Download
You can download my example code here:
Included are a couple of files:
XeroComponent.php - This is the main component that does all the communication with Xero.
XerosController.php - This is an example controller that has a few test functions and the initial authorisation calls.
The controller is only supplied as an example, so you’ll need to supply the views and a method of saving persistent data yourself.
Configuration
Before you can use the component, you will need to setup a couple of things. First add your API access details into XeroComponent.php.
public function __construct($props) {
$this->provider = new \League\OAuth2\Client\Provider\GenericProvider([
'clientId' => 'YOUR-CLIENT-ID',
'clientSecret' => 'YOUR-CLIENT-SECRET',
'redirectUri' => 'https://YOUR-DOMAIN-HERE/xeros/callback',
'urlAuthorize' => 'https://login.xero.com/identity/connect/authorize',
'urlAccessToken' => 'https://identity.xero.com/connect/token',
'urlResourceOwnerDetails' => 'https://api.xero.com/api.xro/2.0/Organisation'
]);
}
Most of this should speak for itself, however it’s worth mentioning the redirectUri option. The setting above will be correct if you use the XerosController.php code included. This is the page that is displayed after you first authorise your web app with Xero.
Permanent Storage
You must store the access token that is provided by the Xero API somewhere. This token expires and requires refreshing every 30 minutes, so you MUST NOT simple copy the token you receive on first authorisation and save it somewhere in a config file. You MUST store it where it can be easily updated by the component.
NOTE This component does not include a method of permanently storing your access token. You will need to roll your own
If you take a look at the bottom of XeroComponent.php your will see two functions, readAuth and writeAuth. These are where the access token is saved and loaded from permanent storage.
Personally, I had a generic Settings model in my CakePHP app, which had functions to read and write named preferences to the database. So in my app, the two functions readAuth and writeAuth simply initialised the Settings model and called its read and write functions, as shown below.
/**
* Write token to permanent storage
*/
private function writeAuth($xero) {
// Write using the Settings model
$Settings = TableRegistry::get('Settings');
$Settings->writeSetting('xero_code', json_encode($xero));
}
/**
* Read token from permanent storage
*/
private function readAuth() {
// Read from the Settings model
$Settings = TableRegistry::get('Settings');
$xero = json_decode($Settings->readSetting('xero_code'), true);
return $xero;
}
You will need to either write your own Settings model or replace the code in readAuth and writeAuth to use your preferred method of saving preferences.
Authorising Xero
Before you can access the Xero API, you must first authorise your app. This is done by calling the component’s authorise function.
If you take a look in XerosController.php you will see the following function which starts the process.
public function authorise() {
$this->Xero->authorise();
}
The user’s browser will then be redirected to Xero where they can log in and authorise your app. When this is complete, the user will then be returned to the page you setup for the redirectUri above. If you are using the example controller included, that will be the following function within XerosController.php.
public function callback() {
$result = $this->Xero->callback();
if (isset($result['error'])) {
$this->Flash->error(__('There was a problem authorising Xero. Any error messages can be read below.'));
}else{
$this->Flash->success(__('Xero has been authorised successfully.'));
}
$this->set('result', $result);
}
This function calls the component’s callback function and displays the result to the user.
The callback function within the component will then use the returned details to obtain an access token, which the component then saves to permanent storage.
If all went well, your web app is now authorised to access the Xero API.
What can the component do?
As mentioned, I only needed a very small subset of functionality for my project, so it definitely won’t cover all needs. The following functions are included:
- Retrieve an organisation list
- Retrieve an accounts list
- Retrieve a list of contacts
- Get the details of a specific invoice
- Tell Xero to email a specific invoice to a client
- Create an invoice
The component offers four functions to call the API:
get($type)
getInvoice($invoice_id)
emailInvoice($invoice_id)
createInvoice($contact_id = null, $lines = null, $account_code = null, $status = 'submitted', $dueDays = 28)
The get function can accept three different parameter values: ‘organisation’, ‘accounts’ and ‘contacts’. So in your controller, an example call to retrieve a list of accounts would be:
$return = $this->Xero->get('accounts');
Beyond this, all other functionality within the component is based on obtaining authorisation and access to the Xero API.
How does the component work?
I’ll try to give a quick overview of what happens when you make a call using the component.
You’ll notice that every call that retrieves data from Xero begins by getting an API instance.
// Grab an API instance
$instance = $this->getApiInstance();
When you call this function, several things happen.
Firstly it grabs the tenant ID from the session using the getSession function. This function basically reads our access token from permanent storage using readAuth and returns it.
public function getSession() {
$xero = $this->readAuth();
return $xero['oauth2'];
}
Then the expiry of the access token is checked. If it has expired, a new access token is requested using the refresh_token value of the expired token. This new token is then formatted and saved using the setToken function.
if ($this->getHasExpired()) {
// Get a new access token
$newAccessToken = $this->provider->getAccessToken('refresh_token', [
'refresh_token' => $this->getRefreshToken()
]);
// Save my token, expiration and refresh token
$this->setToken(
$newAccessToken->getToken(),
$newAccessToken->getExpires(),
$xeroTenantId,
$newAccessToken->getRefreshToken(),
$newAccessToken->getValues()["id_token"]
);
}
The token (either the existing one or if expired, the newly obtained one) is then used to obtain a new instance from Xero.
So provided your app is authorised with Xero, calling getApiInstance will always return an API instance you can use. If the access token has expired, a new one will be automatically requested, so you don’t need to worry about token management, the component will handle it for you.
Once you have the API instance, it can be used for any calls you need to make. The component includes a few examples of API calls, but there are many more that could be added. Links to the full API documentation can be found on the Xero PHP OAuth 2 SDK GitHub page.
What’s returned by the component functions?
Each of the included example API calls just returns the raw data that Xero supplied. This can vary in format, so refer to the API documentation for help.
If the API call failed, the component will log the error and return an array of the following format:
[
'error' => true,
'message' => 'error message'
]
The response from any calls you make to the component should be checked for errors in the controller.
I hope this component is useful to someone and saves them some time with their Xero integration.