payment-kit 1.13.17 → 1.13.19
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/README.md +14 -0
- package/api/src/index.ts +17 -6
- package/api/src/integrations/stripe/handlers/index.ts +53 -0
- package/api/src/integrations/stripe/handlers/invoice.ts +252 -0
- package/api/src/integrations/stripe/handlers/payment-intent.ts +172 -0
- package/api/src/integrations/stripe/handlers/setup-intent.ts +42 -0
- package/api/src/integrations/stripe/handlers/subscription.ts +61 -0
- package/api/src/integrations/stripe/resource.ts +317 -0
- package/api/src/integrations/stripe/setup.ts +50 -0
- package/api/src/jobs/invoice.ts +11 -0
- package/api/src/jobs/payment.ts +15 -7
- package/api/src/jobs/subscription.ts +18 -2
- package/api/src/libs/session.ts +104 -8
- package/api/src/libs/util.ts +47 -1
- package/api/src/routes/checkout-sessions.ts +134 -27
- package/api/src/routes/connect/collect.ts +12 -4
- package/api/src/routes/connect/pay.ts +30 -20
- package/api/src/routes/connect/setup.ts +12 -4
- package/api/src/routes/connect/shared.ts +28 -4
- package/api/src/routes/connect/subscribe.ts +12 -5
- package/api/src/routes/customers.ts +5 -5
- package/api/src/routes/events.ts +9 -6
- package/api/src/routes/index.ts +2 -0
- package/api/src/routes/integrations/stripe.ts +64 -0
- package/api/src/routes/invoices.ts +19 -9
- package/api/src/routes/payment-intents.ts +19 -9
- package/api/src/routes/payment-links.ts +57 -15
- package/api/src/routes/payment-methods.ts +98 -1
- package/api/src/routes/prices.ts +71 -14
- package/api/src/routes/products.ts +79 -22
- package/api/src/routes/settings.ts +10 -11
- package/api/src/routes/subscription-items.ts +5 -5
- package/api/src/routes/subscriptions.ts +61 -10
- package/api/src/routes/usage-records.ts +52 -18
- package/api/src/routes/webhook-attempts.ts +5 -5
- package/api/src/routes/webhook-endpoints.ts +5 -5
- package/api/src/store/migrations/20230905-genesis.ts +2 -2
- package/api/src/store/migrations/20230911-seeding.ts +4 -3
- package/api/src/store/models/checkout-session.ts +15 -7
- package/api/src/store/models/index.ts +31 -7
- package/api/src/store/models/invoice.ts +1 -1
- package/api/src/store/models/payment-intent.ts +2 -5
- package/api/src/store/models/payment-link.ts +1 -1
- package/api/src/store/models/payment-method.ts +54 -33
- package/api/src/store/models/price.ts +52 -17
- package/api/src/store/models/product.ts +0 -3
- package/api/src/store/models/subscription.ts +3 -5
- package/api/src/store/models/types.ts +56 -2
- package/api/third.d.ts +2 -0
- package/blocklet.yml +1 -1
- package/package.json +36 -29
- package/public/currencies/dai.png +0 -0
- package/public/currencies/dollar.png +0 -0
- package/public/currencies/usdc.png +0 -0
- package/public/currencies/usdt.png +0 -0
- package/public/methods/arcblock.png +0 -0
- package/public/methods/binance.png +0 -0
- package/public/methods/coinbase.png +0 -0
- package/public/methods/ethereum.jpg +0 -0
- package/public/methods/stripe.png +0 -0
- package/src/components/checkout/form/address.tsx +86 -10
- package/src/components/checkout/form/index.tsx +169 -83
- package/src/components/checkout/form/phone.tsx +96 -0
- package/src/components/checkout/form/stripe.tsx +195 -0
- package/src/components/checkout/pay.tsx +115 -34
- package/src/components/checkout/product-item.tsx +4 -3
- package/src/components/checkout/summary.tsx +5 -4
- package/src/components/drawer-form.tsx +4 -4
- package/src/components/input.tsx +22 -4
- package/src/components/invoice/table.tsx +8 -3
- package/src/components/payment-link/before-pay.tsx +11 -6
- package/src/components/payment-link/chrome.tsx +13 -0
- package/src/components/payment-link/preview.tsx +31 -0
- package/src/components/payment-link/product-select.tsx +8 -3
- package/src/components/payment-method/arcblock.tsx +53 -0
- package/src/components/payment-method/bitcoin.tsx +53 -0
- package/src/components/payment-method/ethereum.tsx +53 -0
- package/src/components/payment-method/form.tsx +54 -0
- package/src/components/payment-method/stripe.tsx +45 -0
- package/src/components/portal/invoice/list.tsx +1 -1
- package/src/components/portal/subscription/list.tsx +1 -1
- package/src/components/price/currency-select.tsx +53 -0
- package/src/components/price/form.tsx +118 -24
- package/src/components/product/add-price.tsx +1 -1
- package/src/components/product/edit-price.tsx +6 -2
- package/src/components/subscription/items/index.tsx +7 -6
- package/src/components/subscription/items/usage-records.tsx +98 -0
- package/src/components/subscription/list.tsx +3 -2
- package/src/components/subscription/status.tsx +68 -0
- package/src/contexts/settings.tsx +2 -2
- package/src/env.d.ts +2 -0
- package/src/libs/util.ts +116 -21
- package/src/locales/en.tsx +71 -3
- package/src/pages/admin/billing/invoices/detail.tsx +5 -2
- package/src/pages/admin/billing/subscriptions/detail.tsx +6 -6
- package/src/pages/admin/customers/customers/detail.tsx +13 -1
- package/src/pages/admin/payments/intents/detail.tsx +8 -3
- package/src/pages/admin/payments/links/create.tsx +23 -3
- package/src/pages/admin/payments/links/detail.tsx +13 -26
- package/src/pages/admin/products/prices/detail.tsx +55 -11
- package/src/pages/admin/products/prices/list.tsx +7 -1
- package/src/pages/admin/products/products/create.tsx +1 -1
- package/src/pages/admin/products/products/detail.tsx +14 -7
- package/src/pages/admin/settings/index.tsx +16 -6
- package/src/pages/admin/settings/payment-methods/create.tsx +81 -0
- package/src/pages/admin/settings/{payment-methods.tsx → payment-methods/index.tsx} +9 -6
- package/src/pages/checkout/pay.tsx +3 -1
- package/src/pages/customer/index.tsx +12 -1
- package/public/.gitkeep +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* eslint-disable react/no-unstable-nested-components */
|
|
2
|
+
import DidAddress from '@arcblock/ux/lib/DID';
|
|
2
3
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
4
|
import Toast from '@arcblock/ux/lib/Toast';
|
|
4
5
|
import type { TCustomerExpanded } from '@did-pay/types';
|
|
@@ -7,6 +8,7 @@ import { Alert, Box, Button, CircularProgress, Stack, Typography } from '@mui/ma
|
|
|
7
8
|
import { styled } from '@mui/system';
|
|
8
9
|
import { useRequest, useSetState } from 'ahooks';
|
|
9
10
|
import { isEmpty } from 'lodash';
|
|
11
|
+
import { FlagEmoji } from 'react-international-phone';
|
|
10
12
|
import { Link, useNavigate } from 'react-router-dom';
|
|
11
13
|
|
|
12
14
|
import Copyable from '../../../../components/copyable';
|
|
@@ -126,6 +128,7 @@ export default function CustomerDetail(props: { id: string }) {
|
|
|
126
128
|
</Button>
|
|
127
129
|
</SectionHeader>
|
|
128
130
|
<Stack>
|
|
131
|
+
<InfoRow label={t('common.did')} value={<DidAddress did={data.did} />} />
|
|
129
132
|
<InfoRow label={t('admin.customer.name')} value={data.name} />
|
|
130
133
|
<InfoRow label={t('admin.customer.phone')} value={data.phone} />
|
|
131
134
|
<InfoRow label={t('admin.customer.email')} value={data.email} />
|
|
@@ -136,7 +139,16 @@ export default function CustomerDetail(props: { id: string }) {
|
|
|
136
139
|
label={t('admin.customer.address.label')}
|
|
137
140
|
value={
|
|
138
141
|
<Stack direction="column">
|
|
139
|
-
<InfoRow
|
|
142
|
+
<InfoRow
|
|
143
|
+
label={t('admin.customer.address.country')}
|
|
144
|
+
value={
|
|
145
|
+
data.address?.country ? (
|
|
146
|
+
<FlagEmoji iso2={data.address?.country} style={{ display: 'flex', width: 24 }} />
|
|
147
|
+
) : (
|
|
148
|
+
''
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
/>
|
|
140
152
|
<InfoRow label={t('admin.customer.address.state')} value={data.address?.state} />
|
|
141
153
|
<InfoRow label={t('admin.customer.address.city')} value={data.address?.city} />
|
|
142
154
|
<InfoRow label={t('admin.customer.address.line1')} value={data.address?.line1} />
|
|
@@ -127,12 +127,12 @@ export default function PaymentIntentDetail(props: { id: string }) {
|
|
|
127
127
|
</Stack>
|
|
128
128
|
</Box>
|
|
129
129
|
<Box className="section">
|
|
130
|
-
<SectionHeader title={t('admin.paymentMethod.
|
|
130
|
+
<SectionHeader title={t('admin.paymentMethod._name')} />
|
|
131
131
|
<Stack>
|
|
132
132
|
<InfoRow label={t('common.id')} value={data.paymentMethod.id} />
|
|
133
133
|
<InfoRow label={t('admin.paymentMethod.type')} value={data.paymentMethod.type} />
|
|
134
134
|
<InfoRow
|
|
135
|
-
label={t('admin.paymentMethod.
|
|
135
|
+
label={t('admin.paymentMethod._name')}
|
|
136
136
|
value={<Currency logo={data.paymentMethod.logo} name={data.paymentMethod.name} />}
|
|
137
137
|
/>
|
|
138
138
|
<InfoRow
|
|
@@ -141,7 +141,12 @@ export default function PaymentIntentDetail(props: { id: string }) {
|
|
|
141
141
|
/>
|
|
142
142
|
<InfoRow
|
|
143
143
|
label={t('common.txHash')}
|
|
144
|
-
value={
|
|
144
|
+
value={
|
|
145
|
+
<TxLink
|
|
146
|
+
hash={data.payment_details?.arcblock?.tx_hash || data.metadata?.txHash}
|
|
147
|
+
method={data.paymentMethod}
|
|
148
|
+
/>
|
|
149
|
+
}
|
|
145
150
|
/>
|
|
146
151
|
</Stack>
|
|
147
152
|
</Box>
|
|
@@ -5,14 +5,16 @@ import Toast from '@arcblock/ux/lib/Toast';
|
|
|
5
5
|
import type { InferFormType, TPaymentLink } from '@did-pay/types';
|
|
6
6
|
import { AddOutlined } from '@mui/icons-material';
|
|
7
7
|
import { Button, Stack, Typography } from '@mui/material';
|
|
8
|
-
import { useState } from 'react';
|
|
8
|
+
import { useEffect, useState } from 'react';
|
|
9
9
|
import { FormProvider, useForm } from 'react-hook-form';
|
|
10
10
|
import { dispatch } from 'use-bus';
|
|
11
11
|
|
|
12
12
|
import DrawerForm from '../../../../components/drawer-form';
|
|
13
13
|
import AfterPay from '../../../../components/payment-link/after-pay';
|
|
14
14
|
import BeforePay from '../../../../components/payment-link/before-pay';
|
|
15
|
+
import PaymentLinkPreview from '../../../../components/payment-link/preview';
|
|
15
16
|
import { ProductsProvider } from '../../../../contexts/products';
|
|
17
|
+
import { useSessionContext } from '../../../../contexts/session';
|
|
16
18
|
import { useSettingsContext } from '../../../../contexts/settings';
|
|
17
19
|
import api from '../../../../libs/api';
|
|
18
20
|
import { formatError } from '../../../../libs/util';
|
|
@@ -23,7 +25,9 @@ type PaymentLink = InferFormType<TPaymentLink> & {
|
|
|
23
25
|
|
|
24
26
|
export default function CreatePaymentLink() {
|
|
25
27
|
const { t } = useLocaleContext();
|
|
28
|
+
const { session } = useSessionContext();
|
|
26
29
|
const [current, setCurrent] = useState('beforePay');
|
|
30
|
+
const [stashed, setStashed] = useState(0);
|
|
27
31
|
const { settings } = useSettingsContext();
|
|
28
32
|
|
|
29
33
|
const methods = useForm<PaymentLink>({
|
|
@@ -65,6 +69,22 @@ export default function CreatePaymentLink() {
|
|
|
65
69
|
},
|
|
66
70
|
});
|
|
67
71
|
|
|
72
|
+
const changes = methods.watch([
|
|
73
|
+
'line_items',
|
|
74
|
+
'allow_promotion_codes',
|
|
75
|
+
'include_free_trial',
|
|
76
|
+
'subscription_data',
|
|
77
|
+
'billing_address_collection',
|
|
78
|
+
'phone_number_collection',
|
|
79
|
+
]);
|
|
80
|
+
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
api.post('/api/payment-links/stash', methods.getValues()).then(() => {
|
|
83
|
+
setStashed(stashed + 1);
|
|
84
|
+
});
|
|
85
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
86
|
+
}, [JSON.stringify(changes)]);
|
|
87
|
+
|
|
68
88
|
const tabs = [
|
|
69
89
|
{ label: t('admin.paymentLink.beforePay'), value: 'beforePay', component: BeforePay },
|
|
70
90
|
{ label: t('admin.paymentLink.afterPay'), value: 'afterPay', component: AfterPay },
|
|
@@ -104,7 +124,7 @@ export default function CreatePaymentLink() {
|
|
|
104
124
|
<DrawerForm
|
|
105
125
|
icon={<AddOutlined />}
|
|
106
126
|
text={t('admin.paymentLink.add')}
|
|
107
|
-
|
|
127
|
+
width={1280}
|
|
108
128
|
addons={
|
|
109
129
|
// @ts-ignore
|
|
110
130
|
<Button variant="contained" size="small" onClick={methods.handleSubmit(onSubmit)}>
|
|
@@ -132,7 +152,7 @@ export default function CreatePaymentLink() {
|
|
|
132
152
|
<Typography variant="h6" sx={{ mb: 2, fontWeight: 600 }}>
|
|
133
153
|
{t('common.preview')}
|
|
134
154
|
</Typography>
|
|
135
|
-
<
|
|
155
|
+
{stashed && <PaymentLinkPreview id={`plink_${session.user.did}`} version={stashed} />}
|
|
136
156
|
</Stack>
|
|
137
157
|
</Stack>
|
|
138
158
|
</FormProvider>
|
|
@@ -6,7 +6,6 @@ import { ArrowBackOutlined, Edit } from '@mui/icons-material';
|
|
|
6
6
|
import { Alert, Box, Button, CircularProgress, Grid, Stack, Typography } from '@mui/material';
|
|
7
7
|
import { styled } from '@mui/system';
|
|
8
8
|
import { useRequest, useSetState } from 'ahooks';
|
|
9
|
-
import IframeResizer from 'iframe-resizer-react';
|
|
10
9
|
import { isEmpty } from 'lodash';
|
|
11
10
|
import { Link, useNavigate } from 'react-router-dom';
|
|
12
11
|
import { joinURL } from 'ufo';
|
|
@@ -17,6 +16,7 @@ import InfoCard from '../../../../components/info-card';
|
|
|
17
16
|
import InfoRow from '../../../../components/info-row';
|
|
18
17
|
import MetadataEditor from '../../../../components/metadata/editor';
|
|
19
18
|
import PaymentLinkActions from '../../../../components/payment-link/actions';
|
|
19
|
+
import PaymentLinkPreview from '../../../../components/payment-link/preview';
|
|
20
20
|
import AddPrice from '../../../../components/product/add-price';
|
|
21
21
|
import SectionHeader from '../../../../components/section/header';
|
|
22
22
|
import Table from '../../../../components/table';
|
|
@@ -216,24 +216,32 @@ export default function PaymentLinkDetail(props: { id: string }) {
|
|
|
216
216
|
</SectionHeader>
|
|
217
217
|
<Stack>
|
|
218
218
|
<InfoRow
|
|
219
|
+
sizes={[1, 1]}
|
|
219
220
|
label={t('admin.paymentLink.allowPromotionCodes')}
|
|
220
221
|
value={data.allow_promotion_codes ? 'Yes' : 'No'}
|
|
221
222
|
/>
|
|
222
223
|
<InfoRow
|
|
224
|
+
sizes={[1, 1]}
|
|
223
225
|
label={t('admin.paymentLink.requireBillingAddress')}
|
|
224
226
|
value={data.billing_address_collection ? 'Yes' : 'No'}
|
|
225
227
|
/>
|
|
226
228
|
<InfoRow
|
|
229
|
+
sizes={[1, 1]}
|
|
227
230
|
label={t('admin.paymentLink.requirePhoneNumber')}
|
|
228
231
|
value={data.phone_number_collection?.enabled ? 'Yes' : 'No'}
|
|
229
232
|
/>
|
|
230
233
|
<InfoRow
|
|
234
|
+
sizes={[1, 1]}
|
|
231
235
|
label={t('admin.paymentLink.includeFreeTrail')}
|
|
232
236
|
value={data.subscription_data?.trial_period_days ? 'Yes' : 'No'}
|
|
233
237
|
/>
|
|
234
|
-
<InfoRow
|
|
235
|
-
|
|
236
|
-
|
|
238
|
+
<InfoRow
|
|
239
|
+
sizes={[1, 1]}
|
|
240
|
+
label={t('admin.paymentLink.showConfirmPage')}
|
|
241
|
+
value={data.after_completion?.type}
|
|
242
|
+
/>
|
|
243
|
+
<InfoRow sizes={[1, 1]} label={t('common.createdAt')} value={formatTime(data.created_at)} />
|
|
244
|
+
<InfoRow sizes={[1, 1]} label={t('common.updatedAt')} value={formatTime(data.updated_at)} />
|
|
237
245
|
</Stack>
|
|
238
246
|
</Box>
|
|
239
247
|
<Box className="section">
|
|
@@ -285,19 +293,7 @@ export default function PaymentLinkDetail(props: { id: string }) {
|
|
|
285
293
|
<Box className="section">
|
|
286
294
|
<SectionHeader title={t('common.preview')} />
|
|
287
295
|
<Box className="section-body">
|
|
288
|
-
<
|
|
289
|
-
<IframeResizer
|
|
290
|
-
style={{
|
|
291
|
-
width: '1px',
|
|
292
|
-
minWidth: '840px',
|
|
293
|
-
minHeight: '840px',
|
|
294
|
-
transform: 'scale(0.65)',
|
|
295
|
-
transformOrigin: '20% 10%',
|
|
296
|
-
border: 'none',
|
|
297
|
-
}}
|
|
298
|
-
src={`${window.blocklet.prefix}checkout/pay/${data.id}?preview=1`}
|
|
299
|
-
/>
|
|
300
|
-
</Chrome>
|
|
296
|
+
<PaymentLinkPreview id={data.id} />
|
|
301
297
|
</Box>
|
|
302
298
|
</Box>
|
|
303
299
|
</Div>
|
|
@@ -307,12 +303,3 @@ export default function PaymentLinkDetail(props: { id: string }) {
|
|
|
307
303
|
}
|
|
308
304
|
|
|
309
305
|
const Div = styled(Stack)``;
|
|
310
|
-
|
|
311
|
-
const Chrome = styled(Box)`
|
|
312
|
-
background-color: #fcfeff;
|
|
313
|
-
border-radius: 8px;
|
|
314
|
-
margin-top: 40px;
|
|
315
|
-
position: relative;
|
|
316
|
-
overflow: hidden;
|
|
317
|
-
box-shadow: 0 20px 44px #32325d1f, 0 -1px 32px #32325d0f, 0 3px 12px #00000014;
|
|
318
|
-
`;
|
|
@@ -16,6 +16,7 @@ import InfoRow from '../../../../components/info-row';
|
|
|
16
16
|
import MetadataEditor from '../../../../components/metadata/editor';
|
|
17
17
|
import EditPrice from '../../../../components/product/edit-price';
|
|
18
18
|
import SectionHeader from '../../../../components/section/header';
|
|
19
|
+
import Table from '../../../../components/table';
|
|
19
20
|
import api from '../../../../libs/api';
|
|
20
21
|
import { formatError, formatPrice, formatTime } from '../../../../libs/util';
|
|
21
22
|
import PriceActions from './actions';
|
|
@@ -77,15 +78,20 @@ export default function PriceDetail(props: { id: string }) {
|
|
|
77
78
|
|
|
78
79
|
return (
|
|
79
80
|
<Root direction="column" spacing={4} sx={{ mb: 4 }}>
|
|
80
|
-
{data.active === false && (
|
|
81
|
-
<Alert severity="warning">
|
|
82
|
-
<AlertTitle>{t('admin.price.archived')}</AlertTitle>
|
|
83
|
-
{t('admin.price.archivedTip')}
|
|
84
|
-
</Alert>
|
|
85
|
-
)}
|
|
86
81
|
<Box>
|
|
82
|
+
{data.active === false && (
|
|
83
|
+
<Alert severity="warning">
|
|
84
|
+
<AlertTitle>{t('admin.price.archived')}</AlertTitle>
|
|
85
|
+
{t('admin.price.archivedTip')}
|
|
86
|
+
</Alert>
|
|
87
|
+
)}
|
|
88
|
+
{data.locked && (
|
|
89
|
+
<Alert severity="warning" sx={{ mb: 1 }}>
|
|
90
|
+
{t('admin.price.locked')}
|
|
91
|
+
</Alert>
|
|
92
|
+
)}
|
|
87
93
|
<Stack className="page-header" direction="row" justifyContent="space-between" alignItems="center">
|
|
88
|
-
<Link to=
|
|
94
|
+
<Link to={`/admin/products/${data.product_id}`}>
|
|
89
95
|
<Stack direction="row" alignItems="center" sx={{ fontWeight: 'normal' }}>
|
|
90
96
|
<ArrowBackOutlined fontSize="small" sx={{ mr: 0.5, color: 'text.secondary' }} />
|
|
91
97
|
<Typography variant="h6" sx={{ color: 'text.secondary', fontWeight: 'normal' }}>
|
|
@@ -137,10 +143,6 @@ export default function PriceDetail(props: { id: string }) {
|
|
|
137
143
|
<InfoRow label={t('admin.price.recurring.interval')} value={data.recurring?.interval} />
|
|
138
144
|
<InfoRow label={t('admin.price.nickname.label')} value={data.nickname} />
|
|
139
145
|
<InfoRow label={t('admin.price.lookupKey')} value={data.lookup_key} />
|
|
140
|
-
<InfoRow
|
|
141
|
-
label={t('admin.paymentCurrency.name')}
|
|
142
|
-
value={<Currency logo={data.currency.logo} name={data.currency.symbol} />}
|
|
143
|
-
/>
|
|
144
146
|
<InfoRow label={t('common.createdAt')} value={formatTime(data.created_at)} />
|
|
145
147
|
<InfoRow label={t('common.updatedAt')} value={formatTime(data.updated_at)} />
|
|
146
148
|
</Grid>
|
|
@@ -190,6 +192,48 @@ export default function PriceDetail(props: { id: string }) {
|
|
|
190
192
|
)}
|
|
191
193
|
</Box>
|
|
192
194
|
</Box>
|
|
195
|
+
<Box className="section">
|
|
196
|
+
<SectionHeader title={t('admin.price.currency.list')} />
|
|
197
|
+
<Box className="section-body">
|
|
198
|
+
<Table
|
|
199
|
+
toolbar={false}
|
|
200
|
+
footer={false}
|
|
201
|
+
data={data.currency_options}
|
|
202
|
+
columns={[
|
|
203
|
+
{
|
|
204
|
+
label: t('admin.price.name'),
|
|
205
|
+
name: 'currency_id',
|
|
206
|
+
width: 200,
|
|
207
|
+
options: {
|
|
208
|
+
// eslint-disable-next-line react/no-unstable-nested-components
|
|
209
|
+
customBodyRenderLite: (_: any, index: number) => {
|
|
210
|
+
const item = data.currency_options[index] as any;
|
|
211
|
+
return <Currency logo={item.currency.logo} name={item.currency.symbol} />;
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
label: t('common.id'),
|
|
217
|
+
name: 'unit_amount',
|
|
218
|
+
options: {
|
|
219
|
+
customBodyRenderLite: (_: any, index: number) => {
|
|
220
|
+
const item = data.currency_options[index] as any;
|
|
221
|
+
return formatPrice(
|
|
222
|
+
{ type: data.type, unit_amount: item.unit_amount, recurring: data.recurring } as TPrice,
|
|
223
|
+
item.currency
|
|
224
|
+
);
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
]}
|
|
229
|
+
options={{
|
|
230
|
+
count: data.currency_options.length,
|
|
231
|
+
page: 0,
|
|
232
|
+
rowsPerPage: 20,
|
|
233
|
+
}}
|
|
234
|
+
/>
|
|
235
|
+
</Box>
|
|
236
|
+
</Box>
|
|
193
237
|
<Box className="section">
|
|
194
238
|
<SectionHeader title={t('admin.events')} />
|
|
195
239
|
<Box className="section-body">
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint-disable react/no-unstable-nested-components */
|
|
2
2
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
|
-
import {
|
|
3
|
+
import { LockOutlined } from '@mui/icons-material';
|
|
4
|
+
import { Stack, Tooltip, Typography } from '@mui/material';
|
|
4
5
|
import { Link } from 'react-router-dom';
|
|
5
6
|
|
|
6
7
|
import Copyable from '../../../../components/copyable';
|
|
@@ -26,6 +27,11 @@ export default function PricesList({ product, onChange }: { product: Product; on
|
|
|
26
27
|
return (
|
|
27
28
|
<Link to={`/admin/products/${price.id}`} color="text.primary">
|
|
28
29
|
<Stack direction="row" alignItems="center" spacing={1}>
|
|
30
|
+
{price.locked && (
|
|
31
|
+
<Tooltip title={t('admin.price.locked')}>
|
|
32
|
+
<LockOutlined sx={{ color: 'text.secondary' }} />
|
|
33
|
+
</Tooltip>
|
|
34
|
+
)}
|
|
29
35
|
<Typography component="span">{formatPrice(price, settings.baseCurrency)}</Typography>
|
|
30
36
|
<Typography component="span">
|
|
31
37
|
{price.id === product.default_price_id && <Status label="default" color="info" sx={{ height: 18 }} />}
|
|
@@ -58,7 +58,7 @@ export default function ProductsCreate() {
|
|
|
58
58
|
<DrawerForm
|
|
59
59
|
icon={<AddOutlined />}
|
|
60
60
|
text={t('admin.product.add')}
|
|
61
|
-
|
|
61
|
+
width={640}
|
|
62
62
|
addons={
|
|
63
63
|
<Button variant="contained" size="small" onClick={handleSubmit(onSubmit)}>
|
|
64
64
|
{t('admin.product.save')}
|
|
@@ -94,15 +94,22 @@ export default function ProductDetail(props: { id: string }) {
|
|
|
94
94
|
}
|
|
95
95
|
};
|
|
96
96
|
|
|
97
|
+
const isLocked = data.locked || data.prices.some((x) => x.locked);
|
|
98
|
+
|
|
97
99
|
return (
|
|
98
100
|
<Root direction="column" spacing={4} sx={{ mb: 4 }}>
|
|
99
|
-
{data.active === false && (
|
|
100
|
-
<Alert severity="warning">
|
|
101
|
-
<AlertTitle>{t('admin.product.archived')}</AlertTitle>
|
|
102
|
-
{t('admin.product.archivedTip')}
|
|
103
|
-
</Alert>
|
|
104
|
-
)}
|
|
105
101
|
<Box>
|
|
102
|
+
{data.active === false && (
|
|
103
|
+
<Alert severity="warning">
|
|
104
|
+
<AlertTitle>{t('admin.product.archived')}</AlertTitle>
|
|
105
|
+
{t('admin.product.archivedTip')}
|
|
106
|
+
</Alert>
|
|
107
|
+
)}
|
|
108
|
+
{isLocked && (
|
|
109
|
+
<Alert severity="warning" sx={{ mb: 1 }}>
|
|
110
|
+
{t('admin.product.locked')}
|
|
111
|
+
</Alert>
|
|
112
|
+
)}
|
|
106
113
|
<Stack className="page-header" direction="row" justifyContent="space-between" alignItems="center">
|
|
107
114
|
<Link to="/admin/products">
|
|
108
115
|
<Stack direction="row" alignItems="center" sx={{ fontWeight: 'normal' }}>
|
|
@@ -236,7 +243,7 @@ export default function ProductDetail(props: { id: string }) {
|
|
|
236
243
|
<Box className="section">
|
|
237
244
|
<SectionHeader title={t('admin.events')} />
|
|
238
245
|
<Box className="section-body">
|
|
239
|
-
<EventList features={{ toolbar: false }} object_id={data.id} />
|
|
246
|
+
<EventList features={{ toolbar: false }} object_id={[data.id, ...data.prices.map((x) => x.id)].join(',')} />
|
|
240
247
|
</Box>
|
|
241
248
|
</Box>
|
|
242
249
|
</Root>
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
2
2
|
import Tabs from '@arcblock/ux/lib/Tabs';
|
|
3
|
-
import { Typography } from '@mui/material';
|
|
3
|
+
import { Stack, Typography } from '@mui/material';
|
|
4
4
|
import React, { isValidElement } from 'react';
|
|
5
5
|
import { useNavigate, useParams } from 'react-router-dom';
|
|
6
6
|
|
|
7
|
+
const PaymentMethodCreate = React.lazy(() => import('./payment-methods/create'));
|
|
8
|
+
|
|
7
9
|
const pages = {
|
|
8
10
|
paymentMethods: React.lazy(() => import('./payment-methods')),
|
|
9
11
|
branding: React.lazy(() => import('./branding')),
|
|
@@ -27,13 +29,21 @@ export default function SettingsIndex() {
|
|
|
27
29
|
{ label: t('admin.business'), value: 'business' },
|
|
28
30
|
];
|
|
29
31
|
|
|
32
|
+
let extra = null;
|
|
33
|
+
if (page === 'payment-methods') {
|
|
34
|
+
extra = <PaymentMethodCreate />;
|
|
35
|
+
}
|
|
36
|
+
|
|
30
37
|
return (
|
|
31
|
-
|
|
32
|
-
<
|
|
33
|
-
{
|
|
34
|
-
|
|
38
|
+
<>
|
|
39
|
+
<Stack direction="row" alignItems="center" justifyContent="space-between">
|
|
40
|
+
<Typography variant="h5" sx={{ mb: 1, fontWeight: 600 }}>
|
|
41
|
+
{t('admin.products')}
|
|
42
|
+
</Typography>
|
|
43
|
+
{extra}
|
|
44
|
+
</Stack>
|
|
35
45
|
<Tabs tabs={tabs} current={page} onChange={onTabChange} scrollButtons="auto" />
|
|
36
46
|
{isValidElement(TabComponent) ? TabComponent : <TabComponent />}
|
|
37
|
-
|
|
47
|
+
</>
|
|
38
48
|
);
|
|
39
49
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/* eslint-disable no-nested-ternary */
|
|
2
|
+
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
|
+
import Toast from '@arcblock/ux/lib/Toast';
|
|
4
|
+
import type { TPaymentMethod } from '@did-pay/types';
|
|
5
|
+
import { AddOutlined } from '@mui/icons-material';
|
|
6
|
+
import { Button, Typography } from '@mui/material';
|
|
7
|
+
import { FormProvider, useForm } from 'react-hook-form';
|
|
8
|
+
import { dispatch } from 'use-bus';
|
|
9
|
+
|
|
10
|
+
import DrawerForm from '../../../../components/drawer-form';
|
|
11
|
+
import PaymentMethodForm from '../../../../components/payment-method/form';
|
|
12
|
+
import api from '../../../../libs/api';
|
|
13
|
+
import { formatError } from '../../../../libs/util';
|
|
14
|
+
|
|
15
|
+
export default function PaymentMethodCreate() {
|
|
16
|
+
const { t } = useLocaleContext();
|
|
17
|
+
|
|
18
|
+
const methods = useForm<TPaymentMethod>({
|
|
19
|
+
defaultValues: {
|
|
20
|
+
type: 'stripe',
|
|
21
|
+
name: '',
|
|
22
|
+
description: '',
|
|
23
|
+
settings: {
|
|
24
|
+
arcblock: {
|
|
25
|
+
chain_id: '',
|
|
26
|
+
api_host: '',
|
|
27
|
+
explorer_host: '',
|
|
28
|
+
},
|
|
29
|
+
stripe: {
|
|
30
|
+
publishable_key: '',
|
|
31
|
+
secret_key: '',
|
|
32
|
+
},
|
|
33
|
+
ethereum: {
|
|
34
|
+
chain_id: 0,
|
|
35
|
+
api_host: '',
|
|
36
|
+
explorer_host: '',
|
|
37
|
+
},
|
|
38
|
+
bitcoin: {
|
|
39
|
+
chain_id: 0,
|
|
40
|
+
api_host: '',
|
|
41
|
+
explorer_host: '',
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
const { handleSubmit } = methods;
|
|
47
|
+
|
|
48
|
+
const onSubmit = (data: TPaymentMethod) => {
|
|
49
|
+
api
|
|
50
|
+
.post('/api/payment-methods', data)
|
|
51
|
+
.then(() => {
|
|
52
|
+
Toast.success(t('admin.paymentMethod.saved'));
|
|
53
|
+
methods.reset();
|
|
54
|
+
dispatch('drawer.submitted');
|
|
55
|
+
dispatch('paymentMethod.created');
|
|
56
|
+
})
|
|
57
|
+
.catch((err) => {
|
|
58
|
+
console.error(err);
|
|
59
|
+
Toast.error(formatError(err));
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<DrawerForm
|
|
65
|
+
icon={<AddOutlined />}
|
|
66
|
+
text={t('admin.paymentMethod.add')}
|
|
67
|
+
width={640}
|
|
68
|
+
addons={
|
|
69
|
+
<Button variant="contained" size="small" onClick={handleSubmit(onSubmit)}>
|
|
70
|
+
{t('admin.paymentMethod.save')}
|
|
71
|
+
</Button>
|
|
72
|
+
}>
|
|
73
|
+
<FormProvider {...methods}>
|
|
74
|
+
<Typography variant="h6" sx={{ mb: 3, fontWeight: 600 }}>
|
|
75
|
+
{t('admin.paymentMethod.type')}
|
|
76
|
+
</Typography>
|
|
77
|
+
<PaymentMethodForm />
|
|
78
|
+
</FormProvider>
|
|
79
|
+
</DrawerForm>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type { TPaymentMethodExpanded } from '@did-pay/types';
|
|
2
2
|
import { Alert, Box, CircularProgress, Grid, Typography } from '@mui/material';
|
|
3
3
|
import { useRequest } from 'ahooks';
|
|
4
|
+
import useBus from 'use-bus';
|
|
4
5
|
|
|
5
|
-
import IconCollapse from '
|
|
6
|
-
import InfoCard from '
|
|
7
|
-
import InfoRow from '
|
|
8
|
-
import Switch from '
|
|
9
|
-
import api from '
|
|
6
|
+
import IconCollapse from '../../../../components/collapse';
|
|
7
|
+
import InfoCard from '../../../../components/info-card';
|
|
8
|
+
import InfoRow from '../../../../components/info-row';
|
|
9
|
+
import Switch from '../../../../components/switch';
|
|
10
|
+
import api from '../../../../libs/api';
|
|
10
11
|
|
|
11
12
|
const getMethods = (params: Record<string, any> = {}): Promise<TPaymentMethodExpanded[]> => {
|
|
12
13
|
const search = new URLSearchParams();
|
|
@@ -27,7 +28,9 @@ const groupByType = (methods: TPaymentMethodExpanded[]) => {
|
|
|
27
28
|
};
|
|
28
29
|
|
|
29
30
|
export default function PaymentMethods() {
|
|
30
|
-
const { loading, error, data } = useRequest(() => getMethods({}));
|
|
31
|
+
const { loading, error, data, runAsync } = useRequest(() => getMethods({}));
|
|
32
|
+
|
|
33
|
+
useBus('paymentMethod.created', runAsync, []);
|
|
31
34
|
|
|
32
35
|
if (error) {
|
|
33
36
|
return <Alert severity="error">{error.message}</Alert>;
|
|
@@ -62,8 +62,10 @@ export default function Payment({ id }: Props) {
|
|
|
62
62
|
}
|
|
63
63
|
} else if (data?.checkoutSession?.success_url) {
|
|
64
64
|
setTimeout(() => {
|
|
65
|
+
const tmp = new URL(data.checkoutSession.success_url as string);
|
|
66
|
+
tmp.searchParams.set('checkout_session_id', data.checkoutSession.id);
|
|
65
67
|
// @ts-ignore
|
|
66
|
-
window.location.href =
|
|
68
|
+
window.location.href = tmp.href;
|
|
67
69
|
}, 1000);
|
|
68
70
|
}
|
|
69
71
|
};
|
|
@@ -4,6 +4,7 @@ import { Edit } from '@mui/icons-material';
|
|
|
4
4
|
import { Alert, Box, Button, CircularProgress, Grid, Stack } from '@mui/material';
|
|
5
5
|
import { styled } from '@mui/system';
|
|
6
6
|
import { useRequest, useSetState } from 'ahooks';
|
|
7
|
+
import { FlagEmoji } from 'react-international-phone';
|
|
7
8
|
|
|
8
9
|
import InfoRow from '../../components/info-row';
|
|
9
10
|
import Layout from '../../components/layout';
|
|
@@ -81,7 +82,17 @@ export default function CustomerHome() {
|
|
|
81
82
|
<InfoRow sizes={[1, 2]} label={t('admin.customer.name')} value={data.name} />
|
|
82
83
|
<InfoRow sizes={[1, 2]} label={t('admin.customer.phone')} value={data.phone} />
|
|
83
84
|
<InfoRow sizes={[1, 2]} label={t('admin.customer.email')} value={data.email} />
|
|
84
|
-
<InfoRow
|
|
85
|
+
<InfoRow
|
|
86
|
+
sizes={[1, 2]}
|
|
87
|
+
label={t('admin.customer.address.country')}
|
|
88
|
+
value={
|
|
89
|
+
data.address?.country ? (
|
|
90
|
+
<FlagEmoji iso2={data.address?.country} style={{ display: 'flex', width: 24 }} />
|
|
91
|
+
) : (
|
|
92
|
+
''
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
/>
|
|
85
96
|
<InfoRow sizes={[1, 2]} label={t('admin.customer.address.state')} value={data.address?.state} />
|
|
86
97
|
<InfoRow sizes={[1, 2]} label={t('admin.customer.address.city')} value={data.address?.city} />
|
|
87
98
|
<InfoRow sizes={[1, 2]} label={t('admin.customer.address.line1')} value={data.address?.line1} />
|
package/public/.gitkeep
DELETED
|
File without changes
|