@blocklet/payment-react 1.13.159 → 1.13.161

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 (57) hide show
  1. package/es/components/input.d.ts +3 -1
  2. package/es/components/input.js +12 -3
  3. package/es/history/invoice/list.js +1 -1
  4. package/es/history/payment/list.js +1 -1
  5. package/es/index.d.ts +5 -1
  6. package/es/index.js +8 -0
  7. package/es/locales/en.js +13 -3
  8. package/es/locales/zh.js +13 -3
  9. package/es/payment/form/address.js +2 -1
  10. package/es/payment/form/currency.d.ts +9 -0
  11. package/es/payment/form/currency.js +81 -0
  12. package/es/payment/form/index.js +10 -44
  13. package/es/payment/index.js +11 -49
  14. package/es/payment/product-item.d.ts +4 -3
  15. package/es/payment/product-item.js +13 -4
  16. package/es/payment/success.d.ts +8 -2
  17. package/es/payment/success.js +5 -2
  18. package/es/payment/summary.d.ts +21 -8
  19. package/es/payment/summary.js +28 -12
  20. package/es/util.d.ts +6 -3
  21. package/es/util.js +19 -8
  22. package/lib/components/input.d.ts +3 -1
  23. package/lib/components/input.js +4 -2
  24. package/lib/history/invoice/list.js +1 -1
  25. package/lib/history/payment/list.js +1 -1
  26. package/lib/index.d.ts +5 -1
  27. package/lib/index.js +32 -0
  28. package/lib/locales/en.js +13 -3
  29. package/lib/locales/zh.js +13 -3
  30. package/lib/payment/form/address.js +4 -1
  31. package/lib/payment/form/currency.d.ts +9 -0
  32. package/lib/payment/form/currency.js +106 -0
  33. package/lib/payment/form/index.js +8 -60
  34. package/lib/payment/index.js +13 -49
  35. package/lib/payment/product-item.d.ts +4 -3
  36. package/lib/payment/product-item.js +5 -4
  37. package/lib/payment/success.d.ts +8 -2
  38. package/lib/payment/success.js +6 -2
  39. package/lib/payment/summary.d.ts +21 -8
  40. package/lib/payment/summary.js +26 -11
  41. package/lib/util.d.ts +6 -3
  42. package/lib/util.js +22 -9
  43. package/package.json +10 -10
  44. package/src/components/input.tsx +12 -2
  45. package/src/history/invoice/list.tsx +1 -1
  46. package/src/history/payment/list.tsx +1 -1
  47. package/src/index.ts +8 -0
  48. package/src/locales/en.tsx +14 -1
  49. package/src/locales/zh.tsx +12 -1
  50. package/src/payment/form/address.tsx +1 -0
  51. package/src/payment/form/currency.tsx +91 -0
  52. package/src/payment/form/index.tsx +12 -53
  53. package/src/payment/index.tsx +18 -50
  54. package/src/payment/product-item.tsx +16 -11
  55. package/src/payment/success.tsx +9 -2
  56. package/src/payment/summary.tsx +41 -21
  57. package/src/util.ts +24 -10
