payment-kit 1.21.16 → 1.22.0
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/api/src/index.ts +3 -1
- package/api/src/integrations/blocklet/user.ts +2 -2
- package/api/src/integrations/ethereum/token.ts +4 -5
- package/api/src/integrations/stripe/handlers/invoice.ts +31 -26
- package/api/src/integrations/stripe/handlers/payment-intent.ts +1 -1
- package/api/src/integrations/stripe/handlers/setup-intent.ts +231 -0
- package/api/src/integrations/stripe/handlers/subscription.ts +31 -9
- package/api/src/integrations/stripe/resource.ts +30 -1
- package/api/src/integrations/stripe/setup.ts +1 -1
- package/api/src/libs/auth.ts +7 -6
- package/api/src/libs/env.ts +1 -1
- package/api/src/libs/notification/template/subscription-trial-will-end.ts +1 -0
- package/api/src/libs/notification/template/subscription-will-renew.ts +1 -1
- package/api/src/libs/payment.ts +11 -6
- package/api/src/libs/refund.ts +1 -1
- package/api/src/libs/remote-signer.ts +93 -0
- package/api/src/libs/security.ts +1 -1
- package/api/src/libs/subscription.ts +4 -7
- package/api/src/libs/util.ts +18 -1
- package/api/src/libs/vendor-util/adapters/didnames-adapter.ts +17 -9
- package/api/src/libs/vendor-util/adapters/launcher-adapter.ts +11 -6
- package/api/src/queues/payment.ts +2 -2
- package/api/src/queues/payout.ts +1 -1
- package/api/src/queues/refund.ts +2 -2
- package/api/src/queues/subscription.ts +1 -1
- package/api/src/queues/usage-record.ts +1 -1
- package/api/src/queues/vendors/status-check.ts +1 -1
- package/api/src/queues/webhook.ts +1 -1
- package/api/src/routes/auto-recharge-configs.ts +1 -1
- package/api/src/routes/checkout-sessions.ts +4 -6
- package/api/src/routes/connect/change-payer.ts +148 -0
- package/api/src/routes/connect/collect-batch.ts +1 -1
- package/api/src/routes/connect/collect.ts +1 -1
- package/api/src/routes/connect/pay.ts +1 -1
- package/api/src/routes/connect/recharge-account.ts +1 -1
- package/api/src/routes/connect/recharge.ts +1 -1
- package/api/src/routes/connect/shared.ts +62 -23
- package/api/src/routes/customers.ts +1 -1
- package/api/src/routes/integrations/stripe.ts +1 -1
- package/api/src/routes/invoices.ts +141 -2
- package/api/src/routes/meter-events.ts +9 -12
- package/api/src/routes/payment-currencies.ts +1 -1
- package/api/src/routes/payment-intents.ts +2 -2
- package/api/src/routes/payment-links.ts +2 -1
- package/api/src/routes/payouts.ts +1 -1
- package/api/src/routes/products.ts +1 -0
- package/api/src/routes/subscriptions.ts +130 -3
- package/api/src/store/models/types.ts +1 -1
- package/api/tests/setup.ts +11 -0
- package/api/third.d.ts +0 -2
- package/blocklet.yml +1 -1
- package/jest.config.js +2 -2
- package/package.json +26 -26
- package/src/components/invoice/table.tsx +2 -2
- package/src/components/invoice-pdf/template.tsx +30 -0
- package/src/components/subscription/payment-method-info.tsx +222 -0
- package/src/global.css +4 -0
- package/src/libs/util.ts +1 -1
- package/src/locales/en.tsx +13 -0
- package/src/locales/zh.tsx +13 -0
- package/src/pages/admin/billing/invoices/detail.tsx +5 -3
- package/src/pages/admin/billing/subscriptions/detail.tsx +16 -0
- package/src/pages/admin/overview.tsx +14 -14
- package/src/pages/customer/invoice/detail.tsx +59 -17
- package/src/pages/customer/subscription/detail.tsx +21 -2
|
@@ -15,13 +15,14 @@ import {
|
|
|
15
15
|
usePaymentContext,
|
|
16
16
|
PaymentBeneficiaries,
|
|
17
17
|
useMobile,
|
|
18
|
+
StripePaymentAction,
|
|
18
19
|
} from '@blocklet/payment-react';
|
|
19
20
|
import type { TCheckoutSession, TCreditGrantExpanded, TInvoiceExpanded, TPaymentLink } from '@blocklet/payment-types';
|
|
20
21
|
import { ArrowBackOutlined } from '@mui/icons-material';
|
|
21
22
|
import { Alert, Box, Button, CircularProgress, Divider, Stack, Tooltip, Typography, useTheme } from '@mui/material';
|
|
22
23
|
import { styled } from '@mui/system';
|
|
23
24
|
import { useRequest, useSetState } from 'ahooks';
|
|
24
|
-
import { useEffect } from 'react';
|
|
25
|
+
import { useEffect, useRef } from 'react';
|
|
25
26
|
import { Link, useParams, useSearchParams } from 'react-router-dom';
|
|
26
27
|
|
|
27
28
|
import { joinURL } from 'ufo';
|
|
@@ -48,7 +49,7 @@ const fetchData = (
|
|
|
48
49
|
relatedCreditGrants?: TCreditGrantExpanded[];
|
|
49
50
|
}
|
|
50
51
|
> => {
|
|
51
|
-
return api.get(`/api/invoices/${id}`).then((res) => res.data);
|
|
52
|
+
return api.get(`/api/invoices/${id}`).then((res: any) => res.data);
|
|
52
53
|
};
|
|
53
54
|
|
|
54
55
|
export default function CustomerInvoiceDetail() {
|
|
@@ -64,16 +65,19 @@ export default function CustomerInvoiceDetail() {
|
|
|
64
65
|
});
|
|
65
66
|
|
|
66
67
|
const { loading, error, data, runAsync } = useRequest(() => fetchData(params.id as string));
|
|
68
|
+
|
|
67
69
|
const action = searchParams.get('action');
|
|
68
70
|
const { session } = useSessionContext();
|
|
69
71
|
const isDonation = data?.checkoutSession?.submit_type === 'donate';
|
|
72
|
+
const payHandlerRef = useRef<(() => void) | null>(null);
|
|
70
73
|
|
|
71
|
-
const
|
|
74
|
+
const handleExternalPayment = () => {
|
|
72
75
|
setState({ paying: true });
|
|
73
76
|
connect.open({
|
|
74
77
|
action: 'collect',
|
|
75
78
|
saveConnect: false,
|
|
76
79
|
locale: locale as 'en' | 'zh',
|
|
80
|
+
extraParams: { invoiceId: params.id, action },
|
|
77
81
|
messages: {
|
|
78
82
|
scan: '',
|
|
79
83
|
title: t(`payment.customer.invoice.${action || 'pay'}`),
|
|
@@ -81,9 +85,9 @@ export default function CustomerInvoiceDetail() {
|
|
|
81
85
|
error: t(`payment.customer.invoice.${action || 'pay'}Error`),
|
|
82
86
|
confirm: '',
|
|
83
87
|
} as any,
|
|
84
|
-
extraParams: { invoiceId: params.id, action },
|
|
85
88
|
onSuccess: async () => {
|
|
86
89
|
connect.close();
|
|
90
|
+
setState({ paying: false });
|
|
87
91
|
await runAsync();
|
|
88
92
|
},
|
|
89
93
|
onClose: () => {
|
|
@@ -110,7 +114,7 @@ export default function CustomerInvoiceDetail() {
|
|
|
110
114
|
['pay', 'renew'].includes(action as string) &&
|
|
111
115
|
['open', 'uncollectible'].includes(data?.status as string)
|
|
112
116
|
) {
|
|
113
|
-
|
|
117
|
+
handleExternalPayment();
|
|
114
118
|
}
|
|
115
119
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
116
120
|
}, [error]);
|
|
@@ -133,6 +137,9 @@ export default function CustomerInvoiceDetail() {
|
|
|
133
137
|
|
|
134
138
|
const hidePayButton = data.billing_reason === 'overdraft_protection';
|
|
135
139
|
const paymentDetails = data.paymentIntent?.payment_details || data.metadata?.payment_details;
|
|
140
|
+
|
|
141
|
+
const showDownloadButton =
|
|
142
|
+
['open', 'paid', 'uncollectible'].includes(data.status) && !isDonation && !isEmpty(data.lines);
|
|
136
143
|
return (
|
|
137
144
|
<InvoiceDetailRoot direction="column" spacing={3}>
|
|
138
145
|
<Stack
|
|
@@ -161,7 +168,7 @@ export default function CustomerInvoiceDetail() {
|
|
|
161
168
|
justifyContent: 'flex-end',
|
|
162
169
|
alignItems: 'center',
|
|
163
170
|
}}>
|
|
164
|
-
{
|
|
171
|
+
{showDownloadButton && <Download data={data} />}
|
|
165
172
|
{data?.paymentLink?.donation_settings?.reference && (
|
|
166
173
|
<Button
|
|
167
174
|
variant="outlined"
|
|
@@ -171,17 +178,52 @@ export default function CustomerInvoiceDetail() {
|
|
|
171
178
|
{t('customer.payout.viewReference')}
|
|
172
179
|
</Button>
|
|
173
180
|
)}
|
|
174
|
-
{['open', 'uncollectible'].includes(data.status) &&
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
181
|
+
{['open', 'uncollectible'].includes(data.status) &&
|
|
182
|
+
!hidePayButton &&
|
|
183
|
+
(data.paymentMethod?.type === 'stripe' ? (
|
|
184
|
+
<StripePaymentAction
|
|
185
|
+
invoice={data}
|
|
186
|
+
paymentMethod={data.paymentMethod}
|
|
187
|
+
onSuccess={async () => {
|
|
188
|
+
Toast.close();
|
|
189
|
+
setState({ paying: false });
|
|
190
|
+
await runAsync();
|
|
191
|
+
}}
|
|
192
|
+
onError={() => {
|
|
193
|
+
setState({ paying: false });
|
|
194
|
+
}}
|
|
195
|
+
onClose={() => {
|
|
196
|
+
setState({ paying: false });
|
|
197
|
+
}}>
|
|
198
|
+
{(onPay: () => void, paying: boolean) => {
|
|
199
|
+
payHandlerRef.current = onPay;
|
|
200
|
+
return (
|
|
201
|
+
<Button
|
|
202
|
+
variant="outlined"
|
|
203
|
+
color="primary"
|
|
204
|
+
size="small"
|
|
205
|
+
sx={{ borderColor: 'divider' }}
|
|
206
|
+
disabled={state.paying || paying}
|
|
207
|
+
onClick={() => {
|
|
208
|
+
setState({ paying: true });
|
|
209
|
+
onPay();
|
|
210
|
+
}}>
|
|
211
|
+
{t('payment.customer.invoice.pay')}
|
|
212
|
+
</Button>
|
|
213
|
+
);
|
|
214
|
+
}}
|
|
215
|
+
</StripePaymentAction>
|
|
216
|
+
) : (
|
|
217
|
+
<Button
|
|
218
|
+
variant="outlined"
|
|
219
|
+
color="primary"
|
|
220
|
+
size="small"
|
|
221
|
+
sx={{ borderColor: 'divider' }}
|
|
222
|
+
disabled={state.paying}
|
|
223
|
+
onClick={handleExternalPayment}>
|
|
224
|
+
{t('payment.customer.invoice.pay')}
|
|
225
|
+
</Button>
|
|
226
|
+
))}
|
|
185
227
|
</Stack>
|
|
186
228
|
</Stack>
|
|
187
229
|
<Box
|
|
@@ -52,6 +52,7 @@ import { useSessionContext } from '../../../contexts/session';
|
|
|
52
52
|
import { usePendingAmountForSubscription, useUnpaidInvoicesCheckForSubscription } from '../../../hooks/subscription';
|
|
53
53
|
import { formatSmartDuration, TimeUnit } from '../../../libs/dayjs';
|
|
54
54
|
import { canChangePaymentMethod } from '../../../libs/util';
|
|
55
|
+
import PaymentMethodInfo from '../../../components/subscription/payment-method-info';
|
|
55
56
|
|
|
56
57
|
const fetchData = (id: string | undefined): Promise<TSubscriptionExpanded> => {
|
|
57
58
|
return api.get(`/api/subscriptions/${id}`).then((res) => res.data);
|
|
@@ -144,7 +145,7 @@ export default function CustomerSubscriptionDetail() {
|
|
|
144
145
|
);
|
|
145
146
|
const estimateAmount = +fromUnitToToken(cycleAmount.amount, data.paymentCurrency?.decimal);
|
|
146
147
|
|
|
147
|
-
const remainingStake = +fromUnitToToken(overdraftProtection?.unused, data.paymentCurrency?.decimal);
|
|
148
|
+
const remainingStake = +fromUnitToToken(overdraftProtection?.unused || '0', data.paymentCurrency?.decimal);
|
|
148
149
|
const formatEstimatedDuration = (cycles: number) => {
|
|
149
150
|
if (!data?.pending_invoice_item_interval) return '';
|
|
150
151
|
const { interval, interval_count: intervalCount } = data.pending_invoice_item_interval;
|
|
@@ -622,7 +623,7 @@ export default function CustomerSubscriptionDetail() {
|
|
|
622
623
|
{canChangePaymentMethod(data) && (
|
|
623
624
|
<Button
|
|
624
625
|
variant="text"
|
|
625
|
-
sx={{ color: 'text.link'
|
|
626
|
+
sx={{ color: 'text.link' }}
|
|
626
627
|
size="small"
|
|
627
628
|
onClick={async () => {
|
|
628
629
|
// only check unpaid invoices when overdraft protection is enabled
|
|
@@ -640,6 +641,24 @@ export default function CustomerSubscriptionDetail() {
|
|
|
640
641
|
</Stack>
|
|
641
642
|
}
|
|
642
643
|
/>
|
|
644
|
+
{(data as any).paymentMethodDetails && (
|
|
645
|
+
<InfoRow
|
|
646
|
+
label={t('admin.subscription.payerAddress')}
|
|
647
|
+
value={
|
|
648
|
+
<PaymentMethodInfo
|
|
649
|
+
subscriptionId={id}
|
|
650
|
+
customer={data.customer}
|
|
651
|
+
paymentMethodDetails={(data as any).paymentMethodDetails}
|
|
652
|
+
editable={['active', 'trialing', 'past_due'].includes(data.status)}
|
|
653
|
+
onUpdate={() => {
|
|
654
|
+
refresh();
|
|
655
|
+
checkUnpaidInvoices();
|
|
656
|
+
}}
|
|
657
|
+
paymentMethodType={data.paymentMethod?.type}
|
|
658
|
+
/>
|
|
659
|
+
}
|
|
660
|
+
/>
|
|
661
|
+
)}
|
|
643
662
|
|
|
644
663
|
{data.payment_details && hasDelegateTxHash(data.payment_details, data.paymentMethod) && (
|
|
645
664
|
<InfoRow
|