@stigg/react-sdk 4.4.0-beta.8 → 4.4.0

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 (95) hide show
  1. package/dist/components/checkout/Checkout.d.ts +3 -2
  2. package/dist/components/checkout/CheckoutContainer.d.ts +5 -2
  3. package/dist/components/checkout/CheckoutProvider.d.ts +3 -2
  4. package/dist/components/checkout/components/DowngradeToFreeContainer.d.ts +3 -7
  5. package/dist/components/checkout/components/StyledArrow.d.ts +5 -0
  6. package/dist/components/checkout/hooks/useCheckoutModel.d.ts +2 -0
  7. package/dist/components/checkout/hooks/useLoadCheckout.d.ts +3 -1
  8. package/dist/components/checkout/hooks/usePreviewSubscription.d.ts +17 -8
  9. package/dist/components/checkout/progressBar/CheckoutProgressBar.style.d.ts +1 -0
  10. package/dist/components/checkout/promotionCode/AddPromotionCode.d.ts +2 -4
  11. package/dist/components/checkout/promotionCode/AddPromotionCodeButton.d.ts +2 -1
  12. package/dist/components/checkout/promotionCode/PromotionCodeSection.d.ts +6 -2
  13. package/dist/components/checkout/steps/payment/stripe/useSubmit.d.ts +2 -2
  14. package/dist/components/checkout/steps/plan/BillingPeriodPicker.d.ts +1 -1
  15. package/dist/components/checkout/steps/plan/CheckoutPlanStep.d.ts +2 -1
  16. package/dist/components/checkout/summary/CheckoutSuccess.d.ts +4 -1
  17. package/dist/components/checkout/summary/CheckoutSummary.d.ts +1 -1
  18. package/dist/components/checkout/summary/components/CheckoutCaptions.d.ts +3 -2
  19. package/dist/components/checkout/summary/components/LineItems.d.ts +12 -8
  20. package/dist/components/checkout/textOverrides.d.ts +65 -21
  21. package/dist/components/checkout/theme.d.ts +0 -1
  22. package/dist/components/checkout/types.d.ts +7 -1
  23. package/dist/components/common/TiersSelectContainer.d.ts +1 -2
  24. package/dist/components/common/Typography.d.ts +2 -2
  25. package/dist/components/common/customIcons.d.ts +2 -0
  26. package/dist/components/customerPortal/subscriptionOverview/subscriptionView/SubscriptionView.style.d.ts +1 -1
  27. package/dist/components/paywall/PlanPrice.d.ts +1 -1
  28. package/dist/components/utils/currencyUtils.d.ts +2 -1
  29. package/dist/components/utils/getFeatureName.d.ts +1 -0
  30. package/dist/react-sdk.cjs.development.js +1153 -624
  31. package/dist/react-sdk.cjs.development.js.map +1 -1
  32. package/dist/react-sdk.cjs.production.min.js +1 -1
  33. package/dist/react-sdk.cjs.production.min.js.map +1 -1
  34. package/dist/react-sdk.esm.js +1176 -651
  35. package/dist/react-sdk.esm.js.map +1 -1
  36. package/dist/stories/mocks/checkout/consts.d.ts +11 -0
  37. package/dist/stories/mocks/checkout/mockCheckoutPreview.d.ts +2 -0
  38. package/dist/stories/mocks/checkout/mockCheckoutState.d.ts +2 -0
  39. package/dist/theme/getResolvedTheme.d.ts +1 -0
  40. package/dist/theme/types.d.ts +1 -0
  41. package/package.json +28 -20
  42. package/src/assets/coupon.svg +6 -0
  43. package/src/assets/pay-as-you-go-charge.svg +11 -0
  44. package/src/components/checkout/Checkout.tsx +5 -2
  45. package/src/components/checkout/CheckoutContainer.tsx +21 -12
  46. package/src/components/checkout/CheckoutProvider.tsx +5 -3
  47. package/src/components/checkout/components/DowngradeToFreeContainer.tsx +33 -36
  48. package/src/components/checkout/components/StyledArrow.tsx +9 -0
  49. package/src/components/checkout/hooks/useCheckoutModel.ts +12 -2
  50. package/src/components/checkout/hooks/useLoadCheckout.ts +10 -2
  51. package/src/components/checkout/hooks/usePlanStepModel.ts +22 -7
  52. package/src/components/checkout/hooks/usePreviewSubscription.ts +103 -50
  53. package/src/components/checkout/planHeader/PlanHeader.tsx +18 -25
  54. package/src/components/checkout/progressBar/CheckoutProgressBar.style.ts +11 -12
  55. package/src/components/checkout/progressBar/CheckoutProgressBar.tsx +7 -6
  56. package/src/components/checkout/promotionCode/AddPromotionCode.tsx +32 -9
  57. package/src/components/checkout/promotionCode/AddPromotionCodeButton.tsx +15 -11
  58. package/src/components/checkout/promotionCode/AppliedPromotionCode.tsx +4 -3
  59. package/src/components/checkout/promotionCode/PromotionCodeSection.tsx +21 -7
  60. package/src/components/checkout/steps/addons/CheckoutAddonsStep.style.tsx +0 -1
  61. package/src/components/checkout/steps/addons/CheckoutAddonsStep.tsx +7 -4
  62. package/src/components/checkout/steps/payment/PaymentMethods.style.ts +4 -1
  63. package/src/components/checkout/steps/payment/PaymentStep.tsx +0 -1
  64. package/src/components/checkout/steps/payment/stripe/useSubmit.ts +8 -4
  65. package/src/components/checkout/steps/plan/BillingPeriodPicker.style.tsx +3 -2
  66. package/src/components/checkout/steps/plan/BillingPeriodPicker.tsx +11 -8
  67. package/src/components/checkout/steps/plan/CheckoutChargeList.tsx +35 -14
  68. package/src/components/checkout/steps/plan/CheckoutPlanStep.tsx +10 -5
  69. package/src/components/checkout/summary/CheckoutSuccess.tsx +52 -6
  70. package/src/components/checkout/summary/CheckoutSummary.tsx +166 -59
  71. package/src/components/checkout/summary/components/CheckoutCaptions.tsx +63 -39
  72. package/src/components/checkout/summary/components/LineItems.tsx +77 -28
  73. package/src/components/checkout/textOverrides.ts +112 -30
  74. package/src/components/checkout/theme.ts +0 -4
  75. package/src/components/checkout/types.ts +15 -1
  76. package/src/components/common/Icon.tsx +4 -6
  77. package/src/components/common/TiersSelectContainer.tsx +7 -8
  78. package/src/components/common/Typography.tsx +12 -3
  79. package/src/components/common/customIcons.ts +2 -0
  80. package/src/components/common/mapExternalTheme.ts +1 -2
  81. package/src/components/customerPortal/paywall/CustomerPortalPaywall.style.ts +4 -3
  82. package/src/components/paywall/PlanOfferingButton.tsx +6 -8
  83. package/src/components/paywall/PlanPrice.tsx +14 -17
  84. package/src/components/utils/currencyUtils.ts +4 -2
  85. package/src/components/utils/getFeatureName.ts +13 -5
  86. package/src/stories/Checkout.stories.tsx +37 -5
  87. package/src/stories/CustomerPortal.stories.tsx +2 -2
  88. package/src/stories/mocks/checkout/consts.ts +15 -0
  89. package/src/stories/mocks/checkout/mockCheckoutPreview.ts +138 -0
  90. package/src/stories/mocks/checkout/mockCheckoutState.ts +206 -0
  91. package/src/theme/Theme.tsx +10 -1
  92. package/src/theme/getResolvedTheme.ts +1 -0
  93. package/src/theme/types.ts +1 -0
  94. package/dist/components/checkout/planHeader/PlanHeader.style.d.ts +0 -25
  95. package/src/components/checkout/planHeader/PlanHeader.style.tsx +0 -23
