@vrplatform/hyperline-client 1.0.29 → 1.0.31

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.
package/src/client.ts CHANGED
@@ -1,5 +1,4 @@
1
- import { utc } from '@vrplatform/utils';
2
- import type { operations, paths } from './_types.generated';
1
+ import type { paths } from './_types.generated';
3
2
  import { BillableEvents, CreateBillableEvent } from './types';
4
3
  import { arrayBufferToBase64 } from './utils';
5
4
 
@@ -64,20 +63,14 @@ export class HyperlineAPI {
64
63
  (content ? JSON.stringify(content) : null) ||
65
64
  'Unknown error';
66
65
 
67
- if (status === 429) {
68
- // get difference between rate limit reset and now
69
- const rateLimitReset = utc(result?.headers.get('x-ratelimit-reset'));
70
- const diff = rateLimitReset.diff(utc(), 'milliseconds');
71
-
72
- // wait until rate limit resets
73
- await new Promise((resolve) => setTimeout(resolve, diff));
74
-
75
- return await this.fetchBasic<T>(path, options, data);
76
- } else if (status === 404 && method === 'GET') {
66
+ if (status === 404 && method === 'GET') {
77
67
  return null;
78
68
  } else {
79
- throw new Error(
80
- `Hyperline Error ${status} (${statusText}) in ${path} for ${method}: ${message}`
69
+ throw Object.assign(
70
+ new Error(
71
+ `Hyperline Error ${status} (${statusText}) in ${path} for ${method}: ${message}`
72
+ ),
73
+ { status }
81
74
  );
82
75
  }
83
76
  } else if (result.headers.get('content-type')?.includes('application/pdf'))
@@ -132,8 +125,8 @@ export class HyperlineAPI {
132
125
  }
133
126
 
134
127
  return await this.fetch<
135
- paths['/v1/customers/{id}']['get']['responses'][200]['content']['application/json']
136
- >(`/v1/customers/${id}`);
128
+ paths['/v2/customers/{id}']['get']['responses'][200]['content']['application/json']
129
+ >(`/v2/customers/${id}`);
137
130
  }
138
131
 
139
132
  async getCustomerById(id?: string) {
@@ -142,48 +135,48 @@ export class HyperlineAPI {
142
135
  throw new Error('Missing tenant ID');
143
136
  }
144
137
 
145
- const results = await this.fetch<
146
- paths['/v1/customers']['get']['responses'][200]['content']['application/json']
147
- >(`/v1/customers?external_id__equals=${id}`);
138
+ const results = await this.getCustomers({
139
+ external_id__equals: id,
140
+ status: 'all',
141
+ limit: 1,
142
+ });
148
143
 
149
144
  return results?.data[0];
150
145
  }
151
146
 
152
147
  async getCustomers(
153
- skip = 0,
154
- take = 100,
155
- query: operations['getCustomers']['parameters']['query'] = {}
148
+ query: paths['/v2/customers']['get']['parameters']['query'] = {}
156
149
  ) {
157
150
  const queryParams = new URLSearchParams();
158
- queryParams.set('take', take.toString());
159
- queryParams.set('skip', skip.toString());
160
151
 
161
- for (const [key, value] of Object.entries(query)) {
152
+ for (const [key, value] of Object.entries({ limit: 100, ...query })) {
162
153
  if (value === undefined || value === null) continue;
163
154
  queryParams.set(key, value.toString());
164
155
  }
165
156
 
166
157
  return await this.fetch<
167
- paths['/v1/customers']['get']['responses'][200]['content']['application/json']
168
- >(`/v1/customers?${queryParams.toString()}`);
158
+ paths['/v2/customers']['get']['responses'][200]['content']['application/json']
159
+ >(`/v2/customers?${queryParams.toString()}`);
169
160
  }
170
161
 
171
162
  async getAllCustomers(
172
- items: paths['/v1/customers']['get']['responses'][200]['content']['application/json']['data'] = [],
173
- query: operations['getCustomers']['parameters']['query'] = {}
163
+ items: paths['/v2/customers']['get']['responses'][200]['content']['application/json']['data'] = [],
164
+ query: paths['/v2/customers']['get']['parameters']['query'] = {}
174
165
  ): Promise<
175
- paths['/v1/customers']['get']['responses'][200]['content']['application/json']['data']
166
+ paths['/v2/customers']['get']['responses'][200]['content']['application/json']['data']
176
167
  > {
177
- const { data } = (await this.getCustomers(items.length, 100, {
168
+ const page = await this.getCustomers({
178
169
  status: 'all',
179
170
  ...query,
180
- })) || {
181
- data: [],
182
- };
171
+ });
172
+ if (!page) throw new Error('Customer list not found');
183
173
 
184
- return data.length
185
- ? this.getAllCustomers([...items, ...data], { status: 'all', ...query })
186
- : [...items, ...data];
174
+ return page.next_cursor
175
+ ? this.getAllCustomers([...items, ...page.data], {
176
+ ...query,
177
+ cursor: page.next_cursor,
178
+ })
179
+ : [...items, ...page.data];
187
180
  }
188
181
 
189
182
  async createCustomer(
@@ -267,7 +260,7 @@ export class HyperlineAPI {
267
260
  async getSubscriptions(
268
261
  skip = 0,
269
262
  take = 100,
270
- query: operations['getSubscriptions']['parameters']['query'] = {
263
+ query: paths['/v2/subscriptions']['get']['parameters']['query'] = {
271
264
  status: 'all',
272
265
  }
273
266
  ) {
@@ -287,7 +280,7 @@ export class HyperlineAPI {
287
280
 
288
281
  async getAllSubscriptions(
289
282
  items: paths['/v2/subscriptions']['get']['responses'][200]['content']['application/json']['data'] = [],
290
- query: operations['getSubscriptions']['parameters']['query'] = {}
283
+ query: paths['/v2/subscriptions']['get']['parameters']['query'] = {}
291
284
  ): Promise<
292
285
  paths['/v2/subscriptions']['get']['responses'][200]['content']['application/json']['data']
293
286
  > {
@@ -359,25 +352,6 @@ export class HyperlineAPI {
359
352
  });
360
353
  }
361
354
 
362
- // PLAN
363
-
364
- async getPlan(id: string) {
365
- if (!id) {
366
- this.logger.error('Missing plan ID', { id });
367
- throw new Error('Missing plan ID');
368
- }
369
-
370
- return await this.fetch<
371
- paths['/v1/plans/{id}']['get']['responses'][200]['content']['application/json']
372
- >(`/v1/plans/${id}`);
373
- }
374
-
375
- async getPlans(skip = 0, take = 100) {
376
- return await this.fetch<
377
- paths['/v1/plans']['get']['responses'][200]['content']['application/json']
378
- >(`/v1/plans?take=${take}&skip=${skip}`);
379
- }
380
-
381
355
  // INVOICE
382
356
 
383
357
  async getInvoice(id: string) {
@@ -387,47 +361,33 @@ export class HyperlineAPI {
387
361
  }
388
362
 
389
363
  return await this.fetch<
390
- paths['/v1/invoices/{id}']['get']['responses'][200]['content']['application/json']
391
- >(`/v1/invoices/${id}`);
364
+ paths['/v2/invoices/{id}']['get']['responses'][200]['content']['application/json']
365
+ >(`/v2/invoices/${id}`);
392
366
  }
393
367
 
394
368
  async getInvoices(
395
- skip = 0,
396
- take = 100,
397
- query: operations['getInvoices']['parameters']['query'] = { status: 'all' }
369
+ query: paths['/v2/invoices']['get']['parameters']['query'] = {}
398
370
  ) {
399
371
  const queryParams = new URLSearchParams();
400
- queryParams.set('take', take.toString());
401
- queryParams.set('skip', skip.toString());
402
-
403
- for (const [key, value] of Object.entries(query)) {
372
+ for (const [key, value] of Object.entries({
373
+ limit: 100,
374
+ status: 'all',
375
+ ...query,
376
+ })) {
404
377
  if (value === undefined || value === null) continue;
405
378
  queryParams.set(key, value.toString());
406
379
  }
407
380
 
408
381
  return await this.fetch<
409
- paths['/v1/invoices']['get']['responses'][200]['content']['application/json']
410
- >(`/v1/invoices?${queryParams.toString()}`);
382
+ paths['/v2/invoices']['get']['responses'][200]['content']['application/json']
383
+ >(`/v2/invoices?${queryParams.toString()}`);
411
384
  }
412
385
 
413
386
  async getInvoicesByCustomerId(
414
387
  id: string,
415
- filters?: Partial<operations['getInvoices']['parameters']['query']>
388
+ filters?: paths['/v2/invoices']['get']['parameters']['query']
416
389
  ) {
417
- const queryParams = new URLSearchParams();
418
- queryParams.append('customerId__equals', id);
419
-
420
- if (filters) {
421
- Object.entries(filters).forEach(([key, value]) => {
422
- if (value !== undefined && value !== null) {
423
- queryParams.append(key, value.toString());
424
- }
425
- });
426
- }
427
-
428
- return await this.fetch<
429
- paths['/v1/invoices']['get']['responses'][200]['content']['application/json']
430
- >(`/v1/invoices?${queryParams.toString()}`);
390
+ return await this.getInvoices({ ...filters, customer_id: id });
431
391
  }
432
392
 
433
393
  async getInvoiceDownload(id: string) {
@@ -441,21 +401,20 @@ export class HyperlineAPI {
441
401
  }
442
402
 
443
403
  async getAllInvoices(
444
- items: paths['/v1/invoices']['get']['responses'][200]['content']['application/json']['data'] = [],
445
- query: operations['getInvoices']['parameters']['query'] = { status: 'all' }
404
+ items: paths['/v2/invoices']['get']['responses'][200]['content']['application/json']['data'] = [],
405
+ query: paths['/v2/invoices']['get']['parameters']['query'] = {}
446
406
  ): Promise<
447
- paths['/v1/invoices']['get']['responses'][200]['content']['application/json']['data']
407
+ paths['/v2/invoices']['get']['responses'][200]['content']['application/json']['data']
448
408
  > {
449
- const { data } = (await this.getInvoices(items.length, 100, {
450
- status: 'all',
451
- ...query,
452
- })) || {
453
- data: [],
454
- };
455
-
456
- return data.length
457
- ? this.getAllInvoices([...items, ...data], { status: 'all', ...query })
458
- : [...items, ...data];
409
+ const page = await this.getInvoices(query);
410
+ if (!page) throw new Error('Invoice list not found');
411
+
412
+ return page.next_cursor
413
+ ? this.getAllInvoices([...items, ...page.data], {
414
+ ...query,
415
+ cursor: page.next_cursor,
416
+ })
417
+ : [...items, ...page.data];
459
418
  }
460
419
 
461
420
  async voidInvoice(
package/src/const.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { TrackingEvent } from '@vrplatform/log';
1
+ import type { TrackingEvent } from '@vrplatform/tracking';
2
2
  import { HyperlineEntities } from './types';
3
3
 
4
4
  // Tenants
@@ -73,6 +73,7 @@ export const WEBHOOKS = {
73
73
  'payment_method.deleted': 'hyperline_payment_method_deleted',
74
74
  'payment_method.expired': 'hyperline_payment_method_expired',
75
75
  'subscription.activated': 'hyperline_subscription_status_changed',
76
+ 'subscription.updated': 'hyperline_subscription_status_changed',
76
77
  'subscription.reactivated': 'hyperline_subscription_status_changed',
77
78
  'subscription.cancellation_scheduled':
78
79
  'hyperline_subscription_cancellation_scheduled',
package/src/types.ts CHANGED
@@ -39,5 +39,6 @@ export type CreateBillableEvent = {
39
39
  amount?: number;
40
40
  amountGl?: number;
41
41
  amountNonGl?: number;
42
+ amountAll?: number;
42
43
  };
43
44
  };
@@ -19,17 +19,18 @@ export async function calculateMrr(
19
19
  liveBilling?: boolean
20
20
  ): Promise<{
21
21
  customer_id: string;
22
+ // BIL-614: legacy minimums remain until the subscription migration is complete.
22
23
  plan_id: string | null;
23
24
  products?: unknown[];
24
25
  coupons?: (
25
26
  | {
26
27
  type: 'amount';
27
- repeat: 'once' | 'forever' | 'custom';
28
+ repeat: HyperlineEntities['Coupon']['repeat'];
28
29
  discount_amount: number;
29
30
  }
30
31
  | {
31
32
  type: 'percent';
32
- repeat: 'once' | 'forever' | 'custom';
33
+ repeat: HyperlineEntities['Coupon']['repeat'];
33
34
  discount_percent: number;
34
35
  }
35
36
  )[];
@@ -62,7 +63,7 @@ export async function calculateMrr(
62
63
  } | null>;
63
64
  getInvoicesByCustomerId(id: string): Promise<{
64
65
  data: {
65
- emitted_at: string;
66
+ issued_at: string;
66
67
  total_amount: number;
67
68
  }[];
68
69
  } | null>;
@@ -185,7 +186,7 @@ export async function calculateMrr(
185
186
 
186
187
  // sort invoices by date
187
188
  const invoices = data
188
- ?.sort((a, b) => a.emitted_at.localeCompare(b.emitted_at))
189
+ ?.sort((a, b) => a.issued_at.localeCompare(b.issued_at))
189
190
  .reverse();
190
191
 
191
192
  // get the last invoice