@stigg/react-sdk 4.4.0-beta.1 → 4.4.0-beta.10
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 +32 -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 +10 -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 +2 -2
- package/dist/components/checkout/textOverrides.d.ts +7 -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 +1147 -524
- 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 +1181 -532
- 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 +118 -0
- package/src/components/checkout/components/Skeletons.style.ts +4 -1
- 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.style.tsx +1 -1
- package/src/components/checkout/planHeader/PlanHeader.tsx +7 -15
- 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 +6 -7
- 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 +48 -33
- package/src/components/checkout/summary/components/CheckoutCaptions.tsx +30 -29
- package/src/components/checkout/summary/components/LineItems.tsx +8 -16
- package/src/components/checkout/textOverrides.ts +15 -12
- 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/steps/surprise/SurpriseStep.d.ts +0 -2
- package/src/assets/nyancat.svg +0 -634
- 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';
|
|
@@ -60,31 +60,37 @@ 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
|
-
export const CheckoutSummary = ({
|
|
87
|
+
export const CheckoutSummary = ({
|
|
88
|
+
onCheckout,
|
|
89
|
+
onCheckoutCompleted,
|
|
90
|
+
disablePromotionCode,
|
|
91
|
+
disableSuccessAnimation,
|
|
92
|
+
isFreeDowngrade,
|
|
93
|
+
}: CheckoutContainerProps & { isFreeDowngrade: boolean }) => {
|
|
88
94
|
const [isCheckoutCompletedSuccessfully, setIsCheckoutCompletedSuccessfully] = useState(false);
|
|
89
95
|
const { setErrorMessage } = usePaymentStepModel();
|
|
90
96
|
const progressBar = useProgressBarModel();
|
|
@@ -96,12 +102,12 @@ export const CheckoutSummary = ({ onCheckout, onCheckoutCompleted }: CheckoutCon
|
|
|
96
102
|
);
|
|
97
103
|
const [baseCharges, usageCharges] = partition(planPrices, (price) => price.pricingModel === BillingModel.FlatFee);
|
|
98
104
|
const [baseCharge] = baseCharges || [];
|
|
99
|
-
const
|
|
100
|
-
const isLastStep = progressBar.isCheckoutComplete && progressBar.isLastStep;
|
|
105
|
+
const isLastStep = isFreeDowngrade || (progressBar.isCheckoutComplete && progressBar.isLastStep);
|
|
101
106
|
|
|
102
107
|
const { subscriptionPreview, isFetchingSubscriptionPreview } = usePreviewSubscription();
|
|
103
108
|
|
|
104
109
|
const { handleSubmit, isLoading } = useSubmit({
|
|
110
|
+
disableSuccessAnimation,
|
|
105
111
|
onCheckout,
|
|
106
112
|
onCheckoutCompleted,
|
|
107
113
|
onSuccess: () => {
|
|
@@ -119,11 +125,9 @@ export const CheckoutSummary = ({ onCheckout, onCheckoutCompleted }: CheckoutCon
|
|
|
119
125
|
if (errorMessage) {
|
|
120
126
|
setErrorMessage(errorMessage);
|
|
121
127
|
setIsCheckoutCompletedSuccessfully(false);
|
|
122
|
-
|
|
128
|
+
} else {
|
|
129
|
+
setErrorMessage(undefined);
|
|
123
130
|
}
|
|
124
|
-
|
|
125
|
-
setErrorMessage(undefined);
|
|
126
|
-
setIsCheckoutCompletedSuccessfully(true);
|
|
127
131
|
};
|
|
128
132
|
|
|
129
133
|
const onCheckoutClick = async (e: any): Promise<void> => {
|
|
@@ -188,11 +192,13 @@ export const CheckoutSummary = ({ onCheckout, onCheckoutCompleted }: CheckoutCon
|
|
|
188
192
|
|
|
189
193
|
if (!addonPrice) return null;
|
|
190
194
|
|
|
195
|
+
const addonQuantity = addon.quantity && addon.quantity > 0 ? addon.quantity : 1;
|
|
196
|
+
|
|
191
197
|
return (
|
|
192
198
|
<BilledPriceLineItem
|
|
193
199
|
key={addon?.addon?.id}
|
|
194
200
|
label={addon.addon.displayName}
|
|
195
|
-
quantity={
|
|
201
|
+
quantity={addonQuantity}
|
|
196
202
|
price={addonPrice}
|
|
197
203
|
/>
|
|
198
204
|
);
|
|
@@ -211,7 +217,7 @@ export const CheckoutSummary = ({ onCheckout, onCheckoutCompleted }: CheckoutCon
|
|
|
211
217
|
|
|
212
218
|
<StyledDivider className="stigg-checkout-summary-divider" />
|
|
213
219
|
|
|
214
|
-
{
|
|
220
|
+
{!disablePromotionCode && !isFreeDowngrade && (
|
|
215
221
|
<>
|
|
216
222
|
<PromotionCodeSection checkoutLocalization={checkoutLocalization} />
|
|
217
223
|
|
|
@@ -219,18 +225,18 @@ export const CheckoutSummary = ({ onCheckout, onCheckoutCompleted }: CheckoutCon
|
|
|
219
225
|
</>
|
|
220
226
|
)}
|
|
221
227
|
|
|
222
|
-
<
|
|
228
|
+
<DiscountLineItem
|
|
223
229
|
subscriptionPreview={subscriptionPreview}
|
|
224
230
|
isFetchingSubscriptionPreview={isFetchingSubscriptionPreview}
|
|
225
|
-
checkoutLocalization={checkoutLocalization}
|
|
226
231
|
/>
|
|
227
232
|
|
|
228
|
-
<
|
|
233
|
+
<TaxLineItem
|
|
229
234
|
subscriptionPreview={subscriptionPreview}
|
|
230
235
|
isFetchingSubscriptionPreview={isFetchingSubscriptionPreview}
|
|
236
|
+
checkoutLocalization={checkoutLocalization}
|
|
231
237
|
/>
|
|
232
238
|
|
|
233
|
-
<
|
|
239
|
+
<AppliedCreditsLineItem
|
|
234
240
|
subscriptionPreview={subscriptionPreview}
|
|
235
241
|
isFetchingSubscriptionPreview={isFetchingSubscriptionPreview}
|
|
236
242
|
checkoutLocalization={checkoutLocalization}
|
|
@@ -253,15 +259,15 @@ export const CheckoutSummary = ({ onCheckout, onCheckoutCompleted }: CheckoutCon
|
|
|
253
259
|
subscriptionPreview={subscriptionPreview}
|
|
254
260
|
isFetchingSubscriptionPreview={isFetchingSubscriptionPreview}
|
|
255
261
|
checkoutLocalization={checkoutLocalization}
|
|
262
|
+
billingPeriod={subscription.billingPeriod}
|
|
256
263
|
/>
|
|
257
264
|
|
|
258
265
|
<Button
|
|
259
|
-
disableRipple={isLoading}
|
|
260
|
-
$isLoading={isLoading}
|
|
261
266
|
$success={isCheckoutCompletedSuccessfully}
|
|
262
|
-
$error={isLastStep &&
|
|
267
|
+
$error={isLastStep && isFreeDowngrade}
|
|
268
|
+
disabled={isLoading || isFetchingSubscriptionPreview || progressBar.progressBarState.isDisabled}
|
|
263
269
|
className="stigg-checkout-summary-cta-button"
|
|
264
|
-
sx={{ textTransform: 'none', borderRadius: '10px', marginTop: '24px' }}
|
|
270
|
+
sx={{ textTransform: 'none', borderRadius: '10px', marginTop: '24px', height: '36px' }}
|
|
265
271
|
variant="contained"
|
|
266
272
|
size="medium"
|
|
267
273
|
onClick={(e: any) => {
|
|
@@ -269,11 +275,18 @@ export const CheckoutSummary = ({ onCheckout, onCheckoutCompleted }: CheckoutCon
|
|
|
269
275
|
}}
|
|
270
276
|
fullWidth>
|
|
271
277
|
<Typography className="stigg-checkout-summary-cta-button-text" color="white" style={{ display: 'flex' }}>
|
|
272
|
-
{isCheckoutCompletedSuccessfully
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
278
|
+
{isCheckoutCompletedSuccessfully ? (
|
|
279
|
+
<Icon icon="Check" style={{ display: 'contents' }} />
|
|
280
|
+
) : isLoading || isFetchingSubscriptionPreview ? (
|
|
281
|
+
<CircularProgress size={20} sx={{ color: 'white' }} />
|
|
282
|
+
) : (
|
|
283
|
+
resolveCheckoutButtonText({
|
|
284
|
+
isLastStep,
|
|
285
|
+
isFreeDowngrade,
|
|
286
|
+
checkoutLocalization,
|
|
287
|
+
isPlanUpdate: !!activeSubscription && activeSubscription?.plan.id === plan?.id,
|
|
288
|
+
})
|
|
289
|
+
)}
|
|
277
290
|
</Typography>
|
|
278
291
|
</Button>
|
|
279
292
|
</SummaryCard>
|
|
@@ -282,7 +295,9 @@ export const CheckoutSummary = ({ onCheckout, onCheckoutCompleted }: CheckoutCon
|
|
|
282
295
|
showWatermark
|
|
283
296
|
style={{ marginTop: 8, display: 'flex', justifyContent: 'center' }}
|
|
284
297
|
/>
|
|
285
|
-
{isCheckoutCompletedSuccessfully &&
|
|
298
|
+
{!disableSuccessAnimation && isCheckoutCompletedSuccessfully && (
|
|
299
|
+
<CheckoutSuccess checkoutLocalization={checkoutLocalization} />
|
|
300
|
+
)}
|
|
286
301
|
</Box>
|
|
287
302
|
);
|
|
288
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) {
|
|
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,12 +40,12 @@ 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 (
|
|
@@ -65,13 +57,13 @@ export const BilledPriceLineItem = ({ label, quantity, price }: { label: string;
|
|
|
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>
|
|
@@ -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,12 +19,14 @@ 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
|
+
downgradeToFreeAlertText: (params: { plan: Plan }) => string;
|
|
29
|
+
checkoutSuccessText: string;
|
|
29
30
|
};
|
|
30
31
|
|
|
31
32
|
export function getResolvedCheckoutLocalize(
|
|
@@ -38,24 +39,26 @@ export function getResolvedCheckoutLocalize(
|
|
|
38
39
|
newPaymentMethodText: 'New payment method',
|
|
39
40
|
newPaymentMethodBillingAddressTitle: 'Billing address',
|
|
40
41
|
newPaymentMethodCardTitle: 'Payment method',
|
|
41
|
-
baseChargeText: (
|
|
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
|
-
taxTitle: ({ taxDetails }) =>
|
|
59
|
+
taxTitle: ({ taxDetails }) => `Tax (${taxDetails?.percentage}%)`,
|
|
60
|
+
downgradeToFreeAlertText: () => `We’re sorry to see you cancel your paid subscription 😭`,
|
|
61
|
+
checkoutSuccessText: 'Payment successful',
|
|
59
62
|
};
|
|
60
63
|
|
|
61
64
|
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
|
)}
|
|
@@ -26,6 +26,8 @@ export type PaywallLocalization = {
|
|
|
26
26
|
};
|
|
27
27
|
price: {
|
|
28
28
|
startingAtCaption: string;
|
|
29
|
+
billingPeriod?: (billingPeriod: BillingPeriod) => string;
|
|
30
|
+
pricePeriod: (billingPeriod: BillingPeriod) => string;
|
|
29
31
|
custom: string;
|
|
30
32
|
priceNotSet: string;
|
|
31
33
|
free: PlanPriceText | ((currency?: PaywallCurrency) => PlanPriceText);
|
|
@@ -56,6 +58,7 @@ export function getResolvedPaywallLocalize(localizeOverride?: DeepPartial<Paywal
|
|
|
56
58
|
},
|
|
57
59
|
price: {
|
|
58
60
|
startingAtCaption: 'Starts at',
|
|
61
|
+
pricePeriod: (billingPeriod: BillingPeriod) => (billingPeriod === BillingPeriod.Monthly ? '/ month' : '/ year'),
|
|
59
62
|
free: currency => ({
|
|
60
63
|
price: `${currency?.symbol}0`,
|
|
61
64
|
}),
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
import { currencyPriceFormatter } from './currencyUtils';
|
|
9
9
|
import { PlanPriceText } from './getPlanPrice';
|
|
10
10
|
import { calculateTierPrice, getPriceFeatureUnit } from './priceTierUtils';
|
|
11
|
+
import { PaywallLocalization } from '../paywall';
|
|
11
12
|
|
|
12
13
|
type GetPaidPriceTextParams = {
|
|
13
14
|
planPrices: Price[];
|
|
@@ -16,6 +17,7 @@ type GetPaidPriceTextParams = {
|
|
|
16
17
|
locale: string;
|
|
17
18
|
shouldShowMonthlyPriceAmount: boolean;
|
|
18
19
|
selectedTierByFeature: Record<string, PriceTierFragment>;
|
|
20
|
+
paywallLocale: PaywallLocalization;
|
|
19
21
|
};
|
|
20
22
|
|
|
21
23
|
export function getPaidPriceText({
|
|
@@ -25,6 +27,7 @@ export function getPaidPriceText({
|
|
|
25
27
|
locale,
|
|
26
28
|
shouldShowMonthlyPriceAmount,
|
|
27
29
|
selectedTierByFeature,
|
|
30
|
+
paywallLocale,
|
|
28
31
|
}: GetPaidPriceTextParams): PlanPriceText {
|
|
29
32
|
const { amount, currency } = paywallCalculatedPrice || planPrices[0];
|
|
30
33
|
const priceAmount = amount || 0;
|
|
@@ -35,7 +38,10 @@ export function getPaidPriceText({
|
|
|
35
38
|
|
|
36
39
|
let tiers;
|
|
37
40
|
let tierUnits;
|
|
38
|
-
|
|
41
|
+
const pricePeriod = paywallLocale.price.pricePeriod(
|
|
42
|
+
shouldShowMonthlyPriceAmount ? BillingPeriod.Monthly : BillingPeriod.Annually,
|
|
43
|
+
);
|
|
44
|
+
let unit = pricePeriod;
|
|
39
45
|
|
|
40
46
|
for (const price of planPrices) {
|
|
41
47
|
if (price.isTieredPrice) {
|
|
@@ -56,7 +62,7 @@ export function getPaidPriceText({
|
|
|
56
62
|
const featureUnit = price.feature?.units || '';
|
|
57
63
|
|
|
58
64
|
if (price.pricingModel === BillingModel.PerUnit && !price.isTieredPrice) {
|
|
59
|
-
unit = shouldShowMonthlyPriceAmount ? `per ${featureUnit}
|
|
65
|
+
unit = shouldShowMonthlyPriceAmount ? `per ${featureUnit} ${pricePeriod}` : `per ${featureUnit} ${pricePeriod}`;
|
|
60
66
|
} else if (price.pricingModel === BillingModel.UsageBased) {
|
|
61
67
|
unit = `per ${featureUnit}`;
|
|
62
68
|
}
|
|
@@ -50,7 +50,7 @@ export function getPlanPrice(
|
|
|
50
50
|
|
|
51
51
|
return paywallLocale.price.paid
|
|
52
52
|
? paywallLocale.price.paid({ ...paidParams, plan })
|
|
53
|
-
: getPaidPriceText({ ...paidParams, locale, shouldShowMonthlyPriceAmount });
|
|
53
|
+
: getPaidPriceText({ ...paidParams, locale, shouldShowMonthlyPriceAmount, paywallLocale });
|
|
54
54
|
}
|
|
55
55
|
default:
|
|
56
56
|
return {
|
|
@@ -46,7 +46,8 @@ const Template: ComponentStory<any> = (args) => (
|
|
|
46
46
|
onChangePlan={({ currentPlan }) => {
|
|
47
47
|
console.log('plan changed clicked!', { currentPlan });
|
|
48
48
|
}}
|
|
49
|
-
|
|
49
|
+
// disableSuccessAnimation
|
|
50
|
+
// billingInformation={{ taxPercentage: 27 }}
|
|
50
51
|
/>
|
|
51
52
|
</Wrapper>
|
|
52
53
|
);
|
|
@@ -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
|
};
|
package/src/theme/Theme.tsx
CHANGED
|
@@ -61,13 +61,22 @@ function createTypographyTheme(theme: StiggTheme): Partial<TypographyProps> {
|
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
const createMuiPalette = (theme: StiggTheme) => {
|
|
65
|
+
return {
|
|
66
|
+
primary: {
|
|
67
|
+
main: theme.palette.primary,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
|
|
64
72
|
export const SdkThemeProvider: React.FC<PropsWithChildren<SdkThemeProviderProps>> = ({ children, componentTheme }) => {
|
|
65
73
|
const theme = useStiggTheme(componentTheme);
|
|
74
|
+
const muiPalette = createMuiPalette(theme);
|
|
66
75
|
|
|
67
76
|
return (
|
|
68
77
|
// We are using styled-components theme here only because we are using styled-typography
|
|
69
78
|
// which depends on styled-components for the typography theming
|
|
70
|
-
<MuiThemeProvider theme={createTheme({ stigg: theme })}>
|
|
79
|
+
<MuiThemeProvider theme={createTheme({ palette: muiPalette, stigg: theme })}>
|
|
71
80
|
<ThemeProvider theme={{ stigg: theme }}>
|
|
72
81
|
<CustomCssGlobal customCss={theme.customCss}>
|
|
73
82
|
<Fonts externalFontUrl={theme.typography.fontFamilyUrl} />
|
|
@@ -21,6 +21,7 @@ export const getResolvedTheme = (customizedTheme?: CustomizedTheme): StiggTheme
|
|
|
21
21
|
palette: {
|
|
22
22
|
primary: primaryColor.hex(),
|
|
23
23
|
primaryDark: primaryColor.darken(0.3).hex(),
|
|
24
|
+
primaryLight: primaryColor.lighten(0.6).hex(),
|
|
24
25
|
backgroundPaper: '#FFFFFF',
|
|
25
26
|
backgroundHighlight: '#F5F6F9',
|
|
26
27
|
backgroundSection: primaryColor.alpha(0.1).toString(),
|