@stigg/react-sdk 4.4.0-beta.2 → 4.4.0-beta.4
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 +6 -2
- package/dist/components/checkout/CheckoutProvider.d.ts +3 -1
- package/dist/components/checkout/components/DowngradeToFreeContainer.d.ts +28 -0
- package/dist/components/checkout/hooks/usePaymentStepModel.d.ts +8 -2
- 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 +1 -0
- 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 +2 -1
- package/dist/components/checkout/steps/plan/CheckoutChargeList.d.ts +5 -1
- package/dist/components/checkout/summary/CheckoutSummary.d.ts +3 -1
- package/dist/components/checkout/summary/components/LineItems.d.ts +2 -2
- package/dist/components/checkout/textOverrides.d.ts +4 -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 +583 -263
- 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 +595 -263
- package/dist/react-sdk.esm.js.map +1 -1
- package/package.json +2 -2
- package/src/assets/payment-method.svg +3 -10
- package/src/components/checkout/Checkout.tsx +3 -1
- package/src/components/checkout/CheckoutContainer.tsx +54 -28
- package/src/components/checkout/CheckoutProvider.tsx +8 -4
- package/src/components/checkout/components/DowngradeToFreeContainer.tsx +98 -0
- package/src/components/checkout/hooks/usePaymentStepModel.ts +22 -3
- package/src/components/checkout/hooks/usePlanStepModel.ts +5 -5
- package/src/components/checkout/hooks/usePreviewSubscription.ts +26 -3
- 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 +1 -0
- package/src/components/checkout/progressBar/CheckoutProgressBar.tsx +3 -2
- 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 +54 -45
- package/src/components/checkout/steps/plan/CheckoutChargeList.tsx +42 -10
- package/src/components/checkout/summary/CheckoutSuccess.tsx +1 -1
- package/src/components/checkout/summary/CheckoutSummary.tsx +24 -19
- package/src/components/checkout/summary/components/LineItems.tsx +8 -16
- package/src/components/checkout/textOverrides.ts +5 -4
- package/src/components/checkout/types.ts +9 -0
- 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 +4 -5
- 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
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "4.4.0-beta.
|
|
2
|
+
"version": "4.4.0-beta.4",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"typings": "dist/index.d.ts",
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"@emotion/react": "^11.10.5",
|
|
102
102
|
"@emotion/styled": "^11.10.5",
|
|
103
103
|
"@mui/material": "^5.10.13",
|
|
104
|
-
"@stigg/js-client-sdk": "2.
|
|
104
|
+
"@stigg/js-client-sdk": "2.22.0",
|
|
105
105
|
"@stripe/react-stripe-js": "^2.1.1",
|
|
106
106
|
"@stripe/stripe-js": "^1.54.1",
|
|
107
107
|
"@types/styled-components": "^5.1.26",
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
<svg width="24" height="
|
|
2
|
-
<
|
|
3
|
-
<path d="
|
|
4
|
-
</g>
|
|
5
|
-
<path opacity="0.3" d="M18.75 12.4286H16.5C16.0125 12.4286 15.75 12.2 15.75 11.6667C15.75 11.1334 16.0125 10.9048 16.5 10.9048H18.75C19.2375 10.9048 19.5 11.1334 19.5 11.6667C19.5 12.2 19.2375 12.4286 18.75 12.4286ZM14.25 12.4286H12C11.5125 12.4286 11.25 12.2 11.25 11.6667C11.25 11.1334 11.5125 10.9048 12 10.9048H14.25C14.7375 10.9048 15 11.1334 15 11.6667C15 12.2 14.7375 12.4286 14.25 12.4286ZM9.75 12.4286H7.5C7.0125 12.4286 6.75 12.2 6.75 11.6667C6.75 11.1334 7.0125 10.9048 7.5 10.9048H9.75C10.2375 10.9048 10.5 11.1334 10.5 11.6667C10.5 12.2 10.2375 12.4286 9.75 12.4286ZM5.25 12.4286H3C2.5125 12.4286 2.25 12.2 2.25 11.6667C2.25 11.1334 2.5125 10.9048 3 10.9048H5.25C5.7375 10.9048 6 11.1334 6 11.6667C6 12.2 5.7375 12.4286 5.25 12.4286Z" fill="black"/>
|
|
6
|
-
<defs>
|
|
7
|
-
<clipPath id="clip0_239_13697">
|
|
8
|
-
<rect width="24" height="16" fill="white"/>
|
|
9
|
-
</clipPath>
|
|
10
|
-
</defs>
|
|
1
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M21.1667 4H2.83333C1.82081 4 1 4.89543 1 6V18C1 19.1046 1.82081 20 2.83333 20H21.1667C22.1792 20 23 19.1046 23 18V6C23 4.89543 22.1792 4 21.1667 4Z" fill="#C6C6C7"/>
|
|
3
|
+
<path opacity="0.3" d="M19.75 16.4286H17.5C17.0125 16.4286 16.75 16.2 16.75 15.6667C16.75 15.1334 17.0125 14.9048 17.5 14.9048H19.75C20.2375 14.9048 20.5 15.1334 20.5 15.6667C20.5 16.2 20.2375 16.4286 19.75 16.4286ZM15.25 16.4286H13C12.5125 16.4286 12.25 16.2 12.25 15.6667C12.25 15.1334 12.5125 14.9048 13 14.9048H15.25C15.7375 14.9048 16 15.1334 16 15.6667C16 16.2 15.7375 16.4286 15.25 16.4286ZM10.75 16.4286H8.5C8.0125 16.4286 7.75 16.2 7.75 15.6667C7.75 15.1334 8.0125 14.9048 8.5 14.9048H10.75C11.2375 14.9048 11.5 15.1334 11.5 15.6667C11.5 16.2 11.2375 16.4286 10.75 16.4286ZM6.25 16.4286H4C3.5125 16.4286 3.25 16.2 3.25 15.6667C3.25 15.1334 3.5125 14.9048 4 14.9048H6.25C6.7375 14.9048 7 15.1334 7 15.6667C7 16.2 6.7375 16.4286 6.25 16.4286Z" fill="black"/>
|
|
11
4
|
</svg>
|
|
@@ -12,6 +12,7 @@ export const Checkout = ({
|
|
|
12
12
|
preferredBillingPeriod,
|
|
13
13
|
billingCountryCode,
|
|
14
14
|
billableFeatures,
|
|
15
|
+
billingInformation,
|
|
15
16
|
...containerProps
|
|
16
17
|
}: CheckoutProps) => {
|
|
17
18
|
return (
|
|
@@ -22,7 +23,8 @@ export const Checkout = ({
|
|
|
22
23
|
planId={planId}
|
|
23
24
|
preferredBillingPeriod={preferredBillingPeriod}
|
|
24
25
|
billingCountryCode={billingCountryCode}
|
|
25
|
-
billableFeatures={billableFeatures}
|
|
26
|
+
billableFeatures={billableFeatures}
|
|
27
|
+
billingInformation={billingInformation}>
|
|
26
28
|
<CheckoutContainer {...containerProps} />
|
|
27
29
|
</CheckoutProvider>
|
|
28
30
|
);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Elements } from '@stripe/react-stripe-js';
|
|
3
|
-
import { ApplySubscription, CheckoutStatePlan } from '@stigg/js-client-sdk';
|
|
3
|
+
import { ApplySubscription, BillingAddress, CheckoutStatePlan, PricingType } from '@stigg/js-client-sdk';
|
|
4
4
|
import { CheckoutContent, CheckoutLayout, CheckoutPanel } from './CheckoutContainer.style';
|
|
5
5
|
import { CheckoutProgressBar } from './progressBar/CheckoutProgressBar';
|
|
6
6
|
import { CheckoutSummary, CheckoutSummarySkeleton } from './summary';
|
|
7
|
-
import { useProgressBarModel } from './hooks';
|
|
7
|
+
import { CheckoutStep, CheckoutStepKey, useCheckoutModel, useProgressBarModel } from './hooks';
|
|
8
8
|
import { PlanHeader } from './planHeader';
|
|
9
9
|
import { CheckoutAddonsStep } from './steps/addons';
|
|
10
10
|
import { PaymentStep } from './steps/payment';
|
|
@@ -12,22 +12,24 @@ import { useStripeIntegration } from './steps/payment/stripe';
|
|
|
12
12
|
import { CheckoutPlanStep } from './steps/plan';
|
|
13
13
|
import { useCheckoutContext } from './CheckoutProvider';
|
|
14
14
|
import { ContentLoadingSkeleton } from './components';
|
|
15
|
-
|
|
16
|
-
// import { SurpriseStep } from './steps/surprise/SurpriseStep';
|
|
15
|
+
import { DowngradeToFreePlan } from './components/DowngradeToFreeContainer';
|
|
17
16
|
|
|
18
17
|
type StepProps = {
|
|
19
18
|
allowChangePlan?: boolean;
|
|
20
19
|
content: React.ReactNode;
|
|
21
20
|
};
|
|
22
21
|
|
|
23
|
-
const getStepProps = (
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
const getStepProps = (
|
|
23
|
+
currentStep: CheckoutStep,
|
|
24
|
+
{ onBillingAddressChange }: Pick<CheckoutContainerProps, 'onBillingAddressChange'>,
|
|
25
|
+
): StepProps => {
|
|
26
|
+
switch (currentStep.key) {
|
|
27
|
+
case CheckoutStepKey.PLAN:
|
|
26
28
|
return { allowChangePlan: true, content: <CheckoutPlanStep /> };
|
|
27
|
-
case
|
|
29
|
+
case CheckoutStepKey.ADDONS:
|
|
28
30
|
return { content: <CheckoutAddonsStep /> };
|
|
29
|
-
case
|
|
30
|
-
return { content: <PaymentStep /> };
|
|
31
|
+
case CheckoutStepKey.PAYMENT:
|
|
32
|
+
return { content: <PaymentStep onBillingAddressChange={onBillingAddressChange} /> };
|
|
31
33
|
default:
|
|
32
34
|
return { content: null };
|
|
33
35
|
}
|
|
@@ -43,26 +45,47 @@ export type CheckoutContainerProps = {
|
|
|
43
45
|
onCheckout?: (params: OnCheckoutParams) => Promise<CheckoutResult>;
|
|
44
46
|
onCheckoutCompleted: (params: OnCheckoutCompletedParams) => Promise<void>;
|
|
45
47
|
onChangePlan?: (params: { currentPlan: CheckoutStatePlan | undefined }) => void;
|
|
48
|
+
onBillingAddressChange?: (params: { billingAddress: BillingAddress }) => Promise<void>;
|
|
49
|
+
disablePromotionCode?: boolean;
|
|
46
50
|
};
|
|
47
51
|
|
|
48
|
-
export function CheckoutContainer({
|
|
52
|
+
export function CheckoutContainer({
|
|
53
|
+
onCheckout,
|
|
54
|
+
onCheckoutCompleted,
|
|
55
|
+
onChangePlan,
|
|
56
|
+
onBillingAddressChange,
|
|
57
|
+
disablePromotionCode,
|
|
58
|
+
}: CheckoutContainerProps) {
|
|
49
59
|
const { stripePromise, setupIntentClientSecret } = useStripeIntegration();
|
|
50
60
|
const [{ stiggTheme, widgetState }] = useCheckoutContext();
|
|
51
|
-
const {
|
|
52
|
-
|
|
61
|
+
const { currentStep } = useProgressBarModel();
|
|
62
|
+
|
|
53
63
|
const { isLoadingCheckoutData } = widgetState;
|
|
54
64
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
65
|
+
const { checkoutState, checkoutLocalization } = useCheckoutModel();
|
|
66
|
+
const { plan, activeSubscription } = checkoutState || {};
|
|
67
|
+
const isFreeDowngrade =
|
|
68
|
+
!!plan &&
|
|
69
|
+
plan.pricingType === PricingType.Free &&
|
|
70
|
+
!!activeSubscription &&
|
|
71
|
+
activeSubscription.pricingType !== PricingType.Free;
|
|
59
72
|
|
|
60
|
-
const { content, allowChangePlan } = getStepProps(
|
|
73
|
+
const { content, allowChangePlan } = getStepProps(currentStep, { onBillingAddressChange });
|
|
61
74
|
|
|
62
75
|
const checkoutContent = (
|
|
63
76
|
<>
|
|
64
|
-
|
|
65
|
-
|
|
77
|
+
{isFreeDowngrade ? (
|
|
78
|
+
<DowngradeToFreePlan
|
|
79
|
+
checkoutLocalization={checkoutLocalization}
|
|
80
|
+
freePlan={plan!}
|
|
81
|
+
activeSubscription={activeSubscription!}
|
|
82
|
+
/>
|
|
83
|
+
) : (
|
|
84
|
+
<>
|
|
85
|
+
<PlanHeader allowChangePlan={allowChangePlan} onChangePlan={onChangePlan} />
|
|
86
|
+
{content}
|
|
87
|
+
</>
|
|
88
|
+
)}
|
|
66
89
|
</>
|
|
67
90
|
);
|
|
68
91
|
|
|
@@ -82,15 +105,18 @@ export function CheckoutContainer({ onCheckout, onCheckoutCompleted, onChangePla
|
|
|
82
105
|
},
|
|
83
106
|
}}>
|
|
84
107
|
<CheckoutLayout className="stigg-checkout-layout">
|
|
85
|
-
<CheckoutProgressBar />
|
|
108
|
+
{!isFreeDowngrade && <CheckoutProgressBar />}
|
|
86
109
|
<CheckoutContent>
|
|
87
|
-
<CheckoutPanel>
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
110
|
+
<CheckoutPanel>{isLoadingCheckoutData ? <ContentLoadingSkeleton /> : checkoutContent}</CheckoutPanel>
|
|
111
|
+
{isLoadingCheckoutData ? (
|
|
112
|
+
<CheckoutSummarySkeleton />
|
|
113
|
+
) : (
|
|
114
|
+
<CheckoutSummary
|
|
115
|
+
disablePromotionCode={disablePromotionCode}
|
|
116
|
+
onCheckout={onCheckout}
|
|
117
|
+
onCheckoutCompleted={onCheckoutCompleted}
|
|
118
|
+
isFreeDowngrade={isFreeDowngrade}
|
|
119
|
+
/>
|
|
94
120
|
)}
|
|
95
121
|
</CheckoutContent>
|
|
96
122
|
</CheckoutLayout>
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { produce } from 'immer';
|
|
2
2
|
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
|
3
|
-
|
|
4
3
|
import { BillableFeature, BillingPeriod, GetCheckoutStateResults } from '@stigg/js-client-sdk';
|
|
5
|
-
|
|
6
4
|
import { CustomizedTheme, SdkThemeProvider, useStiggTheme } from '../../theme/Theme';
|
|
7
5
|
import { DeepPartial } from '../../types';
|
|
8
6
|
import { mapCheckoutConfiguration } from '../common/mapExternalTheme';
|
|
@@ -21,6 +19,7 @@ import {
|
|
|
21
19
|
import { CheckoutLocalization, getResolvedCheckoutLocalize } from './textOverrides';
|
|
22
20
|
import { CheckoutTheme, getResolvedCheckoutTheme } from './theme';
|
|
23
21
|
import { StiggTheme } from '../../theme/types';
|
|
22
|
+
import { BillingInformation } from './types';
|
|
24
23
|
|
|
25
24
|
export interface CheckoutContextState {
|
|
26
25
|
checkout?: GetCheckoutStateResults | null;
|
|
@@ -80,6 +79,7 @@ export type CheckoutProviderProps = {
|
|
|
80
79
|
preferredBillingPeriod?: BillingPeriod;
|
|
81
80
|
billingCountryCode?: string;
|
|
82
81
|
billableFeatures?: BillableFeature[];
|
|
82
|
+
billingInformation?: BillingInformation;
|
|
83
83
|
};
|
|
84
84
|
|
|
85
85
|
export function CheckoutProvider({
|
|
@@ -91,6 +91,7 @@ export function CheckoutProvider({
|
|
|
91
91
|
resourceId,
|
|
92
92
|
planId,
|
|
93
93
|
billingCountryCode,
|
|
94
|
+
billingInformation,
|
|
94
95
|
}: {
|
|
95
96
|
children: React.ReactNode;
|
|
96
97
|
} & CheckoutProviderProps) {
|
|
@@ -115,7 +116,10 @@ export function CheckoutProvider({
|
|
|
115
116
|
billingPeriod: planStep.billingPeriod,
|
|
116
117
|
activeSubscription: checkout?.activeSubscription,
|
|
117
118
|
});
|
|
118
|
-
const paymentStep = getPaymentStepInitialState({
|
|
119
|
+
const paymentStep = getPaymentStepInitialState({
|
|
120
|
+
customer: checkout?.customer,
|
|
121
|
+
taxPercentage: billingInformation?.taxDetails?.taxPercentage,
|
|
122
|
+
});
|
|
119
123
|
const progressBar = getProgressBarInitialState({
|
|
120
124
|
availableAddons: isLoading ? undefined : addonsStep.availableAddons,
|
|
121
125
|
});
|
|
@@ -135,7 +139,7 @@ export function CheckoutProvider({
|
|
|
135
139
|
|
|
136
140
|
return initialState;
|
|
137
141
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
138
|
-
}, [preferredBillingPeriod, billingCountryCode, checkout, isLoading]);
|
|
142
|
+
}, [preferredBillingPeriod, billingCountryCode, checkout, isLoading, billingInformation?.taxDetails?.taxPercentage]);
|
|
139
143
|
|
|
140
144
|
return (
|
|
141
145
|
<SdkThemeProvider key={checkout?.plan.id} componentTheme={configuration}>
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { StyledArrowRightIcon } from '../planHeader/PlanHeader.style';
|
|
3
|
+
import styled from '@emotion/styled/macro';
|
|
4
|
+
import { Alert, Box } from '@mui/material';
|
|
5
|
+
import { Typography } from '../../common/Typography';
|
|
6
|
+
import { CheckoutStatePlan, Subscription } from '@stigg/js-client-sdk';
|
|
7
|
+
import { Currency, BillingPeriod } from '@stigg/js-client-sdk';
|
|
8
|
+
import { currencyPriceFormatter } from '../../utils/currencyUtils';
|
|
9
|
+
import { CheckoutLocalization } from '../textOverrides';
|
|
10
|
+
|
|
11
|
+
const DowngradeToFreePlansContainer = styled(Box)`
|
|
12
|
+
display: flex;
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
const DowngradeToFreeAlert = styled(Alert)`
|
|
16
|
+
margin-bottom: 16px;
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
export const DowngradeToFreePlanBox = styled(Box)`
|
|
20
|
+
padding: 16px;
|
|
21
|
+
border-radius: 10px;
|
|
22
|
+
width: 100%;
|
|
23
|
+
border: ${({ theme }) => `1px solid ${theme.stigg.palette.outlinedBorder}`};
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
export const DowngradeToFreeContent = ({
|
|
27
|
+
planName,
|
|
28
|
+
totalPrice,
|
|
29
|
+
billingPeriod,
|
|
30
|
+
}: {
|
|
31
|
+
planName: string;
|
|
32
|
+
totalPrice?: {
|
|
33
|
+
amount: number;
|
|
34
|
+
currency: Currency;
|
|
35
|
+
};
|
|
36
|
+
billingPeriod?: BillingPeriod;
|
|
37
|
+
}) => {
|
|
38
|
+
const priceText = totalPrice
|
|
39
|
+
? currencyPriceFormatter({ amount: totalPrice.amount, currency: totalPrice.currency })
|
|
40
|
+
: 'Free';
|
|
41
|
+
|
|
42
|
+
const billingPeriodText = billingPeriod ? ` / Paid ${billingPeriod.toLowerCase()}` : '';
|
|
43
|
+
return (
|
|
44
|
+
<DowngradeToFreePlanBox className="stigg-checkout-free-downgrade-plan-box">
|
|
45
|
+
<Typography className="stigg-checkout-downgrade-to-free-text-plan" color="secondary">
|
|
46
|
+
{planName}
|
|
47
|
+
</Typography>
|
|
48
|
+
<div>
|
|
49
|
+
<Typography
|
|
50
|
+
className="stigg-checkout-downgrade-to-free-text-price"
|
|
51
|
+
span
|
|
52
|
+
bold={true}
|
|
53
|
+
variant="h3"
|
|
54
|
+
color="primary">
|
|
55
|
+
{priceText}
|
|
56
|
+
</Typography>
|
|
57
|
+
<Typography className="stigg-checkout-downgrade-to-free-text-billing-period" span color="secondary">
|
|
58
|
+
{billingPeriodText}
|
|
59
|
+
</Typography>
|
|
60
|
+
</div>
|
|
61
|
+
</DowngradeToFreePlanBox>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const DowngradeToFreePlan = ({
|
|
66
|
+
checkoutLocalization,
|
|
67
|
+
activeSubscription,
|
|
68
|
+
freePlan,
|
|
69
|
+
}: {
|
|
70
|
+
checkoutLocalization: CheckoutLocalization;
|
|
71
|
+
activeSubscription: Subscription;
|
|
72
|
+
freePlan: CheckoutStatePlan;
|
|
73
|
+
}) => {
|
|
74
|
+
return (
|
|
75
|
+
<>
|
|
76
|
+
<DowngradeToFreeAlert className="stigg-checkout-downgrade-to-free-alert" severity="info">
|
|
77
|
+
<Typography color="secondary">
|
|
78
|
+
{checkoutLocalization.downgradeToFreeAlertText({ plan: activeSubscription.plan })}
|
|
79
|
+
</Typography>
|
|
80
|
+
</DowngradeToFreeAlert>
|
|
81
|
+
|
|
82
|
+
<DowngradeToFreePlansContainer className="stigg-checkout-downgrade-to-free-plans-container">
|
|
83
|
+
<DowngradeToFreeContent
|
|
84
|
+
planName={activeSubscription.plan.displayName}
|
|
85
|
+
totalPrice={activeSubscription.totalPrice?.total}
|
|
86
|
+
billingPeriod={activeSubscription.prices[0].billingPeriod}
|
|
87
|
+
/>
|
|
88
|
+
|
|
89
|
+
<StyledArrowRightIcon
|
|
90
|
+
className="stigg-checkout-downgrade-to-free-arrow"
|
|
91
|
+
style={{ margin: 'auto 16px', minWidth: '16px' }}
|
|
92
|
+
/>
|
|
93
|
+
|
|
94
|
+
<DowngradeToFreeContent planName={freePlan.displayName} />
|
|
95
|
+
</DowngradeToFreePlansContainer>
|
|
96
|
+
</>
|
|
97
|
+
);
|
|
98
|
+
};
|
|
@@ -1,18 +1,27 @@
|
|
|
1
|
-
import { Customer } from '@stigg/js-client-sdk';
|
|
1
|
+
import { BillingAddress, Customer } from '@stigg/js-client-sdk';
|
|
2
2
|
|
|
3
3
|
import { useCheckoutContext } from '../CheckoutProvider';
|
|
4
4
|
|
|
5
5
|
export type PaymentStepState = {
|
|
6
6
|
useNewPaymentMethod: boolean;
|
|
7
7
|
errorMessage?: string;
|
|
8
|
+
billingAddress?: BillingAddress;
|
|
9
|
+
taxPercentage?: number;
|
|
8
10
|
};
|
|
9
11
|
|
|
10
12
|
type GetPaymentStepInitialStateProps = {
|
|
11
13
|
customer?: Customer;
|
|
14
|
+
taxPercentage?: number;
|
|
12
15
|
};
|
|
13
16
|
|
|
14
|
-
export function getPaymentStepInitialState({
|
|
15
|
-
|
|
17
|
+
export function getPaymentStepInitialState({
|
|
18
|
+
customer,
|
|
19
|
+
taxPercentage,
|
|
20
|
+
}: GetPaymentStepInitialStateProps): PaymentStepState {
|
|
21
|
+
return {
|
|
22
|
+
useNewPaymentMethod: !customer?.paymentMethodDetails,
|
|
23
|
+
taxPercentage,
|
|
24
|
+
};
|
|
16
25
|
}
|
|
17
26
|
|
|
18
27
|
function useSetUseNewPaymentMethod() {
|
|
@@ -33,6 +42,15 @@ function useSetErrorMessage() {
|
|
|
33
42
|
});
|
|
34
43
|
}
|
|
35
44
|
|
|
45
|
+
function useSetBillingAddress() {
|
|
46
|
+
const [, setState] = useCheckoutContext();
|
|
47
|
+
|
|
48
|
+
return (billingAddress?: BillingAddress) =>
|
|
49
|
+
setState((draft) => {
|
|
50
|
+
draft.paymentStep.billingAddress = billingAddress;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
36
54
|
function usePaymentState() {
|
|
37
55
|
const [{ paymentStep }] = useCheckoutContext();
|
|
38
56
|
return paymentStep;
|
|
@@ -45,5 +63,6 @@ export function usePaymentStepModel() {
|
|
|
45
63
|
...state,
|
|
46
64
|
setUseNewPaymentMethod: useSetUseNewPaymentMethod(),
|
|
47
65
|
setErrorMessage: useSetErrorMessage(),
|
|
66
|
+
setBillingAddress: useSetBillingAddress(),
|
|
48
67
|
};
|
|
49
68
|
}
|
|
@@ -88,11 +88,6 @@ function resolveBillingPeriod({
|
|
|
88
88
|
const hasMonthlyPrices = plan?.pricePoints.some((pricePoint) => pricePoint.billingPeriod === BillingPeriod.Monthly);
|
|
89
89
|
const hasAnnualPrices = plan?.pricePoints.some((pricePoint) => pricePoint.billingPeriod === BillingPeriod.Annually);
|
|
90
90
|
|
|
91
|
-
const isUpdate = activeSubscription?.plan?.id === plan?.id;
|
|
92
|
-
if (isUpdate) {
|
|
93
|
-
return activeSubscription?.price?.billingPeriod || BillingPeriod.Monthly;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
91
|
if (preferredBillingPeriod) {
|
|
97
92
|
if (preferredBillingPeriod === BillingPeriod.Monthly && hasMonthlyPrices) {
|
|
98
93
|
return BillingPeriod.Monthly;
|
|
@@ -102,6 +97,11 @@ function resolveBillingPeriod({
|
|
|
102
97
|
}
|
|
103
98
|
}
|
|
104
99
|
|
|
100
|
+
const isUpdate = activeSubscription?.plan?.id === plan?.id;
|
|
101
|
+
if (isUpdate) {
|
|
102
|
+
return activeSubscription?.price?.billingPeriod || BillingPeriod.Monthly;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
105
|
if (activeSubscription?.price?.billingPeriod) {
|
|
106
106
|
return activeSubscription?.price?.billingPeriod;
|
|
107
107
|
}
|
|
@@ -1,10 +1,29 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
-
import { PreviewSubscription, SubscriptionPreview } from '@stigg/js-client-sdk';
|
|
2
|
+
import { BillingAddress, PreviewSubscription, SubscriptionPreview } from '@stigg/js-client-sdk';
|
|
3
3
|
import { useStiggContext } from '../../StiggProvider';
|
|
4
4
|
import { useCheckoutContext } from '../CheckoutProvider';
|
|
5
5
|
import { useCheckoutModel } from './useCheckoutModel';
|
|
6
6
|
import { useSubscriptionModel } from './useSubscriptionModel';
|
|
7
7
|
|
|
8
|
+
function mapBillingInformation({
|
|
9
|
+
billingAddress,
|
|
10
|
+
taxPercentage,
|
|
11
|
+
}: {
|
|
12
|
+
billingAddress?: BillingAddress;
|
|
13
|
+
taxPercentage?: number;
|
|
14
|
+
}): Pick<PreviewSubscription, 'billingInformation'> {
|
|
15
|
+
if (!billingAddress && !taxPercentage) {
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
billingInformation: {
|
|
21
|
+
...(billingAddress ? { billingAddress } : {}),
|
|
22
|
+
...(taxPercentage ? { taxPercentage } : {}),
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
8
27
|
export const usePreviewSubscriptionAction = () => {
|
|
9
28
|
const { stigg } = useStiggContext();
|
|
10
29
|
const subscription = useSubscriptionModel();
|
|
@@ -29,8 +48,10 @@ export const usePreviewSubscriptionAction = () => {
|
|
|
29
48
|
promotionCode: promotionCode ?? subscription.promotionCode,
|
|
30
49
|
resourceId,
|
|
31
50
|
billingCountryCode: planStep.billingCountryCode,
|
|
32
|
-
|
|
33
|
-
|
|
51
|
+
...mapBillingInformation({
|
|
52
|
+
billingAddress: subscription.billingAddress,
|
|
53
|
+
taxPercentage: subscription.taxPercentage,
|
|
54
|
+
}),
|
|
34
55
|
};
|
|
35
56
|
subscriptionPreview = await stigg.previewSubscription(previewSubscriptionProps);
|
|
36
57
|
}
|
|
@@ -51,6 +72,8 @@ export const usePreviewSubscriptionAction = () => {
|
|
|
51
72
|
subscription.billingPeriod,
|
|
52
73
|
subscription.billableFeatures,
|
|
53
74
|
subscription.promotionCode,
|
|
75
|
+
subscription.billingAddress,
|
|
76
|
+
subscription.taxPercentage,
|
|
54
77
|
planStep.billingCountryCode,
|
|
55
78
|
],
|
|
56
79
|
);
|
|
@@ -22,12 +22,14 @@ export type ProgressBarState = {
|
|
|
22
22
|
activeStep: number;
|
|
23
23
|
completedSteps: number[];
|
|
24
24
|
steps: CheckoutStep[];
|
|
25
|
+
isDisabled: boolean;
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
const INITIAL_STATE: ProgressBarState = {
|
|
28
29
|
activeStep: 0,
|
|
29
30
|
completedSteps: [],
|
|
30
31
|
steps: CHECKOUT_STEPS,
|
|
32
|
+
isDisabled: false,
|
|
31
33
|
};
|
|
32
34
|
|
|
33
35
|
export function getProgressBarInitialState({ availableAddons }: { availableAddons?: Addon[] }) {
|
|
@@ -66,6 +68,10 @@ function useGoNext() {
|
|
|
66
68
|
const [, setState] = useCheckoutContext();
|
|
67
69
|
return () =>
|
|
68
70
|
setState(({ progressBar }) => {
|
|
71
|
+
if (progressBar.isDisabled) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
69
75
|
if (!progressBar.completedSteps.includes(progressBar.activeStep)) {
|
|
70
76
|
progressBar.completedSteps.push(progressBar.activeStep);
|
|
71
77
|
}
|
|
@@ -76,14 +82,26 @@ function useGoNext() {
|
|
|
76
82
|
});
|
|
77
83
|
}
|
|
78
84
|
|
|
85
|
+
function useSetIsDisabled() {
|
|
86
|
+
const [, setState] = useCheckoutContext();
|
|
87
|
+
return (isDisabled?: boolean) =>
|
|
88
|
+
setState(({ progressBar }) => {
|
|
89
|
+
progressBar.isDisabled = !!isDisabled;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
79
93
|
export function useProgressBarModel() {
|
|
80
94
|
const progressBarState = useProgressBarState();
|
|
95
|
+
const currentStep = progressBarState.steps[progressBarState.activeStep];
|
|
96
|
+
|
|
81
97
|
return {
|
|
98
|
+
currentStep,
|
|
82
99
|
progressBarState,
|
|
83
100
|
isLastStep: progressBarState.activeStep === progressBarState.steps.length - 1,
|
|
84
101
|
isCheckoutComplete: isCheckoutComplete(progressBarState),
|
|
85
102
|
setActiveStep: useSetActiveStep(),
|
|
86
103
|
markStepAsCompleted: useMarkStepAsCompleted(),
|
|
87
104
|
goNext: useGoNext(),
|
|
105
|
+
setIsDisabled: useSetIsDisabled(),
|
|
88
106
|
};
|
|
89
107
|
}
|
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
import { useCheckoutContext } from '../CheckoutProvider';
|
|
2
2
|
import { AddonsStepState } from './useAddonsStepModel';
|
|
3
3
|
import { PromotionCodeState } from './useCouponModel';
|
|
4
|
+
import { PaymentStepState } from './usePaymentStepModel';
|
|
4
5
|
import { PlanStepState } from './usePlanStepModel';
|
|
5
6
|
|
|
6
|
-
export type SubscriptionState = PlanStepState &
|
|
7
|
+
export type SubscriptionState = PlanStepState &
|
|
8
|
+
PromotionCodeState &
|
|
9
|
+
Pick<AddonsStepState, 'addons'> &
|
|
10
|
+
Pick<PaymentStepState, 'billingAddress' | 'taxPercentage'>;
|
|
7
11
|
|
|
8
12
|
export function useSubscriptionModel(): SubscriptionState {
|
|
9
|
-
const [{ planStep, addonsStep, promotionCode }] = useCheckoutContext();
|
|
13
|
+
const [{ planStep, addonsStep, promotionCode, paymentStep }] = useCheckoutContext();
|
|
10
14
|
|
|
11
15
|
return {
|
|
12
16
|
...planStep,
|
|
13
17
|
addons: addonsStep.addons,
|
|
14
18
|
promotionCode,
|
|
19
|
+
billingAddress: paymentStep.billingAddress,
|
|
20
|
+
taxPercentage: paymentStep.taxPercentage,
|
|
15
21
|
};
|
|
16
22
|
}
|
|
@@ -11,7 +11,7 @@ export function useSubscriptionState(): ApplySubscription | undefined {
|
|
|
11
11
|
const addons = subscription.addons.map(({ addon, quantity }) => ({ addonId: addon.id, quantity }));
|
|
12
12
|
|
|
13
13
|
if (!plan?.id) {
|
|
14
|
-
return;
|
|
14
|
+
return undefined;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
return {
|
|
@@ -22,5 +22,6 @@ export function useSubscriptionState(): ApplySubscription | undefined {
|
|
|
22
22
|
addons,
|
|
23
23
|
promotionCode: subscription.promotionCode,
|
|
24
24
|
billingCountryCode: subscription.billingCountryCode,
|
|
25
|
+
...(subscription.taxPercentage ? { billingInformation: { taxPercentage: subscription.taxPercentage } } : {}),
|
|
25
26
|
};
|
|
26
27
|
}
|
|
@@ -2,3 +2,4 @@ export { CheckoutTheme } from './theme';
|
|
|
2
2
|
export { Checkout, CheckoutProps } from './Checkout';
|
|
3
3
|
export { OnCheckoutCompletedParams, OnCheckoutParams, CheckoutResult } from './CheckoutContainer';
|
|
4
4
|
export { CheckoutLocalization } from './textOverrides';
|
|
5
|
+
export * from './types';
|
|
@@ -24,8 +24,9 @@ export const CheckoutProgressBar = () => {
|
|
|
24
24
|
|
|
25
25
|
return (
|
|
26
26
|
<Grid key={key} item display="flex" flexDirection="row" flex={1} justifyContent="flex-start">
|
|
27
|
-
{isLoadingCheckoutData
|
|
28
|
-
|
|
27
|
+
{isLoadingCheckoutData ? (
|
|
28
|
+
<Skeleton width={120} height={20} style={{ marginTop: 8 }} />
|
|
29
|
+
) : (
|
|
29
30
|
<StyledStepButton onClick={() => setActiveStep(index)} fullWidth disabled={isDisabled}>
|
|
30
31
|
<Grid item display="flex" flexDirection="row" alignItems="center" gap={1}>
|
|
31
32
|
<StepIcon
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from './PaymentMethods.style';
|
|
14
14
|
import { StripePaymentForm } from './stripe';
|
|
15
15
|
import { CheckoutLocalization } from '../../textOverrides';
|
|
16
|
+
import { CheckoutContainerProps } from '../../CheckoutContainer';
|
|
16
17
|
|
|
17
18
|
export type PaymentMethodLayoutProps = {
|
|
18
19
|
checked: boolean;
|
|
@@ -28,13 +29,13 @@ export type PaymentMethodProps = Pick<Customer, 'paymentMethodDetails'> &
|
|
|
28
29
|
export type NewPaymentMethodProps = Pick<PaymentMethodLayoutProps, 'checked' | 'readOnly'> & {
|
|
29
30
|
onSelect: () => void;
|
|
30
31
|
checkoutLocalization: CheckoutLocalization;
|
|
31
|
-
}
|
|
32
|
+
} & Pick<CheckoutContainerProps, 'onBillingAddressChange'>;
|
|
32
33
|
|
|
33
34
|
function PaymentMethodLayout({ checked, icon, text, subtitle, readOnly }: PaymentMethodLayoutProps) {
|
|
34
35
|
return (
|
|
35
36
|
<PaymentMethodLayoutContainer>
|
|
36
37
|
<Radio checked={checked} disabled={readOnly} />
|
|
37
|
-
<Icon icon={icon} style={{ display: '
|
|
38
|
+
<Icon icon={icon} style={{ display: 'flex' }} />
|
|
38
39
|
<PaymentMethodTextContainer container>
|
|
39
40
|
<Grid item>{text}</Grid>
|
|
40
41
|
{subtitle && <Grid item>{subtitle}</Grid>}
|
|
@@ -55,11 +56,11 @@ export function ExistingPaymentMethod({ checked, paymentMethodDetails, readOnly,
|
|
|
55
56
|
checked={checked}
|
|
56
57
|
readOnly={readOnly}
|
|
57
58
|
icon="PaymentMethod"
|
|
58
|
-
text={<Typography variant="h6">{`
|
|
59
|
+
text={<Typography variant="h6">{`Card ending in ${last4Digits}`}</Typography>}
|
|
59
60
|
subtitle={
|
|
60
61
|
!!expirationMonth &&
|
|
61
62
|
!!expirationYear && (
|
|
62
|
-
<Typography variant="body1">{`
|
|
63
|
+
<Typography variant="body1">{`Expires ${expirationMonth
|
|
63
64
|
.toString()
|
|
64
65
|
.padStart(2, '0')}/${expirationYear}`}</Typography>
|
|
65
66
|
)
|
|
@@ -69,7 +70,13 @@ export function ExistingPaymentMethod({ checked, paymentMethodDetails, readOnly,
|
|
|
69
70
|
);
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
export function NewPaymentMethod({
|
|
73
|
+
export function NewPaymentMethod({
|
|
74
|
+
checked,
|
|
75
|
+
onSelect,
|
|
76
|
+
readOnly,
|
|
77
|
+
checkoutLocalization,
|
|
78
|
+
onBillingAddressChange,
|
|
79
|
+
}: NewPaymentMethodProps) {
|
|
73
80
|
return (
|
|
74
81
|
<NewPaymentMethodContainer item onClick={onSelect} $disabled={readOnly}>
|
|
75
82
|
<PaymentMethodLayout
|
|
@@ -79,7 +86,7 @@ export function NewPaymentMethod({ checked, onSelect, readOnly, checkoutLocaliza
|
|
|
79
86
|
text={<Typography variant="h6">{checkoutLocalization.newPaymentMethodText}</Typography>}
|
|
80
87
|
/>
|
|
81
88
|
<Collapse in={checked}>
|
|
82
|
-
<StripePaymentForm />
|
|
89
|
+
<StripePaymentForm onBillingAddressChange={onBillingAddressChange} />
|
|
83
90
|
</Collapse>
|
|
84
91
|
</NewPaymentMethodContainer>
|
|
85
92
|
);
|
|
@@ -4,6 +4,7 @@ import { Alert, Grid } from '@mui/material';
|
|
|
4
4
|
import { useCheckoutModel, usePaymentStepModel } from '../../hooks';
|
|
5
5
|
import { ExistingPaymentMethod, NewPaymentMethod } from './PaymentMethods';
|
|
6
6
|
import { Typography } from '../../../common/Typography';
|
|
7
|
+
import { CheckoutContainerProps } from '../../CheckoutContainer';
|
|
7
8
|
|
|
8
9
|
const PaymentContainer = styled(Grid)`
|
|
9
10
|
display: flex;
|
|
@@ -12,7 +13,7 @@ const PaymentContainer = styled(Grid)`
|
|
|
12
13
|
margin: 32px 0;
|
|
13
14
|
`;
|
|
14
15
|
|
|
15
|
-
export function PaymentStep() {
|
|
16
|
+
export function PaymentStep({ onBillingAddressChange }: Pick<CheckoutContainerProps, 'onBillingAddressChange'>) {
|
|
16
17
|
const { checkoutState, checkoutLocalization, widgetState } = useCheckoutModel();
|
|
17
18
|
const { customer } = checkoutState || {};
|
|
18
19
|
const { errorMessage, useNewPaymentMethod, setUseNewPaymentMethod } = usePaymentStepModel();
|
|
@@ -44,6 +45,7 @@ export function PaymentStep() {
|
|
|
44
45
|
checked={useNewPaymentMethod}
|
|
45
46
|
checkoutLocalization={checkoutLocalization}
|
|
46
47
|
onSelect={() => handleOnSelect(true)}
|
|
48
|
+
onBillingAddressChange={onBillingAddressChange}
|
|
47
49
|
/>
|
|
48
50
|
</PaymentContainer>
|
|
49
51
|
);
|