xeroapi/xero-php-oauth2

Xero官方PHP SDK,基于OpenAPI规范3生成,用于OAuth2

7.3.0 2024-09-16 10:32 UTC

This package is auto-updated.

Last update: 2024-09-17 06:59:31 UTC


README

Latest Stable Version Total Downloads Github forks Github stars License

xero-php-oauth2 SDK使开发者能够轻松地在PHP代码中访问Xero的API,并使用小型商业和总账会计数据构建稳健的应用程序和软件。

目录

API客户端文档

此SDK支持以下Xero API集的完整方法覆盖

drawing

示例应用程序

示例应用程序可以帮助您快速入门,包括简单的身份验证流程和高级使用示例。

Xero账户要求

安装

要在项目中安装此SDK,我们建议使用Composer(对于OSX,我们建议使用Homebrew)。

所有第三方库依赖项均由Composer管理,SDK需要PHP 5.6及更高版本。

要通过Composer安装绑定,并将xero-php-oauth2 SDK添加到您的composer.json中,然后转到您的composer.json文件所在位置并运行以下命令

composer require xeroapi/xero-php-oauth2

如果不存在composer.json文件,请运行以下命令创建一个。您需要安装Composer

composer init

配置PHPStorm

我们收到反馈,PHPStorm IDE默认文件大小太小,无法加载AccountingApi类。

"PHPStorm似乎无法解析XeroAPI\XeroPHP\Api\AccountingApi类。它只显示'AccountingApi'未定义类,因此无法自动完成任何方法等。"

为了解决这个问题,将以下内容添加到idea.properties文件中,以将此限制增加到5000千字节

idea.max.intellisense.filesize=5000

有关在Mac/Windows/Linux上配置PHPStorm平台属性的说明配置PHPStorm平台属性

Laravel

Xero不提供在不同框架中如何使用我们的SDK的支持。我们在开发者社区中收到了Matt @hailwood的建议。他们使用以下包集成了xero-php-oauth2和Laravel。

身份验证

以下是带有授权流的入门代码。您可以通过创建4个独立的PHP文件并安全地替换您的CLIENT_ID, CLIENT_SECRET和REDIRECT_URI来使用以下代码。

所有API请求都通过Xero的OAuth2.0网关进行,并在client上设置有效的access_token,该access_token将JWT附加到每个请求的头部。

如果您是第一次进行API调用,以下代码展示了使用4个独立的PHP文件实现的身份验证流程,并且可以使用以下安全凭据替换您自己的凭据

  • CLIENT_ID
  • CLIENT_SECRET
  • REDIRECT_URI

您还可以在我们的示例应用中查看SDK的使用方法。

重要

您代码中的RedirectURI(例如:http://localhost:8888/pathToApp/callback.php)需要指向callback.php文件,并且与您在创建Xero应用时设置的RedirectURI相匹配。

  1. 将您的浏览器指向authorization.php,您将被重定向到Xero,在那里您将登录并选择一个Xero org进行授权。我们建议选择演示公司 org,因为此代码可以修改您连接的org中的数据。
  2. 完成操作后,您将返回到您的应用,重定向URI应该指向callback.php。
  3. 在callback.php中,您将获得一个访问令牌,我们将在authorizedResource.php中使用此令牌在连接的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;
		}
	}
}
?>

配置

storage.php是一个简单的建议。在您自己的应用中,您应该安全地持久化与已认证的Xero API连接的用户相关的令牌数据。每次您想要调用Xero API时,您都需要访问之前生成的令牌集,在SDK的client上初始化它,并在进行API调用之前刷新access_token

令牌集

自定义连接

自定义连接是Xero的一个高级选项,用于构建与单一组织的M2M集成。自定义连接使用OAuth2.0的client_credentials授权,从而消除了交换临时代码以获取令牌集的步骤。

我们还有一个入门应用,其中包含更多PHP中此身份验证流程的代码示例。

要使用此SDK和自定义连接

  $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());
  }

由于自定义连接仅针对单一组织有效,因此您不需要将xero-tenant-id作为每个方法的第一个参数传递,或者更具体地说,对于此SDK,xeroTenantId可以是一个空字符串。

应用商店订阅

如果您正在实施订阅以参与Xero的应用商店,您需要设置应用商店订阅端点。

