payment-kit 1.19.9 → 1.19.11
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/libs/env.ts +4 -0
- package/api/src/libs/payment.ts +3 -7
- package/api/src/routes/checkout-sessions.ts +42 -23
- package/api/src/routes/connect/change-payment.ts +2 -2
- package/api/src/routes/connect/change-plan.ts +2 -2
- package/api/src/routes/connect/setup.ts +2 -2
- package/api/src/routes/connect/subscribe.ts +20 -6
- package/api/src/routes/subscriptions.ts +8 -3
- package/blocklet.yml +1 -1
- package/package.json +10 -10
- package/src/components/payment-link/product-select.tsx +14 -1
- package/src/components/pricing-table/product-item.tsx +8 -1
- package/src/contexts/products.tsx +6 -1
- package/src/pages/admin/products/links/detail.tsx +14 -1
- package/src/pages/admin/products/prices/actions.tsx +4 -0
- package/src/pages/admin/products/prices/detail.tsx +2 -0
- package/src/pages/admin/products/products/create.tsx +1 -1
- package/src/pages/admin/products/products/detail.tsx +3 -0
- package/src/pages/admin/products/products/index.tsx +3 -1
package/api/src/libs/env.ts
CHANGED
|
@@ -24,6 +24,10 @@ export const sequelizeOptionsPoolMax: number = process.env.SEQUELIZE_OPTIONS_POO
|
|
|
24
24
|
export const sequelizeOptionsPoolIdle: number = process.env.SEQUELIZE_OPTIONS_POOL_IDLE
|
|
25
25
|
? +process.env.SEQUELIZE_OPTIONS_POOL_IDLE
|
|
26
26
|
: 10 * 1000;
|
|
27
|
+
|
|
28
|
+
export const updateDataConcurrency: number = process.env.UPDATE_DATA_CONCURRENCY
|
|
29
|
+
? +process.env.UPDATE_DATA_CONCURRENCY
|
|
30
|
+
: 5; // 默认并发数为 5
|
|
27
31
|
export default {
|
|
28
32
|
...env,
|
|
29
33
|
};
|
package/api/src/libs/payment.ts
CHANGED
|
@@ -70,13 +70,6 @@ export async function checkTokenBalance(args: {
|
|
|
70
70
|
let delegator = userDid;
|
|
71
71
|
if (!args.skipUserCheck) {
|
|
72
72
|
const { user } = await blocklet.getUser(userDid, { enableConnectedAccount: true });
|
|
73
|
-
if (!user) {
|
|
74
|
-
return {
|
|
75
|
-
sufficient: false,
|
|
76
|
-
reason: 'NO_CUSTOMER',
|
|
77
|
-
requestedAmount: totalAmount.toString(),
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
73
|
if (user) {
|
|
81
74
|
delegator = getWalletDid(user);
|
|
82
75
|
}
|
|
@@ -216,6 +209,9 @@ export async function isDelegationSufficientForPayment(args: {
|
|
|
216
209
|
lineItems?: TLineItemExpanded[];
|
|
217
210
|
}): Promise<SufficientForPaymentResult> {
|
|
218
211
|
const { paymentCurrency, paymentMethod, userDid, amount, delegatorAmounts } = args;
|
|
212
|
+
if (!userDid) {
|
|
213
|
+
return { sufficient: false, reason: 'NO_DID_WALLET' };
|
|
214
|
+
}
|
|
219
215
|
const tokenAddress = paymentCurrency.contract as string;
|
|
220
216
|
|
|
221
217
|
let totalAmount = new BN(amount);
|
|
@@ -14,6 +14,7 @@ import uniq from 'lodash/uniq';
|
|
|
14
14
|
import type { WhereOptions } from 'sequelize';
|
|
15
15
|
|
|
16
16
|
import { CustomError, formatError, getStatusFromError } from '@blocklet/error';
|
|
17
|
+
import pAll from 'p-all';
|
|
17
18
|
import { MetadataSchema } from '../libs/api';
|
|
18
19
|
import { checkPassportForPaymentLink } from '../integrations/blocklet/passport';
|
|
19
20
|
import dayjs from '../libs/dayjs';
|
|
@@ -89,6 +90,7 @@ import { handleStripeSubscriptionSucceed } from '../integrations/stripe/handlers
|
|
|
89
90
|
import { CHARGE_SUPPORTED_CHAIN_TYPES } from '../libs/constants';
|
|
90
91
|
import { blocklet } from '../libs/auth';
|
|
91
92
|
import { addSubscriptionJob } from '../queues/subscription';
|
|
93
|
+
import { updateDataConcurrency } from '../libs/env';
|
|
92
94
|
|
|
93
95
|
const router = Router();
|
|
94
96
|
|
|
@@ -110,7 +112,7 @@ const getPaymentTypes = async (items: any[]) => {
|
|
|
110
112
|
};
|
|
111
113
|
|
|
112
114
|
export async function validateInventory(line_items: LineItem[], includePendingQuantity = false) {
|
|
113
|
-
const checks = line_items.map(async (
|
|
115
|
+
const checks = line_items.map((item) => async () => {
|
|
114
116
|
const priceId = item.price_id;
|
|
115
117
|
const quantity = Number(item.quantity || 0);
|
|
116
118
|
|
|
@@ -156,7 +158,7 @@ export async function validateInventory(line_items: LineItem[], includePendingQu
|
|
|
156
158
|
throw new Error(`Can not exceed available quantity for price: ${priceId}`);
|
|
157
159
|
}
|
|
158
160
|
});
|
|
159
|
-
await
|
|
161
|
+
await pAll(checks, { concurrency: updateDataConcurrency });
|
|
160
162
|
}
|
|
161
163
|
|
|
162
164
|
export async function validatePaymentSettings(paymentMethodId: string, paymentCurrencyId: string) {
|
|
@@ -624,11 +626,12 @@ async function processSubscriptionFastCheckout({
|
|
|
624
626
|
}> {
|
|
625
627
|
try {
|
|
626
628
|
const primarySubscription = subscriptions.find((x) => x.metadata?.is_primary_subscription) || subscriptions[0];
|
|
627
|
-
const subscriptionAmounts = await
|
|
628
|
-
subscriptions.map(async (
|
|
629
|
+
const subscriptionAmounts = await pAll(
|
|
630
|
+
subscriptions.map((sub) => async () => {
|
|
629
631
|
const subItems = await getSubscriptionLineItems(sub, lineItems, primarySubscription);
|
|
630
632
|
return getFastCheckoutAmount(subItems, 'subscription', paymentCurrency.id, trialEnd > now);
|
|
631
|
-
})
|
|
633
|
+
}),
|
|
634
|
+
{ concurrency: updateDataConcurrency }
|
|
632
635
|
);
|
|
633
636
|
const totalAmount = subscriptionAmounts
|
|
634
637
|
.reduce((sum: BN, amt: string) => sum.add(new BN(amt)), new BN('0'))
|
|
@@ -669,15 +672,23 @@ async function processSubscriptionFastCheckout({
|
|
|
669
672
|
|
|
670
673
|
if (executePayment) {
|
|
671
674
|
// Update payment settings for all subscriptions
|
|
672
|
-
await
|
|
675
|
+
await pAll(
|
|
676
|
+
subscriptions.map((sub) => async () => {
|
|
677
|
+
await sub.update({
|
|
678
|
+
payment_settings: paymentSettings,
|
|
679
|
+
payment_details: { [paymentMethod.type]: { payer: customer.did } },
|
|
680
|
+
});
|
|
681
|
+
}),
|
|
682
|
+
{ concurrency: updateDataConcurrency }
|
|
683
|
+
);
|
|
673
684
|
if (paymentCurrency.isCredit()) {
|
|
674
685
|
// skip invoice creation for credit subscriptions
|
|
675
686
|
checkoutSession.update({
|
|
676
687
|
status: 'complete',
|
|
677
688
|
payment_status: 'paid',
|
|
678
689
|
});
|
|
679
|
-
await
|
|
680
|
-
subscriptions.map(async (
|
|
690
|
+
await pAll(
|
|
691
|
+
subscriptions.map((sub) => async () => {
|
|
681
692
|
await sub.update({
|
|
682
693
|
payment_settings: paymentSettings,
|
|
683
694
|
status: sub.trial_end ? 'trialing' : 'active',
|
|
@@ -688,8 +699,9 @@ async function processSubscriptionFastCheckout({
|
|
|
688
699
|
},
|
|
689
700
|
},
|
|
690
701
|
});
|
|
691
|
-
addSubscriptionJob(sub, 'cycle', false, sub.trial_end);
|
|
692
|
-
})
|
|
702
|
+
await addSubscriptionJob(sub, 'cycle', false, sub.trial_end);
|
|
703
|
+
}),
|
|
704
|
+
{ concurrency: updateDataConcurrency }
|
|
693
705
|
);
|
|
694
706
|
return {
|
|
695
707
|
success: true,
|
|
@@ -705,17 +717,22 @@ async function processSubscriptionFastCheckout({
|
|
|
705
717
|
subscriptions,
|
|
706
718
|
});
|
|
707
719
|
// Update invoice settings and push to queue
|
|
708
|
-
await
|
|
709
|
-
invoices.map(async (
|
|
720
|
+
await pAll(
|
|
721
|
+
invoices.map((invoice) => async () => {
|
|
710
722
|
if (invoice) {
|
|
711
723
|
await invoice.update({ auto_advance: true, payment_settings: paymentSettings });
|
|
712
|
-
invoiceQueue.push({ id: invoice.id, job: { invoiceId: invoice.id, retryOnError: false } });
|
|
724
|
+
return invoiceQueue.push({ id: invoice.id, job: { invoiceId: invoice.id, retryOnError: false } });
|
|
713
725
|
}
|
|
714
|
-
})
|
|
726
|
+
}),
|
|
727
|
+
{ concurrency: updateDataConcurrency }
|
|
715
728
|
);
|
|
716
|
-
|
|
717
729
|
// Add subscription cycle jobs
|
|
718
|
-
await
|
|
730
|
+
await pAll(
|
|
731
|
+
subscriptions.map((sub) => async () => {
|
|
732
|
+
await addSubscriptionJob(sub, 'cycle', false, sub.trial_end);
|
|
733
|
+
}),
|
|
734
|
+
{ concurrency: updateDataConcurrency }
|
|
735
|
+
);
|
|
719
736
|
|
|
720
737
|
logger.info('Created and queued invoices for fast checkout with subscriptions', {
|
|
721
738
|
checkoutSessionId: checkoutSession.id,
|
|
@@ -1382,8 +1399,8 @@ router.put('/:id/submit', user, ensureCheckoutSessionOpen, async (req, res) => {
|
|
|
1382
1399
|
};
|
|
1383
1400
|
}
|
|
1384
1401
|
stripeContext.stripe_subscriptions = '';
|
|
1385
|
-
await
|
|
1386
|
-
subscriptions.map(async (
|
|
1402
|
+
await pAll(
|
|
1403
|
+
subscriptions.map((sub) => async () => {
|
|
1387
1404
|
const subscriptionItems = await SubscriptionItem.findAll({ where: { subscription_id: sub.id } });
|
|
1388
1405
|
let stripeItems = lineItems.filter((x) =>
|
|
1389
1406
|
subscriptionItems.some((y) => y.price_id === x.price_id || y.price_id === x.upsell_price_id)
|
|
@@ -1438,7 +1455,8 @@ router.put('/:id/submit', user, ensureCheckoutSessionOpen, async (req, res) => {
|
|
|
1438
1455
|
stripeSubscription,
|
|
1439
1456
|
subscription: sub,
|
|
1440
1457
|
};
|
|
1441
|
-
})
|
|
1458
|
+
}),
|
|
1459
|
+
{ concurrency: updateDataConcurrency }
|
|
1442
1460
|
);
|
|
1443
1461
|
if (subscriptions.length > 1) {
|
|
1444
1462
|
stripeContext.has_multiple_subscriptions = true;
|
|
@@ -1707,8 +1725,8 @@ router.post('/:id/fast-checkout-confirm', user, ensureCheckoutSessionOpen, async
|
|
|
1707
1725
|
status: 'complete',
|
|
1708
1726
|
payment_status: 'paid',
|
|
1709
1727
|
});
|
|
1710
|
-
await
|
|
1711
|
-
subscriptions.map(async (
|
|
1728
|
+
await pAll(
|
|
1729
|
+
subscriptions.map((sub) => async () => {
|
|
1712
1730
|
await sub.update({
|
|
1713
1731
|
payment_settings: paymentSettings,
|
|
1714
1732
|
status: sub.trial_end ? 'trialing' : 'active',
|
|
@@ -1719,8 +1737,9 @@ router.post('/:id/fast-checkout-confirm', user, ensureCheckoutSessionOpen, async
|
|
|
1719
1737
|
},
|
|
1720
1738
|
},
|
|
1721
1739
|
});
|
|
1722
|
-
addSubscriptionJob(sub, 'cycle', false, sub.trial_end);
|
|
1723
|
-
})
|
|
1740
|
+
await addSubscriptionJob(sub, 'cycle', false, sub.trial_end);
|
|
1741
|
+
}),
|
|
1742
|
+
{ concurrency: updateDataConcurrency }
|
|
1724
1743
|
);
|
|
1725
1744
|
delegation = {
|
|
1726
1745
|
sufficient: true,
|
|
@@ -28,7 +28,7 @@ export default {
|
|
|
28
28
|
},
|
|
29
29
|
onConnect: async ({ userDid, userPk, extraParams }: CallbackArgs) => {
|
|
30
30
|
const { subscriptionId } = extraParams;
|
|
31
|
-
const { subscription, paymentMethod, paymentCurrency
|
|
31
|
+
const { subscription, paymentMethod, paymentCurrency } = await ensureChangePaymentContext(subscriptionId);
|
|
32
32
|
|
|
33
33
|
const claimsList: any[] = [];
|
|
34
34
|
// @ts-ignore
|
|
@@ -41,7 +41,7 @@ export default {
|
|
|
41
41
|
const delegation = await isDelegationSufficientForPayment({
|
|
42
42
|
paymentMethod,
|
|
43
43
|
paymentCurrency,
|
|
44
|
-
userDid
|
|
44
|
+
userDid,
|
|
45
45
|
amount: fastCheckoutAmount,
|
|
46
46
|
});
|
|
47
47
|
const needDelegation = delegation.sufficient === false;
|
|
@@ -30,7 +30,7 @@ export default {
|
|
|
30
30
|
},
|
|
31
31
|
onConnect: async ({ userDid, userPk, extraParams }: CallbackArgs) => {
|
|
32
32
|
const { subscriptionId } = extraParams;
|
|
33
|
-
const { paymentMethod, paymentCurrency, subscription
|
|
33
|
+
const { paymentMethod, paymentCurrency, subscription } = await ensureSubscription(subscriptionId);
|
|
34
34
|
|
|
35
35
|
const claimsList: any[] = [];
|
|
36
36
|
// @ts-ignore
|
|
@@ -43,7 +43,7 @@ export default {
|
|
|
43
43
|
const delegation = await isDelegationSufficientForPayment({
|
|
44
44
|
paymentMethod,
|
|
45
45
|
paymentCurrency,
|
|
46
|
-
userDid
|
|
46
|
+
userDid,
|
|
47
47
|
amount: fastCheckoutAmount,
|
|
48
48
|
});
|
|
49
49
|
|
|
@@ -33,7 +33,7 @@ export default {
|
|
|
33
33
|
onConnect: async (args: CallbackArgs) => {
|
|
34
34
|
const { userDid, userPk, extraParams } = args;
|
|
35
35
|
const { checkoutSessionId } = extraParams;
|
|
36
|
-
const { paymentMethod, paymentCurrency, checkoutSession, subscription
|
|
36
|
+
const { paymentMethod, paymentCurrency, checkoutSession, subscription } =
|
|
37
37
|
await ensureSetupIntent(checkoutSessionId);
|
|
38
38
|
if (!subscription) {
|
|
39
39
|
throw new Error('Subscription for checkoutSession not found');
|
|
@@ -56,7 +56,7 @@ export default {
|
|
|
56
56
|
const delegation = await isDelegationSufficientForPayment({
|
|
57
57
|
paymentMethod,
|
|
58
58
|
paymentCurrency,
|
|
59
|
-
userDid
|
|
59
|
+
userDid,
|
|
60
60
|
amount: fastCheckoutAmount,
|
|
61
61
|
});
|
|
62
62
|
// if we can complete purchase without any wallet interaction
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import pAll from 'p-all';
|
|
1
2
|
import { broadcastEvmTransaction, executeEvmTransaction, waitForEvmTxConfirm } from '../../integrations/ethereum/tx';
|
|
2
3
|
import type { CallbackArgs } from '../../libs/auth';
|
|
3
4
|
import dayjs from '../../libs/dayjs';
|
|
@@ -19,13 +20,24 @@ import {
|
|
|
19
20
|
} from './shared';
|
|
20
21
|
import { ensureStakeInvoice } from '../../libs/invoice';
|
|
21
22
|
import { EVM_CHAIN_TYPES } from '../../libs/constants';
|
|
23
|
+
import { updateDataConcurrency } from '../../libs/env';
|
|
22
24
|
|
|
23
25
|
const updateInvoices = async (invoices: Invoice[], update: Partial<Invoice>) => {
|
|
24
|
-
await
|
|
26
|
+
await pAll(
|
|
27
|
+
invoices.map((invoice) => async () => {
|
|
28
|
+
await invoice.update(update);
|
|
29
|
+
}),
|
|
30
|
+
{ concurrency: updateDataConcurrency }
|
|
31
|
+
);
|
|
25
32
|
};
|
|
26
33
|
|
|
27
34
|
const updateSubscriptions = async (subscriptions: Subscription[], update: Partial<Subscription>) => {
|
|
28
|
-
await
|
|
35
|
+
await pAll(
|
|
36
|
+
subscriptions.map((subscription) => async () => {
|
|
37
|
+
await subscription.update(update);
|
|
38
|
+
}),
|
|
39
|
+
{ concurrency: updateDataConcurrency }
|
|
40
|
+
);
|
|
29
41
|
};
|
|
30
42
|
|
|
31
43
|
export default {
|
|
@@ -46,7 +58,6 @@ export default {
|
|
|
46
58
|
paymentMethod,
|
|
47
59
|
paymentCurrency,
|
|
48
60
|
subscriptions,
|
|
49
|
-
customer,
|
|
50
61
|
subscription: primarySubscription,
|
|
51
62
|
} = await ensurePaymentIntent(checkoutSessionId, connectedDid || sessionUserDid || userDid);
|
|
52
63
|
if (!subscriptions || subscriptions.length === 0) {
|
|
@@ -71,7 +82,7 @@ export default {
|
|
|
71
82
|
const delegation = await isDelegationSufficientForPayment({
|
|
72
83
|
paymentMethod,
|
|
73
84
|
paymentCurrency,
|
|
74
|
-
userDid
|
|
85
|
+
userDid,
|
|
75
86
|
amount: fastCheckoutAmount,
|
|
76
87
|
});
|
|
77
88
|
|
|
@@ -208,8 +219,11 @@ export default {
|
|
|
208
219
|
}
|
|
209
220
|
}
|
|
210
221
|
|
|
211
|
-
await
|
|
212
|
-
subscriptions.map((subscription) =>
|
|
222
|
+
await pAll(
|
|
223
|
+
subscriptions.map((subscription) => async () => {
|
|
224
|
+
await addSubscriptionJob(subscription, 'cycle', false, subscription.trial_end);
|
|
225
|
+
}),
|
|
226
|
+
{ concurrency: updateDataConcurrency }
|
|
213
227
|
);
|
|
214
228
|
|
|
215
229
|
logger.info('CheckoutSession updated with multiple subscriptions', {
|
|
@@ -1052,10 +1052,11 @@ router.put('/:id', authPortal, async (req, res) => {
|
|
|
1052
1052
|
if (due === '0') {
|
|
1053
1053
|
hasNext = false;
|
|
1054
1054
|
} else {
|
|
1055
|
+
const payer = getSubscriptionPaymentAddress(subscription, paymentMethod.type);
|
|
1055
1056
|
const delegation = await isDelegationSufficientForPayment({
|
|
1056
1057
|
paymentMethod,
|
|
1057
1058
|
paymentCurrency,
|
|
1058
|
-
userDid:
|
|
1059
|
+
userDid: payer,
|
|
1059
1060
|
amount: setup.amount.setup,
|
|
1060
1061
|
});
|
|
1061
1062
|
if (delegation.sufficient) {
|
|
@@ -1657,10 +1658,11 @@ router.post('/:id/change-payment', authPortal, async (req, res) => {
|
|
|
1657
1658
|
}
|
|
1658
1659
|
} else {
|
|
1659
1660
|
// changing from crypto to crypto: just update the subscription
|
|
1661
|
+
const payer = getSubscriptionPaymentAddress(subscription, paymentMethod.type);
|
|
1660
1662
|
delegation = await isDelegationSufficientForPayment({
|
|
1661
1663
|
paymentMethod,
|
|
1662
1664
|
paymentCurrency,
|
|
1663
|
-
userDid:
|
|
1665
|
+
userDid: payer,
|
|
1664
1666
|
amount: getFastCheckoutAmount(lineItems, 'subscription', paymentCurrency.id, false),
|
|
1665
1667
|
});
|
|
1666
1668
|
const noStake = subscription.billing_thresholds?.no_stake;
|
|
@@ -1679,7 +1681,10 @@ router.post('/:id/change-payment', authPortal, async (req, res) => {
|
|
|
1679
1681
|
default_payment_method_id: paymentMethod.id,
|
|
1680
1682
|
payment_settings: {
|
|
1681
1683
|
payment_method_types: [paymentMethod.type],
|
|
1682
|
-
payment_method_options: {
|
|
1684
|
+
payment_method_options: {
|
|
1685
|
+
...(subscription.payment_settings?.payment_method_options || {}),
|
|
1686
|
+
[paymentMethod.type]: { payer },
|
|
1687
|
+
},
|
|
1683
1688
|
},
|
|
1684
1689
|
});
|
|
1685
1690
|
logger.info('Subscription payment change done on delegation enough', {
|
package/blocklet.yml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.11",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "blocklet dev --open",
|
|
6
6
|
"lint": "tsc --noEmit && eslint src api/src --ext .mjs,.js,.jsx,.ts,.tsx",
|
|
@@ -46,20 +46,20 @@
|
|
|
46
46
|
"@abtnode/cron": "^1.16.46",
|
|
47
47
|
"@arcblock/did": "^1.21.0",
|
|
48
48
|
"@arcblock/did-auth-storage-nedb": "^1.7.1",
|
|
49
|
-
"@arcblock/did-connect": "^3.0.
|
|
49
|
+
"@arcblock/did-connect": "^3.0.37",
|
|
50
50
|
"@arcblock/did-util": "^1.21.0",
|
|
51
51
|
"@arcblock/jwt": "^1.21.0",
|
|
52
|
-
"@arcblock/ux": "^3.0.
|
|
52
|
+
"@arcblock/ux": "^3.0.37",
|
|
53
53
|
"@arcblock/validator": "^1.21.0",
|
|
54
|
-
"@blocklet/did-space-js": "^1.1.
|
|
54
|
+
"@blocklet/did-space-js": "^1.1.13",
|
|
55
55
|
"@blocklet/error": "^0.2.5",
|
|
56
56
|
"@blocklet/js-sdk": "^1.16.46",
|
|
57
57
|
"@blocklet/logger": "^1.16.46",
|
|
58
|
-
"@blocklet/payment-react": "1.19.
|
|
58
|
+
"@blocklet/payment-react": "1.19.11",
|
|
59
59
|
"@blocklet/sdk": "^1.16.46",
|
|
60
|
-
"@blocklet/ui-react": "^3.0.
|
|
61
|
-
"@blocklet/uploader": "^0.2.
|
|
62
|
-
"@blocklet/xss": "^0.2.
|
|
60
|
+
"@blocklet/ui-react": "^3.0.37",
|
|
61
|
+
"@blocklet/uploader": "^0.2.5",
|
|
62
|
+
"@blocklet/xss": "^0.2.3",
|
|
63
63
|
"@mui/icons-material": "^7.1.2",
|
|
64
64
|
"@mui/lab": "7.0.0-beta.14",
|
|
65
65
|
"@mui/material": "^7.1.2",
|
|
@@ -124,7 +124,7 @@
|
|
|
124
124
|
"devDependencies": {
|
|
125
125
|
"@abtnode/types": "^1.16.46",
|
|
126
126
|
"@arcblock/eslint-config-ts": "^0.3.3",
|
|
127
|
-
"@blocklet/payment-types": "1.19.
|
|
127
|
+
"@blocklet/payment-types": "1.19.11",
|
|
128
128
|
"@types/cookie-parser": "^1.4.9",
|
|
129
129
|
"@types/cors": "^2.8.19",
|
|
130
130
|
"@types/debug": "^4.1.12",
|
|
@@ -170,5 +170,5 @@
|
|
|
170
170
|
"parser": "typescript"
|
|
171
171
|
}
|
|
172
172
|
},
|
|
173
|
-
"gitHead": "
|
|
173
|
+
"gitHead": "8742626f35fe37db1691744217cfa13547e91591"
|
|
174
174
|
}
|
|
@@ -4,10 +4,11 @@ import type { TProductExpanded } from '@blocklet/payment-types';
|
|
|
4
4
|
import { AddOutlined } from '@mui/icons-material';
|
|
5
5
|
import { Avatar, Box, ListSubheader, MenuItem, Select, Stack, Typography } from '@mui/material';
|
|
6
6
|
import cloneDeep from 'lodash/cloneDeep';
|
|
7
|
-
import { useState } from 'react';
|
|
7
|
+
import { useRef, useState } from 'react';
|
|
8
8
|
import type { LiteralUnion } from 'type-fest';
|
|
9
9
|
|
|
10
10
|
import Empty from '@arcblock/ux/lib/Empty';
|
|
11
|
+
import useBus, { EventAction } from 'use-bus';
|
|
11
12
|
import { useProductsContext } from '../../contexts/products';
|
|
12
13
|
|
|
13
14
|
type Props = {
|
|
@@ -53,14 +54,26 @@ export default function ProductSelect({
|
|
|
53
54
|
const { products } = useProductsContext();
|
|
54
55
|
const { settings } = usePaymentContext();
|
|
55
56
|
const [value, setValue] = useState('');
|
|
57
|
+
const valueRef = useRef(value);
|
|
56
58
|
const size = { width: 16, height: 16 };
|
|
57
59
|
|
|
58
60
|
const handleSelect = (e: any) => {
|
|
59
61
|
setValue(e.target.value);
|
|
62
|
+
valueRef.current = e.target.value;
|
|
60
63
|
setMode('waiting');
|
|
61
64
|
onSelect(e.target.value);
|
|
62
65
|
};
|
|
63
66
|
|
|
67
|
+
useBus(
|
|
68
|
+
(event: EventAction) => event.type === 'product.select.price.removed',
|
|
69
|
+
(event: EventAction) => {
|
|
70
|
+
if (valueRef.current === event.priceId) {
|
|
71
|
+
setValue('');
|
|
72
|
+
valueRef.current = '';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
|
|
64
77
|
const items = (callback?: any) => {
|
|
65
78
|
const filteredProducts = filterProducts(products, hasSelected, filterPrice);
|
|
66
79
|
|
|
@@ -5,6 +5,7 @@ import type { PricingTableItem, TProduct, TProductExpanded } from '@blocklet/pay
|
|
|
5
5
|
import { Box, Stack } from '@mui/material';
|
|
6
6
|
import { useSetState } from 'ahooks';
|
|
7
7
|
|
|
8
|
+
import { dispatch } from 'use-bus';
|
|
8
9
|
import { useProductsContext } from '../../contexts/products';
|
|
9
10
|
import { getPriceFromProducts } from '../../libs/util';
|
|
10
11
|
import Actions from '../actions';
|
|
@@ -74,7 +75,13 @@ export default function ProductItem({ product, prices, valid, onUpdate, onRemove
|
|
|
74
75
|
key={x.index}
|
|
75
76
|
prefix={`items.${x.index}`}
|
|
76
77
|
price={getPriceFromProducts(products, x.price_id) as any}
|
|
77
|
-
onRemove={() =>
|
|
78
|
+
onRemove={() => {
|
|
79
|
+
onRemove(x.index);
|
|
80
|
+
dispatch({
|
|
81
|
+
type: 'product.select.price.removed',
|
|
82
|
+
priceId: x.price_id,
|
|
83
|
+
});
|
|
84
|
+
}}
|
|
78
85
|
/>
|
|
79
86
|
))}
|
|
80
87
|
</Stack>
|
|
@@ -33,7 +33,12 @@ const fetchProducts = (forceRefresh = false): Promise<{ list: TProductExpanded[]
|
|
|
33
33
|
// eslint-disable-next-line react/prop-types
|
|
34
34
|
function ProductsProvider({ children }: { children: any }): JSX.Element {
|
|
35
35
|
const { data, error, run, loading } = useRequest((forceRefresh = false) => fetchProducts(forceRefresh));
|
|
36
|
-
useBus('
|
|
36
|
+
useBus('product.created', () => run(true), []);
|
|
37
|
+
useBus('product.updated', () => run(true), []);
|
|
38
|
+
useBus('product.deleted', () => run(true), []);
|
|
39
|
+
useBus('price.created', () => run(true), []);
|
|
40
|
+
useBus('price.updated', () => run(true), []);
|
|
41
|
+
useBus('price.deleted', () => run(true), []);
|
|
37
42
|
if (error) {
|
|
38
43
|
return <Alert severity="error">{error.message}</Alert>;
|
|
39
44
|
}
|
|
@@ -102,6 +102,16 @@ export default function PaymentLinkDetail(props: { id: string }) {
|
|
|
102
102
|
setState((prev) => ({ editing: { ...prev.editing, metadata: true } }));
|
|
103
103
|
};
|
|
104
104
|
|
|
105
|
+
const renderConfirmPage = (record: any) => {
|
|
106
|
+
if (record.type === 'hosted_confirmation') {
|
|
107
|
+
return <Typography>{record.hosted_confirmation.custom_message || t('common.none')}</Typography>;
|
|
108
|
+
}
|
|
109
|
+
if (record.type === 'redirect' && record.redirect.url) {
|
|
110
|
+
return <Link to={record.redirect.url}>{record.redirect.url}</Link>;
|
|
111
|
+
}
|
|
112
|
+
return t('common.none');
|
|
113
|
+
};
|
|
114
|
+
|
|
105
115
|
return (
|
|
106
116
|
<Grid container spacing={4} sx={{ mb: 4 }}>
|
|
107
117
|
<Grid size={12}>
|
|
@@ -362,7 +372,10 @@ export default function PaymentLinkDetail(props: { id: string }) {
|
|
|
362
372
|
value={data.phone_number_collection?.enabled ? t('common.yes') : t('common.no')}
|
|
363
373
|
/>
|
|
364
374
|
|
|
365
|
-
<InfoRow
|
|
375
|
+
<InfoRow
|
|
376
|
+
label={t('admin.paymentLink.showConfirmPage')}
|
|
377
|
+
value={renderConfirmPage(data.after_completion)}
|
|
378
|
+
/>
|
|
366
379
|
<InfoRow label={t('admin.paymentLink.mintNft')} value={data.nft_mint_settings?.factory || ''} />
|
|
367
380
|
<InfoRow label={t('common.createdAt')} value={formatTime(data.created_at)} />
|
|
368
381
|
<InfoRow label={t('common.updatedAt')} value={formatTime(data.updated_at)} />
|
|
@@ -6,6 +6,7 @@ import { useSetState } from 'ahooks';
|
|
|
6
6
|
import noop from 'lodash/noop';
|
|
7
7
|
import { useNavigate } from 'react-router-dom';
|
|
8
8
|
|
|
9
|
+
import { dispatch } from 'use-bus';
|
|
9
10
|
import Actions from '../../../../components/actions';
|
|
10
11
|
import EditPrice from '../../../../components/product/edit-price';
|
|
11
12
|
|
|
@@ -45,6 +46,7 @@ export default function PriceActions({
|
|
|
45
46
|
setState({ loading: true });
|
|
46
47
|
await api.put(`/api/prices/${data.id}`, updates).then((res) => res.data);
|
|
47
48
|
Toast.success(t('common.saved'));
|
|
49
|
+
dispatch('price.updated');
|
|
48
50
|
onChange(state.action);
|
|
49
51
|
} catch (err) {
|
|
50
52
|
console.error(err);
|
|
@@ -59,6 +61,7 @@ export default function PriceActions({
|
|
|
59
61
|
setState({ loading: true });
|
|
60
62
|
await api.put(`/api/prices/${data.id}/archive`).then((res) => res.data);
|
|
61
63
|
Toast.success(t('common.saved'));
|
|
64
|
+
dispatch('price.updated');
|
|
62
65
|
onChange(state.action);
|
|
63
66
|
} catch (err) {
|
|
64
67
|
console.error(err);
|
|
@@ -72,6 +75,7 @@ export default function PriceActions({
|
|
|
72
75
|
setState({ loading: true });
|
|
73
76
|
await api.delete(`/api/prices/${data.id}`).then((res) => res.data);
|
|
74
77
|
Toast.success(t('common.removed'));
|
|
78
|
+
dispatch('price.deleted');
|
|
75
79
|
onChange(state.action);
|
|
76
80
|
} catch (err) {
|
|
77
81
|
console.error(err);
|
|
@@ -17,6 +17,7 @@ import { styled } from '@mui/system';
|
|
|
17
17
|
import { useRequest, useSetState } from 'ahooks';
|
|
18
18
|
import { Link, useNavigate } from 'react-router-dom';
|
|
19
19
|
|
|
20
|
+
import { dispatch } from 'use-bus';
|
|
20
21
|
import Copyable from '../../../../components/copyable';
|
|
21
22
|
import Currency from '../../../../components/currency';
|
|
22
23
|
import EventList from '../../../../components/event/list';
|
|
@@ -68,6 +69,7 @@ export default function PriceDetail(props: { id: string }) {
|
|
|
68
69
|
setState((prev) => ({ loading: { ...prev.loading, [key]: true } }));
|
|
69
70
|
await api.put(`/api/prices/${props.id}`, updates).then((res) => res.data);
|
|
70
71
|
Toast.success(t('common.saved'));
|
|
72
|
+
dispatch('price.updated');
|
|
71
73
|
runAsync();
|
|
72
74
|
} catch (err) {
|
|
73
75
|
console.error(err);
|
|
@@ -159,7 +159,7 @@ export default function ProductsCreate({
|
|
|
159
159
|
Toast.success(t('admin.product.saved'));
|
|
160
160
|
methods.reset();
|
|
161
161
|
dispatch('drawer.submitted');
|
|
162
|
-
dispatch('
|
|
162
|
+
dispatch('product.created');
|
|
163
163
|
if (onSubmitCallback) {
|
|
164
164
|
onSubmitCallback();
|
|
165
165
|
}
|
|
@@ -20,6 +20,7 @@ import { useRequest, useSetState } from 'ahooks';
|
|
|
20
20
|
import { useNavigate } from 'react-router-dom';
|
|
21
21
|
|
|
22
22
|
import { isEmpty } from 'lodash';
|
|
23
|
+
import { dispatch } from 'use-bus';
|
|
23
24
|
import Copyable from '../../../../components/copyable';
|
|
24
25
|
import EventList from '../../../../components/event/list';
|
|
25
26
|
import InfoMetric from '../../../../components/info-metric';
|
|
@@ -78,6 +79,7 @@ export default function ProductDetail(props: { id: string }) {
|
|
|
78
79
|
setState((prev) => ({ loading: { ...prev.loading, [key]: true } }));
|
|
79
80
|
await api.put(`/api/products/${props.id}`, updates).then((res) => res.data);
|
|
80
81
|
Toast.success(t('common.saved'));
|
|
82
|
+
dispatch('product.updated');
|
|
81
83
|
runAsync();
|
|
82
84
|
} catch (err) {
|
|
83
85
|
console.error(err);
|
|
@@ -92,6 +94,7 @@ export default function ProductDetail(props: { id: string }) {
|
|
|
92
94
|
setState((prev) => ({ loading: { ...prev.loading, price: true } }));
|
|
93
95
|
await api.post('/api/prices', { ...price, product_id: props.id });
|
|
94
96
|
Toast.success(t('common.saved'));
|
|
97
|
+
dispatch('price.created');
|
|
95
98
|
runAsync();
|
|
96
99
|
} catch (err) {
|
|
97
100
|
console.error(err);
|
|
@@ -60,7 +60,9 @@ export default function ProductsList() {
|
|
|
60
60
|
setData(res);
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
useBus('
|
|
63
|
+
useBus('product.created', () => refresh(), []);
|
|
64
|
+
useBus('product.updated', () => refresh(), []);
|
|
65
|
+
useBus('product.deleted', () => refresh(), []);
|
|
64
66
|
|
|
65
67
|
useEffect(() => {
|
|
66
68
|
refresh();
|