@stigg/react-sdk 4.4.0-beta.8 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/dist/components/checkout/Checkout.d.ts +3 -2
  2. package/dist/components/checkout/CheckoutContainer.d.ts +5 -2
  3. package/dist/components/checkout/CheckoutProvider.d.ts +3 -2
  4. package/dist/components/checkout/components/DowngradeToFreeContainer.d.ts +3 -7
  5. package/dist/components/checkout/components/StyledArrow.d.ts +5 -0
  6. package/dist/components/checkout/hooks/useCheckoutModel.d.ts +2 -0
  7. package/dist/components/checkout/hooks/useLoadCheckout.d.ts +3 -1
  8. package/dist/components/checkout/hooks/usePreviewSubscription.d.ts +17 -8
  9. package/dist/components/checkout/progressBar/CheckoutProgressBar.style.d.ts +1 -0
  10. package/dist/components/checkout/promotionCode/AddPromotionCode.d.ts +2 -4
  11. package/dist/components/checkout/promotionCode/AddPromotionCodeButton.d.ts +2 -1
  12. package/dist/components/checkout/promotionCode/PromotionCodeSection.d.ts +6 -2
  13. package/dist/components/checkout/steps/payment/stripe/useSubmit.d.ts +2 -2
  14. package/dist/components/checkout/steps/plan/BillingPeriodPicker.d.ts +1 -1
  15. package/dist/components/checkout/steps/plan/CheckoutPlanStep.d.ts +2 -1
  16. package/dist/components/checkout/summary/CheckoutSuccess.d.ts +4 -1
  17. package/dist/components/checkout/summary/CheckoutSummary.d.ts +1 -1
  18. package/dist/components/checkout/summary/components/CheckoutCaptions.d.ts +3 -2
  19. package/dist/components/checkout/summary/components/LineItems.d.ts +12 -8
  20. package/dist/components/checkout/textOverrides.d.ts +65 -21
  21. package/dist/components/checkout/theme.d.ts +0 -1
  22. package/dist/components/checkout/types.d.ts +7 -1
  23. package/dist/components/common/TiersSelectContainer.d.ts +1 -2
  24. package/dist/components/common/Typography.d.ts +2 -2
  25. package/dist/components/common/customIcons.d.ts +2 -0
  26. package/dist/components/customerPortal/subscriptionOverview/subscriptionView/SubscriptionView.style.d.ts +1 -1
  27. package/dist/components/paywall/PlanPrice.d.ts +1 -1
  28. package/dist/components/utils/currencyUtils.d.ts +2 -1
  29. package/dist/components/utils/getFeatureName.d.ts +1 -0
  30. package/dist/react-sdk.cjs.development.js +1153 -624
  31. package/dist/react-sdk.cjs.development.js.map +1 -1
  32. package/dist/react-sdk.cjs.production.min.js +1 -1
  33. package/dist/react-sdk.cjs.production.min.js.map +1 -1
  34. package/dist/react-sdk.esm.js +1176 -651
  35. package/dist/react-sdk.esm.js.map +1 -1
  36. package/dist/stories/mocks/checkout/consts.d.ts +11 -0
  37. package/dist/stories/mocks/checkout/mockCheckoutPreview.d.ts +2 -0
  38. package/dist/stories/mocks/checkout/mockCheckoutState.d.ts +2 -0
  39. package/dist/theme/getResolvedTheme.d.ts +1 -0
  40. package/dist/theme/types.d.ts +1 -0
  41. package/package.json +28 -20
  42. package/src/assets/coupon.svg +6 -0
  43. package/src/assets/pay-as-you-go-charge.svg +11 -0
  44. package/src/components/checkout/Checkout.tsx +5 -2
  45. package/src/components/checkout/CheckoutContainer.tsx +21 -12
  46. package/src/components/checkout/CheckoutProvider.tsx +5 -3
  47. package/src/components/checkout/components/DowngradeToFreeContainer.tsx +33 -36
  48. package/src/components/checkout/components/StyledArrow.tsx +9 -0
  49. package/src/components/checkout/hooks/useCheckoutModel.ts +12 -2
  50. package/src/components/checkout/hooks/useLoadCheckout.ts +10 -2
  51. package/src/components/checkout/hooks/usePlanStepModel.ts +22 -7
  52. package/src/components/checkout/hooks/usePreviewSubscription.ts +103 -50
  53. package/src/components/checkout/planHeader/PlanHeader.tsx +18 -25
  54. package/src/components/checkout/progressBar/CheckoutProgressBar.style.ts +11 -12
  55. package/src/components/checkout/progressBar/CheckoutProgressBar.tsx +7 -6
  56. package/src/components/checkout/promotionCode/AddPromotionCode.tsx +32 -9
  57. package/src/components/checkout/promotionCode/AddPromotionCodeButton.tsx +15 -11
  58. package/src/components/checkout/promotionCode/AppliedPromotionCode.tsx +4 -3
  59. package/src/components/checkout/promotionCode/PromotionCodeSection.tsx +21 -7
  60. package/src/components/checkout/steps/addons/CheckoutAddonsStep.style.tsx +0 -1
  61. package/src/components/checkout/steps/addons/CheckoutAddonsStep.tsx +7 -4
  62. package/src/components/checkout/steps/payment/PaymentMethods.style.ts +4 -1
  63. package/src/components/checkout/steps/payment/PaymentStep.tsx +0 -1
  64. package/src/components/checkout/steps/payment/stripe/useSubmit.ts +8 -4
  65. package/src/components/checkout/steps/plan/BillingPeriodPicker.style.tsx +3 -2
  66. package/src/components/checkout/steps/plan/BillingPeriodPicker.tsx +11 -8
  67. package/src/components/checkout/steps/plan/CheckoutChargeList.tsx +35 -14
  68. package/src/components/checkout/steps/plan/CheckoutPlanStep.tsx +10 -5
  69. package/src/components/checkout/summary/CheckoutSuccess.tsx +52 -6
  70. package/src/components/checkout/summary/CheckoutSummary.tsx +166 -59
  71. package/src/components/checkout/summary/components/CheckoutCaptions.tsx +63 -39
  72. package/src/components/checkout/summary/components/LineItems.tsx +77 -28
  73. package/src/components/checkout/textOverrides.ts +112 -30
  74. package/src/components/checkout/theme.ts +0 -4
  75. package/src/components/checkout/types.ts +15 -1
  76. package/src/components/common/Icon.tsx +4 -6
  77. package/src/components/common/TiersSelectContainer.tsx +7 -8
  78. package/src/components/common/Typography.tsx +12 -3
  79. package/src/components/common/customIcons.ts +2 -0
  80. package/src/components/common/mapExternalTheme.ts +1 -2
  81. package/src/components/customerPortal/paywall/CustomerPortalPaywall.style.ts +4 -3
  82. package/src/components/paywall/PlanOfferingButton.tsx +6 -8
  83. package/src/components/paywall/PlanPrice.tsx +14 -17
  84. package/src/components/utils/currencyUtils.ts +4 -2
  85. package/src/components/utils/getFeatureName.ts +13 -5
  86. package/src/stories/Checkout.stories.tsx +37 -5
  87. package/src/stories/CustomerPortal.stories.tsx +2 -2
  88. package/src/stories/mocks/checkout/consts.ts +15 -0
  89. package/src/stories/mocks/checkout/mockCheckoutPreview.ts +138 -0
  90. package/src/stories/mocks/checkout/mockCheckoutState.ts +206 -0
  91. package/src/theme/Theme.tsx +10 -1
  92. package/src/theme/getResolvedTheme.ts +1 -0
  93. package/src/theme/types.ts +1 -0
  94. package/dist/components/checkout/planHeader/PlanHeader.style.d.ts +0 -25
  95. package/src/components/checkout/planHeader/PlanHeader.style.tsx +0 -23
