@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.
- package/dist/components/checkout/Checkout.d.ts +3 -2
- package/dist/components/checkout/CheckoutContainer.d.ts +5 -2
- package/dist/components/checkout/CheckoutProvider.d.ts +3 -2
- package/dist/components/checkout/components/DowngradeToFreeContainer.d.ts +3 -7
- package/dist/components/checkout/components/StyledArrow.d.ts +5 -0
- package/dist/components/checkout/hooks/useCheckoutModel.d.ts +2 -0
- package/dist/components/checkout/hooks/useLoadCheckout.d.ts +3 -1
- package/dist/components/checkout/hooks/usePreviewSubscription.d.ts +17 -8
- package/dist/components/checkout/progressBar/CheckoutProgressBar.style.d.ts +1 -0
- package/dist/components/checkout/promotionCode/AddPromotionCode.d.ts +2 -4
- package/dist/components/checkout/promotionCode/AddPromotionCodeButton.d.ts +2 -1
- package/dist/components/checkout/promotionCode/PromotionCodeSection.d.ts +6 -2
- package/dist/components/checkout/steps/payment/stripe/useSubmit.d.ts +2 -2
- package/dist/components/checkout/steps/plan/BillingPeriodPicker.d.ts +1 -1
- package/dist/components/checkout/steps/plan/CheckoutPlanStep.d.ts +2 -1
- package/dist/components/checkout/summary/CheckoutSuccess.d.ts +4 -1
- package/dist/components/checkout/summary/CheckoutSummary.d.ts +1 -1
- package/dist/components/checkout/summary/components/CheckoutCaptions.d.ts +3 -2
- package/dist/components/checkout/summary/components/LineItems.d.ts +12 -8
- package/dist/components/checkout/textOverrides.d.ts +65 -21
- package/dist/components/checkout/theme.d.ts +0 -1
- package/dist/components/checkout/types.d.ts +7 -1
- package/dist/components/common/TiersSelectContainer.d.ts +1 -2
- package/dist/components/common/Typography.d.ts +2 -2
- package/dist/components/common/customIcons.d.ts +2 -0
- package/dist/components/customerPortal/subscriptionOverview/subscriptionView/SubscriptionView.style.d.ts +1 -1
- package/dist/components/paywall/PlanPrice.d.ts +1 -1
- package/dist/components/utils/currencyUtils.d.ts +2 -1
- package/dist/components/utils/getFeatureName.d.ts +1 -0
- package/dist/react-sdk.cjs.development.js +1153 -624
- package/dist/react-sdk.cjs.development.js.map +1 -1
- package/dist/react-sdk.cjs.production.min.js +1 -1
- package/dist/react-sdk.cjs.production.min.js.map +1 -1
- package/dist/react-sdk.esm.js +1176 -651
- package/dist/react-sdk.esm.js.map +1 -1
- package/dist/stories/mocks/checkout/consts.d.ts +11 -0
- package/dist/stories/mocks/checkout/mockCheckoutPreview.d.ts +2 -0
- package/dist/stories/mocks/checkout/mockCheckoutState.d.ts +2 -0
- package/dist/theme/getResolvedTheme.d.ts +1 -0
- package/dist/theme/types.d.ts +1 -0
- package/package.json +28 -20
- package/src/assets/coupon.svg +6 -0
- package/src/assets/pay-as-you-go-charge.svg +11 -0
- package/src/components/checkout/Checkout.tsx +5 -2
- package/src/components/checkout/CheckoutContainer.tsx +21 -12
- package/src/components/checkout/CheckoutProvider.tsx +5 -3
- package/src/components/checkout/components/DowngradeToFreeContainer.tsx +33 -36
- package/src/components/checkout/components/StyledArrow.tsx +9 -0
- package/src/components/checkout/hooks/useCheckoutModel.ts +12 -2
- package/src/components/checkout/hooks/useLoadCheckout.ts +10 -2
- package/src/components/checkout/hooks/usePlanStepModel.ts +22 -7
- package/src/components/checkout/hooks/usePreviewSubscription.ts +103 -50
- package/src/components/checkout/planHeader/PlanHeader.tsx +18 -25
- package/src/components/checkout/progressBar/CheckoutProgressBar.style.ts +11 -12
- package/src/components/checkout/progressBar/CheckoutProgressBar.tsx +7 -6
- package/src/components/checkout/promotionCode/AddPromotionCode.tsx +32 -9
- package/src/components/checkout/promotionCode/AddPromotionCodeButton.tsx +15 -11
- package/src/components/checkout/promotionCode/AppliedPromotionCode.tsx +4 -3
- package/src/components/checkout/promotionCode/PromotionCodeSection.tsx +21 -7
- package/src/components/checkout/steps/addons/CheckoutAddonsStep.style.tsx +0 -1
- package/src/components/checkout/steps/addons/CheckoutAddonsStep.tsx +7 -4
- package/src/components/checkout/steps/payment/PaymentMethods.style.ts +4 -1
- package/src/components/checkout/steps/payment/PaymentStep.tsx +0 -1
- package/src/components/checkout/steps/payment/stripe/useSubmit.ts +8 -4
- package/src/components/checkout/steps/plan/BillingPeriodPicker.style.tsx +3 -2
- package/src/components/checkout/steps/plan/BillingPeriodPicker.tsx +11 -8
- package/src/components/checkout/steps/plan/CheckoutChargeList.tsx +35 -14
- package/src/components/checkout/steps/plan/CheckoutPlanStep.tsx +10 -5
- package/src/components/checkout/summary/CheckoutSuccess.tsx +52 -6
- package/src/components/checkout/summary/CheckoutSummary.tsx +166 -59
- package/src/components/checkout/summary/components/CheckoutCaptions.tsx +63 -39
- package/src/components/checkout/summary/components/LineItems.tsx +77 -28
- package/src/components/checkout/textOverrides.ts +112 -30
- package/src/components/checkout/theme.ts +0 -4
- package/src/components/checkout/types.ts +15 -1
- package/src/components/common/Icon.tsx +4 -6
- package/src/components/common/TiersSelectContainer.tsx +7 -8
- package/src/components/common/Typography.tsx +12 -3
- package/src/components/common/customIcons.ts +2 -0
- package/src/components/common/mapExternalTheme.ts +1 -2
- package/src/components/customerPortal/paywall/CustomerPortalPaywall.style.ts +4 -3
- package/src/components/paywall/PlanOfferingButton.tsx +6 -8
- package/src/components/paywall/PlanPrice.tsx +14 -17
- package/src/components/utils/currencyUtils.ts +4 -2
- package/src/components/utils/getFeatureName.ts +13 -5
- package/src/stories/Checkout.stories.tsx +37 -5
- package/src/stories/CustomerPortal.stories.tsx +2 -2
- package/src/stories/mocks/checkout/consts.ts +15 -0
- package/src/stories/mocks/checkout/mockCheckoutPreview.ts +138 -0
- package/src/stories/mocks/checkout/mockCheckoutState.ts +206 -0
- package/src/theme/Theme.tsx +10 -1
- package/src/theme/getResolvedTheme.ts +1 -0
- package/src/theme/types.ts +1 -0
- package/dist/components/checkout/planHeader/PlanHeader.style.d.ts +0 -25
- 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:
|
|
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}
|
|
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 =
|
|
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?.
|
|
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:
|
|
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,
|
|
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()
|
|
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
|
|
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
|
|
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
|
|
19
|
-
minimumFractionDigits
|
|
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
|
-
|
|
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(
|
|
8
|
-
displayNameLowerCase === lowercase(
|
|
15
|
+
displayNameLowerCase === lowercase(featureUnits || '') ||
|
|
16
|
+
displayNameLowerCase === lowercase(featureUnitsPlural || '')
|
|
9
17
|
) {
|
|
10
|
-
return
|
|
18
|
+
return featureDisplayName;
|
|
11
19
|
}
|
|
12
20
|
|
|
13
|
-
return `${
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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
|
+
}
|