xeroapi / xero-php-oauth2
Xero official PHP SDK for oAuth2 generated with OpenAPI spec 3
Installs: 2 568 832
Dependents: 11
Suggesters: 0
Security: 0
Stars: 93
Watchers: 24
Forks: 65
Open Issues: 32
Requires
- php: >=8.1
- ext-curl: *
- ext-json: *
- ext-mbstring: *
- firebase/php-jwt: ^6.0
- guzzlehttp/guzzle: ^6.2 | ^7.0
- league/oauth2-client: ^2.5.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.60
- squizlabs/php_codesniffer: ^3.10
- dev-master
- 13.3.0.x-dev
- 7.4.0.x-dev
- 7.4.0-alpha
- 7.3.0
- 7.3.0-alpha
- 7.2.0.x-dev
- 7.2.0
- 7.1.0
- 7.0.1
- 7.0.0
- 6.0.0
- 5.0.0
- 4.0.0
- 3.0.0
- 2.23.3
- 2.23.2
- 2.23.1
- 2.23.0
- 2.22.0
- 2.21.0
- 2.20.0
- 2.19.0
- 2.18.0
- 2.17.0
- 2.16.0
- 2.15.0
- 2.14.1
- 2.14.0
- 2.13.0
- 2.12.1
- 2.12.0
- 2.11.0
- 2.10.0
- 2.9.0
- 2.8.1
- 2.8.0
- 2.7.0
- 2.6.0
- 2.5.4
- 2.5.3
- 2.5.2
- 2.5.1
- 2.5.0
- 2.4.3
- 2.4.2
- 2.4.1
- 2.4.0
- 2.3.0
- 2.3.0-beta1
- 2.2.0
- 2.1.7
- 2.1.6
- 2.1.5
- 2.1.4
- 2.1.3
- 2.1.2
- 2.1.1
- 2.1.0
- 2.0.5
- 2.0.4
- 2.0.3
- 2.0.2
- 2.0.1
- 2.0.0
- 1.6.5
- 1.6.4
- 1.6.3
- 1.6.2
- 1.6.1
- 1.6.0
- 1.5.3
- 1.5.2
- 1.5.1
- 1.5.0
- 1.4.1
- 1.4.0
- 1.3.1
- 1.3.0
- 1.2.2
- 1.2.1
- 1.2.0
- 1.1.4
- 1.1.3
- 1.1.2
- 1.1.1
- 1.1.0
- 1.0.10
- 1.0.9
- 1.0.8
- 1.0.7
- 1.0.6
- 1.0.5
- 1.0.4
- 1.0.3
- 1.0.2
- 1.0.1
- 1.0.0
- 0.2.7
- 0.2.6
- 0.2.5
- 0.2.4
- 0.2.3
- 0.2.2
- 0.2.1
- 0.2.0
- 0.1.5
- 0.1.4
- 0.1.3
- 0.1.2
- 0.1.1
- 0.1.0
- dev-PETOSS-674-Create-Cortex-ID
- dev-PETOSS-656-Enable-enumUnknownDefaultCase-for-supported-SDKs
- dev-petoss-576-slack-alert
- dev-PETOSS-604-Replace-Test-instance-API-Key-with-the-shared-user-API-Key-for-all-the-publish-job
- dev-cab/gha-automation
- dev-OAS-6.3.0-php
- dev-PETOSS-583-Build-script-Issue
- dev-PETOSS-530-Create-GitHub-actions-for-validating-PR-in-PHP-SDK-repo
- dev-OAS-6.2.0-xero-php
- dev-OAS-6.1.2-xero-php
- dev-OAS-6.1.1-xero-php
- dev-Fix-478-Xero-Php-v2
- dev-xero-php-7.0.0-alpha1
- dev-xero-php-7.0.0
- dev-xero-php-6.0.1
- dev-OAS-5.0.0-php-sdk
- dev-xero-php-6.0.0
- dev-xero-php-5.0.0
- dev-xero-php-4.0.0
- dev-xero-php-2.23.2
- dev-xero-php-2.24.0
- dev-xero-php-2.23.0-version
- dev-xero-php2.23.0
- dev-xero-php-2.22.0
- dev-Add-preferred-username
- dev-getDateofBirthAsDate-issue272
- dev-xero-php-2.21.0
- dev-xero-php-2.20.0
- dev-xero-php-2.19.0
- dev-xero-php-2.18.0
- dev-xero-php-2.17.0
- dev-xero-php-2.15.0
- dev-xero-php-2.14.1
- dev-xerophp2.14.0
- dev-xero-php-2.13.0
- dev-xero-lineitems-validation
- dev-xero-php-2.12.1
- dev-xero-php-2.12.0
- dev-xero-php-2.11.0
- dev-xero-php-2.10.0
- dev-lee-development
- dev-xero-php-2.9.0
- dev-app_store
- dev-regen
- dev-custom_connections_launch
- dev-reports_and_budgets_build
- dev-sid-development
- dev-files-api
- dev-php-1.x
This package is auto-updated.
Last update: 2024-12-05 10:32:45 UTC
README
The xero-php-oauth2
SDK makes it easy for developers to access Xero's APIs in their PHP code, and build robust applications and software using small business & general ledger accounting data.
Table of Contents
- API Client documentation
- Sample Applications
- Xero Account Requirements
- Installation
- Authentication
- Configuration
- Custom Connections
- API Clients
- SDK conventions
- Contributing
API Client documentation
This SDK supports full method coverage for the following Xero API sets:
Sample Applications
Sample apps can get you started quickly with simple auth flows and advanced usage examples.
Xero Account Requirements
- Create a free Xero user account
- Login to your Xero developer dashboard and create an API application
- Copy the credentials from your API app and store them using a secure ENV variable strategy
- Decide the neccesary scopes for your app's functionality
Installation
To install this SDK in your project we recommend using Composer (For OSX we recommend using Homebrew).
All third party libraries dependencies managed with Composer and the SDK requires PHP 8.1
and later.
To install the bindings via Composer, and add the xero-php-oauth2 sdk to your composer.json
and navigate to where your composer.json file is and run the command:
composer require xeroapi/xero-php-oauth2
If no composer.json
file exists, create one by running the following command. You'll need Composer installed.
composer init
Configure PHPStorm
We've received feedback that PHPStorm IDE default file size is too small to load the AccountingApi class.
"PHPStorm seems unable to resolve the XeroAPI\XeroPHP\Api\AccountingApi class. It just shows Undefined class 'AccountingApi' and therefore can't autocomplete any of the methods etc."
To fix this, add the following to the idea.properties file to increase this limit to 5000 kilobytes
idea.max.intellisense.filesize=5000
Instructions here for configuring PHPStorm platform properties on Mac/Windows/Linux
Laravel
Xero doesn't offer support on how to use of our SDKs in different frameworks, etc. We had a recommendation by Matt @hailwood in our developer community. They integrates xero-php-oauth2 and Laravel using the following package.
- https://github.com/webfox/laravel-xero-oauth2
- https://packagist.org/packages/webfox/laravel-xero-oauth2
Authentication
Below is starter code with the authorization flow. You can use the code below by creating 4 separate PHP files and securely replacing your CLIENT_ID, CLIENT_SECRET and REDIRECT_URI
All API requests go through Xero's OAuth2.0 gateway and require a valid access_token
to be set on the client
which appends the access_token
JWT to the header of each request.
If you are making an API call for the first time the code below shows the auth flow using 4 separate PHP files and will work with these secure credentials replaced with your own:
- CLIENT_ID
- CLIENT_SECRET
- REDIRECT_URI
You can also see usage of the sdk in our sample app.
Important
The RedirectURI (ex. http://localhost:8888/pathToApp/callback.php) in your code needs to point to the callback.php file and match the RedirectURI you set when creating your Xero app.
- Point your browser to authorization.php, you'll be redirected to Xero where you'll login and select a Xero org to authorize. We recommend the Demo Company org, since this code can modify data in the org you connect to.
- Once complete, you'll be returned to your app to the redirect URI which should point to the callback.php.
- In callback.php, you'll obtain an access token which we'll use in authorizedResource.php to create, read, update and delete information in the connected Xero org.
authorization.php
<?php ini_set('display_errors', 'On'); require __DIR__ . '/vendor/autoload.php'; session_start(); $provider = new \League\OAuth2\Client\Provider\GenericProvider([ 'clientId' => '__CLIENT_ID__', 'clientSecret' => '__CLIENT_SECRET__', 'redirectUri' => '__REDIRECT_URI__', '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' ]); // Scope defines the data your app has permission to access. // Learn more about scopes at https://developer.xero.com/documentation/oauth2/scopes $options = [ 'scope' => ['openid email profile offline_access accounting.settings accounting.transactions accounting.contacts accounting.journals.read accounting.reports.read accounting.attachments'] ]; // This returns the authorizeUrl with necessary parameters applied (e.g. state). $authorizationUrl = $provider->getAuthorizationUrl($options); // Save the state generated for you and store it to the session. // For security, on callback we compare the saved state with the one returned to ensure they match. $_SESSION['oauth2state'] = $provider->getState(); // Redirect the user to the authorization URL. header('Location: ' . $authorizationUrl); exit(); ?>
callback.php
<?php ini_set('display_errors', 'On'); require __DIR__ . '/vendor/autoload.php'; require_once('storage.php'); // Storage Classe uses sessions for storing token > extend to your DB of choice $storage = new StorageClass(); $provider = new \League\OAuth2\Client\Provider\GenericProvider([ 'clientId' => '__CLIENT_ID__', 'clientSecret' => '__CLIENT_SECRET__', 'redirectUri' => '__REDIRECT_URI__', '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' ]); // If we don't have an authorization code then get one if (!isset($_GET['code'])) { echo "Something went wrong, no authorization code found"; exit("Something went wrong, no authorization code found"); // Check given state against previously stored one to mitigate CSRF attack } elseif (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) { echo "Invalid State"; unset($_SESSION['oauth2state']); exit('Invalid state'); } else { try { // Try to get an access token using the authorization code grant. $accessToken = $provider->getAccessToken('authorization_code', [ 'code' => $_GET['code'] ]); $config = XeroAPI\XeroPHP\Configuration::getDefaultConfiguration()->setAccessToken( (string)$accessToken->getToken() ); $identityApi = new XeroAPI\XeroPHP\Api\IdentityApi( new GuzzleHttp\Client(), $config ); $result = $identityApi->getConnections(); // Save my tokens, expiration tenant_id $storage->setToken( $accessToken->getToken(), $accessToken->getExpires(), $result[0]->getTenantId(), $accessToken->getRefreshToken(), $accessToken->getValues()["id_token"] ); header('Location: ' . './authorizedResource.php'); exit(); } catch (\League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) { echo "Callback failed"; exit(); } } ?>
authorizedResource.php
<?php ini_set('display_errors', 'On'); require __DIR__ . '/vendor/autoload.php'; require_once('storage.php'); // Use this class to deserialize error caught use XeroAPI\XeroPHP\AccountingObjectSerializer; // Storage Classe uses sessions for storing token > extend to your DB of choice $storage = new StorageClass(); $xeroTenantId = (string)$storage->getSession()['tenant_id']; if ($storage->getHasExpired()) { $provider = new \League\OAuth2\Client\Provider\GenericProvider([ 'clientId' => '__CLIENT_ID__', 'clientSecret' => '__CLIENT_SECRET__', 'redirectUri' => 'http://localhost:8888/xero-php-oauth2-starter/callback.php', 'urlAuthorize' => 'https://login.xero.com/identity/connect/authorize', 'urlAccessToken' => 'https://identity.xero.com/connect/token', 'urlResourceOwnerDetails' => 'https://identity.xero.com/resources' ]); $newAccessToken = $provider->getAccessToken('refresh_token', [ 'refresh_token' => $storage->getRefreshToken() ]); // Save my token, expiration and refresh token $storage->setToken( $newAccessToken->getToken(), $newAccessToken->getExpires(), $xeroTenantId, $newAccessToken->getRefreshToken(), $newAccessToken->getValues()["id_token"] ); } $config = XeroAPI\XeroPHP\Configuration::getDefaultConfiguration()->setAccessToken( (string)$storage->getSession()['token'] ); $accountingApi = new XeroAPI\XeroPHP\Api\AccountingApi( new GuzzleHttp\Client(), $config ); $assetApi = new XeroAPI\XeroPHP\Api\AssetApi( new GuzzleHttp\Client(), $config ); $identityApi = new XeroAPI\XeroPHP\Api\IdentityApi( new GuzzleHttp\Client(), $config ); $projectApi = new XeroAPI\XeroPHP\Api\ProjectApi( new GuzzleHttp\Client(), $config ); $message = "no API calls"; if (isset($_GET['action'])) { if ($_GET["action"] == 1) { // Get Organisation details $apiResponse = $accountingApi->getOrganisations($xeroTenantId); $message = 'Organisation Name: ' . $apiResponse->getOrganisations()[0]->getName(); } else if ($_GET["action"] == 2) { // Create Contact try { $person = new XeroAPI\XeroPHP\Models\Accounting\ContactPerson; $person->setFirstName("John") ->setLastName("Smith") ->setEmailAddress("john.smith@24locks.com") ->setIncludeInEmails(true); $arr_persons = []; array_push($arr_persons, $person); $contact = new XeroAPI\XeroPHP\Models\Accounting\Contact; $contact->setName('FooBar') ->setFirstName("Foo") ->setLastName("Bar") ->setEmailAddress("ben.bowden@24locks.com") ->setContactPersons($arr_persons); $arr_contacts = []; array_push($arr_contacts, $contact); $contacts = new XeroAPI\XeroPHP\Models\Accounting\Contacts; $contacts->setContacts($arr_contacts); $apiResponse = $accountingApi->createContacts($xeroTenantId,$contacts); $message = 'New Contact Name: ' . $apiResponse->getContacts()[0]->getName(); } catch (\XeroAPI\XeroPHP\ApiException $e) { $error = AccountingObjectSerializer::deserialize( $e->getResponseBody(), '\XeroAPI\XeroPHP\Models\Accounting\Error', [] ); $message = "ApiException - " . $error->getElements()[0]["validation_errors"][0]["message"]; } } else if ($_GET["action"] == 3) { $if_modified_since = new \DateTime("2019-01-02T19:20:30+01:00"); // \DateTime | Only records created or modified since this timestamp will be returned $if_modified_since = null; $where = 'Type=="ACCREC"'; // string $where = null; $order = null; // string $ids = null; // string[] | Filter by a comma-separated list of Invoice Ids. $invoice_numbers = null; // string[] | Filter by a comma-separated list of Invoice Numbers. $contact_ids = null; // string[] | Filter by a comma-separated list of ContactIDs. $statuses = array("DRAFT", "SUBMITTED");; $page = 1; // int | e.g. page=1 – Up to 100 invoices will be returned in a single API call with line items $include_archived = null; // bool | e.g. includeArchived=true - Contacts with a status of ARCHIVED will be included $created_by_my_app = null; // bool | When set to true you'll only retrieve Invoices created by your app $unitdp = null; // int | e.g. unitdp=4 – You can opt in to use four decimal places for unit amounts try { $apiResponse = $accountingApi->getInvoices($xeroTenantId, $if_modified_since, $where, $order, $ids, $invoice_numbers, $contact_ids, $statuses, $page, $include_archived, $created_by_my_app, $unitdp); if ( count($apiResponse->getInvoices()) > 0 ) { $message = 'Total invoices found: ' . count($apiResponse->getInvoices()); } else { $message = "No invoices found matching filter criteria"; } } catch (Exception $e) { echo 'Exception when calling AccountingApi->getInvoices: ', $e->getMessage(), PHP_EOL; } } else if ($_GET["action"] == 4) { // Create Multiple Contacts try { $contact = new XeroAPI\XeroPHP\Models\Accounting\Contact; $contact->setName('George Jetson') ->setFirstName("George") ->setLastName("Jetson") ->setEmailAddress("george.jetson@aol.com"); // Add the same contact twice - the first one will succeed, but the // second contact will throw a validation error which we'll catch. $arr_contacts = []; array_push($arr_contacts, $contact); array_push($arr_contacts, $contact); $contacts = new XeroAPI\XeroPHP\Models\Accounting\Contacts; $contacts->setContacts($arr_contacts); $apiResponse = $accountingApi->createContacts($xeroTenantId,$contacts,false); $message = 'First contacts created: ' . $apiResponse->getContacts()[0]->getName(); if ($apiResponse->getContacts()[1]->getHasValidationErrors()) { $message = $message . '<br> Second contact validation error : ' . $apiResponse->getContacts()[1]->getValidationErrors()[0]["message"]; } } catch (\XeroAPI\XeroPHP\ApiException $e) { $error = AccountingObjectSerializer::deserialize( $e->getResponseBody(), '\XeroAPI\XeroPHP\Models\Accounting\Error', [] ); $message = "ApiException - " . $error->getElements()[0]["validation_errors"][0]["message"]; } } else if ($_GET["action"] == 5) { // DELETE the org FIRST Connection returned $connections = $identityApi->getConnections(); $id = $connections[0]->getId(); $result = $identityApi->deleteConnection($id); } } ?> <html> <body> <ul> <li><a href="authorizedResource.php?action=1">Get Organisation Name</a></li> <li><a href="authorizedResource.php?action=2">Create one Contact</a></li> <li><a href="authorizedResource.php?action=3">Get Invoice with Filters</a></li> <li><a href="authorizedResource.php?action=4">Create multiple contacts and summarizeErrors</a></li> <li><a href="authorizedResource.php?action=5">Delete an organisation connection</a></li> </ul> <div> <?php echo($message ); ?> </div> </body> </html>
storage.php
<?php class StorageClass { function __construct() { if( !isset($_SESSION) ){ $this->init_session(); } } public function init_session(){ session_start(); } public function getSession() { return $_SESSION['oauth2']; } public function startSession($token, $secret, $expires = null) { session_start(); } public function setToken($token, $expires = null, $tenantId, $refreshToken, $idToken) { $_SESSION['oauth2'] = [ 'token' => $token, 'expires' => $expires, 'tenant_id' => $tenantId, 'refresh_token' => $refreshToken, 'id_token' => $idToken ]; } public function getToken() { //If it doesn't exist or is expired, return null if (empty($this->getSession()) || ($_SESSION['oauth2']['expires'] !== null && $_SESSION['oauth2']['expires'] <= time()) ) { return null; } return $this->getSession(); } public function getAccessToken() { return $_SESSION['oauth2']['token']; } public function getRefreshToken() { return $_SESSION['oauth2']['refresh_token']; } public function getExpires() { return $_SESSION['oauth2']['expires']; } public function getXeroTenantId() { return $_SESSION['oauth2']['tenant_id']; } public function getIdToken() { return $_SESSION['oauth2']['id_token']; } public function getHasExpired() { if (!empty($this->getSession())) { if(time() > $this->getExpires()) { return true; } else { return false; } } else { return true; } } } ?>
Configuration
The storage.php
is a simple recommendation. In your own app you should be securely persisting the token set data in relation to the user who has authenticated the Xero API connection. Each time you want to call the Xero API you will need to access the previously generated token set, initialize it on the SDK client
, and refresh the access_token
prior to making API calls.
Token Set
Custom Connections
Custom Connections are a Xero premium option used for building M2M integrations to a single organisation. A custom connection uses OAuth2.0's client_credentis
grant which eliminates the step of exchanging the temporary code for a token set.
We also have a starter application for more code samples of this auth flow in PHP.
To use this SDK with a Custom Connections:
$provider = new \League\OAuth2\Client\Provider\GenericProvider([ 'clientId' => '__CLIENT_ID__', 'clientSecret' => '__CLIENT_SECRET__', 'redirectUri' => '__REDIRECT_URI__ ', 'urlAuthorize' => 'https://login.xero.com/identity/connect/authorize', 'urlAccessToken' => 'https://identity.xero.com/connect/token', 'urlResourceOwnerDetails' => 'https://identity.xero.com/resources' ]); try { // Try to get an access token using the client credentials grant. $accessToken = $provider->getAccessToken('client_credentials'); echo($accessToken->getToken()); } catch (\League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) { // Failed to get the access token exit($e->getMessage()); }
Because Custom Connections are only valid for a single organisation you don't need to pass the xero-tenant-id
as the first parameter to every method, or more specifically for this SDK xeroTenantId
can be an empty string.
App Store Subscriptions
If you are implementing subscriptions to participate in Xero's App Store you will need to setup App Store subscriptions endpoints.
When a plan is successfully purchased, the user is redirected back to the URL specified in the setup process. The Xero App Store appends the subscription Id to this URL so you can immediately determine what plan the user has subscribed to through the subscriptions API.
With your app credentials you can create a client via client_credentials
grant_type with the marketplace.billing
scope. This unique access_token will allow you to query any functions in appStoreApi
. Client Credentials tokens to query app store endpoints will only work for apps that have completed the App Store on-boarding process.
// => /post-purchase-url?subscriptionId=03bc74f2-1237-4477-b782-2dfb1a6d8b21 $provider = new \League\OAuth2\Client\Provider\GenericProvider([ 'clientId' => '__CLIENT_ID__', 'clientSecret' => '__CLIENT_SECRET__', 'urlAuthorize' => 'https://login.xero.com/identity/connect/authorize', 'urlAccessToken' => 'https://identity.xero.com/connect/token', 'urlResourceOwnerDetails' => 'https://identity.xero.com/resources' ]); $apiInstance = new XeroAPI\XeroPHP\Api\AppStoreApi( new GuzzleHttp\Client(), $config ); $accessToken = $provider->getAccessToken('client_credentials'); $apiResponse = $apiInstance->getSubscription($subscriptionId); echo($apiResponse);
You should use this subscription data to provision user access/permissions to your application.
App Store Subscription Webhooks
In additon to a subscription Id being passed through the URL, when a purchase or an upgrade takes place you will be notified via a webhook. You can then use the subscription Id in the webhook payload to query the AppStore endpoints and determine what plan the user purchased, upgraded, downgraded or cancelled.
Refer to Xero's documenation to learn more about setting up and receiving webhooks.
https://developer.xero.com/documentation/guides/webhooks/overview/
API Clients
You can access the different API sets and their available methods through the following API sets:
- AccountingApi
- AssetApi
- ProjectApi
- FilesApi
- PayrollAuApi
- PayrollNzApi
- PayrollUkApi
- AppStoreApi
<?php require_once(__DIR__ . '/vendor/autoload.php'); // Configure OAuth2 access token for authorization: OAuth2 $config = XeroAPI\XeroPHP\Configuration::getDefaultConfiguration()->setAccessToken( 'YOUR_ACCESS_TOKEN' ); $apiInstance = new XeroAPI\XeroPHP\Api\AccountingApi( new GuzzleHttp\Client(), $config ); $xeroTenantId = "YOUR_XERO_TENANT_ID"; $account = new XeroAPI\XeroPHP\Models\Accounting\Account; $account->setCode('123456'); $account->setName('FooBar'); $account->setType(XeroAPI\XeroPHP\Models\Accounting\AccountType::EXPENSE); $account->setDescription('Hello World'); try { $result = $apiInstance->createAccount($xeroTenantId, $account); } catch (Exception $e) { echo 'Exception when calling AccountingApi->createAccount: ', $e->getMessage(), PHP_EOL; } ?>
Or for the Assets API:
<?php require_once(__DIR__ . '/vendor/autoload.php'); // Configure OAuth2 access token for authorization: OAuth2 $config = XeroAPI\XeroPHP\Configuration::getDefaultConfiguration()->setAccessToken( 'YOUR_ACCESS_TOKEN' ); $apiInstance = new XeroAPI\XeroPHP\Api\AssetApi( new GuzzleHttp\Client(), $config ); $xeroTenantId = "YOUR_XERO_TENANT_ID"; $status = ; $page = 1; $pageSize = 5; $orderBy = "AssetName"; $sortDirection = "ASC"; $filterBy = "Company Car"; try { $result = $apiInstance->getAssets($xeroTenantId, $status, $page, $pageSize, $orderBy, $sortDirection, $filterBy); } catch (Exception $e) { echo 'Exception when calling AssetApi->getAssets: ', $e->getMessage(), PHP_EOL; } ?>
Full method docs can be browsed here: https://xeroapi.github.io/xero-php-oauth2/docs/v2/accounting/index.html
SDK conventions
Accessing HTTP Headers
Every function has with it a more verbose WithHttpInfo
option in case you need to build logic around any request headers.
For example:
getInvoices
->getInvoicesWithHttpInfo
getContacts
->getContactsWithHttpInfo
This will return an array of 3 elements from the HTTP request.
- The object deserialized via the accounting object
- The HTTP status code
- The Response Headers
return [ AccountingObjectSerializer::deserialize($content, '\XeroAPI\XeroPHP\Models\Accounting\Organisations', []), $response->getStatusCode(), $response->getHeaders() ];
$apiResponse = $apiInstance->getInvoicesWithHttpInfo($xeroTenantId, $if_modified_since, $where, $order, $ids, $invoice_numbers, $contact_ids, $statuses, $page,$include_archived, $created_by_my_app, $unitdp); echo '$apiResponse: ' . json_encode($apiResponse[2]);
$apiResponse: {"Content-Type":["application\/json; charset=utf-8"],"Content-Length":["2116"],"Server":["nginx"],"Xero-Correlation-Id":["9a8fb7f7-e3e6-4f66-a170-88effabe9f4e"],"X-AppMinLimit-Remaining":["9997"],"X-MinLimit-Remaining":["57"],"X-DayLimit-Remaining":["4954"],"Expires":["Fri, 23 Jul 2021 17:32:31 GMT"],"Cache-Control":["max-age=0, no-cache, no-store"],"Pragma":["no-cache"],"Date":["Fri, 23 Jul 2021 17:32:31 GMT"],"Connection":["keep-alive"],"X-Client-TLS-ver":["tls1.3"]}
JWT decoding and Signup with Xero
Looking to implement Signup with Xero? We've added built in decoding and verification for both Access tokens and ID token in xero-php-oauth2.
Json Web Tokens (JWT) claims are pieces of information asserted about a subject.
The code below shows how to securely read claims about the access token (a user authentication) and abut the id token (a user's identity & profile).
// DECODE & VERIFY ACCESS_TOKEN $accessToken = (string)$storage->getSession()['token']; $jwtAccessTokenClaims = new XeroAPI\XeroPHP\JWTClaims(); $jwtAccessTokenClaims->decodeAccessToken($accessToken); echo($jwtAccessTokenClaims->getNbf()); echo($jwtAccessTokenClaims->getExp()); echo($jwtAccessTokenClaims->getIss()); echo($jwtAccessTokenClaims->getAudValue()); echo($jwtAccessTokenClaims->getClientId()); echo($jwtAccessTokenClaims->getAuthTime()); echo($jwtAccessTokenClaims->getXeroUserId()); echo($jwtAccessTokenClaims->getGlobalSessionId()); echo($jwtAccessTokenClaims->getJti()); echo($jwtAccessTokenClaims->getAuthenticationEventId()); // scopes are an array therfore we dump not echo them. var_dump($jwtAccessTokenClaims->getScope()); //DECODE & VERIFY ID_TOKEN $IdToken = (string)$storage->getSession()['id_token']; $jwtIdTokenClaims = new XeroAPI\XeroPHP\JWTClaims(); $jwtIdTokenClaims->decodeIdToken($IdToken); // 13 Claims are available echo($jwtIdTokenClaims->getNbf()); echo($jwtIdTokenClaims->getExp()); echo($jwtIdTokenClaims->getIss()); echo($jwtIdTokenClaims->getAudValue()); echo($jwtIdTokenClaims->getIat()); echo($jwtIdTokenClaims->getAtHash()); echo($jwtIdTokenClaims->getSid()); echo($jwtIdTokenClaims->getSub()); echo($jwtIdTokenClaims->getAuthTime()); echo($jwtIdTokenClaims->getPreferredUsername()); echo($jwtIdTokenClaims->getEmail()); echo($jwtIdTokenClaims->getGivenName()); echo($jwtIdTokenClaims->getFamilyName());
Methods to access Dates in Accounting have changed since version 2.x
Both our Accounting and AU Payroll APIs use Microsoft .NET JSON format i.e. "/Date(1439434356790)/". Our other APIs use standard date formatting i.e. "2020-03-24T18:43:43.860852". Building our SDKs from OpenAPI specs with such different date formats has been challenging.
For this reason, we've decided dates in MS .NET JSON format will be strings with NO date or date-time format in our OpenAPI specs. This means developers wanting to use our OpenAPI specs with code generators won't run into deserialization issues trying to handle MS .NET JSON format dates.
The side effect is accounting and AU payroll models now have two getter methods. For example, getDateOfBirth() returns the string "/Date(1439434356790)/" while getDateOfBirthAsDate() return a standard date "2020-05-14". Since you can override methods in Java setDateOfBirth() can accept a String or a LocalDate.
//Get account by id $result = $apiInstance->getAccount($xeroTenantId,$accountId); // display formatted date echo($result->getAccounts()[0]->getUpdatedDateUtcAsDate()->format('Y-m-d H:i:s') ): // display string in MS .NET JSON format \/Date(1439434356790)\/ echo($result->getAccounts()[0]->getUpdatedDateUtc() ): //When setting a date for accounting or AU Payroll, remember to use the correct method // For example setStartDate has a 2nd method with "AsDate" if you wish to pass a native date // This converts the date object to MS DateFormat $leaveapplication->setStartDateAsDate(new DateTime('2020-05-02')); // You'll get an error from the AU Payroll API if you try setStartDate("2020-05-02") // But if you want to pass in MS Dateformat, this string will work. $leaveapplication->setStartDate("/Date(1547164800000+0000)/");
Contributing
PRs, issues, and discussion are highly appreciated and encouraged. Note that the majority of this project is generated code based on Xero's OpenAPI specs - PR's will be evaluated and pre-merge will be incorporated into the root generation templates.
Versioning
We do our best to keep OS industry semver
standards, but we can make mistakes! If something is not accurately reflected in a version's release notes please let the team know.
Participating in Xero’s developer community
This SDK is one of a number of SDK’s that the Xero Developer team builds and maintains. We are grateful for all the contributions that the community makes.
Here are a few things you should be aware of as a contributor:
- Xero has adopted the Contributor Covenant Code of Conduct, we expect all contributors in our community to adhere to it
- If you raise an issue then please make sure to fill out the github issue template, doing so helps us help you
- You’re welcome to raise PRs. As our SDKs are generated we may use your code in the core SDK build instead of merging your code
- We have a contribution guide for you to follow when contributing to this SDK
- Curious about how we generate our SDK’s? Have a read of our process and have a look at our OpenAPISpec
- This software is published under the MIT License
For questions that aren’t related to SDKs please refer to our developer support page.