@@ -0,0 +1,91 @@
1
+ import type { TPaymentCurrency } from '@blocklet/payment-types';
2
+ import { Avatar, Card, Stack, Typography } from '@mui/material';
3
+ import { styled } from '@mui/system';
4
+
5
+ type Props = {
6
+ value: number;
7
+ currencies: TPaymentCurrency[];
8
+ onChange: Function;
9
+ };
10
+
11
+ export default function CurrencySelector({ value, currencies, onChange }: Props) {
12
+ return (
13
+ <Root
14
+ style={{
15
+ display: currencies.length > 1 ? 'grid' : 'block',
16
+ gridTemplateColumns: '50% 50%',
17
+ width: '100%',
18
+ }}>
19
+ {currencies.map((x, i) => {
20
+ const selected = i === value;
21
+ return (
22
+ <Card
23
+ key={x.id}
24
+ variant="outlined"
25
+ onClick={() => onChange(i)}
26
+ className={selected ? 'cko-payment-card' : 'cko-payment-card-unselect'}>
27
+ <Stack direction="row" alignItems="center">
28
+ <Avatar src={x.logo} alt={x.name} sx={{ width: 30, height: 30, marginRight: '10px' }} />
29
+ <div>
30
+ <Typography variant="h5" component="div" sx={{ fontSize: '18px' }}>
31
+ {x.symbol}
32
+ </Typography>
33
+ <Typography sx={{ fontSize: 14 }} color="text.secondary" gutterBottom>
34
+ {(x as any).method.name}
35
+ </Typography>
36
+ </div>
37
+ </Stack>
38
+ </Card>
39
+ );
40
+ })}
41
+ </Root>
42
+ );
43
+ }
44
+
45
+ const Root = styled('section')`
46
+ .cko-payment-card {
47
+ position: relative;
48
+ border: 2px solid ${(props) => props.theme.palette.primary.main};
49
+ padding: 5px 10px;
50
+ margin: 5px 0;
51
+ cursor: pointer;
52
+ }
53
+
54
+ .cko-payment-card::before {
55
+ content: '';
56
+ position: absolute;
57
+ right: 0;
58
+ bottom: 0;
59
+ border: 12px solid ${(props) => props.theme.palette.primary.main};
60
+ border-top-color: transparent;
61
+ border-left-color: transparent;
62
+ }
63
+
64
+ .cko-payment-card-unselect {
65
+ border: 2px solid #ddd;
66
+ padding: 5px 10px;
67
+ margin: 5px 0;
68
+ cursor: pointer;
69
+ }
70
+
71
+ .cko-payment-card:nth-child(odd) {
72
+ margin-right: 8px;
73
+ }
74
+
75
+ .cko-payment-card-unselect:nth-child(odd) {
76
+ margin-right: 8px;
77
+ }
78
+
79
+ .cko-payment-card::after {
80
+ content: '';
81
+ width: 6px;
82
+ height: 10px;
83
+ position: absolute;
84
+ right: 3px;
85
+ bottom: 3px;
86
+ border: 2px solid #fff;
87
+ border-top-color: transparent;
88
+ border-left-color: transparent;
89
+ transform: rotate(35deg);
90
+ }
91
+ `;
@@ -3,9 +3,9 @@ import 'react-international-phone/style.css';
3
3
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
4
4
  import { useTheme } from '@arcblock/ux/lib/Theme';
5
5
  import Toast from '@arcblock/ux/lib/Toast';
6
- import type { PaymentCurrency, TCustomer, TPaymentIntent, TPaymentMethodExpanded } from '@blocklet/payment-types';
6
+ import type { TCustomer, TPaymentIntent, TPaymentMethodExpanded } from '@blocklet/payment-types';
7
7
  import { LoadingButton } from '@mui/lab';
8
- import { Avatar, Card, Fade, InputAdornment, Stack, Typography } from '@mui/material';
8
+ import { Fade, InputAdornment, Stack, Typography } from '@mui/material';
9
9
  import { useCreation, useSetState, useSize } from 'ahooks';
10
10
  import { PhoneNumberUtil } from 'google-libphonenumber';
11
11
  import pWaitFor from 'p-wait-for';
@@ -20,9 +20,10 @@ import ConfirmDialog from '../../components/confirm';
20
20
  import FormInput from '../../components/input';
21
21
  import { usePaymentContext } from '../../contexts/payment';
22
22
  import { CheckoutCallbacks, CheckoutContext } from '../../types';
23
- import { formatError, getPrefix, getStatementDescriptor } from '../../util';
23
+ import { flattenPaymentMethods, formatError, getPrefix, getStatementDescriptor } from '../../util';
24
24
  import UserButtons from './addon';
25
25
  import AddressForm from './address';
26
+ import CurrencySelector from './currency';
26
27
  import PhoneInput from './phone';
