@vrplatform/hyperline-client 1.0.30 → 1.0.32
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/build/main/_types.generated.d.ts +44928 -16283
- package/build/main/_types.generated.js.map +1 -1
- package/build/main/client.d.ts +1634 -428
- package/build/main/client.js +87 -62
- package/build/main/client.js.map +1 -1
- package/build/main/const.d.ts +19 -20
- package/build/main/const.js +31 -22
- package/build/main/const.js.map +1 -1
- package/build/main/hyperlineError.d.ts +5 -0
- package/build/main/hyperlineError.js +14 -0
- package/build/main/hyperlineError.js.map +1 -0
- package/build/main/index.d.ts +1 -0
- package/build/main/index.js +1 -0
- package/build/main/index.js.map +1 -1
- package/build/main/types.d.ts +3 -10
- package/build/main/types.js.map +1 -1
- package/build/main/utils/calculateMrr.d.ts +5 -5
- package/build/main/utils/calculateMrr.js +7 -16
- package/build/main/utils/calculateMrr.js.map +1 -1
- package/build/main/utils/index.d.ts +0 -1
- package/build/main/utils/index.js +0 -1
- package/build/main/utils/index.js.map +1 -1
- package/build/module/_types.generated.d.ts +44928 -16283
- package/build/module/_types.generated.js.map +1 -1
- package/build/module/client.d.ts +1634 -428
- package/build/module/client.js +87 -62
- package/build/module/client.js.map +1 -1
- package/build/module/const.d.ts +19 -20
- package/build/module/const.js +31 -22
- package/build/module/const.js.map +1 -1
- package/build/module/hyperlineError.d.ts +5 -0
- package/build/module/hyperlineError.js +16 -0
- package/build/module/hyperlineError.js.map +1 -0
- package/build/module/index.d.ts +1 -0
- package/build/module/index.js +1 -0
- package/build/module/index.js.map +1 -1
- package/build/module/types.d.ts +3 -10
- package/build/module/types.js.map +1 -1
- package/build/module/utils/calculateMrr.d.ts +5 -5
- package/build/module/utils/calculateMrr.js +7 -16
- package/build/module/utils/calculateMrr.js.map +1 -1
- package/build/module/utils/index.d.ts +0 -1
- package/build/module/utils/index.js +0 -1
- package/build/module/utils/index.js.map +1 -1
- package/package.json +4 -4
- package/src/_types.generated.ts +44918 -16274
- package/src/client.ts +152 -92
- package/src/const.ts +41 -28
- package/src/hyperlineError.ts +17 -0
- package/src/index.ts +1 -0
- package/src/types.ts +3 -11
- package/src/utils/calculateMrr.ts +15 -33
- package/src/utils/index.ts +0 -1
- package/build/main/utils/isTestingTeam.d.ts +0 -5
- package/build/main/utils/isTestingTeam.js +0 -4
- package/build/main/utils/isTestingTeam.js.map +0 -1
- package/build/module/utils/isTestingTeam.d.ts +0 -5
- package/build/module/utils/isTestingTeam.js +0 -4
- package/build/module/utils/isTestingTeam.js.map +0 -1
- package/src/utils/isTestingTeam.ts +0 -11
package/src/client.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import type { paths } from './_types.generated';
|
|
2
|
+
import { HyperlineError } from './hyperlineError';
|
|
3
3
|
import { BillableEvents, CreateBillableEvent } from './types';
|
|
4
4
|
import { arrayBufferToBase64 } from './utils';
|
|
5
5
|
|
|
@@ -64,20 +64,13 @@ export class HyperlineAPI {
|
|
|
64
64
|
(content ? JSON.stringify(content) : null) ||
|
|
65
65
|
'Unknown error';
|
|
66
66
|
|
|
67
|
-
if (status ===
|
|
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') {
|
|
67
|
+
if (status === 404 && method === 'GET') {
|
|
77
68
|
return null;
|
|
78
69
|
} else {
|
|
79
|
-
throw new
|
|
80
|
-
`Hyperline Error ${status} (${statusText}) in ${path} for ${method}: ${message}
|
|
70
|
+
throw new HyperlineError(
|
|
71
|
+
`Hyperline Error ${status} (${statusText}) in ${path} for ${method}: ${message}`,
|
|
72
|
+
status,
|
|
73
|
+
result.headers.get('Retry-After')
|
|
81
74
|
);
|
|
82
75
|
}
|
|
83
76
|
} else if (result.headers.get('content-type')?.includes('application/pdf'))
|
|
@@ -125,6 +118,25 @@ export class HyperlineAPI {
|
|
|
125
118
|
|
|
126
119
|
// CUSTOMER
|
|
127
120
|
|
|
121
|
+
async getOrganisation(id: string) {
|
|
122
|
+
if (!id) throw new Error('Missing organisation ID');
|
|
123
|
+
return await this.fetch<
|
|
124
|
+
paths['/v1/organisations/{id}']['get']['responses'][200]['content']['application/json']
|
|
125
|
+
>(`/v1/organisations/${id}`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async updateOrganisation(
|
|
129
|
+
id: string,
|
|
130
|
+
data: NonNullable<
|
|
131
|
+
paths['/v1/organisations/{id}']['patch']['requestBody']
|
|
132
|
+
>['content']['application/json']
|
|
133
|
+
) {
|
|
134
|
+
if (!id) throw new Error('Missing organisation ID');
|
|
135
|
+
return await this.fetch<
|
|
136
|
+
paths['/v1/organisations/{id}']['patch']['responses'][200]['content']['application/json']
|
|
137
|
+
>(`/v1/organisations/${id}`, { method: 'PATCH' }, data);
|
|
138
|
+
}
|
|
139
|
+
|
|
128
140
|
async getCustomer(id?: string) {
|
|
129
141
|
if (!id) {
|
|
130
142
|
this.logger.error('Missing customer ID', { id });
|
|
@@ -132,8 +144,8 @@ export class HyperlineAPI {
|
|
|
132
144
|
}
|
|
133
145
|
|
|
134
146
|
return await this.fetch<
|
|
135
|
-
paths['/
|
|
136
|
-
>(`/
|
|
147
|
+
paths['/v2/customers/{id}']['get']['responses'][200]['content']['application/json']
|
|
148
|
+
>(`/v2/customers/${id}`);
|
|
137
149
|
}
|
|
138
150
|
|
|
139
151
|
async getCustomerById(id?: string) {
|
|
@@ -142,48 +154,51 @@ export class HyperlineAPI {
|
|
|
142
154
|
throw new Error('Missing tenant ID');
|
|
143
155
|
}
|
|
144
156
|
|
|
145
|
-
const results = await this.
|
|
146
|
-
|
|
147
|
-
|
|
157
|
+
const results = await this.getCustomers({
|
|
158
|
+
external_id__equals: id,
|
|
159
|
+
status: 'all',
|
|
160
|
+
limit: 1,
|
|
161
|
+
});
|
|
148
162
|
|
|
149
163
|
return results?.data[0];
|
|
150
164
|
}
|
|
151
165
|
|
|
152
166
|
async getCustomers(
|
|
153
|
-
|
|
154
|
-
take = 100,
|
|
155
|
-
query: operations['getCustomers']['parameters']['query'] = {}
|
|
167
|
+
query: paths['/v2/customers']['get']['parameters']['query'] = {}
|
|
156
168
|
) {
|
|
157
169
|
const queryParams = new URLSearchParams();
|
|
158
|
-
queryParams.set('take', take.toString());
|
|
159
|
-
queryParams.set('skip', skip.toString());
|
|
160
170
|
|
|
161
|
-
for (const [key, value] of Object.entries(query)) {
|
|
171
|
+
for (const [key, value] of Object.entries({ limit: 100, ...query })) {
|
|
162
172
|
if (value === undefined || value === null) continue;
|
|
163
173
|
queryParams.set(key, value.toString());
|
|
164
174
|
}
|
|
165
175
|
|
|
166
176
|
return await this.fetch<
|
|
167
|
-
paths['/
|
|
168
|
-
>(`/
|
|
177
|
+
paths['/v2/customers']['get']['responses'][200]['content']['application/json']
|
|
178
|
+
>(`/v2/customers?${queryParams.toString()}`);
|
|
169
179
|
}
|
|
170
180
|
|
|
171
181
|
async getAllCustomers(
|
|
172
|
-
items: paths['/
|
|
173
|
-
query:
|
|
182
|
+
items: paths['/v2/customers']['get']['responses'][200]['content']['application/json']['data'] = [],
|
|
183
|
+
query: paths['/v2/customers']['get']['parameters']['query'] = {}
|
|
174
184
|
): Promise<
|
|
175
|
-
paths['/
|
|
185
|
+
paths['/v2/customers']['get']['responses'][200]['content']['application/json']['data']
|
|
176
186
|
> {
|
|
177
|
-
|
|
187
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
188
|
+
const page = await this.getCustomers({
|
|
178
189
|
status: 'all',
|
|
179
190
|
...query,
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
|
|
191
|
+
});
|
|
192
|
+
if (!page) throw new Error('Customer list not found');
|
|
193
|
+
if (page.has_more && !page.next_cursor)
|
|
194
|
+
throw new Error('Incomplete customer list');
|
|
183
195
|
|
|
184
|
-
return
|
|
185
|
-
? this.getAllCustomers([...items, ...data], {
|
|
186
|
-
|
|
196
|
+
return page.next_cursor
|
|
197
|
+
? this.getAllCustomers([...items, ...page.data], {
|
|
198
|
+
...query,
|
|
199
|
+
cursor: page.next_cursor,
|
|
200
|
+
})
|
|
201
|
+
: [...items, ...page.data];
|
|
187
202
|
}
|
|
188
203
|
|
|
189
204
|
async createCustomer(
|
|
@@ -249,6 +264,39 @@ export class HyperlineAPI {
|
|
|
249
264
|
>(`/v1/customers/${id}/portal`);
|
|
250
265
|
}
|
|
251
266
|
|
|
267
|
+
// SUBSCRIPTION TEMPLATES
|
|
268
|
+
|
|
269
|
+
async getSubscriptionTemplate(id: string) {
|
|
270
|
+
if (!id) {
|
|
271
|
+
this.logger.error('Missing subscription template ID', { id });
|
|
272
|
+
throw new Error('Missing subscription template ID');
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return await this.fetch<
|
|
276
|
+
paths['/v1/subscriptions/templates/{id}']['get']['responses'][200]['content']['application/json']
|
|
277
|
+
>(`/v1/subscriptions/templates/${id}`);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async previewSubscriptionTransition(
|
|
281
|
+
data: NonNullable<
|
|
282
|
+
paths['/v2/subscriptions/transitions/preview']['post']['requestBody']
|
|
283
|
+
>['content']['application/json']
|
|
284
|
+
) {
|
|
285
|
+
return await this.fetch<
|
|
286
|
+
paths['/v2/subscriptions/transitions/preview']['post']['responses'][200]['content']['application/json']
|
|
287
|
+
>('/v2/subscriptions/transitions/preview', { method: 'POST' }, data);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async createSubscriptionTransition(
|
|
291
|
+
data: NonNullable<
|
|
292
|
+
paths['/v2/subscriptions/transitions']['post']['requestBody']
|
|
293
|
+
>['content']['application/json']
|
|
294
|
+
) {
|
|
295
|
+
return await this.fetch<
|
|
296
|
+
paths['/v2/subscriptions/transitions']['post']['responses'][200]['content']['application/json']
|
|
297
|
+
>('/v2/subscriptions/transitions', { method: 'POST' }, data);
|
|
298
|
+
}
|
|
299
|
+
|
|
252
300
|
// SUBSCRIPTION
|
|
253
301
|
|
|
254
302
|
async getSubscription(id: string, liveBilling = false) {
|
|
@@ -267,7 +315,7 @@ export class HyperlineAPI {
|
|
|
267
315
|
async getSubscriptions(
|
|
268
316
|
skip = 0,
|
|
269
317
|
take = 100,
|
|
270
|
-
query:
|
|
318
|
+
query: paths['/v2/subscriptions']['get']['parameters']['query'] = {
|
|
271
319
|
status: 'all',
|
|
272
320
|
}
|
|
273
321
|
) {
|
|
@@ -287,7 +335,7 @@ export class HyperlineAPI {
|
|
|
287
335
|
|
|
288
336
|
async getAllSubscriptions(
|
|
289
337
|
items: paths['/v2/subscriptions']['get']['responses'][200]['content']['application/json']['data'] = [],
|
|
290
|
-
query:
|
|
338
|
+
query: paths['/v2/subscriptions']['get']['parameters']['query'] = {}
|
|
291
339
|
): Promise<
|
|
292
340
|
paths['/v2/subscriptions']['get']['responses'][200]['content']['application/json']['data']
|
|
293
341
|
> {
|
|
@@ -312,6 +360,12 @@ export class HyperlineAPI {
|
|
|
312
360
|
>('/v2/subscriptions', { method: 'POST' }, data);
|
|
313
361
|
}
|
|
314
362
|
|
|
363
|
+
async activateSubscription(id: string) {
|
|
364
|
+
return await this.fetch<
|
|
365
|
+
paths['/v1/subscriptions/{id}/activate']['post']['responses'][200]['content']['application/json']
|
|
366
|
+
>(`/v1/subscriptions/${id}/activate`, { method: 'POST' });
|
|
367
|
+
}
|
|
368
|
+
|
|
315
369
|
async updateSubscription(
|
|
316
370
|
id: string,
|
|
317
371
|
data: NonNullable<
|
|
@@ -334,6 +388,17 @@ export class HyperlineAPI {
|
|
|
334
388
|
>(`/v1/subscriptions/${id}/update`, { method: 'POST' }, data);
|
|
335
389
|
}
|
|
336
390
|
|
|
391
|
+
async createSubscriptionUpdates(
|
|
392
|
+
id: string,
|
|
393
|
+
data: NonNullable<
|
|
394
|
+
paths['/v1/subscriptions/{id}/update-many']['post']['requestBody']
|
|
395
|
+
>['content']['application/json']
|
|
396
|
+
) {
|
|
397
|
+
return await this.fetch<
|
|
398
|
+
paths['/v1/subscriptions/{id}/update-many']['post']['responses'][201]['content']['application/json']
|
|
399
|
+
>(`/v1/subscriptions/${id}/update-many`, { method: 'POST' }, data);
|
|
400
|
+
}
|
|
401
|
+
|
|
337
402
|
async cancelSubscription(
|
|
338
403
|
id: string,
|
|
339
404
|
data: NonNullable<
|
|
@@ -359,27 +424,30 @@ export class HyperlineAPI {
|
|
|
359
424
|
});
|
|
360
425
|
}
|
|
361
426
|
|
|
362
|
-
//
|
|
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
|
-
}
|
|
427
|
+
// INVOICE
|
|
369
428
|
|
|
429
|
+
async createInvoice(
|
|
430
|
+
data: NonNullable<
|
|
431
|
+
paths['/v1/invoices']['post']['requestBody']
|
|
432
|
+
>['content']['application/json']
|
|
433
|
+
) {
|
|
370
434
|
return await this.fetch<
|
|
371
|
-
paths['/v1/
|
|
372
|
-
>(
|
|
435
|
+
paths['/v1/invoices']['post']['responses'][201]['content']['application/json']
|
|
436
|
+
>('/v1/invoices', { method: 'POST' }, data);
|
|
373
437
|
}
|
|
374
438
|
|
|
375
|
-
async
|
|
439
|
+
async updateInvoice(
|
|
440
|
+
id: string,
|
|
441
|
+
data: NonNullable<
|
|
442
|
+
paths['/v1/invoices/{id}']['patch']['requestBody']
|
|
443
|
+
>['content']['application/json']
|
|
444
|
+
) {
|
|
445
|
+
if (!id) throw new Error('Missing invoice ID');
|
|
376
446
|
return await this.fetch<
|
|
377
|
-
paths['/v1/
|
|
378
|
-
>(`/v1/
|
|
447
|
+
paths['/v1/invoices/{id}']['patch']['responses'][200]['content']['application/json']
|
|
448
|
+
>(`/v1/invoices/${id}`, { method: 'PATCH' }, data);
|
|
379
449
|
}
|
|
380
450
|
|
|
381
|
-
// INVOICE
|
|
382
|
-
|
|
383
451
|
async getInvoice(id: string) {
|
|
384
452
|
if (!id) {
|
|
385
453
|
this.logger.error('Missing invoice ID', { id });
|
|
@@ -387,47 +455,40 @@ export class HyperlineAPI {
|
|
|
387
455
|
}
|
|
388
456
|
|
|
389
457
|
return await this.fetch<
|
|
390
|
-
paths['/
|
|
391
|
-
>(`/
|
|
458
|
+
paths['/v2/invoices/{id}']['get']['responses'][200]['content']['application/json']
|
|
459
|
+
>(`/v2/invoices/${id}`);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
async validateInvoice(id: string) {
|
|
463
|
+
if (!id) throw new Error('Missing invoice ID');
|
|
464
|
+
return await this.fetch<
|
|
465
|
+
paths['/v1/invoices/{id}/validate']['post']['responses'][200]['content']['application/json']
|
|
466
|
+
>(`/v1/invoices/${id}/validate`, { method: 'POST' });
|
|
392
467
|
}
|
|
393
468
|
|
|
394
469
|
async getInvoices(
|
|
395
|
-
|
|
396
|
-
take = 100,
|
|
397
|
-
query: operations['getInvoices']['parameters']['query'] = { status: 'all' }
|
|
470
|
+
query: paths['/v2/invoices']['get']['parameters']['query'] = {}
|
|
398
471
|
) {
|
|
399
472
|
const queryParams = new URLSearchParams();
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
473
|
+
for (const [key, value] of Object.entries({
|
|
474
|
+
limit: 100,
|
|
475
|
+
status: 'all',
|
|
476
|
+
...query,
|
|
477
|
+
})) {
|
|
404
478
|
if (value === undefined || value === null) continue;
|
|
405
479
|
queryParams.set(key, value.toString());
|
|
406
480
|
}
|
|
407
481
|
|
|
408
482
|
return await this.fetch<
|
|
409
|
-
paths['/
|
|
410
|
-
>(`/
|
|
483
|
+
paths['/v2/invoices']['get']['responses'][200]['content']['application/json']
|
|
484
|
+
>(`/v2/invoices?${queryParams.toString()}`);
|
|
411
485
|
}
|
|
412
486
|
|
|
413
487
|
async getInvoicesByCustomerId(
|
|
414
488
|
id: string,
|
|
415
|
-
filters?:
|
|
489
|
+
filters?: paths['/v2/invoices']['get']['parameters']['query']
|
|
416
490
|
) {
|
|
417
|
-
|
|
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()}`);
|
|
491
|
+
return await this.getInvoices({ ...filters, customer_id: id });
|
|
431
492
|
}
|
|
432
493
|
|
|
433
494
|
async getInvoiceDownload(id: string) {
|
|
@@ -441,21 +502,20 @@ export class HyperlineAPI {
|
|
|
441
502
|
}
|
|
442
503
|
|
|
443
504
|
async getAllInvoices(
|
|
444
|
-
items: paths['/
|
|
445
|
-
query:
|
|
505
|
+
items: paths['/v2/invoices']['get']['responses'][200]['content']['application/json']['data'] = [],
|
|
506
|
+
query: paths['/v2/invoices']['get']['parameters']['query'] = {}
|
|
446
507
|
): Promise<
|
|
447
|
-
paths['/
|
|
508
|
+
paths['/v2/invoices']['get']['responses'][200]['content']['application/json']['data']
|
|
448
509
|
> {
|
|
449
|
-
const
|
|
450
|
-
|
|
451
|
-
...query,
|
|
452
|
-
})) || {
|
|
453
|
-
data: [],
|
|
454
|
-
};
|
|
510
|
+
const page = await this.getInvoices(query);
|
|
511
|
+
if (!page) throw new Error('Invoice list not found');
|
|
455
512
|
|
|
456
|
-
return
|
|
457
|
-
? this.getAllInvoices([...items, ...data], {
|
|
458
|
-
|
|
513
|
+
return page.next_cursor
|
|
514
|
+
? this.getAllInvoices([...items, ...page.data], {
|
|
515
|
+
...query,
|
|
516
|
+
cursor: page.next_cursor,
|
|
517
|
+
})
|
|
518
|
+
: [...items, ...page.data];
|
|
459
519
|
}
|
|
460
520
|
|
|
461
521
|
async voidInvoice(
|
|
@@ -505,7 +565,7 @@ export class HyperlineAPI {
|
|
|
505
565
|
}
|
|
506
566
|
|
|
507
567
|
async createBillableEvent(data: CreateBillableEvent[]) {
|
|
508
|
-
if (!data.length) return { events_created: [] };
|
|
568
|
+
if (!data.length) return { events_created: [], events_failed: [] };
|
|
509
569
|
|
|
510
570
|
return await this.fetchIngest<BillableEvents>(
|
|
511
571
|
'/v1/events/batch',
|
package/src/const.ts
CHANGED
|
@@ -7,15 +7,20 @@ export const DEMO_TEST_PARTNER_TENANT_ID =
|
|
|
7
7
|
'0b6a7bd6-56cc-46b5-b2ab-1bd7d1e994fa';
|
|
8
8
|
export const XIMPLIFI_TENANT_ID = 'd6ab2bbe-e0fc-48d9-89a3-c3c05f4e79b5';
|
|
9
9
|
export const TEST_TENANT_ID = 'fa4d1a20-2f47-4021-9c72-c5a809be9c48';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
export const
|
|
10
|
+
export const BOBW_TENANT_ID = 'cb23b44b-c96b-4fc4-95aa-1bf71b1c0f92';
|
|
11
|
+
export const HOSTAWAY_PRODUCTION_TENANT_ID =
|
|
12
|
+
'4d1583e6-1c76-4bd2-bf8d-ca07d45eedb7';
|
|
13
|
+
export const HOSTAWAY_STAGING_TENANT_ID =
|
|
14
|
+
'10834664-606b-4b92-acb9-f3e454632f7a';
|
|
15
|
+
export const HOSTAWAY_COSELLING_TENANT_ID =
|
|
16
|
+
'636dcf13-7b34-4a7f-9b45-60bbb1380d15';
|
|
17
|
+
export const HOSTAWAY_PARTNER_TENANT_IDS = [
|
|
18
|
+
HOSTAWAY_STAGING_TENANT_ID,
|
|
19
|
+
HOSTAWAY_PRODUCTION_TENANT_ID,
|
|
20
|
+
HOSTAWAY_COSELLING_TENANT_ID,
|
|
21
|
+
];
|
|
14
22
|
|
|
15
23
|
// Coupons
|
|
16
|
-
export const XIMPLIFI_COUPON_ID = 'cou_MXCaik3r19_Xkn';
|
|
17
|
-
export const XIMPLIFI_DEV_COUPON_ID = 'cou_MGtxHbT8IR1gK9';
|
|
18
|
-
|
|
19
24
|
export const ACCOUNTING_PARTNER_COUPON_ID = 'cou_PJQ328MBAD9kxY';
|
|
20
25
|
export const ACCOUNTING_PARTNER_DEV_COUPON_ID = 'cou_y6UfaI2X1m34Qg';
|
|
21
26
|
|
|
@@ -40,32 +45,39 @@ export const HYPERLINE_PARTNER_TIER_COUPON_IDS = {
|
|
|
40
45
|
60: HYPERLINE_PARTNER_TIER_60_PERCENT_COUPON_ID,
|
|
41
46
|
} as const;
|
|
42
47
|
|
|
43
|
-
//
|
|
44
|
-
export const HYPERLINE_DEFAULT_PLAN_ID = 'plan_Dyb5P0ICZ6HsIT';
|
|
45
|
-
export const HYPERLINE_DEFAULT_DEV_PLAN_ID = 'plan_UTCVXFQnKD0oMS';
|
|
46
|
-
export const HYPERLINE_DEFAULT_PLAN_NAME = 'All-in-One VRP Subscription';
|
|
47
|
-
export const HYPERLINE_DEFAULT_PRODUCT_ID = 'itm_9NU7ffT14I4ris';
|
|
48
|
-
export const HYPERLINE_DEFAULT_MIN_AMOUNT = 12500;
|
|
48
|
+
// Subscription templates
|
|
49
49
|
|
|
50
|
-
export const
|
|
51
|
-
export const
|
|
52
|
-
'itm_wrrT0HuAZ8Tlh1';
|
|
50
|
+
export const DIRECT_PRICING_TEMPLATE_ID = 'subt_Knj7TspKQNAdgE';
|
|
51
|
+
export const DIRECT_PRICING_DEV_TEMPLATE_ID = 'subt_3kdBL2APUz94Vh';
|
|
53
52
|
|
|
54
|
-
export const
|
|
55
|
-
export const
|
|
56
|
-
export const HYPERLINE_PARTNER_PLAN_NAME = 'VRP Subscription - Partners';
|
|
57
|
-
export const HYPERLINE_PARTNER_PRODUCT_ID = 'itm_AJBM0_jPsrqizl';
|
|
53
|
+
export const PARTNER_CLIENT_PRICING_TEMPLATE_ID = 'subt_p0bWvUpe59ilo4';
|
|
54
|
+
export const PARTNER_CLIENT_PRICING_DEV_TEMPLATE_ID = 'subt_c972Mz800ZB3C8';
|
|
58
55
|
|
|
59
|
-
export const
|
|
60
|
-
export const
|
|
61
|
-
|
|
62
|
-
export const
|
|
63
|
-
export const
|
|
56
|
+
export const PARTNER_PRICING_TEMPLATE_ID = 'subt_qt58CknWGtPHxH';
|
|
57
|
+
export const PARTNER_PRICING_DEV_TEMPLATE_ID = 'subt_NIoe1kA3Xs4353';
|
|
58
|
+
|
|
59
|
+
export const HOSTAWAY_PRICING_TEMPLATE_ID = 'subt_gVo8boDZBZvsZk';
|
|
60
|
+
export const HOSTAWAY_PRICING_DEV_TEMPLATE_ID = 'subt_PkEm701PH78Bgn';
|
|
64
61
|
|
|
65
|
-
export const
|
|
66
|
-
export const
|
|
67
|
-
|
|
62
|
+
export const BOBW_PRICING_TEMPLATE_ID = 'subt_XpHcieqR2Exn4q';
|
|
63
|
+
export const BOBW_PRICING_DEV_TEMPLATE_ID = 'subt_yRmLj6Fdr5R9Sh';
|
|
64
|
+
|
|
65
|
+
export const SUBSCRIPTION_TEMPLATE_NAMES = [
|
|
66
|
+
'VRPlatform Subscription - Direct',
|
|
67
|
+
'VRPlatform Subscription - Partner Clients',
|
|
68
|
+
'VRPlatform Subscription - Partner',
|
|
69
|
+
'VRPlatform Subscription - Hostaway',
|
|
70
|
+
'VRPlatform Subscription - BobW',
|
|
71
|
+
] as const;
|
|
72
|
+
|
|
73
|
+
// Product identifiers retained only for historical invoice comparisons.
|
|
74
|
+
export const HYPERLINE_DEFAULT_PRODUCT_ID = 'itm_9NU7ffT14I4ris';
|
|
75
|
+
export const HYPERLINE_PARTNER_PRODUCT_ID = 'itm_AJBM0_jPsrqizl';
|
|
76
|
+
export const GL_HYPERLINE_DEFAULT_PRODUCT_ID = 'itm_NRTRBQ0fc8bFGW';
|
|
68
77
|
export const GL_HYPERLINE_PARTNER_PRODUCT_ID = 'itm_bE4y9lhrZ6Qtsf';
|
|
78
|
+
export const HYPERLINE_NEW_PRICING_DIRECT_PRODUCT_ID = 'itm_VeR6AJi9OyUQN2';
|
|
79
|
+
export const HYPERLINE_NEW_PRICING_PARTNER_CLIENT_PRODUCT_ID =
|
|
80
|
+
'itm_wrrT0HuAZ8Tlh1';
|
|
69
81
|
|
|
70
82
|
export const WEBHOOKS = {
|
|
71
83
|
'payment_method.created': 'hyperline_payment_method_created',
|
|
@@ -73,6 +85,7 @@ export const WEBHOOKS = {
|
|
|
73
85
|
'payment_method.deleted': 'hyperline_payment_method_deleted',
|
|
74
86
|
'payment_method.expired': 'hyperline_payment_method_expired',
|
|
75
87
|
'subscription.activated': 'hyperline_subscription_status_changed',
|
|
88
|
+
'subscription.updated': 'hyperline_subscription_status_changed',
|
|
76
89
|
'subscription.reactivated': 'hyperline_subscription_status_changed',
|
|
77
90
|
'subscription.cancellation_scheduled':
|
|
78
91
|
'hyperline_subscription_cancellation_scheduled',
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export class HyperlineError extends Error {
|
|
2
|
+
readonly retryAt?: Date;
|
|
3
|
+
|
|
4
|
+
constructor(
|
|
5
|
+
message: string,
|
|
6
|
+
readonly status: number,
|
|
7
|
+
retryAfter: string | null
|
|
8
|
+
) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = 'HyperlineError';
|
|
11
|
+
if (status !== 429 || retryAfter === null) return;
|
|
12
|
+
const seconds = Number(retryAfter);
|
|
13
|
+
if (!Number.isFinite(seconds) || seconds < 0)
|
|
14
|
+
throw new Error('Invalid Hyperline Retry-After header');
|
|
15
|
+
this.retryAt = new Date(Date.now() + seconds * 1000);
|
|
16
|
+
}
|
|
17
|
+
}
|
package/src/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -14,18 +14,9 @@ export type HyperlinePayload<T extends HyperlineWebhook> = {
|
|
|
14
14
|
data: Record<string, any>;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
export type BillableEvent = {
|
|
18
|
-
record: {
|
|
19
|
-
id: '202408_cus_w_Q7M9bXcHMKB6';
|
|
20
|
-
amount: 26;
|
|
21
|
-
};
|
|
22
|
-
timestamp: '2024-08-07T07:35:20.307Z';
|
|
23
|
-
event_type: 'api_call';
|
|
24
|
-
customer_id: 'cus_w_Q7M9bXcHMKB6';
|
|
25
|
-
};
|
|
26
|
-
|
|
27
17
|
export type BillableEvents = {
|
|
28
|
-
events_created:
|
|
18
|
+
events_created: CreateBillableEvent[];
|
|
19
|
+
events_failed: unknown[];
|
|
29
20
|
};
|
|
30
21
|
|
|
31
22
|
export type CreateBillableEvent = {
|
|
@@ -39,5 +30,6 @@ export type CreateBillableEvent = {
|
|
|
39
30
|
amount?: number;
|
|
40
31
|
amountGl?: number;
|
|
41
32
|
amountNonGl?: number;
|
|
33
|
+
amountAll?: number;
|
|
42
34
|
};
|
|
43
35
|
};
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
GL_HYPERLINE_DEFAULT_DEV_PLAN_ID,
|
|
3
|
-
GL_HYPERLINE_DEFAULT_MIN_AMOUNT,
|
|
4
|
-
GL_HYPERLINE_DEFAULT_PLAN_ID,
|
|
5
|
-
HYPERLINE_DEFAULT_DEV_PLAN_ID,
|
|
6
|
-
HYPERLINE_DEFAULT_MIN_AMOUNT,
|
|
7
|
-
HYPERLINE_DEFAULT_PLAN_ID,
|
|
8
|
-
} from '../const';
|
|
9
1
|
import { useHyperlineByToken } from '../hooks';
|
|
10
2
|
import type { HyperlineEntities } from '../types';
|
|
11
3
|
|
|
@@ -19,22 +11,22 @@ export async function calculateMrr(
|
|
|
19
11
|
liveBilling?: boolean
|
|
20
12
|
): Promise<{
|
|
21
13
|
customer_id: string;
|
|
22
|
-
plan_id: string | null;
|
|
23
|
-
products?: unknown[];
|
|
24
14
|
coupons?: (
|
|
25
15
|
| {
|
|
26
16
|
type: 'amount';
|
|
27
|
-
repeat: '
|
|
17
|
+
repeat: HyperlineEntities['Coupon']['repeat'];
|
|
28
18
|
discount_amount: number;
|
|
29
19
|
}
|
|
30
20
|
| {
|
|
31
21
|
type: 'percent';
|
|
32
|
-
repeat: '
|
|
22
|
+
repeat: HyperlineEntities['Coupon']['repeat'];
|
|
33
23
|
discount_percent: number;
|
|
34
24
|
}
|
|
35
25
|
)[];
|
|
36
26
|
phases?: {
|
|
37
27
|
type: HyperlineEntities['SubscriptionDetails']['phases'][number]['type'];
|
|
28
|
+
status?: HyperlineEntities['SubscriptionDetails']['phases'][number]['status'];
|
|
29
|
+
do_not_invoice_phase: boolean;
|
|
38
30
|
products: {
|
|
39
31
|
min_amount?: number | null;
|
|
40
32
|
max_amount?: number | null;
|
|
@@ -62,24 +54,27 @@ export async function calculateMrr(
|
|
|
62
54
|
} | null>;
|
|
63
55
|
getInvoicesByCustomerId(id: string): Promise<{
|
|
64
56
|
data: {
|
|
65
|
-
|
|
57
|
+
issued_at: string;
|
|
66
58
|
total_amount: number;
|
|
67
59
|
}[];
|
|
68
60
|
} | null>;
|
|
69
61
|
} = useHyperlineByToken(process.env.HYPERLINE_TOKEN!),
|
|
70
62
|
includeLiveBilling = false
|
|
71
63
|
) {
|
|
72
|
-
const
|
|
64
|
+
const subscription = await hyperline.getSubscription(
|
|
73
65
|
subscriptionId,
|
|
74
66
|
includeLiveBilling
|
|
75
67
|
);
|
|
76
|
-
if (!
|
|
77
|
-
|
|
78
|
-
const { products, phases, ...subscription } = _subscription;
|
|
68
|
+
if (!subscription) throw new Error('Subscription not found');
|
|
79
69
|
|
|
80
70
|
let mrr = 0;
|
|
81
71
|
const activeListings = _activeListings || 0;
|
|
82
|
-
const phase = phases?.find(
|
|
72
|
+
const phase = subscription.phases?.find(
|
|
73
|
+
(x) =>
|
|
74
|
+
x.type === 'standard' &&
|
|
75
|
+
x.status !== 'finished' &&
|
|
76
|
+
!x.do_not_invoice_phase
|
|
77
|
+
);
|
|
83
78
|
const product = phase?.products?.[0];
|
|
84
79
|
const type = product?.prices?.[0].type;
|
|
85
80
|
if (type === 'volume') {
|
|
@@ -148,20 +143,7 @@ export async function calculateMrr(
|
|
|
148
143
|
? firstTier.amount *
|
|
149
144
|
((firstTier.to - firstTier.from) / firstTier.unit_count)
|
|
150
145
|
: 0;
|
|
151
|
-
const
|
|
152
|
-
GL_HYPERLINE_DEFAULT_PLAN_ID,
|
|
153
|
-
GL_HYPERLINE_DEFAULT_DEV_PLAN_ID,
|
|
154
|
-
].includes(subscription.plan_id!)
|
|
155
|
-
? GL_HYPERLINE_DEFAULT_MIN_AMOUNT
|
|
156
|
-
: [HYPERLINE_DEFAULT_PLAN_ID, HYPERLINE_DEFAULT_DEV_PLAN_ID].includes(
|
|
157
|
-
subscription.plan_id!
|
|
158
|
-
)
|
|
159
|
-
? HYPERLINE_DEFAULT_MIN_AMOUNT
|
|
160
|
-
: 0;
|
|
161
|
-
|
|
162
|
-
const min_amount =
|
|
163
|
-
((product_min_amount || plan_min_amount || inferred_min_amount) ?? 0) /
|
|
164
|
-
100;
|
|
146
|
+
const min_amount = (product_min_amount ?? inferred_min_amount) / 100;
|
|
165
147
|
mrr = Math.max(mrr, min_amount);
|
|
166
148
|
|
|
167
149
|
const max_amount = ((product_max_amount || 0) ?? 0) / 100;
|
|
@@ -185,7 +167,7 @@ export async function calculateMrr(
|
|
|
185
167
|
|
|
186
168
|
// sort invoices by date
|
|
187
169
|
const invoices = data
|
|
188
|
-
?.sort((a, b) => a.
|
|
170
|
+
?.sort((a, b) => a.issued_at.localeCompare(b.issued_at))
|
|
189
171
|
.reverse();
|
|
190
172
|
|
|
191
173
|
// get the last invoice
|
package/src/utils/index.ts
CHANGED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { DEMO_TEST_PARTNER_TENANT_ID, TEST_TENANT_ID } from '../const';
|
|
2
|
-
export const isTestingTeam = (tenant) => tenant.id === TEST_TENANT_ID ||
|
|
3
|
-
[tenant.id, tenant.partnerId, tenant.billingPartnerId].includes(DEMO_TEST_PARTNER_TENANT_ID);
|
|
4
|
-
//# sourceMappingURL=isTestingTeam.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"isTestingTeam.js","sourceRoot":"src/","sources":["utils/isTestingTeam.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAEvE,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,MAI7B,EAAE,EAAE,CACH,MAAM,CAAC,EAAE,KAAK,cAAc;IAC5B,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,gBAAgB,CAAC,CAAC,QAAQ,CAC7D,2BAA2B,CAC5B,CAAC","sourcesContent":["import { DEMO_TEST_PARTNER_TENANT_ID, TEST_TENANT_ID } from '../const';\n\nexport const isTestingTeam = (tenant: {\n id?: string;\n partnerId?: string | null;\n billingPartnerId?: string | null;\n}) =>\n tenant.id === TEST_TENANT_ID ||\n [tenant.id, tenant.partnerId, tenant.billingPartnerId].includes(\n DEMO_TEST_PARTNER_TENANT_ID\n );\n"]}
|