@blocklet/payment-react 1.13.210 → 1.13.211
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/index.d.ts +2 -1
- package/es/index.js +2 -0
- 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 +1 -0
- package/es/util.js +44 -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/index.d.ts +2 -1
- package/lib/index.js +8 -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 +1 -0
- package/lib/util.js +39 -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/index.ts +2 -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 +50 -3
package/lib/util.js
CHANGED
|
@@ -24,6 +24,7 @@ exports.formatUpsellSaving = formatUpsellSaving;
|
|
|
24
24
|
exports.getCheckoutAmount = getCheckoutAmount;
|
|
25
25
|
exports.getInvoiceStatusColor = getInvoiceStatusColor;
|
|
26
26
|
exports.getPaymentIntentStatusColor = getPaymentIntentStatusColor;
|
|
27
|
+
exports.getPayoutStatusColor = getPayoutStatusColor;
|
|
27
28
|
exports.getPrefix = void 0;
|
|
28
29
|
exports.getPriceCurrencyOptions = getPriceCurrencyOptions;
|
|
29
30
|
exports.getPriceUintAmountByCurrency = getPriceUintAmountByCurrency;
|
|
@@ -129,6 +130,9 @@ function formatNumber(n, precision = 6, trim = true) {
|
|
|
129
130
|
return trim ? (0, _trimEnd.default)(num.format(options), "0.") : num.format(options);
|
|
130
131
|
}
|
|
131
132
|
const formatPrice = (price, currency, unit_label, quantity = 1, bn = true, locale = "en") => {
|
|
133
|
+
if (price.custom_unit_amount) {
|
|
134
|
+
return `Custom (${currency.symbol})`;
|
|
135
|
+
}
|
|
132
136
|
const unit = getPriceUintAmountByCurrency(price, currency);
|
|
133
137
|
const amount = bn ? (0, _util.fromUnitToToken)(new _util.BN(unit).mul(new _util.BN(quantity)), currency.decimal).toString() : +unit * quantity;
|
|
134
138
|
if (price?.type === "recurring" && price.recurring) {
|
|
@@ -198,9 +202,15 @@ function getPriceUintAmountByCurrency(price, currency) {
|
|
|
198
202
|
const options = getPriceCurrencyOptions(price);
|
|
199
203
|
const option = options.find(x => x.currency_id === currency.id);
|
|
200
204
|
if (option) {
|
|
205
|
+
if (option.custom_unit_amount) {
|
|
206
|
+
return option.custom_unit_amount.preset || option.custom_unit_amount.presets[0];
|
|
207
|
+
}
|
|
201
208
|
return option.unit_amount;
|
|
202
209
|
}
|
|
203
210
|
if (price.currency_id === currency.id) {
|
|
211
|
+
if (price.custom_unit_amount) {
|
|
212
|
+
return price.custom_unit_amount.preset || price.custom_unit_amount.presets[0];
|
|
213
|
+
}
|
|
204
214
|
return price.unit_amount;
|
|
205
215
|
}
|
|
206
216
|
console.warn(`Currency ${currency.id} not configured for price`, price);
|
|
@@ -213,8 +223,8 @@ function getPriceCurrencyOptions(price) {
|
|
|
213
223
|
return [{
|
|
214
224
|
currency_id: price.currency_id,
|
|
215
225
|
unit_amount: price.unit_amount,
|
|
216
|
-
|
|
217
|
-
|
|
226
|
+
custom_unit_amount: price.custom_unit_amount || null,
|
|
227
|
+
tiers: null
|
|
218
228
|
}];
|
|
219
229
|
}
|
|
220
230
|
function formatLineItemPricing(item, currency, trial, locale = "en") {
|
|
@@ -305,6 +315,19 @@ function getRefundStatusColor(status) {
|
|
|
305
315
|
return "default";
|
|
306
316
|
}
|
|
307
317
|
}
|
|
318
|
+
function getPayoutStatusColor(status) {
|
|
319
|
+
switch (status) {
|
|
320
|
+
case "paid":
|
|
321
|
+
return "success";
|
|
322
|
+
case "failed":
|
|
323
|
+
return "warning";
|
|
324
|
+
case "canceled":
|
|
325
|
+
case "pending":
|
|
326
|
+
case "in_transit":
|
|
327
|
+
default:
|
|
328
|
+
return "default";
|
|
329
|
+
}
|
|
330
|
+
}
|
|
308
331
|
function getInvoiceStatusColor(status) {
|
|
309
332
|
switch (status) {
|
|
310
333
|
case "paid":
|
|
@@ -329,14 +352,26 @@ function getWebhookStatusColor(status) {
|
|
|
329
352
|
}
|
|
330
353
|
}
|
|
331
354
|
function getCheckoutAmount(items, currency, trialing = false, upsell = true) {
|
|
355
|
+
if (items.find(x => (x.upsell_price || x.price).custom_unit_amount) && items.length > 1) {
|
|
356
|
+
throw new Error("Multiple items with custom unit amount are not supported");
|
|
357
|
+
}
|
|
332
358
|
let renew = new _util.BN(0);
|
|
333
359
|
const total = items.filter(x => {
|
|
334
360
|
const price = upsell ? x.upsell_price || x.price : x.price;
|
|
335
361
|
return price != null;
|
|
336
362
|
}).reduce((acc, x) => {
|
|
363
|
+
if (x.custom_amount) {
|
|
364
|
+
return acc.add(new _util.BN(x.custom_amount));
|
|
365
|
+
}
|
|
337
366
|
const price = upsell ? x.upsell_price || x.price : x.price;
|
|
367
|
+
const unitPrice = getPriceUintAmountByCurrency(price, currency);
|
|
368
|
+
if (price.custom_unit_amount) {
|
|
369
|
+
if (unitPrice) {
|
|
370
|
+
return acc.add(new _util.BN(unitPrice).mul(new _util.BN(x.quantity)));
|
|
371
|
+
}
|
|
372
|
+
}
|
|
338
373
|
if (price?.type === "recurring") {
|
|
339
|
-
renew = renew.add(new _util.BN(
|
|
374
|
+
renew = renew.add(new _util.BN(unitPrice).mul(new _util.BN(x.quantity)));
|
|
340
375
|
if (trialing) {
|
|
341
376
|
return acc;
|
|
342
377
|
}
|
|
@@ -344,7 +379,7 @@ function getCheckoutAmount(items, currency, trialing = false, upsell = true) {
|
|
|
344
379
|
return acc;
|
|
345
380
|
}
|
|
346
381
|
}
|
|
347
|
-
return acc.add(new _util.BN(
|
|
382
|
+
return acc.add(new _util.BN(unitPrice).mul(new _util.BN(x.quantity)));
|
|
348
383
|
}, new _util.BN(0)).toString();
|
|
349
384
|
return {
|
|
350
385
|
subtotal: total,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blocklet/payment-react",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.211",
|
|
4
4
|
"description": "Reusable react components for payment kit v2",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -52,14 +52,14 @@
|
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@arcblock/did-connect": "^2.9.
|
|
56
|
-
"@arcblock/ux": "^2.9.
|
|
55
|
+
"@arcblock/did-connect": "^2.9.66",
|
|
56
|
+
"@arcblock/ux": "^2.9.66",
|
|
57
57
|
"@mui/icons-material": "^5.15.14",
|
|
58
58
|
"@mui/lab": "^5.0.0-alpha.169",
|
|
59
59
|
"@mui/material": "^5.15.14",
|
|
60
60
|
"@mui/styles": "^5.15.14",
|
|
61
61
|
"@mui/system": "^5.15.14",
|
|
62
|
-
"@ocap/util": "^1.18.
|
|
62
|
+
"@ocap/util": "^1.18.115",
|
|
63
63
|
"@stripe/react-stripe-js": "^2.4.0",
|
|
64
64
|
"@stripe/stripe-js": "^2.4.0",
|
|
65
65
|
"@vitejs/plugin-legacy": "^5.3.2",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"@babel/core": "^7.23.9",
|
|
91
91
|
"@babel/preset-env": "^7.23.9",
|
|
92
92
|
"@babel/preset-react": "^7.23.3",
|
|
93
|
-
"@blocklet/payment-types": "1.13.
|
|
93
|
+
"@blocklet/payment-types": "1.13.211",
|
|
94
94
|
"@storybook/addon-essentials": "^7.6.13",
|
|
95
95
|
"@storybook/addon-interactions": "^7.6.13",
|
|
96
96
|
"@storybook/addon-links": "^7.6.13",
|
|
@@ -119,5 +119,5 @@
|
|
|
119
119
|
"vite-plugin-babel": "^1.2.0",
|
|
120
120
|
"vite-plugin-node-polyfills": "^0.21.0"
|
|
121
121
|
},
|
|
122
|
-
"gitHead": "
|
|
122
|
+
"gitHead": "2fe03028e2284261a1a62260d82a2e79ec45ce4a"
|
|
123
123
|
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import Dialog from '@arcblock/ux/lib/Dialog';
|
|
2
|
+
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
|
+
import type {
|
|
4
|
+
DonationSettings,
|
|
5
|
+
PaymentDetails,
|
|
6
|
+
TCheckoutSessionExpanded,
|
|
7
|
+
TPaymentCurrency,
|
|
8
|
+
TPaymentLink,
|
|
9
|
+
TPaymentMethod,
|
|
10
|
+
} from '@blocklet/payment-types';
|
|
11
|
+
import {
|
|
12
|
+
Alert,
|
|
13
|
+
Avatar,
|
|
14
|
+
AvatarGroup,
|
|
15
|
+
Box,
|
|
16
|
+
Button,
|
|
17
|
+
CircularProgress,
|
|
18
|
+
Hidden,
|
|
19
|
+
Stack,
|
|
20
|
+
Table,
|
|
21
|
+
TableCell,
|
|
22
|
+
TableRow,
|
|
23
|
+
Typography,
|
|
24
|
+
} from '@mui/material';
|
|
25
|
+
import { useRequest, useSetState } from 'ahooks';
|
|
26
|
+
import omit from 'lodash/omit';
|
|
27
|
+
import uniqBy from 'lodash/unionBy';
|
|
28
|
+
import { useEffect } from 'react';
|
|
29
|
+
|
|
30
|
+
import api from '../api';
|
|
31
|
+
import TxLink from '../components/blockchain/tx';
|
|
32
|
+
import { CheckoutProps } from '../types';
|
|
33
|
+
import { formatAmount, formatDateTime, formatError } from '../util';
|
|
34
|
+
import CheckoutForm from './form';
|
|
35
|
+
|
|
36
|
+
export type DonateHistory = {
|
|
37
|
+
supporters: TCheckoutSessionExpanded[];
|
|
38
|
+
currency: TPaymentCurrency;
|
|
39
|
+
method: TPaymentMethod;
|
|
40
|
+
total: number;
|
|
41
|
+
};
|
|
42
|
+
export type DonateProps = Pick<CheckoutProps, 'onPaid' | 'onError'> & {
|
|
43
|
+
settings: DonationSettings;
|
|
44
|
+
livemode?: boolean;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const donationCache: { [key: string]: Promise<TPaymentLink> } = {};
|
|
48
|
+
const createOrUpdateDonation = (settings: DonationSettings, livemode: boolean = true): Promise<TPaymentLink> => {
|
|
49
|
+
if (!donationCache[settings.target]) {
|
|
50
|
+
donationCache[settings.target] = api
|
|
51
|
+
.post(`/api/donations?livemode=${livemode}`, omit(settings, ['appearance']))
|
|
52
|
+
.then((res) => res.data)
|
|
53
|
+
.finally(() => {
|
|
54
|
+
setTimeout(() => {
|
|
55
|
+
delete donationCache[settings.target];
|
|
56
|
+
}, 3000);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return donationCache[settings.target];
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const supporterCache: { [key: string]: Promise<DonateHistory> } = {};
|
|
64
|
+
const fetchSupporters = (target: string): Promise<DonateHistory> => {
|
|
65
|
+
if (!supporterCache[target]) {
|
|
66
|
+
supporterCache[target] = api
|
|
67
|
+
.get(`/api/donations?&target=${target}`)
|
|
68
|
+
.then((res) => res.data)
|
|
69
|
+
.finally(() => {
|
|
70
|
+
setTimeout(() => {
|
|
71
|
+
delete supporterCache[target];
|
|
72
|
+
}, 3000);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return supporterCache[target];
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
80
|
+
function SupporterAvatar({ supporters = [], total, currency, method }: DonateHistory) {
|
|
81
|
+
const { t } = useLocaleContext();
|
|
82
|
+
const customers = uniqBy(supporters, 'customer_did');
|
|
83
|
+
return (
|
|
84
|
+
<Box
|
|
85
|
+
display="flex"
|
|
86
|
+
flexDirection="column"
|
|
87
|
+
alignItems="center"
|
|
88
|
+
sx={{
|
|
89
|
+
'.MuiAvatar-root': {
|
|
90
|
+
width: '32px',
|
|
91
|
+
height: '32px',
|
|
92
|
+
},
|
|
93
|
+
}}
|
|
94
|
+
gap={{
|
|
95
|
+
xs: 0.5,
|
|
96
|
+
sm: 1,
|
|
97
|
+
}}>
|
|
98
|
+
<Typography component="p" color="text.secondary">
|
|
99
|
+
{t('payment.checkout.donation.summary', { total })}
|
|
100
|
+
</Typography>
|
|
101
|
+
<AvatarGroup total={total} max={20}>
|
|
102
|
+
{customers.map((x) => (
|
|
103
|
+
<Avatar
|
|
104
|
+
key={x.id}
|
|
105
|
+
title={x.customer?.name}
|
|
106
|
+
src={`/.well-known/service/user/avatar/${x.customer?.did}?imageFilter=resize&w=48&h=48`}
|
|
107
|
+
variant="circular"
|
|
108
|
+
sx={{ width: 32, height: 32 }}
|
|
109
|
+
/>
|
|
110
|
+
))}
|
|
111
|
+
</AvatarGroup>
|
|
112
|
+
</Box>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
117
|
+
function SupporterTable({ supporters = [], total, currency, method }: DonateHistory) {
|
|
118
|
+
const { t } = useLocaleContext();
|
|
119
|
+
return (
|
|
120
|
+
<Box display="flex" flexDirection="column" alignItems="center" sx={{ width: '100%' }} gap={{ xs: 0.5, sm: 1 }}>
|
|
121
|
+
<Typography component="p" color="text.secondary">
|
|
122
|
+
{t('payment.checkout.donation.summary', { total })}
|
|
123
|
+
</Typography>
|
|
124
|
+
<Table size="small" sx={{ width: '100%', overflow: 'hidden' }}>
|
|
125
|
+
{supporters.map((x) => (
|
|
126
|
+
<TableRow
|
|
127
|
+
key={x.id}
|
|
128
|
+
sx={{
|
|
129
|
+
'> td': { padding: '8px 16px 8px 0', borderTop: '1px solid #e0e0e0', borderBottom: '1px solid #e0e0e0' },
|
|
130
|
+
}}>
|
|
131
|
+
<TableCell>
|
|
132
|
+
<Stack direction="row" alignItems="center" spacing={0.5}>
|
|
133
|
+
<Avatar
|
|
134
|
+
key={x.id}
|
|
135
|
+
src={`/.well-known/service/user/avatar/${x.customer?.did}?imageFilter=resize&w=48&h=48`}
|
|
136
|
+
variant="circular"
|
|
137
|
+
sx={{ width: 24, height: 24 }}
|
|
138
|
+
/>
|
|
139
|
+
<Hidden smDown>
|
|
140
|
+
<Typography>{x.customer?.name}</Typography>
|
|
141
|
+
</Hidden>
|
|
142
|
+
</Stack>
|
|
143
|
+
</TableCell>
|
|
144
|
+
<TableCell align="right">
|
|
145
|
+
<Stack direction="row" alignItems="center" justifyContent="flex-end" spacing={0.5}>
|
|
146
|
+
<Typography fontWeight={500} component="strong">
|
|
147
|
+
{formatAmount(x.amount_total, currency.decimal)}
|
|
148
|
+
</Typography>
|
|
149
|
+
<Typography component="span">{currency.symbol}</Typography>
|
|
150
|
+
</Stack>
|
|
151
|
+
</TableCell>
|
|
152
|
+
<Hidden smDown>
|
|
153
|
+
<TableCell align="right">
|
|
154
|
+
<Typography>{formatDateTime(x.created_at)}</Typography>
|
|
155
|
+
</TableCell>
|
|
156
|
+
</Hidden>
|
|
157
|
+
<TableCell align="right">
|
|
158
|
+
<TxLink method={method} details={x.payment_details as PaymentDetails} mode="customer" align="right" />
|
|
159
|
+
</TableCell>
|
|
160
|
+
</TableRow>
|
|
161
|
+
))}
|
|
162
|
+
</Table>
|
|
163
|
+
</Box>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export default function CheckoutDonate({ settings, livemode, onPaid, onError }: DonateProps) {
|
|
168
|
+
const [state, setState] = useSetState({
|
|
169
|
+
open: false,
|
|
170
|
+
supporterLoaded: false,
|
|
171
|
+
exist: false,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const donation = useRequest(() => createOrUpdateDonation(settings, livemode));
|
|
175
|
+
const supporters = useRequest(() => (donation.data ? fetchSupporters(donation.data.id) : Promise.resolve({})));
|
|
176
|
+
|
|
177
|
+
useEffect(() => {
|
|
178
|
+
if (donation.data && state.supporterLoaded === false) {
|
|
179
|
+
setState({ supporterLoaded: true });
|
|
180
|
+
supporters.runAsync().catch(console.error);
|
|
181
|
+
}
|
|
182
|
+
}, [donation.data]); // eslint-disable-line
|
|
183
|
+
|
|
184
|
+
const handlePaid = (...args: any[]) => {
|
|
185
|
+
if (onPaid) {
|
|
186
|
+
// @ts-ignore
|
|
187
|
+
onPaid(...args);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
supporters.runAsync().catch(console.error);
|
|
191
|
+
|
|
192
|
+
setTimeout(() => {
|
|
193
|
+
setState({ open: false });
|
|
194
|
+
}, 3000);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
if (donation.error) {
|
|
198
|
+
return <Alert severity="error">{formatError(donation.error)}</Alert>;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (donation.loading || !donation.data) {
|
|
202
|
+
return <CircularProgress />;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return (
|
|
206
|
+
<Box
|
|
207
|
+
sx={{ width: '100%', minWidth: 300, maxWidth: 720 }}
|
|
208
|
+
display="flex"
|
|
209
|
+
flexDirection="column"
|
|
210
|
+
alignItems="center"
|
|
211
|
+
gap={{ xs: 1, sm: 2 }}>
|
|
212
|
+
<Button
|
|
213
|
+
size={(settings.appearance?.button?.size || 'medium') as any}
|
|
214
|
+
color={(settings.appearance?.button?.color || 'primary') as any}
|
|
215
|
+
variant={(settings.appearance?.button?.variant || 'contained') as any}
|
|
216
|
+
onClick={() => setState({ open: true })}>
|
|
217
|
+
<Stack direction="row" alignItems="center" spacing={0.5}>
|
|
218
|
+
{settings.appearance.button.icon}
|
|
219
|
+
{typeof settings.appearance.button.text === 'string' ? (
|
|
220
|
+
<Typography>{settings.appearance.button.text}</Typography>
|
|
221
|
+
) : (
|
|
222
|
+
settings.appearance.button.text
|
|
223
|
+
)}
|
|
224
|
+
</Stack>
|
|
225
|
+
</Button>
|
|
226
|
+
{supporters.data && settings.appearance.history.variant === 'avatar' && (
|
|
227
|
+
<SupporterAvatar {...(supporters.data as DonateHistory)} />
|
|
228
|
+
)}
|
|
229
|
+
{supporters.data && settings.appearance.history.variant === 'table' && (
|
|
230
|
+
<SupporterTable {...(supporters.data as DonateHistory)} />
|
|
231
|
+
)}
|
|
232
|
+
<Dialog
|
|
233
|
+
open={state.open}
|
|
234
|
+
title={settings.title}
|
|
235
|
+
maxWidth="md"
|
|
236
|
+
showCloseButton
|
|
237
|
+
disableBackdropClick
|
|
238
|
+
disableEscapeKeyDown
|
|
239
|
+
onClose={(e: any, reason: string) => setState({ open: reason === 'backdropClick' })}>
|
|
240
|
+
<Box sx={{ mb: 1, mt: -2 }}>
|
|
241
|
+
<CheckoutForm
|
|
242
|
+
id={donation.data.id}
|
|
243
|
+
onPaid={handlePaid}
|
|
244
|
+
onError={onError}
|
|
245
|
+
action={settings.appearance?.button?.text}
|
|
246
|
+
mode="inline"
|
|
247
|
+
/>
|
|
248
|
+
</Box>
|
|
249
|
+
</Dialog>
|
|
250
|
+
</Box>
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
CheckoutDonate.defaultProps = {
|
|
255
|
+
livemode: true,
|
|
256
|
+
};
|
package/src/checkout/form.tsx
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { TCheckoutSessionExpanded, TPaymentMethodExpanded } from '@blocklet/payment-types';
|
|
1
|
+
import type { TCheckoutSessionExpanded, TPaymentMethodExpanded } from '@blocklet/payment-types';
|
|
3
2
|
import { useRequest, useSetState } from 'ahooks';
|
|
4
3
|
import noop from 'lodash/noop';
|
|
5
4
|
import { useEffect } from 'react';
|
|
@@ -31,8 +30,16 @@ const fetchCheckoutSession = async (id: string): Promise<CheckoutContext> => {
|
|
|
31
30
|
return data;
|
|
32
31
|
};
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
export default function CheckoutForm({
|
|
34
|
+
id,
|
|
35
|
+
mode,
|
|
36
|
+
onPaid,
|
|
37
|
+
onError,
|
|
38
|
+
onChange,
|
|
39
|
+
goBack,
|
|
40
|
+
extraParams,
|
|
41
|
+
action,
|
|
42
|
+
}: CheckoutProps) {
|
|
36
43
|
if (!id.startsWith('plink_') && !id.startsWith('cs_')) {
|
|
37
44
|
throw new Error('Either a checkoutSession or a paymentLink id is required.');
|
|
38
45
|
}
|
|
@@ -80,6 +87,7 @@ export default function CheckoutForm({ id, mode, onPaid, onError, onChange, goBa
|
|
|
80
87
|
onChange={onChange}
|
|
81
88
|
goBack={goBack}
|
|
82
89
|
mode={mode as string}
|
|
90
|
+
action={action}
|
|
83
91
|
/>
|
|
84
92
|
);
|
|
85
93
|
}
|
|
@@ -88,5 +96,6 @@ CheckoutForm.defaultProps = {
|
|
|
88
96
|
onPaid: noop,
|
|
89
97
|
onError: console.error,
|
|
90
98
|
mode: 'inline',
|
|
99
|
+
action: '',
|
|
91
100
|
extraParams: {},
|
|
92
101
|
};
|
|
@@ -7,12 +7,14 @@ import { getTxLink } from '../../util';
|
|
|
7
7
|
|
|
8
8
|
TxLink.defaultProps = {
|
|
9
9
|
mode: 'dashboard',
|
|
10
|
+
align: 'left',
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
export default function TxLink(props: {
|
|
13
14
|
details: PaymentDetails;
|
|
14
15
|
method: TPaymentMethod;
|
|
15
16
|
mode?: 'customer' | 'dashboard';
|
|
17
|
+
align?: 'left' | 'right';
|
|
16
18
|
}) {
|
|
17
19
|
const { t } = useLocaleContext();
|
|
18
20
|
|
|
@@ -29,7 +31,12 @@ export default function TxLink(props: {
|
|
|
29
31
|
if (link) {
|
|
30
32
|
return (
|
|
31
33
|
<Link href={link} target="_blank" rel="noopener noreferrer">
|
|
32
|
-
<Stack
|
|
34
|
+
<Stack
|
|
35
|
+
component="span"
|
|
36
|
+
direction="row"
|
|
37
|
+
alignItems="center"
|
|
38
|
+
justifyContent={props.align === 'left' ? 'flex-start' : 'flex-end'}
|
|
39
|
+
spacing={1}>
|
|
33
40
|
<Typography component="span" color="primary">
|
|
34
41
|
{text.length > 40 ? [text.slice(0, 8), text.slice(-8)].join('...') : text}
|
|
35
42
|
</Typography>
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import api from './api';
|
|
2
|
+
import CheckoutDonate from './checkout/donate';
|
|
2
3
|
import CheckoutForm from './checkout/form';
|
|
3
4
|
import CheckoutTable from './checkout/table';
|
|
4
5
|
import TxLink from './components/blockchain/tx';
|
|
@@ -39,6 +40,7 @@ export {
|
|
|
39
40
|
ConfirmDialog,
|
|
40
41
|
CheckoutForm,
|
|
41
42
|
CheckoutTable,
|
|
43
|
+
CheckoutDonate,
|
|
42
44
|
CurrencySelector,
|
|
43
45
|
Payment,
|
|
44
46
|
PaymentSummary,
|
package/src/locales/en.tsx
CHANGED
|
@@ -88,6 +88,7 @@ export default flat({
|
|
|
88
88
|
try: 'Try for free',
|
|
89
89
|
include: 'This includes:',
|
|
90
90
|
subscription: 'Subscribe',
|
|
91
|
+
donate: 'Donate',
|
|
91
92
|
select: 'Select',
|
|
92
93
|
selected: 'Selected',
|
|
93
94
|
noPricing: 'No items to purchase',
|
|
@@ -104,6 +105,12 @@ export default flat({
|
|
|
104
105
|
tooltip:
|
|
105
106
|
'Staking is used to ensure that future invoices can be paid normally. Revoking the staking from DID Wallet means canceling the subscription.',
|
|
106
107
|
},
|
|
108
|
+
donation: {
|
|
109
|
+
between: 'Please enter an amount between {min} and {max}.',
|
|
110
|
+
custom: 'Custom Amount',
|
|
111
|
+
select: 'Select Amount',
|
|
112
|
+
summary: '{total} supporters',
|
|
113
|
+
},
|
|
107
114
|
cardPay: '{action} with card',
|
|
108
115
|
empty: 'No thing to pay',
|
|
109
116
|
per: 'per',
|
|
@@ -119,6 +126,7 @@ export default flat({
|
|
|
119
126
|
payment: 'Thanks for your purchase',
|
|
120
127
|
subscription: 'Thanks for your subscribing',
|
|
121
128
|
setup: 'Thanks for your subscribing',
|
|
129
|
+
donate: 'Thanks for your support',
|
|
122
130
|
tip: 'A payment to {payee} has been completed. You can view the details of this payment in your account.',
|
|
123
131
|
},
|
|
124
132
|
confirm:
|
package/src/locales/zh.tsx
CHANGED
|
@@ -88,6 +88,7 @@ export default flat({
|
|
|
88
88
|
try: '免费试用',
|
|
89
89
|
include: '包括:',
|
|
90
90
|
subscription: '订阅',
|
|
91
|
+
donate: '捐赠',
|
|
91
92
|
select: '选择',
|
|
92
93
|
selected: '已选',
|
|
93
94
|
noPricing: '没有可购买的物品',
|
|
@@ -103,6 +104,12 @@ export default flat({
|
|
|
103
104
|
title: '质押数量',
|
|
104
105
|
tooltip: '质押相当于保证金,用于确保未来的账单能够正常扣款,如果你从 DID Wallet 撤销质押,订阅也会被取消。',
|
|
105
106
|
},
|
|
107
|
+
donation: {
|
|
108
|
+
between: '金额必须大于 {min} 且小于 {max}',
|
|
109
|
+
custom: '输入金额',
|
|
110
|
+
select: '选择金额',
|
|
111
|
+
summary: '已经有 {total} 人支持',
|
|
112
|
+
},
|
|
106
113
|
cardPay: '使用卡片{action}',
|
|
107
114
|
empty: '没有可支付的项目',
|
|
108
115
|
per: '每',
|
|
@@ -118,6 +125,7 @@ export default flat({
|
|
|
118
125
|
payment: '感谢您的购买',
|
|
119
126
|
subscription: '感谢您的订阅',
|
|
120
127
|
setup: '感谢您的订阅',
|
|
128
|
+
donate: '感谢您的支持',
|
|
121
129
|
tip: '向{payee}的付款已完成。您可以在您的账户中查看此付款的详细信息。',
|
|
122
130
|
},
|
|
123
131
|
confirm:
|
package/src/payment/error.tsx
CHANGED
|
@@ -4,11 +4,12 @@ type Props = {
|
|
|
4
4
|
title: string;
|
|
5
5
|
description: string;
|
|
6
6
|
button?: string;
|
|
7
|
+
mode?: string;
|
|
7
8
|
};
|
|
8
9
|
|
|
9
|
-
export default function PaymentError({ title, description, button }: Props) {
|
|
10
|
+
export default function PaymentError({ title, description, button, mode }: Props) {
|
|
10
11
|
return (
|
|
11
|
-
<Stack sx={{ height: '100vh' }} alignItems="center" justifyContent="center">
|
|
12
|
+
<Stack sx={{ height: mode === 'inline' ? 'auto' : '100vh' }} alignItems="center" justifyContent="center">
|
|
12
13
|
<Stack sx={{ width: '280px' }} direction="column" alignItems="center" justifyContent="center">
|
|
13
14
|
<Typography variant="h5" sx={{ mb: 2 }}>
|
|
14
15
|
{title}
|
|
@@ -26,4 +27,5 @@ export default function PaymentError({ title, description, button }: Props) {
|
|
|
26
27
|
|
|
27
28
|
PaymentError.defaultProps = {
|
|
28
29
|
button: 'Back',
|
|
30
|
+
mode: 'standalone',
|
|
29
31
|
};
|
|
@@ -11,6 +11,7 @@ type Props = {
|
|
|
11
11
|
export default function CurrencySelector({ value, currencies, onChange }: Props) {
|
|
12
12
|
return (
|
|
13
13
|
<Root
|
|
14
|
+
count={currencies.length}
|
|
14
15
|
style={{
|
|
15
16
|
display: currencies.length > 1 ? 'grid' : 'block',
|
|
16
17
|
gridTemplateColumns: '50% 50%',
|
|
@@ -42,12 +43,12 @@ export default function CurrencySelector({ value, currencies, onChange }: Props)
|
|
|
42
43
|
);
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
const Root = styled('section')`
|
|
46
|
+
const Root = styled<any>('section')`
|
|
46
47
|
.cko-payment-card {
|
|
47
48
|
position: relative;
|
|
48
|
-
border:
|
|
49
|
-
padding:
|
|
50
|
-
margin:
|
|
49
|
+
border: 1px solid ${(props) => props.theme.palette.primary.main};
|
|
50
|
+
padding: 4px 8px;
|
|
51
|
+
margin: 8px 0 0;
|
|
51
52
|
cursor: pointer;
|
|
52
53
|
}
|
|
53
54
|
|
|
@@ -62,18 +63,15 @@ const Root = styled('section')`
|
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
.cko-payment-card-unselect {
|
|
65
|
-
border:
|
|
66
|
-
padding:
|
|
67
|
-
margin:
|
|
66
|
+
border: 1px solid #ddd;
|
|
67
|
+
padding: 4px 8px;
|
|
68
|
+
margin: 8px 0 0;
|
|
68
69
|
cursor: pointer;
|
|
69
70
|
}
|
|
70
71
|
|
|
71
|
-
.cko-payment-card:nth-child(odd)
|
|
72
|
-
margin-right: 8px;
|
|
73
|
-
}
|
|
74
|
-
|
|
72
|
+
.cko-payment-card:nth-child(odd),
|
|
75
73
|
.cko-payment-card-unselect:nth-child(odd) {
|
|
76
|
-
margin-right:
|
|
74
|
+
margin-right: ${(props) => (props.count > 1 ? 8 : 0)}px;
|
|
77
75
|
}
|
|
78
76
|
|
|
79
77
|
.cko-payment-card::after {
|
|
@@ -83,7 +81,7 @@ const Root = styled('section')`
|
|
|
83
81
|
position: absolute;
|
|
84
82
|
right: 3px;
|
|
85
83
|
bottom: 3px;
|
|
86
|
-
border:
|
|
84
|
+
border: 1px solid #fff;
|
|
87
85
|
border-top-color: transparent;
|
|
88
86
|
border-left-color: transparent;
|
|
89
87
|
transform: rotate(35deg);
|
|
@@ -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"
|