@vrplatform/hyperline-client 1.0.24 → 1.0.25

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/package.json CHANGED
@@ -1,13 +1,18 @@
1
1
  {
2
2
  "name": "@vrplatform/hyperline-client",
3
- "version": "1.0.24",
4
- "main": "build/main/index.js",
3
+ "version": "1.0.25",
4
+ "main": "src/index.ts",
5
5
  "publishConfig": {
6
- "access": "public"
6
+ "access": "public",
7
+ "main": "build/main/index.js",
8
+ "typings": "build/main/index.d.ts",
9
+ "module": "build/module/index.js"
7
10
  },
8
11
  "files": [
9
12
  "build/main",
10
13
  "build/module",
14
+ "!build/**/src",
15
+ "!build/**/*.tsbuildinfo",
11
16
  "src",
12
17
  "!**/*.spec.*"
13
18
  ],
@@ -15,19 +20,8 @@
15
20
  "repository": "https://github.com/finalytic/core",
16
21
  "license": "UNLICENSED",
17
22
  "keywords": [],
18
- "engines": {
19
- "node": ">=14"
20
- },
21
- "dependencies": {
22
- "@vrplatform/log": "*",
23
- "@vrplatform/utils": "*",
24
- "@vrplatform/graphql": "*"
25
- },
26
- "devDependencies": {
27
- "openapi-typescript": "^7.10.1"
28
- },
29
23
  "scripts": {
30
- "pub": "bun run build && pnpm version patch --no-commit-hooks --no-git-tag-version --no-workspaces-update && pnpm publish --no-git-checks",
24
+ "pub": "bun run build && npm version patch --no-commit-hooks --no-git-tag-version --no-workspaces-update && bun publish --access public",
31
25
  "build": "bun build:main & bun build:module",
32
26
  "build:main": "tsc -b tsconfig.main.json",
33
27
  "build:module": "tsc -b tsconfig.esm.json",
@@ -36,6 +30,15 @@
36
30
  "generate:types": "openapi-typescript https://api.hyperline.co/openapi --output ./src/_types.generated.ts",
37
31
  "generate:sed": "sed -i '' 's/& (Record<string, never> | null)//g; s/& Record<string, never>//g' ./src/_types.generated.ts"
38
32
  },
39
- "typings": "build/main/index.d.ts",
40
- "module": "build/module/index.js"
41
- }
33
+ "engines": {
34
+ "node": ">=14"
35
+ },
36
+ "dependencies": {
37
+ "@vrplatform/log": "*",
38
+ "@vrplatform/utils": "*",
39
+ "@vrplatform/graphql": "*"
40
+ },
41
+ "devDependencies": {
42
+ "openapi-typescript": "^7.13.0"
43
+ }
44
+ }
package/src/client.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  import { utc } from '@vrplatform/utils';
2
2
  import type { operations, paths } from './_types.generated';
3
- import { BillableEvents, CreateBillableEvent } from './types';
3
+ import {
4
+ BillableEvents,
5
+ CreateBillableEvent,
6
+ HyperlineCreateSubscriptionPayload,
7
+ } from './types';
4
8
  import { arrayBufferToBase64 } from './utils';
5
9
 
6
10
  export type Logger = { error(message?: any, ...optionalParams: any[]): void };
@@ -25,6 +29,7 @@ export class HyperlineAPI {
25
29
  ): Promise<T | null> {
26
30
  if (data) options = { body: JSON.stringify(data), ...(options || {}) };
27
31
 
32
+ const method = (options?.method || 'GET').toUpperCase();
28
33
  const sandbox = this.headers.Authorization.includes('test')
29
34
  ? 'sandbox.'
30
35
  : '';
@@ -35,9 +40,33 @@ export class HyperlineAPI {
35
40
  headers: { ...this.headers, ...options?.headers },
36
41
  });
37
42
 
