@vrplatform/hyperline-client 1.0.31 → 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.
Files changed (55) hide show
  1. package/build/main/client.d.ts +977 -88
  2. package/build/main/client.js +49 -2
  3. package/build/main/client.js.map +1 -1
  4. package/build/main/const.d.ts +18 -20
  5. package/build/main/const.js +30 -22
  6. package/build/main/const.js.map +1 -1
  7. package/build/main/hyperlineError.d.ts +5 -0
  8. package/build/main/hyperlineError.js +14 -0
  9. package/build/main/hyperlineError.js.map +1 -0
  10. package/build/main/index.d.ts +1 -0
  11. package/build/main/index.js +1 -0
  12. package/build/main/index.js.map +1 -1
  13. package/build/main/types.d.ts +2 -10
  14. package/build/main/types.js.map +1 -1
  15. package/build/main/utils/calculateMrr.d.ts +2 -2
  16. package/build/main/utils/calculateMrr.js +6 -15
  17. package/build/main/utils/calculateMrr.js.map +1 -1
  18. package/build/main/utils/index.d.ts +0 -1
  19. package/build/main/utils/index.js +0 -1
  20. package/build/main/utils/index.js.map +1 -1
  21. package/build/module/client.d.ts +977 -88
  22. package/build/module/client.js +49 -2
  23. package/build/module/client.js.map +1 -1
  24. package/build/module/const.d.ts +18 -20
  25. package/build/module/const.js +30 -22
  26. package/build/module/const.js.map +1 -1
  27. package/build/module/hyperlineError.d.ts +5 -0
  28. package/build/module/hyperlineError.js +16 -0
  29. package/build/module/hyperlineError.js.map +1 -0
  30. package/build/module/index.d.ts +1 -0
  31. package/build/module/index.js +1 -0
  32. package/build/module/index.js.map +1 -1
  33. package/build/module/types.d.ts +2 -10
  34. package/build/module/types.js.map +1 -1
  35. package/build/module/utils/calculateMrr.d.ts +2 -2
  36. package/build/module/utils/calculateMrr.js +6 -15
  37. package/build/module/utils/calculateMrr.js.map +1 -1
  38. package/build/module/utils/index.d.ts +0 -1
  39. package/build/module/utils/index.js +0 -1
  40. package/build/module/utils/index.js.map +1 -1
  41. package/package.json +1 -1
  42. package/src/client.ts +107 -6
  43. package/src/const.ts +40 -28
  44. package/src/hyperlineError.ts +17 -0
  45. package/src/index.ts +1 -0
  46. package/src/types.ts +2 -11
  47. package/src/utils/calculateMrr.ts +11 -30
  48. package/src/utils/index.ts +0 -1
  49. package/build/main/utils/isTestingTeam.d.ts +0 -5
  50. package/build/main/utils/isTestingTeam.js +0 -4
  51. package/build/main/utils/isTestingTeam.js.map +0 -1
  52. package/build/module/utils/isTestingTeam.d.ts +0 -5
  53. package/build/module/utils/isTestingTeam.js +0 -4
  54. package/build/module/utils/isTestingTeam.js.map +0 -1
  55. package/src/utils/isTestingTeam.ts +0 -11
@@ -1,13 +1,13 @@
1
- import { GL_HYPERLINE_DEFAULT_DEV_PLAN_ID, GL_HYPERLINE_DEFAULT_MIN_AMOUNT, GL_HYPERLINE_DEFAULT_PLAN_ID, HYPERLINE_DEFAULT_DEV_PLAN_ID, HYPERLINE_DEFAULT_MIN_AMOUNT, HYPERLINE_DEFAULT_PLAN_ID, } from '../const';
2
1
  import { useHyperlineByToken } from '../hooks';
