xero-node 13.4.0 → 14.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/CODE_OF_CONDUCT.md +76 -76
  2. package/CONTRIBUTING.md +112 -112
  3. package/LICENSE +21 -21
  4. package/README.md +468 -468
  5. package/dist/gen/api/accountingApi.d.ts +1 -1
  6. package/dist/gen/api/accountingApi.js +2 -2
  7. package/dist/gen/api/appStoreApi.d.ts +1 -1
  8. package/dist/gen/api/appStoreApi.js +2 -2
  9. package/dist/gen/api/assetApi.d.ts +1 -1
  10. package/dist/gen/api/assetApi.js +2 -2
  11. package/dist/gen/api/bankfeedsApi.d.ts +1 -1
  12. package/dist/gen/api/bankfeedsApi.js +2 -2
  13. package/dist/gen/api/filesApi.d.ts +1 -1
  14. package/dist/gen/api/filesApi.js +2 -2
  15. package/dist/gen/api/financeApi.d.ts +1 -62
  16. package/dist/gen/api/financeApi.js +2 -286
  17. package/dist/gen/api/financeApi.js.map +1 -1
  18. package/dist/gen/api/payrollAUApi.d.ts +1 -1
  19. package/dist/gen/api/payrollAUApi.js +2 -2
  20. package/dist/gen/api/payrollAUV2Api.d.ts +178 -0
  21. package/dist/gen/api/payrollAUV2Api.js +776 -0
  22. package/dist/gen/api/payrollAUV2Api.js.map +1 -0
  23. package/dist/gen/api/payrollNZApi.d.ts +1 -1
  24. package/dist/gen/api/payrollNZApi.js +2 -2
  25. package/dist/gen/api/payrollUKApi.d.ts +1 -1
  26. package/dist/gen/api/payrollUKApi.js +2 -2
  27. package/dist/gen/api/projectApi.d.ts +1 -1
  28. package/dist/gen/api/projectApi.js +2 -2
  29. package/dist/gen/model/finance/models.d.ts +0 -10
  30. package/dist/gen/model/finance/models.js +0 -30
  31. package/dist/gen/model/finance/models.js.map +1 -1
  32. package/dist/gen/model/payroll-au/earningsRate.d.ts +4 -0
  33. package/dist/gen/model/payroll-au/earningsRate.js +5 -0
  34. package/dist/gen/model/payroll-au/earningsRate.js.map +1 -1
  35. package/dist/gen/model/payroll-au/employee.d.ts +0 -2
  36. package/dist/gen/model/payroll-au/employee.js +0 -5
  37. package/dist/gen/model/payroll-au/employee.js.map +1 -1
  38. package/dist/gen/model/payroll-au/leaveLine.d.ts +4 -0
  39. package/dist/gen/model/payroll-au/leaveLine.js +5 -0
  40. package/dist/gen/model/payroll-au/leaveLine.js.map +1 -1
  41. package/dist/gen/model/payroll-au/leaveType.d.ts +4 -0
  42. package/dist/gen/model/payroll-au/leaveType.js +5 -0
  43. package/dist/gen/model/payroll-au/leaveType.js.map +1 -1
  44. package/dist/gen/model/payroll-au/models.d.ts +1 -0
  45. package/dist/gen/model/payroll-au/models.js +3 -0
  46. package/dist/gen/model/payroll-au/models.js.map +1 -1
  47. package/dist/gen/model/{finance/userActivitiesResponse.d.ts → payroll-au/openingBalanceLeaveLine.d.ts} +5 -7
  48. package/dist/gen/model/payroll-au/openingBalanceLeaveLine.js +23 -0
  49. package/dist/gen/model/payroll-au/openingBalanceLeaveLine.js.map +1 -0
  50. package/dist/gen/model/payroll-au/openingBalances.d.ts +2 -2
  51. package/dist/gen/model/payroll-au/openingBalances.js +1 -1
  52. package/dist/gen/model/payroll-au/openingBalances.js.map +1 -1
  53. package/dist/gen/model/payroll-au/taxDeclaration.d.ts +4 -0
  54. package/dist/gen/model/payroll-au/taxDeclaration.js +5 -0
  55. package/dist/gen/model/payroll-au/taxDeclaration.js.map +1 -1
  56. package/dist/gen/model/{finance/lockHistoryModel.d.ts → payroll-au-v2/invalidField.d.ts} +5 -9
  57. package/dist/gen/model/payroll-au-v2/invalidField.js +23 -0
  58. package/dist/gen/model/payroll-au-v2/invalidField.js.map +1 -0
  59. package/dist/gen/model/payroll-au-v2/models.d.ts +42 -0
  60. package/dist/gen/model/payroll-au-v2/models.js +244 -0
  61. package/dist/gen/model/payroll-au-v2/models.js.map +1 -0
  62. package/dist/gen/model/payroll-au-v2/pagination.d.ts +17 -0
  63. package/dist/gen/model/payroll-au-v2/pagination.js +33 -0
  64. package/dist/gen/model/payroll-au-v2/pagination.js.map +1 -0
  65. package/dist/gen/model/payroll-au-v2/problem.d.ts +35 -0
  66. package/dist/gen/model/payroll-au-v2/problem.js +46 -0
  67. package/dist/gen/model/payroll-au-v2/problem.js.map +1 -0
  68. package/dist/gen/model/payroll-au-v2/timesheet.d.ts +54 -0
  69. package/dist/gen/model/payroll-au-v2/timesheet.js +66 -0
  70. package/dist/gen/model/payroll-au-v2/timesheet.js.map +1 -0
  71. package/dist/gen/model/payroll-au-v2/timesheetLine.d.ts +33 -0
  72. package/dist/gen/model/payroll-au-v2/timesheetLine.js +38 -0
  73. package/dist/gen/model/payroll-au-v2/timesheetLine.js.map +1 -0
  74. package/dist/gen/model/payroll-au-v2/timesheetLineObject.d.ts +19 -0
  75. package/dist/gen/model/payroll-au-v2/timesheetLineObject.js +28 -0
  76. package/dist/gen/model/payroll-au-v2/timesheetLineObject.js.map +1 -0
  77. package/dist/gen/model/payroll-au-v2/timesheetObject.d.ts +19 -0
  78. package/dist/gen/model/payroll-au-v2/timesheetObject.js +28 -0
  79. package/dist/gen/model/payroll-au-v2/timesheetObject.js.map +1 -0
  80. package/dist/gen/model/payroll-au-v2/timesheets.d.ts +19 -0
  81. package/dist/gen/model/payroll-au-v2/timesheets.js +28 -0
  82. package/dist/gen/model/payroll-au-v2/timesheets.js.map +1 -0
  83. package/package.json +1 -1
  84. package/dist/gen/model/finance/accountUsage.d.ts +0 -65
  85. package/dist/gen/model/finance/accountUsage.js +0 -78
  86. package/dist/gen/model/finance/accountUsage.js.map +0 -1
  87. package/dist/gen/model/finance/accountUsageResponse.d.ts +0 -27
  88. package/dist/gen/model/finance/accountUsageResponse.js +0 -33
  89. package/dist/gen/model/finance/accountUsageResponse.js.map +0 -1
  90. package/dist/gen/model/finance/historyRecordResponse.d.ts +0 -33
  91. package/dist/gen/model/finance/historyRecordResponse.js +0 -38
  92. package/dist/gen/model/finance/historyRecordResponse.js.map +0 -1
  93. package/dist/gen/model/finance/lockHistoryModel.js +0 -28
  94. package/dist/gen/model/finance/lockHistoryModel.js.map +0 -1
  95. package/dist/gen/model/finance/lockHistoryResponse.d.ts +0 -23
  96. package/dist/gen/model/finance/lockHistoryResponse.js +0 -28
  97. package/dist/gen/model/finance/lockHistoryResponse.js.map +0 -1
  98. package/dist/gen/model/finance/practiceResponse.d.ts +0 -33
  99. package/dist/gen/model/finance/practiceResponse.js +0 -38
  100. package/dist/gen/model/finance/practiceResponse.js.map +0 -1
  101. package/dist/gen/model/finance/reportHistoryModel.d.ts +0 -25
  102. package/dist/gen/model/finance/reportHistoryModel.js +0 -28
  103. package/dist/gen/model/finance/reportHistoryModel.js.map +0 -1
  104. package/dist/gen/model/finance/reportHistoryResponse.d.ts +0 -23
  105. package/dist/gen/model/finance/reportHistoryResponse.js +0 -28
  106. package/dist/gen/model/finance/reportHistoryResponse.js.map +0 -1
  107. package/dist/gen/model/finance/userActivitiesResponse.js +0 -28
  108. package/dist/gen/model/finance/userActivitiesResponse.js.map +0 -1
  109. package/dist/gen/model/finance/userResponse.d.ts +0 -57
  110. package/dist/gen/model/finance/userResponse.js +0 -73
  111. package/dist/gen/model/finance/userResponse.js.map +0 -1
