@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.
- package/es/components/input.d.ts +3 -1
- package/es/components/input.js +12 -3
- package/es/history/invoice/list.js +1 -1
- package/es/history/payment/list.js +1 -1
- package/es/index.d.ts +5 -1
- package/es/index.js +8 -0
- package/es/locales/en.js +13 -3
- package/es/locales/zh.js +13 -3
- package/es/payment/form/address.js +2 -1
- package/es/payment/form/currency.d.ts +9 -0
- package/es/payment/form/currency.js +81 -0
- package/es/payment/form/index.js +10 -44
- package/es/payment/index.js +11 -49
- package/es/payment/product-item.d.ts +4 -3
- package/es/payment/product-item.js +13 -4
- package/es/payment/success.d.ts +8 -2
- package/es/payment/success.js +5 -2
- package/es/payment/summary.d.ts +21 -8
- package/es/payment/summary.js +28 -12
- package/es/util.d.ts +6 -3
- package/es/util.js +19 -8
- package/lib/components/input.d.ts +3 -1
- package/lib/components/input.js +4 -2
- package/lib/history/invoice/list.js +1 -1
- package/lib/history/payment/list.js +1 -1
- package/lib/index.d.ts +5 -1
- package/lib/index.js +32 -0
- package/lib/locales/en.js +13 -3
- package/lib/locales/zh.js +13 -3
- package/lib/payment/form/address.js +4 -1
- package/lib/payment/form/currency.d.ts +9 -0
- package/lib/payment/form/currency.js +106 -0
- package/lib/payment/form/index.js +8 -60
- package/lib/payment/index.js +13 -49
- package/lib/payment/product-item.d.ts +4 -3
- package/lib/payment/product-item.js +5 -4
- package/lib/payment/success.d.ts +8 -2
- package/lib/payment/success.js +6 -2
- package/lib/payment/summary.d.ts +21 -8
- package/lib/payment/summary.js +26 -11
- package/lib/util.d.ts +6 -3
- package/lib/util.js +22 -9
- package/package.json +10 -10
- package/src/components/input.tsx +12 -2
- package/src/history/invoice/list.tsx +1 -1
- package/src/history/payment/list.tsx +1 -1
- package/src/index.ts +8 -0
- package/src/locales/en.tsx +14 -1
- package/src/locales/zh.tsx +12 -1
- package/src/payment/form/address.tsx +1 -0
- package/src/payment/form/currency.tsx +91 -0
- package/src/payment/form/index.tsx +12 -53
- package/src/payment/index.tsx +18 -50
- package/src/payment/product-item.tsx +16 -11
- package/src/payment/success.tsx +9 -2
- package/src/payment/summary.tsx +41 -21
- 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 {
|
|
6
|
+
import type { TCustomer, TPaymentIntent, TPaymentMethodExpanded } from '@blocklet/payment-types';
|
|
7
7
|
import { LoadingButton } from '@mui/lab';
|
|
8
|
-
import {
|
|
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 =
|
|
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
|
-
<
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
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>
|
package/src/payment/index.tsx
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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={
|
|
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
|
-
|
|
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({
|
|
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,
|
|
33
|
-
const saving = formatUpsellSaving(
|
|
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' &&
|
|
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%' }}>
|
package/src/payment/success.tsx
CHANGED
|
@@ -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,
|
|
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;
|
package/src/payment/summary.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
2
|
-
import type {
|
|
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
|
-
|
|
34
|
+
items: TLineItemExpanded[];
|
|
35
35
|
currency: TPaymentCurrency;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
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(() =>
|
|
66
|
-
|
|
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
|
-
{
|
|
144
|
+
{items.map((x: TLineItemExpanded) => (
|
|
127
145
|
<ProductItem
|
|
128
146
|
key={x.price_id}
|
|
129
147
|
item={x}
|
|
130
|
-
|
|
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 &&
|
|
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
|
-
|
|
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
|
-
{
|
|
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={
|
|
181
|
-
variant={
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
+
};
|