@@ -1,13 +1,15 @@
1
1
  import { PriceTierFragment } from '@stigg/js-client-sdk';
2
2
  import { MenuItem, OutlinedInput, Select, SelectChangeEvent } from '@mui/material';
3
- import { Typography } from '../common/Typography';
4
3
  import { map } from 'lodash';
5
4
  import React, { ReactNode } from 'react';
6
5
  import styled from '@emotion/styled/macro';
7
6
  import { getTierByQuantity } from '../utils/priceTierUtils';
7
+ import { Typography } from './Typography';
8
8
 
9
9
  const TierSelect = styled(Select)`
10
10
  border-radius: 10px;
11
+ min-height: 42px;
12
+ min-width: 120px;
11
13
 
12
14
  &:hover .MuiOutlinedInput-notchedOutline {
13
15
  border-color: ${({ theme }) => theme.stigg.palette.outlinedBorder};
@@ -16,7 +18,7 @@ const TierSelect = styled(Select)`
16
18
 
17
19
  const TierInput = styled(OutlinedInput)`
18
20
  & .MuiInputBase-input {
19
- padding: 7px 12px;
21
+ padding: 10px 12px;
20
22
  }
21
23
 
22
24
  &.Mui-focused .MuiOutlinedInput-notchedOutline {
@@ -27,13 +29,11 @@ const TierInput = styled(OutlinedInput)`
27
29
  export function TiersSelectContainer({
28
30
  componentId,
29
31
  tiers,
30
- tierUnits,
31
32
  selectedTier,
32
33
  handleTierChange,
33
34
  }: {
34
35
  componentId: string;
35
36
  tiers?: PriceTierFragment[] | null;
36
- tierUnits?: string;
37
37
  selectedTier?: PriceTierFragment;
38
38
  handleTierChange: (tier: PriceTierFragment) => void;
39
39
  }) {
@@ -57,12 +57,11 @@ export function TiersSelectContainer({
57
57
  PaperProps: {
58
58
  sx: { marginTop: '4px', borderRadius: '10px' },
59
59
  },
60
- }}
61
- >
60
+ }}>
62
61
  {map(tiers, (tier: PriceTierFragment) => (
63
62
  <MenuItem className="stigg-price-tier-menu-item-text" key={tier.upTo} value={tier.upTo.toString()}>
64
- <Typography variant="body1" color="primary">
65
- {tier.upTo} {tierUnits}
63
+ <Typography variant="body1" color="primary" style={{ lineHeight: 'unset' }}>
64
+ {tier.upTo}
66
65
  </Typography>
67
66
  </MenuItem>
68
67
  ))}
