@stigg/react-sdk 4.4.0-beta.1 → 4.4.0-beta.11
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 +1 -1
- package/dist/components/checkout/CheckoutContainer.d.ts +7 -2
- package/dist/components/checkout/CheckoutProvider.d.ts +3 -1
- package/dist/components/checkout/components/Button.d.ts +0 -1
- package/dist/components/checkout/components/ChangePlanButton.d.ts +8 -0
- package/dist/components/checkout/components/DowngradeToFreeContainer.d.ts +28 -0
- 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/usePaymentStepModel.d.ts +8 -2
- package/dist/components/checkout/hooks/usePreviewSubscription.d.ts +8 -1
- package/dist/components/checkout/hooks/useProgressBarModel.d.ts +3 -0
- package/dist/components/checkout/hooks/useSubscriptionModel.d.ts +2 -1
- package/dist/components/checkout/index.d.ts +2 -0
- package/dist/components/checkout/progressBar/CheckoutProgressBar.style.d.ts +3 -2
- package/dist/components/checkout/steps/payment/PaymentMethods.d.ts +3 -2
- package/dist/components/checkout/steps/payment/PaymentStep.d.ts +2 -1
- package/dist/components/checkout/steps/payment/stripe/StripePaymentForm.d.ts +2 -1
- package/dist/components/checkout/steps/payment/stripe/stripe.utils.d.ts +4 -0
- package/dist/components/checkout/steps/payment/stripe/useSubmit.d.ts +4 -3
- package/dist/components/checkout/steps/plan/BillingPeriodPicker.style.d.ts +1 -0
- package/dist/components/checkout/steps/plan/CheckoutChargeList.d.ts +6 -1
- package/dist/components/checkout/summary/CheckoutSuccess.d.ts +4 -1
- package/dist/components/checkout/summary/CheckoutSummary.d.ts +3 -1
- package/dist/components/checkout/summary/components/CheckoutCaptions.d.ts +2 -1
- package/dist/components/checkout/summary/components/LineItems.d.ts +8 -8
- package/dist/components/checkout/textOverrides.d.ts +28 -3
- package/dist/components/checkout/theme.d.ts +0 -1
- package/dist/components/checkout/types.d.ts +7 -0
- package/dist/components/customerPortal/subscriptionOverview/subscriptionView/SubscriptionView.style.d.ts +1 -1
- package/dist/components/paywall/paywallTextOverrides.d.ts +4 -0
- package/dist/components/utils/getPaidPriceText.d.ts +3 -1
- package/dist/react-sdk.cjs.development.js +1234 -599
- 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 +1273 -622
- package/dist/react-sdk.esm.js.map +1 -1
- package/dist/theme/getResolvedTheme.d.ts +1 -0
- package/dist/theme/types.d.ts +1 -0
- package/package.json +2 -2
- package/src/assets/payment-method.svg +3 -10
- package/src/components/checkout/Checkout.tsx +2 -1
- package/src/components/checkout/CheckoutContainer.style.ts +1 -0
- package/src/components/checkout/CheckoutContainer.tsx +59 -28
- package/src/components/checkout/CheckoutProvider.tsx +18 -18
- package/src/components/checkout/components/Button.tsx +19 -35
- package/src/components/checkout/components/ChangePlanButton.tsx +32 -0
- package/src/components/checkout/components/DowngradeToFreeContainer.tsx +115 -0
- package/src/components/checkout/components/Skeletons.style.ts +4 -1
- package/src/components/checkout/components/StyledArrow.tsx +9 -0
- package/src/components/checkout/hooks/useCheckoutModel.ts +12 -2
- package/src/components/checkout/hooks/usePaymentStepModel.ts +22 -3
- package/src/components/checkout/hooks/usePlanStepModel.ts +25 -10
- package/src/components/checkout/hooks/usePreviewSubscription.ts +112 -40
- package/src/components/checkout/hooks/useProgressBarModel.ts +18 -0
- package/src/components/checkout/hooks/useSubscriptionModel.ts +8 -2
- package/src/components/checkout/hooks/useSubscriptionState.ts +2 -1
- package/src/components/checkout/index.ts +2 -0
- package/src/components/checkout/planHeader/PlanHeader.tsx +19 -34
- package/src/components/checkout/progressBar/CheckoutProgressBar.style.ts +6 -3
- package/src/components/checkout/progressBar/CheckoutProgressBar.tsx +13 -9
- package/src/components/checkout/promotionCode/AddPromotionCode.tsx +8 -8
- package/src/components/checkout/steps/addons/CheckoutAddonsStep.tsx +58 -11
- package/src/components/checkout/steps/payment/PaymentMethods.style.ts +1 -0
- package/src/components/checkout/steps/payment/PaymentMethods.tsx +13 -6
- package/src/components/checkout/steps/payment/PaymentStep.tsx +3 -1
- package/src/components/checkout/steps/payment/stripe/StripePaymentForm.tsx +35 -4
- package/src/components/checkout/steps/payment/stripe/stripe.utils.ts +4 -3
- package/src/components/checkout/steps/payment/stripe/useSubmit.ts +61 -48
- package/src/components/checkout/steps/plan/BillingPeriodPicker.style.tsx +27 -6
- package/src/components/checkout/steps/plan/BillingPeriodPicker.tsx +26 -5
- package/src/components/checkout/steps/plan/CheckoutChargeList.tsx +62 -12
- package/src/components/checkout/summary/CheckoutSuccess.tsx +52 -6
- package/src/components/checkout/summary/CheckoutSummary.tsx +74 -48
- package/src/components/checkout/summary/components/CheckoutCaptions.tsx +30 -29
- package/src/components/checkout/summary/components/LineItems.tsx +18 -33
- package/src/components/checkout/textOverrides.ts +33 -13
- package/src/components/checkout/theme.ts +0 -4
- package/src/components/checkout/types.ts +9 -0
- package/src/components/common/Icon.tsx +4 -6
- package/src/components/common/mapExternalTheme.ts +1 -2
- package/src/components/paywall/PlanPrice.tsx +10 -2
- package/src/components/paywall/paywallTextOverrides.ts +3 -0
- package/src/components/utils/getPaidPriceText.ts +8 -2
- package/src/components/utils/getPlanPrice.ts +1 -1
- package/src/stories/Checkout.stories.tsx +6 -5
- 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/dist/components/checkout/steps/surprise/SurpriseStep.d.ts +0 -2
- package/src/assets/nyancat.svg +0 -634
- package/src/components/checkout/planHeader/PlanHeader.style.tsx +0 -23
- package/src/components/checkout/steps/surprise/SurpriseStep.tsx +0 -27
|
@@ -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';
|
|
@@ -44,6 +44,7 @@ SummaryCard.defaultProps = {
|
|
|
44
44
|
|
|
45
45
|
const PlanName = styled(Typography)`
|
|
46
46
|
margin-bottom: 16px;
|
|
47
|
+
font-weight: 500;
|
|
47
48
|
`;
|
|
48
49
|
|
|
49
50
|
const StyledDivider = styled(Divider)`
|
|
@@ -60,31 +61,37 @@ const TotalDueText = styled(Typography)`
|
|
|
60
61
|
|
|
61
62
|
function resolveCheckoutButtonText({
|
|
62
63
|
isLastStep,
|
|
63
|
-
|
|
64
|
+
isFreeDowngrade,
|
|
64
65
|
checkoutLocalization,
|
|
65
|
-
|
|
66
|
+
isPlanUpdate,
|
|
66
67
|
}: {
|
|
67
68
|
isLastStep?: boolean;
|
|
68
|
-
|
|
69
|
-
subscriptionPreview?: SubscriptionPreview | null;
|
|
69
|
+
isFreeDowngrade: boolean;
|
|
70
70
|
checkoutLocalization: CheckoutLocalization;
|
|
71
|
+
isPlanUpdate?: boolean;
|
|
71
72
|
}) {
|
|
72
73
|
if (!isLastStep) {
|
|
73
74
|
return checkoutLocalization.checkoutButton.nextText;
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
if (
|
|
77
|
-
return checkoutLocalization.checkoutButton.
|
|
77
|
+
if (isPlanUpdate) {
|
|
78
|
+
return checkoutLocalization.checkoutButton.updateText;
|
|
78
79
|
}
|
|
79
80
|
|
|
80
|
-
if (
|
|
81
|
-
return checkoutLocalization.checkoutButton.
|
|
81
|
+
if (isFreeDowngrade) {
|
|
82
|
+
return checkoutLocalization.checkoutButton.downgradeToFreeText;
|
|
82
83
|
}
|
|
83
84
|
|
|
84
|
-
return checkoutLocalization.checkoutButton.
|
|
85
|
+
return checkoutLocalization.checkoutButton.upgradeText;
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
export const CheckoutSummary = ({
|
|
88
|
+
export const CheckoutSummary = ({
|
|
89
|
+
onCheckout,
|
|
90
|
+
onCheckoutCompleted,
|
|
91
|
+
disablePromotionCode,
|
|
92
|
+
disableSuccessAnimation,
|
|
93
|
+
isFreeDowngrade,
|
|
94
|
+
}: CheckoutContainerProps & { isFreeDowngrade: boolean }) => {
|
|
88
95
|
const [isCheckoutCompletedSuccessfully, setIsCheckoutCompletedSuccessfully] = useState(false);
|
|
89
96
|
const { setErrorMessage } = usePaymentStepModel();
|
|
90
97
|
const progressBar = useProgressBarModel();
|
|
@@ -96,12 +103,12 @@ export const CheckoutSummary = ({ onCheckout, onCheckoutCompleted }: CheckoutCon
|
|
|
96
103
|
);
|
|
97
104
|
const [baseCharges, usageCharges] = partition(planPrices, (price) => price.pricingModel === BillingModel.FlatFee);
|
|
98
105
|
const [baseCharge] = baseCharges || [];
|
|
99
|
-
const
|
|
100
|
-
const isLastStep = progressBar.isCheckoutComplete && progressBar.isLastStep;
|
|
106
|
+
const isLastStep = isFreeDowngrade || (progressBar.isCheckoutComplete && progressBar.isLastStep);
|
|
101
107
|
|
|
102
108
|
const { subscriptionPreview, isFetchingSubscriptionPreview } = usePreviewSubscription();
|
|
103
109
|
|
|
104
110
|
const { handleSubmit, isLoading } = useSubmit({
|
|
111
|
+
disableSuccessAnimation,
|
|
105
112
|
onCheckout,
|
|
106
113
|
onCheckoutCompleted,
|
|
107
114
|
onSuccess: () => {
|
|
@@ -119,11 +126,9 @@ export const CheckoutSummary = ({ onCheckout, onCheckoutCompleted }: CheckoutCon
|
|
|
119
126
|
if (errorMessage) {
|
|
120
127
|
setErrorMessage(errorMessage);
|
|
121
128
|
setIsCheckoutCompletedSuccessfully(false);
|
|
122
|
-
|
|
129
|
+
} else {
|
|
130
|
+
setErrorMessage(undefined);
|
|
123
131
|
}
|
|
124
|
-
|
|
125
|
-
setErrorMessage(undefined);
|
|
126
|
-
setIsCheckoutCompletedSuccessfully(true);
|
|
127
132
|
};
|
|
128
133
|
|
|
129
134
|
const onCheckoutClick = async (e: any): Promise<void> => {
|
|
@@ -138,6 +143,14 @@ export const CheckoutSummary = ({ onCheckout, onCheckoutCompleted }: CheckoutCon
|
|
|
138
143
|
}
|
|
139
144
|
};
|
|
140
145
|
|
|
146
|
+
const showPromotionCodeLine = !disablePromotionCode && !isFreeDowngrade;
|
|
147
|
+
const showDiscountLine = !!subscriptionPreview?.subTotal && !!subscriptionPreview?.discount && !isFreeDowngrade;
|
|
148
|
+
const showTaxLine =
|
|
149
|
+
!!subscriptionPreview?.taxDetails && !!subscriptionPreview?.tax && subscriptionPreview?.tax.amount > 0;
|
|
150
|
+
const showAppliedCreditsLine = !!subscriptionPreview?.credits && subscriptionPreview?.credits.used.amount > 0;
|
|
151
|
+
|
|
152
|
+
const showAdditionalDivider = showPromotionCodeLine || showDiscountLine || showTaxLine || showAppliedCreditsLine;
|
|
153
|
+
|
|
141
154
|
return (
|
|
142
155
|
<Box flex={1}>
|
|
143
156
|
<SummaryCard>
|
|
@@ -146,7 +159,7 @@ export const CheckoutSummary = ({ onCheckout, onCheckoutCompleted }: CheckoutCon
|
|
|
146
159
|
{baseCharge && (
|
|
147
160
|
<LineItemContainer>
|
|
148
161
|
<LineItemRow>
|
|
149
|
-
<Typography variant="
|
|
162
|
+
<Typography variant="body1" color="secondary">
|
|
150
163
|
{typeof checkoutLocalization.baseChargeText === 'function'
|
|
151
164
|
? checkoutLocalization.baseChargeText({ billingPeriod: subscription.billingPeriod })
|
|
152
165
|
: checkoutLocalization.baseChargeText}
|
|
@@ -188,11 +201,13 @@ export const CheckoutSummary = ({ onCheckout, onCheckoutCompleted }: CheckoutCon
|
|
|
188
201
|
|
|
189
202
|
if (!addonPrice) return null;
|
|
190
203
|
|
|
204
|
+
const addonQuantity = addon.quantity && addon.quantity > 0 ? addon.quantity : 1;
|
|
205
|
+
|
|
191
206
|
return (
|
|
192
207
|
<BilledPriceLineItem
|
|
193
208
|
key={addon?.addon?.id}
|
|
194
209
|
label={addon.addon.displayName}
|
|
195
|
-
quantity={
|
|
210
|
+
quantity={addonQuantity}
|
|
196
211
|
price={addonPrice}
|
|
197
212
|
/>
|
|
198
213
|
);
|
|
@@ -211,30 +226,32 @@ export const CheckoutSummary = ({ onCheckout, onCheckoutCompleted }: CheckoutCon
|
|
|
211
226
|
|
|
212
227
|
<StyledDivider className="stigg-checkout-summary-divider" />
|
|
213
228
|
|
|
214
|
-
{
|
|
215
|
-
<>
|
|
216
|
-
<PromotionCodeSection checkoutLocalization={checkoutLocalization} />
|
|
229
|
+
{showPromotionCodeLine && <PromotionCodeSection checkoutLocalization={checkoutLocalization} />}
|
|
217
230
|
|
|
218
|
-
|
|
219
|
-
|
|
231
|
+
{showDiscountLine && (
|
|
232
|
+
<DiscountLineItem
|
|
233
|
+
subscriptionPreview={subscriptionPreview!}
|
|
234
|
+
isFetchingSubscriptionPreview={isFetchingSubscriptionPreview}
|
|
235
|
+
/>
|
|
220
236
|
)}
|
|
221
237
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
238
|
+
{showTaxLine && (
|
|
239
|
+
<TaxLineItem
|
|
240
|
+
subscriptionPreview={subscriptionPreview!}
|
|
241
|
+
isFetchingSubscriptionPreview={isFetchingSubscriptionPreview}
|
|
242
|
+
checkoutLocalization={checkoutLocalization}
|
|
243
|
+
/>
|
|
244
|
+
)}
|
|
227
245
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
246
|
+
{showAppliedCreditsLine && (
|
|
247
|
+
<AppliedCreditsLineItem
|
|
248
|
+
subscriptionPreview={subscriptionPreview!}
|
|
249
|
+
isFetchingSubscriptionPreview={isFetchingSubscriptionPreview}
|
|
250
|
+
checkoutLocalization={checkoutLocalization}
|
|
251
|
+
/>
|
|
252
|
+
)}
|
|
232
253
|
|
|
233
|
-
<
|
|
234
|
-
subscriptionPreview={subscriptionPreview}
|
|
235
|
-
isFetchingSubscriptionPreview={isFetchingSubscriptionPreview}
|
|
236
|
-
checkoutLocalization={checkoutLocalization}
|
|
237
|
-
/>
|
|
254
|
+
{showAdditionalDivider && <StyledDivider className="stigg-checkout-summary-divider" />}
|
|
238
255
|
|
|
239
256
|
<LineItemContainer>
|
|
240
257
|
<LineItemRow>
|
|
@@ -253,15 +270,15 @@ export const CheckoutSummary = ({ onCheckout, onCheckoutCompleted }: CheckoutCon
|
|
|
253
270
|
subscriptionPreview={subscriptionPreview}
|
|
254
271
|
isFetchingSubscriptionPreview={isFetchingSubscriptionPreview}
|
|
255
272
|
checkoutLocalization={checkoutLocalization}
|
|
273
|
+
billingPeriod={subscription.billingPeriod}
|
|
256
274
|
/>
|
|
257
275
|
|
|
258
276
|
<Button
|
|
259
|
-
disableRipple={isLoading}
|
|
260
|
-
$isLoading={isLoading}
|
|
261
277
|
$success={isCheckoutCompletedSuccessfully}
|
|
262
|
-
$error={isLastStep &&
|
|
278
|
+
$error={isLastStep && isFreeDowngrade}
|
|
279
|
+
disabled={isLoading || isFetchingSubscriptionPreview || progressBar.progressBarState.isDisabled}
|
|
263
280
|
className="stigg-checkout-summary-cta-button"
|
|
264
|
-
sx={{ textTransform: 'none', borderRadius: '10px', marginTop: '24px' }}
|
|
281
|
+
sx={{ textTransform: 'none', borderRadius: '10px', marginTop: '24px', height: '36px' }}
|
|
265
282
|
variant="contained"
|
|
266
283
|
size="medium"
|
|
267
284
|
onClick={(e: any) => {
|
|
@@ -269,11 +286,18 @@ export const CheckoutSummary = ({ onCheckout, onCheckoutCompleted }: CheckoutCon
|
|
|
269
286
|
}}
|
|
270
287
|
fullWidth>
|
|
271
288
|
<Typography className="stigg-checkout-summary-cta-button-text" color="white" style={{ display: 'flex' }}>
|
|
272
|
-
{isCheckoutCompletedSuccessfully
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
289
|
+
{isCheckoutCompletedSuccessfully ? (
|
|
290
|
+
<Icon icon="Check" style={{ display: 'contents' }} />
|
|
291
|
+
) : isLoading || isFetchingSubscriptionPreview ? (
|
|
292
|
+
<CircularProgress size={20} sx={{ color: 'white' }} />
|
|
293
|
+
) : (
|
|
294
|
+
resolveCheckoutButtonText({
|
|
295
|
+
isLastStep,
|
|
296
|
+
isFreeDowngrade,
|
|
297
|
+
checkoutLocalization,
|
|
298
|
+
isPlanUpdate: !!activeSubscription && activeSubscription?.plan.id === plan?.id,
|
|
299
|
+
})
|
|
300
|
+
)}
|
|
277
301
|
</Typography>
|
|
278
302
|
</Button>
|
|
279
303
|
</SummaryCard>
|
|
@@ -282,7 +306,9 @@ export const CheckoutSummary = ({ onCheckout, onCheckoutCompleted }: CheckoutCon
|
|
|
282
306
|
showWatermark
|
|
283
307
|
style={{ marginTop: 8, display: 'flex', justifyContent: 'center' }}
|
|
284
308
|
/>
|
|
285
|
-
{isCheckoutCompletedSuccessfully &&
|
|
309
|
+
{!disableSuccessAnimation && isCheckoutCompletedSuccessfully && (
|
|
310
|
+
<CheckoutSuccess checkoutLocalization={checkoutLocalization} />
|
|
311
|
+
)}
|
|
286
312
|
</Box>
|
|
287
313
|
);
|
|
288
314
|
};
|
|
@@ -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) {
|
|
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
|
);
|
|
@@ -21,24 +21,16 @@ export const LineItemRow = styled.div`
|
|
|
21
21
|
justify-content: space-between;
|
|
22
22
|
`;
|
|
23
23
|
|
|
24
|
-
export const getPriceString = ({
|
|
25
|
-
amountPerMonth,
|
|
26
|
-
price,
|
|
27
|
-
quantity,
|
|
28
|
-
}: {
|
|
29
|
-
amountPerMonth: number;
|
|
30
|
-
price: Price;
|
|
31
|
-
quantity: number;
|
|
32
|
-
}) => {
|
|
24
|
+
export const getPriceString = ({ amount, price, quantity }: { amount: number; price: Price; quantity: number }) => {
|
|
33
25
|
const { billingPeriod } = price;
|
|
34
26
|
let billingPeriodString = null;
|
|
35
27
|
|
|
36
28
|
if (billingPeriod === BillingPeriod.Annually) {
|
|
37
|
-
|
|
29
|
+
amount /= 12;
|
|
38
30
|
billingPeriodString = '12 months';
|
|
39
31
|
}
|
|
40
32
|
|
|
41
|
-
const addonPriceFormat = currencyPriceFormatter({ amount
|
|
33
|
+
const addonPriceFormat = currencyPriceFormatter({ amount, currency: price.currency });
|
|
42
34
|
|
|
43
35
|
return `${quantity > 1 ? `${quantity} x ${addonPriceFormat} each` : addonPriceFormat}${
|
|
44
36
|
billingPeriodString ? ` x ${billingPeriodString}` : ''
|
|
@@ -48,30 +40,30 @@ export const getPriceString = ({
|
|
|
48
40
|
export const BilledPriceLineItem = ({ label, quantity, price }: { label: string; quantity: number; price: Price }) => {
|
|
49
41
|
const { billingPeriod } = price;
|
|
50
42
|
|
|
51
|
-
let
|
|
43
|
+
let amount;
|
|
52
44
|
if (price.isTieredPrice) {
|
|
53
45
|
const tier = getTierByQuantity(price.tiers!, quantity);
|
|
54
|
-
|
|
46
|
+
amount = tier!.unitPrice.amount!;
|
|
55
47
|
} else {
|
|
56
|
-
|
|
48
|
+
amount = price.amount!;
|
|
57
49
|
}
|
|
58
50
|
|
|
59
51
|
return (
|
|
60
52
|
<LineItemContainer>
|
|
61
53
|
<LineItemRow style={{ alignItems: 'flex-end' }}>
|
|
62
54
|
<Grid item>
|
|
63
|
-
<Typography variant="
|
|
55
|
+
<Typography variant="body1" color="secondary">
|
|
64
56
|
{label}
|
|
65
57
|
</Typography>
|
|
66
58
|
{(quantity > 1 || billingPeriod === BillingPeriod.Annually) && (
|
|
67
59
|
<Typography variant="body1" color="secondary">
|
|
68
|
-
{getPriceString({
|
|
60
|
+
{getPriceString({ amount, price, quantity })}
|
|
69
61
|
</Typography>
|
|
70
62
|
)}
|
|
71
63
|
</Grid>
|
|
72
64
|
<Grid item>
|
|
73
65
|
<Typography variant="body1" color="secondary" style={{ wordBreak: 'break-word' }}>
|
|
74
|
-
{currencyPriceFormatter({ amount: quantity *
|
|
66
|
+
{currencyPriceFormatter({ amount: quantity * amount, currency: price.currency })}
|
|
75
67
|
</Typography>
|
|
76
68
|
</Grid>
|
|
77
69
|
</LineItemRow>
|
|
@@ -83,13 +75,11 @@ export const DiscountLineItem = ({
|
|
|
83
75
|
subscriptionPreview,
|
|
84
76
|
isFetchingSubscriptionPreview,
|
|
85
77
|
}: {
|
|
86
|
-
subscriptionPreview: SubscriptionPreview
|
|
78
|
+
subscriptionPreview: SubscriptionPreview;
|
|
87
79
|
isFetchingSubscriptionPreview: boolean;
|
|
88
80
|
}) => {
|
|
89
|
-
const { subTotal
|
|
90
|
-
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
81
|
+
const { subTotal } = subscriptionPreview;
|
|
82
|
+
const discount = subscriptionPreview.discount!;
|
|
93
83
|
|
|
94
84
|
let discountAmount: number;
|
|
95
85
|
if (discount.type === DiscountType.Percentage) {
|
|
@@ -121,14 +111,11 @@ export const AppliedCreditsLineItem = ({
|
|
|
121
111
|
isFetchingSubscriptionPreview,
|
|
122
112
|
checkoutLocalization,
|
|
123
113
|
}: {
|
|
124
|
-
subscriptionPreview: SubscriptionPreview
|
|
114
|
+
subscriptionPreview: SubscriptionPreview;
|
|
125
115
|
isFetchingSubscriptionPreview: boolean;
|
|
126
116
|
checkoutLocalization: CheckoutLocalization;
|
|
127
117
|
}) => {
|
|
128
|
-
const { credits } = subscriptionPreview
|
|
129
|
-
if (!credits || credits.used.amount <= 0) {
|
|
130
|
-
return null;
|
|
131
|
-
}
|
|
118
|
+
const { credits } = subscriptionPreview;
|
|
132
119
|
|
|
133
120
|
return (
|
|
134
121
|
<LineItemContainer>
|
|
@@ -138,7 +125,7 @@ export const AppliedCreditsLineItem = ({
|
|
|
138
125
|
</Typography>
|
|
139
126
|
<Typography variant="body1" color="secondary">
|
|
140
127
|
<WithSkeleton isLoading={isFetchingSubscriptionPreview}>
|
|
141
|
-
{currencyPriceFormatter({ amount: -1 * credits
|
|
128
|
+
{currencyPriceFormatter({ amount: -1 * credits!.used.amount, currency: credits!.used.currency })}
|
|
142
129
|
</WithSkeleton>
|
|
143
130
|
</Typography>
|
|
144
131
|
</LineItemRow>
|
|
@@ -151,14 +138,12 @@ export const TaxLineItem = ({
|
|
|
151
138
|
isFetchingSubscriptionPreview,
|
|
152
139
|
checkoutLocalization,
|
|
153
140
|
}: {
|
|
154
|
-
subscriptionPreview: SubscriptionPreview
|
|
141
|
+
subscriptionPreview: SubscriptionPreview;
|
|
155
142
|
isFetchingSubscriptionPreview: boolean;
|
|
156
143
|
checkoutLocalization: CheckoutLocalization;
|
|
157
144
|
}) => {
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
return null;
|
|
161
|
-
}
|
|
145
|
+
const tax = subscriptionPreview.tax!;
|
|
146
|
+
const taxDetails = subscriptionPreview.taxDetails!;
|
|
162
147
|
|
|
163
148
|
return (
|
|
164
149
|
<LineItemContainer>
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { BillingPeriod, SubscriptionPreviewTaxDetails } from '@stigg/js-client-sdk';
|
|
1
|
+
import { BillingPeriod, Plan, SubscriptionPreviewTaxDetails } from '@stigg/js-client-sdk';
|
|
2
2
|
import moment from 'moment';
|
|
3
3
|
import merge from 'lodash/merge';
|
|
4
4
|
import { DeepPartial } from '../../types';
|
|
5
|
-
import { formatBillingPeriod } from './formatting';
|
|
6
5
|
|
|
7
6
|
export type CheckoutLocalization = {
|
|
8
7
|
changePlan: string;
|
|
@@ -20,42 +19,63 @@ export type CheckoutLocalization = {
|
|
|
20
19
|
changesWillApplyAtEndOfBillingPeriod: string | ((params: { billingPeriodEnd: Date }) => string);
|
|
21
20
|
checkoutButton: {
|
|
22
21
|
nextText: string;
|
|
23
|
-
|
|
22
|
+
updateText: string;
|
|
23
|
+
downgradeToFreeText: string;
|
|
24
24
|
upgradeText: string;
|
|
25
|
-
purchaseText: string;
|
|
26
25
|
};
|
|
27
26
|
appliedCreditsTitle: string;
|
|
28
27
|
taxTitle: (params: { taxDetails: SubscriptionPreviewTaxDetails }) => string;
|
|
28
|
+
downgradeToFree: {
|
|
29
|
+
alertText: (params: { plan: Plan }) => string;
|
|
30
|
+
freePlanHeader: (params: { plan: Plan }) => string;
|
|
31
|
+
freePlanName: (params: { plan: Plan }) => string;
|
|
32
|
+
freePlanPriceText: (params: { plan: Plan }) => string;
|
|
33
|
+
paidPlanHeader: (params: { plan: Plan }) => string;
|
|
34
|
+
paidPlanName: (params: { plan: Plan }) => string;
|
|
35
|
+
paidPlanPriceText: (params: { plan: Plan; billingPeriod?: BillingPeriod }) => string;
|
|
36
|
+
};
|
|
37
|
+
checkoutSuccessText: string;
|
|
29
38
|
};
|
|
30
39
|
|
|
31
40
|
export function getResolvedCheckoutLocalize(
|
|
32
41
|
localizeOverride?: DeepPartial<CheckoutLocalization>,
|
|
33
42
|
): CheckoutLocalization {
|
|
34
43
|
const checkoutDefaultLocalization: CheckoutLocalization = {
|
|
35
|
-
changePlan: 'Change
|
|
44
|
+
changePlan: 'Change',
|
|
36
45
|
billingPeriodsTitle: 'Billing cycle',
|
|
37
46
|
addAddonText: 'Add',
|
|
38
47
|
newPaymentMethodText: 'New payment method',
|
|
39
48
|
newPaymentMethodBillingAddressTitle: 'Billing address',
|
|
40
49
|
newPaymentMethodCardTitle: 'Payment method',
|
|
41
|
-
baseChargeText: (
|
|
50
|
+
baseChargeText: () => 'Base charge',
|
|
42
51
|
totalText: 'Total due today',
|
|
43
52
|
subTotalText: 'Subtotal',
|
|
44
|
-
addCouponCodeText: 'Add
|
|
45
|
-
couponCodeTitle: '
|
|
53
|
+
addCouponCodeText: 'Add promotion code',
|
|
54
|
+
couponCodeTitle: 'Promotion code',
|
|
46
55
|
addonsSectionTitle: 'Add-ons',
|
|
47
56
|
changesWillApplyAtEndOfBillingPeriod: ({ billingPeriodEnd }) =>
|
|
48
|
-
`Your changes will
|
|
57
|
+
`Your changes will be applied at the end of your billing cycle on ${moment(billingPeriodEnd).format(
|
|
49
58
|
'MMM D, YYYY',
|
|
50
59
|
)}.`,
|
|
51
60
|
checkoutButton: {
|
|
52
61
|
nextText: 'Next',
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
62
|
+
updateText: 'Update subscription',
|
|
63
|
+
downgradeToFreeText: 'Cancel subscription',
|
|
64
|
+
upgradeText: 'Subscribe',
|
|
56
65
|
},
|
|
57
66
|
appliedCreditsTitle: 'Applied credits',
|
|
58
|
-
taxTitle: ({ taxDetails }) =>
|
|
67
|
+
taxTitle: ({ taxDetails }) => `Tax (${taxDetails?.percentage}%)`,
|
|
68
|
+
downgradeToFree: {
|
|
69
|
+
alertText: () => `We’re sorry to see you cancel your paid subscription 😭`,
|
|
70
|
+
freePlanHeader: () => 'New plan',
|
|
71
|
+
freePlanName: ({ plan }) => `${plan.displayName}`,
|
|
72
|
+
freePlanPriceText: () => 'Free',
|
|
73
|
+
paidPlanHeader: () => 'Current plan',
|
|
74
|
+
paidPlanName: ({ plan }) => `${plan.displayName}`,
|
|
75
|
+
paidPlanPriceText: ({ billingPeriod }) =>
|
|
76
|
+
`Paid plan${billingPeriod ? `, billed ${billingPeriod.toLowerCase()}` : ''}`,
|
|
77
|
+
},
|
|
78
|
+
checkoutSuccessText: 'Changes applied',
|
|
59
79
|
};
|
|
60
80
|
|
|
61
81
|
return merge(checkoutDefaultLocalization, localizeOverride);
|
|
@@ -7,7 +7,6 @@ export type CheckoutTheme = {
|
|
|
7
7
|
textColor: string;
|
|
8
8
|
backgroundColor: string;
|
|
9
9
|
borderColor: string;
|
|
10
|
-
selectionColor: string;
|
|
11
10
|
summaryBackgroundColor: string;
|
|
12
11
|
};
|
|
13
12
|
|
|
@@ -16,7 +15,6 @@ const defaultCheckoutTheme: CheckoutTheme = {
|
|
|
16
15
|
textColor: 'rgb(0, 30, 108)',
|
|
17
16
|
backgroundColor: 'rgb(255, 255, 255)',
|
|
18
17
|
borderColor: 'rgb(235, 237, 243)',
|
|
19
|
-
selectionColor: 'rgb(229, 242, 255)',
|
|
20
18
|
summaryBackgroundColor: 'rgb(109, 121, 144)',
|
|
21
19
|
};
|
|
22
20
|
|
|
@@ -33,8 +31,6 @@ export function getResolvedCheckoutTheme(
|
|
|
33
31
|
backgroundColor:
|
|
34
32
|
themeOverride?.backgroundColor || globalPalette?.backgroundPaper || defaultCheckoutTheme.backgroundColor,
|
|
35
33
|
borderColor: themeOverride?.borderColor || globalPalette?.outlinedBorder || defaultCheckoutTheme.borderColor,
|
|
36
|
-
selectionColor:
|
|
37
|
-
themeOverride?.selectionColor || globalPalette?.backgroundSection || defaultCheckoutTheme.selectionColor,
|
|
38
34
|
summaryBackgroundColor:
|
|
39
35
|
themeOverride?.summaryBackgroundColor ||
|
|
40
36
|
globalPalette?.backgroundHighlight ||
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import styled from '@emotion/styled/macro';
|
|
3
|
-
import { CSSProperties } from 'react';
|
|
4
|
-
import { css,
|
|
2
|
+
import React, { CSSProperties } from 'react';
|
|
3
|
+
import { css, useTheme } from '@emotion/react';
|
|
5
4
|
import { getIconColor, IconColor } from './iconColor';
|
|
6
5
|
import * as customIcons from './customIcons';
|
|
7
6
|
|
|
@@ -45,7 +44,7 @@ export type IconProps = {
|
|
|
45
44
|
|
|
46
45
|
export function Icon({ icon, className, style, svgPathColor, svgRectColor, svgStrokeColor }: IconProps) {
|
|
47
46
|
const IconComponent = (customIcons as any)[icon];
|
|
48
|
-
const theme = useTheme()
|
|
47
|
+
const theme = useTheme();
|
|
49
48
|
|
|
50
49
|
return (
|
|
51
50
|
<IconWrapper
|
|
@@ -53,8 +52,7 @@ export function Icon({ icon, className, style, svgPathColor, svgRectColor, svgSt
|
|
|
53
52
|
style={style}
|
|
54
53
|
$pathColor={getIconColor(svgPathColor, theme)}
|
|
55
54
|
$rectColor={getIconColor(svgRectColor, theme)}
|
|
56
|
-
$strokeColor={getIconColor(svgStrokeColor, theme)}
|
|
57
|
-
>
|
|
55
|
+
$strokeColor={getIconColor(svgStrokeColor, theme)}>
|
|
58
56
|
<IconComponent />
|
|
59
57
|
</IconWrapper>
|
|
60
58
|
);
|
|
@@ -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
|
};
|
|
@@ -28,14 +28,21 @@ type PriceBillingPeriodProps = {
|
|
|
28
28
|
billingPeriod: BillingPeriod;
|
|
29
29
|
hasMonthlyPrice: boolean;
|
|
30
30
|
hasAnnuallyPrice: boolean;
|
|
31
|
+
paywallLocale: PaywallLocalization;
|
|
31
32
|
};
|
|
32
33
|
|
|
33
|
-
function PriceBillingPeriod({
|
|
34
|
+
function PriceBillingPeriod({
|
|
35
|
+
plan,
|
|
36
|
+
billingPeriod,
|
|
37
|
+
hasMonthlyPrice,
|
|
38
|
+
hasAnnuallyPrice,
|
|
39
|
+
paywallLocale,
|
|
40
|
+
}: PriceBillingPeriodProps) {
|
|
34
41
|
const hasPrice = plan.pricePoints.find(pricePoint => pricePoint.billingPeriod === billingPeriod);
|
|
35
42
|
|
|
36
43
|
let content = EMPTY_CHAR;
|
|
37
44
|
if (hasPrice && hasMonthlyPrice && hasAnnuallyPrice) {
|
|
38
|
-
content = `, billed ${billingPeriod.toLowerCase()}`;
|
|
45
|
+
content = paywallLocale.price.billingPeriod?.(billingPeriod) || `, billed ${billingPeriod.toLowerCase()}`;
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
return (
|
|
@@ -133,6 +140,7 @@ export const PlanPrice = ({
|
|
|
133
140
|
billingPeriod={billingPeriod}
|
|
134
141
|
hasAnnuallyPrice={hasAnnuallyPrice}
|
|
135
142
|
hasMonthlyPrice={hasMonthlyPrice}
|
|
143
|
+
paywallLocale={paywallLocale}
|
|
136
144
|
/>
|
|
137
145
|
</Typography>
|
|
138
146
|
)}
|