選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

320 行
12KB

  1. <?php
  2. /**
  3. * Plugin Name: Xero
  4. * Plugin URI: https://procomhost.com
  5. * Description: Xero Integration for Hair on the move created by Patrick
  6. * Version: 1.0
  7. * Author: Lena, Patrick
  8. * Author URI: http://github.com/
  9. */
  10. namespace Biukop;
  11. require_once(dirname(__FILE__) . '/vendor/autoload.php');
  12. require_once (ABSPATH . 'wp-includes/pluggable.php');
  13. require_once('storage.php');
  14. use Carbon_Fields\Container;
  15. use Carbon_Fields\Field;
  16. class Xero {
  17. public $provider;
  18. public $options = [
  19. 'scope' => ['openid email profile offline_access assets projects accounting.settings accounting.transactions accounting.contacts accounting.journals.read accounting.reports.read accounting.attachments']
  20. ];
  21. public $storage;
  22. public $config;
  23. public $apiInstance;
  24. public $xeroTenantId;
  25. public function __construct() {
  26. add_action( 'after_setup_theme', array($this,'boot_carbon' ));
  27. add_action( 'carbon_fields_register_fields', array($this, 'build_settings_page' ));
  28. add_action( 'carbon_fields_container_activated', array($this, 'field_activated'));
  29. add_action( 'parse_request', array($this, 'xero_callback'));
  30. add_shortcode( 'xero_org_name', array($this, 'xero_org_name'));
  31. add_shortcode( 'xero_org_contacts', array($this, 'xero_org_contacts'));
  32. add_shortcode( 'xero_org_invoices', array($this, 'xero_org_invoices'));
  33. add_action('init', array($this, "xero_init"));
  34. add_action('plugins_loaded', array($this, load_storage));
  35. // $this->xero_init();
  36. $this->storage = new StorageClass();
  37. }
  38. public function xero_init() {
  39. $this->provider = new \League\OAuth2\Client\Provider\GenericProvider([
  40. 'clientId' => '85B4A7B8AF35442CBB9943799B402FE1',
  41. 'clientSecret' => 'bNSYPDdBTGNMVF7kB56iOLhEmqQTlsitwH6cc4kQy83XW3iO',
  42. 'redirectUri' => 'https://procomhost.com/?xero_callback',
  43. 'urlAuthorize' => 'https://login.xero.com/identity/connect/authorize',
  44. 'urlAccessToken' => 'https://identity.xero.com/connect/token',
  45. 'urlResourceOwnerDetails' => 'https://api.xero.com/api.xro/2.0/Organisation'
  46. ]);
  47. }
  48. public function load_storage() {
  49. //$this->storage->read_value();
  50. }
  51. public function boot_carbon() {
  52. \Carbon_Fields\Carbon_Fields::boot();
  53. }
  54. function build_settings_page() {
  55. Container::make( 'theme_options', __( 'Xero Integration' ) )
  56. ->set_page_parent( 'options-general.php' )
  57. ->add_fields( array(
  58. Field::make( 'text', 'xero_oauth2state', 'Xero Oauth2 State' )
  59. ->set_attribute( 'maxLength', 2048)
  60. ->set_attribute( 'readOnly', true)
  61. ->set_default_value($this->storage->getOauth2State()),
  62. Field::make( 'text', 'xero_token', 'Xero Token' )
  63. ->set_attribute( 'maxLength', 2048)
  64. ->set_attribute( 'readOnly', true)
  65. ->set_default_value($this->storage->getSession()['token']),
  66. Field::make( 'text', 'xero_refresh_token', 'RefreshToken' )
  67. ->set_attribute( 'readOnly', true)
  68. ->set_default_value( $this->storage->getSession()['refresh_token'] ),
  69. Field::make( 'text', 'xero_id_token', 'ID Token' )
  70. ->set_attribute( 'readOnly', true)
  71. ->set_default_value( $this->storage->getSession()['id_token'] ),
  72. Field::make( 'text', 'xero_tenant_id', 'Tenant ID' )
  73. ->set_attribute( 'readOnly', true)
  74. ->set_default_value( $this->storage->getSession()['tenant_id'] ),
  75. Field::make( 'text', 'xero_expires', 'Expires' )
  76. ->set_attribute( 'readOnly', true)
  77. ->set_default_value( $this->storage->getSession()['expires'] ),
  78. Field::make( 'text', 'xero_expires_human', 'Expires' )
  79. ->set_attribute( 'readOnly', true)
  80. ->set_default_value( $this->storage->getSession()['expires_human'] ),
  81. Field::make( 'html', 'crb_information_text' )
  82. ->set_html( '<h1>Connect/Reconnect</h2><p>if the above field is empty,
  83. or the expire date looks suspicous, please reconnect to Xero </p>
  84. <form action="/">
  85. <input type="hidden" name="xero_reauth" value="1" />
  86. <input type="submit" class="button button-primary button-large" value="Xero Connect" />
  87. </form>' )
  88. ) );
  89. $this->storage->read_value();
  90. }
  91. function field_activated() {
  92. $this->storage->read_value();
  93. $xeroTenantId = (string)$this->storage->getSession()['tenant_id'];
  94. if ( $xeroTenantId == "" ){
  95. $this->startAuthorization();
  96. }else{
  97. $this->refresh_token();
  98. }
  99. }
  100. public function startAuthorization() {
  101. // This returns the authorizeUrl with necessary parameters applied (e.g. state).
  102. $authorizationUrl = $this->provider->getAuthorizationUrl($this->options);
  103. // Save the state generated for you and store it to the session.
  104. // For security, on callback we compare the saved state with the one returned to ensure they match.
  105. $this->storage->setOauth2State($this->provider->getState());
  106. // Redirect the user to the authorization URL.
  107. header('Location: ' . $authorizationUrl);
  108. exit();
  109. }
  110. function xero_callback() {
  111. if( isset($_GET['xero_reauth']) ) {
  112. $this->startAuthorization();
  113. return;
  114. }
  115. if( ! isset($_GET['xero_callback']) ) {
  116. return ;
  117. }
  118. // If we don't have an authorization code then get one
  119. if (!isset($_GET['code'])) {
  120. echo "Something went wrong, no authorization code found";
  121. exit("Something went wrong, no authorization code found");
  122. // Check given state against previously stored one to mitigate CSRF attack
  123. } elseif (empty($_GET['state']) || ($_GET['state'] !== $this->storage->getOauth2State() )) {
  124. echo "Invalid State";
  125. $this->storage->setOauth2State("");
  126. exit('Invalid state');
  127. } else {
  128. try {
  129. // Try to get an access token using the authorization code grant.
  130. $accessToken = $this->provider->getAccessToken('authorization_code', [
  131. 'code' => $_GET['code']
  132. ]);
  133. $config = \XeroAPI\XeroPHP\Configuration::getDefaultConfiguration()->setAccessToken( (string)$accessToken->getToken() );
  134. $identityInstance = new \XeroAPI\XeroPHP\Api\IdentityApi(
  135. new \GuzzleHttp\Client(),
  136. $config
  137. );
  138. $result = $identityInstance->getConnections();
  139. // Save my tokens, expiration tenant_id
  140. $this->storage->setToken(
  141. $accessToken->getToken(),
  142. $accessToken->getExpires(),
  143. $result[0]->getTenantId(),
  144. $accessToken->getRefreshToken(),
  145. $accessToken->getValues()["id_token"]
  146. );
  147. // related to $this->build_settings_page
  148. $url = "/wp-admin/options-general.php?page=crb_carbon_fields_container_xero_integration.php";
  149. header('Location: ' . $url);
  150. exit();
  151. } catch (\League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) {
  152. echo "Xero Callback failed";
  153. exit();
  154. }
  155. }
  156. }
  157. function refresh_token() {
  158. $this->storage->read_value();
  159. $this->xeroTenantId = (string)$this->storage->getSession()['tenant_id'];
  160. if ($this->storage->getHasExpired()) {
  161. $provider = new \League\OAuth2\Client\Provider\GenericProvider([
  162. 'clientId' => '85B4A7B8AF35442CBB9943799B402FE1',
  163. 'clientSecret' => 'bNSYPDdBTGNMVF7kB56iOLhEmqQTlsitwH6cc4kQy83XW3iO',
  164. 'redirectUri' => 'https://procomhost.com/test/xero-php-oauth2-starter/callback.php',
  165. 'urlAuthorize' => 'https://login.xero.com/identity/connect/authorize',
  166. 'urlAccessToken' => 'https://identity.xero.com/connect/token',
  167. 'urlResourceOwnerDetails' => 'https://api.xero.com/api.xro/2.0/Organisation'
  168. ]);
  169. try {
  170. $newAccessToken = $provider->getAccessToken('refresh_token', [
  171. 'refresh_token' => $this->storage->getRefreshToken()
  172. ]);
  173. }catch (\Exception $e) {
  174. $this->startAuthorization();
  175. return;
  176. }
  177. // Save my token, expiration and refresh token
  178. $this->storage->setToken(
  179. $newAccessToken->getToken(),
  180. $newAccessToken->getExpires(),
  181. $this->xeroTenantId,
  182. $newAccessToken->getRefreshToken(),
  183. $newAccessToken->getValues()["id_token"] );
  184. }
  185. if ( $this->apiInstance == null ){
  186. $this->config = \XeroAPI\XeroPHP\Configuration::getDefaultConfiguration()->setAccessToken( (string)$this->storage->getSession()['token'] );
  187. $this->apiInstance = new \XeroAPI\XeroPHP\Api\AccountingApi(
  188. new \GuzzleHttp\Client(),
  189. $this->config
  190. );
  191. }
  192. }
  193. public function xero_org_name() {
  194. ini_set('display_errors', 'On');
  195. $this->refresh_token();
  196. $apiResponse = $this->apiInstance->getOrganisations($this->xeroTenantId);
  197. $message = 'Organisation Name: <b> ' . $apiResponse->getOrganisations()[0]->getName() . "</b> ";
  198. return $message;
  199. }
  200. public function xero_org_contacts() {
  201. ini_set('display_errors', 'On');
  202. $this->refresh_token();
  203. $apiResponse = $this->apiInstance->getContacts($this->xeroTenantId);
  204. $contacts = $apiResponse->getContacts();
  205. $message = "<table> ";
  206. $message .= "<tr> <td> # </td>
  207. <td> unique-id </td>
  208. <td> name </td>
  209. <td> Supplier </td>
  210. <td> Customer </td>
  211. <td> Group </td>
  212. <td> Status </td> </tr> ";
  213. $count = 1;
  214. foreach ( $contacts as $c) {
  215. $group = "";
  216. foreach ( $c->getContactGroups() as $g) {
  217. $group .= $g->getName() . " - " . $g->getStatus() . "<br>";
  218. }
  219. $message .= "<tr> <td>" . $count ++ . "</td> " .
  220. "<td> " . $c->getContactId() . "</td> " .
  221. "<td> " . $c->getName() . "</td> " .
  222. "<td> " . ( $c->getIsSupplier() ? "yes" : " - " ) . "</td> " .
  223. "<td> " . ( $c->getIsCustomer() ? "yes" : " - " ) . "</td> " .
  224. "<td> " . $group . "</td> " .
  225. "<td> " . $c->getContactStatus() . "</td> " .
  226. "</tr>";
  227. }
  228. $message .= "</table>";
  229. return $message;
  230. }
  231. public function xero_org_invoices() {
  232. ini_set('display_errors', 'On');
  233. $this->refresh_token();
  234. $apiResponse = $this->apiInstance->getInvoices($this->xeroTenantId);
  235. $invoices = $apiResponse->getInvoices();
  236. $message = "<table> ";
  237. $message .= "<tr> <td> # </td>
  238. <td> invoice id</td>
  239. <td> date </td>
  240. <td> contact </td>
  241. <td> amount </td>
  242. <td> Type </td>
  243. <td> Status </td> </tr> ";
  244. $count = 1;
  245. foreach ( $invoices as $c) {
  246. $strDate = "";
  247. $d = $c->getDate();
  248. if ( $d != null) {
  249. $da = $c->getDateAsDate();
  250. $strDate = $da->format("Y-m-d");
  251. } else {
  252. $strDate = $d;
  253. }
  254. $message .= "<tr> <td>" . $count ++ . "</td> " .
  255. "<td> " . $c->getInvoiceNumber() . "</td> " .
  256. "<td> " . $strDate . "</td> " .
  257. "<td> " . $c->getContact()->getName() . "</td> " .
  258. "<td> " . $c->getTotal() . "</td> " .
  259. "<td> " . $c->getType() . "</td> " .
  260. "<td> " . $c->getStatus() . "</td> " .
  261. "</tr>";
  262. }
  263. $message .= "</table>";
  264. return $message;
  265. }
  266. }
  267. $b = new Xero();