27
28
  import StripeCheckout from './stripe';
28
29
 
@@ -58,22 +59,6 @@ const waitForCheckoutComplete = async (sessionId: string) => {
58
59
 
59
60
  type PageData = CheckoutContext & CheckoutCallbacks;
60
61
 
61
- const flatPaymentMethods = (methods: TPaymentMethodExpanded[] = []) => {
62
- const out: PaymentCurrency[] = [];
63
-
64
- methods.forEach((method: any) => {
65
- const currencies = method.paymentCurrencies || method.payment_currencies || [];
66
- currencies.forEach((currency: any) => {
67
- out.push({
68
- ...currency,
69
- method,
70
- });
71
- });
72
- });
73
-
74
- return out;
75
- };
76
-
77
62
  PaymentForm.defaultProps = {};
78
63
 
79
64
  // FIXME: https://stripe.com/docs/elements/address-element
@@ -118,7 +103,7 @@ export default function PaymentForm({
118
103
  stripePaying: false,
119
104
  });
120
105
 
121
- const currencies = flatPaymentMethods(paymentMethods);
106
+ const currencies = flattenPaymentMethods(paymentMethods);
122
107
  const [paymentCurrencyIndex, setPaymentCurrencyIndex] = useState(0);
123
108
 
124
109
  useEffect(() => {
@@ -161,10 +146,6 @@ export default function PaymentForm({
161
146
 
162
147
  const method = paymentMethods.find((x) => x.id === paymentMethod) as TPaymentMethodExpanded;
163
148
 
164
- const handleCurrencyChange = (index: number) => {
165
- setPaymentCurrencyIndex(index);
166
- };
167
-
168
149
  const handleConnected = async () => {
169
150
  try {
170
151
  const result = await waitForCheckoutComplete(checkoutSession.id);
@@ -243,6 +224,7 @@ export default function PaymentForm({
243
224
  setState({ submitting: false, paying: false });
244
225
  },
245
226
  onError: (err: any) => {
227
+ console.error(err);
246
228
  setState({ submitting: false, paying: false });
247
229
  onError(err);
248
230
  },
@@ -257,6 +239,7 @@ export default function PaymentForm({
257
239
  }
258
240
  }
259
241
  } catch (err: any) {
242
+ console.error(err);
260
243
  let shouldToast = true;
261
244
  if (err.response?.data?.code) {
262
245
  dispatch(`error.${err.response?.data?.code}`);
@@ -360,35 +343,11 @@ export default function PaymentForm({
360
343
  name="payment_currency"
361
344
  control={control}
362
345
  render={() => (
363
- <section
364
- style={{
365
- display: currencies.length > 1 ? 'grid' : 'block',
366
- gridTemplateColumns: '50% 50%',
367
- width: '100%',
368
- }}>
369
- {currencies.map((x, i) => {
370
- const selected = i === paymentCurrencyIndex;
371
- return (
372
- <Card
373
- key={x.id}
374
- variant="outlined"
375
- onClick={() => handleCurrencyChange(i)}
376
- className={selected ? 'cko-payment-card' : 'cko-payment-card-unselect'}>
377
- <Stack direction="row" alignItems="center">
378
- <Avatar src={x.logo} alt={x.name} sx={{ width: 30, height: 30, marginRight: '10px' }} />
379
- <div>
380
- <Typography variant="h5" component="div" sx={{ fontSize: '18px' }}>
381
- {x.symbol}
382
- </Typography>
383
- <Typography sx={{ fontSize: 14 }} color="text.secondary" gutterBottom>
384
- {(x as any).method.name}
385
- </Typography>
386
- </div>
387
- </Stack>
388
- </Card>
389
- );
390
- })}
391
- </section>
346
+ <CurrencySelector
347
+ value={paymentCurrencyIndex}
348
+ currencies={currencies}
349
+ onChange={setPaymentCurrencyIndex}
350
+ />
392
351
  )}
393
352
  />
394
353
  </Stack>
@@ -1,7 +1,12 @@
1
1
  /* eslint-disable import/no-extraneous-dependencies */
2
2
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
3
3
  import Toast from '@arcblock/ux/lib/Toast';
4
- import type { TCustomer, TPaymentCurrency, TPaymentMethodExpanded } from '@blocklet/payment-types';
4
+ import type {
5
+ TCheckoutSessionExpanded,
6
+ TCustomer,
7
+ TPaymentCurrency,
8
+ TPaymentMethodExpanded,
9
+ } from '@blocklet/payment-types';
5
10
  import { ArrowBackOutlined } from '@mui/icons-material';
6
11
  import { Box, Fade, Stack } from '@mui/material';
7
12
  import { styled } from '@mui/system';
@@ -141,7 +146,7 @@ export function PaymentInner({
141
146
  }: MainProps) {
142
147
  const { t } = useLocaleContext();
143
148
  const { settings, session } = usePaymentContext();
144
- const [state, setState] = useSetState({ checkoutSession });
149
+ const [state, setState] = useSetState<{ checkoutSession: TCheckoutSessionExpanded }>({ checkoutSession });
145
150
 
146
151
  const defaultCurrencyId = state.checkoutSession.currency_id || state.checkoutSession.line_items[0]?.price.currency_id;
147
152
  const defaultMethodId = paymentMethods.find((m) => m.payment_currencies.some((c) => c.id === defaultCurrencyId))?.id;
@@ -213,6 +218,11 @@ export function PaymentInner({
213
218
  }
214
219
  };
215
220
 
221
+ const handlePaid = (result: any) => {
222
+ setState({ checkoutSession: result.checkoutSession });
223
+ onPaid(result);
224
+ };
225
+
216
226
  return (
217
227
  <FormProvider {...methods}>
218
228
  <Root mode={mode}>
@@ -228,12 +238,15 @@ export function PaymentInner({
228
238
  <Stack className="cko-overview" direction="column">
229
239
  {mode === 'standalone' ? <PaymentHeader checkoutSession={state.checkoutSession} /> : null}
230
240
  <PaymentSummary
231
- checkoutSession={state.checkoutSession}
241
+ items={state.checkoutSession.line_items}
242
+ trialInDays={state.checkoutSession.subscription_data?.trial_period_days || 0}
232
243
  currency={currency}
233
244
  onUpsell={onUpsell}
234
245
  onDownsell={onDownsell}
235
246
  onApplyCrossSell={onApplyCrossSell}
236
247
  onCancelCrossSell={onCancelCrossSell}
248
+ checkoutSessionId={state.checkoutSession.id}
249
+ crossSellBehavior={state.checkoutSession.cross_sell_behavior}
237
250
  />
238
251
  </Stack>
239
252
  </Fade>
@@ -242,6 +255,7 @@ export function PaymentInner({
242
255
  <PaymentSuccess
243
256
  payee={getStatementDescriptor(state.checkoutSession.line_items)}
244
257
  action={state.checkoutSession.mode}
258
+ subscriptionId={state.checkoutSession.subscription_id}
245
259
  message={
246
260
  paymentLink?.after_completion?.hosted_confirmation?.custom_message ||
247
261
  t(`payment.checkout.completed.${state.checkoutSession.mode}`)
@@ -254,7 +268,7 @@ export function PaymentInner({
254
268
  paymentMethods={paymentMethods as TPaymentMethodExpanded[]}
255
269
  paymentIntent={paymentIntent}
256
270
  customer={customer}
257
- onPaid={onPaid}
271
+ onPaid={handlePaid}
258
272
  onError={onError}
259
273
  mode={mode}
260
274
  />
@@ -351,52 +365,6 @@ export const Root = styled(Box)<{ mode: LiteralUnion<'standalone' | 'inline' | '
351
365
  .cko-payment-methods {
352
366
  }
353
367
 
354
- .cko-payment-card {
355
- position: relative;
356
- border: 2px solid #3773f2;
357
- padding: 5px 10px;
358
- margin: 5px 0;
359
- cursor: pointer;
360
- }
361
-
362
- .cko-payment-card::before {
363
- content: '';
364
- position: absolute;
365
- right: 0;
366
- top: 0;
367
- border: 12px solid #3773f2;
368
- border-bottom-color: transparent;
369
- border-left-color: transparent;
370
- }
371
-
372
- .cko-payment-card-unselect {
373
- border: 2px solid #ddd;
374
- padding: 5px 10px;
375
- margin: 5px 0;
376
- cursor: pointer;
377
- }
378
-
379
- .cko-payment-card:nth-child(odd) {
380
- margin-right: 8px;
381
- }
382
-
383
- .cko-payment-card-unselect:nth-child(odd) {
384
- margin-right: 8px;
385
- }
386
-
387
- .cko-payment-card::after {
388
- content: '';
389
- width: 6px;
390
- height: 10px;
391
- position: absolute;
392
- right: 3px;
393
- top: 0px;
394
- border: 2px solid #fff;
395
- border-top-color: transparent;
396
- border-left-color: transparent;
397
- transform: rotate(35deg);
398
- }
399
-
400
368
  .cko-payment-submit {
401
369
  .MuiButtonBase-root {
402
370
  border-radius: 0;
@@ -1,10 +1,5 @@
1
1
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
- import type {
3
- PriceRecurring,
4
- TCheckoutSessionExpanded,
5
- TLineItemExpanded,
6
- TPaymentCurrency,
7
- } from '@blocklet/payment-types';
2
+ import type { PriceRecurring, TLineItemExpanded, TPaymentCurrency } from '@blocklet/payment-types';
8
3
  import { Stack, Typography } from '@mui/material';
9
4
 
10
5
  import Status from '../components/status';
@@ -14,7 +9,8 @@ import ProductCard from './product-card';
14
9
 
15
10
  type Props = {
16
11
  item: TLineItemExpanded;
17
- session: TCheckoutSessionExpanded;
12
+ items: TLineItemExpanded[];
13
+ trialInDays: number;
18
14
  currency: TPaymentCurrency;
19
15
  onUpsell: Function;
20
16
  onDownsell: Function;
@@ -27,12 +23,21 @@ ProductItem.defaultProps = {
27
23
  children: null,
28
24
  };
29
25
 
30
- export default function ProductItem({ item, session, currency, mode, children, onUpsell, onDownsell }: Props) {
26
+ export default function ProductItem({
27
+ item,
28
+ items,
29
+ trialInDays,
30
+ currency,
31
+ mode,
32
+ children,
33
+ onUpsell,
34
+ onDownsell,
35
+ }: Props) {
31
36
  const { t, locale } = useLocaleContext();
32
- const pricing = formatLineItemPricing(item, currency, session.subscription_data?.trial_period_days || 0, locale);
33
- const saving = formatUpsellSaving(session, currency);
37
+ const pricing = formatLineItemPricing(item, currency, trialInDays, locale);
38
+ const saving = formatUpsellSaving(items, currency);
34
39
  const metered = item.price?.recurring?.usage_type === 'metered' ? t('common.metered') : '';
35
- const canUpsell = mode === 'normal' && session.line_items.length === 1;
40
+ const canUpsell = mode === 'normal' && items.length === 1;
36
41
  return (
37
42
  <Stack direction="column" alignItems="flex-start" spacing={1} sx={{ width: '100%' }}>
38
43
  <Stack direction="column" alignItems="flex-end" sx={{ width: '100%' }}>
@@ -9,9 +9,10 @@ type Props = {
9
9
  message: string;
10
10
  action: string;
11
11
  payee: string;
12
+ subscriptionId?: string;
12
13
  };
13
14
 
14
- export default function PaymentSuccess({ message, action, payee }: Props) {
15
+ export default function PaymentSuccess({ message, action, payee, subscriptionId }: Props) {
15
16
  const { t } = useLocaleContext();
16
17
  const { prefix } = usePaymentContext();
17
18
  return (
@@ -33,7 +34,9 @@ export default function PaymentSuccess({ message, action, payee }: Props) {
33
34
  </Typography>
34
35
  {['subscription', 'setup'].includes(action) && (
35
36
  <Typography textAlign="center" sx={{ mt: 2 }}>
36
- <Link href={joinURL(prefix, '/customer')}>{t('payment.checkout.portal', { payee })}</Link>
37
+ <Link href={joinURL(prefix, `/customer/subscription/${subscriptionId}`)}>
38
+ {t('payment.checkout.portal', { payee })}
39
+ </Link>
37
40
  </Typography>
38
41
  )}
39
42
  </Stack>
@@ -41,6 +44,10 @@ export default function PaymentSuccess({ message, action, payee }: Props) {
41
44
  );
42
45
  }
43
46
 
47
+ PaymentSuccess.defaultProps = {
48
+ subscriptionId: '',
49
+ };
50
+
44
51
  const Div = styled('div')`
45
52
  width: 80px;
46
53
  height: 115px;
@@ -1,5 +1,5 @@
1
1
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
- import type { TCheckoutSessionExpanded, TLineItemExpanded, TPaymentCurrency } from '@blocklet/payment-types';
2
+ import type { TLineItemExpanded, TPaymentCurrency } from '@blocklet/payment-types';
3
3
  import { LoadingButton } from '@mui/lab';
4
4
  import { Fade, Grow, Stack, Typography, keyframes } from '@mui/material';
5
5
  import { useRequest, useSetState } from 'ahooks';
@@ -31,12 +31,15 @@ const shake = keyframes`
31
31
  `;
32
32
 
33
33
  type Props = {
34
- checkoutSession: TCheckoutSessionExpanded;
34
+ items: TLineItemExpanded[];
35
35
  currency: TPaymentCurrency;
36
- onUpsell: Function;
37
- onDownsell: Function;
38
- onApplyCrossSell: Function;
39
- onCancelCrossSell: Function;
36
+ trialInDays: number;
37
+ onUpsell?: Function;
38
+ onDownsell?: Function;
39
+ onApplyCrossSell?: Function;
40
+ onCancelCrossSell?: Function;
41
+ checkoutSessionId?: string;
42
+ crossSellBehavior?: string;
40
43
  };
41
44
 
42
45
  async function fetchCrossSell(id: string) {
@@ -52,18 +55,33 @@ async function fetchCrossSell(id: string) {
52
55
  }
53
56
  }
54
57
 
58
+ PaymentSummary.defaultProps = {
59
+ onUpsell: noop,
60
+ onDownsell: noop,
61
+ onApplyCrossSell: noop,
62
+ onCancelCrossSell: noop,
63
+ checkoutSessionId: '',
64
+ crossSellBehavior: '',
65
+ };
66
+
55
67
  export default function PaymentSummary({
56
- checkoutSession,
68
+ items,
57
69
  currency,
70
+ trialInDays,
58
71
  onUpsell,
59
72
  onDownsell,
60
73
  onApplyCrossSell,
61
74
  onCancelCrossSell,
75
+ checkoutSessionId,
76
+ crossSellBehavior,
77
+ ...rest
62
78
  }: Props) {
63
79
  const { t, locale } = useLocaleContext();
64
80
  const [state, setState] = useSetState({ loading: false, shake: false });
65
- const { data, runAsync } = useRequest(() => fetchCrossSell(checkoutSession.id));
66
- const headlines = formatCheckoutHeadlines(checkoutSession, currency, locale);
81
+ const { data, runAsync } = useRequest(() =>
82
+ checkoutSessionId ? fetchCrossSell(checkoutSessionId) : Promise.resolve(null)
83
+ );
84
+ const headlines = formatCheckoutHeadlines(items, currency, trialInDays, locale);
67
85
 
68
86
  useBus(
69
87
  'error.REQUIRE_CROSS_SELL',
@@ -77,12 +95,12 @@ export default function PaymentSummary({
77
95
  );
78
96
 
79
97
  const handleUpsell = async (from: string, to: string) => {
80
- await onUpsell(from, to);
98
+ await onUpsell!(from, to);
81
99
  runAsync();
82
100
  };
83
101
 
84
102
  const handleDownsell = async (from: string) => {
85
- await onDownsell(from);
103
+ await onDownsell!(from);
86
104
  runAsync();
87
105
  };
88
106
 
@@ -90,7 +108,7 @@ export default function PaymentSummary({
90
108
  if (data) {
91
109
  try {
92
110
  setState({ loading: true });
93
- await onApplyCrossSell(data.id);
111
+ await onApplyCrossSell!(data.id);
94
112
  } catch (err) {
95
113
  console.error(err);
96
114
  } finally {
@@ -102,7 +120,7 @@ export default function PaymentSummary({
102
120
  const handleCancelCrossSell = async () => {
103
121
  try {
104
122
  setState({ loading: true });
105
- await onCancelCrossSell();
123
+ await onCancelCrossSell!();
106
124
  } catch (err) {
107
125
  console.error(err);
108
126
  } finally {
@@ -112,7 +130,7 @@ export default function PaymentSummary({
112
130
 
113
131
  return (
114
132
  <Fade in>
115
- <Stack className="cko-product" direction="column">
133
+ <Stack className="cko-product" direction="column" {...rest}>
116
134
  <Stack className="cko-product-summary" direction="column" alignItems="flex-start" sx={{ mb: 4 }}>
117
135
  <Typography sx={{ fontWeight: 500, fontSize: '1.15rem', color: 'text.secondary' }}>
118
136
  {headlines.action}
@@ -123,11 +141,12 @@ export default function PaymentSummary({
123
141
  )}
124
142
  </Stack>
125
143
  <Stack spacing={2}>
126
- {checkoutSession.line_items.map((x: TLineItemExpanded) => (
144
+ {items.map((x: TLineItemExpanded) => (
127
145
  <ProductItem
128
146
  key={x.price_id}
129
147
  item={x}
130
- session={checkoutSession}
148
+ items={items}
149
+ trialInDays={trialInDays}
131
150
  currency={currency}
132
151
  onUpsell={handleUpsell}
133
152
  onDownsell={handleDownsell}>
@@ -145,7 +164,7 @@ export default function PaymentSummary({
145
164
  </ProductItem>
146
165
  ))}
147
166
  </Stack>
148
- {data && checkoutSession.line_items.some((x) => x.price_id === data.id) === false && (
167
+ {data && items.some((x) => x.price_id === data.id) === false && (
149
168
  <Grow in>
150
169
  <Stack
151
170
  direction="column"
@@ -163,22 +182,23 @@ export default function PaymentSummary({
163
182
  }}>
164
183
  <ProductItem
165
184
  item={{ quantity: 1, price: data, price_id: data.id, cross_sell: true } as TLineItemExpanded}
166
- session={checkoutSession}
185
+ items={items}
186
+ trialInDays={trialInDays}
167
187
  currency={currency}
168
188
  onUpsell={noop}
169
189
  onDownsell={noop}
170
190
  />
171
191
  <Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ width: 1 }}>
172
192
  <Typography>
173
- {checkoutSession.cross_sell_behavior === 'required' && (
193
+ {crossSellBehavior === 'required' && (
174
194
  <Status label={t('payment.checkout.required')} color="info" variant="outlined" sx={{ mr: 1 }} />
175
195
  )}
176
196
  </Typography>
177
197
  <LoadingButton
178
198
  size="small"
179
199
  loadingPosition="end"
180
- color={checkoutSession.cross_sell_behavior === 'required' ? 'info' : 'info'}
181
- variant={checkoutSession.cross_sell_behavior === 'required' ? 'text' : 'text'}
200
+ color={crossSellBehavior === 'required' ? 'info' : 'info'}
201
+ variant={crossSellBehavior === 'required' ? 'text' : 'text'}
182
202
  loading={state.loading}
183
203
  onClick={handleApplyCrossSell}>
184
204
  {t('payment.checkout.cross_sell.add')}
package/src/util.ts CHANGED
@@ -3,7 +3,6 @@
3
3
  import type {
4
4
  PriceCurrency,
5
5
  PriceRecurring,
6
- TCheckoutSessionExpanded,
7
6
  TLineItemExpanded,
8
7
  TPaymentCurrency,
9
8
  TPaymentMethodExpanded,
@@ -382,8 +381,7 @@ export function getRecurringPeriod(recurring: PriceRecurring) {
382
381
  }
383
382
  }
384
383
 
385
- export function formatUpsellSaving(session: TCheckoutSessionExpanded, currency: TPaymentCurrency) {
386
- const items = session.line_items as TLineItemExpanded[];
384
+ export function formatUpsellSaving(items: TLineItemExpanded[], currency: TPaymentCurrency) {
387
385
  if (items[0]?.upsell_price_id) {
388
386
  return '0';
389
387
  }
@@ -418,8 +416,9 @@ export function formatUpsellSaving(session: TCheckoutSessionExpanded, currency:
418
416
  }
419
417
 
420
418
  export function formatCheckoutHeadlines(
421
- session: TCheckoutSessionExpanded,
419
+ items: TLineItemExpanded[],
422
420
  currency: TPaymentCurrency,
421
+ trialInDays: number,
423
422
  locale: string = 'en'
424
423
  ): {
425
424
  action: string;
@@ -427,9 +426,7 @@ export function formatCheckoutHeadlines(
427
426
  then?: string;
428
427
  secondary?: string;
429
428
  } {
430
- const items = session.line_items as TLineItemExpanded[];
431
- const trial = session.subscription_data?.trial_period_days || 0;
432
-
429
+ const trial = trialInDays || 0;
433
430
  const brand = getStatementDescriptor(items);
434
431
  const { total } = getCheckoutAmount(items, currency, !!trial);
435
432
  const amount = `${fromUnitToToken(total, currency.decimal)} ${currency.symbol}`;
@@ -596,9 +593,6 @@ export const getSubscriptionTimeSummary = (subscription: TSubscriptionExpanded)
596
593
 
597
594
  export const getSubscriptionAction = (subscription: TSubscriptionExpanded) => {
598
595
  if (subscription.status === 'active' || subscription.status === 'trialing') {
599
- if (subscription.cancel_at) {
600
- return null;
601
- }
602
596
  if (subscription.cancel_at_period_end) {
603
597
  if (subscription.cancelation_details?.reason === 'payment_failed') {
604
598
  return null;
@@ -607,6 +601,10 @@ export const getSubscriptionAction = (subscription: TSubscriptionExpanded) => {
607
601
  return { action: 'recover', variant: 'contained', color: 'primary', canRenew: false };
608
602
  }
609
603
 
604
+ if (subscription.cancel_at && subscription.cancel_at !== subscription.current_period_end) {
605
+ return null;
606
+ }
607
+
610
608
  return { action: 'cancel', variant: 'outlined', color: 'inherit', canRenew: false };
611
609
  }
612
610
 
@@ -630,3 +628,19 @@ export const mergeExtraParams = (extra: Record<string, any> = {}) => {
630
628
 
631
629
  return params.toString();
632
630
  };
631
+
632
+ export const flattenPaymentMethods = (methods: TPaymentMethodExpanded[] = []) => {
633
+ const out: TPaymentCurrency[] = [];
634
+
635
+ methods.forEach((method: any) => {
636
+ const currencies = method.paymentCurrencies || method.payment_currencies || [];
637
+ currencies.forEach((currency: any) => {
638
+ out.push({
639
+ ...currency,
640
+ method,
641
+ });
642
+ });
643
+ });
644
+
645
+ return out;
646
+ };