@@ -4,7 +4,15 @@ import { FontWeight as StyledFontWeight, Text, TypeProps } from 'styled-typograp
4
4
  import { Theme, useTheme } from '@emotion/react';
5
5
  import { FontWeight } from '../../theme/types';
6
6
 
7
- type Colors = 'primary' | 'primary.main' | 'secondary' | 'disabled' | 'white' | 'warning' | 'error';
7
+ type Colors =
8
+ | 'primary'
9
+ | 'primary.main'
10
+ | 'primary.main.light'
11
+ | 'secondary'
12
+ | 'disabled'
13
+ | 'white'
14
+ | 'warning'
15
+ | 'error';
8
16
 
9
17
  export type TypographyProps = {
10
18
  children: React.ReactNode;
@@ -25,6 +33,8 @@ function getColor(theme: Theme, $color: Colors) {
25
33
  return 'white';
26
34
  case 'primary.main':
27
35
  return theme.stigg.palette.primary;
36
+ case 'primary.main.light':
37
+ return theme.stigg.palette.primaryLight;
28
38
  case 'warning':
29
39
  return theme.stigg.palette.warning;
30
40
  case 'error':
@@ -111,8 +121,7 @@ export const Typography = forwardRef((props: TypographyProps, ref) => {
111
121
  level={level}
112
122
  color={overrideColor || getColor(theme, color)}
113
123
  fontWeight={fontWeight ?? (bold ? StyledFontWeight.Bold : getFontWeight(theme, variant))}
114
- $span={span}
115
- >
124
+ $span={span}>
116
125
  {children}
117
126
  </StyledText>
118
127
  );
@@ -17,3 +17,5 @@ export { default as OutlinedCheckedCircleDisabled } from '../../assets/outlined-
17
17
  export { default as ArrowForward } from '../../assets/arrow-forward.svg';
18
18
  export { default as Close } from '../../assets/close.svg';
19
19
  export { default as Check } from '../../assets/check.svg';
20
+ export { default as PayAsYouGoCharge } from '../../assets/pay-as-you-go-charge.svg';
21
+ export { default as Coupon } from '../../assets/coupon.svg';
@@ -123,8 +123,7 @@ export function mapCheckoutConfiguration(configuration: CheckoutConfiguration):
123
123
  text: {
124
124
  primary: palette?.textColor || undefined,
125
125
  },
126
- backgroundHighlight: palette?.selectionColor || undefined,
127
- backgroundSection: palette?.summaryBackgroundColor || undefined,
126
+ backgroundHighlight: palette?.summaryBackgroundColor || undefined,
128
127
  },
129
128
  typography: mapTypography(typography),
130
129
  };
@@ -13,14 +13,15 @@ export const CustomerPortalPaywallLayout = styled(SectionContainer)<{
13
13
  .stigg-paywall-layout {
14
14
  width: 100%;
15
15
  }
16
-
16
+
17
17
  .stigg-paywall-plans-layout {
18
18
  flex-wrap: nowrap;
19
19
  width: 100%;
20
20
  overflow-x: auto;
21
21
  justify-content: unset;
22
- padding: 0;
22
+ padding: 10px 0px 0px 0px;
23
23
  }
24
+
24
25
  .stigg-paywall-plans-layout .stigg-plan-offering-container:first-of-type {
25
26
  margin-left: auto;
26
27
  }
@@ -37,6 +38,6 @@ export const CustomerPortalPaywallLayout = styled(SectionContainer)<{
37
38
  `}
38
39
 
39
40
  .${STIGG_WATERMARK_CLASSNAME} {
40
- display: none
41
+ display: none;
41
42
  }
42
43
  `;
@@ -1,14 +1,14 @@
1
1
  import React, { useState } from 'react';
2
2
  import { BillingPeriod, Customer, PriceTierFragment, PricingType, Subscription } from '@stigg/js-client-sdk';
3
+ import { isFunction } from 'lodash';
4
+ import ClipLoader from 'react-spinners/ClipLoader';
3
5
  import styled from '@emotion/styled/macro';
4
- import { css, Theme, useTheme } from '@emotion/react';
6
+ import { css, useTheme } from '@emotion/react';
5
7
  import { PaywallPlan, SubscribeIntentionType } from './types';
6
8
  import { PaywallLocalization } from './paywallTextOverrides';
7
9
  import { flexLayoutMapper } from '../../theme/getResolvedTheme';
8
- import ClipLoader from 'react-spinners/ClipLoader';
9
10
  import { Typography } from '../common/Typography';
10
11
  import { getSubscriptionScheduleUpdateTexts } from '../utils/getSubscriptionScheduleUpdateTexts';
11
- import { isFunction } from 'lodash';
12
12
  import { compareSelectedTierToCurrentTier, PriceTierComparison } from '../utils/priceTierUtils';
13
13
 
14
14
  const LoadingIndicator = styled(ClipLoader)`
@@ -99,7 +99,7 @@ export function PlanOfferingButton({
99
99
  currentSubscription,
100
100
  selectedTierByFeature,
101
101
  }: PlanOfferingButtonProps) {
102
- const theme = useTheme() as Theme;
102
+ const theme = useTheme();
103
103
  const [isLoading, setIsLoading] = useState(false);
104
104
  const {
105
105
  currentPlan,
@@ -187,8 +187,7 @@ export function PlanOfferingButton({
187
187
  onClick={() => {
188
188
  setIsLoading(true);
189
189
  Promise.resolve(onPlanSelected(buttonProps.intentionType)).finally(() => setIsLoading(false));
190
- }}
191
- >
190
+ }}>
192
191
  {buttonProps.title}
193
192
  </CancelScheduledUpdateButton>
194
193
  </ScheduledUpdateText>
@@ -202,8 +201,7 @@ export function PlanOfferingButton({
202
201
  onClick={() => {
203
202
  setIsLoading(true);
204
203
  Promise.resolve(onPlanSelected(buttonProps.intentionType)).finally(() => setIsLoading(false));
205
- }}
206
- >
204
+ }}>
207
205
  <ButtonText className="stigg-paywall-plan-button-text" variant="h6" color="primary.main">
208
206
  {buttonProps.title}
209
207
  </ButtonText>
@@ -1,10 +1,10 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import styled from '@emotion/styled/macro';
3
+ import { BillingPeriod, PriceTierFragment } from '@stigg/js-client-sdk';
1
4
  import { PaywallPlan } from './types';
2
5
  import { PaywallLocalization } from './paywallTextOverrides';
3
6
  import { getPlanPrice } from '../utils/getPlanPrice';
4
7
  import { Typography } from '../common/Typography';
5
- import React, { useEffect, useState } from 'react';
6
- import styled from '@emotion/styled/macro';
7
- import { BillingPeriod, PriceTierFragment } from '@stigg/js-client-sdk';
8
8
  import { TiersSelectContainer } from '../common/TiersSelectContainer';
9
9
 
10
10
  const EMPTY_CHAR = '‎';
@@ -38,7 +38,7 @@ function PriceBillingPeriod({
38
38
  hasAnnuallyPrice,
39
39
  paywallLocale,
40
40
  }: PriceBillingPeriodProps) {
41
- const hasPrice = plan.pricePoints.find(pricePoint => pricePoint.billingPeriod === billingPeriod);
41
+ const hasPrice = plan.pricePoints.find((pricePoint) => pricePoint.billingPeriod === billingPeriod);
42
42
 
43
43
  let content = EMPTY_CHAR;
44
44
  if (hasPrice && hasMonthlyPrice && hasAnnuallyPrice) {
@@ -79,7 +79,7 @@ export const PlanPrice = ({
79
79
  hasAnnuallyPrice: boolean;
80
80
  locale: string;
81
81
  }) => {
82
- const { price, unit, tiers, tierUnits } = getPlanPrice(
82
+ const { price, unit, tiers } = getPlanPrice(
83
83
  plan,
84
84
  billingPeriod,
85
85
  paywallLocale,
@@ -87,18 +87,17 @@ export const PlanPrice = ({
87
87
  hasMonthlyPrice,
88
88
  selectedTierByFeature,
89
89
  );
90
-
91
90
  const [selectedTier, setSelectedTier] = useState<PriceTierFragment>();
92
91
 
93
- useEffect(() => {
94
- setSelectedTier(selectedTierByFeature[featureId!]);
95
- }, [selectedTierByFeature]);
96
-
97
92
  // We currently only support prices with one tier - so we select the first one
98
- const tieredPrice = plan.pricePoints.find(planPrice => {
93
+ const tieredPrice = plan.pricePoints.find((planPrice) => {
99
94
  return planPrice.billingPeriod === billingPeriod && planPrice.isTieredPrice;
100
95
  });
101
- const featureId = tieredPrice ? tieredPrice!.feature!.featureId : undefined;
96
+ const featureId = tieredPrice ? tieredPrice.feature!.featureId : undefined;
97
+
98
+ useEffect(() => {
99
+ setSelectedTier(featureId ? selectedTierByFeature[featureId] : undefined);
100
+ }, [featureId, selectedTierByFeature]);
102
101
 
103
102
  const handleTierChange = (tier: PriceTierFragment) => {
104
103
  const updatedTierByFeature: Record<string, PriceTierFragment> = {};
@@ -121,8 +120,7 @@ export const PlanPrice = ({
121
120
  style={{ minHeight: '20px' }}
122
121
  className="stigg-starting-at-text"
123
122
  variant="body1"
124
- color="secondary"
125
- >
123
+ color="secondary">
126
124
  {showStartingAt ? paywallLocale.price.startingAtCaption : EMPTY_CHAR}
127
125
  </Typography>
128
126
  )}
@@ -145,15 +143,14 @@ export const PlanPrice = ({
145
143
  </Typography>
146
144
  )}
147
145
 
148
- {withTiersRow && (
146
+ {withTiersRow ? (
149
147
  <TiersSelectContainer
150
148
  componentId={`${plan.id}_${featureId}_tier`}
151
149
  tiers={tiers}
152
- tierUnits={tierUnits}
153
150
  selectedTier={selectedTier}
154
151
  handleTierChange={handleTierChange}
155
152
  />
156
- )}
153
+ ) : null}
157
154
  </>
158
155
  </PlanPriceContainer>
159
156
  );
@@ -6,17 +6,19 @@ export const currencyPriceFormatter = ({
6
6
  currency = Currency.Usd,
7
7
  locale,
8
8
  maximumFractionDigits = 5,
9
+ minimumFractionDigits = 0,
9
10
  }: {
10
11
  amount: number;
11
12
  currency?: Currency | string;
12
13
  locale?: string;
13
14
  maximumFractionDigits?: number;
15
+ minimumFractionDigits?: number;
14
16
  }) => {
15
17
  const currencyString = currency.toString();
16
18
  const currencySymbol = getSymbolFromCurrency(currencyString);
17
19
  let formattedPrice = new Intl.NumberFormat(locale, {
18
- maximumFractionDigits: maximumFractionDigits,
19
- minimumFractionDigits: 0,
20
+ maximumFractionDigits,
21
+ minimumFractionDigits,
20
22
  style: 'currency',
21
23
  currency: currencyString,
22
24
  ...(currencySymbol ? { currencyDisplay: 'code' } : {}),
@@ -2,13 +2,21 @@ import { FeatureFragment } from '@stigg/api-client-js/src/generated/sdk';
2
2
  import lowercase from 'lodash/lowerCase';
3
3
 
4
4
  export function getFeatureDisplayName(feature: FeatureFragment) {
5
- const displayNameLowerCase = lowercase(feature.displayName);
5
+ return getFeatureDisplayNameText(feature.displayName, feature.featureUnits, feature.featureUnitsPlural);
6
+ }
7
+
8
+ export function getFeatureDisplayNameText(featureDisplayName: string, featureUnits: string | undefined | null, featureUnitsPlural: string | undefined | null) {
9
+ if(!featureUnits && !featureUnitsPlural) {
10
+ return featureDisplayName;
11
+ }
12
+
13
+ const displayNameLowerCase = lowercase(featureDisplayName);
6
14
  if (
7
- displayNameLowerCase === lowercase(feature.featureUnits || '') ||
8
- displayNameLowerCase === lowercase(feature.featureUnitsPlural || '')
15
+ displayNameLowerCase === lowercase(featureUnits || '') ||
16
+ displayNameLowerCase === lowercase(featureUnitsPlural || '')
9
17
  ) {
10
- return feature.displayName;
18
+ return featureDisplayName;
11
19
  }
12
20
 
13
- return `${feature.displayName} (${feature.featureUnitsPlural})`;
21
+ return `${featureDisplayName} (${featureUnitsPlural})`;
14
22
  }
@@ -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`
@@ -40,13 +54,34 @@ const Template: ComponentStory<any> = (args) => (
40
54
  }}
41
55
  onCheckout={async ({ checkoutParams, checkoutAction }) => {
42
56
  console.log('checkout started!', checkoutParams);
57
+ if (args.useMockData) {
58
+ console.log('returning mock checkout success response');
59
+ return { success: true };
60
+ }
61
+
43
62
  const { success, errorMessage } = await checkoutAction();
44
63
  return { success, errorMessage };
45
64
  }}
65
+ billableFeatures={[{ featureId: 'feature-seat', quantity: 30 }]}
46
66
  onChangePlan={({ currentPlan }) => {
47
67
  console.log('plan changed clicked!', { currentPlan });
48
68
  }}
49
- // billingInformation={{ taxPercentage: 27 }}
69
+ onMockCheckoutState={
70
+ args.useMockData
71
+ ? (params) => {
72
+ console.log('mocking checkout state', params);
73
+ return mockCheckoutState(params);
74
+ }
75
+ : undefined
76
+ }
77
+ onMockCheckoutPreview={
78
+ args.useMockData
79
+ ? (params) => {
80
+ console.log('mocking checkout preview');
81
+ return mockPreviewSubscription(params);
82
+ }
83
+ : undefined
84
+ }
50
85
  />
51
86
  </Wrapper>
52
87
  );
@@ -55,8 +90,5 @@ export const DefaultCheckout = Template.bind({});
55
90
  DefaultCheckout.args = {
56
91
  ...defaultArgsWithCustomer,
57
92
  planId: 'plan-revvenu-essentials',
58
- baseUri: 'http://localhost:4000',
59
- apiKey: 'client-72b058a6-0f22-4c86-adce-bf266d12e12e:9f356ceb-c94c-42a4-9572-10b12824da81',
60
- // baseUri: 'https://api-staging.stigg.io',
61
- // apiKey: 'client-79584f52-7ef9-4c58-b9ac-5080acf492e4:71f2274c-100a-4fa4-8a43-48fa3b16c627',
93
+ useMockData: false,
62
94
  };
@@ -33,7 +33,7 @@ export default {
33
33
  }
34
34
  }
