payment-kit 1.18.33 → 1.18.35
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/session.ts +1 -1
- package/api/src/routes/checkout-sessions.ts +232 -44
- package/api/src/routes/subscriptions.ts +12 -3
- package/api/src/store/models/subscription.ts +2 -2
- package/blocklet.yml +1 -1
- package/package.json +7 -7
- package/scripts/sdk.js +65 -1
- package/src/components/customer/link.tsx +32 -57
- package/src/components/invoice/list.tsx +1 -1
- package/src/components/payouts/list.tsx +8 -2
- package/src/components/subscription/portal/actions.tsx +12 -0
- package/src/libs/util.ts +0 -13
- package/src/pages/admin/customers/customers/detail.tsx +2 -2
- package/src/pages/admin/customers/customers/index.tsx +1 -2
- package/src/pages/admin/payments/payouts/detail.tsx +1 -1
- package/src/pages/customer/invoice/detail.tsx +1 -0
- package/src/pages/customer/invoice/past-due.tsx +7 -1
- package/src/pages/customer/recharge/account.tsx +8 -1
- package/src/pages/customer/recharge/subscription.tsx +8 -1
- package/src/pages/customer/subscription/change-payment.tsx +7 -1
- package/src/pages/customer/subscription/change-plan.tsx +1 -0
- package/src/pages/customer/subscription/embed.tsx +1 -0
package/api/src/libs/session.ts
CHANGED
|
@@ -773,7 +773,7 @@ async function createOrUpdateSubscription(params: {
|
|
|
773
773
|
notification_settings: checkoutSession.subscription_data.notification_settings,
|
|
774
774
|
}
|
|
775
775
|
: {}),
|
|
776
|
-
...omit(checkoutSession.metadata || {}, ['days_until_due', 'days_until_cancel']),
|
|
776
|
+
...omit(checkoutSession.metadata || {}, ['days_until_due', 'days_until_cancel', 'page_info']),
|
|
777
777
|
...(itemsSubscriptionData.metadata || {}),
|
|
778
778
|
...metadata,
|
|
779
779
|
};
|
|
@@ -598,6 +598,7 @@ async function processSubscriptionFastCheckout({
|
|
|
598
598
|
lineItems,
|
|
599
599
|
trialEnd,
|
|
600
600
|
now,
|
|
601
|
+
executePayment = true,
|
|
601
602
|
}: {
|
|
602
603
|
checkoutSession: CheckoutSession;
|
|
603
604
|
customer: Customer;
|
|
@@ -608,6 +609,7 @@ async function processSubscriptionFastCheckout({
|
|
|
608
609
|
lineItems: TLineItemExpanded[];
|
|
609
610
|
trialEnd: number;
|
|
610
611
|
now: number;
|
|
612
|
+
executePayment?: boolean;
|
|
611
613
|
}): Promise<{
|
|
612
614
|
success: boolean;
|
|
613
615
|
invoices?: Invoice[];
|
|
@@ -651,38 +653,42 @@ async function processSubscriptionFastCheckout({
|
|
|
651
653
|
subscriptionIds: subscriptions.map((s) => s.id),
|
|
652
654
|
});
|
|
653
655
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
+
if (executePayment) {
|
|
657
|
+
// Update payment settings for all subscriptions
|
|
658
|
+
await Promise.all(subscriptions.map((sub) => sub.update({ payment_settings: paymentSettings })));
|
|
656
659
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
);
|
|
673
|
-
|
|
674
|
-
// Add subscription cycle jobs
|
|
675
|
-
await Promise.all(subscriptions.map((sub) => addSubscriptionJob(sub, 'cycle', false, sub.trial_end)));
|
|
660
|
+
// Create invoices for all subscriptions
|
|
661
|
+
const { invoices } = await ensureInvoicesForSubscriptions({
|
|
662
|
+
checkoutSession,
|
|
663
|
+
customer,
|
|
664
|
+
subscriptions,
|
|
665
|
+
});
|
|
666
|
+
// Update invoice settings and push to queue
|
|
667
|
+
await Promise.all(
|
|
668
|
+
invoices.map(async (invoice) => {
|
|
669
|
+
if (invoice) {
|
|
670
|
+
await invoice.update({ auto_advance: true, payment_settings: paymentSettings });
|
|
671
|
+
invoiceQueue.push({ id: invoice.id, job: { invoiceId: invoice.id, retryOnError: false } });
|
|
672
|
+
}
|
|
673
|
+
})
|
|
674
|
+
);
|
|
676
675
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
subscriptionIds: subscriptions.map((s) => s.id),
|
|
680
|
-
invoiceIds: invoices.map((inv) => inv.id),
|
|
681
|
-
});
|
|
676
|
+
// Add subscription cycle jobs
|
|
677
|
+
await Promise.all(subscriptions.map((sub) => addSubscriptionJob(sub, 'cycle', false, sub.trial_end)));
|
|
682
678
|
|
|
679
|
+
logger.info('Created and queued invoices for fast checkout with subscriptions', {
|
|
680
|
+
checkoutSessionId: checkoutSession.id,
|
|
681
|
+
subscriptionIds: subscriptions.map((s) => s.id),
|
|
682
|
+
invoiceIds: invoices.map((inv) => inv.id),
|
|
683
|
+
});
|
|
684
|
+
return {
|
|
685
|
+
success: true,
|
|
686
|
+
invoices,
|
|
687
|
+
};
|
|
688
|
+
}
|
|
683
689
|
return {
|
|
684
690
|
success: true,
|
|
685
|
-
invoices,
|
|
691
|
+
invoices: [],
|
|
686
692
|
};
|
|
687
693
|
} catch (error) {
|
|
688
694
|
logger.error('Error processing subscription fast checkout', {
|
|
@@ -1217,6 +1223,7 @@ router.put('/:id/submit', user, ensureCheckoutSessionOpen, async (req, res) => {
|
|
|
1217
1223
|
|
|
1218
1224
|
const isPayment = checkoutSession.mode === 'payment';
|
|
1219
1225
|
let canFastPay = isPayment && canPayWithDelegation(paymentIntent?.beneficiaries || []);
|
|
1226
|
+
let fastPayInfo = null;
|
|
1220
1227
|
let delegation: SufficientForPaymentResult | null = null;
|
|
1221
1228
|
if (isPayment && paymentIntent && canFastPay) {
|
|
1222
1229
|
// if we can complete purchase without any wallet interaction
|
|
@@ -1226,24 +1233,12 @@ router.put('/:id/submit', user, ensureCheckoutSessionOpen, async (req, res) => {
|
|
|
1226
1233
|
userDid: customer.did,
|
|
1227
1234
|
amount: fastCheckoutAmount,
|
|
1228
1235
|
});
|
|
1229
|
-
if (balance.sufficient) {
|
|
1230
|
-
logger.info(`CheckoutSession ${checkoutSession.id} will pay from balance ${paymentIntent?.id}`);
|
|
1231
|
-
}
|
|
1232
|
-
if (delegation.sufficient) {
|
|
1233
|
-
logger.info(`CheckoutSession ${checkoutSession.id} will pay from delegation ${paymentIntent?.id}`);
|
|
1234
|
-
}
|
|
1235
1236
|
if (balance.sufficient || delegation.sufficient) {
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
} else {
|
|
1242
|
-
paymentQueue.push({
|
|
1243
|
-
id: paymentIntent.id,
|
|
1244
|
-
job: { paymentIntentId: paymentIntent.id, paymentSettings, retryOnError: false },
|
|
1245
|
-
});
|
|
1246
|
-
}
|
|
1237
|
+
fastPayInfo = {
|
|
1238
|
+
type: balance.sufficient ? 'balance' : 'delegation',
|
|
1239
|
+
amount: fastCheckoutAmount,
|
|
1240
|
+
payer: customer.did,
|
|
1241
|
+
};
|
|
1247
1242
|
}
|
|
1248
1243
|
} else if (
|
|
1249
1244
|
paymentMethod.type === 'arcblock' &&
|
|
@@ -1261,6 +1256,7 @@ router.put('/:id/submit', user, ensureCheckoutSessionOpen, async (req, res) => {
|
|
|
1261
1256
|
lineItems,
|
|
1262
1257
|
trialEnd,
|
|
1263
1258
|
now,
|
|
1259
|
+
executePayment: false,
|
|
1264
1260
|
});
|
|
1265
1261
|
|
|
1266
1262
|
if (!result.success) {
|
|
@@ -1272,6 +1268,11 @@ router.put('/:id/submit', user, ensureCheckoutSessionOpen, async (req, res) => {
|
|
|
1272
1268
|
sufficient: true,
|
|
1273
1269
|
};
|
|
1274
1270
|
canFastPay = true;
|
|
1271
|
+
fastPayInfo = {
|
|
1272
|
+
type: 'delegation',
|
|
1273
|
+
amount: fastCheckoutAmount,
|
|
1274
|
+
payer: customer.did,
|
|
1275
|
+
};
|
|
1275
1276
|
}
|
|
1276
1277
|
}
|
|
1277
1278
|
|
|
@@ -1403,6 +1404,7 @@ router.put('/:id/submit', user, ensureCheckoutSessionOpen, async (req, res) => {
|
|
|
1403
1404
|
customer,
|
|
1404
1405
|
delegation: canFastPay ? delegation : null,
|
|
1405
1406
|
balance: canFastPay ? balance : null,
|
|
1407
|
+
fastPayInfo,
|
|
1406
1408
|
});
|
|
1407
1409
|
} catch (err) {
|
|
1408
1410
|
logger.error('Error submitting checkout session', {
|
|
@@ -1480,6 +1482,192 @@ router.put('/:id/donate-submit', ensureCheckoutSessionOpen, async (req, res) =>
|
|
|
1480
1482
|
}
|
|
1481
1483
|
});
|
|
1482
1484
|
|
|
1485
|
+
router.post('/:id/fast-checkout-confirm', user, ensureCheckoutSessionOpen, async (req, res) => {
|
|
1486
|
+
try {
|
|
1487
|
+
if (!req.user) {
|
|
1488
|
+
return res.status(403).json({ code: 'REQUIRE_LOGIN', error: 'Please login to continue' });
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
const checkoutSession = req.doc as CheckoutSession;
|
|
1492
|
+
|
|
1493
|
+
if (checkoutSession.line_items) {
|
|
1494
|
+
try {
|
|
1495
|
+
await validateInventory(checkoutSession.line_items);
|
|
1496
|
+
} catch (err) {
|
|
1497
|
+
logger.error('validateInventory failed', {
|
|
1498
|
+
error: err,
|
|
1499
|
+
line_items: checkoutSession.line_items,
|
|
1500
|
+
checkoutSessionId: checkoutSession.id,
|
|
1501
|
+
});
|
|
1502
|
+
return res.status(400).json({ error: err.message });
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
// validate cross sell
|
|
1506
|
+
if (checkoutSession.cross_sell_behavior === 'required') {
|
|
1507
|
+
if (checkoutSession.line_items.some((x) => x.cross_sell) === false) {
|
|
1508
|
+
const result = await getCrossSellItem(checkoutSession);
|
|
1509
|
+
// @ts-ignore
|
|
1510
|
+
if (result.id) {
|
|
1511
|
+
return res
|
|
1512
|
+
.status(400)
|
|
1513
|
+
.json({ code: 'REQUIRE_CROSS_SELL', error: 'Please select cross sell product to continue' });
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
const paymentCurrency = await PaymentCurrency.findByPk(checkoutSession.currency_id);
|
|
1519
|
+
if (!paymentCurrency) {
|
|
1520
|
+
return res.status(400).json({ error: 'Payment currency not found' });
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
const paymentMethod = await PaymentMethod.findByPk(paymentCurrency.payment_method_id);
|
|
1524
|
+
if (!paymentMethod) {
|
|
1525
|
+
return res.status(400).json({ error: 'Payment method not found' });
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
if (paymentMethod.type !== 'arcblock') {
|
|
1529
|
+
return res.status(400).json({ error: 'Payment method not supported for fast checkout' });
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
const customer = await Customer.findByPkOrDid(req.user.did);
|
|
1533
|
+
if (!customer) {
|
|
1534
|
+
return res.status(400).json({ error: '' });
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
// check if customer can make new purchase
|
|
1538
|
+
const canMakeNewPurchase = await customer.canMakeNewPurchase(checkoutSession.invoice_id);
|
|
1539
|
+
if (!canMakeNewPurchase) {
|
|
1540
|
+
return res.status(403).json({
|
|
1541
|
+
code: 'CUSTOMER_LIMITED',
|
|
1542
|
+
error: 'Customer can not make new purchase, maybe you have unpaid invoices from previous purchases',
|
|
1543
|
+
});
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
const { lineItems, trialInDays, trialEnd, now } = await calculateAndUpdateAmount(
|
|
1547
|
+
checkoutSession,
|
|
1548
|
+
paymentCurrency.id,
|
|
1549
|
+
true
|
|
1550
|
+
);
|
|
1551
|
+
|
|
1552
|
+
let paymentIntent: PaymentIntent | null = null;
|
|
1553
|
+
if (checkoutSession.mode === 'payment') {
|
|
1554
|
+
const result = await createOrUpdatePaymentIntent(
|
|
1555
|
+
checkoutSession,
|
|
1556
|
+
paymentMethod,
|
|
1557
|
+
paymentCurrency,
|
|
1558
|
+
lineItems,
|
|
1559
|
+
customer.id,
|
|
1560
|
+
customer.email
|
|
1561
|
+
);
|
|
1562
|
+
paymentIntent = result.paymentIntent;
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
const fastCheckoutAmount = getFastCheckoutAmount(
|
|
1566
|
+
lineItems,
|
|
1567
|
+
checkoutSession.mode,
|
|
1568
|
+
paymentCurrency.id,
|
|
1569
|
+
trialInDays > 0 || trialEnd > now
|
|
1570
|
+
);
|
|
1571
|
+
|
|
1572
|
+
const paymentSettings = {
|
|
1573
|
+
payment_method_types: checkoutSession.payment_method_types,
|
|
1574
|
+
payment_method_options: {
|
|
1575
|
+
[paymentMethod.type]: { payer: customer.did },
|
|
1576
|
+
},
|
|
1577
|
+
};
|
|
1578
|
+
const balance = isCreditSufficientForPayment({
|
|
1579
|
+
paymentMethod,
|
|
1580
|
+
paymentCurrency,
|
|
1581
|
+
customer,
|
|
1582
|
+
amount: fastCheckoutAmount,
|
|
1583
|
+
});
|
|
1584
|
+
|
|
1585
|
+
const isPayment = checkoutSession.mode === 'payment';
|
|
1586
|
+
let fastPaid = false;
|
|
1587
|
+
let canFastPay = isPayment && canPayWithDelegation(paymentIntent?.beneficiaries || []);
|
|
1588
|
+
let delegation: SufficientForPaymentResult | null = null;
|
|
1589
|
+
if (isPayment && paymentIntent && canFastPay) {
|
|
1590
|
+
// if we can complete purchase without any wallet interaction
|
|
1591
|
+
delegation = await isDelegationSufficientForPayment({
|
|
1592
|
+
paymentMethod,
|
|
1593
|
+
paymentCurrency,
|
|
1594
|
+
userDid: customer.did,
|
|
1595
|
+
amount: fastCheckoutAmount,
|
|
1596
|
+
});
|
|
1597
|
+
if (balance.sufficient) {
|
|
1598
|
+
logger.info(`CheckoutSession ${checkoutSession.id} will pay from balance ${paymentIntent?.id}`);
|
|
1599
|
+
}
|
|
1600
|
+
if (delegation.sufficient) {
|
|
1601
|
+
logger.info(`CheckoutSession ${checkoutSession.id} will pay from delegation ${paymentIntent?.id}`);
|
|
1602
|
+
}
|
|
1603
|
+
if (balance.sufficient || delegation.sufficient) {
|
|
1604
|
+
fastPaid = true;
|
|
1605
|
+
await paymentIntent.update({ status: 'requires_capture' });
|
|
1606
|
+
const { invoice } = await ensureInvoiceForCheckout({ checkoutSession, customer, paymentIntent });
|
|
1607
|
+
if (invoice) {
|
|
1608
|
+
await invoice.update({ auto_advance: true, payment_settings: paymentSettings });
|
|
1609
|
+
invoiceQueue.push({ id: invoice.id, job: { invoiceId: invoice.id, retryOnError: false } });
|
|
1610
|
+
} else {
|
|
1611
|
+
paymentQueue.push({
|
|
1612
|
+
id: paymentIntent.id,
|
|
1613
|
+
job: { paymentIntentId: paymentIntent.id, paymentSettings, retryOnError: false },
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
} else if (
|
|
1618
|
+
paymentMethod.type === 'arcblock' &&
|
|
1619
|
+
checkoutSession.mode === 'subscription' &&
|
|
1620
|
+
checkoutSession.subscription_data?.no_stake
|
|
1621
|
+
) {
|
|
1622
|
+
const subscriptionIds = getCheckoutSessionSubscriptionIds(checkoutSession);
|
|
1623
|
+
const subscriptions = await Subscription.findAll({ where: { id: subscriptionIds } });
|
|
1624
|
+
// if we can complete purchase without any wallet interaction
|
|
1625
|
+
const result = await processSubscriptionFastCheckout({
|
|
1626
|
+
checkoutSession,
|
|
1627
|
+
customer,
|
|
1628
|
+
subscriptions,
|
|
1629
|
+
paymentMethod,
|
|
1630
|
+
paymentCurrency,
|
|
1631
|
+
paymentSettings,
|
|
1632
|
+
lineItems,
|
|
1633
|
+
trialEnd,
|
|
1634
|
+
now,
|
|
1635
|
+
});
|
|
1636
|
+
if (!result.success) {
|
|
1637
|
+
logger.warn(`Fast checkout processing failed: ${result.message}`, {
|
|
1638
|
+
checkoutSessionId: checkoutSession.id,
|
|
1639
|
+
});
|
|
1640
|
+
} else {
|
|
1641
|
+
fastPaid = true;
|
|
1642
|
+
delegation = {
|
|
1643
|
+
sufficient: true,
|
|
1644
|
+
};
|
|
1645
|
+
canFastPay = true;
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
logger.info('Checkout session submitted successfully', {
|
|
1650
|
+
sessionId: req.params.id,
|
|
1651
|
+
paymentIntentId: paymentIntent?.id,
|
|
1652
|
+
customerId: customer?.id,
|
|
1653
|
+
});
|
|
1654
|
+
|
|
1655
|
+
return res.json({
|
|
1656
|
+
paymentIntent,
|
|
1657
|
+
checkoutSession,
|
|
1658
|
+
customer,
|
|
1659
|
+
fastPaid,
|
|
1660
|
+
});
|
|
1661
|
+
} catch (err) {
|
|
1662
|
+
logger.error('Error confirming fast checkout', {
|
|
1663
|
+
sessionId: req.params.id,
|
|
1664
|
+
error: err.message,
|
|
1665
|
+
stack: err.stack,
|
|
1666
|
+
});
|
|
1667
|
+
res.status(500).json({ code: err.code, error: err.message });
|
|
1668
|
+
}
|
|
1669
|
+
});
|
|
1670
|
+
|
|
1483
1671
|
// upsell
|
|
1484
1672
|
router.put('/:id/upsell', user, ensureCheckoutSessionOpen, async (req, res) => {
|
|
1485
1673
|
try {
|
|
@@ -2032,21 +2032,30 @@ router.post('/:id/overdraft-protection', authPortal, async (req, res) => {
|
|
|
2032
2032
|
if (overdraftProtectionError) {
|
|
2033
2033
|
return res.status(400).json({ error: `Overdraft protection invalid: ${overdraftProtectionError.message}` });
|
|
2034
2034
|
}
|
|
2035
|
+
|
|
2035
2036
|
const subscription = await Subscription.findByPk(req.params.id);
|
|
2036
2037
|
if (!subscription) {
|
|
2037
2038
|
return res.status(404).json({ error: 'Subscription not found' });
|
|
2038
2039
|
}
|
|
2040
|
+
const previousOverdraftProtection = {
|
|
2041
|
+
enabled: subscription.overdraft_protection?.enabled || false,
|
|
2042
|
+
payment_method_id: subscription.overdraft_protection?.payment_method_id || null,
|
|
2043
|
+
payment_details: subscription.overdraft_protection?.payment_details || null,
|
|
2044
|
+
};
|
|
2039
2045
|
const customer = await Customer.findByPkOrDid(req.user?.did as string);
|
|
2040
2046
|
if (!customer) {
|
|
2041
2047
|
return res.status(404).json({ error: 'Customer not found' });
|
|
2042
2048
|
}
|
|
2043
|
-
const { remaining, used } = await isSubscriptionOverdraftProtectionEnabled(subscription);
|
|
2049
|
+
const { remaining, used, unused } = await isSubscriptionOverdraftProtectionEnabled(subscription);
|
|
2050
|
+
if (unused === '0' && !amount && enabled) {
|
|
2051
|
+
return res.status(400).json({ error: 'Please add stake to enable SubGuard™' });
|
|
2052
|
+
}
|
|
2044
2053
|
if (returnStake && remaining !== '0' && !enabled) {
|
|
2045
2054
|
// disable overdraft protection
|
|
2046
2055
|
await subscription.update({
|
|
2047
2056
|
// @ts-ignore
|
|
2048
2057
|
overdraft_protection: {
|
|
2049
|
-
...
|
|
2058
|
+
...previousOverdraftProtection,
|
|
2050
2059
|
enabled: false,
|
|
2051
2060
|
},
|
|
2052
2061
|
});
|
|
@@ -2077,7 +2086,7 @@ router.post('/:id/overdraft-protection', authPortal, async (req, res) => {
|
|
|
2077
2086
|
await subscription.update({
|
|
2078
2087
|
// @ts-ignore
|
|
2079
2088
|
overdraft_protection: {
|
|
2080
|
-
...
|
|
2089
|
+
...previousOverdraftProtection,
|
|
2081
2090
|
enabled,
|
|
2082
2091
|
},
|
|
2083
2092
|
});
|
|
@@ -320,11 +320,11 @@ export class Subscription extends Model<InferAttributes<Subscription>, InferCrea
|
|
|
320
320
|
overdraft_protection: {
|
|
321
321
|
type: DataTypes.JSON,
|
|
322
322
|
allowNull: true,
|
|
323
|
-
defaultValue:
|
|
323
|
+
defaultValue: {
|
|
324
324
|
enabled: false,
|
|
325
325
|
payment_method_id: null,
|
|
326
326
|
payment_details: null,
|
|
327
|
-
}
|
|
327
|
+
},
|
|
328
328
|
},
|
|
329
329
|
},
|
|
330
330
|
{
|
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.35",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "blocklet dev --open",
|
|
6
6
|
"eject": "vite eject",
|
|
@@ -47,16 +47,16 @@
|
|
|
47
47
|
"@abtnode/cron": "^1.16.42",
|
|
48
48
|
"@arcblock/did": "^1.20.2",
|
|
49
49
|
"@arcblock/did-auth-storage-nedb": "^1.7.1",
|
|
50
|
-
"@arcblock/did-connect": "^2.13.
|
|
50
|
+
"@arcblock/did-connect": "^2.13.12",
|
|
51
51
|
"@arcblock/did-util": "^1.20.2",
|
|
52
52
|
"@arcblock/jwt": "^1.20.2",
|
|
53
|
-
"@arcblock/ux": "^2.13.
|
|
53
|
+
"@arcblock/ux": "^2.13.12",
|
|
54
54
|
"@arcblock/validator": "^1.20.2",
|
|
55
55
|
"@blocklet/js-sdk": "^1.16.42",
|
|
56
56
|
"@blocklet/logger": "^1.16.42",
|
|
57
|
-
"@blocklet/payment-react": "1.18.
|
|
57
|
+
"@blocklet/payment-react": "1.18.35",
|
|
58
58
|
"@blocklet/sdk": "^1.16.42",
|
|
59
|
-
"@blocklet/ui-react": "^2.13.
|
|
59
|
+
"@blocklet/ui-react": "^2.13.12",
|
|
60
60
|
"@blocklet/uploader": "^0.1.83",
|
|
61
61
|
"@blocklet/xss": "^0.1.32",
|
|
62
62
|
"@mui/icons-material": "^5.16.6",
|
|
@@ -122,7 +122,7 @@
|
|
|
122
122
|
"devDependencies": {
|
|
123
123
|
"@abtnode/types": "^1.16.42",
|
|
124
124
|
"@arcblock/eslint-config-ts": "^0.3.3",
|
|
125
|
-
"@blocklet/payment-types": "1.18.
|
|
125
|
+
"@blocklet/payment-types": "1.18.35",
|
|
126
126
|
"@types/cookie-parser": "^1.4.7",
|
|
127
127
|
"@types/cors": "^2.8.17",
|
|
128
128
|
"@types/debug": "^4.1.12",
|
|
@@ -168,5 +168,5 @@
|
|
|
168
168
|
"parser": "typescript"
|
|
169
169
|
}
|
|
170
170
|
},
|
|
171
|
-
"gitHead": "
|
|
171
|
+
"gitHead": "802a98b5ca81475f8cd7b9dcbb77fce7240b9788"
|
|
172
172
|
}
|
package/scripts/sdk.js
CHANGED
|
@@ -74,6 +74,70 @@ const checkoutModule = {
|
|
|
74
74
|
return checkoutSession;
|
|
75
75
|
},
|
|
76
76
|
|
|
77
|
+
// 批量订阅 + 免质押 + 自定义表单规则
|
|
78
|
+
async createBatchSubscriptionWithCustomField() {
|
|
79
|
+
const checkoutSession = await payment.checkout.sessions.create({
|
|
80
|
+
mode: 'subscription',
|
|
81
|
+
line_items: [
|
|
82
|
+
{
|
|
83
|
+
price_id: 'price_fQFIS12yi0JR3KePLmitjrhA',
|
|
84
|
+
quantity: 1,
|
|
85
|
+
subscription_data: {
|
|
86
|
+
metadata: { test: 'test price_fQFIS12yi0JR3KePLmitjrhA' },
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
price_id: 'price_PXyI9Duz99eqty1AqbaEc73u',
|
|
91
|
+
quantity: 1,
|
|
92
|
+
subscription_data: {
|
|
93
|
+
metadata: { test: 'test price_PXyI9Duz99eqty1AqbaEc73u' },
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
enable_subscription_grouping: true,
|
|
98
|
+
subscription_data: {
|
|
99
|
+
no_stake: true,
|
|
100
|
+
},
|
|
101
|
+
phone_number_collection: {
|
|
102
|
+
enabled: true,
|
|
103
|
+
},
|
|
104
|
+
billing_address_collection: 'required',
|
|
105
|
+
metadata: {
|
|
106
|
+
page_info: {
|
|
107
|
+
form_purpose_description: {
|
|
108
|
+
en: 'Information collected helps us process your payment and deliver our services.',
|
|
109
|
+
zh: '收集的信息帮助我们处理您的付款并提供服务。',
|
|
110
|
+
},
|
|
111
|
+
field_validation: {
|
|
112
|
+
customer_name: {
|
|
113
|
+
pattern: '^[a-zA-Z\\s]{2,50}$',
|
|
114
|
+
pattern_message: {
|
|
115
|
+
en: 'Name should only contain 2-50 letters and spaces',
|
|
116
|
+
zh: '姓名应只包含2-50个字母和空格',
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
customer_email: {
|
|
120
|
+
pattern: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$',
|
|
121
|
+
pattern_message: {
|
|
122
|
+
en: 'Please enter a valid email address',
|
|
123
|
+
zh: '请输入有效的电子邮件地址',
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
'billing_address.line1': {
|
|
127
|
+
pattern: '^.{5,100}$',
|
|
128
|
+
pattern_message: {
|
|
129
|
+
en: 'Address should be 5-100 characters',
|
|
130
|
+
zh: '地址应为5-100个字符',
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
console.log('createBatchSubscriptionWithCustomField', checkoutSession);
|
|
138
|
+
return checkoutSession;
|
|
139
|
+
},
|
|
140
|
+
|
|
77
141
|
// 批量订阅 + 试用 + 免质押
|
|
78
142
|
async createWithTrial() {
|
|
79
143
|
const checkoutSession = await payment.checkout.sessions.create({
|
|
@@ -461,7 +525,7 @@ const testModules = {
|
|
|
461
525
|
|
|
462
526
|
async function runTest() {
|
|
463
527
|
payment.environments.setTestMode(true);
|
|
464
|
-
await testModules.checkout.
|
|
528
|
+
await testModules.checkout.createBatchSubscriptionWithCustomField();
|
|
465
529
|
}
|
|
466
530
|
|
|
467
531
|
async function main() {
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
import type { TCustomer } from '@blocklet/payment-types';
|
|
2
2
|
import { Link } from 'react-router-dom';
|
|
3
|
-
|
|
3
|
+
import UserCard from '@arcblock/ux/lib/UserCard';
|
|
4
4
|
import { getCustomerAvatar } from '@blocklet/payment-react';
|
|
5
|
-
import DID from '@arcblock/ux/lib/DID';
|
|
6
|
-
import { Box, Typography } from '@mui/material';
|
|
7
|
-
import InfoCard from '../info-card';
|
|
8
5
|
|
|
9
6
|
export default function CustomerLink({
|
|
10
7
|
customer,
|
|
11
8
|
linked,
|
|
12
9
|
linkTo,
|
|
13
10
|
size,
|
|
14
|
-
tooltip,
|
|
15
11
|
}: {
|
|
16
12
|
customer: TCustomer;
|
|
17
13
|
linked?: boolean;
|
|
@@ -22,61 +18,40 @@ export default function CustomerLink({
|
|
|
22
18
|
if (!customer) {
|
|
23
19
|
return null;
|
|
24
20
|
}
|
|
21
|
+
const CustomerCard = (
|
|
22
|
+
// @ts-ignore
|
|
23
|
+
<UserCard
|
|
24
|
+
did={customer?.did}
|
|
25
|
+
showHoverCard
|
|
26
|
+
sx={{
|
|
27
|
+
border: 'none',
|
|
28
|
+
p: 0,
|
|
29
|
+
minWidth: 0,
|
|
30
|
+
}}
|
|
31
|
+
avatarProps={{
|
|
32
|
+
size: size === 'small' ? 24 : 40,
|
|
33
|
+
}}
|
|
34
|
+
showDid={size !== 'small'}
|
|
35
|
+
{...(customer.metadata.anonymous === true
|
|
36
|
+
? {
|
|
37
|
+
user: {
|
|
38
|
+
fullName: customer.name || customer.email,
|
|
39
|
+
did: customer.did,
|
|
40
|
+
email: customer.email,
|
|
41
|
+
avatar: getCustomerAvatar(
|
|
42
|
+
customer?.did,
|
|
43
|
+
customer?.updated_at ? new Date(customer.updated_at).toISOString() : ''
|
|
44
|
+
),
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
: {})}
|
|
48
|
+
/>
|
|
49
|
+
);
|
|
25
50
|
if (linked) {
|
|
26
|
-
return
|
|
27
|
-
<Link to={linkTo || `/admin/customers/${customer.id}`}>
|
|
28
|
-
<Box sx={{ '.info-card-wrapper': { cursor: 'pointer' }, '.info-card': { minWidth: 0 } }}>
|
|
29
|
-
{/* @ts-ignore */}
|
|
30
|
-
<InfoCard
|
|
31
|
-
logo={getCustomerAvatar(
|
|
32
|
-
customer?.did,
|
|
33
|
-
customer?.updated_at ? new Date(customer.updated_at).toISOString() : '',
|
|
34
|
-
size === 'small' ? 24 : 48
|
|
35
|
-
)}
|
|
36
|
-
name={
|
|
37
|
-
<Typography
|
|
38
|
-
sx={{ maxWidth: 208, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}
|
|
39
|
-
className="customer-link-name">
|
|
40
|
-
{customer.name || customer.email}
|
|
41
|
-
</Typography>
|
|
42
|
-
}
|
|
43
|
-
{...(size === 'small'
|
|
44
|
-
? { tooltip: tooltip ? <DID did={customer?.did} /> : false, size: 24 }
|
|
45
|
-
: {
|
|
46
|
-
description: <DID did={customer?.did} compact responsive={false} sx={{ whiteSpace: 'nowrap' }} />,
|
|
47
|
-
size: 48,
|
|
48
|
-
})}
|
|
49
|
-
/>
|
|
50
|
-
</Box>
|
|
51
|
-
</Link>
|
|
52
|
-
);
|
|
51
|
+
return <Link to={linkTo || `/admin/customers/${customer.id}`}>{CustomerCard}</Link>;
|
|
53
52
|
}
|
|
54
53
|
|
|
55
|
-
return
|
|
56
|
-
<Box sx={{ '.info-card': { minWidth: 0 } }}>
|
|
57
|
-
{/* @ts-ignore */}
|
|
58
|
-
<InfoCard
|
|
59
|
-
logo={getCustomerAvatar(
|
|
60
|
-
customer.did,
|
|
61
|
-
customer.updated_at ? new Date(customer.updated_at).toISOString() : '',
|
|
62
|
-
size === 'small' ? 24 : 48
|
|
63
|
-
)}
|
|
64
|
-
name={
|
|
65
|
-
<Typography
|
|
66
|
-
sx={{ maxWidth: 320, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}
|
|
67
|
-
className="customer-link-name">
|
|
68
|
-
{customer.name || customer.email}
|
|
69
|
-
</Typography>
|
|
70
|
-
}
|
|
71
|
-
{...(size === 'small'
|
|
72
|
-
? { tooltip: tooltip ? <DID did={customer?.did} /> : false, size: 24 }
|
|
73
|
-
: {
|
|
74
|
-
description: <DID did={customer?.did} compact responsive={false} sx={{ whiteSpace: 'nowrap' }} />,
|
|
75
|
-
size: 48,
|
|
76
|
-
})}
|
|
77
|
-
/>
|
|
78
|
-
</Box>
|
|
79
|
-
);
|
|
54
|
+
return CustomerCard;
|
|
80
55
|
}
|
|
81
56
|
|
|
82
57
|
CustomerLink.defaultProps = {
|
|
@@ -282,7 +282,7 @@ export default function InvoiceList({
|
|
|
282
282
|
options: {
|
|
283
283
|
customBodyRenderLite: (_: string, index: number) => {
|
|
284
284
|
const item = data.list[index] as TInvoiceExpanded;
|
|
285
|
-
return <CustomerLink customer={item.customer} />;
|
|
285
|
+
return <CustomerLink customer={item.customer} size="small" />;
|
|
286
286
|
},
|
|
287
287
|
},
|
|
288
288
|
});
|
|
@@ -15,6 +15,8 @@ import { useLocalStorageState } from 'ahooks';
|
|
|
15
15
|
import { useEffect, useState } from 'react';
|
|
16
16
|
import { Link } from 'react-router-dom';
|
|
17
17
|
|
|
18
|
+
import DID from '@arcblock/ux/lib/DID';
|
|
19
|
+
import ShortenLabel from '@arcblock/ux/lib/UserCard/Content/shorten-label';
|
|
18
20
|
import { debounce, getAppInfo } from '../../libs/util';
|
|
19
21
|
import CustomerLink from '../customer/link';
|
|
20
22
|
import FilterToolbar from '../filter-toolbar';
|
|
@@ -202,8 +204,12 @@ export default function PayoutList({ customer_id, payment_intent_id, status, fea
|
|
|
202
204
|
if (appInfo) {
|
|
203
205
|
return (
|
|
204
206
|
<InfoCard
|
|
205
|
-
name={
|
|
206
|
-
|
|
207
|
+
name={
|
|
208
|
+
<ShortenLabel sx={{ fontWeight: 500 }} maxLength={30}>
|
|
209
|
+
{appInfo.name}
|
|
210
|
+
</ShortenLabel>
|
|
211
|
+
}
|
|
212
|
+
description={<DID did={item.destination} compact responsive={false} sx={{ whiteSpace: 'nowrap' }} />}
|
|
207
213
|
logo={appInfo.avatar}
|
|
208
214
|
size={40}
|
|
209
215
|
/>
|
|
@@ -241,8 +241,14 @@ export function SubscriptionActionsInner({
|
|
|
241
241
|
containerEl: undefined as unknown as Element,
|
|
242
242
|
saveConnect: false,
|
|
243
243
|
action: 'delegation',
|
|
244
|
+
locale: locale as 'en' | 'zh',
|
|
244
245
|
prefix: joinURL(getPrefix(), '/api/did'),
|
|
245
246
|
extraParams: { subscriptionId: subscription.id, sessionUserDid: session.user.did },
|
|
247
|
+
messages: {
|
|
248
|
+
scan: t('common.connect.defaultScan'),
|
|
249
|
+
title: t('customer.delegation.title'),
|
|
250
|
+
confirm: t('common.connect.confirm'),
|
|
251
|
+
} as any,
|
|
246
252
|
onSuccess: () => {
|
|
247
253
|
connect.close();
|
|
248
254
|
Toast.success(t('customer.delegation.success'));
|
|
@@ -287,9 +293,15 @@ export function SubscriptionActionsInner({
|
|
|
287
293
|
connect.open({
|
|
288
294
|
containerEl: undefined as unknown as Element,
|
|
289
295
|
saveConnect: false,
|
|
296
|
+
locale: locale as 'en' | 'zh',
|
|
290
297
|
action: 'overdraft-protection',
|
|
291
298
|
prefix: joinURL(getPrefix(), '/api/did'),
|
|
292
299
|
extraParams: { subscriptionId: subscription.id, amount, sessionUserDid: session.user.did },
|
|
300
|
+
messages: {
|
|
301
|
+
scan: t('common.connect.defaultScan'),
|
|
302
|
+
title: t('customer.overdraftProtection.title'),
|
|
303
|
+
confirm: t('common.connect.confirm'),
|
|
304
|
+
} as any,
|
|
293
305
|
onSuccess: () => {
|
|
294
306
|
connect.close();
|
|
295
307
|
Toast.success(t('customer.overdraftProtection.settingSuccess'));
|
package/src/libs/util.ts
CHANGED
|
@@ -3,14 +3,12 @@
|
|
|
3
3
|
/* eslint-disable @typescript-eslint/indent */
|
|
4
4
|
import { formatCheckoutHeadlines, formatPrice, getPrefix, getPriceCurrencyOptions } from '@blocklet/payment-react';
|
|
5
5
|
import type {
|
|
6
|
-
ChainType,
|
|
7
6
|
LineItem,
|
|
8
7
|
PriceRecurring,
|
|
9
8
|
TInvoiceExpanded,
|
|
10
9
|
TLineItemExpanded,
|
|
11
10
|
TPaymentCurrency,
|
|
12
11
|
TPaymentLinkExpanded,
|
|
13
|
-
TPaymentMethod,
|
|
14
12
|
TPaymentMethodExpanded,
|
|
15
13
|
TPrice,
|
|
16
14
|
TProductExpanded,
|
|
@@ -350,17 +348,6 @@ export function getAppInfo(address: string): { name: string; avatar: string; typ
|
|
|
350
348
|
return null;
|
|
351
349
|
}
|
|
352
350
|
|
|
353
|
-
export function getTokenBalanceLink(method: TPaymentMethod, address: string) {
|
|
354
|
-
const explorerHost = (method?.settings?.[method?.type as ChainType] as any)?.explorer_host || '';
|
|
355
|
-
if (method.type === 'arcblock' && address) {
|
|
356
|
-
return joinURL(explorerHost, 'accounts', address, 'tokens');
|
|
357
|
-
}
|
|
358
|
-
if (['ethereum', 'base'].includes(method.type) && address) {
|
|
359
|
-
return joinURL(explorerHost, 'address', address);
|
|
360
|
-
}
|
|
361
|
-
return '';
|
|
362
|
-
}
|
|
363
|
-
|
|
364
351
|
export function isWillCanceled(subscription: TSubscriptionExpanded) {
|
|
365
352
|
const now = Date.now() / 1000;
|
|
366
353
|
if (
|
|
@@ -211,8 +211,8 @@ export default function CustomerDetail(props: { id: string }) {
|
|
|
211
211
|
52
|
|
212
212
|
)}
|
|
213
213
|
alt={data.customer.name}
|
|
214
|
-
variant="
|
|
215
|
-
sx={{ width: 52, height: 52
|
|
214
|
+
variant="circular"
|
|
215
|
+
sx={{ width: 52, height: 52 }}
|
|
216
216
|
/>
|
|
217
217
|
<Typography variant="h2" sx={{ fontWeight: 600 }}>
|
|
218
218
|
{data.customer.name}
|
|
@@ -75,9 +75,8 @@ export default function CustomersList() {
|
|
|
75
75
|
item?.updated_at ? new Date(item.updated_at).toISOString() : '',
|
|
76
76
|
48
|
|
77
77
|
)}
|
|
78
|
-
variant="
|
|
78
|
+
variant="circular"
|
|
79
79
|
alt={item?.name}
|
|
80
|
-
sx={{ borderRadius: 'var(--radius-m, 8px)' }}
|
|
81
80
|
/>
|
|
82
81
|
<Typography sx={{ wordBreak: 'break-all' }}>{item.name}</Typography>
|
|
83
82
|
</Stack>
|
|
@@ -203,6 +203,7 @@ export default function PayoutDetail(props: { id: string }) {
|
|
|
203
203
|
label={t('customer.payout.payer')}
|
|
204
204
|
value={
|
|
205
205
|
<InfoCard
|
|
206
|
+
variant="circular"
|
|
206
207
|
logo={getCustomerAvatar(
|
|
207
208
|
paymentIntent?.customer?.did,
|
|
208
209
|
paymentIntent?.customer?.updated_at
|
|
@@ -242,7 +243,6 @@ export default function PayoutDetail(props: { id: string }) {
|
|
|
242
243
|
/>
|
|
243
244
|
}
|
|
244
245
|
size={40}
|
|
245
|
-
variant="rounded"
|
|
246
246
|
/>
|
|
247
247
|
}
|
|
248
248
|
divider
|
|
@@ -28,7 +28,7 @@ const fetchData = (): Promise<TCustomerExpanded> => {
|
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
export default function CustomerInvoicePastDue() {
|
|
31
|
-
const { t } = useLocaleContext();
|
|
31
|
+
const { t, locale } = useLocaleContext();
|
|
32
32
|
const { events } = useSessionContext();
|
|
33
33
|
const { connect, session } = usePaymentContext();
|
|
34
34
|
const [params] = useSearchParams();
|
|
@@ -67,8 +67,14 @@ export default function CustomerInvoicePastDue() {
|
|
|
67
67
|
containerEl: undefined as unknown as Element,
|
|
68
68
|
saveConnect: false,
|
|
69
69
|
action: 'collect-batch',
|
|
70
|
+
locale: locale as 'en' | 'zh',
|
|
70
71
|
prefix: joinURL(getPrefix(), '/api/did'),
|
|
71
72
|
extraParams: { subscriptionId, currencyId },
|
|
73
|
+
messages: {
|
|
74
|
+
scan: t('common.connect.defaultScan'),
|
|
75
|
+
title: t('payment.customer.invoice.payBatch'),
|
|
76
|
+
confirm: t('common.connect.confirm'),
|
|
77
|
+
} as any,
|
|
72
78
|
onSuccess: () => {
|
|
73
79
|
connect.close();
|
|
74
80
|
},
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
formatBNStr,
|
|
28
28
|
formatPrice,
|
|
29
29
|
formatNumber,
|
|
30
|
+
getTokenBalanceLink,
|
|
30
31
|
} from '@blocklet/payment-react';
|
|
31
32
|
import type { TPaymentCurrency, TPaymentMethod } from '@blocklet/payment-types';
|
|
32
33
|
import { joinURL } from 'ufo';
|
|
@@ -34,7 +35,7 @@ import { AccountBalanceWalletOutlined, ArrowBackOutlined, ArrowForwardOutlined }
|
|
|
34
35
|
import Empty from '@arcblock/ux/lib/Empty';
|
|
35
36
|
import { BN, fromUnitToToken } from '@ocap/util';
|
|
36
37
|
import RechargeList from '../../../components/invoice/recharge';
|
|
37
|
-
import {
|
|
38
|
+
import { goBackOrFallback } from '../../../libs/util';
|
|
38
39
|
import { useSessionContext } from '../../../contexts/session';
|
|
39
40
|
import { formatSmartDuration, TimeUnit } from '../../../libs/dayjs';
|
|
40
41
|
|
|
@@ -225,12 +226,18 @@ export default function BalanceRechargePage() {
|
|
|
225
226
|
containerEl: undefined as unknown as Element,
|
|
226
227
|
saveConnect: false,
|
|
227
228
|
action: 'recharge-account',
|
|
229
|
+
locale: locale as 'en' | 'zh',
|
|
228
230
|
prefix: joinURL(getPrefix(), '/api/did'),
|
|
229
231
|
extraParams: {
|
|
230
232
|
customerDid: session?.user?.did,
|
|
231
233
|
currencyId: currency.id,
|
|
232
234
|
amount: Number(amount),
|
|
233
235
|
},
|
|
236
|
+
messages: {
|
|
237
|
+
scan: t('common.connect.defaultScan'),
|
|
238
|
+
title: t('customer.recharge.title'),
|
|
239
|
+
confirm: t('common.connect.confirm'),
|
|
240
|
+
} as any,
|
|
234
241
|
onSuccess: () => {
|
|
235
242
|
connect.close();
|
|
236
243
|
Toast.success(t('customer.recharge.success'));
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
formatTime,
|
|
26
26
|
api,
|
|
27
27
|
formatBNStr,
|
|
28
|
+
getTokenBalanceLink,
|
|
28
29
|
} from '@blocklet/payment-react';
|
|
29
30
|
import { joinURL } from 'ufo';
|
|
30
31
|
import type { TSubscriptionExpanded } from '@blocklet/payment-types';
|
|
@@ -35,7 +36,7 @@ import SubscriptionDescription from '../../../components/subscription/descriptio
|
|
|
35
36
|
import InfoRow from '../../../components/info-row';
|
|
36
37
|
import Currency from '../../../components/currency';
|
|
37
38
|
import SubscriptionMetrics from '../../../components/subscription/metrics';
|
|
38
|
-
import {
|
|
39
|
+
import { goBackOrFallback } from '../../../libs/util';
|
|
39
40
|
import CustomerLink from '../../../components/customer/link';
|
|
40
41
|
import { useSessionContext } from '../../../contexts/session';
|
|
41
42
|
import { formatSmartDuration, TimeUnit } from '../../../libs/dayjs';
|
|
@@ -163,8 +164,14 @@ export default function RechargePage() {
|
|
|
163
164
|
containerEl: undefined as unknown as Element,
|
|
164
165
|
saveConnect: false,
|
|
165
166
|
action: 'recharge',
|
|
167
|
+
locale: locale as 'en' | 'zh',
|
|
166
168
|
prefix: joinURL(getPrefix(), '/api/did'),
|
|
167
169
|
extraParams: { subscriptionId, amount: Number(amount) },
|
|
170
|
+
messages: {
|
|
171
|
+
scan: t('common.connect.defaultScan'),
|
|
172
|
+
title: t('customer.recharge.title'),
|
|
173
|
+
confirm: t('common.connect.confirm'),
|
|
174
|
+
} as any,
|
|
168
175
|
onSuccess: () => {
|
|
169
176
|
connect.close();
|
|
170
177
|
Toast.success(t('customer.recharge.success'));
|
|
@@ -75,7 +75,7 @@ const waitForCheckoutComplete = async (sessionId: string) => {
|
|
|
75
75
|
};
|
|
76
76
|
|
|
77
77
|
function CustomerSubscriptionChangePayment({ subscription, customer, onComplete }: Props) {
|
|
78
|
-
const { t } = useLocaleContext();
|
|
78
|
+
const { t, locale } = useLocaleContext();
|
|
79
79
|
const navigate = useNavigate();
|
|
80
80
|
const [searchParams] = useSearchParams();
|
|
81
81
|
const { settings, connect } = usePaymentContext();
|
|
@@ -159,11 +159,17 @@ function CustomerSubscriptionChangePayment({ subscription, customer, onComplete
|
|
|
159
159
|
await handleConnected();
|
|
160
160
|
} else {
|
|
161
161
|
connect.open({
|
|
162
|
+
locale: locale as 'en' | 'zh',
|
|
162
163
|
containerEl: undefined as unknown as Element,
|
|
163
164
|
action: 'change-payment',
|
|
164
165
|
prefix: joinURL(getPrefix(), '/api/did'),
|
|
165
166
|
saveConnect: false,
|
|
166
167
|
extraParams: { subscriptionId: subscription.id },
|
|
168
|
+
messages: {
|
|
169
|
+
scan: t('common.connect.defaultScan'),
|
|
170
|
+
title: t('payment.customer.changePayment.title'),
|
|
171
|
+
confirm: t('common.connect.confirm'),
|
|
172
|
+
} as any,
|
|
167
173
|
onSuccess: async () => {
|
|
168
174
|
connect.close();
|
|
169
175
|
await handleConnected();
|