@stigg/react-sdk 4.4.0-beta.11 → 4.4.0-beta.12

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 (26) hide show
  1. package/dist/components/checkout/Checkout.d.ts +1 -1
  2. package/dist/components/checkout/CheckoutContainer.d.ts +4 -2
  3. package/dist/components/checkout/CheckoutProvider.d.ts +4 -2
  4. package/dist/components/checkout/hooks/useLoadCheckout.d.ts +3 -1
  5. package/dist/components/checkout/hooks/usePreviewSubscription.d.ts +8 -3
  6. package/dist/components/checkout/summary/CheckoutSummary.d.ts +1 -1
  7. package/dist/react-sdk.cjs.development.js +94 -46
  8. package/dist/react-sdk.cjs.development.js.map +1 -1
  9. package/dist/react-sdk.cjs.production.min.js +1 -1
  10. package/dist/react-sdk.cjs.production.min.js.map +1 -1
  11. package/dist/react-sdk.esm.js +94 -46
  12. package/dist/react-sdk.esm.js.map +1 -1
  13. package/dist/stories/mocks/checkout/consts.d.ts +11 -0
  14. package/dist/stories/mocks/checkout/mockCheckoutPreview.d.ts +2 -0
  15. package/dist/stories/mocks/checkout/mockCheckoutState.d.ts +2 -0
  16. package/package.json +1 -1
  17. package/src/components/checkout/Checkout.tsx +3 -1
  18. package/src/components/checkout/CheckoutContainer.tsx +21 -2
  19. package/src/components/checkout/CheckoutProvider.tsx +6 -2
  20. package/src/components/checkout/hooks/useLoadCheckout.ts +10 -2
  21. package/src/components/checkout/hooks/usePreviewSubscription.ts +15 -5
  22. package/src/components/checkout/summary/CheckoutSummary.tsx +2 -1
  23. package/src/stories/Checkout.stories.tsx +32 -6
  24. package/src/stories/mocks/checkout/consts.ts +15 -0
  25. package/src/stories/mocks/checkout/mockCheckoutPreview.ts +121 -0
  26. package/src/stories/mocks/checkout/mockCheckoutState.ts +206 -0
@@ -0,0 +1,11 @@
1
+ export declare const BASE_FEE_MONTHLY = 200;
2
+ export declare const BASE_FEE_YEARLY = 175;
3
+ export declare const TIERS: number[];
4
+ export declare const TIERS_PRICE_MONTHLY: number[];
5
+ export declare const TIERS_PRICE_YEARLY: number[];
6
+ export declare const PER_UNIT_PRICE_MONTHLY = 12;
7
+ export declare const PER_UNIT_PRICE_YEARLY = 10;
8
+ export declare const ADDON_PRICE_MONTHLY = 50;
9
+ export declare const ADDON_PRICE_YEARLY = 35;
10
+ export declare const STRIPE_MOCK_ACCOUNT_ID = "acct_1NnHoQG6EyqgvTaj";
11
+ export declare const STRIPE_MOCK_ACCOUNT_PK = "pk_test_51NnHoQG6EyqgvTajznajopWC01AozNtq7zgySeQ1qx4PH9TAXvMj0TnbZvYT3yOt46jbQAcCDs1EU2QKcfG8eEoO00tlW0Jp3r";
@@ -0,0 +1,2 @@
1
+ import { PreviewSubscription, SubscriptionPreview } from '../../../../../js-client-sdk';
2
+ export declare function mockPreviewSubscription(input: PreviewSubscription): SubscriptionPreview;
@@ -0,0 +1,2 @@
1
+ import { GetCheckoutState, GetCheckoutStateResults } from '../../../../../js-client-sdk';
2
+ export declare function mockCheckoutState(params: GetCheckoutState): GetCheckoutStateResults;
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.4.0-beta.11",
2
+ "version": "4.4.0-beta.12",
3
3
  "license": "MIT",