package/README.md CHANGED
@@ -1,468 +1,468 @@
1
- # xero-node
2
- [![npm version](https://badge.fury.io/js/xero-node.svg)](https://badge.fury.io/js/xero-node)
3
- [![Github forks](https://img.shields.io/github/forks/XeroAPI/xero-node.svg)](https://github.com/XeroAPI/xero-node/network)
4
- [![Github stars](https://img.shields.io/github/stars/XeroAPI/xero-node.svg)](https://github.com/XeroAPI/xero-node/stargazers)
5
- ![npm](https://img.shields.io/npm/dt/xero-node)
6
-
7
- The xero-node SDK makes it easy for developers to access Xero's APIs in their JavaScript code, and build robust applications and software using small business & general ledger accounting data.
8
- # Table of Contents
9
- - [API Client documentation](#api-client-documentation)
10
- - [Sample Applications](#sample-applications)
11
- - [Xero Account Requirements](#xero-account-requirements)
12
- - [Installation](#installation)
13
- - [Configuration](#configuration)
14
- - [Authentication](#authentication)
15
- - [Custom Connections](#custom-connections)
16
- - [App Store Subscriptions](#app-store-subscriptions)
17
- - [API Clients](#api-clients)
18
- - [Helper Methods](#helper-methods)
19
- - [Usage Examples](#usage-examples)
20
- - [SDK conventions](#sdk-conventions)
21
- - [Security](#security)
22
- - [Contributing](#contributing)
23
-
24
- <hr>
25
-
26
- ## API Client documentation
27
- This SDK supports full method coverage for the following Xero API sets:
28
-
29
- | API Set | Description |
30
- | --- | --- |
31
- | [`Accounting`](https://xeroapi.github.io/xero-node/accounting/index.html) | The Accounting API exposes accounting functions of the main Xero application *(most commonly used)*
32
- | [Assets](https://xeroapi.github.io/xero-node/assets/index.html) | The Assets API exposes fixed asset related functions of the Xero Accounting application |
33
- | [Bankfeeds](https://xeroapi.github.io/xero-node/bankfeeds/index.html) | The Bankfeeds API facilitates the flow of transaction and statement data |
34
- | [Files](https://xeroapi.github.io/xero-node/files/index.html) | The Files API provides access to the files, folders, and the association of files within a Xero organisation |
35
- | [Projects](https://xeroapi.github.io/xero-node/projects/index.html) | Xero Projects allows businesses to track time and costs on projects/jobs and report on profitability |
36
- | [Payroll (AU)](https://xeroapi.github.io/xero-node/payroll-au/index.html) | The (AU) Payroll API exposes payroll related functions of the payroll Xero application |
37
- | [Payroll (NZ)](https://xeroapi.github.io/xero-node/payroll-nz/index.html) | The (NZ) Payroll API exposes payroll related functions of the payroll Xero application |
38
- | [Payroll (UK)](https://xeroapi.github.io/xero-node/payroll-uk/index.html) | The (UK) Payroll API exposes payroll related functions of the payroll Xero application |
39
-
40
- <img src="https://i.imgur.com/3ISSOwp.png" alt="drawing" width="350"/>
41
-
42
- <hr>
43
-
44
- ## Sample Applications
45
- Sample apps can get you started quickly with simple auth flows and advanced usage examples.
46
-
47
- | Sample App | Description | Screenshot |
48
- | --- | --- | --- |
49
- | [`starter-app`](https://github.com/XeroAPI/xero-node-oauth2-ts-starter) | Basic getting started code samples | <img src="https://i.imgur.com/k208KAv.png" alt="drawing" width="200"/>
50
- | [`full-app`](https://github.com/XeroAPI/xero-node-oauth2-app) | Complete app with more complex examples | <img src="https://i.imgur.com/TaMQvnp.png" alt="drawing" width="500"/>
51
- | [`custom-connections-starter`](https://github.com/XeroAPI/xero-node-custom-connections-starter) | Basic app showing Custom Connections - a Xero [premium option](https://developer.xero.com/documentation/guides/oauth2/custom-connections) for building M2M integrations to a single org | <img src="https://i.imgur.com/HoQHLuq.png" alt="drawing" width="300"/>
52
- | [`xero-node-sso-app`](https://github.com/XeroAPI/xero-node-sso-app) | App showing Xero Single Sign On - as well as basic setup and usage of the Xero App Store `appStoreApi.getSubscription` endpoint | <img src="https://i.imgur.com/4NGowZz.png" alt="drawing" width="300"/>
53
- | [`xero-node-sso-form`](https://github.com/XeroAPI/xero-node-sso-form) | App showing Sign up with Xero to Lead | <img src="https://raw.githubusercontent.com/XeroAPI/xero-node-sso-form/main/public/images/ssu-demo-screenshot.png" alt="drawing" width="300"/>
54
-
55
- <hr>
56
-
57
- ## Xero Account Requirements
58
- - Create a [free Xero user account](https://www.xero.com/us/signup/api/)
59
- - Login to your Xero developer [dashboard](https://developer.xero.com/app/manage) and create an API application
60
- - Copy the credentials from your API app and store them using a secure ENV variable strategy
61
- - Decide the [neccesary scopes](https://developer.xero.com/documentation/oauth2/scopes) for your app's functionality
62
-
63
- # Installation
64
- To install this SDK in your project:
65
- ```
66
- npm install xero-node
67
- ```
68
-
69
- ---
70
- ## Configuration
71
- ```js
72
- import { XeroClient } from 'xero-node';
73
-
74
- const xero = new XeroClient({
75
- clientId: 'YOUR_CLIENT_ID',
76
- clientSecret: 'YOUR_CLIENT_SECRET',
77
- redirectUris: [`http://localhost:${port}/callback`],
78
- scopes: 'openid profile email accounting.settings accounting.transactions offline_access'.split(" "),
79
- state: 'returnPage=my-sweet-dashboard', // custom params (optional)
80
- httpTimeout: 3000, // ms (optional)
81
- clockTolerance: 10 // seconds (optional)
82
- });
83
- ```
84
-
85
- ---
86
- ## Authentication
87
- 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](https://jwt.io/) to the header of each request.
88
-
89
- If you are making an API call for the first time:
90
-
91
- 1. Send the user to the Xero authorization URL
92
- ```js
93
- let consentUrl = await xero.buildConsentUrl();
94
- res.redirect(consentUrl);
95
- ```
96
- 2. The user will authorize your application and be sent to your `redirect_uri`
97
- ```js
98
- process.env.REDIRECT_URI
99
- => /callback?code=xyz123
100
- ```
101
- 3. You exchange the temporary `code` for a valid `token_set`
102
- ```js
103
- const tokenSet = await xero.apiCallback(req.url);
104
- // save the tokenSet
105
- ```
106
-
107
- It is recommended that you store this token set JSON in a datastore 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.
108
-
109
- ### Token Set
110
- | key | value | description |
111
- | --- | --- | --- |
112
- | id_token: | "xxx.yyy.zzz" | [OpenID Connect](https://openid.net/connect/) token returned if `openid profile email` scopes accepted |
113
- | access_token: | "xxx.yyy.zzz" | [Bearer token](https://oauth.net/2/jwt/) with a 30 minute expiration required for all API calls |
114
- | expires_in: | 1800 | Time in seconds till the token expires - 1800s is 30m |
115
- | refresh_token: | "XXXXXXX" | Alphanumeric string used to obtain a new Token Set w/ a fresh access_token - 60 day expiry |
116
- | scope: | ["email", "profile", "openid", "accounting.settings", "accounting.transactions", "offline_access"] | The Xero permissions that are embedded in the `access_token` |
117
-
118
- Example Token Set JSON:
119
- ```
120
- {
121
- "id_token": "xxx.yyy.zz",
122
- "access_token": "xxx.yyy.zzz",
123
- "expires_in": 1800,
124
- "token_type": "Bearer",
125
- "refresh_token": "xxxxxxxxx",
126
- "scope": ["email", "profile", "openid", "accounting.settings", "accounting.transactions", "offline_access"]
127
- }
128
- ```
129
-
130
- ---
131
- ## Custom Connections
132
-
133
- Custom Connections are a Xero [premium option](https://developer.xero.com/documentation/oauth2/custom-connections) used for building M2M integrations to a single organisation. A custom connection uses OAuth2.0's [`client_credentials`](https://www.oauth.com/oauth2-servers/access-tokens/client-credentials/) grant which eliminates the step of exchanging the temporary code for a token set.
134
-
135
- To use this SDK with a `Custom Connection`:
136
- ```js
137
- import { XeroClient } from 'xero-node';
138
-
139
- const xero = new XeroClient({
140
- clientId: 'YOUR_CLIENT_ID',
141
- clientSecret: 'YOUR_CLIENT_SECRET',
142
- grantType: 'client_credentials'
143
- });
144
-
145
- const tokenSet = await xero.getClientCredentialsToken();
146
- // save the tokenSet
147
-
148
- const invoices = await xero.accountingApi.getInvoices('');
149
- ```
150
-
151
- 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.
152
-
153
- > Becuase the SDK is generated from the OpenAPI spec the parameter remains which requires you to pass an empty string to use this SDK with a Custom Connection.
154
-
155
- ---
156
-
157
- ## App Store Subscriptions
158
-
159
- If you are implementing subscriptions to participate in Xero's App Store you will need to setup [App Store subscriptions](https://developer.xero.com/documentation/xero-app-store/app-partner-guides/xero-app-store-subscriptions) endpoints.
160
-
161
- 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.
162
-
163
- 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.
164
-
165
- ```ts
166
- // => /post-purchase-url
167
-
168
- const xeroAppStoreClient = new XeroClient({
169
- clientId: process.env.CLIENT_ID,
170
- clientSecret: process.env.CLIENT_SECRET,
171
- grantType: 'client_credentials',
172
- scopes: ['marketplace.billing']
173
- });
174
-
175
- try {
176
- await xeroAppStoreClient.getClientCredentialsToken()
177
- } catch(e) {
178
- console.log('ERROR: ', e)
179
- }
180
-
181
- const subscriptionRequest = await xeroAppStoreClient.appStoreApi.getSubscription(subscripionId)
182
-
183
- console.log(subscriptionRequest.body)
184
- {
185
- currentPeriodEnd: 2021-09-02T20:08:58.772Z,
186
- endDate: null,
187
- id: '03bc74f2-1237-4477-b782-2dfb1a6d8b21',
188
- organisationId: '79e8b2e5-c63d-4dce-888f-e0f3e9eac647',
189
- plans: [
190
- {
191
- id: '6abc26f3-9390-4194-8b25-ce8b9942fda9',
192
- name: 'Small',
193
- status: 'ACTIVE',
194
- subscriptionItems: [
195
- endDate: null,
196
- id: '834cff4c-b753-4de2-9e7a-3451e14fa17a',
197
- price: {
198
- id: '2310de92-c7c0-4bcb-b972-fb7612177bc7',
199
- amount: 0.1,
200
- currency: 'NZD'
201
- },
202
- product: Product {
203
- id: '9586421f-7325-4493-bac9-d93be06a6a38',
204
- name: '',
205
- type: 'FIXED'
206
- },
207
- startDate: 2021-08-02T20:08:58.772Z,
208
- testMode: true
209
-
210
- ]
211
- }
212
- ],
213
- startDate: 2021-08-02T20:08:58.772Z,
214
- status: 'ACTIVE',
215
- testMode: true
216
- }
217
- ```
218
- You should use the subscription data to provision user access/permissions to your application.
219
- ### App Store Subscription Webhooks
220
-
221
- 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.
222
-
223
- Refer to Xero's documenation to learn more about setting up and receiving webhooks or review [this blogpost](https://devblog.xero.com/keeping-your-integration-in-sync-implementing-xero-webhooks-using-node-express-and-ngrok-6d2976baac6d) explaing webhooks using xero-node sdk.
224
- > https://developer.xero.com/documentation/guides/webhooks/overview/
225
-
226
- ---
227
- ## API Clients
228
- You can access the different API sets and their available methods through the following:
229
-
230
- ```js
231
- const xero = new XeroClient({
232
- clientId: 'YOUR_CLIENT_ID', // required
233
- clientSecret: 'YOUR_CLIENT_SECRET', // required
234
- redirectUris: [`http://localhost:${port}/callback`], // not used for client_credentials auth flow
235
- grantType: 'client_credentials', // only used for client_credentials auth flow
236
- scopes: 'openid profile email accounting.settings accounting.transactions offline_access'.split(" "), // not used for client_credentials auth flow
237
- state: 'returnPage=my-sweet-dashboard', // custom params (optional), not used for client_credentials auth flow
238
- httpTimeout: 3000, // ms (optional)
239
- clockTolerance: 10 // seconds (optional)
240
- });
241
-
242
- xero.accountingApi
243
- xero.assetApi
244
- xero.projectApi
245
- xero.filesApi
246
- xero.payrollAUApi
247
- xero.payrollNZApi
248
- xero.payrollUKApi
249
- ```
250
- ---
251
- ## Helper Methods
252
-
253
- Once you have a valid Token Set in your datastore, the next time you want to call the Xero API simply initialize a new `client` and refresh the token set. There are two ways to refresh a token
254
-
255
- ```js
256
- // you can refresh the token using the fully initialized client leveraging openid-client
257
-
258
- import { XeroClient } from 'xero-node';
259
-
260
- const xero = new XeroClient({
261
- clientId: 'YOUR_CLIENT_ID',
262
- clientSecret: 'YOUR_CLIENT_SECRET',
263
- redirectUris: [`http://localhost:${port}/callback`],
264
- scopes: 'openid profile email accounting.settings accounting.transactions offline_access'.split(" ")
265
- });
266
-
267
- await xero.initialize();
268
-
269
- const tokenSet = getTokenSetFromDatabase(userId); // example function name
270
-
271
- await xero.setTokenSet(tokenSet);
272
-
273
- if(tokenSet.expired()){
274
- const validTokenSet = await xero.refreshToken();
275
- // save the new tokenset
276
- }
277
- ```
278
- ```js
279
- // or if you already generated a tokenSet and have a valid (< 60 days refresh token),
280
- // you can initialize an empty client and refresh by passing the client, secret, and refresh_token
281
-
282
- import { XeroClient } from 'xero-node';
283
-
284
- const tokenSet = getTokenSetFromDatabase(userId); // example function name
285
-
286
- if(tokenSet.expired()){
287
- const xero = new XeroClient();
288
- const validTokenSet = await xero.refreshWithRefreshToken(client_id, client_secret, tokenSet.refresh_token)
289
- // save the new tokenset
290
- }
291
- ```
292
-
293
- A full list of the SDK client's methods:
294
-
295
- | method | description |
296
- | --- | --- |
297
- | client.`initialize` | Initializes the Xero Client with the provided configuration |
298
- | client.`buildConsentUrl` | Returns a url concatenated from the provided redirect uri, scope, and the issuer ( Xero identity authorize url) |
299
- | client.`apiCallback`(callbackUrl) | Leverages openid-client library to exchange temporary auth code for token set |
300
- | client.`disconnect`(connectionId) | Removes an individual tenant connection by connection ID |
301
- | client.`readTokenSet` | Returns token set currently set on the Xero Client |
302
- | client.`setTokenSet`(tokenSet) | Sets a specified token set on the Xero Client |
303
- | client.`refreshToken` | Leverages openid-client library to refresh token set currently set on the Xero Client and returns updated token set |
304
- | client.`revokeToken` | Revokes a users refresh token and removes all their connections to your app |
305
- | client.`formatMsDate`(dateString) | Takes a date string and returns it formatted as an MS Date |
306
- | client.`refreshWithRefreshToken`(clientId, clientSecret, refreshToken) | Refresh a token set without leveraging openid-client |
307
- | client.`getClientCredentialsToken` | Get a token set without user intervention via the client credentials grant type for custom connections only |
308
- | client.`updateTenants`(fullOrgDetails: boolean = true) | GET request to the /connections endpoint. Accepts a boolean to indicate whether or not to also make a GET request to the /organisations endpoint and map full org data to each connection object prior to returning the array of connections |
309
- ---
310
- ## Usage Examples
311
- ### Accounting API
312
- ```js
313
- import { XeroClient, HistoryRecords, Invoice } from 'xero-node';
314
-
315
- const xero = new XeroClient({
316
- clientId: 'YOUR_CLIENT_ID',
317
- clientSecret: 'YOUR_CLIENT_SECRET',
318
- redirectUris: [`http://localhost:${port}/callback`],
319
- scopes: 'openid profile email accounting.settings accounting.transactions offline_access'.split(" ")
320
- });
321
-
322
- await xero.initialize();
323
-
324
- const tokenSet = getTokenSetFromDatabase(userId); // example function name
325
-
326
- await xero.setTokenSet(tokenSet);
327
-
328
- if(tokenSet.expired()){
329
- const validTokenSet = await xero.refreshToken();
330
- // save the new tokenset
331
- }
332
-
333
- await xero.updateTenants();
334
-
335
- const activeTenantId = xero.tenants[0].tenantId;
336
-
337
- // GET all Accounts
338
- const getAccountsResponse = await xero.accountingApi.getAccounts(activeTenantId);
339
-
340
- const accountId = getAccountsResponse.body.accounts[0].accountID
341
-
342
- // GET one Account by ID
343
- const getAccountResponse = await xero.accountingApi.getAccount(activeTenantId, accountId);
344
-
345
- // CREATE an Invoice
346
- const invoices = {
347
- invoices: [
348
- {
349
- type: Invoice.TypeEnum.ACCREC,
350
- contact: {
351
- contactID: contactId
352
- },
353
- lineItems: [
354
- {
355
- description: "Acme Tires",
356
- quantity: 2.0,
357
- unitAmount: 20.0,
358
- accountCode: "500",
359
- taxType: "NONE",
360
- lineAmount: 40.0
361
- }
362
- ],
363
- date: "2019-03-11",
364
- dueDate: "2018-12-10",
365
- reference: "Website Design",
366
- status: Invoice.StatusEnum.AUTHORISED
367
- }
368
- ]
369
- };
370
-
371
- const createdInvoicesResponse = await xero.accountingApi.createInvoices(activeTenantId, invoices)
372
-
373
- const invoiceId = createdInvoicesResponse.body.invoices[0].invoiceID;
374
-
375
- // CREATE a History Record
376
- const historyRecords: HistoryRecords = {
377
- historyRecords: [
378
- {
379
- details: "This is a history record"
380
- }
381
- ]
382
- };
383
-
384
- const createdInvoiceHistoryResponse = await xero.accountingApi.createInvoiceHistory(activeTenantId, invoiceId, historyRecords);
385
-
386
- // CREATE Attachment
387
- const filename = "xero-dev.png";
388
- const pathToUpload = path.resolve(__dirname, "../public/images/xero-dev.png");
389
- const readStream = fs.createReadStream(pathToUpload);
390
- const contentType = mime.lookup(filename);
391
-
392
- const accountAttachmentsResponse = await xero.accountingApi.createInvoiceAttachmentByFileName(activeTenantId, invoiceId, filename, readStream, {
393
- headers: {
394
- 'Content-Type': contentType
395
- }
396
- });
397
- ```
398
-
399
- ---
400
- ## SDK conventions
401
-
402
-
403
- ### Querying & Filtering
404
-
405
-
406
- ```js
407
- const activeTenantId = 'XERO_TENANT_ID';
408
- const ifModifiedSince: Date = new Date("2020-02-06T12:17:43.202-08:00");
409
- const where = 'Status=="AUTHORISED" AND Type=="SPEND"';
410
- const order = 'Reference ASC';
411
- const page = 1;
412
- const unitdp = 4;
413
-
414
- const response = await xero.accountingApi.getBankTransactions(activeTenantId, ifModifiedSince, where, order, page, unitdp);
415
- ```
416
- Note that you should set the query param to undefined instead of null if you wish to ignore a specific filter.
417
- ```js
418
- const purchaseOrders = xero.accountingApi.getPurchaseOrders(tenant.tenantId, null, null, '2021-01-01', '2021-04-25', null, 1);
419
-
420
- // http://api-oauth2.xero.com/api.xro/2.0/PurchaseOrders?Status=&DateFrom=2008-01-01&DateTo=2021-04-25&order=&page=1
421
- // "Status=&" is breaking the above query
422
- // purchaseOrders will be an empty array
423
-
424
- const purchaseOrders = xero.accountingApi.getPurchaseOrders(tenant.tenantId, undefined, undefined, '2021-01-01', '2021-04-25', undefined, 1);
425
-
426
- // http://api-oauth2.xero.com/api.xro/2.0/PurchaseOrders?DateFrom=2008-01-01&DateTo=2021-04-25&order=&page=1
427
- // params are omitted
428
- // purchaseOrders array will have results now
429
- ```
430
- ---
431
- ## Security
432
- This repo leverages a certified OA2 and OIDC library called openid-client. For a deeper dive the repo's functionality, check out them directly https://github.com/panva/node-openid-client.
433
-
434
- ### Preventing CSRF Using Xero-Node
435
- When xero.buildConsentUrl is called we call openid-client authorizationUrl method, passing redirect_uri, scope, and state (if present) as arguments and returns a formatted url string made up from the given config. The user is then directed to the consentUrl to begin the auth process with Xero. When the auth process is complete Xero redirects the user to the specified callback route and passes along params including the state if it was initially provided. At this point openid-client takes over verifying params.state and check.state match if provided. If the state does not match the initial user's, the openid-client library throws an error:
436
-
437
- ```
438
- RPError: state mismatch, expected user=1234, got: user=666
439
- ```
440
- ### JWT Verification Using Xero-Node
441
- JWT verification of both the `access_token` and `id_token` are baked into the openid-client library we leverage. When `xero.apiCallback` is called, openid-client `validateJARM` is triggered which also invokes `validateJWT`. If openid-client fails to validate the JWT signature it will throw an error.
442
-
443
- ---
444
- ## Contributing
445
- 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](https://github.com/XeroAPI/Xero-OpenAPI) - PR's will be evaluated and pre-merge will be incorporated into the root generation templates.
446
-
447
- Please add tests for net new functionality and ensure existing test suite passes all tests.
448
- ```
449
- npm test
450
- ```
451
-
452
- ### Versioning
453
- 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.
454
-
455
-
456
- ## Participating in Xero’s developer community
457
-
458
- 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.
459
-
460
- Here are a few things you should be aware of as a contributor:
461
- * Xero has adopted the Contributor Covenant [Code of Conduct](https://github.com/XeroAPI/xero-node/blob/master/CODE_OF_CONDUCT.md), we expect all contributors in our community to adhere to it
462
- * If you raise an issue then please make sure to fill out the github issue template, doing so helps us help you
463
- * 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
464
- * We have a [contribution guide](https://github.com/XeroAPI/xero-node/blob/master/CONTRIBUTING.md) for you to follow when contributing to this SDK
465
- * Curious about how we generate our SDK’s? Have a [read of our process](https://devblog.xero.com/building-sdks-for-the-future-b79ff726dfd6) and have a look at our [OpenAPISpec](https://github.com/XeroAPI/Xero-OpenAPI)
466
- * This software is published under the [MIT License](https://github.com/XeroAPI/xero-node/blob/master/LICENSE)
467
-
468
- For questions that aren’t related to SDKs please refer to our [developer support page](https://developer.xero.com/support/).
1
+ # xero-node
2
+ [![npm version](https://badge.fury.io/js/xero-node.svg)](https://badge.fury.io/js/xero-node)
3
+ [![Github forks](https://img.shields.io/github/forks/XeroAPI/xero-node.svg)](https://github.com/XeroAPI/xero-node/network)
4
+ [![Github stars](https://img.shields.io/github/stars/XeroAPI/xero-node.svg)](https://github.com/XeroAPI/xero-node/stargazers)
5
+ ![npm](https://img.shields.io/npm/dt/xero-node)
6
+
7
+ The xero-node SDK makes it easy for developers to access Xero's APIs in their JavaScript code, and build robust applications and software using small business & general ledger accounting data.
8
+ # Table of Contents
9
+ - [API Client documentation](#api-client-documentation)
10
+ - [Sample Applications](#sample-applications)
11
+ - [Xero Account Requirements](#xero-account-requirements)
12
+ - [Installation](#installation)
13
+ - [Configuration](#configuration)
14
+ - [Authentication](#authentication)
15
+ - [Custom Connections](#custom-connections)
16
+ - [App Store Subscriptions](#app-store-subscriptions)
17
+ - [API Clients](#api-clients)
18
+ - [Helper Methods](#helper-methods)
19
+ - [Usage Examples](#usage-examples)
20
+ - [SDK conventions](#sdk-conventions)
21
+ - [Security](#security)
22
+ - [Contributing](#contributing)
23
+
24
+ <hr>
25
+
26
+ ## API Client documentation
27
+ This SDK supports full method coverage for the following Xero API sets:
28
+
29
+ | API Set | Description |
30
+ | --- | --- |
31
+ | [`Accounting`](https://xeroapi.github.io/xero-node/accounting/index.html) | The Accounting API exposes accounting functions of the main Xero application *(most commonly used)*
32
+ | [Assets](https://xeroapi.github.io/xero-node/assets/index.html) | The Assets API exposes fixed asset related functions of the Xero Accounting application |
33
+ | [Bankfeeds](https://xeroapi.github.io/xero-node/bankfeeds/index.html) | The Bankfeeds API facilitates the flow of transaction and statement data |
34
+ | [Files](https://xeroapi.github.io/xero-node/files/index.html) | The Files API provides access to the files, folders, and the association of files within a Xero organisation |
35
+ | [Projects](https://xeroapi.github.io/xero-node/projects/index.html) | Xero Projects allows businesses to track time and costs on projects/jobs and report on profitability |
36
+ | [Payroll (AU)](https://xeroapi.github.io/xero-node/payroll-au/index.html) | The (AU) Payroll API exposes payroll related functions of the payroll Xero application |
37
+ | [Payroll (NZ)](https://xeroapi.github.io/xero-node/payroll-nz/index.html) | The (NZ) Payroll API exposes payroll related functions of the payroll Xero application |
38
+ | [Payroll (UK)](https://xeroapi.github.io/xero-node/payroll-uk/index.html) | The (UK) Payroll API exposes payroll related functions of the payroll Xero application |
39
+
40
+ <img src="https://i.imgur.com/3ISSOwp.png" alt="drawing" width="350"/>
41
+
42
+ <hr>
43
+
44
+ ## Sample Applications
45
+ Sample apps can get you started quickly with simple auth flows and advanced usage examples.
46
+
47
+ | Sample App | Description | Screenshot |
48
+ | --- | --- | --- |
49
+ | [`starter-app`](https://github.com/XeroAPI/xero-node-oauth2-ts-starter) | Basic getting started code samples | <img src="https://i.imgur.com/k208KAv.png" alt="drawing" width="200"/>
50
+ | [`full-app`](https://github.com/XeroAPI/xero-node-oauth2-app) | Complete app with more complex examples | <img src="https://i.imgur.com/TaMQvnp.png" alt="drawing" width="500"/>
51
+ | [`custom-connections-starter`](https://github.com/XeroAPI/xero-node-custom-connections-starter) | Basic app showing Custom Connections - a Xero [premium option](https://developer.xero.com/documentation/guides/oauth2/custom-connections) for building M2M integrations to a single org | <img src="https://i.imgur.com/HoQHLuq.png" alt="drawing" width="300"/>
52
+ | [`xero-node-sso-app`](https://github.com/XeroAPI/xero-node-sso-app) | App showing Xero Single Sign On - as well as basic setup and usage of the Xero App Store `appStoreApi.getSubscription` endpoint | <img src="https://i.imgur.com/4NGowZz.png" alt="drawing" width="300"/>
53
+ | [`xero-node-sso-form`](https://github.com/XeroAPI/xero-node-sso-form) | App showing Sign up with Xero to Lead | <img src="https://raw.githubusercontent.com/XeroAPI/xero-node-sso-form/main/public/images/ssu-demo-screenshot.png" alt="drawing" width="300"/>
54
+
55
+ <hr>
56
+
57
+ ## Xero Account Requirements
58
+ - Create a [free Xero user account](https://www.xero.com/us/signup/api/)
59
+ - Login to your Xero developer [dashboard](https://developer.xero.com/app/manage) and create an API application
60
+ - Copy the credentials from your API app and store them using a secure ENV variable strategy
61
+ - Decide the [neccesary scopes](https://developer.xero.com/documentation/oauth2/scopes) for your app's functionality
62
+
63
+ # Installation
64
+ To install this SDK in your project:
65
+ ```
66
+ npm install xero-node
67
+ ```
68
+
69
+ ---
70
+ ## Configuration
71
+ ```js
72
+ import { XeroClient } from 'xero-node';
73
+
74
+ const xero = new XeroClient({
75
+ clientId: 'YOUR_CLIENT_ID',
76
+ clientSecret: 'YOUR_CLIENT_SECRET',
77
+ redirectUris: [`http://localhost:${port}/callback`],
78
+ scopes: 'openid profile email accounting.settings accounting.transactions offline_access'.split(" "),
79
+ state: 'returnPage=my-sweet-dashboard', // custom params (optional)
80
+ httpTimeout: 3000, // ms (optional)
81
+ clockTolerance: 10 // seconds (optional)
82
+ });
83
+ ```
84
+
85
+ ---
86
+ ## Authentication
87
+ 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](https://jwt.io/) to the header of each request.
88
+
89
+ If you are making an API call for the first time:
90
+
91
+ 1. Send the user to the Xero authorization URL
92
+ ```js
93
+ let consentUrl = await xero.buildConsentUrl();
94
+ res.redirect(consentUrl);
95
+ ```
96
+ 2. The user will authorize your application and be sent to your `redirect_uri`
97
+ ```js
98
+ process.env.REDIRECT_URI
99
+ => /callback?code=xyz123
100
+ ```
101
+ 3. You exchange the temporary `code` for a valid `token_set`
102
+ ```js
103
+ const tokenSet = await xero.apiCallback(req.url);
104
+ // save the tokenSet
105
+ ```
106
+
107
+ It is recommended that you store this token set JSON in a datastore 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.
108
+
109
+ ### Token Set
110
+ | key | value | description |
111
+ | --- | --- | --- |
112
+ | id_token: | "xxx.yyy.zzz" | [OpenID Connect](https://openid.net/connect/) token returned if `openid profile email` scopes accepted |
113
+ | access_token: | "xxx.yyy.zzz" | [Bearer token](https://oauth.net/2/jwt/) with a 30 minute expiration required for all API calls |
114
+ | expires_in: | 1800 | Time in seconds till the token expires - 1800s is 30m |
115
+ | refresh_token: | "XXXXXXX" | Alphanumeric string used to obtain a new Token Set w/ a fresh access_token - 60 day expiry |
116
+ | scope: | ["email", "profile", "openid", "accounting.settings", "accounting.transactions", "offline_access"] | The Xero permissions that are embedded in the `access_token` |
117
+
118
+ Example Token Set JSON:
119
+ ```
120
+ {
121
+ "id_token": "xxx.yyy.zz",
122
+ "access_token": "xxx.yyy.zzz",
123
+ "expires_in": 1800,
124
+ "token_type": "Bearer",
125
+ "refresh_token": "xxxxxxxxx",
126
+ "scope": ["email", "profile", "openid", "accounting.settings", "accounting.transactions", "offline_access"]
127
+ }
128
+ ```
129
+
130
+ ---
131
+ ## Custom Connections
132
+
133
+ Custom Connections are a Xero [premium option](https://developer.xero.com/documentation/oauth2/custom-connections) used for building M2M integrations to a single organisation. A custom connection uses OAuth2.0's [`client_credentials`](https://www.oauth.com/oauth2-servers/access-tokens/client-credentials/) grant which eliminates the step of exchanging the temporary code for a token set.
134
+
135
+ To use this SDK with a `Custom Connection`:
136
+ ```js
137
+ import { XeroClient } from 'xero-node';
138
+
139
+ const xero = new XeroClient({
140
+ clientId: 'YOUR_CLIENT_ID',
141
+ clientSecret: 'YOUR_CLIENT_SECRET',
142
+ grantType: 'client_credentials'
143
+ });
144
+
145
+ const tokenSet = await xero.getClientCredentialsToken();
146
+ // save the tokenSet
147
+
148
+ const invoices = await xero.accountingApi.getInvoices('');
149
+ ```
150
+
151
+ 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.
152
+
153
+ > Becuase the SDK is generated from the OpenAPI spec the parameter remains which requires you to pass an empty string to use this SDK with a Custom Connection.
154
+
155
+ ---
156
+
157
+ ## App Store Subscriptions
158
+
159
+ If you are implementing subscriptions to participate in Xero's App Store you will need to setup [App Store subscriptions](https://developer.xero.com/documentation/xero-app-store/app-partner-guides/xero-app-store-subscriptions) endpoints.
160
+
161
+ 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.
162
+
163
+ 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.
164
+
165
+ ```ts
166
+ // => /post-purchase-url
167
+
168
+ const xeroAppStoreClient = new XeroClient({
169
+ clientId: process.env.CLIENT_ID,
170
+ clientSecret: process.env.CLIENT_SECRET,
171
+ grantType: 'client_credentials',
172
+ scopes: ['marketplace.billing']
173
+ });
174
+
175
+ try {
176
+ await xeroAppStoreClient.getClientCredentialsToken()
177
+ } catch(e) {
178
+ console.log('ERROR: ', e)
179
+ }
180
+
181
+ const subscriptionRequest = await xeroAppStoreClient.appStoreApi.getSubscription(subscripionId)
182
+
183
+ console.log(subscriptionRequest.body)
184
+ {
185
+ currentPeriodEnd: 2021-09-02T20:08:58.772Z,
186
+ endDate: null,
187
+ id: '03bc74f2-1237-4477-b782-2dfb1a6d8b21',
188
+ organisationId: '79e8b2e5-c63d-4dce-888f-e0f3e9eac647',
189
+ plans: [
190
+ {
191
+ id: '6abc26f3-9390-4194-8b25-ce8b9942fda9',
192
+ name: 'Small',
193
+ status: 'ACTIVE',
194
+ subscriptionItems: [
195
+ endDate: null,
196
+ id: '834cff4c-b753-4de2-9e7a-3451e14fa17a',
197
+ price: {
198
+ id: '2310de92-c7c0-4bcb-b972-fb7612177bc7',
199
+ amount: 0.1,
200
+ currency: 'NZD'
201
+ },
202
+ product: Product {
203
+ id: '9586421f-7325-4493-bac9-d93be06a6a38',
204
+ name: '',
205
+ type: 'FIXED'
206
+ },
207
+ startDate: 2021-08-02T20:08:58.772Z,
208
+ testMode: true
209
+
210
+ ]
211
+ }
212
+ ],
213
+ startDate: 2021-08-02T20:08:58.772Z,
214
+ status: 'ACTIVE',
215
+ testMode: true
216
+ }
217
+ ```
218
+ You should use the subscription data to provision user access/permissions to your application.
219
+ ### App Store Subscription Webhooks
220
+
221
+ 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.
222
+
223
+ Refer to Xero's documenation to learn more about setting up and receiving webhooks or review [this blogpost](https://devblog.xero.com/keeping-your-integration-in-sync-implementing-xero-webhooks-using-node-express-and-ngrok-6d2976baac6d) explaing webhooks using xero-node sdk.
224
+ > https://developer.xero.com/documentation/guides/webhooks/overview/
225
+
226
+ ---
227
+ ## API Clients
228
+ You can access the different API sets and their available methods through the following:
229
+
230
+ ```js
231
+ const xero = new XeroClient({
232
+ clientId: 'YOUR_CLIENT_ID', // required
233
+ clientSecret: 'YOUR_CLIENT_SECRET', // required
234
+ redirectUris: [`http://localhost:${port}/callback`], // not used for client_credentials auth flow
235
+ grantType: 'client_credentials', // only used for client_credentials auth flow
236
+ scopes: 'openid profile email accounting.settings accounting.transactions offline_access'.split(" "), // not used for client_credentials auth flow
237
+ state: 'returnPage=my-sweet-dashboard', // custom params (optional), not used for client_credentials auth flow
238
+ httpTimeout: 3000, // ms (optional)
239
+ clockTolerance: 10 // seconds (optional)
240
+ });
241
+
242
+ xero.accountingApi
243
+ xero.assetApi
244
+ xero.projectApi
245
+ xero.filesApi
246
+ xero.payrollAUApi
247
+ xero.payrollNZApi
248
+ xero.payrollUKApi
249
+ ```
250
+ ---
251
+ ## Helper Methods
252
+
253
+ Once you have a valid Token Set in your datastore, the next time you want to call the Xero API simply initialize a new `client` and refresh the token set. There are two ways to refresh a token
254
+
255
+ ```js
256
+ // you can refresh the token using the fully initialized client leveraging openid-client
257
+
258
+ import { XeroClient } from 'xero-node';
259
+
260
+ const xero = new XeroClient({
261
+ clientId: 'YOUR_CLIENT_ID',
262
+ clientSecret: 'YOUR_CLIENT_SECRET',
263
+ redirectUris: [`http://localhost:${port}/callback`],
264
+ scopes: 'openid profile email accounting.settings accounting.transactions offline_access'.split(" ")
265
+ });
266
+
267
+ await xero.initialize();
268
+
269
+ const tokenSet = getTokenSetFromDatabase(userId); // example function name
270
+
271
+ await xero.setTokenSet(tokenSet);
272
+
273
+ if(tokenSet.expired()){
274
+ const validTokenSet = await xero.refreshToken();
275
+ // save the new tokenset
276
+ }
277
+ ```
278
+ ```js
279
+ // or if you already generated a tokenSet and have a valid (< 60 days refresh token),
280
+ // you can initialize an empty client and refresh by passing the client, secret, and refresh_token
281
+
282
+ import { XeroClient } from 'xero-node';
283
+
284
+ const tokenSet = getTokenSetFromDatabase(userId); // example function name
285
+
286
+ if(tokenSet.expired()){
287
+ const xero = new XeroClient();
288
+ const validTokenSet = await xero.refreshWithRefreshToken(client_id, client_secret, tokenSet.refresh_token)
289
+ // save the new tokenset
290
+ }
291
+ ```
292
+
293
+ A full list of the SDK client's methods:
294
+
295
+ | method | description |
296
+ | --- | --- |
297
+ | client.`initialize` | Initializes the Xero Client with the provided configuration |
298
+ | client.`buildConsentUrl` | Returns a url concatenated from the provided redirect uri, scope, and the issuer ( Xero identity authorize url) |
299
+ | client.`apiCallback`(callbackUrl) | Leverages openid-client library to exchange temporary auth code for token set |
300
+ | client.`disconnect`(connectionId) | Removes an individual tenant connection by connection ID |
301
+ | client.`readTokenSet` | Returns token set currently set on the Xero Client |
302
+ | client.`setTokenSet`(tokenSet) | Sets a specified token set on the Xero Client |
303
+ | client.`refreshToken` | Leverages openid-client library to refresh token set currently set on the Xero Client and returns updated token set |
304
+ | client.`revokeToken` | Revokes a users refresh token and removes all their connections to your app |
305
+ | client.`formatMsDate`(dateString) | Takes a date string and returns it formatted as an MS Date |
306
+ | client.`refreshWithRefreshToken`(clientId, clientSecret, refreshToken) | Refresh a token set without leveraging openid-client |
307
+ | client.`getClientCredentialsToken` | Get a token set without user intervention via the client credentials grant type for custom connections only |
308
+ | client.`updateTenants`(fullOrgDetails: boolean = true) | GET request to the /connections endpoint. Accepts a boolean to indicate whether or not to also make a GET request to the /organisations endpoint and map full org data to each connection object prior to returning the array of connections |
309
+ ---
310
+ ## Usage Examples
311
+ ### Accounting API
312
+ ```js
313
+ import { XeroClient, HistoryRecords, Invoice } from 'xero-node';
314
+
315
+ const xero = new XeroClient({
316
+ clientId: 'YOUR_CLIENT_ID',
317
+ clientSecret: 'YOUR_CLIENT_SECRET',
318
+ redirectUris: [`http://localhost:${port}/callback`],
319
+ scopes: 'openid profile email accounting.settings accounting.transactions offline_access'.split(" ")
320
+ });
321
+
322
+ await xero.initialize();
323
+
324
+ const tokenSet = getTokenSetFromDatabase(userId); // example function name
325
+
326
+ await xero.setTokenSet(tokenSet);
327
+
328
+ if(tokenSet.expired()){
329
+ const validTokenSet = await xero.refreshToken();
330
+ // save the new tokenset
331
+ }
332
+
333
+ await xero.updateTenants();
334
+
335
+ const activeTenantId = xero.tenants[0].tenantId;
336
+
337
+ // GET all Accounts
338
+ const getAccountsResponse = await xero.accountingApi.getAccounts(activeTenantId);
339
+
340
+ const accountId = getAccountsResponse.body.accounts[0].accountID
341
+
342
+ // GET one Account by ID
343
+ const getAccountResponse = await xero.accountingApi.getAccount(activeTenantId, accountId);
344
+
345
+ // CREATE an Invoice
346
+ const invoices = {
347
+ invoices: [
348
+ {
349
+ type: Invoice.TypeEnum.ACCREC,
350
+ contact: {
351
+ contactID: contactId
352
+ },
353
+ lineItems: [
354
+ {
355
+ description: "Acme Tires",
356
+ quantity: 2.0,
357
+ unitAmount: 20.0,
358
+ accountCode: "500",
359
+ taxType: "NONE",
360
+ lineAmount: 40.0
361
+ }
362
+ ],
363
+ date: "2019-03-11",
364
+ dueDate: "2018-12-10",
365
+ reference: "Website Design",
366
+ status: Invoice.StatusEnum.AUTHORISED
367
+ }
368
+ ]
369
+ };
370
+
371
+ const createdInvoicesResponse = await xero.accountingApi.createInvoices(activeTenantId, invoices)
372
+
373
+ const invoiceId = createdInvoicesResponse.body.invoices[0].invoiceID;
374
+
375
+ // CREATE a History Record
376
+ const historyRecords: HistoryRecords = {
377
+ historyRecords: [
378
+ {
379
+ details: "This is a history record"
380
+ }
381
+ ]
382
+ };
383
+
384
+ const createdInvoiceHistoryResponse = await xero.accountingApi.createInvoiceHistory(activeTenantId, invoiceId, historyRecords);
385
+
386
+ // CREATE Attachment
387
+ const filename = "xero-dev.png";
388
+ const pathToUpload = path.resolve(__dirname, "../public/images/xero-dev.png");
389
+ const readStream = fs.createReadStream(pathToUpload);
390
+ const contentType = mime.lookup(filename);
391
+
392
+ const accountAttachmentsResponse = await xero.accountingApi.createInvoiceAttachmentByFileName(activeTenantId, invoiceId, filename, readStream, {
393
+ headers: {
394
+ 'Content-Type': contentType
395
+ }
396
+ });
397
+ ```
398
+
399
+ ---
400
+ ## SDK conventions
401
+
402
+
403
+ ### Querying & Filtering
404
+
405
+
406
+ ```js
407
+ const activeTenantId = 'XERO_TENANT_ID';
408
+ const ifModifiedSince: Date = new Date("2020-02-06T12:17:43.202-08:00");
409
+ const where = 'Status=="AUTHORISED" AND Type=="SPEND"';
410
+ const order = 'Reference ASC';
411
+ const page = 1;
412
+ const unitdp = 4;
413
+
414
+ const response = await xero.accountingApi.getBankTransactions(activeTenantId, ifModifiedSince, where, order, page, unitdp);
415
+ ```
416
+ Note that you should set the query param to undefined instead of null if you wish to ignore a specific filter.
417
+ ```js
418
+ const purchaseOrders = xero.accountingApi.getPurchaseOrders(tenant.tenantId, null, null, '2021-01-01', '2021-04-25', null, 1);
419
+
420
+ // http://api-oauth2.xero.com/api.xro/2.0/PurchaseOrders?Status=&DateFrom=2008-01-01&DateTo=2021-04-25&order=&page=1
421
+ // "Status=&" is breaking the above query
422
+ // purchaseOrders will be an empty array
423
+
424
+ const purchaseOrders = xero.accountingApi.getPurchaseOrders(tenant.tenantId, undefined, undefined, '2021-01-01', '2021-04-25', undefined, 1);
425
+
426
+ // http://api-oauth2.xero.com/api.xro/2.0/PurchaseOrders?DateFrom=2008-01-01&DateTo=2021-04-25&order=&page=1
427
+ // params are omitted
428
+ // purchaseOrders array will have results now
429
+ ```
430
+ ---
431
+ ## Security
432
+ This repo leverages a certified OA2 and OIDC library called openid-client. For a deeper dive the repo's functionality, check out them directly https://github.com/panva/node-openid-client.
433
+
434
+ ### Preventing CSRF Using Xero-Node
435
+ When xero.buildConsentUrl is called we call openid-client authorizationUrl method, passing redirect_uri, scope, and state (if present) as arguments and returns a formatted url string made up from the given config. The user is then directed to the consentUrl to begin the auth process with Xero. When the auth process is complete Xero redirects the user to the specified callback route and passes along params including the state if it was initially provided. At this point openid-client takes over verifying params.state and check.state match if provided. If the state does not match the initial user's, the openid-client library throws an error:
436
+
437
+ ```
438
+ RPError: state mismatch, expected user=1234, got: user=666
439
+ ```
440
+ ### JWT Verification Using Xero-Node
441
+ JWT verification of both the `access_token` and `id_token` are baked into the openid-client library we leverage. When `xero.apiCallback` is called, openid-client `validateJARM` is triggered which also invokes `validateJWT`. If openid-client fails to validate the JWT signature it will throw an error.
442
+
443
+ ---
444
+ ## Contributing
445
+ 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](https://github.com/XeroAPI/Xero-OpenAPI) - PR's will be evaluated and pre-merge will be incorporated into the root generation templates.
446
+
447
+ Please add tests for net new functionality and ensure existing test suite passes all tests.
448
+ ```
449
+ npm test
450
+ ```
451
+
452
+ ### Versioning
453
+ 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.
454
+
455
+
456
+ ## Participating in Xero’s developer community
457
+
458
+ 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.
459
+
460
+ Here are a few things you should be aware of as a contributor:
461
+ * Xero has adopted the Contributor Covenant [Code of Conduct](https://github.com/XeroAPI/xero-node/blob/master/CODE_OF_CONDUCT.md), we expect all contributors in our community to adhere to it
462
+ * If you raise an issue then please make sure to fill out the github issue template, doing so helps us help you
463
+ * 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
464
+ * We have a [contribution guide](https://github.com/XeroAPI/xero-node/blob/master/CONTRIBUTING.md) for you to follow when contributing to this SDK
465
+ * Curious about how we generate our SDK’s? Have a [read of our process](https://devblog.xero.com/building-sdks-for-the-future-b79ff726dfd6) and have a look at our [OpenAPISpec](https://github.com/XeroAPI/Xero-OpenAPI)
466
+ * This software is published under the [MIT License](https://github.com/XeroAPI/xero-node/blob/master/LICENSE)
467
+
468
+ For questions that aren’t related to SDKs please refer to our [developer support page](https://developer.xero.com/support/).