@stigg/react-sdk 4.4.0-beta.8 → 4.4.0-beta.9
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/CheckoutContainer.d.ts +2 -1
- package/dist/components/checkout/steps/payment/stripe/useSubmit.d.ts +2 -2
- 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 +2 -1
- package/dist/components/checkout/textOverrides.d.ts +3 -2
- package/dist/react-sdk.cjs.development.js +185 -124
- 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 +192 -126
- package/dist/react-sdk.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/checkout/CheckoutContainer.tsx +3 -0
- package/src/components/checkout/hooks/usePlanStepModel.ts +22 -7
- package/src/components/checkout/hooks/usePreviewSubscription.ts +2 -1
- package/src/components/checkout/steps/addons/CheckoutAddonsStep.tsx +3 -1
- package/src/components/checkout/steps/payment/stripe/useSubmit.ts +8 -4
- package/src/components/checkout/steps/plan/BillingPeriodPicker.tsx +6 -6
- package/src/components/checkout/summary/CheckoutSuccess.tsx +52 -6
- package/src/components/checkout/summary/CheckoutSummary.tsx +23 -13
- package/src/components/checkout/summary/components/CheckoutCaptions.tsx +30 -29
- package/src/components/checkout/textOverrides.ts +10 -8
- package/src/stories/Checkout.stories.tsx +5 -4
package/package.json
CHANGED
|
@@ -47,6 +47,7 @@ export type CheckoutContainerProps = {
|
|
|
47
47
|
onChangePlan?: (params: { currentPlan: CheckoutStatePlan | undefined }) => void;
|
|
48
48
|
onBillingAddressChange?: (params: { billingAddress: BillingAddress }) => Promise<void>;
|
|
49
49
|
disablePromotionCode?: boolean;
|
|
50
|
+
disableSuccessAnimation?: boolean;
|
|
50
51
|
};
|
|
51
52
|
|
|
52
53
|
export function CheckoutContainer({
|
|
@@ -55,6 +56,7 @@ export function CheckoutContainer({
|
|
|
55
56
|
onChangePlan,
|
|
56
57
|
onBillingAddressChange,
|
|
57
58
|
disablePromotionCode,
|
|
59
|
+
disableSuccessAnimation,
|
|
58
60
|
}: CheckoutContainerProps) {
|
|
59
61
|
const { stripePromise, setupIntentClientSecret } = useStripeIntegration();
|
|
60
62
|
const [{ stiggTheme, widgetState }] = useCheckoutContext();
|
|
@@ -115,6 +117,7 @@ export function CheckoutContainer({
|
|
|
115
117
|
) : (
|
|
116
118
|
<CheckoutSummary
|
|
117
119
|
disablePromotionCode={disablePromotionCode}
|
|
120
|
+
disableSuccessAnimation={disableSuccessAnimation}
|
|
118
121
|
onCheckout={onCheckout}
|
|
119
122
|
onCheckoutCompleted={onCheckoutCompleted}
|
|
120
123
|
isFreeDowngrade={isFreeDowngrade}
|
|
@@ -20,6 +20,16 @@ type GetPlanStepInitialStateProps = {
|
|
|
20
20
|
preconfiguredBillableFeatures: BillableFeature[];
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
const getBillingPeriod = (billingPeriod: BillingPeriod, hasMonthlyPrices?: boolean, hasAnnualPrices?: boolean) => {
|
|
24
|
+
if (billingPeriod === BillingPeriod.Monthly && hasMonthlyPrices) {
|
|
25
|
+
return billingPeriod;
|
|
26
|
+
}
|
|
27
|
+
if (billingPeriod === BillingPeriod.Annually && hasAnnualPrices) {
|
|
28
|
+
return billingPeriod;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
};
|
|
32
|
+
|
|
23
33
|
const isInAdvanceCommitmentCharge = ({ pricingModel }: Price) => {
|
|
24
34
|
return pricingModel === BillingModel.PerUnit;
|
|
25
35
|
};
|
|
@@ -89,11 +99,9 @@ function resolveBillingPeriod({
|
|
|
89
99
|
const hasAnnualPrices = plan?.pricePoints.some((pricePoint) => pricePoint.billingPeriod === BillingPeriod.Annually);
|
|
90
100
|
|
|
91
101
|
if (preferredBillingPeriod) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (preferredBillingPeriod === BillingPeriod.Annually && hasAnnualPrices) {
|
|
96
|
-
return BillingPeriod.Annually;
|
|
102
|
+
const billingPeriod = getBillingPeriod(preferredBillingPeriod, hasMonthlyPrices, hasAnnualPrices);
|
|
103
|
+
if (billingPeriod) {
|
|
104
|
+
return billingPeriod;
|
|
97
105
|
}
|
|
98
106
|
}
|
|
99
107
|
|
|
@@ -102,8 +110,15 @@ function resolveBillingPeriod({
|
|
|
102
110
|
return activeSubscription?.price?.billingPeriod || BillingPeriod.Monthly;
|
|
103
111
|
}
|
|
104
112
|
|
|
105
|
-
if (activeSubscription?.
|
|
106
|
-
|
|
113
|
+
if (activeSubscription?.prices && activeSubscription?.prices.length > 0) {
|
|
114
|
+
const billingPeriod = getBillingPeriod(
|
|
115
|
+
activeSubscription?.prices[0].billingPeriod,
|
|
116
|
+
hasMonthlyPrices,
|
|
117
|
+
hasAnnualPrices,
|
|
118
|
+
);
|
|
119
|
+
if (billingPeriod) {
|
|
120
|
+
return billingPeriod;
|
|
121
|
+
}
|
|
107
122
|
}
|
|
108
123
|
|
|
109
124
|
return hasAnnualPrices ? BillingPeriod.Annually : BillingPeriod.Monthly;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import isEmpty from 'lodash/isEmpty';
|
|
2
3
|
import { BillingAddress, PreviewSubscription, SubscriptionPreview } from '@stigg/js-client-sdk';
|
|
3
4
|
import { useStiggContext } from '../../StiggProvider';
|
|
4
5
|
import { useCheckoutContext } from '../CheckoutProvider';
|
|
@@ -48,7 +49,7 @@ export const usePreviewSubscriptionAction = () => {
|
|
|
48
49
|
const previewSubscriptionProps: PreviewSubscription = {
|
|
49
50
|
customerId: customer.id,
|
|
50
51
|
planId: plan?.id,
|
|
51
|
-
billableFeatures: subscription.billableFeatures,
|
|
52
|
+
billableFeatures: isEmpty(subscription.billableFeatures) ? undefined : subscription.billableFeatures,
|
|
52
53
|
addons: estimateAddons,
|
|
53
54
|
billingPeriod: subscription.billingPeriod,
|
|
54
55
|
promotionCode: promotionCode ?? subscription.promotionCode,
|
|
@@ -115,7 +115,9 @@ function AddonListItem({
|
|
|
115
115
|
)}
|
|
116
116
|
{!isAdded && (
|
|
117
117
|
<Button sx={{ paddingX: '22px', paddingY: '8px' }} onClick={() => handleQuantityChange(1)}>
|
|
118
|
-
|
|
118
|
+
<Typography color="primary.main" variant="body1">
|
|
119
|
+
{checkoutLocalization.addAddonText}
|
|
120
|
+
</Typography>
|
|
119
121
|
</Button>
|
|
120
122
|
)}
|
|
121
123
|
</Grid>
|
|
@@ -14,9 +14,9 @@ export type HandleSubmitResult = { results?: ApplySubscriptionResults; success:
|
|
|
14
14
|
|
|
15
15
|
export type UseSubmitProps = {
|
|
16
16
|
onSuccess?: () => void;
|
|
17
|
-
} & Pick<CheckoutContainerProps, 'onCheckout' | 'onCheckoutCompleted'>;
|
|
17
|
+
} & Pick<CheckoutContainerProps, 'onCheckout' | 'onCheckoutCompleted' | 'disableSuccessAnimation'>;
|
|
18
18
|
|
|
19
|
-
export function useSubmit({ onCheckout, onCheckoutCompleted, onSuccess }: UseSubmitProps) {
|
|
19
|
+
export function useSubmit({ onCheckout, onCheckoutCompleted, onSuccess, disableSuccessAnimation }: UseSubmitProps) {
|
|
20
20
|
const { stigg } = useStiggContext();
|
|
21
21
|
const { useNewPaymentMethod } = usePaymentStepModel();
|
|
22
22
|
const subscriptionState = useSubscriptionState();
|
|
@@ -83,25 +83,29 @@ export function useSubmit({ onCheckout, onCheckoutCompleted, onSuccess }: UseSub
|
|
|
83
83
|
|
|
84
84
|
setWidgetReadOnly(true);
|
|
85
85
|
|
|
86
|
+
let success = false;
|
|
86
87
|
if (onCheckout) {
|
|
87
88
|
const externalCheckoutResults = await onCheckout({ checkoutParams, checkoutAction });
|
|
88
89
|
if (!externalCheckoutResults.success && externalCheckoutResults.errorMessage) {
|
|
89
90
|
errorMessage = externalCheckoutResults.errorMessage;
|
|
90
91
|
}
|
|
92
|
+
success = externalCheckoutResults.success && !errorMessage;
|
|
91
93
|
} else {
|
|
92
94
|
const checkoutActionResults = await checkoutAction();
|
|
93
95
|
if (!checkoutActionResults.success && checkoutActionResults.errorMessage) {
|
|
94
96
|
errorMessage = checkoutActionResults.errorMessage;
|
|
95
97
|
}
|
|
98
|
+
success = checkoutActionResults.success && !errorMessage;
|
|
96
99
|
}
|
|
97
100
|
|
|
98
101
|
setWidgetReadOnly(false);
|
|
99
102
|
|
|
100
|
-
const success = !errorMessage && !!checkoutResults?.subscription;
|
|
101
103
|
if (success && onSuccess) {
|
|
102
104
|
onSuccess();
|
|
103
105
|
|
|
104
|
-
|
|
106
|
+
if (!disableSuccessAnimation) {
|
|
107
|
+
await delay(ANIMATION_DURATION); // Wait for animation to finish
|
|
108
|
+
}
|
|
105
109
|
}
|
|
106
110
|
|
|
107
111
|
await onCheckoutCompleted({ success, error: errorMessage });
|
|
@@ -64,17 +64,17 @@ export const BillingPeriodPicker = ({ plan, checkoutLocalization }: BillingPerio
|
|
|
64
64
|
</Typography>
|
|
65
65
|
|
|
66
66
|
<BillingPeriodOptions>
|
|
67
|
-
{!!
|
|
67
|
+
{!!monthlyPrices?.length && (
|
|
68
68
|
<BillingPeriodOption
|
|
69
|
-
key={BillingPeriod.
|
|
70
|
-
billingPeriod={BillingPeriod.
|
|
69
|
+
key={BillingPeriod.Monthly}
|
|
70
|
+
billingPeriod={BillingPeriod.Monthly}
|
|
71
71
|
isOnlyBillingPeriod={!hasBothBillingPeriods}
|
|
72
72
|
/>
|
|
73
73
|
)}
|
|
74
|
-
{!!
|
|
74
|
+
{!!annualPrices?.length && (
|
|
75
75
|
<BillingPeriodOption
|
|
76
|
-
key={BillingPeriod.
|
|
77
|
-
billingPeriod={BillingPeriod.
|
|
76
|
+
key={BillingPeriod.Annually}
|
|
77
|
+
billingPeriod={BillingPeriod.Annually}
|
|
78
78
|
isOnlyBillingPeriod={!hasBothBillingPeriods}
|
|
79
79
|
/>
|
|
80
80
|
)}
|
|
@@ -4,27 +4,73 @@ import Color from 'color';
|
|
|
4
4
|
import React from 'react';
|
|
5
5
|
import Lottie from 'react-lottie';
|
|
6
6
|
import animationData from '../../../assets/lottie/checkout-success.json';
|
|
7
|
+
import { Typography } from '../../common/Typography';
|
|
8
|
+
import { CheckoutLocalization } from '../textOverrides';
|
|
7
9
|
|
|
8
10
|
export const ANIMATION_DURATION = 5000;
|
|
9
11
|
|
|
10
|
-
const BACKGROUND_COLOR = Color('#0b2f7a').alpha(0.3).toString();
|
|
11
|
-
|
|
12
12
|
const CheckoutSuccessContainer = styled(Box)`
|
|
13
|
+
@keyframes blurFade {
|
|
14
|
+
0% {
|
|
15
|
+
background-color: ${({ theme }) => Color(theme.stigg.palette.backgroundPaper).alpha(0).toString()};
|
|
16
|
+
backdrop-filter: blur(0px);
|
|
17
|
+
}
|
|
18
|
+
100% {
|
|
19
|
+
background-color: ${({ theme }) => Color(theme.stigg.palette.backgroundPaper).alpha(0.9).toString()};
|
|
20
|
+
backdrop-filter: blur(6.5px);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
13
24
|
position: absolute;
|
|
14
25
|
top: 0;
|
|
15
26
|
left: 0;
|
|
16
27
|
bottom: 0;
|
|
17
28
|
right: 0;
|
|
18
|
-
|
|
29
|
+
z-index: 5;
|
|
30
|
+
background-color: ${({ theme }) => Color(theme.stigg.palette.backgroundPaper).alpha(0.9).toString()};
|
|
31
|
+
animation: blurFade 2s ease-in forwards;
|
|
32
|
+
display: flex;
|
|
33
|
+
flex-direction: column;
|
|
34
|
+
justify-content: center;
|
|
35
|
+
|
|
19
36
|
* rect {
|
|
20
37
|
fill: transparent;
|
|
21
38
|
}
|
|
39
|
+
|
|
40
|
+
& path {
|
|
41
|
+
stroke: ${({ theme }) => theme.stigg.palette.primary};
|
|
42
|
+
}
|
|
43
|
+
`;
|
|
44
|
+
|
|
45
|
+
const CheckoutSuccessText = styled(Typography)`
|
|
46
|
+
@keyframes fadeIn {
|
|
47
|
+
0% {
|
|
48
|
+
opacity: 0;
|
|
49
|
+
}
|
|
50
|
+
75% {
|
|
51
|
+
opacity: 0;
|
|
52
|
+
}
|
|
53
|
+
100% {
|
|
54
|
+
opacity: 1;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
align-self: center;
|
|
59
|
+
animation: fadeIn 5s ease-in forwards;
|
|
22
60
|
`;
|
|
23
61
|
|
|
24
|
-
export function CheckoutSuccess() {
|
|
62
|
+
export function CheckoutSuccess({ checkoutLocalization }: { checkoutLocalization: CheckoutLocalization }) {
|
|
25
63
|
return (
|
|
26
|
-
<CheckoutSuccessContainer>
|
|
27
|
-
<Lottie
|
|
64
|
+
<CheckoutSuccessContainer className="stigg-checkout-success-container">
|
|
65
|
+
<Lottie
|
|
66
|
+
width={350}
|
|
67
|
+
height="auto"
|
|
68
|
+
isClickToPauseDisabled
|
|
69
|
+
options={{ loop: false, autoplay: true, animationData }}
|
|
70
|
+
/>
|
|
71
|
+
<CheckoutSuccessText variant="h1" color="primary.main">
|
|
72
|
+
{checkoutLocalization.checkoutSuccessText}
|
|
73
|
+
</CheckoutSuccessText>
|
|
28
74
|
</CheckoutSuccessContainer>
|
|
29
75
|
);
|
|
30
76
|
}
|
|
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
|
|
2
2
|
import partition from 'lodash/partition';
|
|
3
3
|
import styled from '@emotion/styled/macro';
|
|
4
4
|
import { Box, CircularProgress, Divider, Grid, Paper } from '@mui/material';
|
|
5
|
-
import { BillingModel
|
|
5
|
+
import { BillingModel } from '@stigg/js-client-sdk';
|
|
6
6
|
import { PoweredByStigg } from '../../common/PoweredByStigg';
|
|
7
7
|
import { Typography } from '../../common/Typography';
|
|
8
8
|
import { useChargesSort } from '../../hooks/useChargeSort';
|
|
@@ -60,34 +60,35 @@ const TotalDueText = styled(Typography)`
|
|
|
60
60
|
|
|
61
61
|
function resolveCheckoutButtonText({
|
|
62
62
|
isLastStep,
|
|
63
|
-
|
|
63
|
+
isFreeDowngrade,
|
|
64
64
|
checkoutLocalization,
|
|
65
|
-
|
|
65
|
+
isPlanUpdate,
|
|
66
66
|
}: {
|
|
67
67
|
isLastStep?: boolean;
|
|
68
|
-
|
|
69
|
-
subscriptionPreview?: SubscriptionPreview | null;
|
|
68
|
+
isFreeDowngrade: boolean;
|
|
70
69
|
checkoutLocalization: CheckoutLocalization;
|
|
70
|
+
isPlanUpdate?: boolean;
|
|
71
71
|
}) {
|
|
72
72
|
if (!isLastStep) {
|
|
73
73
|
return checkoutLocalization.checkoutButton.nextText;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
if (
|
|
77
|
-
return checkoutLocalization.checkoutButton.
|
|
76
|
+
if (isPlanUpdate) {
|
|
77
|
+
return checkoutLocalization.checkoutButton.updateText;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
if (
|
|
81
|
-
return checkoutLocalization.checkoutButton.
|
|
80
|
+
if (isFreeDowngrade) {
|
|
81
|
+
return checkoutLocalization.checkoutButton.downgradeToFreeText;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
return checkoutLocalization.checkoutButton.
|
|
84
|
+
return checkoutLocalization.checkoutButton.upgradeText;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
export const CheckoutSummary = ({
|
|
88
88
|
onCheckout,
|
|
89
89
|
onCheckoutCompleted,
|
|
90
90
|
disablePromotionCode,
|
|
91
|
+
disableSuccessAnimation,
|
|
91
92
|
isFreeDowngrade,
|
|
92
93
|
}: CheckoutContainerProps & { isFreeDowngrade: boolean }) => {
|
|
93
94
|
const [isCheckoutCompletedSuccessfully, setIsCheckoutCompletedSuccessfully] = useState(false);
|
|
@@ -106,6 +107,7 @@ export const CheckoutSummary = ({
|
|
|
106
107
|
const { subscriptionPreview, isFetchingSubscriptionPreview } = usePreviewSubscription();
|
|
107
108
|
|
|
108
109
|
const { handleSubmit, isLoading } = useSubmit({
|
|
110
|
+
disableSuccessAnimation,
|
|
109
111
|
onCheckout,
|
|
110
112
|
onCheckoutCompleted,
|
|
111
113
|
onSuccess: () => {
|
|
@@ -257,11 +259,12 @@ export const CheckoutSummary = ({
|
|
|
257
259
|
subscriptionPreview={subscriptionPreview}
|
|
258
260
|
isFetchingSubscriptionPreview={isFetchingSubscriptionPreview}
|
|
259
261
|
checkoutLocalization={checkoutLocalization}
|
|
262
|
+
billingPeriod={subscription.billingPeriod}
|
|
260
263
|
/>
|
|
261
264
|
|
|
262
265
|
<Button
|
|
263
266
|
$success={isCheckoutCompletedSuccessfully}
|
|
264
|
-
$error={isLastStep &&
|
|
267
|
+
$error={isLastStep && isFreeDowngrade}
|
|
265
268
|
disabled={isLoading || isFetchingSubscriptionPreview || progressBar.progressBarState.isDisabled}
|
|
266
269
|
className="stigg-checkout-summary-cta-button"
|
|
267
270
|
sx={{ textTransform: 'none', borderRadius: '10px', marginTop: '24px', height: '36px' }}
|
|
@@ -277,7 +280,12 @@ export const CheckoutSummary = ({
|
|
|
277
280
|
) : isLoading || isFetchingSubscriptionPreview ? (
|
|
278
281
|
<CircularProgress size={20} sx={{ color: 'white' }} />
|
|
279
282
|
) : (
|
|
280
|
-
resolveCheckoutButtonText({
|
|
283
|
+
resolveCheckoutButtonText({
|
|
284
|
+
isLastStep,
|
|
285
|
+
isFreeDowngrade,
|
|
286
|
+
checkoutLocalization,
|
|
287
|
+
isPlanUpdate: !!activeSubscription && activeSubscription?.plan.id === plan?.id,
|
|
288
|
+
})
|
|
281
289
|
)}
|
|
282
290
|
</Typography>
|
|
283
291
|
</Button>
|
|
@@ -287,7 +295,9 @@ export const CheckoutSummary = ({
|
|
|
287
295
|
showWatermark
|
|
288
296
|
style={{ marginTop: 8, display: 'flex', justifyContent: 'center' }}
|
|
289
297
|
/>
|
|
290
|
-
{isCheckoutCompletedSuccessfully &&
|
|
298
|
+
{!disableSuccessAnimation && isCheckoutCompletedSuccessfully && (
|
|
299
|
+
<CheckoutSuccess checkoutLocalization={checkoutLocalization} />
|
|
300
|
+
)}
|
|
291
301
|
</Box>
|
|
292
302
|
);
|
|
293
303
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import moment from 'moment';
|
|
3
|
-
import { Plan, Subscription, SubscriptionPreview } from '@stigg/js-client-sdk';
|
|
3
|
+
import { BillingModel, BillingPeriod, Plan, Subscription, SubscriptionPreview } from '@stigg/js-client-sdk';
|
|
4
4
|
import { Typography } from '../../../common/Typography';
|
|
5
5
|
import { currencyPriceFormatter } from '../../../utils/currencyUtils';
|
|
6
6
|
import { CheckoutLocalization } from '../../textOverrides';
|
|
@@ -12,6 +12,7 @@ export type CheckoutCaptionProps = {
|
|
|
12
12
|
activeSubscription?: Subscription | null;
|
|
13
13
|
plan?: Plan;
|
|
14
14
|
checkoutLocalization: CheckoutLocalization;
|
|
15
|
+
billingPeriod: BillingPeriod;
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
// TODO: move to localization
|
|
@@ -21,12 +22,17 @@ const RemainingCreditsCaption = ({ subscriptionPreview, isFetchingSubscriptionPr
|
|
|
21
22
|
return null;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
const
|
|
25
|
+
const positiveAmount = subscriptionPreview.proration.netAmount.amount * -1;
|
|
26
|
+
|
|
27
|
+
const credits = currencyPriceFormatter({
|
|
28
|
+
amount: positiveAmount,
|
|
29
|
+
currency: subscriptionPreview.proration.netAmount.currency,
|
|
30
|
+
});
|
|
25
31
|
|
|
26
32
|
return (
|
|
27
33
|
<Typography variant="caption" style={{ marginTop: 14 }}>
|
|
28
34
|
<WithSkeleton isLoading={isFetchingSubscriptionPreview} width="100%">
|
|
29
|
-
{`Your
|
|
35
|
+
{`Your account will be granted credits worth ${credits} for unused time, which will be automatically applied to future payments.`}
|
|
30
36
|
</WithSkeleton>
|
|
31
37
|
</Typography>
|
|
32
38
|
);
|
|
@@ -57,49 +63,46 @@ const ScheduledUpdatesCaption = ({
|
|
|
57
63
|
);
|
|
58
64
|
};
|
|
59
65
|
|
|
60
|
-
const ChargeDueTodayCaption = ({ subscriptionPreview, plan, isFetchingSubscriptionPreview }: CheckoutCaptionProps) => {
|
|
61
|
-
if (!subscriptionPreview?.total || subscriptionPreview.total.amount <= 0) {
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const total = currencyPriceFormatter(subscriptionPreview.total);
|
|
66
|
-
const usedCredits = !!subscriptionPreview.credits?.used;
|
|
67
|
-
|
|
68
|
-
return (
|
|
69
|
-
<Typography variant="caption" style={{ marginTop: 14 }}>
|
|
70
|
-
<WithSkeleton isLoading={isFetchingSubscriptionPreview} width="100%">
|
|
71
|
-
{`Today we’ll charge you ${total} (incl. taxes) for your new ${plan?.displayName} plan${
|
|
72
|
-
usedCredits ? ' minus credits left from your existing plan.' : ''
|
|
73
|
-
} `}
|
|
74
|
-
</WithSkeleton>
|
|
75
|
-
</Typography>
|
|
76
|
-
);
|
|
77
|
-
};
|
|
78
|
-
|
|
79
66
|
const NextBillingCaption = ({
|
|
80
67
|
subscriptionPreview,
|
|
81
68
|
activeSubscription,
|
|
82
69
|
plan,
|
|
83
70
|
isFetchingSubscriptionPreview,
|
|
71
|
+
billingPeriod,
|
|
84
72
|
}: CheckoutCaptionProps) => {
|
|
85
73
|
if (!subscriptionPreview?.subscription?.total) {
|
|
86
74
|
return null;
|
|
87
75
|
}
|
|
88
76
|
|
|
89
|
-
const isUpdatingSubscription = activeSubscription?.plan?.id && plan?.id && activeSubscription.plan.id === plan.id;
|
|
90
|
-
const isScheduledPlanDowngrade = subscriptionPreview.isPlanDowngrade && subscriptionPreview.hasScheduledUpdates;
|
|
91
77
|
const currentBillingPeriodEnd = subscriptionPreview.hasScheduledUpdates
|
|
92
78
|
? activeSubscription?.currentBillingPeriodEnd
|
|
93
79
|
: subscriptionPreview?.billingPeriodRange.end;
|
|
94
80
|
const billingDate = moment(currentBillingPeriodEnd).format('MMM D, YYYY');
|
|
95
81
|
const total = currencyPriceFormatter(subscriptionPreview?.subscription.total);
|
|
96
82
|
|
|
83
|
+
const hasUnitBasedPricing = plan?.pricePoints.some((price) => price.pricingModel === BillingModel.UsageBased);
|
|
84
|
+
const hasNonUnitBasedPricing = plan?.pricePoints.some((price) => price.pricingModel !== BillingModel.UsageBased);
|
|
85
|
+
|
|
86
|
+
let text = 'We will bill you ';
|
|
87
|
+
let totalAmountText = `${total} `;
|
|
88
|
+
|
|
89
|
+
if (hasUnitBasedPricing) {
|
|
90
|
+
if (!hasNonUnitBasedPricing) {
|
|
91
|
+
totalAmountText = 'for';
|
|
92
|
+
} else {
|
|
93
|
+
totalAmountText += '+';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
totalAmountText += ' applicable usage-based fees for this subscription ';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const billingPeriodText = billingPeriod === BillingPeriod.Monthly ? 'month' : 'year';
|
|
100
|
+
text += `${totalAmountText}for this subscription every ${billingPeriodText} on ${billingDate}, unless you cancel.`;
|
|
101
|
+
|
|
97
102
|
return (
|
|
98
103
|
<Typography variant="caption" style={{ marginTop: 14 }}>
|
|
99
104
|
<WithSkeleton isLoading={isFetchingSubscriptionPreview} width="100%">
|
|
100
|
-
{
|
|
101
|
-
isScheduledPlanDowngrade ? 'start' : 'renew'
|
|
102
|
-
} on ${billingDate} for ${total} (incl. taxes) unless you cancel it.`}
|
|
105
|
+
{text}
|
|
103
106
|
</WithSkeleton>
|
|
104
107
|
</Typography>
|
|
105
108
|
);
|
|
@@ -112,8 +115,6 @@ export function CheckoutCaptions(props: CheckoutCaptionProps) {
|
|
|
112
115
|
|
|
113
116
|
<ScheduledUpdatesCaption {...props} />
|
|
114
117
|
|
|
115
|
-
<ChargeDueTodayCaption {...props} />
|
|
116
|
-
|
|
117
118
|
<NextBillingCaption {...props} />
|
|
118
119
|
</>
|
|
119
120
|
);
|
|
@@ -19,13 +19,14 @@ export type CheckoutLocalization = {
|
|
|
19
19
|
changesWillApplyAtEndOfBillingPeriod: string | ((params: { billingPeriodEnd: Date }) => string);
|
|
20
20
|
checkoutButton: {
|
|
21
21
|
nextText: string;
|
|
22
|
-
|
|
22
|
+
updateText: string;
|
|
23
|
+
downgradeToFreeText: string;
|
|
23
24
|
upgradeText: string;
|
|
24
|
-
purchaseText: string;
|
|
25
25
|
};
|
|
26
26
|
appliedCreditsTitle: string;
|
|
27
27
|
taxTitle: (params: { taxDetails: SubscriptionPreviewTaxDetails }) => string;
|
|
28
28
|
downgradeToFreeAlertText: (params: { plan: Plan }) => string;
|
|
29
|
+
checkoutSuccessText: string;
|
|
29
30
|
};
|
|
30
31
|
|
|
31
32
|
export function getResolvedCheckoutLocalize(
|
|
@@ -41,22 +42,23 @@ export function getResolvedCheckoutLocalize(
|
|
|
41
42
|
baseChargeText: () => 'Base charge',
|
|
42
43
|
totalText: 'Total due today',
|
|
43
44
|
subTotalText: 'Subtotal',
|
|
44
|
-
addCouponCodeText: 'Add
|
|
45
|
-
couponCodeTitle: '
|
|
45
|
+
addCouponCodeText: 'Add promotion code',
|
|
46
|
+
couponCodeTitle: 'Promotion code',
|
|
46
47
|
addonsSectionTitle: 'Add-ons',
|
|
47
48
|
changesWillApplyAtEndOfBillingPeriod: ({ billingPeriodEnd }) =>
|
|
48
|
-
`Your changes will
|
|
49
|
+
`Your changes will be applied at the end of your billing cycle on ${moment(billingPeriodEnd).format(
|
|
49
50
|
'MMM D, YYYY',
|
|
50
51
|
)}.`,
|
|
51
52
|
checkoutButton: {
|
|
52
53
|
nextText: 'Next',
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
updateText: 'Update subscription',
|
|
55
|
+
downgradeToFreeText: 'Cancel subscription',
|
|
56
|
+
upgradeText: 'Subscribe',
|
|
56
57
|
},
|
|
57
58
|
appliedCreditsTitle: 'Applied credits',
|
|
58
59
|
taxTitle: ({ taxDetails }) => `Tax (${taxDetails?.percentage}%)`,
|
|
59
60
|
downgradeToFreeAlertText: () => `We’re sorry to see you cancel your paid subscription 😭`,
|
|
61
|
+
checkoutSuccessText: 'Payment successful',
|
|
60
62
|
};
|
|
61
63
|
|
|
62
64
|
return merge(checkoutDefaultLocalization, localizeOverride);
|
|
@@ -46,6 +46,7 @@ const Template: ComponentStory<any> = (args) => (
|
|
|
46
46
|
onChangePlan={({ currentPlan }) => {
|
|
47
47
|
console.log('plan changed clicked!', { currentPlan });
|
|
48
48
|
}}
|
|
49
|
+
// disableSuccessAnimation
|
|
49
50
|
// billingInformation={{ taxPercentage: 27 }}
|
|
50
51
|
/>
|
|
51
52
|
</Wrapper>
|
|
@@ -55,8 +56,8 @@ export const DefaultCheckout = Template.bind({});
|
|
|
55
56
|
DefaultCheckout.args = {
|
|
56
57
|
...defaultArgsWithCustomer,
|
|
57
58
|
planId: 'plan-revvenu-essentials',
|
|
58
|
-
baseUri: 'http://localhost:4000',
|
|
59
|
-
apiKey: 'client-72b058a6-0f22-4c86-adce-bf266d12e12e:9f356ceb-c94c-42a4-9572-10b12824da81',
|
|
60
|
-
|
|
61
|
-
|
|
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',
|
|
62
63
|
};
|