payment-kit 1.18.51 → 1.18.52
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/routes/invoices.ts +47 -0
- package/blocklet.yml +1 -1
- package/package.json +4 -4
- package/src/components/invoice/action.tsx +20 -0
- package/src/components/invoice/list.tsx +4 -1
- package/src/components/payment-intent/list.tsx +2 -4
- package/src/locales/en.tsx +5 -0
- package/src/locales/zh.tsx +5 -0
|
@@ -12,6 +12,7 @@ import { createListParamSchema, getOrder, getWhereFromKvQuery, MetadataSchema }
|
|
|
12
12
|
import { authenticate } from '../libs/security';
|
|
13
13
|
import { expandLineItems } from '../libs/session';
|
|
14
14
|
import { formatMetadata, getBlockletJson, getUserOrAppInfo } from '../libs/util';
|
|
15
|
+
import dayjs from '../libs/dayjs';
|
|
15
16
|
import { Customer } from '../store/models/customer';
|
|
16
17
|
import { Invoice } from '../store/models/invoice';
|
|
17
18
|
import { InvoiceItem } from '../store/models/invoice-item';
|
|
@@ -671,4 +672,50 @@ router.put('/:id', authAdmin, async (req, res) => {
|
|
|
671
672
|
}
|
|
672
673
|
});
|
|
673
674
|
|
|
675
|
+
router.post('/:id/void', authAdmin, async (req, res) => {
|
|
676
|
+
const invoice = await Invoice.findByPk(req.params.id as string);
|
|
677
|
+
if (!invoice) {
|
|
678
|
+
return res.status(404).json({ error: 'Invoice not found' });
|
|
679
|
+
}
|
|
680
|
+
if (['paid', 'void', 'draft'].includes(invoice.status)) {
|
|
681
|
+
return res.status(400).json({ error: 'Can not void this invoice' });
|
|
682
|
+
}
|
|
683
|
+
const paymentMethod = await PaymentMethod.findByPk(invoice.default_payment_method_id);
|
|
684
|
+
if (!paymentMethod) {
|
|
685
|
+
return res.status(400).json({ error: 'Payment method not found' });
|
|
686
|
+
}
|
|
687
|
+
if (invoice.subscription_id) {
|
|
688
|
+
const subscription = await Subscription.findByPk(invoice.subscription_id);
|
|
689
|
+
if (subscription && !subscription.isImmutable()) {
|
|
690
|
+
return res.status(400).json({ error: 'Subscription is not immutable, can not void invoice' });
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
try {
|
|
694
|
+
if (invoice.payment_intent_id) {
|
|
695
|
+
const paymentIntent = await PaymentIntent.findByPk(invoice.payment_intent_id);
|
|
696
|
+
if (paymentIntent && paymentIntent.status !== 'canceled') {
|
|
697
|
+
await paymentIntent.update({
|
|
698
|
+
status: 'canceled',
|
|
699
|
+
canceled_at: dayjs().unix(),
|
|
700
|
+
cancellation_reason: 'void_invoice',
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
if (paymentMethod.type === 'stripe' && invoice.metadata?.stripe_id) {
|
|
705
|
+
const client = paymentMethod.getStripeClient();
|
|
706
|
+
await client.invoices.voidInvoice(invoice.metadata.stripe_id);
|
|
707
|
+
}
|
|
708
|
+
await invoice.update({
|
|
709
|
+
status: 'void',
|
|
710
|
+
status_transitions: {
|
|
711
|
+
...(invoice.status_transitions || {}),
|
|
712
|
+
voided_at: dayjs().unix(),
|
|
713
|
+
},
|
|
714
|
+
});
|
|
715
|
+
return res.json(invoice);
|
|
716
|
+
} catch (error) {
|
|
717
|
+
logger.error('Failed to void invoice', { error, invoiceId: invoice.id });
|
|
718
|
+
return res.status(400).json({ error: 'Failed to void invoice' });
|
|
719
|
+
}
|
|
720
|
+
});
|
|
674
721
|
export default router;
|
package/blocklet.yml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.18.
|
|
3
|
+
"version": "1.18.52",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "blocklet dev --open",
|
|
6
6
|
"eject": "vite eject",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"@blocklet/did-space-js": "^1.0.57",
|
|
56
56
|
"@blocklet/js-sdk": "^1.16.43",
|
|
57
57
|
"@blocklet/logger": "^1.16.43",
|
|
58
|
-
"@blocklet/payment-react": "1.18.
|
|
58
|
+
"@blocklet/payment-react": "1.18.52",
|
|
59
59
|
"@blocklet/sdk": "^1.16.43",
|
|
60
60
|
"@blocklet/ui-react": "^2.13.61",
|
|
61
61
|
"@blocklet/uploader": "^0.1.95",
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
"devDependencies": {
|
|
124
124
|
"@abtnode/types": "^1.16.43",
|
|
125
125
|
"@arcblock/eslint-config-ts": "^0.3.3",
|
|
126
|
-
"@blocklet/payment-types": "1.18.
|
|
126
|
+
"@blocklet/payment-types": "1.18.52",
|
|
127
127
|
"@types/cookie-parser": "^1.4.7",
|
|
128
128
|
"@types/cors": "^2.8.17",
|
|
129
129
|
"@types/debug": "^4.1.12",
|
|
@@ -169,5 +169,5 @@
|
|
|
169
169
|
"parser": "typescript"
|
|
170
170
|
}
|
|
171
171
|
},
|
|
172
|
-
"gitHead": "
|
|
172
|
+
"gitHead": "dfa2927758a635868ca2ce251d098ab15178a409"
|
|
173
173
|
}
|
|
@@ -68,6 +68,10 @@ export default function InvoiceActions({ data, variant, onChange, mode }: Props)
|
|
|
68
68
|
Toast.error(result.error);
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
+
if (state.action === 'void') {
|
|
72
|
+
await api.post(`/api/invoices/${data.id}/void`).then((res) => res.data);
|
|
73
|
+
Toast.success(t('admin.invoice.void.success'));
|
|
74
|
+
}
|
|
71
75
|
onChange(state.action);
|
|
72
76
|
} catch (err) {
|
|
73
77
|
console.error(err);
|
|
@@ -99,6 +103,13 @@ export default function InvoiceActions({ data, variant, onChange, mode }: Props)
|
|
|
99
103
|
color: 'primary',
|
|
100
104
|
divider: true,
|
|
101
105
|
},
|
|
106
|
+
isAdmin &&
|
|
107
|
+
!['paid', 'void', 'draft'].includes(data.status) && {
|
|
108
|
+
label: t('admin.invoice.void.title'),
|
|
109
|
+
handler: () => setState({ action: 'void' }),
|
|
110
|
+
color: 'primary',
|
|
111
|
+
divider: true,
|
|
112
|
+
},
|
|
102
113
|
{
|
|
103
114
|
label: t('admin.customer.view'),
|
|
104
115
|
handler: () => {
|
|
@@ -138,6 +149,15 @@ export default function InvoiceActions({ data, variant, onChange, mode }: Props)
|
|
|
138
149
|
loading={state.loading}
|
|
139
150
|
/>
|
|
140
151
|
)}
|
|
152
|
+
{state.action === 'void' && (
|
|
153
|
+
<ConfirmDialog
|
|
154
|
+
onConfirm={handleAction}
|
|
155
|
+
onCancel={() => setState({ action: '' })}
|
|
156
|
+
title={t('admin.invoice.void.title')}
|
|
157
|
+
message={t('admin.invoice.void.tip')}
|
|
158
|
+
loading={state.loading}
|
|
159
|
+
/>
|
|
160
|
+
)}
|
|
141
161
|
</ClickBoundary>
|
|
142
162
|
);
|
|
143
163
|
}
|
|
@@ -174,7 +174,10 @@ export default function InvoiceList({
|
|
|
174
174
|
const item = data.list[index] as TInvoiceExpanded;
|
|
175
175
|
return (
|
|
176
176
|
<InvoiceLink invoice={item}>
|
|
177
|
-
<Typography
|
|
177
|
+
<Typography
|
|
178
|
+
component="strong"
|
|
179
|
+
fontWeight={600}
|
|
180
|
+
sx={{ textDecoration: item.status === 'void' ? 'line-through' : 'none' }}>
|
|
178
181
|
{formatBNStr(item?.total, item?.paymentCurrency.decimal)}
|
|
179
182
|
{item?.paymentCurrency.symbol}
|
|
180
183
|
</Typography>
|
|
@@ -117,12 +117,10 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
|
|
|
117
117
|
options: {
|
|
118
118
|
customBodyRenderLite: (_: string, index: number) => {
|
|
119
119
|
const item = data.list[index] as TPaymentIntentExpanded;
|
|
120
|
+
const highlight = item.amount_received === '0' && item.status !== 'canceled';
|
|
120
121
|
return (
|
|
121
122
|
<Link to={`/admin/payments/${item.id}`}>
|
|
122
|
-
<Typography
|
|
123
|
-
component="strong"
|
|
124
|
-
sx={{ color: item.amount_received === '0' ? 'warning.main' : 'inherit' }}
|
|
125
|
-
fontWeight={600}>
|
|
123
|
+
<Typography component="strong" sx={{ color: highlight ? 'warning.main' : 'inherit' }} fontWeight={600}>
|
|
126
124
|
{formatBNStr(
|
|
127
125
|
item.amount_received === '0' ? item.amount : item.amount_received,
|
|
128
126
|
item?.paymentCurrency.decimal
|
package/src/locales/en.tsx
CHANGED
|
@@ -524,6 +524,11 @@ export default flat({
|
|
|
524
524
|
tip: 'Are you sure you want to return the stake? This action will return the stake to the customer immediately.',
|
|
525
525
|
success: 'Stake return application has been successfully created',
|
|
526
526
|
},
|
|
527
|
+
void: {
|
|
528
|
+
title: 'Void Invoice',
|
|
529
|
+
tip: 'Are you sure you want to void this invoice? This action will immediately void the invoice.',
|
|
530
|
+
success: 'Invoice voided',
|
|
531
|
+
},
|
|
527
532
|
},
|
|
528
533
|
subscription: {
|
|
529
534
|
view: 'View subscription',
|
package/src/locales/zh.tsx
CHANGED