当计划成功购买时,用户将被重定向回设置过程中指定的URL。Xero应用商店将订阅ID附加到该URL,这样您就可以通过订阅API立即确定用户订阅了哪个计划。

您可以使用应用凭据通过client_credentials grant_type和marketplace.billing作用域创建客户端。此唯一的access_token将允许您查询appStoreApi中的任何功能。用于查询应用商店端点的客户端凭据令牌仅适用于已完成应用商店入职流程的应用。

// => /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);

您应该使用此订阅数据为您的应用配置用户访问/权限。

应用商店订阅Webhooks

除了通过URL传递订阅ID之外,当发生购买或升级时,您将通过webhook收到通知。然后,您可以使用webhook有效负载中的订阅ID来查询AppStore端点,并确定用户购买了、升级了、降级或取消了哪个计划。

有关设置和接收webhook的更多信息,请参阅Xero的文档。

https://developer.xero.com/documentation/guides/webhooks/overview/

API客户端

您可以通过以下API集访问不同的API集及其可用方法

  • 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;
}
?>

或者,对于资产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;
}
?>

完整的方法文档可以在此浏览:https://xeroapi.github.io/xero-php-oauth2/docs/v2/accounting/index.html

SDK约定

访问HTTP头部信息

每个函数都有一个更详细的WithHttpInfo选项,以防你需要围绕任何请求头部构建逻辑。

例如

  • getInvoices -> getInvoicesWithHttpInfo
  • getContacts -> getContactsWithHttpInfo

这将返回HTTP请求的3个元素数组。

  1. 通过会计对象反序列化的对象
  2. HTTP状态码
  3. 响应头部
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解码和Xero注册

想要实现Xero注册?我们已经在xero-php-oauth2中添加了内置的访问令牌和ID令牌的解码和验证。

Json Web Tokens (JWT)声称为主题断言的信息。

下面的代码显示了如何安全地读取关于访问令牌(用户身份验证)和关于ID令牌(用户的身份和配置文件)的断言。

  // 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());

会计API中访问日期的方法从2.x版本以来已更改

我们的会计和AU工资单API使用Microsoft .NET JSON格式,即"/Date(1439434356790)/"。我们的其他API使用标准的日期格式,即"2020-03-24T18:43:43.860852"。使用具有不同日期格式的OpenAPI规范构建我们的SDK是一项挑战。

因此,我们决定在OpenAPI规范中将日期以MS .NET JSON格式表示为没有日期或日期时间的字符串。这意味着想要使用我们的OpenAPI规范并与代码生成器一起使用的开发者不会在处理MS .NET JSON格式日期时遇到反序列化问题。

副作用是会计和AU工资单模型现在有两个getter方法。例如,getDateOfBirth()返回字符串"/Date(1439434356790)/",而getDateOfBirthAsDate()返回标准的日期"2020-05-14"。由于你可以重写方法,Java中的setDateOfBirth()可以接受一个String或一个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)/");

贡献

PRs、问题和讨论非常受欢迎,并鼓励大家参与。请注意,这个项目的绝大部分是基于Xero的OpenAPI规范生成的代码 - PRs将被评估,并将在预合并之前纳入根生成模板。

版本控制

我们尽最大努力遵守OS行业semver标准,但我们可能会犯错误!如果某个版本号的发布说明中没有准确反映问题,请告知团队。

参与Xero的开发者社区

这个SDK是Xero开发者团队构建和维护的多个SDK之一。我们对社区做出的所有贡献表示感谢。

以下是一些你应该注意的事项,如果你是贡献者

  • Xero已采用贡献者公约行为准则,我们期望我们社区的所有贡献者都遵守它
  • 如果你提出一个问题,请确保填写github问题模板,这样做有助于我们帮助你
  • 你欢迎提出PRs。由于我们的SDK是生成的,我们可能会在核心SDK构建中使用你的代码,而不是合并你的代码
  • 我们为您提供了贡献指南,供您在贡献此SDK时参考。
  • 好奇我们是怎样生成SDK的吗?阅读我们的过程,并查看我们的OpenAPISpec
  • 本软件遵循MIT许可发布。

对于与SDK无关的问题,请参阅我们的开发者支持页面