38
- if (!result || !result?.ok) {
43
+ if (!result?.ok) {
39
44
  const status = result?.status;
40
45
  const statusText = result?.statusText;
46
+ let content: any = null;
47
+
48
+ try {
49
+ const rawBody = await result.text();
50
+ const trimmedBody = rawBody.trim();
51
+
52
+ if (trimmedBody) {
53
+ try {
54
+ content = JSON.parse(trimmedBody);
55
+ } catch (_) {
56
+ content = rawBody;
57
+ }
58
+ }
59
+ } catch (_) {
60
+ // ignore
61
+ }
62
+
63
+ const message =
64
+ (content && typeof content === 'object' && 'message' in content
65
+ ? (content as any).message
66
+ : null) ||
67
+ (typeof content === 'string' ? content : null) ||
68
+ (content ? JSON.stringify(content) : null) ||
69
+ 'Unknown error';
41
70
 
42
71
  if (status === 429) {
43
72
  // get difference between rate limit reset and now
@@ -48,17 +77,18 @@ export class HyperlineAPI {
48
77
  await new Promise((resolve) => setTimeout(resolve, diff));
49
78
 
50
79
  return await this.fetchBasic<T>(path, options, data);
51
- } else if (status === 404) {
80
+ } else if (status === 404 && method === 'GET') {
52
81
  return null;
53
82
  } else {
54
- const content: any = await result.json();
55
83
  throw new Error(
56
- `Hyperline Error ${status} (${statusText}) in ${path} for ${options?.method || 'GET'}: ${content.message}`
84
+ `Hyperline Error ${status} (${statusText}) in ${path} for ${method}: ${message}`
57
85
  );
58
86
  }
59
87
  } else if (result.headers.get('content-type')?.includes('application/pdf'))
60
88
  return arrayBufferToBase64(await result.arrayBuffer()) as T;
61
89
 
90
+ if (result.status === 204) return null;
91
+
62
92
  return (await result.json()) as T;
63
93
  }
64
94
 
@@ -78,6 +108,25 @@ export class HyperlineAPI {
78
108
  return await this.fetchBasic(`ingest.hyperline.co${path}`, options, data);
79
109
  }
80
110
 
111
+ // INVOICING ENTITY
112
+
113
+ async getInvoicingEntities() {
114
+ return await this.fetch<
115
+ paths['/v1/invoicing-entities']['get']['responses'][200]['content']['application/json']
116
+ >('/v1/invoicing-entities');
117
+ }
118
+
119
+ async getInvoicingEntity(id?: string) {
120
+ if (!id) {
121
+ this.logger.error('Missing invoicing entity ID', { id });
122
+ throw new Error('Missing invoicing entity ID');
123
+ }
124
+
125
+ return await this.fetch<
126
+ paths['/v1/invoicing-entities/{id}']['get']['responses'][200]['content']['application/json']
127
+ >(`/v1/invoicing-entities/${id}`);
128
+ }
129
+
81
130
  // CUSTOMER
82
131
 
83
132
  async getCustomer(id?: string) {
@@ -109,14 +158,18 @@ export class HyperlineAPI {
109
158
  take = 100,
110
159
  query: operations['getCustomers']['parameters']['query'] = {}
111
160
  ) {
161
+ const queryParams = new URLSearchParams();
162
+ queryParams.set('take', take.toString());
163
+ queryParams.set('skip', skip.toString());
164
+
165
+ for (const [key, value] of Object.entries(query)) {
166
+ if (value === undefined || value === null) continue;
167
+ queryParams.set(key, value.toString());
168
+ }
169
+
112
170
  return await this.fetch<
113
171
  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
- );
172
+ >(`/v1/customers?${queryParams.toString()}`);
120
173
  }
121
174
 
122
175
  async getAllCustomers(
@@ -222,14 +275,18 @@ export class HyperlineAPI {
222
275
  status: 'all',
223
276
  }
224
277
  ) {
278
+ const queryParams = new URLSearchParams();
279
+ queryParams.set('take', take.toString());
280
+ queryParams.set('skip', skip.toString());
281
+
282
+ for (const [key, value] of Object.entries(query)) {
283
+ if (value === undefined || value === null) continue;
284
+ queryParams.set(key, value.toString());
285
+ }
286
+
225
287
  return await this.fetch<
226
288
  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
- );
289
+ >(`/v2/subscriptions?${queryParams.toString()}`);
233
290
  }
234
291
 