35
35
  baseUri={args.baseUri}
36
- enableEdge={args.disableEdge}>
36
+ enableEdge={!args.disableEdge}>
37
37
  <Story />
38
38
  </StiggProvider>
39
39
  ),
@@ -70,7 +70,7 @@ const Template: ComponentStory<any> = (args) => {
70
70
  onPlanSelected={(...args) => {
71
71
  console.log('onPlanSelected', args);
72
72
  }}
73
- highlightedPlanId="plan-revvenu-unity"
73
+ highlightedPlanId="plan-revvenu-essentials"
74
74
  />
75
75
  }
76
76
  theme={customerPortalTheme}
@@ -0,0 +1,15 @@
1
+ export const BASE_FEE_MONTHLY = 200;
2
+ export const BASE_FEE_YEARLY = 175 * 12;
3
+ export const TIERS = [100, 500, 750];
4
+ export const TIERS_PRICE_MONTHLY = [10, 49, 70];
5
+ export const TIERS_PRICE_YEARLY = [8, 35, 50].map((p) => p * 12);
6
+
7
+ export const PER_UNIT_PRICE_MONTHLY = 12;
8
+ export const PER_UNIT_PRICE_YEARLY = 10 * 12;
9
+
10
+ export const ADDON_PRICE_MONTHLY = 50;
11
+ export const ADDON_PRICE_YEARLY = 35 * 12;
12
+
13
+ export const STRIPE_MOCK_ACCOUNT_ID = 'acct_1NnHoQG6EyqgvTaj';
14
+ export const STRIPE_MOCK_ACCOUNT_PK =
15
+ 'pk_test_51NnHoQG6EyqgvTajznajopWC01AozNtq7zgySeQ1qx4PH9TAXvMj0TnbZvYT3yOt46jbQAcCDs1EU2QKcfG8eEoO00tlW0Jp3r';
@@ -0,0 +1,138 @@
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
+ SubscriptionPreviewV2,
13
+ } from '@stigg/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 = (): SubscriptionPreviewV2 => {
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): SubscriptionPreviewV2 {
104
+ const {
105
+ planId,
106
+ billingPeriod = BillingPeriod.Monthly,
107
+ billableFeatures: features = [],
108
+ addons = [],
109
+ promotionCode,
110
+ } = input;
111
+
112
+ if (promotionCode) {
113
+ throw new Error('Error: Invalid promotion code');
114
+ }
115
+
116
+ const data = mockCheckoutState({ planId });
117
+
118
+ if (!data || !data.plan) {
119
+ return defaultPreviewSubscription();
120
+ }
121
+
122
+ const total = mockTotalPrice(data.plan, billingPeriod, addons, features);
123
+
124
+ if (!total) {
125
+ return defaultPreviewSubscription();
126
+ }
127
+
128
+ return {
129
+ ...total,
130
+ billingPeriodRange: mockBillingPeriod(billingPeriod),
131
+ proration: {
132
+ prorationDate: moment().toDate(),
133
+ credit: total.total,
134
+ debit: total.total,
135
+ netAmount: total.total,
136
+ },
137
+ };
138
+ }