@vrplatform/hyperline-client 1.0.24 → 1.0.26
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/client.d.ts +215 -0
- package/build/main/client.js +87 -16
- package/build/main/client.js.map +1 -1
- package/build/main/const.d.ts +17 -0
- package/build/main/const.js +17 -0
- package/build/main/const.js.map +1 -1
- package/build/main/src/_types.generated.d.ts +22310 -0
- package/build/main/src/_types.generated.js +6 -0
- package/build/main/src/_types.generated.js.map +1 -0
- package/build/main/src/client.d.ts +1706 -0
- package/build/main/src/client.js +294 -0
- package/build/main/src/client.js.map +1 -0
- package/build/main/src/const.d.ts +73 -0
- package/build/main/src/const.js +257 -0
- package/build/main/src/const.js.map +1 -0
- package/build/main/src/hooks.d.ts +10 -0
- package/build/main/src/hooks.js +13 -0
- package/build/main/src/hooks.js.map +1 -0
- package/build/main/src/index.d.ts +5 -0
- package/build/main/src/index.js +6 -0
- package/build/main/src/index.js.map +1 -0
- package/build/main/src/types.d.ts +35 -0
- package/build/main/src/types.js +2 -0
- package/build/main/src/types.js.map +1 -0
- package/build/main/src/utils/arrayBufferToBase64.d.ts +1 -0
- package/build/main/src/utils/arrayBufferToBase64.js +9 -0
- package/build/main/src/utils/arrayBufferToBase64.js.map +1 -0
- package/build/main/src/utils/calculateMrr.d.ts +45 -0
- package/build/main/src/utils/calculateMrr.js +107 -0
- package/build/main/src/utils/calculateMrr.js.map +1 -0
- package/build/main/src/utils/getMrrDiffReasons.d.ts +15 -0
- package/build/main/src/utils/getMrrDiffReasons.js +217 -0
- package/build/main/src/utils/getMrrDiffReasons.js.map +1 -0
- package/build/main/src/utils/index.d.ts +3 -0
- package/build/main/src/utils/index.js +4 -0
- package/build/main/src/utils/index.js.map +1 -0
- package/build/main/tsconfig.main.tsbuildinfo +1 -1
- package/build/main/utils/arrayBufferToBase64.js +5 -2
- package/build/main/utils/arrayBufferToBase64.js.map +1 -1
- package/build/main/utils/calculateMrr.d.ts +45 -0
- package/build/main/utils/calculateMrr.js +107 -0
- package/build/main/utils/calculateMrr.js.map +1 -0
- package/build/main/utils/index.d.ts +1 -0
- package/build/main/utils/index.js +1 -0
- package/build/main/utils/index.js.map +1 -1
- package/build/module/client.d.ts +215 -0
- package/build/module/client.js +87 -16
- package/build/module/client.js.map +1 -1
- package/build/module/const.d.ts +17 -0
- package/build/module/const.js +17 -0
- package/build/module/const.js.map +1 -1
- package/build/module/src/_types.generated.d.ts +22310 -0
- package/build/module/src/_types.generated.js +6 -0
- package/build/module/src/_types.generated.js.map +1 -0
- package/build/module/src/client.d.ts +1706 -0
- package/build/module/src/client.js +296 -0
- package/build/module/src/client.js.map +1 -0
- package/build/module/src/const.d.ts +73 -0
- package/build/module/src/const.js +257 -0
- package/build/module/src/const.js.map +1 -0
- package/build/module/src/hooks.d.ts +10 -0
- package/build/module/src/hooks.js +13 -0
- package/build/module/src/hooks.js.map +1 -0
- package/build/module/src/index.d.ts +5 -0
- package/build/module/src/index.js +6 -0
- package/build/module/src/index.js.map +1 -0
- package/build/module/src/types.d.ts +35 -0
- package/build/module/src/types.js +2 -0
- package/build/module/src/types.js.map +1 -0
- package/build/module/src/utils/arrayBufferToBase64.d.ts +1 -0
- package/build/module/src/utils/arrayBufferToBase64.js +9 -0
- package/build/module/src/utils/arrayBufferToBase64.js.map +1 -0
- package/build/module/src/utils/calculateMrr.d.ts +45 -0
- package/build/module/src/utils/calculateMrr.js +107 -0
- package/build/module/src/utils/calculateMrr.js.map +1 -0
- package/build/module/src/utils/getMrrDiffReasons.d.ts +15 -0
- package/build/module/src/utils/getMrrDiffReasons.js +217 -0
- package/build/module/src/utils/getMrrDiffReasons.js.map +1 -0
- package/build/module/src/utils/index.d.ts +3 -0
- package/build/module/src/utils/index.js +4 -0
- package/build/module/src/utils/index.js.map +1 -0
- package/build/module/tsconfig.esm.tsbuildinfo +1 -1
- package/build/module/utils/arrayBufferToBase64.js +5 -2
- package/build/module/utils/arrayBufferToBase64.js.map +1 -1
- package/build/module/utils/calculateMrr.d.ts +45 -0
- package/build/module/utils/calculateMrr.js +107 -0
- package/build/module/utils/calculateMrr.js.map +1 -0
- package/build/module/utils/index.d.ts +1 -0
- package/build/module/utils/index.js +1 -0
- package/build/module/utils/index.js.map +1 -1
- package/package.json +19 -18
- package/src/client.ts +117 -22
- package/src/const.ts +21 -0
- package/src/utils/arrayBufferToBase64.ts +9 -2
- package/src/utils/calculateMrr.ts +206 -0
- package/src/utils/index.ts +1 -0
package/src/client.ts
CHANGED
|
@@ -25,6 +25,7 @@ export class HyperlineAPI {
|
|
|
25
25
|
): Promise<T | null> {
|
|
26
26
|
if (data) options = { body: JSON.stringify(data), ...(options || {}) };
|
|
27
27
|
|
|
28
|
+
const method = (options?.method || 'GET').toUpperCase();
|
|
28
29
|
const sandbox = this.headers.Authorization.includes('test')
|
|
29
30
|
? 'sandbox.'
|
|
30
31
|
: '';
|
|
@@ -35,9 +36,33 @@ export class HyperlineAPI {
|
|
|
35
36
|
headers: { ...this.headers, ...options?.headers },
|
|
36
37
|
});
|
|
37
38
|
|
|
38
|
-
if (!result
|
|
39
|
+
if (!result?.ok) {
|
|
39
40
|
const status = result?.status;
|
|
40
41
|
const statusText = result?.statusText;
|
|
42
|
+
let content: any = null;
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const rawBody = await result.text();
|
|
46
|
+
const trimmedBody = rawBody.trim();
|
|
47
|
+
|
|
48
|
+
if (trimmedBody) {
|
|
49
|
+
try {
|
|
50
|
+
content = JSON.parse(trimmedBody);
|
|
51
|
+
} catch (_) {
|
|
52
|
+
content = rawBody;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
} catch (_) {
|
|
56
|
+
// ignore
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const message =
|
|
60
|
+
(content && typeof content === 'object' && 'message' in content
|
|
61
|
+
? (content as any).message
|
|
62
|
+
: null) ||
|
|
63
|
+
(typeof content === 'string' ? content : null) ||
|
|
64
|
+
(content ? JSON.stringify(content) : null) ||
|
|
65
|
+
'Unknown error';
|
|
41
66
|
|
|
42
67
|
if (status === 429) {
|
|
43
68
|
// get difference between rate limit reset and now
|
|
@@ -48,17 +73,18 @@ export class HyperlineAPI {
|
|
|
48
73
|
await new Promise((resolve) => setTimeout(resolve, diff));
|
|
49
74
|
|
|
50
75
|
return await this.fetchBasic<T>(path, options, data);
|
|
51
|
-
} else if (status === 404) {
|
|
76
|
+
} else if (status === 404 && method === 'GET') {
|
|
52
77
|
return null;
|
|
53
78
|
} else {
|
|
54
|
-
const content: any = await result.json();
|
|
55
79
|
throw new Error(
|
|
56
|
-
`Hyperline Error ${status} (${statusText}) in ${path} for ${
|
|
80
|
+
`Hyperline Error ${status} (${statusText}) in ${path} for ${method}: ${message}`
|
|
57
81
|
);
|
|
58
82
|
}
|
|
59
83
|
} else if (result.headers.get('content-type')?.includes('application/pdf'))
|
|
60
84
|
return arrayBufferToBase64(await result.arrayBuffer()) as T;
|
|
61
85
|
|
|
86
|
+
if (result.status === 204) return null;
|
|
87
|
+
|
|
62
88
|
return (await result.json()) as T;
|
|
63
89
|
}
|
|
64
90
|
|
|
@@ -78,6 +104,25 @@ export class HyperlineAPI {
|
|
|
78
104
|
return await this.fetchBasic(`ingest.hyperline.co${path}`, options, data);
|
|
79
105
|
}
|
|
80
106
|
|
|
107
|
+
// INVOICING ENTITY
|
|
108
|
+
|
|
109
|
+
async getInvoicingEntities() {
|
|
110
|
+
return await this.fetch<
|
|
111
|
+
paths['/v1/invoicing-entities']['get']['responses'][200]['content']['application/json']
|
|
112
|
+
>('/v1/invoicing-entities');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async getInvoicingEntity(id?: string) {
|
|
116
|
+
if (!id) {
|
|
117
|
+
this.logger.error('Missing invoicing entity ID', { id });
|
|
118
|
+
throw new Error('Missing invoicing entity ID');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return await this.fetch<
|
|
122
|
+
paths['/v1/invoicing-entities/{id}']['get']['responses'][200]['content']['application/json']
|
|
123
|
+
>(`/v1/invoicing-entities/${id}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
81
126
|
// CUSTOMER
|
|
82
127
|
|
|
83
128
|
async getCustomer(id?: string) {
|
|
@@ -109,14 +154,18 @@ export class HyperlineAPI {
|
|
|
109
154
|
take = 100,
|
|
110
155
|
query: operations['getCustomers']['parameters']['query'] = {}
|
|
111
156
|
) {
|
|
157
|
+
const queryParams = new URLSearchParams();
|
|
158
|
+
queryParams.set('take', take.toString());
|
|
159
|
+
queryParams.set('skip', skip.toString());
|
|
160
|
+
|
|
161
|
+
for (const [key, value] of Object.entries(query)) {
|
|
162
|
+
if (value === undefined || value === null) continue;
|
|
163
|
+
queryParams.set(key, value.toString());
|
|
164
|
+
}
|
|
165
|
+
|
|
112
166
|
return await this.fetch<
|
|
113
167
|
paths['/v1/customers']['get']['responses'][200]['content']['application/json']
|
|
114
|
-
>(
|
|
115
|
-
`/v1/customers?take=${take}&skip=${skip}&${Object.entries(query)
|
|
116
|
-
.filter(([_, value]) => value !== undefined)
|
|
117
|
-
.map(([key, value]) => `&${key}=${value}`)
|
|
118
|
-
.join('')}`
|
|
119
|
-
);
|
|
168
|
+
>(`/v1/customers?${queryParams.toString()}`);
|
|
120
169
|
}
|
|
121
170
|
|
|
122
171
|
async getAllCustomers(
|
|
@@ -222,14 +271,18 @@ export class HyperlineAPI {
|
|
|
222
271
|
status: 'all',
|
|
223
272
|
}
|
|
224
273
|
) {
|
|
274
|
+
const queryParams = new URLSearchParams();
|
|
275
|
+
queryParams.set('take', take.toString());
|
|
276
|
+
queryParams.set('skip', skip.toString());
|
|
277
|
+
|
|
278
|
+
for (const [key, value] of Object.entries(query)) {
|
|
279
|
+
if (value === undefined || value === null) continue;
|
|
280
|
+
queryParams.set(key, value.toString());
|
|
281
|
+
}
|
|
282
|
+
|
|
225
283
|
return await this.fetch<
|
|
226
284
|
paths['/v2/subscriptions']['get']['responses'][200]['content']['application/json']
|
|
227
|
-
>(
|
|
228
|
-
`/v2/subscriptions?take=${take}&skip=${skip}${Object.entries(query)
|
|
229
|
-
.filter(([_, value]) => value !== undefined)
|
|
230
|
-
.map(([key, value]) => `&${key}=${value}`)
|
|
231
|
-
.join('')}`
|
|
232
|
-
);
|
|
285
|
+
>(`/v2/subscriptions?${queryParams.toString()}`);
|
|
233
286
|
}
|
|
234
287
|
|
|
235
288
|
async getAllSubscriptions(
|
|
@@ -343,14 +396,18 @@ export class HyperlineAPI {
|
|
|
343
396
|
take = 100,
|
|
344
397
|
query: operations['getInvoices']['parameters']['query'] = { status: 'all' }
|
|
345
398
|
) {
|
|
399
|
+
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)) {
|
|
404
|
+
if (value === undefined || value === null) continue;
|
|
405
|
+
queryParams.set(key, value.toString());
|
|
406
|
+
}
|
|
407
|
+
|
|
346
408
|
return await this.fetch<
|
|
347
409
|
paths['/v1/invoices']['get']['responses'][200]['content']['application/json']
|
|
348
|
-
>(
|
|
349
|
-
`/v1/invoices?take=${take}&skip=${skip}&${Object.entries(query)
|
|
350
|
-
.filter(([_, value]) => value !== undefined)
|
|
351
|
-
.map(([key, value]) => `&${key}=${value}`)
|
|
352
|
-
.join('')}`
|
|
353
|
-
);
|
|
410
|
+
>(`/v1/invoices?${queryParams.toString()}`);
|
|
354
411
|
}
|
|
355
412
|
|
|
356
413
|
async getInvoicesByCustomerId(
|
|
@@ -401,8 +458,46 @@ export class HyperlineAPI {
|
|
|
401
458
|
: [...items, ...data];
|
|
402
459
|
}
|
|
403
460
|
|
|
461
|
+
async voidInvoice(
|
|
462
|
+
id: string,
|
|
463
|
+
data: NonNullable<
|
|
464
|
+
paths['/v1/invoices/{id}/void']['post']['requestBody']
|
|
465
|
+
>['content']['application/json'] = { send_email: false }
|
|
466
|
+
) {
|
|
467
|
+
if (!id) {
|
|
468
|
+
this.logger.error('Missing invoice ID', { id });
|
|
469
|
+
throw new Error('Missing invoice ID');
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return await this.fetch<
|
|
473
|
+
paths['/v1/invoices/{id}/void']['post']['responses'][200]['content']['application/json']
|
|
474
|
+
>(`/v1/invoices/${id}/void`, { method: 'POST' }, data);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
async deleteInvoice(id: string) {
|
|
478
|
+
if (!id) {
|
|
479
|
+
this.logger.error('Missing invoice ID', { id });
|
|
480
|
+
throw new Error('Missing invoice ID');
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return await this.fetch<
|
|
484
|
+
paths['/v1/invoices/{id}']['delete']['responses'][204]['content']
|
|
485
|
+
>(`/v1/invoices/${id}`, { method: 'DELETE' });
|
|
486
|
+
}
|
|
487
|
+
|
|
404
488
|
// OTHERS
|
|
405
489
|
|
|
490
|
+
async getCustomerPaymentMethods(customerId: string) {
|
|
491
|
+
if (!customerId) {
|
|
492
|
+
this.logger.error('Missing customer ID', { customerId });
|
|
493
|
+
throw new Error('Missing customer ID');
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return await this.fetch<
|
|
497
|
+
paths['/v1/customers/{id}/payment-methods']['get']['responses'][200]['content']['application/json']
|
|
498
|
+
>(`/v1/customers/${customerId}/payment-methods`);
|
|
499
|
+
}
|
|
500
|
+
|
|
406
501
|
async getPaymentMethod(customerId: string, paymentMethodId: string) {
|
|
407
502
|
return await this.fetch<
|
|
408
503
|
paths['/v1/customers/{id}/payment-methods/{paymentMethodId}']['get']['responses'][200]['content']['application/json']
|
package/src/const.ts
CHANGED
|
@@ -24,6 +24,22 @@ export const VRTRUST_HALF_PRICE_DEV_COUPON_ID = 'cou_tHLiRdwQ6xGqKA'; // 50% off
|
|
|
24
24
|
export const VRTRUST_FREE_PRICE_COUPON_ID = 'cou_JGiLite8_nFtnX'; // 100% off
|
|
25
25
|
export const VRTRUST_FREE_PRICE_DEV_COUPON_ID = 'cou_N4GNqSSLEFscto'; // 100% off
|
|
26
26
|
|
|
27
|
+
export const HYPERLINE_PARTNER_TIER_10_PERCENT_COUPON_ID = 'cou_56kMhdo4fYNs7c';
|
|
28
|
+
export const HYPERLINE_PARTNER_TIER_20_PERCENT_COUPON_ID = 'cou_ExjGVUcu15XKeq';
|
|
29
|
+
export const HYPERLINE_PARTNER_TIER_30_PERCENT_COUPON_ID = 'cou_PJI6mXZHOyoWlx';
|
|
30
|
+
export const HYPERLINE_PARTNER_TIER_40_PERCENT_COUPON_ID = 'cou_gnYic7Uu8zHJX9';
|
|
31
|
+
export const HYPERLINE_PARTNER_TIER_50_PERCENT_COUPON_ID = 'cou_1saVofWCoJsVyz';
|
|
32
|
+
export const HYPERLINE_PARTNER_TIER_60_PERCENT_COUPON_ID = 'cou_tJI9hmHkQXjXOp';
|
|
33
|
+
|
|
34
|
+
export const HYPERLINE_PARTNER_TIER_COUPON_IDS = {
|
|
35
|
+
10: HYPERLINE_PARTNER_TIER_10_PERCENT_COUPON_ID,
|
|
36
|
+
20: HYPERLINE_PARTNER_TIER_20_PERCENT_COUPON_ID,
|
|
37
|
+
30: HYPERLINE_PARTNER_TIER_30_PERCENT_COUPON_ID,
|
|
38
|
+
40: HYPERLINE_PARTNER_TIER_40_PERCENT_COUPON_ID,
|
|
39
|
+
50: HYPERLINE_PARTNER_TIER_50_PERCENT_COUPON_ID,
|
|
40
|
+
60: HYPERLINE_PARTNER_TIER_60_PERCENT_COUPON_ID,
|
|
41
|
+
} as const;
|
|
42
|
+
|
|
27
43
|
// Plans
|
|
28
44
|
export const HYPERLINE_DEFAULT_PLAN_ID = 'plan_Dyb5P0ICZ6HsIT';
|
|
29
45
|
export const HYPERLINE_DEFAULT_DEV_PLAN_ID = 'plan_UTCVXFQnKD0oMS';
|
|
@@ -31,6 +47,10 @@ export const HYPERLINE_DEFAULT_PLAN_NAME = 'All-in-One VRP Subscription';
|
|
|
31
47
|
export const HYPERLINE_DEFAULT_PRODUCT_ID = 'itm_9NU7ffT14I4ris';
|
|
32
48
|
export const HYPERLINE_DEFAULT_MIN_AMOUNT = 12500;
|
|
33
49
|
|
|
50
|
+
export const HYPERLINE_NEW_PRICING_DIRECT_PRODUCT_ID = 'itm_VeR6AJi9OyUQN2';
|
|
51
|
+
export const HYPERLINE_NEW_PRICING_PARTNER_CLIENT_PRODUCT_ID =
|
|
52
|
+
'itm_wrrT0HuAZ8Tlh1';
|
|
53
|
+
|
|
34
54
|
export const HYPERLINE_PARTNER_PLAN_ID = 'plan_vkYJcCz7z0XD0N';
|
|
35
55
|
export const HYPERLINE_PARTNER_DEV_PLAN_ID = 'plan_8qtj8T4j0W3oiS';
|
|
36
56
|
export const HYPERLINE_PARTNER_PLAN_NAME = 'VRP Subscription - Partners';
|
|
@@ -51,6 +71,7 @@ export const WEBHOOKS = {
|
|
|
51
71
|
'payment_method.created': 'hyperline_payment_method_created',
|
|
52
72
|
'payment_method.errored': 'hyperline_payment_method_errored',
|
|
53
73
|
'payment_method.deleted': 'hyperline_payment_method_deleted',
|
|
74
|
+
'payment_method.expired': 'hyperline_payment_method_expired',
|
|
54
75
|
'subscription.activated': 'hyperline_subscription_status_changed',
|
|
55
76
|
'subscription.reactivated': 'hyperline_subscription_status_changed',
|
|
56
77
|
'subscription.cancellation_scheduled':
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
export function arrayBufferToBase64(arrayBuffer: ArrayBuffer) {
|
|
2
|
+
const binaryChunks: string[] = [];
|
|
2
3
|
const uint8Array = new Uint8Array(arrayBuffer);
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
// Avoid passing the full PDF as function arguments, which overflows worker stacks on larger files.
|
|
6
|
+
for (let index = 0; index < uint8Array.length; index += 0x8000)
|
|
7
|
+
binaryChunks.push(
|
|
8
|
+
String.fromCharCode(...uint8Array.subarray(index, index + 0x8000))
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
return btoa(binaryChunks.join(''));
|
|
5
12
|
}
|
|
@@ -0,0 +1,206 @@
|
|
|
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
|
+
import { useHyperlineByToken } from '../hooks';
|
|
10
|
+
|
|
11
|
+
export async function calculateMrr(
|
|
12
|
+
subscriptionId: string,
|
|
13
|
+
estimated = false,
|
|
14
|
+
_activeListings?: number,
|
|
15
|
+
hyperline: {
|
|
16
|
+
getSubscription(
|
|
17
|
+
id: string,
|
|
18
|
+
liveBilling?: boolean
|
|
19
|
+
): Promise<{
|
|
20
|
+
customer_id: string;
|
|
21
|
+
plan_id: string | null;
|
|
22
|
+
products?: unknown[];
|
|
23
|
+
coupons?: (
|
|
24
|
+
| {
|
|
25
|
+
type: 'amount';
|
|
26
|
+
repeat: 'once' | 'forever' | 'custom';
|
|
27
|
+
discount_amount: number;
|
|
28
|
+
}
|
|
29
|
+
| {
|
|
30
|
+
type: 'percent';
|
|
31
|
+
repeat: 'once' | 'forever' | 'custom';
|
|
32
|
+
discount_percent: number;
|
|
33
|
+
}
|
|
34
|
+
)[];
|
|
35
|
+
phases?: {
|
|
36
|
+
type: 'setup' | 'trial' | 'standard';
|
|
37
|
+
status: 'pending' | 'active' | 'finished';
|
|
38
|
+
products: {
|
|
39
|
+
min_amount?: number | null;
|
|
40
|
+
max_amount?: number | null;
|
|
41
|
+
prices: (
|
|
42
|
+
| {
|
|
43
|
+
type: 'volume';
|
|
44
|
+
amount: number;
|
|
45
|
+
from: number;
|
|
46
|
+
to: number | null;
|
|
47
|
+
unit_count: number;
|
|
48
|
+
}
|
|
49
|
+
| {
|
|
50
|
+
type:
|
|
51
|
+
| 'bps'
|
|
52
|
+
| 'bulk'
|
|
53
|
+
| 'bundle'
|
|
54
|
+
| 'fee'
|
|
55
|
+
| 'flat_fee'
|
|
56
|
+
| 'packaged';
|
|
57
|
+
}
|
|
58
|
+
)[];
|
|
59
|
+
}[];
|
|
60
|
+
}[];
|
|
61
|
+
live_billing?: { total_amount: number };
|
|
62
|
+
} | null>;
|
|
63
|
+
getInvoicesByCustomerId(id: string): Promise<{
|
|
64
|
+
data: {
|
|
65
|
+
emitted_at: string;
|
|
66
|
+
total_amount: number;
|
|
67
|
+
}[];
|
|
68
|
+
} | null>;
|
|
69
|
+
} = useHyperlineByToken(process.env.HYPERLINE_TOKEN!),
|
|
70
|
+
includeLiveBilling = false
|
|
71
|
+
) {
|
|
72
|
+
const _subscription = await hyperline.getSubscription(
|
|
73
|
+
subscriptionId,
|
|
74
|
+
includeLiveBilling
|
|
75
|
+
);
|
|
76
|
+
if (!_subscription) throw new Error('Subscription not found');
|
|
77
|
+
|
|
78
|
+
const { products, phases, ...subscription } = _subscription;
|
|
79
|
+
|
|
80
|
+
let mrr = 0;
|
|
81
|
+
const activeListings = _activeListings || 0;
|
|
82
|
+
const phase = phases?.filter((x) =>
|
|
83
|
+
estimated
|
|
84
|
+
? x.type === 'standard'
|
|
85
|
+
: (x.type === 'trial' && x.status === 'active') || x.type === 'standard'
|
|
86
|
+
)?.[0];
|
|
87
|
+
const product = phase?.products?.[0];
|
|
88
|
+
const type = product?.prices?.[0].type;
|
|
89
|
+
if (type === 'volume') {
|
|
90
|
+
const volumePrices: Array<{
|
|
91
|
+
amount: number;
|
|
92
|
+
from: number;
|
|
93
|
+
to: number | null;
|
|
94
|
+
unit_count: number;
|
|
95
|
+
}> = [];
|
|
96
|
+
|
|
97
|
+
product?.prices?.forEach((price) => {
|
|
98
|
+
if (
|
|
99
|
+
price.type !== 'volume' ||
|
|
100
|
+
!('amount' in price) ||
|
|
101
|
+
!('from' in price) ||
|
|
102
|
+
!('to' in price) ||
|
|
103
|
+
!('unit_count' in price)
|
|
104
|
+
)
|
|
105
|
+
return;
|
|
106
|
+
|
|
107
|
+
volumePrices.push({
|
|
108
|
+
amount: price.amount,
|
|
109
|
+
from: price.from,
|
|
110
|
+
to: price.to,
|
|
111
|
+
unit_count: price.unit_count,
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
volumePrices.sort((a, b) => a.from - b.from);
|
|
115
|
+
|
|
116
|
+
mrr =
|
|
117
|
+
volumePrices.reduce((acc, price) => {
|
|
118
|
+
const listings =
|
|
119
|
+
price.to && activeListings >= price.to
|
|
120
|
+
? price.to - price.from
|
|
121
|
+
: activeListings >= price.from
|
|
122
|
+
? activeListings - price.from
|
|
123
|
+
: 0;
|
|
124
|
+
|
|
125
|
+
acc += (price.amount / 100) * (listings / price.unit_count);
|
|
126
|
+
|
|
127
|
+
return acc;
|
|
128
|
+
}, 0) || 0;
|
|
129
|
+
|
|
130
|
+
const firstTier = volumePrices[0];
|
|
131
|
+
const secondTier = volumePrices[1];
|
|
132
|
+
const product_min_amount =
|
|
133
|
+
product &&
|
|
134
|
+
'min_amount' in product &&
|
|
135
|
+
typeof product.min_amount === 'number'
|
|
136
|
+
? product.min_amount
|
|
137
|
+
: null;
|
|
138
|
+
const product_max_amount =
|
|
139
|
+
product &&
|
|
140
|
+
'max_amount' in product &&
|
|
141
|
+
typeof product.max_amount === 'number'
|
|
142
|
+
? product.max_amount
|
|
143
|
+
: null;
|
|
144
|
+
const inferred_min_amount =
|
|
145
|
+
firstTier &&
|
|
146
|
+
secondTier &&
|
|
147
|
+
firstTier.from === 0 &&
|
|
148
|
+
firstTier.to != null &&
|
|
149
|
+
secondTier.from === firstTier.to &&
|
|
150
|
+
firstTier.amount === secondTier.amount &&
|
|
151
|
+
firstTier.unit_count === secondTier.unit_count
|
|
152
|
+
? firstTier.amount *
|
|
153
|
+
((firstTier.to - firstTier.from) / firstTier.unit_count)
|
|
154
|
+
: 0;
|
|
155
|
+
const plan_min_amount = [
|
|
156
|
+
GL_HYPERLINE_DEFAULT_PLAN_ID,
|
|
157
|
+
GL_HYPERLINE_DEFAULT_DEV_PLAN_ID,
|
|
158
|
+
].includes(subscription.plan_id!)
|
|
159
|
+
? GL_HYPERLINE_DEFAULT_MIN_AMOUNT
|
|
160
|
+
: [HYPERLINE_DEFAULT_PLAN_ID, HYPERLINE_DEFAULT_DEV_PLAN_ID].includes(
|
|
161
|
+
subscription.plan_id!
|
|
162
|
+
)
|
|
163
|
+
? HYPERLINE_DEFAULT_MIN_AMOUNT
|
|
164
|
+
: 0;
|
|
165
|
+
|
|
166
|
+
const min_amount =
|
|
167
|
+
((product_min_amount || plan_min_amount || inferred_min_amount) ?? 0) /
|
|
168
|
+
100;
|
|
169
|
+
mrr = Math.max(mrr, min_amount);
|
|
170
|
+
|
|
171
|
+
const max_amount = ((product_max_amount || 0) ?? 0) / 100;
|
|
172
|
+
mrr = max_amount > 0 ? Math.min(mrr, max_amount) : mrr;
|
|
173
|
+
|
|
174
|
+
// apply discounts (only these who repeat forever if estimated is true)
|
|
175
|
+
(subscription.coupons || [])
|
|
176
|
+
.filter((coupon) => (estimated ? coupon.repeat === 'forever' : true))
|
|
177
|
+
.forEach((x) => {
|
|
178
|
+
if ('discount_amount' in x && x.discount_amount)
|
|
179
|
+
mrr -= x.discount_amount / 100;
|
|
180
|
+
else if ('discount_percent' in x && x.discount_percent)
|
|
181
|
+
mrr -= (x.discount_percent / 100) * mrr;
|
|
182
|
+
});
|
|
183
|
+
} else if (type === 'bps') {
|
|
184
|
+
const { data } = subscription?.customer_id
|
|
185
|
+
? (await hyperline.getInvoicesByCustomerId(subscription.customer_id)) || {
|
|
186
|
+
data: [],
|
|
187
|
+
}
|
|
188
|
+
: { data: [] };
|
|
189
|
+
|
|
190
|
+
// sort invoices by date
|
|
191
|
+
const invoices = data
|
|
192
|
+
?.sort((a, b) => a.emitted_at.localeCompare(b.emitted_at))
|
|
193
|
+
.reverse();
|
|
194
|
+
|
|
195
|
+
// get the last invoice
|
|
196
|
+
mrr = (invoices[0]?.total_amount || 0) / 100;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
mrr,
|
|
201
|
+
mrrAlt:
|
|
202
|
+
subscription.live_billing?.total_amount == null
|
|
203
|
+
? null
|
|
204
|
+
: subscription.live_billing.total_amount / 100,
|
|
205
|
+
};
|
|
206
|
+
}
|
package/src/utils/index.ts
CHANGED