@stigg/react-sdk 4.3.0 → 4.4.0-beta.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 (184) hide show
  1. package/README.md +1 -1
  2. package/dist/components/checkout/Checkout.d.ts +5 -0
  3. package/dist/components/checkout/CheckoutContainer.d.ts +22 -0
  4. package/dist/components/checkout/CheckoutContainer.style.d.ts +26 -0
  5. package/dist/components/checkout/CheckoutProvider.d.ts +34 -0
  6. package/dist/components/checkout/components/Button.d.ts +8 -0
  7. package/dist/components/checkout/components/ContentLoadingSkeleton.d.ts +2 -0
  8. package/dist/components/checkout/components/InputField.d.ts +8 -0
  9. package/dist/components/checkout/components/Skeletons.style.d.ts +97 -0
  10. package/dist/components/checkout/components/index.d.ts +3 -0
  11. package/dist/components/checkout/formatting.d.ts +2 -0
  12. package/dist/components/checkout/hooks/index.d.ts +8 -0
  13. package/dist/components/checkout/hooks/useAddonsStepModel.d.ts +21 -0
  14. package/dist/components/checkout/hooks/useCheckoutModel.d.ts +10 -0
  15. package/dist/components/checkout/hooks/useCouponModel.d.ts +7 -0
  16. package/dist/components/checkout/hooks/useLoadCheckout.d.ts +11 -0
  17. package/dist/components/checkout/hooks/usePaymentStepModel.d.ts +16 -0
  18. package/dist/components/checkout/hooks/usePlanStepModel.d.ts +23 -0
  19. package/dist/components/checkout/hooks/usePreviewSubscription.d.ts +13 -0
  20. package/dist/components/checkout/hooks/useProgressBarModel.d.ts +26 -0
  21. package/dist/components/checkout/hooks/useSubscriptionModel.d.ts +5 -0
  22. package/dist/components/checkout/hooks/useSubscriptionState.d.ts +2 -0
  23. package/dist/components/checkout/index.d.ts +3 -0
  24. package/dist/components/checkout/planHeader/PlanHeader.d.ts +7 -0
  25. package/dist/components/checkout/planHeader/PlanHeader.style.d.ts +25 -0
  26. package/dist/components/checkout/planHeader/index.d.ts +1 -0
  27. package/dist/components/checkout/progressBar/CheckoutProgressBar.d.ts +2 -0
  28. package/dist/components/checkout/progressBar/CheckoutProgressBar.style.d.ts +48 -0
  29. package/dist/components/checkout/promotionCode/AddPromotionCode.d.ts +5 -0
  30. package/dist/components/checkout/promotionCode/AddPromotionCodeButton.d.ts +7 -0
  31. package/dist/components/checkout/promotionCode/AppliedPromotionCode.d.ts +6 -0
  32. package/dist/components/checkout/promotionCode/PromotionCodeSection.d.ts +5 -0
  33. package/dist/components/checkout/promotionCode/index.d.ts +1 -0
  34. package/dist/components/checkout/steps/addons/CheckoutAddonsStep.d.ts +2 -0
  35. package/dist/components/checkout/steps/addons/CheckoutAddonsStep.style.d.ts +93 -0
  36. package/dist/components/checkout/steps/addons/addon.utils.d.ts +15 -0
  37. package/dist/components/checkout/steps/addons/index.d.ts +1 -0
  38. package/dist/components/checkout/steps/payment/PaymentMethods.d.ts +20 -0
  39. package/dist/components/checkout/steps/payment/PaymentMethods.style.d.ts +117 -0
  40. package/dist/components/checkout/steps/payment/PaymentStep.d.ts +2 -0
  41. package/dist/components/checkout/steps/payment/index.d.ts +1 -0
  42. package/dist/components/checkout/steps/payment/stripe/StripePaymentForm.d.ts +2 -0
  43. package/dist/components/checkout/steps/payment/stripe/index.d.ts +3 -0
  44. package/dist/components/checkout/steps/payment/stripe/stripe.utils.d.ts +33 -0
  45. package/dist/components/checkout/steps/payment/stripe/useStripeIntegration.d.ts +5 -0
  46. package/dist/components/checkout/steps/payment/stripe/useSubmit.d.ts +13 -0
  47. package/dist/components/checkout/steps/plan/BillingPeriodPicker.d.ts +9 -0
  48. package/dist/components/checkout/steps/plan/BillingPeriodPicker.style.d.ts +52 -0
  49. package/dist/components/checkout/steps/plan/CheckoutChargeList.d.ts +16 -0
  50. package/dist/components/checkout/steps/plan/CheckoutPlanStep.d.ts +2 -0
  51. package/dist/components/checkout/steps/plan/CheckoutPlanStep.style.d.ts +12 -0
  52. package/dist/components/checkout/steps/plan/index.d.ts +1 -0
  53. package/dist/components/checkout/steps/surprise/SurpriseStep.d.ts +2 -0
  54. package/dist/components/checkout/summary/CheckoutSuccess.d.ts +3 -0
  55. package/dist/components/checkout/summary/CheckoutSummary.d.ts +16 -0
  56. package/dist/components/checkout/summary/CheckoutSummarySkeleton.d.ts +2 -0
  57. package/dist/components/checkout/summary/components/CheckoutCaptions.d.ts +11 -0
  58. package/dist/components/checkout/summary/components/LineItems.d.ts +36 -0
  59. package/dist/components/checkout/summary/components/WithSkeleton.d.ts +6 -0
  60. package/dist/components/checkout/summary/index.d.ts +2 -0
  61. package/dist/components/checkout/textOverrides.d.ts +32 -0
  62. package/dist/components/checkout/theme.d.ts +12 -0
  63. package/dist/components/common/Icon.d.ts +3 -2
  64. package/dist/components/common/PoweredByStigg.d.ts +1 -1
  65. package/dist/components/{paywall/TiersLayout.d.ts → common/TiersSelectContainer.d.ts} +2 -3
  66. package/dist/components/common/customIcons.d.ts +19 -5
  67. package/dist/components/common/mapExternalTheme.d.ts +2 -1
  68. package/dist/components/customerPortal/subscriptionOverview/tabs/SubscriptionTabs.d.ts +1 -1
  69. package/dist/components/hooks/useChargeSort.d.ts +3 -0
  70. package/dist/components/paywall/paywallTextOverrides.d.ts +0 -2
  71. package/dist/components/utils/calculateDiscountRate.d.ts +1 -0
  72. package/dist/components/utils/currencyUtils.d.ts +1 -1
  73. package/dist/components/{paywall/planPriceTier.d.ts → utils/priceTierUtils.d.ts} +3 -1
  74. package/dist/components/utils/priceUtils.d.ts +2 -0
  75. package/dist/index.d.ts +1 -0
  76. package/dist/react-sdk.cjs.development.js +6930 -228
  77. package/dist/react-sdk.cjs.development.js.map +1 -1
  78. package/dist/react-sdk.cjs.production.min.js +1 -1
  79. package/dist/react-sdk.cjs.production.min.js.map +1 -1
  80. package/dist/react-sdk.esm.js +7089 -232
  81. package/dist/react-sdk.esm.js.map +1 -1
  82. package/dist/stories/Checkout.stories.d.ts +3 -0
  83. package/dist/stories/CustomerPortal.stories.d.ts +1 -1
  84. package/dist/theme/getResolvedTheme.d.ts +4 -0
  85. package/dist/theme/types.d.ts +4 -0
  86. package/package.json +11 -5
  87. package/src/assets/arrow-forward.svg +3 -0
  88. package/src/assets/arrow-right.svg +6 -0
  89. package/src/assets/check.svg +5 -0
  90. package/src/assets/close.svg +3 -0
  91. package/src/assets/lottie/checkout-success.json +1 -0
  92. package/src/assets/nyancat.svg +634 -0
  93. package/src/assets/outlined-checked-circle-disabled.svg +6 -0
  94. package/src/assets/outlined-checked-circle.svg +6 -0
  95. package/src/assets/outlined-circle.svg +3 -0
  96. package/src/assets/payment-method.svg +11 -0
  97. package/src/assets/plus-icon.svg +6 -0
  98. package/src/assets/trash.svg +8 -0
  99. package/src/components/checkout/Checkout.tsx +30 -0
  100. package/src/components/checkout/CheckoutContainer.style.ts +35 -0
  101. package/src/components/checkout/CheckoutContainer.tsx +99 -0
  102. package/src/components/checkout/CheckoutProvider.tsx +149 -0
  103. package/src/components/checkout/components/Button.tsx +51 -0
  104. package/src/components/checkout/components/ContentLoadingSkeleton.tsx +41 -0
  105. package/src/components/checkout/components/InputField.tsx +22 -0
  106. package/src/components/checkout/components/Skeletons.style.ts +27 -0
  107. package/src/components/checkout/components/index.ts +3 -0
  108. package/src/components/checkout/formatting.ts +12 -0
  109. package/src/components/checkout/hooks/index.ts +8 -0
  110. package/src/components/checkout/hooks/useAddonsStepModel.ts +96 -0
  111. package/src/components/checkout/hooks/useCheckoutModel.ts +32 -0
  112. package/src/components/checkout/hooks/useCouponModel.ts +28 -0
  113. package/src/components/checkout/hooks/useLoadCheckout.ts +39 -0
  114. package/src/components/checkout/hooks/usePaymentStepModel.ts +49 -0
  115. package/src/components/checkout/hooks/usePlanStepModel.ts +169 -0
  116. package/src/components/checkout/hooks/usePreviewSubscription.ts +82 -0
  117. package/src/components/checkout/hooks/useProgressBarModel.ts +89 -0
  118. package/src/components/checkout/hooks/useSubscriptionModel.ts +16 -0
  119. package/src/components/checkout/hooks/useSubscriptionState.ts +26 -0
  120. package/src/components/checkout/index.ts +3 -0
  121. package/src/components/checkout/planHeader/PlanHeader.style.tsx +23 -0
  122. package/src/components/checkout/planHeader/PlanHeader.tsx +59 -0
  123. package/src/components/checkout/planHeader/index.ts +1 -0
  124. package/src/components/checkout/progressBar/CheckoutProgressBar.style.ts +34 -0
  125. package/src/components/checkout/progressBar/CheckoutProgressBar.tsx +53 -0
  126. package/src/components/checkout/promotionCode/AddPromotionCode.tsx +85 -0
  127. package/src/components/checkout/promotionCode/AddPromotionCodeButton.tsx +39 -0
  128. package/src/components/checkout/promotionCode/AppliedPromotionCode.tsx +37 -0
  129. package/src/components/checkout/promotionCode/PromotionCodeSection.tsx +27 -0
  130. package/src/components/checkout/promotionCode/index.ts +1 -0
  131. package/src/components/checkout/steps/addons/CheckoutAddonsStep.style.tsx +24 -0
  132. package/src/components/checkout/steps/addons/CheckoutAddonsStep.tsx +125 -0
  133. package/src/components/checkout/steps/addons/addon.utils.ts +68 -0
  134. package/src/components/checkout/steps/addons/index.ts +1 -0
  135. package/src/components/checkout/steps/payment/PaymentMethods.style.ts +26 -0
  136. package/src/components/checkout/steps/payment/PaymentMethods.tsx +86 -0
  137. package/src/components/checkout/steps/payment/PaymentStep.tsx +50 -0
  138. package/src/components/checkout/steps/payment/index.ts +1 -0
  139. package/src/components/checkout/steps/payment/stripe/StripePaymentForm.tsx +45 -0
  140. package/src/components/checkout/steps/payment/stripe/index.ts +3 -0
  141. package/src/components/checkout/steps/payment/stripe/stripe.utils.ts +109 -0
  142. package/src/components/checkout/steps/payment/stripe/useStripeIntegration.ts +27 -0
  143. package/src/components/checkout/steps/payment/stripe/useSubmit.ts +104 -0
  144. package/src/components/checkout/steps/plan/BillingPeriodPicker.style.tsx +46 -0
  145. package/src/components/checkout/steps/plan/BillingPeriodPicker.tsx +63 -0
  146. package/src/components/checkout/steps/plan/CheckoutChargeList.tsx +138 -0
  147. package/src/components/checkout/steps/plan/CheckoutPlanStep.style.tsx +6 -0
  148. package/src/components/checkout/steps/plan/CheckoutPlanStep.tsx +20 -0
  149. package/src/components/checkout/steps/plan/index.ts +1 -0
  150. package/src/components/checkout/steps/surprise/SurpriseStep.tsx +27 -0
  151. package/src/components/checkout/summary/CheckoutSuccess.tsx +32 -0
  152. package/src/components/checkout/summary/CheckoutSummary.tsx +288 -0
  153. package/src/components/checkout/summary/CheckoutSummarySkeleton.tsx +40 -0
  154. package/src/components/checkout/summary/components/CheckoutCaptions.tsx +120 -0
  155. package/src/components/checkout/summary/components/LineItems.tsx +177 -0
  156. package/src/components/checkout/summary/components/WithSkeleton.tsx +16 -0
  157. package/src/components/checkout/summary/index.ts +2 -0
  158. package/src/components/checkout/textOverrides.ts +62 -0
  159. package/src/components/checkout/theme.ts +43 -0
  160. package/src/components/common/Icon.tsx +17 -22
  161. package/src/components/common/PoweredByStigg.tsx +1 -1
  162. package/src/components/{paywall/TiersLayout.tsx → common/TiersSelectContainer.tsx} +8 -7
  163. package/src/components/common/Typography.tsx +11 -1
  164. package/src/components/common/customIcons.ts +19 -28
  165. package/src/components/common/mapExternalTheme.ts +29 -9
  166. package/src/components/customerPortal/subscriptionOverview/tabs/SubscriptionTabs.tsx +6 -12
  167. package/src/components/hooks/useChargeSort.ts +17 -0
  168. package/src/components/paywall/Paywall.tsx +1 -1
  169. package/src/components/paywall/PlanOffering.tsx +1 -1
  170. package/src/components/paywall/PlanOfferingButton.tsx +1 -1
  171. package/src/components/paywall/PlanPrice.tsx +5 -13
  172. package/src/components/paywall/paywallTextOverrides.ts +0 -1
  173. package/src/components/paywall/utils/calculateUnitQuantityText.ts +9 -4
  174. package/src/components/utils/calculateDiscountRate.ts +1 -1
  175. package/src/components/utils/currencyUtils.ts +1 -1
  176. package/src/components/utils/getPaidPriceText.ts +2 -2
  177. package/src/components/{paywall/planPriceTier.ts → utils/priceTierUtils.ts} +25 -3
  178. package/src/components/utils/priceUtils.ts +10 -0
  179. package/src/index.ts +1 -0
  180. package/src/stories/Checkout.stories.tsx +61 -0
  181. package/src/stories/CustomerPortal.stories.tsx +1 -1
  182. package/src/theme/Theme.tsx +6 -6
  183. package/src/theme/getResolvedTheme.ts +7 -1
  184. package/src/theme/types.ts +4 -0
