@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.
Files changed (96) hide show
  1. package/build/main/client.d.ts +215 -0
  2. package/build/main/client.js +87 -16
  3. package/build/main/client.js.map +1 -1
  4. package/build/main/const.d.ts +17 -0
  5. package/build/main/const.js +17 -0
  6. package/build/main/const.js.map +1 -1
  7. package/build/main/src/_types.generated.d.ts +22310 -0
  8. package/build/main/src/_types.generated.js +6 -0
  9. package/build/main/src/_types.generated.js.map +1 -0
  10. package/build/main/src/client.d.ts +1706 -0
  11. package/build/main/src/client.js +294 -0
  12. package/build/main/src/client.js.map +1 -0
  13. package/build/main/src/const.d.ts +73 -0
  14. package/build/main/src/const.js +257 -0
  15. package/build/main/src/const.js.map +1 -0
  16. package/build/main/src/hooks.d.ts +10 -0
  17. package/build/main/src/hooks.js +13 -0
  18. package/build/main/src/hooks.js.map +1 -0
  19. package/build/main/src/index.d.ts +5 -0
  20. package/build/main/src/index.js +6 -0
  21. package/build/main/src/index.js.map +1 -0
  22. package/build/main/src/types.d.ts +35 -0
  23. package/build/main/src/types.js +2 -0
  24. package/build/main/src/types.js.map +1 -0
  25. package/build/main/src/utils/arrayBufferToBase64.d.ts +1 -0
  26. package/build/main/src/utils/arrayBufferToBase64.js +9 -0
  27. package/build/main/src/utils/arrayBufferToBase64.js.map +1 -0
  28. package/build/main/src/utils/calculateMrr.d.ts +45 -0
  29. package/build/main/src/utils/calculateMrr.js +107 -0
  30. package/build/main/src/utils/calculateMrr.js.map +1 -0
  31. package/build/main/src/utils/getMrrDiffReasons.d.ts +15 -0
  32. package/build/main/src/utils/getMrrDiffReasons.js +217 -0
  33. package/build/main/src/utils/getMrrDiffReasons.js.map +1 -0
  34. package/build/main/src/utils/index.d.ts +3 -0
  35. package/build/main/src/utils/index.js +4 -0
  36. package/build/main/src/utils/index.js.map +1 -0
  37. package/build/main/tsconfig.main.tsbuildinfo +1 -1
  38. package/build/main/utils/arrayBufferToBase64.js +5 -2
  39. package/build/main/utils/arrayBufferToBase64.js.map +1 -1
  40. package/build/main/utils/calculateMrr.d.ts +45 -0
  41. package/build/main/utils/calculateMrr.js +107 -0
  42. package/build/main/utils/calculateMrr.js.map +1 -0
  43. package/build/main/utils/index.d.ts +1 -0
  44. package/build/main/utils/index.js +1 -0
  45. package/build/main/utils/index.js.map +1 -1
  46. package/build/module/client.d.ts +215 -0
  47. package/build/module/client.js +87 -16
  48. package/build/module/client.js.map +1 -1
  49. package/build/module/const.d.ts +17 -0
  50. package/build/module/const.js +17 -0
  51. package/build/module/const.js.map +1 -1
  52. package/build/module/src/_types.generated.d.ts +22310 -0
  53. package/build/module/src/_types.generated.js +6 -0
  54. package/build/module/src/_types.generated.js.map +1 -0
  55. package/build/module/src/client.d.ts +1706 -0
  56. package/build/module/src/client.js +296 -0
  57. package/build/module/src/client.js.map +1 -0
  58. package/build/module/src/const.d.ts +73 -0
  59. package/build/module/src/const.js +257 -0
  60. package/build/module/src/const.js.map +1 -0
  61. package/build/module/src/hooks.d.ts +10 -0
  62. package/build/module/src/hooks.js +13 -0
  63. package/build/module/src/hooks.js.map +1 -0
  64. package/build/module/src/index.d.ts +5 -0
  65. package/build/module/src/index.js +6 -0
  66. package/build/module/src/index.js.map +1 -0
  67. package/build/module/src/types.d.ts +35 -0
  68. package/build/module/src/types.js +2 -0
  69. package/build/module/src/types.js.map +1 -0
  70. package/build/module/src/utils/arrayBufferToBase64.d.ts +1 -0
  71. package/build/module/src/utils/arrayBufferToBase64.js +9 -0
  72. package/build/module/src/utils/arrayBufferToBase64.js.map +1 -0
  73. package/build/module/src/utils/calculateMrr.d.ts +45 -0
  74. package/build/module/src/utils/calculateMrr.js +107 -0
  75. package/build/module/src/utils/calculateMrr.js.map +1 -0
  76. package/build/module/src/utils/getMrrDiffReasons.d.ts +15 -0
  77. package/build/module/src/utils/getMrrDiffReasons.js +217 -0
  78. package/build/module/src/utils/getMrrDiffReasons.js.map +1 -0
  79. package/build/module/src/utils/index.d.ts +3 -0
  80. package/build/module/src/utils/index.js +4 -0
  81. package/build/module/src/utils/index.js.map +1 -0
  82. package/build/module/tsconfig.esm.tsbuildinfo +1 -1
  83. package/build/module/utils/arrayBufferToBase64.js +5 -2
  84. package/build/module/utils/arrayBufferToBase64.js.map +1 -1
  85. package/build/module/utils/calculateMrr.d.ts +45 -0
  86. package/build/module/utils/calculateMrr.js +107 -0
  87. package/build/module/utils/calculateMrr.js.map +1 -0
  88. package/build/module/utils/index.d.ts +1 -0
  89. package/build/module/utils/index.js +1 -0
  90. package/build/module/utils/index.js.map +1 -1
  91. package/package.json +19 -18
  92. package/src/client.ts +117 -22
  93. package/src/const.ts +21 -0
  94. package/src/utils/arrayBufferToBase64.ts +9 -2
  95. package/src/utils/calculateMrr.ts +206 -0
  96. 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 || !result?.ok) {
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 ${options?.method || 'GET'}: ${content.message}`
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
- const binaryString = String.fromCharCode.apply(null, uint8Array as any);
4
- return btoa(binaryString);
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
+ }
@@ -1,2 +1,3 @@
1
1
  export * from './arrayBufferToBase64';
2
+ export * from './calculateMrr';
2
3
  export * from './getMrrDiffReasons';