@blocklet/payment-react 1.13.210 → 1.13.212
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/checkout/donate.d.ts +20 -0
- package/es/checkout/donate.js +199 -0
- package/es/checkout/form.d.ts +2 -1
- package/es/checkout/form.js +13 -2
- package/es/components/blockchain/tx.d.ts +2 -0
- package/es/components/blockchain/tx.js +16 -5
- package/es/components/safe-guard.d.ts +3 -0
- package/es/components/safe-guard.js +4 -0
- package/es/index.d.ts +3 -1
- package/es/index.js +5 -1
- package/es/locales/en.js +8 -0
- package/es/locales/zh.js +8 -0
- package/es/payment/error.d.ts +3 -1
- package/es/payment/error.js +4 -3
- package/es/payment/form/currency.js +10 -12
- package/es/payment/form/index.d.ts +1 -1
- package/es/payment/form/index.js +15 -3
- package/es/payment/index.d.ts +3 -3
- package/es/payment/index.js +38 -13
- package/es/payment/product-donation.d.ts +7 -0
- package/es/payment/product-donation.js +99 -0
- package/es/payment/skeleton/overview.js +2 -2
- package/es/payment/skeleton/payment.js +2 -5
- package/es/payment/success.d.ts +2 -1
- package/es/payment/success.js +21 -12
- package/es/payment/summary.d.ts +8 -2
- package/es/payment/summary.js +46 -29
- package/es/types/index.d.ts +2 -0
- package/es/util.d.ts +2 -0
- package/es/util.js +47 -3
- package/lib/checkout/donate.d.ts +20 -0
- package/lib/checkout/donate.js +284 -0
- package/lib/checkout/form.d.ts +2 -1
- package/lib/checkout/form.js +5 -2
- package/lib/components/blockchain/tx.d.ts +2 -0
- package/lib/components/blockchain/tx.js +3 -1
- package/lib/components/safe-guard.d.ts +3 -0
- package/lib/components/safe-guard.js +12 -0
- package/lib/index.d.ts +3 -1
- package/lib/index.js +16 -0
- package/lib/locales/en.js +8 -0
- package/lib/locales/zh.js +8 -0
- package/lib/payment/error.d.ts +3 -1
- package/lib/payment/error.js +5 -3
- package/lib/payment/form/currency.js +10 -12
- package/lib/payment/form/index.d.ts +1 -1
- package/lib/payment/form/index.js +16 -4
- package/lib/payment/index.d.ts +3 -3
- package/lib/payment/index.js +56 -24
- package/lib/payment/product-donation.d.ts +7 -0
- package/lib/payment/product-donation.js +169 -0
- package/lib/payment/skeleton/overview.js +2 -2
- package/lib/payment/skeleton/payment.js +4 -8
- package/lib/payment/success.d.ts +2 -1
- package/lib/payment/success.js +3 -2
- package/lib/payment/summary.d.ts +8 -2
- package/lib/payment/summary.js +30 -7
- package/lib/types/index.d.ts +2 -0
- package/lib/util.d.ts +2 -0
- package/lib/util.js +44 -4
- package/package.json +6 -6
- package/src/checkout/donate.tsx +256 -0
- package/src/checkout/form.tsx +13 -4
- package/src/components/blockchain/tx.tsx +8 -1
- package/src/components/safe-guard.tsx +5 -0
- package/src/index.ts +4 -0
- package/src/locales/en.tsx +8 -0
- package/src/locales/zh.tsx +8 -0
- package/src/payment/error.tsx +4 -2
- package/src/payment/form/currency.tsx +11 -13
- package/src/payment/form/index.tsx +14 -4
- package/src/payment/index.tsx +40 -14
- package/src/payment/product-donation.tsx +118 -0
- package/src/payment/skeleton/overview.tsx +2 -2
- package/src/payment/skeleton/payment.tsx +1 -4
- package/src/payment/success.tsx +7 -2
- package/src/payment/summary.tsx +47 -28
- package/src/types/index.ts +2 -0
- package/src/util.ts +54 -3
|
@@ -70,10 +70,12 @@ export default function PaymentForm({
|
|
|
70
70
|
checkoutSession,
|
|
71
71
|
paymentMethods,
|
|
72
72
|
paymentIntent,
|
|
73
|
+
paymentLink,
|
|
73
74
|
customer,
|
|
74
75
|
onPaid,
|
|
75
76
|
onError,
|
|
76
77
|
mode,
|
|
78
|
+
action,
|
|
77
79
|
}: PageData) {
|
|
78
80
|
const theme = useTheme();
|
|
79
81
|
const { t } = useLocaleContext();
|
|
@@ -140,9 +142,17 @@ export default function PaymentForm({
|
|
|
140
142
|
}, [domSize, theme]);
|
|
141
143
|
|
|
142
144
|
const payee = getStatementDescriptor(checkoutSession.line_items);
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
145
|
+
let buttonText = '';
|
|
146
|
+
if (paymentLink?.donation_settings) {
|
|
147
|
+
if (action) {
|
|
148
|
+
buttonText = action;
|
|
149
|
+
} else {
|
|
150
|
+
buttonText = t('payment.checkout.donate');
|
|
151
|
+
}
|
|
152
|
+
} else {
|
|
153
|
+
buttonText = t(`payment.checkout.${checkoutSession.mode}`);
|
|
154
|
+
}
|
|
155
|
+
buttonText = session?.user ? buttonText : t('payment.checkout.connect', { action: buttonText });
|
|
146
156
|
|
|
147
157
|
const method = paymentMethods.find((x) => x.id === paymentMethod) as TPaymentMethodExpanded;
|
|
148
158
|
|
|
@@ -348,7 +358,7 @@ export default function PaymentForm({
|
|
|
348
358
|
<AddressForm mode={checkoutSession.billing_address_collection as string} stripe={method?.type === 'stripe'} />
|
|
349
359
|
<Fade in>
|
|
350
360
|
<Stack direction="column" alignItems="flex-start" className="cko-payment-methods">
|
|
351
|
-
<Typography sx={{ mb:
|
|
361
|
+
<Typography sx={{ mb: 1, color: 'text.primary', fontWeight: 600 }}>{t('payment.checkout.method')}</Typography>
|
|
352
362
|
<Stack direction="row" sx={{ width: '100%' }}>
|
|
353
363
|
<Controller
|
|
354
364
|
name="payment_currency"
|
package/src/payment/index.tsx
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
/* eslint-disable import/no-extraneous-dependencies */
|
|
1
|
+
/* eslint-disable no-nested-ternary */
|
|
3
2
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
4
3
|
import Toast from '@arcblock/ux/lib/Toast';
|
|
5
4
|
import type {
|
|
@@ -12,10 +11,11 @@ import type {
|
|
|
12
11
|
import { ArrowBackOutlined } from '@mui/icons-material';
|
|
13
12
|
import { Box, Fade, Stack } from '@mui/material';
|
|
14
13
|
import { styled } from '@mui/system';
|
|
14
|
+
import { fromTokenToUnit } from '@ocap/util';
|
|
15
15
|
import { useSetState } from 'ahooks';
|
|
16
16
|
import { useEffect, useState } from 'react';
|
|
17
17
|
import { FormProvider, useForm, useWatch } from 'react-hook-form';
|
|
18
|
-
import { LiteralUnion } from 'type-fest';
|
|
18
|
+
import type { LiteralUnion } from 'type-fest';
|
|
19
19
|
|
|
20
20
|
import api from '../api';
|
|
21
21
|
import { usePaymentContext } from '../contexts/payment';
|
|
@@ -50,6 +50,7 @@ export default function Payment({
|
|
|
50
50
|
onError,
|
|
51
51
|
onChange,
|
|
52
52
|
goBack,
|
|
53
|
+
action,
|
|
53
54
|
}: Props) {
|
|
54
55
|
const { t } = useLocaleContext();
|
|
55
56
|
const { refresh, livemode, setLivemode } = usePaymentContext();
|
|
@@ -74,7 +75,7 @@ export default function Payment({
|
|
|
74
75
|
}, [checkoutSession, livemode, setLivemode, refresh]);
|
|
75
76
|
|
|
76
77
|
if (error) {
|
|
77
|
-
return <PaymentError title="Oops" description={formatError(error)} />;
|
|
78
|
+
return <PaymentError mode={mode} title="Oops" description={formatError(error)} />;
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
if (!checkoutSession || !delay) {
|
|
@@ -87,7 +88,7 @@ export default function Payment({
|
|
|
87
88
|
<Stack className="cko-payment">
|
|
88
89
|
<PaymentSkeleton />
|
|
89
90
|
</Stack>
|
|
90
|
-
<CheckoutFooter className="cko-footer" />
|
|
91
|
+
{mode === 'standalone' && <CheckoutFooter className="cko-footer" />}
|
|
91
92
|
</Stack>
|
|
92
93
|
</Root>
|
|
93
94
|
);
|
|
@@ -96,12 +97,11 @@ export default function Payment({
|
|
|
96
97
|
// expired session
|
|
97
98
|
if (checkoutSession.expires_at <= Math.round(Date.now() / 1000)) {
|
|
98
99
|
return (
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
</Center>
|
|
100
|
+
<PaymentError
|
|
101
|
+
mode={mode}
|
|
102
|
+
title={t('payment.checkout.expired.title')}
|
|
103
|
+
description={t('payment.checkout.expired.description')}
|
|
104
|
+
/>
|
|
105
105
|
);
|
|
106
106
|
}
|
|
107
107
|
|
|
@@ -109,6 +109,7 @@ export default function Payment({
|
|
|
109
109
|
if (checkoutSession.status === 'complete') {
|
|
110
110
|
return (
|
|
111
111
|
<PaymentError
|
|
112
|
+
mode={mode}
|
|
112
113
|
title={t('payment.checkout.complete.title')}
|
|
113
114
|
description={t('payment.checkout.complete.description')}
|
|
114
115
|
/>
|
|
@@ -128,6 +129,7 @@ export default function Payment({
|
|
|
128
129
|
onChange={onChange}
|
|
129
130
|
goBack={goBack}
|
|
130
131
|
mode={mode}
|
|
132
|
+
action={action}
|
|
131
133
|
/>
|
|
132
134
|
);
|
|
133
135
|
}
|
|
@@ -150,6 +152,7 @@ export function PaymentInner({
|
|
|
150
152
|
onError,
|
|
151
153
|
onChange,
|
|
152
154
|
goBack,
|
|
155
|
+
action,
|
|
153
156
|
}: MainProps) {
|
|
154
157
|
const { t } = useLocaleContext();
|
|
155
158
|
const { settings, session } = usePaymentContext();
|
|
@@ -232,6 +235,19 @@ export function PaymentInner({
|
|
|
232
235
|
}
|
|
233
236
|
};
|
|
234
237
|
|
|
238
|
+
const onChangeAmount = async ({ priceId, amount }: { priceId: string; amount: string }) => {
|
|
239
|
+
try {
|
|
240
|
+
const { data } = await api.put(`/api/checkout-sessions/${state.checkoutSession.id}/amount`, {
|
|
241
|
+
priceId,
|
|
242
|
+
amount: fromTokenToUnit(amount, currency.decimal).toString(),
|
|
243
|
+
});
|
|
244
|
+
setState({ checkoutSession: data });
|
|
245
|
+
} catch (err) {
|
|
246
|
+
console.error(err);
|
|
247
|
+
Toast.error(formatError(err));
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
235
251
|
const handlePaid = (result: any) => {
|
|
236
252
|
setState({ checkoutSession: result.checkoutSession });
|
|
237
253
|
onPaid(result);
|
|
@@ -247,7 +263,7 @@ export function PaymentInner({
|
|
|
247
263
|
fontSize="medium"
|
|
248
264
|
/>
|
|
249
265
|
)}
|
|
250
|
-
<Stack className="cko-container" sx={{ gap: { sm: mode === 'standalone' ? 0 : 8 } }}>
|
|
266
|
+
<Stack className="cko-container" sx={{ gap: { sm: mode === 'standalone' ? 0 : mode === 'inline' ? 4 : 8 } }}>
|
|
251
267
|
<Fade in>
|
|
252
268
|
<Stack className="cko-overview" direction="column">
|
|
253
269
|
{mode === 'standalone' ? <PaymentHeader checkoutSession={state.checkoutSession} /> : null}
|
|
@@ -261,21 +277,29 @@ export function PaymentInner({
|
|
|
261
277
|
onDownsell={onDownsell}
|
|
262
278
|
onApplyCrossSell={onApplyCrossSell}
|
|
263
279
|
onCancelCrossSell={onCancelCrossSell}
|
|
280
|
+
onChangeAmount={onChangeAmount}
|
|
264
281
|
checkoutSessionId={state.checkoutSession.id}
|
|
265
282
|
crossSellBehavior={state.checkoutSession.cross_sell_behavior}
|
|
283
|
+
donationSettings={paymentLink?.donation_settings}
|
|
284
|
+
action={action}
|
|
266
285
|
/>
|
|
267
286
|
</Stack>
|
|
268
287
|
</Fade>
|
|
269
|
-
<Stack className="cko-payment" direction="column" spacing={4}>
|
|
288
|
+
<Stack className="cko-payment" direction="column" spacing={{ xs: 2, sm: 4 }}>
|
|
270
289
|
{completed && (
|
|
271
290
|
<PaymentSuccess
|
|
291
|
+
mode={mode}
|
|
272
292
|
payee={getStatementDescriptor(state.checkoutSession.line_items)}
|
|
273
293
|
action={state.checkoutSession.mode}
|
|
274
294
|
invoiceId={state.checkoutSession.invoice_id}
|
|
275
295
|
subscriptionId={state.checkoutSession.subscription_id}
|
|
276
296
|
message={
|
|
277
297
|
paymentLink?.after_completion?.hosted_confirmation?.custom_message ||
|
|
278
|
-
t(
|
|
298
|
+
t(
|
|
299
|
+
`payment.checkout.completed.${
|
|
300
|
+
paymentLink?.donation_settings ? 'donate' : state.checkoutSession.mode
|
|
301
|
+
}`
|
|
302
|
+
)
|
|
279
303
|
}
|
|
280
304
|
/>
|
|
281
305
|
)}
|
|
@@ -284,10 +308,12 @@ export function PaymentInner({
|
|
|
284
308
|
checkoutSession={state.checkoutSession}
|
|
285
309
|
paymentMethods={paymentMethods as TPaymentMethodExpanded[]}
|
|
286
310
|
paymentIntent={paymentIntent}
|
|
311
|
+
paymentLink={paymentLink}
|
|
287
312
|
customer={customer}
|
|
288
313
|
onPaid={handlePaid}
|
|
289
314
|
onError={onError}
|
|
290
315
|
mode={mode}
|
|
316
|
+
action={action}
|
|
291
317
|
/>
|
|
292
318
|
)}
|
|
293
319
|
</Stack>
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
2
|
+
import type { DonationSettings, TLineItemExpanded } from '@blocklet/payment-types';
|
|
3
|
+
import { Box, Card, CardActionArea, FormControlLabel, Stack, TextField, Typography } from '@mui/material';
|
|
4
|
+
import { useSetState } from 'ahooks';
|
|
5
|
+
import { useEffect } from 'react';
|
|
6
|
+
|
|
7
|
+
import Switch from '../components/switch-button';
|
|
8
|
+
|
|
9
|
+
export default function ProductDonation({
|
|
10
|
+
item,
|
|
11
|
+
settings,
|
|
12
|
+
onChange,
|
|
13
|
+
}: {
|
|
14
|
+
item: TLineItemExpanded;
|
|
15
|
+
settings: DonationSettings;
|
|
16
|
+
onChange: Function;
|
|
17
|
+
}) {
|
|
18
|
+
const { t } = useLocaleContext();
|
|
19
|
+
const preset = settings.amount.preset || settings.amount.presets?.[0] || '0';
|
|
20
|
+
const [state, setState] = useSetState({
|
|
21
|
+
selected: preset,
|
|
22
|
+
input: '',
|
|
23
|
+
custom: settings.amount.presets?.length === 0,
|
|
24
|
+
error: '',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Set default amount
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (settings.amount.preset) {
|
|
30
|
+
setState({ selected: settings.amount.preset, custom: false });
|
|
31
|
+
} else if (settings.amount.presets && settings.amount.presets.length > 0) {
|
|
32
|
+
setState({ selected: settings.amount.presets[0], custom: false });
|
|
33
|
+
}
|
|
34
|
+
}, [settings.amount.preset, settings.amount.presets, setState]);
|
|
35
|
+
|
|
36
|
+
const handleSelect = (amount: string) => {
|
|
37
|
+
setState({ selected: amount, custom: false, error: '' });
|
|
38
|
+
onChange({ priceId: item.price_id, amount });
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const handleInput = (event: any) => {
|
|
42
|
+
const { value } = event.target;
|
|
43
|
+
const min = parseFloat(settings.amount.minimum || '0');
|
|
44
|
+
const max = settings.amount.maximum ? parseFloat(settings.amount.maximum) : Infinity;
|
|
45
|
+
|
|
46
|
+
if (value < min || value > max) {
|
|
47
|
+
setState({ input: value, error: t('payment.checkout.donation.between', { min, max }) });
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
setState({ error: '', input: value });
|
|
52
|
+
onChange({ priceId: item.price_id, amount: value });
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const handleToggle = (event: any) => {
|
|
56
|
+
if (event.target.checked) {
|
|
57
|
+
setState({ custom: true, input: state.selected, error: '' });
|
|
58
|
+
} else {
|
|
59
|
+
setState({ custom: false, input: '', error: '' });
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<Box display="flex" flexDirection="column" alignItems="flex-start" gap={1.5}>
|
|
65
|
+
{settings.amount.custom && preset !== '0' && (
|
|
66
|
+
<FormControlLabel
|
|
67
|
+
control={<Switch checked={state.custom} sx={{ marginRight: 0.4 }} onChange={handleToggle} />}
|
|
68
|
+
label={state.custom ? t('payment.checkout.donation.select') : t('payment.checkout.donation.custom')}
|
|
69
|
+
sx={{ marginRight: 2, marginLeft: 0.5, color: 'text.secondary' }}
|
|
70
|
+
/>
|
|
71
|
+
)}
|
|
72
|
+
{!state.custom && (
|
|
73
|
+
<Box display="flex" flexWrap="wrap" alignItems="center" gap={1.5}>
|
|
74
|
+
{settings.amount.presets &&
|
|
75
|
+
settings.amount.presets.length > 0 &&
|
|
76
|
+
settings.amount.presets.map((amount) => (
|
|
77
|
+
<Card
|
|
78
|
+
key={amount}
|
|
79
|
+
variant="outlined"
|
|
80
|
+
sx={{
|
|
81
|
+
minWidth: 120,
|
|
82
|
+
textAlign: 'center',
|
|
83
|
+
...(state.selected === amount && !state.custom ? { borderColor: 'primary.main' } : {}),
|
|
84
|
+
}}>
|
|
85
|
+
<CardActionArea onClick={() => handleSelect(amount)}>
|
|
86
|
+
<Stack direction="row" sx={{ py: 1 }} spacing={0.5} alignItems="flex-end" justifyContent="center">
|
|
87
|
+
<Typography
|
|
88
|
+
component="strong"
|
|
89
|
+
lineHeight={1}
|
|
90
|
+
variant="h5"
|
|
91
|
+
sx={{ fontVariantNumeric: 'tabular-nums', fontWeight: 400 }}>
|
|
92
|
+
{amount}
|
|
93
|
+
</Typography>
|
|
94
|
+
<Typography component="small" lineHeight={1} fontSize={12}>
|
|
95
|
+
ABT
|
|
96
|
+
</Typography>{' '}
|
|
97
|
+
</Stack>
|
|
98
|
+
</CardActionArea>
|
|
99
|
+
</Card>
|
|
100
|
+
))}
|
|
101
|
+
</Box>
|
|
102
|
+
)}
|
|
103
|
+
{state.custom && (
|
|
104
|
+
<TextField
|
|
105
|
+
label={preset !== '0' ? null : t('payment.checkout.donation.custom')}
|
|
106
|
+
type="number"
|
|
107
|
+
value={state.input}
|
|
108
|
+
onChange={handleInput}
|
|
109
|
+
inputProps={{ min: settings.amount.minimum, max: settings.amount.maximum }}
|
|
110
|
+
margin="none"
|
|
111
|
+
fullWidth
|
|
112
|
+
error={!!state.error}
|
|
113
|
+
helperText={state.error}
|
|
114
|
+
/>
|
|
115
|
+
)}
|
|
116
|
+
</Box>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
@@ -8,13 +8,13 @@ export default function OverviewSkeleton() {
|
|
|
8
8
|
<Skeleton variant="circular" width={32} height={32} />
|
|
9
9
|
<Skeleton variant="text" sx={{ fontSize: '2rem', width: '40%' }} />
|
|
10
10
|
</Stack>
|
|
11
|
-
<Typography mt={
|
|
11
|
+
<Typography mt={2} component="div" variant="h4">
|
|
12
12
|
<Skeleton />
|
|
13
13
|
</Typography>
|
|
14
14
|
<Typography component="div" variant="h2">
|
|
15
15
|
<Skeleton />
|
|
16
16
|
</Typography>
|
|
17
|
-
<Skeleton sx={{ mt:
|
|
17
|
+
<Skeleton sx={{ mt: 2 }} variant="rounded" width={200} height={200} />
|
|
18
18
|
</Stack>
|
|
19
19
|
</Fade>
|
|
20
20
|
);
|
|
@@ -3,7 +3,7 @@ import { Box, Fade, Skeleton, Stack, Typography } from '@mui/material';
|
|
|
3
3
|
export default function PaymentSkeleton() {
|
|
4
4
|
return (
|
|
5
5
|
<Fade in>
|
|
6
|
-
<Stack direction="column" spacing={
|
|
6
|
+
<Stack direction="column" spacing={2}>
|
|
7
7
|
<Skeleton variant="text" sx={{ fontSize: '2rem', width: '40%' }} />
|
|
8
8
|
<Box>
|
|
9
9
|
<Typography component="div" variant="h4">
|
|
@@ -25,9 +25,6 @@ export default function PaymentSkeleton() {
|
|
|
25
25
|
<Typography component="div" variant="h4">
|
|
26
26
|
<Skeleton />
|
|
27
27
|
</Typography>
|
|
28
|
-
<Typography component="div" variant="h1">
|
|
29
|
-
<Skeleton />
|
|
30
|
-
</Typography>
|
|
31
28
|
</Box>
|
|
32
29
|
</Stack>
|
|
33
30
|
</Fade>
|
package/src/payment/success.tsx
CHANGED
|
@@ -6,6 +6,7 @@ import { joinURL } from 'ufo';
|
|
|
6
6
|
import { usePaymentContext } from '../contexts/payment';
|
|
7
7
|
|
|
8
8
|
type Props = {
|
|
9
|
+
mode: string;
|
|
9
10
|
message: string;
|
|
10
11
|
action: string;
|
|
11
12
|
payee: string;
|
|
@@ -13,7 +14,7 @@ type Props = {
|
|
|
13
14
|
subscriptionId?: string;
|
|
14
15
|
};
|
|
15
16
|
|
|
16
|
-
export default function PaymentSuccess({ message, action, payee, invoiceId, subscriptionId }: Props) {
|
|
17
|
+
export default function PaymentSuccess({ mode, message, action, payee, invoiceId, subscriptionId }: Props) {
|
|
17
18
|
const { t } = useLocaleContext();
|
|
18
19
|
const { prefix } = usePaymentContext();
|
|
19
20
|
let next: any = null;
|
|
@@ -37,7 +38,11 @@ export default function PaymentSuccess({ message, action, payee, invoiceId, subs
|
|
|
37
38
|
|
|
38
39
|
return (
|
|
39
40
|
<Grow in>
|
|
40
|
-
<Stack
|
|
41
|
+
<Stack
|
|
42
|
+
direction="column"
|
|
43
|
+
alignItems="center"
|
|
44
|
+
justifyContent={mode === 'standalone' ? 'center' : 'flex-start'}
|
|
45
|
+
sx={{ height: mode === 'standalone' ? 360 : 300 }}>
|
|
41
46
|
<Div>
|
|
42
47
|
<div className="check-icon">
|
|
43
48
|
<span className="icon-line line-tip" />
|
package/src/payment/summary.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
2
|
-
import type { TLineItemExpanded, TPaymentCurrency } from '@blocklet/payment-types';
|
|
2
|
+
import type { DonationSettings, TLineItemExpanded, TPaymentCurrency } from '@blocklet/payment-types';
|
|
3
3
|
import { InfoOutlined } from '@mui/icons-material';
|
|
4
4
|
import { LoadingButton } from '@mui/lab';
|
|
5
5
|
import { Fade, Grow, Stack, Tooltip, Typography, keyframes } from '@mui/material';
|
|
@@ -12,6 +12,7 @@ import api from '../api';
|
|
|
12
12
|
import Status from '../components/status';
|
|
13
13
|
import { formatAmount, formatCheckoutHeadlines, getPriceUintAmountByCurrency } from '../util';
|
|
14
14
|
import PaymentAmount from './amount';
|
|
15
|
+
import ProductDonation from './product-donation';
|
|
15
16
|
import ProductItem from './product-item';
|
|
16
17
|
|
|
17
18
|
const shake = keyframes`
|
|
@@ -40,10 +41,13 @@ type Props = {
|
|
|
40
41
|
showStaking?: boolean;
|
|
41
42
|
onUpsell?: Function;
|
|
42
43
|
onDownsell?: Function;
|
|
44
|
+
onChangeAmount?: Function;
|
|
43
45
|
onApplyCrossSell?: Function;
|
|
44
46
|
onCancelCrossSell?: Function;
|
|
45
47
|
checkoutSessionId?: string;
|
|
46
48
|
crossSellBehavior?: string;
|
|
49
|
+
donationSettings?: DonationSettings; // only include backend part
|
|
50
|
+
action?: string;
|
|
47
51
|
};
|
|
48
52
|
|
|
49
53
|
async function fetchCrossSell(id: string) {
|
|
@@ -91,9 +95,12 @@ PaymentSummary.defaultProps = {
|
|
|
91
95
|
onDownsell: noop,
|
|
92
96
|
onApplyCrossSell: noop,
|
|
93
97
|
onCancelCrossSell: noop,
|
|
98
|
+
onChangeAmount: noop,
|
|
94
99
|
checkoutSessionId: '',
|
|
95
100
|
crossSellBehavior: '',
|
|
96
101
|
showStaking: false,
|
|
102
|
+
donationSettings: null,
|
|
103
|
+
action: '',
|
|
97
104
|
};
|
|
98
105
|
|
|
99
106
|
export default function PaymentSummary({
|
|
@@ -105,9 +112,12 @@ export default function PaymentSummary({
|
|
|
105
112
|
onDownsell,
|
|
106
113
|
onApplyCrossSell,
|
|
107
114
|
onCancelCrossSell,
|
|
115
|
+
onChangeAmount,
|
|
108
116
|
checkoutSessionId,
|
|
109
117
|
crossSellBehavior,
|
|
110
118
|
showStaking,
|
|
119
|
+
donationSettings,
|
|
120
|
+
action,
|
|
111
121
|
...rest
|
|
112
122
|
}: Props) {
|
|
113
123
|
const { t, locale } = useLocaleContext();
|
|
@@ -166,38 +176,47 @@ export default function PaymentSummary({
|
|
|
166
176
|
return (
|
|
167
177
|
<Fade in>
|
|
168
178
|
<Stack className="cko-product" direction="column" {...rest}>
|
|
169
|
-
<Stack className="cko-product-summary" direction="column" alignItems="flex-start" sx={{ mb:
|
|
179
|
+
<Stack className="cko-product-summary" direction="column" alignItems="flex-start" sx={{ mb: { xs: 0, sm: 3 } }}>
|
|
170
180
|
<Typography sx={{ fontWeight: 500, fontSize: '1.15rem', color: 'text.secondary' }}>
|
|
171
|
-
{headlines.action}
|
|
181
|
+
{action || headlines.action}
|
|
172
182
|
</Typography>
|
|
173
183
|
<PaymentAmount amount={headlines.amount} />
|
|
174
184
|
{headlines.then && (
|
|
175
185
|
<Typography sx={{ fontSize: '0.9rem', color: 'text.secondary' }}>{headlines.then}</Typography>
|
|
176
186
|
)}
|
|
177
187
|
</Stack>
|
|
178
|
-
<Stack spacing={2}>
|
|
179
|
-
{items.map((x: TLineItemExpanded) =>
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
188
|
+
<Stack spacing={{ xs: 1, sm: 2 }}>
|
|
189
|
+
{items.map((x: TLineItemExpanded) =>
|
|
190
|
+
x.price.custom_unit_amount && onChangeAmount && donationSettings ? (
|
|
191
|
+
<ProductDonation
|
|
192
|
+
key={`${x.price_id}-${currency.id}`}
|
|
193
|
+
item={x}
|
|
194
|
+
settings={donationSettings}
|
|
195
|
+
onChange={onChangeAmount}
|
|
196
|
+
/>
|
|
197
|
+
) : (
|
|
198
|
+
<ProductItem
|
|
199
|
+
key={`${x.price_id}-${currency.id}`}
|
|
200
|
+
item={x}
|
|
201
|
+
items={items}
|
|
202
|
+
trialInDays={trialInDays}
|
|
203
|
+
currency={currency}
|
|
204
|
+
onUpsell={handleUpsell}
|
|
205
|
+
onDownsell={handleDownsell}>
|
|
206
|
+
{x.cross_sell && (
|
|
207
|
+
<LoadingButton
|
|
208
|
+
size="small"
|
|
209
|
+
loadingPosition="end"
|
|
210
|
+
color="error"
|
|
211
|
+
variant="text"
|
|
212
|
+
loading={state.loading}
|
|
213
|
+
onClick={handleCancelCrossSell}>
|
|
214
|
+
{t('payment.checkout.cross_sell.remove')}
|
|
215
|
+
</LoadingButton>
|
|
216
|
+
)}
|
|
217
|
+
</ProductItem>
|
|
218
|
+
)
|
|
219
|
+
)}
|
|
201
220
|
</Stack>
|
|
202
221
|
{data && items.some((x) => x.price_id === data.id) === false && (
|
|
203
222
|
<Grow in>
|
|
@@ -210,7 +229,7 @@ export default function PaymentSummary({
|
|
|
210
229
|
borderRadius: 1,
|
|
211
230
|
padding: 1,
|
|
212
231
|
animation: state.shake ? `${shake} 0.2s 5 ease-in-out` : 'none',
|
|
213
|
-
mt:
|
|
232
|
+
mt: { xs: 1, sm: 2 },
|
|
214
233
|
}}>
|
|
215
234
|
<ProductItem
|
|
216
235
|
item={{ quantity: 1, price: data, price_id: data.id, cross_sell: true } as TLineItemExpanded}
|
|
@@ -249,7 +268,7 @@ export default function PaymentSummary({
|
|
|
249
268
|
border: '1px solid #eee',
|
|
250
269
|
borderRadius: 1,
|
|
251
270
|
padding: 1,
|
|
252
|
-
mt:
|
|
271
|
+
mt: { xs: 1, sm: 2 },
|
|
253
272
|
}}>
|
|
254
273
|
<Stack direction="row" alignItems="center" spacing={0.5}>
|
|
255
274
|
<Typography>{t('payment.checkout.staking.title')}</Typography>
|
package/src/types/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ export type CheckoutContext = {
|
|
|
15
15
|
paymentIntent?: TPaymentIntent;
|
|
16
16
|
customer?: TCustomer;
|
|
17
17
|
mode: LiteralUnion<'standalone' | 'inline' | 'popup', string>;
|
|
18
|
+
action?: string;
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
export type CheckoutFormData = {
|
|
@@ -36,6 +37,7 @@ export type CheckoutFormData = {
|
|
|
36
37
|
export type CheckoutProps = Partial<CheckoutCallbacks> & {
|
|
37
38
|
id: string;
|
|
38
39
|
extraParams?: Record<string, any>;
|
|
40
|
+
action?: string;
|
|
39
41
|
mode?: LiteralUnion<'standalone' | 'inline' | 'popup' | 'inline-minimal' | 'popup-minimal', string>;
|
|
40
42
|
};
|
|
41
43
|
|
package/src/util.ts
CHANGED
|
@@ -23,6 +23,10 @@ import { t } from './locales';
|
|
|
23
23
|
|
|
24
24
|
export const PAYMENT_KIT_DID = 'z2qaCNvKMv5GjouKdcDWexv6WqtHbpNPQDnAk';
|
|
25
25
|
|
|
26
|
+
export const isPaymentKitMounted = () => {
|
|
27
|
+
return (window.blocklet?.componentMountPoints || []).some((x: any) => x.did === PAYMENT_KIT_DID);
|
|
28
|
+
};
|
|
29
|
+
|
|
26
30
|
export const getPrefix = () => {
|
|
27
31
|
const componentId = (window?.blocklet?.componentId || '').split('/').pop();
|
|
28
32
|
if (componentId === PAYMENT_KIT_DID) {
|
|
@@ -125,6 +129,10 @@ export const formatPrice = (
|
|
|
125
129
|
bn: boolean = true,
|
|
126
130
|
locale: string = 'en'
|
|
127
131
|
) => {
|
|
132
|
+
if (price.custom_unit_amount) {
|
|
133
|
+
return `Custom (${currency.symbol})`;
|
|
134
|
+
}
|
|
135
|
+
|
|
128
136
|
const unit = getPriceUintAmountByCurrency(price, currency);
|
|
129
137
|
const amount = bn
|
|
130
138
|
? fromUnitToToken(new BN(unit).mul(new BN(quantity)), currency.decimal).toString()
|
|
@@ -219,10 +227,16 @@ export function getPriceUintAmountByCurrency(price: TPrice, currency: TPaymentCu
|
|
|
219
227
|
const options = getPriceCurrencyOptions(price);
|
|
220
228
|
const option = options.find((x) => x.currency_id === currency.id);
|
|
221
229
|
if (option) {
|
|
230
|
+
if (option.custom_unit_amount) {
|
|
231
|
+
return option.custom_unit_amount.preset || option.custom_unit_amount.presets[0];
|
|
232
|
+
}
|
|
222
233
|
return option.unit_amount;
|
|
223
234
|
}
|
|
224
235
|
|
|
225
236
|
if (price.currency_id === currency.id) {
|
|
237
|
+
if (price.custom_unit_amount) {
|
|
238
|
+
return price.custom_unit_amount.preset || price.custom_unit_amount.presets[0];
|
|
239
|
+
}
|
|
226
240
|
return price.unit_amount;
|
|
227
241
|
}
|
|
228
242
|
|
|
@@ -235,7 +249,14 @@ export function getPriceCurrencyOptions(price: TPrice): PriceCurrency[] {
|
|
|
235
249
|
return price.currency_options;
|
|
236
250
|
}
|
|
237
251
|
|
|
238
|
-
return [
|
|
252
|
+
return [
|
|
253
|
+
{
|
|
254
|
+
currency_id: price.currency_id,
|
|
255
|
+
unit_amount: price.unit_amount,
|
|
256
|
+
custom_unit_amount: price.custom_unit_amount || null,
|
|
257
|
+
tiers: null,
|
|
258
|
+
},
|
|
259
|
+
];
|
|
239
260
|
}
|
|
240
261
|
|
|
241
262
|
export function formatLineItemPricing(
|
|
@@ -339,6 +360,20 @@ export function getRefundStatusColor(status: string) {
|
|
|
339
360
|
}
|
|
340
361
|
}
|
|
341
362
|
|
|
363
|
+
export function getPayoutStatusColor(status: string) {
|
|
364
|
+
switch (status) {
|
|
365
|
+
case 'paid':
|
|
366
|
+
return 'success';
|
|
367
|
+
case 'failed':
|
|
368
|
+
return 'warning';
|
|
369
|
+
case 'canceled':
|
|
370
|
+
case 'pending':
|
|
371
|
+
case 'in_transit':
|
|
372
|
+
default:
|
|
373
|
+
return 'default';
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
342
377
|
export function getInvoiceStatusColor(status: string) {
|
|
343
378
|
switch (status) {
|
|
344
379
|
case 'paid':
|
|
@@ -370,6 +405,10 @@ export function getCheckoutAmount(
|
|
|
370
405
|
trialing = false,
|
|
371
406
|
upsell = true
|
|
372
407
|
) {
|
|
408
|
+
if (items.find((x) => (x.upsell_price || x.price).custom_unit_amount) && items.length > 1) {
|
|
409
|
+
throw new Error('Multiple items with custom unit amount are not supported');
|
|
410
|
+
}
|
|
411
|
+
|
|
373
412
|
let renew = new BN(0);
|
|
374
413
|
const total = items
|
|
375
414
|
.filter((x) => {
|
|
@@ -377,9 +416,20 @@ export function getCheckoutAmount(
|
|
|
377
416
|
return price != null;
|
|
378
417
|
})
|
|
379
418
|
.reduce((acc, x) => {
|
|
419
|
+
if (x.custom_amount) {
|
|
420
|
+
return acc.add(new BN(x.custom_amount));
|
|
421
|
+
}
|
|
422
|
+
|
|
380
423
|
const price = upsell ? x.upsell_price || x.price : x.price;
|
|
424
|
+
const unitPrice = getPriceUintAmountByCurrency(price, currency);
|
|
425
|
+
if (price.custom_unit_amount) {
|
|
426
|
+
if (unitPrice) {
|
|
427
|
+
return acc.add(new BN(unitPrice).mul(new BN(x.quantity)));
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
381
431
|
if (price?.type === 'recurring') {
|
|
382
|
-
renew = renew.add(new BN(
|
|
432
|
+
renew = renew.add(new BN(unitPrice).mul(new BN(x.quantity)));
|
|
383
433
|
|
|
384
434
|
if (trialing) {
|
|
385
435
|
return acc;
|
|
@@ -388,7 +438,8 @@ export function getCheckoutAmount(
|
|
|
388
438
|
return acc;
|
|
389
439
|
}
|
|
390
440
|
}
|
|
391
|
-
|
|
441
|
+
|
|
442
|
+
return acc.add(new BN(unitPrice).mul(new BN(x.quantity)));
|
|
392
443
|
}, new BN(0))
|
|
393
444
|
.toString();
|
|
394
445
|
|