@@ -0,0 +1,6 @@
1
+ <svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g id="Stepper indicators">
3
+ <circle id="Ellipse 5" cx="10.667" cy="10" r="9.25" fill="#B3BCD3" stroke="#B3BCD3" stroke-width="1.5"/>
4
+ <path id="Shape" d="M5.66699 10.3636L9.30366 14L13.304 10L15.3042 8" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
5
+ </g>
6
+ </svg>
@@ -0,0 +1,6 @@
1
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g id="Group 16159">
3
+ <circle id="Ellipse 5" cx="10" cy="10" r="9.25" fill="#327EEE" stroke="#327EEE" stroke-width="1.5"/>
4
+ <path id="Shape" d="M5 10.3636L8.63667 14L12.637 10L14.6372 8" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
5
+ </g>
6
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <circle cx="10.333" cy="10" r="9" fill="white" stroke="#327EEE" stroke-width="2"/>
3
+ </svg>
@@ -0,0 +1,11 @@
1
+ <svg width="24" height="16" viewBox="0 0 24 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g clip-path="url(#clip0_239_13697)">
3
+ <path d="M22 0H2C0.89543 0 0 0.89543 0 2V14C0 15.1046 0.89543 16 2 16H22C23.1046 16 24 15.1046 24 14V2C24 0.89543 23.1046 0 22 0Z" fill="#C6C6C7"/>
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>
11
+ </svg>
@@ -0,0 +1,6 @@
1
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g id="plus">
3
+ <path id="Shape" d="M10 4.16666V15.8333" stroke="#327EEE" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
4
+ <path id="Shape_2" d="M4.16699 10H15.8337" stroke="#327EEE" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
5
+ </g>
6
+ </svg>
@@ -0,0 +1,8 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g id="trash-2">
3
+ <path id="Shape" d="M3 6H5H21" stroke="#F88078" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
4
+ <path id="Shape_2" d="M20 6C20 5.44772 19.5523 5 19 5C18.4477 5 18 5.44772 18 6H20ZM6 6C6 5.44772 5.55228 5 5 5C4.44772 5 4 5.44772 4 6H6ZM7 6C7 6.55228 7.44772 7 8 7C8.55228 7 9 6.55228 9 6H7ZM15 6C15 6.55228 15.4477 7 16 7C16.5523 7 17 6.55228 17 6H15ZM18 6V20H20V6H18ZM18 20C18 20.5523 17.5523 21 17 21V23C18.6569 23 20 21.6569 20 20H18ZM17 21H7V23H17V21ZM7 21C6.44772 21 6 20.5523 6 20H4C4 21.6569 5.34315 23 7 23V21ZM6 20V6H4V20H6ZM9 6V4H7V6H9ZM9 4C9 3.44772 9.44772 3 10 3V1C8.34315 1 7 2.34315 7 4H9ZM10 3H14V1H10V3ZM14 3C14.5523 3 15 3.44772 15 4H17C17 2.34315 15.6569 1 14 1V3ZM15 4V6H17V4H15Z" fill="#F88078"/>
5
+ <path id="Shape_3" d="M10 11V17" stroke="#F88078" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
6
+ <path id="Shape_4" d="M14 11V17" stroke="#F88078" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
7
+ </g>
8
+ </svg>
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import { CheckoutProvider, CheckoutProviderProps } from './CheckoutProvider';
3
+ import { CheckoutContainer, CheckoutContainerProps } from './CheckoutContainer';
4
+
5
+ export type CheckoutProps = CheckoutProviderProps & CheckoutContainerProps;
6
+
7
+ export const Checkout = ({
8
+ textOverrides,
9
+ theme,
10
+ resourceId,
11
+ planId,
12
+ preferredBillingPeriod,
13
+ billingCountryCode,
14
+ billableFeatures,
15
+ ...containerProps
16
+ }: CheckoutProps) => {
17
+ return (
18
+ <CheckoutProvider
19
+ textOverrides={textOverrides}
20
+ theme={theme}
21
+ resourceId={resourceId}
22
+ planId={planId}
23
+ preferredBillingPeriod={preferredBillingPeriod}
24
+ billingCountryCode={billingCountryCode}
25
+ billableFeatures={billableFeatures}
26
+ >
27
+ <CheckoutContainer {...containerProps} />
28
+ </CheckoutProvider>
29
+ );
30
+ };
@@ -0,0 +1,35 @@
1
+ import styled from '@emotion/styled/macro';
2
+ import Box from '@mui/material/Box';
3
+
4
+ export const CheckoutLayout = styled.div`
5
+ margin: auto;
6
+ width: 100%;
7
+ max-width: 920px;
8
+ display: flex;
9
+ position: relative;
10
+ flex-direction: column;
11
+ align-items: center;
12
+
13
+ padding: 16px 32px;
14
+ border-radius: 10px;
15
+ background-color: ${({ theme }) => theme.stigg.palette.backgroundPaper};
16
+ border: ${({ theme }) => `1px solid ${theme.stigg.palette.outlinedBorder}`};
17
+
18
+ & * {
19
+ box-sizing: border-box;
20
+ }
21
+ `;
22
+ export const CheckoutContent = styled(Box)`
23
+ display: flex;
24
+ align-items: flex-start;
25
+ gap: 32px;
26
+ flex-wrap: wrap;
27
+ width: 100%;
28
+ `;
29
+
30
+ export const CheckoutPanel = styled(Box)`
31
+ display: flex;
32
+ flex-direction: column;
33
+ gap: 0;
34
+ flex: 2;
35
+ `;
@@ -0,0 +1,99 @@
1
+ import React from 'react';
2
+ import { Elements } from '@stripe/react-stripe-js';
3
+ import { ApplySubscription, CheckoutStatePlan } from '@stigg/js-client-sdk';
4
+ import { CheckoutContent, CheckoutLayout, CheckoutPanel } from './CheckoutContainer.style';
5
+ import { CheckoutProgressBar } from './progressBar/CheckoutProgressBar';
6
+ import { CheckoutSummary, CheckoutSummarySkeleton } from './summary';
7
+ import { useProgressBarModel } from './hooks';
8
+ import { PlanHeader } from './planHeader';
9
+ import { CheckoutAddonsStep } from './steps/addons';
10
+ import { PaymentStep } from './steps/payment';
11
+ import { useStripeIntegration } from './steps/payment/stripe';
12
+ import { CheckoutPlanStep } from './steps/plan';
13
+ import { useCheckoutContext } from './CheckoutProvider';
14
+ import { ContentLoadingSkeleton } from './components';
15
+
16
+ // import { SurpriseStep } from './steps/surprise/SurpriseStep';
17
+
18
+ type StepProps = {
19
+ allowChangePlan?: boolean;
20
+ content: React.ReactNode;
21
+ };
22
+
23
+ const getStepProps = (step: number): StepProps => {
24
+ switch (step) {
25
+ case 0:
26
+ return { allowChangePlan: true, content: <CheckoutPlanStep /> };
27
+ case 1:
28
+ return { content: <CheckoutAddonsStep /> };
29
+ case 2:
30
+ return { content: <PaymentStep /> };
31
+ default:
32
+ return { content: null };
33
+ }
34
+ };
35
+
36
+ export type CheckoutResult = { success: boolean; errorMessage?: string };
37
+
38
+ export type OnCheckoutParams = { checkoutParams: ApplySubscription; checkoutAction: () => Promise<CheckoutResult> };
39
+
40
+ export type OnCheckoutCompletedParams = { success: boolean; error?: string };
41
+
42
+ export type CheckoutContainerProps = {
43
+ onCheckout?: (params: OnCheckoutParams) => Promise<CheckoutResult>;
44
+ onCheckoutCompleted: (params: OnCheckoutCompletedParams) => Promise<void>;
45
+ onChangePlan?: (params: { currentPlan: CheckoutStatePlan | undefined }) => void;
46
+ };
47
+
48
+ export function CheckoutContainer({ onCheckout, onCheckoutCompleted, onChangePlan }: CheckoutContainerProps) {
49
+ const { stripePromise, setupIntentClientSecret } = useStripeIntegration();
50
+ const [{ stiggTheme, widgetState }] = useCheckoutContext();
51
+ const { progressBarState } = useProgressBarModel();
52
+ const { activeStep } = progressBarState;
53
+ const { isLoadingCheckoutData } = widgetState;
54
+
55
+ // uncomment for fun!
56
+ // if (activeStep > 2) {
57
+ // return <SurpriseStep />;
58
+ // }
59
+
60
+ const { content, allowChangePlan } = getStepProps(activeStep);
61
+
62
+ const checkoutContent = (
63
+ <>
64
+ <PlanHeader allowChangePlan={allowChangePlan} onChangePlan={onChangePlan} />
65
+ {content}
66
+ </>
67
+ );
68
+
69
+ return (
70
+ <Elements
71
+ stripe={stripePromise}
72
+ options={{
73
+ clientSecret: setupIntentClientSecret,
74
+ appearance: {
75
+ theme: 'stripe',
76
+ variables: {
77
+ colorText: stiggTheme.palette.text.primary,
78
+ colorPrimaryText: stiggTheme.palette.text.primary,
79
+ colorTextPlaceholder: stiggTheme.palette.text.disabled,
80
+ fontFamily: stiggTheme.typography.fontFamily,
81
+ },
82
+ },
83
+ }}>
84
+ <CheckoutLayout className="stigg-checkout-layout">
85
+ <CheckoutProgressBar />
86
+ <CheckoutContent>
87
+ <CheckoutPanel>
88
+ {isLoadingCheckoutData && <ContentLoadingSkeleton />}
89
+ {!isLoadingCheckoutData && checkoutContent}
90
+ </CheckoutPanel>
91
+ {isLoadingCheckoutData && <CheckoutSummarySkeleton />}
92
+ {!isLoadingCheckoutData && (
93
+ <CheckoutSummary onCheckout={onCheckout} onCheckoutCompleted={onCheckoutCompleted} />
94
+ )}
95
+ </CheckoutContent>
96
+ </CheckoutLayout>
97
+ </Elements>
98
+ );
99
+ }
@@ -0,0 +1,149 @@
1
+ import { produce } from 'immer';
2
+ import React, { useCallback, useContext, useMemo, useState } from 'react';
3
+
4
+ import { BillableFeature, BillingPeriod, GetCheckoutStateResults } from '@stigg/js-client-sdk';
5
+
6
+ import { CustomizedTheme, SdkThemeProvider, useStiggTheme } from '../../theme/Theme';
7
+ import { DeepPartial } from '../../types';
8
+ import { mapCheckoutConfiguration } from '../common/mapExternalTheme';
9
+ import {
10
+ AddonsStepState,
11
+ getAddonsStepInitialState,
12
+ getPaymentStepInitialState,
13
+ getPlanStepInitialState,
14
+ getProgressBarInitialState,
15
+ PaymentStepState,
16
+ PlanStepState,
17
+ ProgressBarState,
18
+ useLoadCheckout,
19
+ WidgetState,
20
+ } from './hooks';
21
+ import { CheckoutLocalization, getResolvedCheckoutLocalize } from './textOverrides';
22
+ import { CheckoutTheme, getResolvedCheckoutTheme } from './theme';
23
+ import { StiggTheme } from '../../theme/types';
24
+
25
+ export interface CheckoutContextState {
26
+ checkout?: GetCheckoutStateResults | null;
27
+ checkoutLocalization: CheckoutLocalization;
28
+ stiggTheme: StiggTheme;
29
+ theme: CheckoutTheme;
30
+ resourceId?: string;
31
+ promotionCode?: string;
32
+ progressBar: ProgressBarState;
33
+ planStep: PlanStepState;
34
+ addonsStep: AddonsStepState;
35
+ paymentStep: PaymentStepState;
36
+ widgetState: WidgetState;
37
+ }
38
+
39
+ export const CheckoutContext = React.createContext<
40
+ [CheckoutContextState, (updater: (state: CheckoutContextState) => void) => void] | null
41
+ >(null);
42
+
43
+ CheckoutContext.displayName = 'CheckoutContext';
44
+
45
+ export const useCheckoutContext = () => {
46
+ const ctx = useContext(CheckoutContext);
47
+ if (!ctx) {
48
+ throw new Error(
49
+ 'Could not find Checkout context; You need to wrap your checkout components in an <CheckoutProvider> component.',
50
+ );
51
+ }
52
+ return ctx;
53
+ };
54
+
55
+ const CheckoutContextProvider: React.FC<{ children: React.ReactNode; initialState: CheckoutContextState }> = ({
56
+ children,
57
+ initialState,
58
+ }) => {
59
+ const [state, innerSetState] = useState(initialState);
60
+
61
+ const setState = useCallback(
62
+ (updater: (state: CheckoutContextState) => void) => innerSetState((old) => produce(old, (draft) => updater(draft))),
63
+ [innerSetState],
64
+ );
65
+
66
+ const [contextValue, setContextValue] = useMemo(() => [state, setState], [state]);
67
+
68
+ return <CheckoutContext.Provider value={[contextValue, setContextValue]}>{children}</CheckoutContext.Provider>;
69
+ };
70
+
71
+ export type CheckoutProviderProps = {
72
+ textOverrides?: DeepPartial<CheckoutLocalization>;
73
+ theme?: DeepPartial<CheckoutTheme>;
74
+ resourceId?: string;
75
+ planId: string;
76
+ preferredBillingPeriod?: BillingPeriod;
77
+ billingCountryCode?: string;
78
+ billableFeatures?: BillableFeature[];
79
+ };
80
+
81
+ export function CheckoutProvider({
82
+ children,
83
+ textOverrides,
84
+ theme,
85
+ resourceId,
86
+ planId,
87
+ preferredBillingPeriod,
88
+ billingCountryCode,
89
+ billableFeatures,
90
+ }: {
91
+ children: React.ReactNode;
92
+ } & CheckoutProviderProps) {
93
+ const { checkout, isLoading } = useLoadCheckout({ resourceId, planId, billingCountryCode });
94
+ const configuration: CustomizedTheme | undefined = checkout?.configuration
95
+ ? mapCheckoutConfiguration(checkout.configuration)
96
+ : undefined;
97
+ const globalTheme: StiggTheme = useStiggTheme(configuration);
98
+
99
+ const initialState = useMemo(() => {
100
+ const checkoutTheme = getResolvedCheckoutTheme(globalTheme, theme, checkout?.configuration);
101
+ const checkoutLocalization = getResolvedCheckoutLocalize(textOverrides);
102
+ const planStep = getPlanStepInitialState({
103
+ preferredBillingPeriod,
104
+ plan: checkout?.plan,
105
+ activeSubscription: checkout?.activeSubscription,
106
+ billingCountryCode,
107
+ preconfiguredBillableFeatures: billableFeatures ?? [],
108
+ });
109
+ const addonsStep = getAddonsStepInitialState({
110
+ plan: checkout?.plan,
111
+ billingPeriod: planStep.billingPeriod,
112
+ activeSubscription: checkout?.activeSubscription,
113
+ });
114
+ const paymentStep = getPaymentStepInitialState({ customer: checkout?.customer });
115
+ const progressBar = getProgressBarInitialState({
116
+ availableAddons: isLoading ? undefined : addonsStep.availableAddons,
117
+ });
118
+
119
+ const initialState: CheckoutContextState = {
120
+ checkout,
121
+ checkoutLocalization,
122
+ stiggTheme: globalTheme,
123
+ theme: checkoutTheme,
124
+ progressBar,
125
+ planStep,
126
+ addonsStep,
127
+ paymentStep,
128
+ resourceId: checkout?.resource?.id,
129
+ widgetState: { readOnly: false, isLoadingCheckoutData: isLoading },
130
+ };
131
+
132
+ return initialState;
133
+ }, [
134
+ theme,
135
+ textOverrides,
136
+ preferredBillingPeriod,
137
+ billingCountryCode,
138
+ billableFeatures,
139
+ globalTheme,
140
+ checkout,
141
+ isLoading,
142
+ ]);
143
+
144
+ return (
145
+ <SdkThemeProvider key={checkout?.plan.id} componentTheme={configuration}>
146
+ <CheckoutContextProvider initialState={initialState}>{children}</CheckoutContextProvider>
147
+ </SdkThemeProvider>
148
+ );
149
+ }
@@ -0,0 +1,51 @@
1
+ import React from 'react';
2
+
3
+ import styled from '@emotion/styled/macro';
4
+ import { Button as MuiButton, ButtonProps, css } from '@mui/material';
5
+
6
+ export type StyledButtonProps = { $isLoading?: boolean; $success?: boolean; $error?: boolean };
7
+
8
+ const StyledButton = styled(MuiButton)<StyledButtonProps>`
9
+ border-radius: 10px;
10
+ text-transform: none;
11
+
12
+ ${({ theme, $isLoading, $success, $error }) => {
13
+ if ($isLoading) {
14
+ return css`
15
+ background-color: ${theme.stigg.palette.primaryDark};
16
+ cursor: no-drop;
17
+
18
+ &:hover {
19
+ background-color: ${theme.stigg.palette.primaryDark};
20
+ }
21
+ `;
22
+ }
23
+
24
+ if ($success) {
25
+ return css`
26
+ background-color: ${theme.stigg.palette.success};
27
+ cursor: 'default';
28
+
29
+ &:hover {
30
+ background-color: ${theme.stigg.palette.successDark};
31
+ }
32
+ `;
33
+ }
34
+
35
+ if ($error) {
36
+ return css`
37
+ background-color: ${theme.stigg.palette.error};
38
+
39
+ &:hover {
40
+ background-color: ${theme.stigg.palette.errorDark};
41
+ }
42
+ `;
43
+ }
44
+
45
+ return '';
46
+ }}
47
+ `;
48
+
49
+ export const Button = ({ variant = 'outlined', ...props }: ButtonProps & StyledButtonProps) => {
50
+ return <StyledButton variant={variant} {...props} />;
51
+ };
@@ -0,0 +1,41 @@
1
+ import React from 'react';
2
+ import { Grid, Divider } from '@mui/material';
3
+ import styled from '@emotion/styled/macro';
4
+ import { FlexedSkeleton, FlexedSkeletonContainer, Skeleton, SkeletonsContainer } from './Skeletons.style';
5
+
6
+ const ContentContainer = styled(Grid)`
7
+ display: flex;
8
+ flex-direction: column;
9
+ `;
10
+
11
+ export function ContentLoadingSkeleton() {
12
+ return (
13
+ <ContentContainer container>
14
+ <SkeletonsContainer item $gap={48}>
15
+ <Skeleton width={120} height={20} />
16
+ <Skeleton width={120} height={20} />
17
+ </SkeletonsContainer>
18
+
19
+ <Divider className="stigg-checkout-plan-header-divider" sx={{ marginY: 2 }} />
20
+
21
+ <ContentContainer item>
22
+ <Skeleton width={120} height={20} />
23
+
24
+ <FlexedSkeletonContainer $gap={16} marginTop={3}>
25
+ <Skeleton height={20} />
26
+ <Skeleton height={20} />
27
+ </FlexedSkeletonContainer>
28
+
29
+ <SkeletonsContainer $gap={130} marginTop={3}>
30
+ <FlexedSkeleton flex={1}>
31
+ <Skeleton height={20} />
32
+ </FlexedSkeleton>
33
+
34
+ <FlexedSkeleton flex={3}>
35
+ <Skeleton height={20} />
36
+ </FlexedSkeleton>
37
+ </SkeletonsContainer>
38
+ </ContentContainer>
39
+ </ContentContainer>
40
+ );
41
+ }
@@ -0,0 +1,22 @@
1
+ import styled from '@emotion/styled';
2
+ import { OutlinedInputProps, TextField } from '@mui/material';
3
+
4
+ export const InputField = styled(TextField)<OutlinedInputProps>(({ theme }) => ({
5
+ '& .MuiOutlinedInput-root': {
6
+ height: '42px',
7
+ borderRadius: theme.stigg.border.radius,
8
+ fieldset: {
9
+ borderColor: theme.stigg.palette.outlinedBorder,
10
+ },
11
+ '&:not(.Mui-focused):hover fieldset': {
12
+ borderColor: theme.stigg.palette.outlinedRestingBorder,
13
+ },
14
+ },
15
+ '& .MuiInputBase-input': {
16
+ padding: '8px 12px',
17
+ borderRadius: theme.stigg.border.radius,
18
+ fontFamily: theme.stigg.typography.fontFamily,
19
+ color: theme.stigg.palette.text.primary,
20
+ ...theme.stigg.typography.body,
21
+ },
22
+ }));
@@ -0,0 +1,27 @@
1
+ import styled from '@emotion/styled/macro';
2
+ import { Grid } from '@mui/material';
3
+ import ReactSkeleton from 'react-loading-skeleton';
4
+
5
+ export const SkeletonsContainer = styled(Grid)<{ $gap: number; $flexDirection?: 'row' | 'column' }>`
6
+ display: flex;
7
+ flex-direction: ${({ $flexDirection }) => $flexDirection || 'row'};
8
+ gap: ${({ $gap }) => $gap}px;
9
+ `;
10
+
11
+ export const FlexedSkeletonContainer = styled(SkeletonsContainer)`
12
+ span {
13
+ display: flex;
14
+ flex: 1;
15
+ }
16
+ `;
17
+
18
+ export const FlexedSkeleton = styled(Grid)<{ flex: number }>`
19
+ span {
20
+ display: flex;
21
+ flex: ${({ flex }) => flex};
22
+ }
23
+ `;
24
+
25
+ export const Skeleton = styled(ReactSkeleton)`
26
+ border-radius: 4px;
27
+ `;
@@ -0,0 +1,3 @@
1
+ export * from './Button';
2
+ export * from './InputField';
3
+ export * from './ContentLoadingSkeleton';
@@ -0,0 +1,12 @@
1
+ import { BillingPeriod } from '@stigg/js-client-sdk';
2
+
3
+ export function formatBillingPeriod(billingPeriod: BillingPeriod) {
4
+ switch (billingPeriod) {
5
+ case BillingPeriod.Annually:
6
+ return 'Annual';
7
+ case BillingPeriod.Monthly:
8
+ return 'Monthly';
9
+ default:
10
+ return '';
11
+ }
12
+ }
@@ -0,0 +1,8 @@
1
+ export * from './useAddonsStepModel';
2
+ export * from './useCheckoutModel';
3
+ export * from './useLoadCheckout';
4
+ export * from './usePlanStepModel';
5
+ export * from './useProgressBarModel';
6
+ export * from './useSubscriptionModel';
7
+ export * from './usePreviewSubscription';
8
+ export * from './usePaymentStepModel';
@@ -0,0 +1,96 @@
1
+ import cloneDeep from 'lodash/cloneDeep';
2
+ import remove from 'lodash/remove';
3
+ import { Addon, BillingPeriod, Currency, Plan, Subscription, SubscriptionAddon } from '@stigg/js-client-sdk';
4
+ import { useCheckoutContext } from '../CheckoutProvider';
5
+ import {
6
+ filterAddons,
7
+ filterSubscriptionAddons,
8
+ sortAddons,
9
+ sortSubscriptionAddons,
10
+ } from '../steps/addons/addon.utils';
11
+
12
+ export type AddonsStepState = {
13
+ addons: SubscriptionAddon[];
14
+ initialAddons?: SubscriptionAddon[];
15
+ availableAddons?: Addon[];
16
+ };
17
+
18
+ type getAddonsStepInitialStateProps = {
19
+ plan?: Plan;
20
+ billingPeriod: BillingPeriod;
21
+ activeSubscription?: Subscription | null;
22
+ billingCountryCode?: string;
23
+ };
24
+
25
+ export function getAddonsStepInitialState({
26
+ activeSubscription,
27
+ plan,
28
+ billingPeriod,
29
+ billingCountryCode,
30
+ }: getAddonsStepInitialStateProps): AddonsStepState {
31
+ let addons: SubscriptionAddon[] = [];
32
+ const currency = plan?.pricePoints?.[0]?.currency || Currency.Usd;
33
+ const availableAddons = sortAddons(
34
+ filterAddons({
35
+ addons: plan?.compatibleAddons,
36
+ billingPeriod,
37
+ billingCountryCode,
38
+ currency,
39
+ }),
40
+ );
41
+
42
+ const activeSubscriptionAddons = filterSubscriptionAddons({
43
+ addons: activeSubscription?.addons,
44
+ billingPeriod,
45
+ billingCountryCode,
46
+ currency,
47
+ });
48
+
49
+ if (activeSubscriptionAddons?.length && availableAddons?.length) {
50
+ addons = sortSubscriptionAddons(
51
+ activeSubscriptionAddons.filter((addon) => availableAddons.some((planAddon) => planAddon.id === addon.addon.id)),
52
+ );
53
+ }
54
+
55
+ return { addons, initialAddons: cloneDeep(addons), availableAddons };
56
+ }
57
+
58
+ function useSetAddon() {
59
+ const [, setState] = useCheckoutContext();
60
+
61
+ return (addon: Addon, quantity: number) =>
62
+ setState((draft) => {
63
+ const addonToUpdate = draft.addonsStep.addons.find((currentAddon) => currentAddon.addon.id === addon.id);
64
+
65
+ if (addonToUpdate) {
66
+ addonToUpdate.quantity = quantity;
67
+ } else {
68
+ draft.addonsStep.addons.push({ addon, quantity });
69
+ draft.addonsStep.addons = sortSubscriptionAddons(draft.addonsStep.addons);
70
+ }
71
+ });
72
+ }
73
+
74
+ function useRemoveAddon() {
75
+ const [, setState] = useCheckoutContext();
76
+
77
+ return (addonId: string) =>
78
+ setState((draft) => {
79
+ remove(draft.addonsStep.addons, (addon) => addon.addon.id === addonId);
80
+ });
81
+ }
82
+
83
+ function useAddonsState() {
84
+ const [{ addonsStep }] = useCheckoutContext();
85
+ return addonsStep;
86
+ }
87
+
88
+ export function useAddonsStepModel() {
89
+ const state = useAddonsState();
90
+
91
+ return {
92
+ ...state,
93
+ setAddon: useSetAddon(),
94
+ removeAddon: useRemoveAddon(),
95
+ };
96
+ }
@@ -0,0 +1,32 @@
1
+ import { useCheckoutContext } from '../CheckoutProvider';
2
+
3
+ export type WidgetState = {
4
+ readOnly?: boolean;
5
+ isLoadingCheckoutData?: boolean;
6
+ };
7
+
8
+ function useCheckoutState() {
9
+ const [{ checkout, widgetState, checkoutLocalization }] = useCheckoutContext();
10
+ return { checkoutState: checkout, widgetState, checkoutLocalization };
11
+ }
12
+
13
+ function useSetWidgetReadonly() {
14
+ const [, setState] = useCheckoutContext();
15
+
16
+ return (readOnly: boolean) =>
17
+ setState((draft) => {
18
+ draft.widgetState.readOnly = readOnly;
19
+ });
20
+ }
21
+
22
+ export function useCheckoutModel() {
23
+ const { checkoutState, widgetState, checkoutLocalization } = useCheckoutState();
24
+ const setWidgetReadOnly = useSetWidgetReadonly();
25
+
26
+ return {
27
+ checkoutState,
28
+ widgetState,
29
+ checkoutLocalization,
30
+ setWidgetReadOnly,
31
+ };
32
+ }