@@ -1,32 +1,42 @@
1
+ /* eslint-disable react/no-unused-prop-types */
1
2
  import React from 'react';
2
3
  import moment from 'moment';
3
- import { Plan, Subscription, SubscriptionPreview } from '@stigg/js-client-sdk';
4
+ import { BillingModel, BillingPeriod, Plan, Subscription, SubscriptionPreviewV2 } from '@stigg/js-client-sdk';
4
5
  import { Typography } from '../../../common/Typography';
5
6
  import { currencyPriceFormatter } from '../../../utils/currencyUtils';
6
7
  import { CheckoutLocalization } from '../../textOverrides';
7
8
  import { WithSkeleton } from './WithSkeleton';
8
9
 
9
10
  export type CheckoutCaptionProps = {
10
- subscriptionPreview?: SubscriptionPreview | null;
11
+ subscriptionPreview?: SubscriptionPreviewV2 | null;
11
12
  isFetchingSubscriptionPreview: boolean;
12
13
  activeSubscription?: Subscription | null;
13
14
  plan?: Plan;
14
15
  checkoutLocalization: CheckoutLocalization;
16
+ billingPeriod: BillingPeriod;
15
17
  };
16
18
 
17
- // TODO: move to localization
18
-
19
- const RemainingCreditsCaption = ({ subscriptionPreview, isFetchingSubscriptionPreview }: CheckoutCaptionProps) => {
19
+ const RemainingCreditsCaption = ({
20
+ subscriptionPreview,
21
+ isFetchingSubscriptionPreview,
22
+ checkoutLocalization,
23
+ }: CheckoutCaptionProps) => {
20
24
  if (!subscriptionPreview?.proration?.netAmount?.amount || subscriptionPreview?.proration?.netAmount?.amount >= 0) {
21
25
  return null;
22
26
  }
23
27
 
24
- const credits = currencyPriceFormatter(subscriptionPreview?.proration?.netAmount);
28
+ const positiveAmount = subscriptionPreview.proration.netAmount.amount * -1;
29
+
30
+ const credits = currencyPriceFormatter({
31
+ amount: positiveAmount,
32
+ currency: subscriptionPreview.proration.netAmount.currency,
33
+ minimumFractionDigits: 2,
34
+ });
25
35
 
26
36
  return (
27
37
  <Typography variant="caption" style={{ marginTop: 14 }}>
28
38
  <WithSkeleton isLoading={isFetchingSubscriptionPreview} width="100%">
29
- {`Your remaining credits, which are ${credits}, will be kept for you for future use.`}
39
+ {checkoutLocalization.summary.creditsForUnusedTimeText({ credits })}
30
40
  </WithSkeleton>
31
41
  </Typography>
32
42
  );
@@ -44,9 +54,9 @@ const ScheduledUpdatesCaption = ({
44
54
 
45
55
  const { currentBillingPeriodEnd } = activeSubscription;
46
56
  const changesWillApplyText =
47
- typeof checkoutLocalization.changesWillApplyAtEndOfBillingPeriod === 'function'
48
- ? checkoutLocalization.changesWillApplyAtEndOfBillingPeriod({ billingPeriodEnd: currentBillingPeriodEnd })
49
- : checkoutLocalization.changesWillApplyAtEndOfBillingPeriod;
57
+ typeof checkoutLocalization.summary.changesWillApplyAtEndOfBillingPeriod === 'function'
58
+ ? checkoutLocalization.summary.changesWillApplyAtEndOfBillingPeriod({ billingPeriodEnd: currentBillingPeriodEnd })
59
+ : checkoutLocalization.summary.changesWillApplyAtEndOfBillingPeriod;
50
60
 
51
61
  return (
52
62
  <Typography variant="caption" style={{ marginTop: 14 }}>
@@ -57,49 +67,65 @@ const ScheduledUpdatesCaption = ({
57
67
  );
58
68
  };
59
69
 
60
- const ChargeDueTodayCaption = ({ subscriptionPreview, plan, isFetchingSubscriptionPreview }: CheckoutCaptionProps) => {
61
- if (!subscriptionPreview?.total || subscriptionPreview.total.amount <= 0) {
62
- return null;
63
- }
64
-
65
- const total = currencyPriceFormatter(subscriptionPreview.total);
66
- const usedCredits = !!subscriptionPreview.credits?.used;
67
-
68
- return (
69
- <Typography variant="caption" style={{ marginTop: 14 }}>
70
- <WithSkeleton isLoading={isFetchingSubscriptionPreview} width="100%">
71
- {`Today we’ll charge you ${total} (incl. taxes) for your new ${plan?.displayName} plan${
72
- usedCredits ? ' minus credits left from your existing plan.' : ''
73
- } `}
74
- </WithSkeleton>
75
- </Typography>
76
- );
77
- };
78
-
79
70
  const NextBillingCaption = ({
80
71
  subscriptionPreview,
81
72
  activeSubscription,
82
73
  plan,
83
74
  isFetchingSubscriptionPreview,
75
+ billingPeriod,
84
76
  }: CheckoutCaptionProps) => {
85
- if (!subscriptionPreview?.subscription?.total) {
77
+ if (!subscriptionPreview?.recurringSubscription?.total) {
86
78
  return null;
87
79
  }
88
80
 
89
- const isUpdatingSubscription = activeSubscription?.plan?.id && plan?.id && activeSubscription.plan.id === plan.id;
90
- const isScheduledPlanDowngrade = subscriptionPreview.isPlanDowngrade && subscriptionPreview.hasScheduledUpdates;
91
81
  const currentBillingPeriodEnd = subscriptionPreview.hasScheduledUpdates
92
82
  ? activeSubscription?.currentBillingPeriodEnd
93
83
  : subscriptionPreview?.billingPeriodRange.end;
94
- const billingDate = moment(currentBillingPeriodEnd).format('MMM D, YYYY');
95
- const total = currencyPriceFormatter(subscriptionPreview?.subscription.total);
84
+
85
+ const total = currencyPriceFormatter({
86
+ ...subscriptionPreview?.recurringSubscription.total,
87
+ minimumFractionDigits: 2,
88
+ });
89
+
90
+ const hasUnitBasedPricing = plan?.pricePoints.some((price) => price.pricingModel === BillingModel.UsageBased);
91
+ const hasNonUnitBasedPricing = plan?.pricePoints.some((price) => price.pricingModel !== BillingModel.UsageBased);
92
+
93
+ let text = 'We will bill you ';
94
+ let totalAmountText = `${total} `;
95
+
96
+ if (hasUnitBasedPricing) {
97
+ if (!hasNonUnitBasedPricing) {
98
+ totalAmountText = 'for';
99
+ } else {
100
+ totalAmountText += '+';
101
+ }
102
+
103
+ totalAmountText += ' applicable usage-based charges for this subscription ';
104
+ }
105
+
106
+ let nextBillingDate;
107
+ let billingPeriodText;
108
+
109
+ switch (billingPeriod) {
110
+ case BillingPeriod.Monthly:
111
+ billingPeriodText = 'month';
112
+ nextBillingDate = `the ${moment(currentBillingPeriodEnd).format('Do')}`;
113
+ break;
114
+ case BillingPeriod.Annually:
115
+ billingPeriodText = 'year';
116
+ nextBillingDate = moment(currentBillingPeriodEnd).format('MMMM Do');
117
+ break;
118
+ default: {
119
+ break;
120
+ }
121
+ }
122
+
123
+ text += `${totalAmountText}for this subscription every ${billingPeriodText} on ${nextBillingDate}, unless you cancel.`;
96
124
 
97
125
  return (
98
126
  <Typography variant="caption" style={{ marginTop: 14 }}>
99
127
  <WithSkeleton isLoading={isFetchingSubscriptionPreview} width="100%">
100
- {`Your${isUpdatingSubscription ? '' : ' new'} ${plan?.displayName} plan will ${
101
- isScheduledPlanDowngrade ? 'start' : 'renew'
102
- } on ${billingDate} for ${total} (incl. taxes) unless you cancel it.`}
128
+ {text}
103
129
  </WithSkeleton>
104
130
  </Typography>
105
131
  );
@@ -112,8 +138,6 @@ export function CheckoutCaptions(props: CheckoutCaptionProps) {
112
138
 
113
139
  <ScheduledUpdatesCaption {...props} />
114
140
 
115
- <ChargeDueTodayCaption {...props} />
116
-
117
141
  <NextBillingCaption {...props} />
118
142
  </>
119
143
  );
@@ -1,13 +1,15 @@
1
1
  import React from 'react';
2
2
  import styled from '@emotion/styled/macro';
3
3
  import { Grid } from '@mui/material';
4
- import { BillingPeriod, DiscountType, Price, SubscriptionPreview } from '@stigg/js-client-sdk';
4
+ import { BillingModel, BillingPeriod, Price, SubscriptionPreviewV2 } from '@stigg/js-client-sdk';
5
5
  import { Typography } from '../../../common/Typography';
6
6
  import { currencyPriceFormatter } from '../../../utils/currencyUtils';
7
7
  import { getTierByQuantity } from '../../../utils/priceTierUtils';
8
8
  import { WithSkeleton } from './WithSkeleton';
9
9
  import { Skeleton } from '../../components/Skeletons.style';
10
10
  import { CheckoutLocalization } from '../../textOverrides';
11
+ import { Icon } from '../../../common/Icon';
12
+ import { InformationTooltip } from '../../../common/InformationTooltip';
11
13
 
12
14
  export const LineItemContainer = styled.div`
13
15
  & + & {
@@ -30,15 +32,35 @@ export const getPriceString = ({ amount, price, quantity }: { amount: number; pr
30
32
  billingPeriodString = '12 months';
31
33
  }
32
34
 
33
- const addonPriceFormat = currencyPriceFormatter({ amount, currency: price.currency });
35
+ const addonPriceFormat = currencyPriceFormatter({ amount, currency: price.currency, minimumFractionDigits: 2 });
34
36
 
35
37
  return `${quantity > 1 ? `${quantity} x ${addonPriceFormat} each` : addonPriceFormat}${
36
38
  billingPeriodString ? ` x ${billingPeriodString}` : ''
37
39
  }`;
38
40
  };
39
41
 
40
- export const BilledPriceLineItem = ({ label, quantity, price }: { label: string; quantity: number; price: Price }) => {
42
+ const PayAsYouGoPriceTooltip = ({ checkoutLocalization }: { checkoutLocalization: CheckoutLocalization }) => {
43
+ const title = <Typography variant="body1">{checkoutLocalization.summary.payAsYouGoTooltipText}</Typography>;
44
+ return (
45
+ <InformationTooltip placement="left" title={title}>
46
+ <Icon icon="PayAsYouGoCharge" style={{ display: 'flex' }} />
47
+ </InformationTooltip>
48
+ );
49
+ };
50
+
51
+ export const BilledPriceLineItem = ({
52
+ checkoutLocalization,
53
+ label,
54
+ quantity,
55
+ price,
56
+ }: {
57
+ checkoutLocalization: CheckoutLocalization;
58
+ label: string;
59
+ quantity: number;
60
+ price: Price;
61
+ }) => {
41
62
  const { billingPeriod } = price;
63
+ const isPayAsYouGo = price.pricingModel === BillingModel.UsageBased;
42
64
 
43
65
  let amount;
44
66
  if (price.isTieredPrice) {
@@ -52,7 +74,7 @@ export const BilledPriceLineItem = ({ label, quantity, price }: { label: string;
52
74
  <LineItemContainer>
53
75
  <LineItemRow style={{ alignItems: 'flex-end' }}>
54
76
  <Grid item>
55
- <Typography variant="h6" color="primary">
77
+ <Typography variant="body1" color="secondary">
56
78
  {label}
57
79
  </Typography>
58
80
  {(quantity > 1 || billingPeriod === BillingPeriod.Annually) && (
@@ -61,9 +83,30 @@ export const BilledPriceLineItem = ({ label, quantity, price }: { label: string;
61
83
  </Typography>
62
84
  )}
63
85
  </Grid>
86
+ <Grid item display="flex" gap={1} alignItems="center">
87
+ {isPayAsYouGo && <PayAsYouGoPriceTooltip checkoutLocalization={checkoutLocalization} />}
88
+ <Typography variant="body1" color="secondary" style={{ wordBreak: 'break-word' }}>
89
+ {currencyPriceFormatter({ amount: quantity * amount, currency: price.currency, minimumFractionDigits: 2 })}
90
+ {isPayAsYouGo && ' / unit'}
91
+ </Typography>
92
+ </Grid>
93
+ </LineItemRow>
94
+ </LineItemContainer>
95
+ );
96
+ };
97
+
98
+ export const FreeChargeLineItem = ({ label }: { label: string }) => {
99
+ return (
100
+ <LineItemContainer>
101
+ <LineItemRow style={{ alignItems: 'flex-end' }}>
64
102
  <Grid item>
103
+ <Typography variant="body1" color="secondary">
104
+ {label}
105
+ </Typography>
106
+ </Grid>
107
+ <Grid item display="flex" gap={1} alignItems="center">
65
108
  <Typography variant="body1" color="secondary" style={{ wordBreak: 'break-word' }}>
66
- {currencyPriceFormatter({ amount: quantity * amount, currency: price.currency })}
109
+ Free
67
110
  </Typography>
68
111
  </Grid>
69
112
  </LineItemRow>
@@ -74,33 +117,35 @@ export const BilledPriceLineItem = ({ label, quantity, price }: { label: string;
74
117
  export const DiscountLineItem = ({
75
118
  subscriptionPreview,
76
119
  isFetchingSubscriptionPreview,
120
+ checkoutLocalization,
77
121
  }: {
78
- subscriptionPreview: SubscriptionPreview | null;
122
+ subscriptionPreview: SubscriptionPreviewV2;
79
123
  isFetchingSubscriptionPreview: boolean;
124
+ checkoutLocalization: CheckoutLocalization;
80
125
  }) => {
81
- const { subTotal, discount } = subscriptionPreview || {};
82
- if (!subTotal || !discount) {
126
+ const { discount, discountDetails } = subscriptionPreview.recurringSubscription || {};
127
+ if (!discount || !discountDetails) {
83
128
  return null;
84
129
  }
85
130
 
86
- let discountAmount: number;
87
- if (discount.type === DiscountType.Percentage) {
88
- discountAmount = -1 * Math.abs((discount.value / 100) * subTotal.amount);
89
- } else {
90
- discountAmount = -1 * Math.abs(discount.value);
91
- }
92
-
93
131
  return (
94
132
  <LineItemContainer>
95
133
  <LineItemRow>
96
134
  <Typography variant="body1" color="secondary">
97
- {`Discount${discount.type === DiscountType.Percentage ? ` (${discount.value}% off)` : ''}`}
135
+ {checkoutLocalization.summary.discountText({
136
+ discountDetails,
137
+ currency: subscriptionPreview.total.currency,
138
+ })}
98
139
  </Typography>
99
140
  <Typography variant="body1" color="secondary">
100
141
  {isFetchingSubscriptionPreview ? (
101
142
  <Skeleton width={50} height={16} />
102
143
  ) : (
103
- currencyPriceFormatter({ amount: discountAmount, currency: subTotal.currency })
144
+ currencyPriceFormatter({
145
+ amount: discount.amount,
146
+ currency: discount.currency,
147
+ minimumFractionDigits: 2,
148
+ })
104
149
  )}
105
150
  </Typography>
106
151
  </LineItemRow>
@@ -113,12 +158,13 @@ export const AppliedCreditsLineItem = ({
113
158
  isFetchingSubscriptionPreview,
114
159
  checkoutLocalization,
115
160
  }: {
116
- subscriptionPreview: SubscriptionPreview | null;
161
+ subscriptionPreview: SubscriptionPreviewV2 | null;
117
162
  isFetchingSubscriptionPreview: boolean;
118
163
  checkoutLocalization: CheckoutLocalization;
119
164
  }) => {
120
165
  const { credits } = subscriptionPreview || {};
121
- if (!credits || credits.used.amount <= 0) {
166
+
167
+ if (!credits || !credits.used || credits.used.amount <= 0) {
122
168
  return null;
123
169
  }
124
170
 
@@ -126,11 +172,15 @@ export const AppliedCreditsLineItem = ({
126
172
  <LineItemContainer>
127
173
  <LineItemRow>
128
174
  <Typography variant="body1" color="secondary">
129
- {checkoutLocalization.appliedCreditsTitle}
175
+ {checkoutLocalization.summary.appliedCreditsTitle}
130
176
  </Typography>
131
177
  <Typography variant="body1" color="secondary">
132
178
  <WithSkeleton isLoading={isFetchingSubscriptionPreview}>
133
- {currencyPriceFormatter({ amount: -1 * credits.used.amount, currency: credits.used.currency })}
179
+ {currencyPriceFormatter({
180
+ amount: -1 * credits.used.amount,
181
+ currency: credits.used.currency,
182
+ minimumFractionDigits: 2,
183
+ })}
134
184
  </WithSkeleton>
135
185
  </Typography>
136
186
  </LineItemRow>
@@ -139,16 +189,15 @@ export const AppliedCreditsLineItem = ({
139
189
  };
140
190
 
141
191
  export const TaxLineItem = ({
142
- subscriptionPreview,
192
+ tax,
193
+ taxDetails,
143
194
  isFetchingSubscriptionPreview,
144
195
  checkoutLocalization,
145
196
  }: {
146
- subscriptionPreview: SubscriptionPreview | null;
147
197
  isFetchingSubscriptionPreview: boolean;
148
198
  checkoutLocalization: CheckoutLocalization;
149
- }) => {
150
- const { tax, taxDetails } = subscriptionPreview || {};
151
- if (!taxDetails || !tax) {
199
+ } & Pick<SubscriptionPreviewV2, 'tax' | 'taxDetails'>) => {
200
+ if (!taxDetails || !tax || tax?.amount <= 0) {
152
201
  return null;
153
202
  }
154
203
 
@@ -156,11 +205,11 @@ export const TaxLineItem = ({
156
205
  <LineItemContainer>
157
206
  <LineItemRow>
158
207
  <Typography variant="body1" color="secondary">
159
- {checkoutLocalization.taxTitle({ taxDetails })}
208
+ {checkoutLocalization.summary.taxTitle({ taxDetails })}
160
209
  </Typography>
161
210
  <Typography variant="body1" color="secondary">
162
211
  <WithSkeleton isLoading={isFetchingSubscriptionPreview}>
163
- {currencyPriceFormatter({ amount: tax?.amount, currency: tax?.currency })}
212
+ {currencyPriceFormatter({ amount: tax?.amount, currency: tax?.currency, minimumFractionDigits: 2 })}
164
213
  </WithSkeleton>
165
214
  </Typography>
166
215
  </LineItemRow>
@@ -1,7 +1,15 @@
1
- import { BillingPeriod, Plan, SubscriptionPreviewTaxDetails } from '@stigg/js-client-sdk';
1
+ import {
2
+ BillingPeriod,
3
+ DiscountType,
4
+ Plan,
5
+ SubscriptionPreviewDiscount,
6
+ SubscriptionPreviewTaxDetails,
7
+ DiscountDurationType,
8
+ } from '@stigg/js-client-sdk';
2
9
  import moment from 'moment';
3
10
  import merge from 'lodash/merge';
4
11
  import { DeepPartial } from '../../types';
12
+ import { currencyPriceFormatter } from '../utils/currencyUtils';
5
13
 
6
14
  export type CheckoutLocalization = {
7
15
  changePlan: string;
@@ -10,53 +18,127 @@ export type CheckoutLocalization = {
10
18
  newPaymentMethodText: string;
11
19
  newPaymentMethodCardTitle: string;
12
20
  newPaymentMethodBillingAddressTitle: string;
13
- baseChargeText: string | ((params: { billingPeriod: BillingPeriod }) => string);
14
- totalText: string;
15
- subTotalText: string;
16
- addCouponCodeText: string;
17
- couponCodeTitle: string;
18
- addonsSectionTitle: string;
19
- changesWillApplyAtEndOfBillingPeriod: string | ((params: { billingPeriodEnd: Date }) => string);
21
+ summary: {
22
+ title: string;
23
+ planName: (params: { plan: Plan }) => string;
24
+ addCouponCodeText: string;
25
+ couponCodeTitle: string;
26
+ addonsSectionTitle: string;
27
+ discountsSectionTitle: string;
28
+ onlyPayAsYouGoText: string;
29
+ startsAtText: string;
30
+ proratedTotalDueText: string;
31
+ baseChargeText: string | ((params: { billingPeriod: BillingPeriod }) => string);
32
+ totalText: string;
33
+ totalDueText: string;
34
+ totalBillingPeriodText: (params: { billingPeriod: BillingPeriod }) => string;
35
+ discountText: (params: { discountDetails: SubscriptionPreviewDiscount; currency: string }) => string;
36
+ appliedCreditsTitle: string;
37
+ taxTitle: (params: { taxDetails: SubscriptionPreviewTaxDetails }) => string;
38
+ checkoutSuccessText: string;
39
+ payAsYouGoTooltipText: string;
40
+ changesWillApplyAtEndOfBillingPeriod: string | ((params: { billingPeriodEnd: Date }) => string);
41
+ creditsForUnusedTimeText: (params: { credits: string }) => string;
42
+ };
20
43
  checkoutButton: {
21
44
  nextText: string;
22
- downgradeText: string;
45
+ noChangesText: string;
46
+ updateText: string;
47
+ downgradeToFreeText: string;
23
48
  upgradeText: string;
24
- purchaseText: string;
25
49
  };
26
- appliedCreditsTitle: string;
27
- taxTitle: (params: { taxDetails: SubscriptionPreviewTaxDetails }) => string;
28
- downgradeToFreeAlertText: (params: { plan: Plan }) => string;
50
+ downgradeToFree: {
51
+ alertText: (params: { plan: Plan }) => string;
52
+ freePlanHeader: (params: { plan: Plan }) => string;
53
+ freePlanName: (params: { plan: Plan }) => string;
54
+ freePlanPriceText: (params: { plan: Plan }) => string;
55
+ paidPlanHeader: (params: { plan: Plan }) => string;
56
+ paidPlanName: (params: { plan: Plan }) => string;
57
+ paidPlanPriceText: (params: { plan: Plan; billingPeriod?: BillingPeriod }) => string;
58
+ };
29
59
  };
30
60
 
31
61
  export function getResolvedCheckoutLocalize(
32
62
  localizeOverride?: DeepPartial<CheckoutLocalization>,
33
63
  ): CheckoutLocalization {
34
64
  const checkoutDefaultLocalization: CheckoutLocalization = {
35
- changePlan: 'Change plan',
65
+ changePlan: 'Change',
36
66
  billingPeriodsTitle: 'Billing cycle',
37
67
  addAddonText: 'Add',
38
68
  newPaymentMethodText: 'New payment method',
39
69
  newPaymentMethodBillingAddressTitle: 'Billing address',
40
70
  newPaymentMethodCardTitle: 'Payment method',
41
- baseChargeText: () => 'Base charge',
42
- totalText: 'Total due today',
43
- subTotalText: 'Subtotal',
44
- addCouponCodeText: 'Add coupon code',
45
- couponCodeTitle: 'Coupon code',
46
- addonsSectionTitle: 'Add-ons',
47
- changesWillApplyAtEndOfBillingPeriod: ({ billingPeriodEnd }) =>
48
- `Your changes will apply on the end of your current billing cycle on ${moment(billingPeriodEnd).format(
49
- 'MMM D, YYYY',
50
- )}.`,
71
+ summary: {
72
+ title: 'Order summary',
73
+ planName: ({ plan }) => `${plan.displayName} plan`,
74
+ addCouponCodeText: 'Add promotion code',
75
+ couponCodeTitle: 'Promotion code',
76
+ addonsSectionTitle: 'Add-ons',
77
+ discountsSectionTitle: 'Discounts',
78
+ onlyPayAsYouGoText: 'Varies by usage',
79
+ startsAtText: 'Starts at ',
80
+ proratedTotalDueText: 'Prorated charge today',
81
+ baseChargeText: () => 'Base charge',
82
+ totalText: 'Total',
83
+ totalDueText: 'Total due today',
84
+ totalBillingPeriodText: ({ billingPeriod }) => `Billed ${billingPeriod.toLowerCase()}`,
85
+ appliedCreditsTitle: 'Applied credits',
86
+ creditsForUnusedTimeText: ({ credits }) =>
87
+ `Your account will be granted credits worth ${credits} for unused time, which will be automatically applied to future payments.`,
88
+ discountText: ({ discountDetails, currency }) => {
89
+ let discountText = '';
90
+ if (discountDetails.type === DiscountType.Percentage) {
91
+ discountText = `${discountDetails.value}% off`;
92
+ } else {
93
+ discountText = `${currencyPriceFormatter({ amount: discountDetails.value, currency })}`;
94
+ }
95
+
96
+ let discountTextPostfix = '';
97
+ switch (discountDetails.durationType) {
98
+ case DiscountDurationType.Once: {
99
+ discountTextPostfix = ' once';
100
+ break;
101
+ }
102
+ case DiscountDurationType.Repeating: {
103
+ const prefix = discountDetails.type === DiscountType.Fixed ? ' per month' : '';
104
+ discountTextPostfix = `${prefix} for ${discountDetails.durationInMonths} months`;
105
+
106
+ break;
107
+ }
108
+
109
+ case DiscountDurationType.Forever:
110
+ default: {
111
+ discountTextPostfix = '';
112
+ }
113
+ }
114
+
115
+ return `${discountText}${discountTextPostfix}`;
116
+ },
117
+ taxTitle: ({ taxDetails }) => `Tax (${taxDetails?.percentage}%)`,
118
+ changesWillApplyAtEndOfBillingPeriod: ({ billingPeriodEnd }) =>
119
+ `Your changes will take effect at the end of your current billing cycle on ${moment(billingPeriodEnd).format(
120
+ 'MMMM D, YYYY',
121
+ )}.`,
122
+ checkoutSuccessText: 'Changes applied',
123
+ payAsYouGoTooltipText: 'Billed according to actual usage',
124
+ },
51
125
  checkoutButton: {
52
126
  nextText: 'Next',
53
- downgradeText: 'Downgrade',
54
- upgradeText: 'Upgrade',
55
- purchaseText: 'Purchase',
127
+ noChangesText: 'No changes',
128
+ updateText: 'Update subscription',
129
+ downgradeToFreeText: 'Cancel subscription',
130
+ upgradeText: 'Subscribe',
131
+ },
132
+ downgradeToFree: {
133
+ alertText: () => `We’re sorry to see you cancel your paid subscription 😭`,
134
+ freePlanHeader: () => 'New plan',
135
+ freePlanName: ({ plan }) => `${plan.displayName}`,
136
+ freePlanPriceText: () => 'Free',
137
+ paidPlanHeader: () => 'Current plan',
138
+ paidPlanName: ({ plan }) => `${plan.displayName}`,
139
+ paidPlanPriceText: ({ billingPeriod }) =>
140
+ `Paid plan${billingPeriod ? `, billed ${billingPeriod.toLowerCase()}` : ''}`,
56
141
  },
57
- appliedCreditsTitle: 'Applied credits',
58
- taxTitle: ({ taxDetails }) => `Tax (${taxDetails?.percentage}%)`,
59
- downgradeToFreeAlertText: () => `We’re sorry to see you cancel your paid subscription 😭`,
60
142
  };
61
143
 
62
144
  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,4 +1,10 @@
1
- import { SubscriptionBillingInfo } from '@stigg/js-client-sdk';
1
+ import {
2
+ GetCheckoutState,
3
+ GetCheckoutStateResults,
4
+ PreviewSubscription,
5
+ SubscriptionBillingInfo,
6
+ SubscriptionPreviewV2,
7
+ } from '@stigg/js-client-sdk';
2
8
 
3
9
  export type BillingInformation = {
4
10
  taxDetails?: TaxDetailsInput;
@@ -7,3 +13,11 @@ export type BillingInformation = {
7
13
  export type TaxDetailsInput = {
8
14
  taxPercentage?: SubscriptionBillingInfo['taxPercentage'];
9
15
  };
16
+
17
+ export type MockCheckoutPreviewCallback = (params: PreviewSubscription) => SubscriptionPreviewV2;
18
+ export type MockCheckoutStateCallback = (params: GetCheckoutState) => GetCheckoutStateResults;
19
+
20
+ export type CheckoutMockProps = {
21
+ onMockCheckoutPreview?: MockCheckoutPreviewCallback;
22
+ onMockCheckoutState?: MockCheckoutStateCallback;
23
+ };
@@ -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, Theme, useTheme } from '@emotion/react';
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() as Theme;
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
  );