235
292
  async getAllSubscriptions(
@@ -249,11 +306,7 @@ export class HyperlineAPI {
249
306
  : [...items, ...data];
250
307
  }
251
308
 
252
- async createSubscription(
253
- data: NonNullable<
254
- paths['/v2/subscriptions']['post']['requestBody']
255
- >['content']['application/json']
256
- ) {
309
+ async createSubscription(data: HyperlineCreateSubscriptionPayload) {
257
310
  return await this.fetch<
258
311
  paths['/v2/subscriptions']['post']['responses'][201]['content']['application/json']
259
312
  >('/v2/subscriptions', { method: 'POST' }, data);
@@ -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,11 +47,29 @@ 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';
37
57
  export const HYPERLINE_PARTNER_PRODUCT_ID = 'itm_AJBM0_jPsrqizl';
38
58
 
59
+ export const HYPERLINE_PARTNER_CLIENT_SHADOW_TEMPLATE_ID =
60
+ 'subt_Ny9vQJcTga4LJs';
61
+ export const HYPERLINE_PARTNER_CLIENT_SHADOW_DEV_TEMPLATE_ID =
62
+ 'subt_yQxDuboHpmXNxW';
63
+ export const HYPERLINE_PARTNER_CLIENT_SHADOW_TEMPLATE_CONFIGURATION_ID =
64
+ 'subtc_ELFdZAOA8fHh7o';
65
+ export const HYPERLINE_PARTNER_CLIENT_SHADOW_DEV_TEMPLATE_CONFIGURATION_ID =
66
+ 'subtc_eeflfPiaYWaHSD';
67
+ export const HYPERLINE_PARTNER_CLIENT_SHADOW_PRODUCT_ID = 'itm_7zZNcdWyUf3VWx';
68
+ export const HYPERLINE_PARTNER_CLIENT_SHADOW_DEV_PRODUCT_ID =
69
+ 'itm_WBqFApJRVuVbYx';
70
+ export const HYPERLINE_PARTNER_CLIENT_SHADOW_TEMPLATE_NAME =
71
+ 'VRP Partner Client Shadow Subscription';
72
+
39
73
  export const GL_HYPERLINE_DEFAULT_PLAN_ID = 'plan_JXviWSpU5yuokD';
40
74
  export const GL_HYPERLINE_DEFAULT_DEV_PLAN_ID = 'plan_Nnozk8_TfYqTkj';
41
75
  export const GL_HYPERLINE_DEFAULT_PLAN_NAME = 'VRTrust Subscription';
@@ -51,6 +85,7 @@ export const WEBHOOKS = {
51
85
  'payment_method.created': 'hyperline_payment_method_created',
52
86
  'payment_method.errored': 'hyperline_payment_method_errored',
53
87
  'payment_method.deleted': 'hyperline_payment_method_deleted',
88
+ 'payment_method.expired': 'hyperline_payment_method_expired',
54
89
  'subscription.activated': 'hyperline_subscription_status_changed',
55
90
  'subscription.reactivated': 'hyperline_subscription_status_changed',
56
91
  'subscription.cancellation_scheduled':
package/src/types.ts CHANGED
@@ -14,6 +14,61 @@ export type HyperlinePayload<T extends HyperlineWebhook> = {
14
14
  data: Record<string, any>;
15
15
  };
16
16
 
17
+ export type HyperlineCreateSubscriptionPayload = {
18
+ customer_id: string;
19
+ invoicing_entity_id?: string | null;
20
+ activation_strategy?: 'start_date' | 'manually' | 'checkout';
21
+ starts_at?: string;
22
+ plan_id?: string;
23
+ template_id?: string;
24
+ template_configuration_id?: string;
25
+ trial?: {
26
+ end_strategy: 'manual' | 'end_date' | 'duration';
27
+ starts_at?: string;
28
+ ends_at?: string;
29
+ duration?: {
30
+ period: 'days' | 'weeks' | 'months' | 'years';
31
+ count: number;
32
+ };
33
+ };
34
+ cancellation_strategy?:
35
+ | 'refund_prorata'
36
+ | 'refund_custom'
37
+ | 'charge_prorata'
38
+ | 'charge_custom'
39
+ | 'end_of_period'
40
+ | 'do_nothing';
41
+ generate_draft_invoices?: boolean;
42
+ renew_automatically?: boolean;
43
+ contract_terms?: {
44
+ activation_strategy:
45
+ | 'start_date'
46
+ | 'immediately'
47
+ | 'manual'
48
+ | 'quote_signature'
49
+ | 'checkout';
50
+ end_strategy: 'end_date' | 'duration' | 'manual';
51
+ starts_at?: string;
52
+ duration?: {
53
+ period: 'days' | 'weeks' | 'months' | 'years';
54
+ count: number;
55
+ };
56
+ renew_automatically?: boolean;
57
+ renew_for_duration?: {
58
+ period: 'days' | 'weeks' | 'months' | 'years';
59
+ count: number;
60
+ };
61
+ };
62
+ billing_cycle_alignment?: 'calendar_period' | 'anniversary';
63
+ coupons?: {
64
+ id: string;
65
+ repeat: 'once' | 'forever' | 'custom';
66
+ expires_at?: string;
67
+ apply_at?: string;
68
+ product_ids?: string[];
69
+ }[];
70
+ };
71
+
17
72
  export type BillableEvent = {
18
73
  record: {
19
74
  id: '202408_cus_w_Q7M9bXcHMKB6';
@@ -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
  }
@@ -1 +0,0 @@
1
- {"root":["../../src/_types.generated.ts","../../src/client.ts","../../src/const.ts","../../src/hooks.ts","../../src/index.ts","../../src/types.ts","../../src/utils/arraybuffertobase64.ts","../../src/utils/getmrrdiffreasons.ts","../../src/utils/index.ts"],"version":"5.9.3"}
@@ -1 +0,0 @@
1
- {"root":["../../src/_types.generated.ts","../../src/client.ts","../../src/const.ts","../../src/hooks.ts","../../src/index.ts","../../src/types.ts","../../src/utils/arraybuffertobase64.ts","../../src/utils/getmrrdiffreasons.ts","../../src/utils/index.ts"],"version":"5.9.3"}