3
2
  export async function calculateMrr(subscriptionId, estimated = false, _activeListings, hyperline = useHyperlineByToken(process.env.HYPERLINE_TOKEN), includeLiveBilling = false) {
4
- const _subscription = await hyperline.getSubscription(subscriptionId, includeLiveBilling);
5
- if (!_subscription)
3
+ const subscription = await hyperline.getSubscription(subscriptionId, includeLiveBilling);
4
+ if (!subscription)
6
5
  throw new Error('Subscription not found');
7
- const { products, phases, ...subscription } = _subscription;
8
6
  let mrr = 0;
9
7
  const activeListings = _activeListings || 0;
10
- const phase = phases?.find((x) => x.type === 'standard');
8
+ const phase = subscription.phases?.find((x) => x.type === 'standard' &&
9
+ x.status !== 'finished' &&
10
+ !x.do_not_invoice_phase);
11
11
  const product = phase?.products?.[0];
12
12
  const type = product?.prices?.[0].type;
13
13
  if (type === 'volume') {
@@ -59,16 +59,7 @@ export async function calculateMrr(subscriptionId, estimated = false, _activeLis
59
59
  ? firstTier.amount *
60
60
  ((firstTier.to - firstTier.from) / firstTier.unit_count)
61
61
  : 0;
62
- const plan_min_amount = [
63
- GL_HYPERLINE_DEFAULT_PLAN_ID,
64
- GL_HYPERLINE_DEFAULT_DEV_PLAN_ID,
65
- ].includes(subscription.plan_id)
66
- ? GL_HYPERLINE_DEFAULT_MIN_AMOUNT
67
- : [HYPERLINE_DEFAULT_PLAN_ID, HYPERLINE_DEFAULT_DEV_PLAN_ID].includes(subscription.plan_id)
68
- ? HYPERLINE_DEFAULT_MIN_AMOUNT
69
- : 0;
70
- const min_amount = ((product_min_amount || plan_min_amount || inferred_min_amount) ?? 0) /
71
- 100;
62
+ const min_amount = (product_min_amount ?? inferred_min_amount) / 100;
72
63
  mrr = Math.max(mrr, min_amount);
73
64
  const max_amount = ((product_max_amount || 0) ?? 0) / 100;
74
65
  mrr = max_amount > 0 ? Math.min(mrr, max_amount) : mrr;
@@ -1 +1 @@
1
- {"version":3,"file":"calculateMrr.js","sourceRoot":"src/","sources":["utils/calculateMrr.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gCAAgC,EAChC,+BAA+B,EAC/B,4BAA4B,EAC5B,6BAA6B,EAC7B,4BAA4B,EAC5B,yBAAyB,GAC1B,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAG/C,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,cAAsB,EACtB,SAAS,GAAG,KAAK,EACjB,eAAwB,EACxB,YAsDI,mBAAmB,CAAC,OAAO,CAAC,GAAG,CAAC,eAAgB,CAAC,EACrD,kBAAkB,GAAG,KAAK;IAE1B,MAAM,aAAa,GAAG,MAAM,SAAS,CAAC,eAAe,CACnD,cAAc,EACd,kBAAkB,CACnB,CAAC;IACF,IAAI,CAAC,aAAa;QAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAE9D,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,YAAY,EAAE,GAAG,aAAa,CAAC;IAE5D,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,MAAM,cAAc,GAAG,eAAe,IAAI,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC;IACzD,MAAM,OAAO,GAAG,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACvC,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,MAAM,YAAY,GAKb,EAAE,CAAC;QAER,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YACjC,IACE,KAAK,CAAC,IAAI,KAAK,QAAQ;gBACvB,CAAC,CAAC,QAAQ,IAAI,KAAK,CAAC;gBACpB,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC;gBAClB,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC;gBAChB,CAAC,CAAC,YAAY,IAAI,KAAK,CAAC;gBAExB,OAAO;YAET,YAAY,CAAC,IAAI,CAAC;gBAChB,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,EAAE,EAAE,KAAK,CAAC,EAAE;gBACZ,UAAU,EAAE,KAAK,CAAC,UAAU;aAC7B,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;QAE7C,GAAG;YACD,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;gBACjC,MAAM,QAAQ,GACZ,KAAK,CAAC,EAAE,IAAI,cAAc,IAAI,KAAK,CAAC,EAAE;oBACpC,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,IAAI;oBACvB,CAAC,CAAC,cAAc,IAAI,KAAK,CAAC,IAAI;wBAC5B,CAAC,CAAC,cAAc,GAAG,KAAK,CAAC,IAAI;wBAC7B,CAAC,CAAC,CAAC,CAAC;gBAEV,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,QAAQ,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC;gBAE5D,OAAO,GAAG,CAAC;YACb,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QAEb,MAAM,SAAS,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;QAClC,MAAM,UAAU,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,kBAAkB,GACtB,OAAO;YACP,YAAY,IAAI,OAAO;YACvB,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ;YACpC,CAAC,CAAC,OAAO,CAAC,UAAU;YACpB,CAAC,CAAC,IAAI,CAAC;QACX,MAAM,kBAAkB,GACtB,OAAO;YACP,YAAY,IAAI,OAAO;YACvB,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ;YACpC,CAAC,CAAC,OAAO,CAAC,UAAU;YACpB,CAAC,CAAC,IAAI,CAAC;QACX,MAAM,mBAAmB,GACvB,SAAS;YACT,UAAU;YACV,SAAS,CAAC,IAAI,KAAK,CAAC;YACpB,SAAS,CAAC,EAAE,IAAI,IAAI;YACpB,UAAU,CAAC,IAAI,KAAK,SAAS,CAAC,EAAE;YAChC,SAAS,CAAC,MAAM,KAAK,UAAU,CAAC,MAAM;YACtC,SAAS,CAAC,UAAU,KAAK,UAAU,CAAC,UAAU;YAC5C,CAAC,CAAC,SAAS,CAAC,MAAM;gBAChB,CAAC,CAAC,SAAS,CAAC,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,UAAU,CAAC;YAC1D,CAAC,CAAC,CAAC,CAAC;QACR,MAAM,eAAe,GAAG;YACtB,4BAA4B;YAC5B,gCAAgC;SACjC,CAAC,QAAQ,CAAC,YAAY,CAAC,OAAQ,CAAC;YAC/B,CAAC,CAAC,+BAA+B;YACjC,CAAC,CAAC,CAAC,yBAAyB,EAAE,6BAA6B,CAAC,CAAC,QAAQ,CAC/D,YAAY,CAAC,OAAQ,CACtB;gBACH,CAAC,CAAC,4BAA4B;gBAC9B,CAAC,CAAC,CAAC,CAAC;QAER,MAAM,UAAU,GACd,CAAC,CAAC,kBAAkB,IAAI,eAAe,IAAI,mBAAmB,CAAC,IAAI,CAAC,CAAC;YACrE,GAAG,CAAC;QACN,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QAEhC,MAAM,UAAU,GAAG,CAAC,CAAC,kBAAkB,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC;QAC1D,GAAG,GAAG,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAEvD,uEAAuE;QACvE,CAAC,YAAY,CAAC,OAAO,IAAI,EAAE,CAAC;aACzB,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;aACpE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;YACb,IAAI,iBAAiB,IAAI,CAAC,IAAI,CAAC,CAAC,eAAe;gBAC7C,GAAG,IAAI,CAAC,CAAC,eAAe,GAAG,GAAG,CAAC;iBAC5B,IAAI,kBAAkB,IAAI,CAAC,IAAI,CAAC,CAAC,gBAAgB;gBACpD,GAAG,IAAI,CAAC,CAAC,CAAC,gBAAgB,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;QAC5C,CAAC,CAAC,CAAC;IACP,CAAC;SAAM,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QAC1B,MAAM,EAAE,IAAI,EAAE,GAAG,YAAY,EAAE,WAAW;YACxC,CAAC,CAAC,CAAC,MAAM,SAAS,CAAC,uBAAuB,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,IAAI;gBACrE,IAAI,EAAE,EAAE;aACT;YACH,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QAEjB,wBAAwB;QACxB,MAAM,QAAQ,GAAG,IAAI;YACnB,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;aACvD,OAAO,EAAE,CAAC;QAEb,uBAAuB;QACvB,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,YAAY,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC;IAC/C,CAAC;IAED,OAAO;QACL,GAAG;QACH,MAAM,EACJ,YAAY,CAAC,YAAY,EAAE,YAAY,IAAI,IAAI;YAC7C,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,YAAY,CAAC,YAAY,CAAC,YAAY,GAAG,GAAG;KACnD,CAAC;AACJ,CAAC","sourcesContent":["import {\n GL_HYPERLINE_DEFAULT_DEV_PLAN_ID,\n GL_HYPERLINE_DEFAULT_MIN_AMOUNT,\n GL_HYPERLINE_DEFAULT_PLAN_ID,\n HYPERLINE_DEFAULT_DEV_PLAN_ID,\n HYPERLINE_DEFAULT_MIN_AMOUNT,\n HYPERLINE_DEFAULT_PLAN_ID,\n} from '../const';\nimport { useHyperlineByToken } from '../hooks';\nimport type { HyperlineEntities } from '../types';\n\nexport async function calculateMrr(\n subscriptionId: string,\n estimated = false,\n _activeListings?: number,\n hyperline: {\n getSubscription(\n id: string,\n liveBilling?: boolean\n ): Promise<{\n customer_id: string;\n // BIL-614: legacy minimums remain until the subscription migration is complete.\n plan_id: string | null;\n products?: unknown[];\n coupons?: (\n | {\n type: 'amount';\n repeat: HyperlineEntities['Coupon']['repeat'];\n discount_amount: number;\n }\n | {\n type: 'percent';\n repeat: HyperlineEntities['Coupon']['repeat'];\n discount_percent: number;\n }\n )[];\n phases?: {\n type: HyperlineEntities['SubscriptionDetails']['phases'][number]['type'];\n products: {\n min_amount?: number | null;\n max_amount?: number | null;\n prices: (\n | {\n type: 'volume';\n amount: number;\n from: number;\n to: number | null;\n unit_count: number;\n }\n | {\n type:\n | 'bps'\n | 'bulk'\n | 'bundle'\n | 'fee'\n | 'flat_fee'\n | 'packaged';\n }\n )[];\n }[];\n }[];\n live_billing?: { total_amount: number };\n } | null>;\n getInvoicesByCustomerId(id: string): Promise<{\n data: {\n issued_at: string;\n total_amount: number;\n }[];\n } | null>;\n } = useHyperlineByToken(process.env.HYPERLINE_TOKEN!),\n includeLiveBilling = false\n) {\n const _subscription = await hyperline.getSubscription(\n subscriptionId,\n includeLiveBilling\n );\n if (!_subscription) throw new Error('Subscription not found');\n\n const { products, phases, ...subscription } = _subscription;\n\n let mrr = 0;\n const activeListings = _activeListings || 0;\n const phase = phases?.find((x) => x.type === 'standard');\n const product = phase?.products?.[0];\n const type = product?.prices?.[0].type;\n if (type === 'volume') {\n const volumePrices: Array<{\n amount: number;\n from: number;\n to: number | null;\n unit_count: number;\n }> = [];\n\n product?.prices?.forEach((price) => {\n if (\n price.type !== 'volume' ||\n !('amount' in price) ||\n !('from' in price) ||\n !('to' in price) ||\n !('unit_count' in price)\n )\n return;\n\n volumePrices.push({\n amount: price.amount,\n from: price.from,\n to: price.to,\n unit_count: price.unit_count,\n });\n });\n volumePrices.sort((a, b) => a.from - b.from);\n\n mrr =\n volumePrices.reduce((acc, price) => {\n const listings =\n price.to && activeListings >= price.to\n ? price.to - price.from\n : activeListings >= price.from\n ? activeListings - price.from\n : 0;\n\n acc += (price.amount / 100) * (listings / price.unit_count);\n\n return acc;\n }, 0) || 0;\n\n const firstTier = volumePrices[0];\n const secondTier = volumePrices[1];\n const product_min_amount =\n product &&\n 'min_amount' in product &&\n typeof product.min_amount === 'number'\n ? product.min_amount\n : null;\n const product_max_amount =\n product &&\n 'max_amount' in product &&\n typeof product.max_amount === 'number'\n ? product.max_amount\n : null;\n const inferred_min_amount =\n firstTier &&\n secondTier &&\n firstTier.from === 0 &&\n firstTier.to != null &&\n secondTier.from === firstTier.to &&\n firstTier.amount === secondTier.amount &&\n firstTier.unit_count === secondTier.unit_count\n ? firstTier.amount *\n ((firstTier.to - firstTier.from) / firstTier.unit_count)\n : 0;\n const plan_min_amount = [\n GL_HYPERLINE_DEFAULT_PLAN_ID,\n GL_HYPERLINE_DEFAULT_DEV_PLAN_ID,\n ].includes(subscription.plan_id!)\n ? GL_HYPERLINE_DEFAULT_MIN_AMOUNT\n : [HYPERLINE_DEFAULT_PLAN_ID, HYPERLINE_DEFAULT_DEV_PLAN_ID].includes(\n subscription.plan_id!\n )\n ? HYPERLINE_DEFAULT_MIN_AMOUNT\n : 0;\n\n const min_amount =\n ((product_min_amount || plan_min_amount || inferred_min_amount) ?? 0) /\n 100;\n mrr = Math.max(mrr, min_amount);\n\n const max_amount = ((product_max_amount || 0) ?? 0) / 100;\n mrr = max_amount > 0 ? Math.min(mrr, max_amount) : mrr;\n\n // apply discounts (only these who repeat forever if estimated is true)\n (subscription.coupons || [])\n .filter((coupon) => (estimated ? coupon.repeat === 'forever' : true))\n .forEach((x) => {\n if ('discount_amount' in x && x.discount_amount)\n mrr -= x.discount_amount / 100;\n else if ('discount_percent' in x && x.discount_percent)\n mrr -= (x.discount_percent / 100) * mrr;\n });\n } else if (type === 'bps') {\n const { data } = subscription?.customer_id\n ? (await hyperline.getInvoicesByCustomerId(subscription.customer_id)) || {\n data: [],\n }\n : { data: [] };\n\n // sort invoices by date\n const invoices = data\n ?.sort((a, b) => a.issued_at.localeCompare(b.issued_at))\n .reverse();\n\n // get the last invoice\n mrr = (invoices[0]?.total_amount || 0) / 100;\n }\n\n return {\n mrr,\n mrrAlt:\n subscription.live_billing?.total_amount == null\n ? null\n : subscription.live_billing.total_amount / 100,\n };\n}\n"]}
1
+ {"version":3,"file":"calculateMrr.js","sourceRoot":"src/","sources":["utils/calculateMrr.ts"],"names":[],"mappings":"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","sourcesContent":["import { useHyperlineByToken } from '../hooks';\nimport type { HyperlineEntities } from '../types';\n\nexport async function calculateMrr(\n subscriptionId: string,\n estimated = false,\n _activeListings?: number,\n hyperline: {\n getSubscription(\n id: string,\n liveBilling?: boolean\n ): Promise<{\n customer_id: string;\n coupons?: (\n | {\n type: 'amount';\n repeat: HyperlineEntities['Coupon']['repeat'];\n discount_amount: number;\n }\n | {\n type: 'percent';\n repeat: HyperlineEntities['Coupon']['repeat'];\n discount_percent: number;\n }\n )[];\n phases?: {\n type: HyperlineEntities['SubscriptionDetails']['phases'][number]['type'];\n status?: HyperlineEntities['SubscriptionDetails']['phases'][number]['status'];\n do_not_invoice_phase: boolean;\n products: {\n min_amount?: number | null;\n max_amount?: number | null;\n prices: (\n | {\n type: 'volume';\n amount: number;\n from: number;\n to: number | null;\n unit_count: number;\n }\n | {\n type:\n | 'bps'\n | 'bulk'\n | 'bundle'\n | 'fee'\n | 'flat_fee'\n | 'packaged';\n }\n )[];\n }[];\n }[];\n live_billing?: { total_amount: number };\n } | null>;\n getInvoicesByCustomerId(id: string): Promise<{\n data: {\n issued_at: string;\n total_amount: number;\n }[];\n } | null>;\n } = useHyperlineByToken(process.env.HYPERLINE_TOKEN!),\n includeLiveBilling = false\n) {\n const subscription = await hyperline.getSubscription(\n subscriptionId,\n includeLiveBilling\n );\n if (!subscription) throw new Error('Subscription not found');\n\n let mrr = 0;\n const activeListings = _activeListings || 0;\n const phase = subscription.phases?.find(\n (x) =>\n x.type === 'standard' &&\n x.status !== 'finished' &&\n !x.do_not_invoice_phase\n );\n const product = phase?.products?.[0];\n const type = product?.prices?.[0].type;\n if (type === 'volume') {\n const volumePrices: Array<{\n amount: number;\n from: number;\n to: number | null;\n unit_count: number;\n }> = [];\n\n product?.prices?.forEach((price) => {\n if (\n price.type !== 'volume' ||\n !('amount' in price) ||\n !('from' in price) ||\n !('to' in price) ||\n !('unit_count' in price)\n )\n return;\n\n volumePrices.push({\n amount: price.amount,\n from: price.from,\n to: price.to,\n unit_count: price.unit_count,\n });\n });\n volumePrices.sort((a, b) => a.from - b.from);\n\n mrr =\n volumePrices.reduce((acc, price) => {\n const listings =\n price.to && activeListings >= price.to\n ? price.to - price.from\n : activeListings >= price.from\n ? activeListings - price.from\n : 0;\n\n acc += (price.amount / 100) * (listings / price.unit_count);\n\n return acc;\n }, 0) || 0;\n\n const firstTier = volumePrices[0];\n const secondTier = volumePrices[1];\n const product_min_amount =\n product &&\n 'min_amount' in product &&\n typeof product.min_amount === 'number'\n ? product.min_amount\n : null;\n const product_max_amount =\n product &&\n 'max_amount' in product &&\n typeof product.max_amount === 'number'\n ? product.max_amount\n : null;\n const inferred_min_amount =\n firstTier &&\n secondTier &&\n firstTier.from === 0 &&\n firstTier.to != null &&\n secondTier.from === firstTier.to &&\n firstTier.amount === secondTier.amount &&\n firstTier.unit_count === secondTier.unit_count\n ? firstTier.amount *\n ((firstTier.to - firstTier.from) / firstTier.unit_count)\n : 0;\n const min_amount = (product_min_amount ?? inferred_min_amount) / 100;\n mrr = Math.max(mrr, min_amount);\n\n const max_amount = ((product_max_amount || 0) ?? 0) / 100;\n mrr = max_amount > 0 ? Math.min(mrr, max_amount) : mrr;\n\n // apply discounts (only these who repeat forever if estimated is true)\n (subscription.coupons || [])\n .filter((coupon) => (estimated ? coupon.repeat === 'forever' : true))\n .forEach((x) => {\n if ('discount_amount' in x && x.discount_amount)\n mrr -= x.discount_amount / 100;\n else if ('discount_percent' in x && x.discount_percent)\n mrr -= (x.discount_percent / 100) * mrr;\n });\n } else if (type === 'bps') {\n const { data } = subscription?.customer_id\n ? (await hyperline.getInvoicesByCustomerId(subscription.customer_id)) || {\n data: [],\n }\n : { data: [] };\n\n // sort invoices by date\n const invoices = data\n ?.sort((a, b) => a.issued_at.localeCompare(b.issued_at))\n .reverse();\n\n // get the last invoice\n mrr = (invoices[0]?.total_amount || 0) / 100;\n }\n\n return {\n mrr,\n mrrAlt:\n subscription.live_billing?.total_amount == null\n ? null\n : subscription.live_billing.total_amount / 100,\n };\n}\n"]}
@@ -1,3 +1,2 @@
1
1
  export * from './arrayBufferToBase64';
2
2
  export * from './calculateMrr';
3
- export * from './isTestingTeam';
@@ -1,4 +1,3 @@
1
1
  export * from './arrayBufferToBase64';
2
2
  export * from './calculateMrr';
3
- export * from './isTestingTeam';
4
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"src/","sources":["utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC","sourcesContent":["export * from './arrayBufferToBase64';\nexport * from './calculateMrr';\nexport * from './isTestingTeam';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"src/","sources":["utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC","sourcesContent":["export * from './arrayBufferToBase64';\nexport * from './calculateMrr';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vrplatform/hyperline-client",
3
- "version": "1.0.31",
3
+ "version": "1.0.32",
4
4
  "main": "src/index.ts",
5
5
  "publishConfig": {
6
6
  "access": "public",
package/src/client.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { paths } from './_types.generated';
2
+ import { HyperlineError } from './hyperlineError';
2
3
  import { BillableEvents, CreateBillableEvent } from './types';
3
4
  import { arrayBufferToBase64 } from './utils';
4
5
 
@@ -66,11 +67,10 @@ export class HyperlineAPI {
66
67
  if (status === 404 && method === 'GET') {
67
68
  return null;
68
69
  } else {
69
- throw Object.assign(
70
- new Error(
71
- `Hyperline Error ${status} (${statusText}) in ${path} for ${method}: ${message}`
72
- ),
73
- { status }
70
+ throw new HyperlineError(
71
+ `Hyperline Error ${status} (${statusText}) in ${path} for ${method}: ${message}`,
72
+ status,
73
+ result.headers.get('Retry-After')
74
74
  );
75
75
  }
76
76
  } else if (result.headers.get('content-type')?.includes('application/pdf'))
@@ -118,6 +118,25 @@ export class HyperlineAPI {
118
118
 
119
119
  // CUSTOMER
120
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
+
121
140
  async getCustomer(id?: string) {
122
141
  if (!id) {
123
142
  this.logger.error('Missing customer ID', { id });
@@ -165,11 +184,14 @@ export class HyperlineAPI {
165
184
  ): Promise<
166
185
  paths['/v2/customers']['get']['responses'][200]['content']['application/json']['data']
167
186
  > {
187
+ await new Promise((resolve) => setTimeout(resolve, 300));
168
188
  const page = await this.getCustomers({
169
189
  status: 'all',
170
190
  ...query,
171
191
  });
172
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');
173
195
 
174
196
  return page.next_cursor
175
197
  ? this.getAllCustomers([...items, ...page.data], {
@@ -242,6 +264,39 @@ export class HyperlineAPI {
242
264
  >(`/v1/customers/${id}/portal`);
243
265
  }
244
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
+
245
300
  // SUBSCRIPTION
246
301
 
247
302
  async getSubscription(id: string, liveBilling = false) {
@@ -305,6 +360,12 @@ export class HyperlineAPI {
305
360
  >('/v2/subscriptions', { method: 'POST' }, data);
306
361
  }
307
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
+
308
369
  async updateSubscription(
309
370
  id: string,
310
371
  data: NonNullable<
@@ -327,6 +388,17 @@ export class HyperlineAPI {
327
388
  >(`/v1/subscriptions/${id}/update`, { method: 'POST' }, data);
328
389
  }
329
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
+
330
402
  async cancelSubscription(
331
403
  id: string,
332
404
  data: NonNullable<
@@ -354,6 +426,28 @@ export class HyperlineAPI {
354
426
 
355
427
  // INVOICE
356
428
 
429
+ async createInvoice(
430
+ data: NonNullable<
431
+ paths['/v1/invoices']['post']['requestBody']
432
+ >['content']['application/json']
433
+ ) {
434
+ return await this.fetch<
435
+ paths['/v1/invoices']['post']['responses'][201]['content']['application/json']
436
+ >('/v1/invoices', { method: 'POST' }, data);
437
+ }
438
+
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');
446
+ return await this.fetch<
447
+ paths['/v1/invoices/{id}']['patch']['responses'][200]['content']['application/json']
448
+ >(`/v1/invoices/${id}`, { method: 'PATCH' }, data);
449
+ }
450
+
357
451
  async getInvoice(id: string) {
358
452
  if (!id) {
359
453
  this.logger.error('Missing invoice ID', { id });
@@ -365,6 +459,13 @@ export class HyperlineAPI {
365
459
  >(`/v2/invoices/${id}`);
366
460
  }
367
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' });
467
+ }
468
+
368
469
  async getInvoices(
369
470
  query: paths['/v2/invoices']['get']['parameters']['query'] = {}
370
471
  ) {
@@ -464,7 +565,7 @@ export class HyperlineAPI {
464
565
  }
465
566
 
466
567
  async createBillableEvent(data: CreateBillableEvent[]) {
467
- if (!data.length) return { events_created: [] };
568
+ if (!data.length) return { events_created: [], events_failed: [] };
468
569
 
469
570
  return await this.fetchIngest<BillableEvents>(
470
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
- // Customers
12
- export const XIMPLIFI_CUSTOMER_ID = 'cus_HDUZawnaJbPa9p';
13
- export const XIMPLIFI_DEV_CUSTOMER_ID = 'cus_dNUGCETGYVu3ZV';
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
- // Plans
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 HYPERLINE_NEW_PRICING_DIRECT_PRODUCT_ID = 'itm_VeR6AJi9OyUQN2';
51
- export const HYPERLINE_NEW_PRICING_PARTNER_CLIENT_PRODUCT_ID =
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 HYPERLINE_PARTNER_PLAN_ID = 'plan_vkYJcCz7z0XD0N';
55
- export const HYPERLINE_PARTNER_DEV_PLAN_ID = 'plan_8qtj8T4j0W3oiS';
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 GL_HYPERLINE_DEFAULT_PLAN_ID = 'plan_JXviWSpU5yuokD';
60
- export const GL_HYPERLINE_DEFAULT_DEV_PLAN_ID = 'plan_Nnozk8_TfYqTkj';
61
- export const GL_HYPERLINE_DEFAULT_PLAN_NAME = 'VRTrust Subscription';
62
- export const GL_HYPERLINE_DEFAULT_PRODUCT_ID = 'itm_NRTRBQ0fc8bFGW';
63
- export const GL_HYPERLINE_DEFAULT_MIN_AMOUNT = 25000;
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 GL_HYPERLINE_PARTNER_PLAN_ID = 'plan_vyxckdvUi8X_mr';
66
- export const GL_HYPERLINE_PARTNER_DEV_PLAN_ID = 'plan_CVXQ47ErOWANhJ';
67
- export const GL_HYPERLINE_PARTNER_PLAN_NAME = 'VRTrust Subscription - Partners';
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',
@@ -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
@@ -1,5 +1,6 @@
1
1
  export * from './client';
2
2
  export * from './const';
3
3
  export * from './hooks';
4
+ export * from './hyperlineError';
4
5
  export * from './types';
5
6
  export * from './utils';
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: BillableEvent[];
18
+ events_created: CreateBillableEvent[];
19
+ events_failed: unknown[];
29
20
  };
30
21
 
31
22
  export type CreateBillableEvent = {
@@ -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,9 +11,6 @@ export async function calculateMrr(
19
11
  liveBilling?: boolean
20
12
  ): Promise<{
21
13
  customer_id: string;
22
- // BIL-614: legacy minimums remain until the subscription migration is complete.
23
- plan_id: string | null;
24
- products?: unknown[];
25
14
  coupons?: (
26
15
  | {
27
16
  type: 'amount';
@@ -36,6 +25,8 @@ export async function calculateMrr(
36
25
  )[];
37
26
  phases?: {
38
27
  type: HyperlineEntities['SubscriptionDetails']['phases'][number]['type'];
28
+ status?: HyperlineEntities['SubscriptionDetails']['phases'][number]['status'];
29
+ do_not_invoice_phase: boolean;
39
30
  products: {
40
31
  min_amount?: number | null;
41
32
  max_amount?: number | null;
@@ -70,17 +61,20 @@ export async function calculateMrr(
70
61
  } = useHyperlineByToken(process.env.HYPERLINE_TOKEN!),
71
62
  includeLiveBilling = false
72
63
  ) {
73
- const _subscription = await hyperline.getSubscription(
64
+ const subscription = await hyperline.getSubscription(
74
65
  subscriptionId,
75
66
  includeLiveBilling
76
67
  );
77
- if (!_subscription) throw new Error('Subscription not found');
78
-
79
- const { products, phases, ...subscription } = _subscription;
68
+ if (!subscription) throw new Error('Subscription not found');
80
69
 
81
70
  let mrr = 0;
82
71
  const activeListings = _activeListings || 0;
83
- const phase = phases?.find((x) => x.type === 'standard');
72
+ const phase = subscription.phases?.find(
73
+ (x) =>
74
+ x.type === 'standard' &&
75
+ x.status !== 'finished' &&
76
+ !x.do_not_invoice_phase
77
+ );
84
78
  const product = phase?.products?.[0];
85
79
  const type = product?.prices?.[0].type;
86
80
  if (type === 'volume') {
@@ -149,20 +143,7 @@ export async function calculateMrr(
149
143
  ? firstTier.amount *
150
144
  ((firstTier.to - firstTier.from) / firstTier.unit_count)
151
145
  : 0;
152
- const plan_min_amount = [
153
- GL_HYPERLINE_DEFAULT_PLAN_ID,
154
- GL_HYPERLINE_DEFAULT_DEV_PLAN_ID,
155
- ].includes(subscription.plan_id!)
156
- ? GL_HYPERLINE_DEFAULT_MIN_AMOUNT
157
- : [HYPERLINE_DEFAULT_PLAN_ID, HYPERLINE_DEFAULT_DEV_PLAN_ID].includes(
158
- subscription.plan_id!
159
- )
160
- ? HYPERLINE_DEFAULT_MIN_AMOUNT
161
- : 0;
162
-
163
- const min_amount =
164
- ((product_min_amount || plan_min_amount || inferred_min_amount) ?? 0) /
165
- 100;
146
+ const min_amount = (product_min_amount ?? inferred_min_amount) / 100;
166
147
  mrr = Math.max(mrr, min_amount);
167
148
 
168
149
  const max_amount = ((product_max_amount || 0) ?? 0) / 100;
@@ -1,3 +1,2 @@
1
1
  export * from './arrayBufferToBase64';
2
2
  export * from './calculateMrr';
3
- export * from './isTestingTeam';
@@ -1,5 +0,0 @@
1
- export declare const isTestingTeam: (tenant: {
2
- id?: string;
3
- partnerId?: string | null;
4
- billingPartnerId?: string | null;
5
- }) => boolean;
@@ -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"]}
@@ -1,5 +0,0 @@
1
- export declare const isTestingTeam: (tenant: {
2
- id?: string;
3
- partnerId?: string | null;
4
- billingPartnerId?: string | null;
5
- }) => boolean;
@@ -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"]}
@@ -1,11 +0,0 @@
1
- import { DEMO_TEST_PARTNER_TENANT_ID, TEST_TENANT_ID } from '../const';
2
-
3
- export const isTestingTeam = (tenant: {
4
- id?: string;
5
- partnerId?: string | null;
6
- billingPartnerId?: string | null;
7
- }) =>
8
- tenant.id === TEST_TENANT_ID ||
9
- [tenant.id, tenant.partnerId, tenant.billingPartnerId].includes(
10
- DEMO_TEST_PARTNER_TENANT_ID
11
- );