4
4
  "main": "dist/index.js",
5
5
  "typings": "dist/index.d.ts",
@@ -13,6 +13,7 @@ export const Checkout = ({
13
13
  billingCountryCode,
14
14
  billableFeatures,
15
15
  billingInformation,
16
+ onMockCheckoutState,
16
17
  ...containerProps
17
18
  }: CheckoutProps) => {
18
19
  return (
@@ -24,7 +25,8 @@ export const Checkout = ({
24
25
  preferredBillingPeriod={preferredBillingPeriod}
25
26
  billingCountryCode={billingCountryCode}
26
27
  billableFeatures={billableFeatures}
27
- billingInformation={billingInformation}>
28
+ billingInformation={billingInformation}
29
+ onMockCheckoutState={onMockCheckoutState}>
28
30
  <CheckoutContainer {...containerProps} />
29
31
  </CheckoutProvider>
30
32
  );
@@ -1,6 +1,13 @@
1
1
  import React from 'react';
2
2
  import { Elements } from '@stripe/react-stripe-js';
3
- import { ApplySubscription, BillingAddress, CheckoutStatePlan, PricingType } from '@stigg/js-client-sdk';
3
+ import {
4
+ PricingType,
5
+ BillingAddress,
6
+ ApplySubscription,
7
+ CheckoutStatePlan,
8
+ PreviewSubscription,
9
+ SubscriptionPreview,
10
+ } from '@stigg/js-client-sdk';
4
11
  import { CheckoutContent, CheckoutLayout, CheckoutPanel } from './CheckoutContainer.style';
5
12
  import { CheckoutProgressBar } from './progressBar/CheckoutProgressBar';
6
13
  import { CheckoutSummary, CheckoutSummarySkeleton } from './summary';
@@ -19,6 +26,9 @@ type StepProps = {
19
26
  content: React.ReactNode;
20
27
  };
21
28
 
29
+ type StripeClientSecret = { clientSecret: string };
30
+ type StripeManualMode = { mode: 'setup' | 'payment' | 'subscription'; currency: string };
31
+
22
32
  const getStepProps = (
23
33
  currentStep: CheckoutStep,
24
34
  { onBillingAddressChange }: Pick<CheckoutContainerProps, 'onBillingAddressChange'>,
@@ -41,6 +51,8 @@ export type OnCheckoutParams = { checkoutParams: ApplySubscription; checkoutActi
41
51
 
42
52
  export type OnCheckoutCompletedParams = { success: boolean; error?: string };
43
53
 
54
+ export type OnMockCheckoutPreviewCallback = (params: PreviewSubscription) => SubscriptionPreview;
55
+
44
56
  export type CheckoutContainerProps = {
45
57
  onCheckout?: (params: OnCheckoutParams) => Promise<CheckoutResult>;
46
58
  onCheckoutCompleted: (params: OnCheckoutCompletedParams) => Promise<void>;
@@ -48,6 +60,7 @@ export type CheckoutContainerProps = {
48
60
  onBillingAddressChange?: (params: { billingAddress: BillingAddress }) => Promise<void>;
49
61
  disablePromotionCode?: boolean;
50
62
  disableSuccessAnimation?: boolean;
63
+ onMockCheckoutPreview?: OnMockCheckoutPreviewCallback;
51
64
  };
52
65
 
53
66
  export function CheckoutContainer({
@@ -57,6 +70,7 @@ export function CheckoutContainer({
57
70
  onBillingAddressChange,
58
71
  disablePromotionCode,
59
72
  disableSuccessAnimation,
73
+ onMockCheckoutPreview,
60
74
  }: CheckoutContainerProps) {
61
75
  const { stripePromise, setupIntentClientSecret } = useStripeIntegration();
62
76
  const [{ stiggTheme, widgetState }] = useCheckoutContext();
@@ -93,11 +107,15 @@ export function CheckoutContainer({
93
107
  </>
94
108
  );
95
109
 
110
+ const stripeElementsMode: StripeClientSecret | StripeManualMode = setupIntentClientSecret
111
+ ? { clientSecret: setupIntentClientSecret }
112
+ : { mode: 'setup', currency: 'usd' };
113
+
96
114
  return (
97
115
  <Elements
98
116
  stripe={stripePromise}
99
117
  options={{
100
- clientSecret: setupIntentClientSecret,
118
+ ...stripeElementsMode,
101
119
  appearance: {
102
120
  theme: 'stripe',
103
121
  variables: {
@@ -121,6 +139,7 @@ export function CheckoutContainer({
121
139
  onCheckout={onCheckout}
122
140
  onCheckoutCompleted={onCheckoutCompleted}
123
141
  isFreeDowngrade={isFreeDowngrade}
142
+ onMockCheckoutPreview={onMockCheckoutPreview}
124
143
  />
125
144
  )}
126
145
  </CheckoutContent>
@@ -1,6 +1,6 @@
1
1
  import { produce } from 'immer';
2
2
  import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
3
- import { BillableFeature, BillingPeriod, GetCheckoutStateResults } from '@stigg/js-client-sdk';
3
+ import { BillableFeature, BillingPeriod, GetCheckoutState, GetCheckoutStateResults } from '@stigg/js-client-sdk';
4
4
  import { CustomizedTheme, SdkThemeProvider, useStiggTheme } from '../../theme/Theme';
5
5
  import { DeepPartial } from '../../types';
6
6
  import { mapCheckoutConfiguration } from '../common/mapExternalTheme';
@@ -71,6 +71,8 @@ const CheckoutContextProvider: React.FC<{ children: React.ReactNode; initialStat
71
71
  return <CheckoutContext.Provider value={[contextValue, setContextValue]}>{children}</CheckoutContext.Provider>;
72
72
  };
73
73
 
74
+ export type MockCheckoutStateCallback = (params: GetCheckoutState) => GetCheckoutStateResults;
75
+
74
76
  export type CheckoutProviderProps = {
75
77
  textOverrides?: DeepPartial<CheckoutLocalization>;
76
78
  theme?: DeepPartial<CheckoutTheme>;
@@ -80,6 +82,7 @@ export type CheckoutProviderProps = {
80
82
  billingCountryCode?: string;
81
83
  billableFeatures?: BillableFeature[];
82
84
  billingInformation?: BillingInformation;
85
+ onMockCheckoutState?: MockCheckoutStateCallback;
83
86
  };
84
87
 
85
88
  export function CheckoutProvider({
@@ -92,10 +95,11 @@ export function CheckoutProvider({
92
95
  planId,
93
96
  billingCountryCode,
94
97
  billingInformation,
98
+ onMockCheckoutState,
95
99
  }: {
96
100
  children: React.ReactNode;
97
101
  } & CheckoutProviderProps) {
98
- const { checkout, isLoading } = useLoadCheckout({ resourceId, planId, billingCountryCode });
102
+ const { checkout, isLoading } = useLoadCheckout({ resourceId, planId, billingCountryCode, onMockCheckoutState });
99
103
  const configuration: CustomizedTheme | undefined = checkout?.configuration
100
104
  ? mapCheckoutConfiguration(checkout.configuration)
101
105
  : undefined;
@@ -2,14 +2,16 @@ import { GetCheckoutStateResults } from '@stigg/js-client-sdk';
2
2
  import { useEffect, useState } from 'react';
3
3
  import logger from '../../../services/logger';
4
4
  import { useStiggContext } from '../../StiggProvider';
5
+ import { MockCheckoutStateCallback } from '../CheckoutProvider';
5
6
 
6
7
  type UseLoadCheckoutProps = {
7
8
  planId: string;
8
9
  resourceId?: string;
9
10
  billingCountryCode?: string;
11
+ onMockCheckoutState?: MockCheckoutStateCallback;
10
12
  };
11
13
 
12
- export function useLoadCheckout({ planId, resourceId, billingCountryCode }: UseLoadCheckoutProps) {
14
+ export function useLoadCheckout({ planId, resourceId, billingCountryCode, onMockCheckoutState }: UseLoadCheckoutProps) {
13
15
  const { stigg } = useStiggContext();
14
16
  const [isLoading, setIsLoading] = useState(true);
15
17
  const [checkout, setCheckout] = useState<GetCheckoutStateResults | null>();
@@ -26,11 +28,17 @@ export function useLoadCheckout({ planId, resourceId, billingCountryCode }: UseL
26
28
  }
27
29
  };
28
30
 
31
+ if (onMockCheckoutState) {
32
+ setIsLoading(false);
33
+ setCheckout(onMockCheckoutState({ planId, resourceId, billingCountryCode }));
34
+ return;
35
+ }
36
+
29
37
  if (stigg.isCustomerLoaded) {
30
38
  setIsLoading(true);
31
39
  void loadCheckout();
32
40
  }
33
- }, [stigg, stigg.isCustomerLoaded, resourceId, planId, billingCountryCode]);
41
+ }, [stigg, stigg.isCustomerLoaded, resourceId, planId, billingCountryCode, onMockCheckoutState]);
34
42
 
35
43
  return {
36
44
  checkout,
@@ -5,6 +5,7 @@ import { useStiggContext } from '../../StiggProvider';
5
5
  import { useCheckoutContext } from '../CheckoutProvider';
6
6
  import { useCheckoutModel } from './useCheckoutModel';
7
7
  import { SubscriptionState, useSubscriptionModel } from './useSubscriptionModel';
8
+ import { OnMockCheckoutPreviewCallback } from '../CheckoutContainer';
8
9
 
9
10
  function mapBillingInformation({
10
11
  billingAddress,
@@ -25,12 +26,17 @@ function mapBillingInformation({
25
26
  };
26
27
  }
27
28
 
29
+ type UsePreviewSubscriptionProps = {
30
+ onMockCheckoutPreview?: OnMockCheckoutPreviewCallback;
31
+ };
32
+
28
33
  export type PreviewSubscriptionProps = {
29
34
  customerId?: string;
30
35
  planId?: string;
31
36
  resourceId?: string;
32
37
  stigg: StiggClient;
33
- } & SubscriptionState;
38
+ } & SubscriptionState &
39
+ UsePreviewSubscriptionProps;
34
40
 
35
41
  const previewSubscription = async ({
36
42
  stigg,
@@ -44,6 +50,7 @@ const previewSubscription = async ({
44
50
  billingPeriod,
45
51
  billingAddress,
46
52
  taxPercentage,
53
+ onMockCheckoutPreview,
47
54
  }: PreviewSubscriptionProps) => {
48
55
  const estimateAddons = addons.map(({ addon, quantity }) => ({ addonId: addon.id, quantity }));
49
56
  let subscriptionPreview: SubscriptionPreview | null = null;
@@ -63,7 +70,9 @@ const previewSubscription = async ({
63
70
  ...mapBillingInformation({ billingAddress, taxPercentage }),
64
71
  };
65
72
 
66
- subscriptionPreview = await stigg.previewSubscription(previewSubscriptionProps);
73
+ subscriptionPreview = onMockCheckoutPreview
74
+ ? onMockCheckoutPreview(previewSubscriptionProps)
75
+ : await stigg.previewSubscription(previewSubscriptionProps);
67
76
  }
68
77
  } catch (error) {
69
78
  const [, errorMsg] = (error as any)?.message?.split('Error:') || [];
@@ -74,7 +83,7 @@ const previewSubscription = async ({
74
83
  return { subscriptionPreview, errorMessage };
75
84
  };
76
85
 
77
- export const usePreviewSubscriptionAction = () => {
86
+ export const usePreviewSubscriptionAction = ({ onMockCheckoutPreview }: UsePreviewSubscriptionProps = {}) => {
78
87
  const { stigg } = useStiggContext();
79
88
  const subscription = useSubscriptionModel();
80
89
  const [{ resourceId }] = useCheckoutContext();
@@ -99,6 +108,7 @@ export const usePreviewSubscriptionAction = () => {
99
108
  billingAddress: subscription.billingAddress,
100
109
  taxPercentage: subscription.taxPercentage,
101
110
  promotionCode: promotionCode ?? subscription.promotionCode,
111
+ onMockCheckoutPreview,
102
112
  });
103
113
  },
104
114
  [
@@ -122,11 +132,11 @@ export const usePreviewSubscriptionAction = () => {
122
132
 
123
133
  const SUBSCRIPTION_PREVIEW_DEBOUNCE_TIME = 500;
124
134
 
125
- export const usePreviewSubscription = () => {
135
+ export const usePreviewSubscription = ({ onMockCheckoutPreview }: UsePreviewSubscriptionProps = {}) => {
126
136
  const [subscriptionPreview, setSubscriptionPreview] = useState<SubscriptionPreview | null>(null);
127
137
  const [isFetchingSubscriptionPreview, setIsFetchingSubscriptionPreview] = useState(false);
128
138
 
129
- const { previewSubscriptionAction } = usePreviewSubscriptionAction();
139
+ const { previewSubscriptionAction } = usePreviewSubscriptionAction({ onMockCheckoutPreview });
130
140
 
131
141
  useEffect(() => {
132
142
  const estimateSubscription = async () => {
@@ -91,6 +91,7 @@ export const CheckoutSummary = ({
91
91
  disablePromotionCode,
92
92
  disableSuccessAnimation,
93
93
  isFreeDowngrade,
94
+ onMockCheckoutPreview,
94
95
  }: CheckoutContainerProps & { isFreeDowngrade: boolean }) => {
95
96
  const [isCheckoutCompletedSuccessfully, setIsCheckoutCompletedSuccessfully] = useState(false);
96
97
  const { setErrorMessage } = usePaymentStepModel();
@@ -105,7 +106,7 @@ export const CheckoutSummary = ({
105
106
  const [baseCharge] = baseCharges || [];
106
107
  const isLastStep = isFreeDowngrade || (progressBar.isCheckoutComplete && progressBar.isLastStep);
107
108
 
108
- const { subscriptionPreview, isFetchingSubscriptionPreview } = usePreviewSubscription();
109
+ const { subscriptionPreview, isFetchingSubscriptionPreview } = usePreviewSubscription({ onMockCheckoutPreview });
109
110
 
110
111
  const { handleSubmit, isLoading } = useSubmit({
111
112
  disableSuccessAnimation,
@@ -4,6 +4,8 @@ import { ComponentMeta, ComponentStory } from '@storybook/react';
4
4
  import { Checkout } from '../components/checkout';
5
5
  import { StiggProvider } from '../components/StiggProvider';
6
6
  import { defaultArgsWithCustomer } from './baseArgs';
7
+ import { mockCheckoutState } from './mocks/checkout/mockCheckoutState';
8
+ import { mockPreviewSubscription } from './mocks/checkout/mockCheckoutPreview';
7
9
 
8
10
  export default {
9
11
  title: 'Stigg React SDK/Checkout',
@@ -21,6 +23,18 @@ export default {
21
23
  </StiggProvider>
22
24
  ),
23
25
  ],
26
+ parameters: {
27
+ controls: {
28
+ exclude: [
29
+ 'onCheckout',
30
+ 'onCheckoutCompleted',
31
+ 'onChangePlan',
32
+ 'onBillingAddressChange',
33
+ 'onMockCheckoutState',
34
+ 'onMockCheckoutPreview',
35
+ ],
36
+ },
37
+ },
24
38
  } as ComponentMeta<typeof Checkout>;
25
39
 
26
40
  const Wrapper = styled.div`
@@ -43,11 +57,26 @@ const Template: ComponentStory<any> = (args) => (
43
57
  const { success, errorMessage } = await checkoutAction();
44
58
  return { success, errorMessage };
45
59
  }}
60
+ billableFeatures={[{ featureId: 'feature-seat', quantity: 30 }]}
46
61
  onChangePlan={({ currentPlan }) => {
47
62
  console.log('plan changed clicked!', { currentPlan });
48
63
  }}
49
- // disableSuccessAnimation
50
- // billingInformation={{ taxPercentage: 27 }}
64
+ onMockCheckoutState={
65
+ args.useMockData
66
+ ? (params) => {
67
+ console.log('mocking checkout state', params);
68
+ return mockCheckoutState(params);
69
+ }
70
+ : undefined
71
+ }
72
+ onMockCheckoutPreview={
73
+ args.useMockData
74
+ ? (params) => {
75
+ console.log('mocking checkout preview');
76
+ return mockPreviewSubscription(params);
77
+ }
78
+ : undefined
79
+ }
51
80
  />
52
81
  </Wrapper>
53
82
  );
@@ -56,8 +85,5 @@ export const DefaultCheckout = Template.bind({});
56
85
  DefaultCheckout.args = {
57
86
  ...defaultArgsWithCustomer,
58
87
  planId: 'plan-revvenu-essentials',
59
- // baseUri: 'http://localhost:4000',
60
- // apiKey: 'client-72b058a6-0f22-4c86-adce-bf266d12e12e:9f356ceb-c94c-42a4-9572-10b12824da81',
61
- baseUri: 'https://api-staging.stigg.io',
62
- apiKey: 'client-79584f52-7ef9-4c58-b9ac-5080acf492e4:71f2274c-100a-4fa4-8a43-48fa3b16c627',
88
+ useMockData: false,
63
89
  };
@@ -0,0 +1,15 @@
1
+ export const BASE_FEE_MONTHLY = 200;
2
+ export const BASE_FEE_YEARLY = 175;
3
+ export const TIERS = [1000, 10000, 50000];
4
+ export const TIERS_PRICE_MONTHLY = [10, 90, 480];
5
+ export const TIERS_PRICE_YEARLY = [8, 75, 350];
6
+
7
+ export const PER_UNIT_PRICE_MONTHLY = 12;
8
+ export const PER_UNIT_PRICE_YEARLY = 10;
9
+
10
+ export const ADDON_PRICE_MONTHLY = 50;
11
+ export const ADDON_PRICE_YEARLY = 35;
12
+
13
+ export const STRIPE_MOCK_ACCOUNT_ID = 'acct_1NnHoQG6EyqgvTaj';
14
+ export const STRIPE_MOCK_ACCOUNT_PK =
15
+ 'pk_test_51NnHoQG6EyqgvTajznajopWC01AozNtq7zgySeQ1qx4PH9TAXvMj0TnbZvYT3yOt46jbQAcCDs1EU2QKcfG8eEoO00tlW0Jp3r';
@@ -0,0 +1,121 @@
1
+ import moment from 'moment';
2
+ import {
3
+ BillingPeriod,
4
+ BillingModel,
5
+ Currency,
6
+ BillableFeature,
7
+ DateRange,
8
+ Money,
9
+ Plan,
10
+ PreviewSubscription,
11
+ SubscriptionEstimationAddon,
12
+ SubscriptionPreview,
13
+ } from '../../../../../js-client-sdk';
14
+ import { mockCheckoutState } from './mockCheckoutState';
15
+
16
+ const mockBillingPeriod = (period?: BillingPeriod): DateRange => {
17
+ return {
18
+ start: moment().toDate(),
19
+ end: moment()
20
+ .add(1, period === BillingPeriod.Monthly ? 'month' : 'year')
21
+ .toDate(),
22
+ };
23
+ };
24
+
25
+ const defaultPreviewSubscription = (): SubscriptionPreview => {
26
+ const defaultCost: Money = {
27
+ amount: 0,
28
+ currency: Currency.Usd,
29
+ };
30
+
31
+ return {
32
+ subTotal: defaultCost,
33
+ total: defaultCost,
34
+ totalExcludingTax: defaultCost,
35
+ billingPeriodRange: mockBillingPeriod(),
36
+ };
37
+ };
38
+
39
+ const flatFeeCost = (plan: Plan, billingPeriod: BillingPeriod | undefined) => {
40
+ const filteredPrice = plan.pricePoints?.find(
41
+ (price) => price.billingPeriod === billingPeriod && price.pricingModel === BillingModel.FlatFee,
42
+ );
43
+
44
+ return filteredPrice?.amount || 0;
45
+ };
46
+
47
+ const addonCost = (plan: Plan, billingPeriod: BillingPeriod, addonId: string, quantity: number) => {
48
+ const addon = plan.compatibleAddons?.find((addon) => addon.id === addonId);
49
+
50
+ const price = addon?.pricePoints.find((price) => price.billingPeriod === billingPeriod);
51
+
52
+ return (price?.amount || 0) * quantity;
53
+ };
54
+
55
+ const billableFeatureCost = (plan: Plan, billingPeriod: BillingPeriod, featureId: string, quantity: number) => {
56
+ const price = plan.pricePoints.find(
57
+ (price) => price.feature?.featureId === featureId && price.billingPeriod === billingPeriod,
58
+ );
59
+
60
+ if (!price) return 0;
61
+
62
+ const { tiers } = price;
63
+
64
+ if (tiers) {
65
+ const quantityTier = tiers.find((tier) => tier.upTo >= quantity);
66
+ const priceTier = quantityTier || tiers[tiers.length - 1];
67
+ return quantity * priceTier.unitPrice.amount;
68
+ }
69
+
70
+ return (price?.amount || 0) * quantity;
71
+ };
72
+
73
+ const mockTotalPrice = (
74
+ plan: Plan,
75
+ billingPeriod: BillingPeriod,
76
+ addons: SubscriptionEstimationAddon[],
77
+ features: BillableFeature[],
78
+ ) => {
79
+ const currency = plan.pricePoints?.[0].currency || Currency.Usd;
80
+
81
+ const totalFlatFeeCost = flatFeeCost(plan, billingPeriod);
82
+ const totalFeaturesCost = features.reduce(
83
+ (total, feature) => total + billableFeatureCost(plan, billingPeriod, feature.featureId, feature.quantity || 0),
84
+ 0,
85
+ );
86
+ const totalAddonsCost = addons.reduce(
87
+ (total, addon) => total + addonCost(plan, billingPeriod, addon.addonId, addon.quantity || 0),
88
+ 0,
89
+ );
90
+
91
+ const cost: Money = {
92
+ amount: totalFlatFeeCost + totalFeaturesCost + totalAddonsCost,
93
+ currency: currency || Currency.Usd,
94
+ };
95
+
96
+ return {
97
+ subTotal: cost,
98
+ total: cost,
99
+ totalExcludingTax: cost,
100
+ };
101
+ };
102
+
103
+ export function mockPreviewSubscription(input: PreviewSubscription): SubscriptionPreview {
104
+ const { planId, billingPeriod = BillingPeriod.Monthly, billableFeatures: features = [], addons = [] } = input;
105
+ const data = mockCheckoutState({ planId });
106
+
107
+ if (!data || !data.plan) {
108
+ return defaultPreviewSubscription();
109
+ }
110
+
111
+ const total = mockTotalPrice(data.plan, billingPeriod, addons, features);
112
+
113
+ if (!total) {
114
+ return defaultPreviewSubscription();
115
+ }
116
+
117
+ return {
118
+ ...total,
119
+ billingPeriodRange: mockBillingPeriod(billingPeriod),
120
+ };
121
+ }