payment-kit 1.18.17 → 1.18.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.
Files changed (35) hide show
  1. package/api/src/libs/subscription.ts +116 -0
  2. package/api/src/routes/checkout-sessions.ts +28 -1
  3. package/api/src/routes/customers.ts +5 -1
  4. package/api/src/store/migrations/20250318-donate-invoice.ts +45 -0
  5. package/api/tests/libs/subscription.spec.ts +311 -0
  6. package/blocklet.yml +1 -1
  7. package/package.json +9 -9
  8. package/src/components/currency.tsx +11 -4
  9. package/src/components/customer/link.tsx +54 -14
  10. package/src/components/customer/overdraft-protection.tsx +36 -2
  11. package/src/components/info-card.tsx +55 -7
  12. package/src/components/info-row-group.tsx +122 -0
  13. package/src/components/info-row.tsx +14 -1
  14. package/src/components/payouts/portal/list.tsx +7 -2
  15. package/src/components/subscription/items/index.tsx +1 -1
  16. package/src/components/subscription/metrics.tsx +14 -6
  17. package/src/contexts/info-row.tsx +4 -0
  18. package/src/locales/en.tsx +1 -0
  19. package/src/locales/zh.tsx +1 -0
  20. package/src/pages/admin/billing/invoices/detail.tsx +54 -76
  21. package/src/pages/admin/billing/subscriptions/detail.tsx +34 -71
  22. package/src/pages/admin/customers/customers/detail.tsx +41 -64
  23. package/src/pages/admin/payments/intents/detail.tsx +28 -42
  24. package/src/pages/admin/payments/payouts/detail.tsx +27 -36
  25. package/src/pages/admin/payments/refunds/detail.tsx +27 -41
  26. package/src/pages/admin/products/links/detail.tsx +30 -55
  27. package/src/pages/admin/products/prices/detail.tsx +43 -50
  28. package/src/pages/admin/products/pricing-tables/detail.tsx +23 -25
  29. package/src/pages/admin/products/products/detail.tsx +52 -81
  30. package/src/pages/customer/index.tsx +183 -108
  31. package/src/pages/customer/invoice/detail.tsx +49 -50
  32. package/src/pages/customer/payout/detail.tsx +16 -22
  33. package/src/pages/customer/recharge/account.tsx +92 -34
  34. package/src/pages/customer/recharge/subscription.tsx +6 -0
  35. package/src/pages/customer/subscription/detail.tsx +176 -94
@@ -1563,3 +1563,119 @@ export async function getSubscriptionUnpaidInvoicesCount(subscription: Subscript
1563
1563
  return 0;
1564
1564
  }
1565
1565
  }
1566
+
1567
+ /**
1568
+ * Calculate recommended recharge amount for subscription cycles
1569
+ * @param subscriptions List of subscriptions
1570
+ * @param currencyId Currency ID
1571
+ * @returns {amount: string, cycle: number, desc: string, interval: string} Recommended amount and cycle info
1572
+ */
1573
+ export function calculateRecommendedRechargeAmount(subscriptions: any[], currencyId: string) {
1574
+ if (!subscriptions || subscriptions.length === 0) {
1575
+ return { amount: '0', cycle: 0, desc: 'no subscription', interval: '' };
1576
+ }
1577
+
1578
+ type IntervalType = 'hour' | 'day' | 'month' | 'year';
1579
+
1580
+ // Group subscriptions by billing interval
1581
+ const intervalSubscriptions: Record<IntervalType, any[]> = {
1582
+ hour: [],
1583
+ day: [],
1584
+ month: [],
1585
+ year: [],
1586
+ };
1587
+
1588
+ // Classify subscriptions by interval
1589
+ subscriptions.forEach((subscription) => {
1590
+ if (!subscription.items?.length) return;
1591
+
1592
+ subscription.items.forEach((item: any) => {
1593
+ const { price } = item;
1594
+ if (price?.type === 'recurring' && price.recurring?.interval) {
1595
+ const interval = price.recurring.interval as IntervalType;
1596
+ if (Object.keys(intervalSubscriptions).includes(interval)) {
1597
+ intervalSubscriptions[interval].push({ subscription, item, price });
1598
+ }
1599
+ }
1600
+ });
1601
+ });
1602
+
1603
+ const hasHourly = intervalSubscriptions.hour.length > 0;
1604
+ const hasDaily = intervalSubscriptions.day.length > 0;
1605
+ const hasMonthly = intervalSubscriptions.month.length > 0;
1606
+ const hasYearly = intervalSubscriptions.year.length > 0;
1607
+
1608
+ // Determine recommended cycle based on subscription intervals
1609
+ let recommendedInterval = '';
1610
+ let cycleInSeconds = 0;
1611
+ let desc = '';
1612
+
1613
+ if (hasMonthly) {
1614
+ recommendedInterval = 'month';
1615
+ cycleInSeconds = 30 * 24 * 60 * 60;
1616
+ desc = 'monthly';
1617
+ } else if ((hasHourly || hasDaily) && !hasYearly) {
1618
+ recommendedInterval = 'week';
1619
+ cycleInSeconds = 7 * 24 * 60 * 60;
1620
+ desc = 'weekly';
1621
+ } else if ((hasDaily || hasHourly) && hasYearly) {
1622
+ recommendedInterval = 'month';
1623
+ cycleInSeconds = 30 * 24 * 60 * 60;
1624
+ desc = 'monthly';
1625
+ } else if (hasYearly) {
1626
+ recommendedInterval = 'year';
1627
+ cycleInSeconds = 365 * 24 * 60 * 60;
1628
+ desc = 'yearly';
1629
+ }
1630
+
1631
+ let totalAmount = new BN(0);
1632
+
1633
+ // Calculate amount based on recommended cycle
1634
+ subscriptions.forEach((subscription) => {
1635
+ if (!subscription.items?.length) return;
1636
+
1637
+ subscription.items.forEach((item: any) => {
1638
+ const { price } = item;
1639
+ if (!price) return;
1640
+
1641
+ const unitAmount = getPriceUintAmountByCurrency(price, currencyId);
1642
+ const quantity = item.quantity || 1;
1643
+ let itemAmount = new BN(unitAmount).mul(new BN(quantity));
1644
+
1645
+ if (price.type === 'recurring' && price.recurring?.interval) {
1646
+ const { interval } = price.recurring;
1647
+ const intervalCount = price.recurring.interval_count || 1;
1648
+
1649
+ // Apply period conversion calculation
1650
+ if (recommendedInterval === 'month') {
1651
+ if (interval === 'hour') {
1652
+ itemAmount = itemAmount.mul(new BN(720)).div(new BN(intervalCount));
1653
+ } else if (interval === 'day') {
1654
+ itemAmount = itemAmount.mul(new BN(30)).div(new BN(intervalCount));
1655
+ } else if (interval === 'month') {
1656
+ itemAmount = itemAmount.div(new BN(intervalCount));
1657
+ } else if (interval === 'year') {
1658
+ itemAmount = itemAmount.div(new BN(12 * intervalCount));
1659
+ }
1660
+ } else if (recommendedInterval === 'week') {
1661
+ if (interval === 'hour') {
1662
+ itemAmount = itemAmount.mul(new BN(168)).div(new BN(intervalCount));
1663
+ } else if (interval === 'day') {
1664
+ itemAmount = itemAmount.mul(new BN(7)).div(new BN(intervalCount));
1665
+ }
1666
+ }
1667
+
1668
+ totalAmount = totalAmount.add(itemAmount);
1669
+ } else if (price.type === 'one_time') {
1670
+ totalAmount = totalAmount.add(itemAmount);
1671
+ }
1672
+ });
1673
+ });
1674
+
1675
+ return {
1676
+ amount: totalAmount.toString(),
1677
+ cycle: cycleInSeconds,
1678
+ desc,
1679
+ interval: recommendedInterval,
1680
+ };
1681
+ }
@@ -46,10 +46,12 @@ import {
46
46
  formatAmountPrecisionLimit,
47
47
  formatMetadata,
48
48
  getDataObjectFromQuery,
49
+ getUserOrAppInfo,
49
50
  isUserInBlocklist,
50
51
  } from '../libs/util';
51
52
  import {
52
53
  Invoice,
54
+ PaymentBeneficiary,
53
55
  SetupIntent,
54
56
  Subscription,
55
57
  SubscriptionItem,
@@ -501,6 +503,11 @@ export async function ensureCheckoutSessionOpen(req: Request, res: Response, nex
501
503
  next();
502
504
  }
503
505
 
506
+ const getBeneficiaryName = async (beneficiary: PaymentBeneficiary) => {
507
+ if (!beneficiary) return '';
508
+ return beneficiary.name || (await getUserOrAppInfo(beneficiary.address || ''))?.name || beneficiary.address;
509
+ };
510
+
504
511
  export async function getCrossSellItem(checkoutSession: CheckoutSession) {
505
512
  // FIXME: perhaps we can support cross sell even if the current session have multiple items
506
513
  if (checkoutSession.line_items.length > 1) {
@@ -624,12 +631,32 @@ export async function startCheckoutSessionFromPaymentLink(id: string, req: Reque
624
631
  payment: 'Thanks for your purchase',
625
632
  subscription: 'Thanks for your subscribing',
626
633
  setup: 'Thanks for your subscribing',
627
- donate: 'Thanks for for your tip',
634
+ donate: 'Thanks for your tip',
628
635
  };
629
636
  const mode = link.submit_type === 'donate' ? 'donate' : raw.mode;
630
637
  raw.payment_intent_data = {
631
638
  description: descriptions[mode || 'payment'],
632
639
  };
640
+ if (mode === 'donate') {
641
+ const beneficiaries = link.donation_settings?.beneficiaries || [];
642
+
643
+ if (beneficiaries.length > 0) {
644
+ // sort beneficiaries by share, the first one is the main beneficiary
645
+ const sortedBeneficiaries = [...beneficiaries].sort((a, b) => {
646
+ // Plain JavaScript comparison without using BN methods
647
+ return Number(b.share) - Number(a.share);
648
+ });
649
+ const mainBeneficiary = sortedBeneficiaries[0];
650
+ const userName = await getBeneficiaryName(mainBeneficiary as PaymentBeneficiary);
651
+
652
+ raw.payment_intent_data.description =
653
+ beneficiaries.length === 1
654
+ ? `Tip to ${userName}`
655
+ : `Tip to ${userName} and ${beneficiaries.length - 1} others`;
656
+ } else {
657
+ raw.payment_intent_data.description = 'Thanks for your tip';
658
+ }
659
+ }
633
660
  }
634
661
  if (link.after_completion?.redirect?.url) {
635
662
  raw.success_url = link.after_completion?.redirect?.url;
@@ -22,7 +22,7 @@ import {
22
22
  Subscription,
23
23
  SubscriptionItem,
24
24
  } from '../store/models';
25
- import { getSubscriptionPaymentAddress } from '../libs/subscription';
25
+ import { getSubscriptionPaymentAddress, calculateRecommendedRechargeAmount } from '../libs/subscription';
26
26
  import { expandLineItems } from '../libs/session';
27
27
 
28
28
  const router = Router();
@@ -272,12 +272,16 @@ router.get('/recharge', sessionMiddleware(), async (req, res) => {
272
272
  return payerAddress === customer.did;
273
273
  });
274
274
 
275
+ // Calculate recommended recharge cycle and amount
276
+ const recommendedRecharge = calculateRecommendedRechargeAmount(relatedSubscriptions, paymentCurrency.id);
277
+
275
278
  return res.json({
276
279
  currency: {
277
280
  ...paymentCurrency.toJSON(),
278
281
  paymentMethod,
279
282
  },
280
283
  relatedSubscriptions,
284
+ recommendedRecharge,
281
285
  });
282
286
  } catch (err) {
283
287
  logger.error('Error getting balance recharge info', err);
@@ -0,0 +1,45 @@
1
+ import { QueryTypes } from 'sequelize';
2
+ import { Migration } from '../migrate';
3
+
4
+ export const up: Migration = async ({ context }) => {
5
+ try {
6
+ await context.sequelize.query(
7
+ "UPDATE payment_intents SET description = 'Thanks for your tip' WHERE description = 'Thanks for for your tip'",
8
+ { type: QueryTypes.UPDATE }
9
+ );
10
+
11
+ await context.sequelize.query(
12
+ `UPDATE payment_intents SET description = 'Thanks for your tip'
13
+ WHERE description = 'Thanks for your support'
14
+ AND id IN (
15
+ SELECT payment_intent_id FROM checkout_sessions
16
+ WHERE submit_type = 'donate' AND payment_intent_id IS NOT NULL
17
+ )`,
18
+ { type: QueryTypes.UPDATE }
19
+ );
20
+
21
+ await context.sequelize.query(
22
+ `UPDATE invoices SET description = 'Thanks for your tip'
23
+ WHERE (description = 'Thanks for your support' OR description = 'Thanks for for your tip')
24
+ AND payment_intent_id IN (
25
+ SELECT payment_intent_id FROM checkout_sessions
26
+ WHERE submit_type = 'donate' AND payment_intent_id IS NOT NULL
27
+ )`,
28
+ { type: QueryTypes.UPDATE }
29
+ );
30
+
31
+ await context.sequelize.query(
32
+ `UPDATE payouts SET description = 'Thanks for your tip'
33
+ WHERE (description = 'Thanks for your support' OR description = 'Thanks for for your tip')
34
+ AND payment_intent_id IN (
35
+ SELECT payment_intent_id FROM checkout_sessions
36
+ WHERE submit_type = 'donate' AND payment_intent_id IS NOT NULL
37
+ )`,
38
+ { type: QueryTypes.UPDATE }
39
+ );
40
+ } catch (error) {
41
+ console.error('Failed to update donation invoice descriptions', error);
42
+ }
43
+ };
44
+
45
+ export const down = async () => {};
@@ -11,6 +11,7 @@ import {
11
11
  shouldCancelSubscription,
12
12
  getSubscriptionStakeAmountSetup,
13
13
  checkUsageReportEmpty,
14
+ calculateRecommendedRechargeAmount,
14
15
  } from '../../src/libs/subscription';
15
16
  import { PaymentMethod, Subscription, SubscriptionItem, UsageRecord, Price } from '../../src/store/models';
16
17
 
@@ -565,3 +566,313 @@ describe('checkUsageReportEmpty', () => {
565
566
  expect(result).toBe(false);
566
567
  });
567
568
  });
569
+
570
+ describe('calculateRecommendedRechargeAmount', () => {
571
+ // 测试空订阅列表
572
+ it('should return zero amount for empty subscriptions', () => {
573
+ const result = calculateRecommendedRechargeAmount([], 'usd');
574
+ expect(result).toEqual({ amount: '0', cycle: 0, desc: 'no subscription', interval: '' });
575
+ });
576
+
577
+ // 测试只有按小时订阅
578
+ it('should recommend weekly cycle for hourly subscriptions', () => {
579
+ const subscriptions = [
580
+ {
581
+ items: [
582
+ {
583
+ quantity: 2,
584
+ price: {
585
+ type: 'recurring',
586
+ recurring: { interval: 'hour', interval_count: 1 },
587
+ unit_amount: '10',
588
+ currency_id: 'usd',
589
+ },
590
+ },
591
+ ],
592
+ },
593
+ ];
594
+
595
+ const result = calculateRecommendedRechargeAmount(subscriptions, 'usd');
596
+ expect(result.cycle).toBe(7 * 24 * 60 * 60); // 一周的秒数
597
+ expect(result.desc).toBe('weekly');
598
+ expect(result.interval).toBe('week');
599
+ // 每小时10,一周168小时,两个单位,总计 10 * 168 * 2 = 3360
600
+ expect(result.amount).toBe('3360');
601
+ });
602
+
603
+ // 测试只有按天订阅
604
+ it('should recommend weekly cycle for daily subscriptions', () => {
605
+ const subscriptions = [
606
+ {
607
+ items: [
608
+ {
609
+ quantity: 1,
610
+ price: {
611
+ type: 'recurring',
612
+ recurring: { interval: 'day', interval_count: 1 },
613
+ unit_amount: '100',
614
+ currency_id: 'usd',
615
+ },
616
+ },
617
+ ],
618
+ },
619
+ ];
620
+
621
+ const result = calculateRecommendedRechargeAmount(subscriptions, 'usd');
622
+ expect(result.cycle).toBe(7 * 24 * 60 * 60);
623
+ expect(result.desc).toBe('weekly');
624
+ expect(result.interval).toBe('week');
625
+ // 每天100,一周7天,总计 100 * 7 = 700
626
+ expect(result.amount).toBe('700');
627
+ });
628
+
629
+ // 测试只有按月订阅
630
+ it('should recommend monthly cycle for monthly subscriptions', () => {
631
+ const subscriptions = [
632
+ {
633
+ items: [
634
+ {
635
+ quantity: 1,
636
+ price: {
637
+ type: 'recurring',
638
+ recurring: { interval: 'month', interval_count: 1 },
639
+ unit_amount: '500',
640
+ currency_id: 'usd',
641
+ },
642
+ },
643
+ ],
644
+ },
645
+ ];
646
+
647
+ const result = calculateRecommendedRechargeAmount(subscriptions, 'usd');
648
+ expect(result.cycle).toBe(30 * 24 * 60 * 60);
649
+ expect(result.desc).toBe('monthly');
650
+ expect(result.interval).toBe('month');
651
+ expect(result.amount).toBe('500');
652
+ });
653
+
654
+ // 测试中间档金额计算 (4x)
655
+ it('should calculate the medium preset amount correctly (4x)', () => {
656
+ const subscriptions = [
657
+ {
658
+ items: [
659
+ {
660
+ quantity: 1,
661
+ price: {
662
+ type: 'recurring',
663
+ recurring: { interval: 'month', interval_count: 1 },
664
+ unit_amount: '500',
665
+ currency_id: 'usd',
666
+ },
667
+ },
668
+ ],
669
+ },
670
+ ];
671
+
672
+ const result = calculateRecommendedRechargeAmount(subscriptions, 'usd');
673
+ expect(result.amount).toBe('500');
674
+
675
+ // 在UI中,我们应用Math.ceil(500 * 4) = 2000作为中间档预设值
676
+ const baseAmount = result.amount;
677
+ const mediumPresetAmount = Math.ceil(Number(baseAmount) * 4).toString();
678
+ expect(mediumPresetAmount).toBe('2000');
679
+ });
680
+
681
+ // 测试高档金额计算 (8x)
682
+ it('should calculate the high preset amount correctly (8x)', () => {
683
+ const subscriptions = [
684
+ {
685
+ items: [
686
+ {
687
+ quantity: 1,
688
+ price: {
689
+ type: 'recurring',
690
+ recurring: { interval: 'month', interval_count: 1 },
691
+ unit_amount: '500',
692
+ currency_id: 'usd',
693
+ },
694
+ },
695
+ ],
696
+ },
697
+ ];
698
+
699
+ const result = calculateRecommendedRechargeAmount(subscriptions, 'usd');
700
+
701
+ // 在UI中,我们应用Math.ceil(500 * 8) = 4000作为高档预设值
702
+ const baseAmount = result.amount;
703
+ const highPresetAmount = Math.ceil(Number(baseAmount) * 8).toString();
704
+ expect(highPresetAmount).toBe('4000');
705
+ });
706
+
707
+ // 测试前端默认选择中间档
708
+ it('should select medium preset by default in the UI', () => {
709
+ const subscriptions = [
710
+ {
711
+ items: [
712
+ {
713
+ quantity: 1,
714
+ price: {
715
+ type: 'recurring',
716
+ recurring: { interval: 'month', interval_count: 1 },
717
+ unit_amount: '500',
718
+ currency_id: 'usd',
719
+ },
720
+ },
721
+ ],
722
+ },
723
+ ];
724
+
725
+ const result = calculateRecommendedRechargeAmount(subscriptions, 'usd');
726
+ const baseAmount = result.amount;
727
+
728
+ // 验证前端默认选择的中间档金额是基础金额的4倍
729
+ const defaultSelectedAmount = Math.ceil(Number(baseAmount) * 4).toString();
730
+ expect(defaultSelectedAmount).toBe('2000');
731
+ });
732
+
733
+ // 测试只有按年订阅
734
+ it('should recommend yearly cycle for yearly subscriptions', () => {
735
+ const subscriptions = [
736
+ {
737
+ items: [
738
+ {
739
+ quantity: 1,
740
+ price: {
741
+ type: 'recurring',
742
+ recurring: { interval: 'year', interval_count: 1 },
743
+ unit_amount: '1200',
744
+ currency_id: 'usd',
745
+ },
746
+ },
747
+ ],
748
+ },
749
+ ];
750
+
751
+ const result = calculateRecommendedRechargeAmount(subscriptions, 'usd');
752
+ expect(result.cycle).toBe(365 * 24 * 60 * 60);
753
+ expect(result.desc).toBe('yearly');
754
+ expect(result.amount).toBe('1200');
755
+ });
756
+
757
+ // 测试混合按小时和按年的订阅
758
+ it('should recommend monthly cycle for mixed hourly and yearly subscriptions', () => {
759
+ const subscriptions = [
760
+ {
761
+ items: [
762
+ {
763
+ quantity: 1,
764
+ price: {
765
+ type: 'recurring',
766
+ recurring: { interval: 'hour', interval_count: 1 },
767
+ unit_amount: '5',
768
+ currency_id: 'usd',
769
+ },
770
+ },
771
+ ],
772
+ },
773
+ {
774
+ items: [
775
+ {
776
+ quantity: 1,
777
+ price: {
778
+ type: 'recurring',
779
+ recurring: { interval: 'year', interval_count: 1 },
780
+ unit_amount: '1200',
781
+ currency_id: 'usd',
782
+ },
783
+ },
784
+ ],
785
+ },
786
+ ];
787
+
788
+ const result = calculateRecommendedRechargeAmount(subscriptions, 'usd');
789
+ expect(result.cycle).toBe(30 * 24 * 60 * 60);
790
+ expect(result.desc).toBe('monthly');
791
+ // 每小时5,一个月约720小时 + 年费1200/12个月
792
+ // 5 * 720 + 1200/12 = 3600 + 100 = 3700
793
+ expect(result.amount).toBe('3700');
794
+ });
795
+
796
+ // 测试混合小时、月、年的复杂订阅
797
+ it('should handle complex subscription combinations', () => {
798
+ const subscriptions = [
799
+ {
800
+ items: [
801
+ {
802
+ quantity: 2,
803
+ price: {
804
+ type: 'recurring',
805
+ recurring: { interval: 'hour', interval_count: 1 },
806
+ unit_amount: '5',
807
+ currency_id: 'usd',
808
+ },
809
+ },
810
+ {
811
+ quantity: 1,
812
+ price: {
813
+ type: 'recurring',
814
+ recurring: { interval: 'month', interval_count: 1 },
815
+ unit_amount: '200',
816
+ currency_id: 'usd',
817
+ },
818
+ },
819
+ ],
820
+ },
821
+ {
822
+ items: [
823
+ {
824
+ quantity: 1,
825
+ price: {
826
+ type: 'recurring',
827
+ recurring: { interval: 'year', interval_count: 1 },
828
+ unit_amount: '1200',
829
+ currency_id: 'usd',
830
+ },
831
+ },
832
+ {
833
+ quantity: 1,
834
+ price: {
835
+ type: 'one_time',
836
+ unit_amount: '50',
837
+ currency_id: 'usd',
838
+ },
839
+ },
840
+ ],
841
+ },
842
+ ];
843
+
844
+ const result = calculateRecommendedRechargeAmount(subscriptions, 'usd');
845
+ expect(result.cycle).toBe(30 * 24 * 60 * 60);
846
+ expect(result.desc).toBe('monthly');
847
+ // 每小时5×2数量=10,一个月约720小时 + 每月200 + 年费1200/12个月 + 一次性费用50
848
+ // 10 * 720 + 200 + 1200/12 + 50 = 7200 + 200 + 100 + 50 = 7550
849
+ expect(result.amount).toBe('7550');
850
+ });
851
+
852
+ // 测试不同货币的处理
853
+ it('should handle currency options', () => {
854
+ const subscriptions = [
855
+ {
856
+ items: [
857
+ {
858
+ quantity: 1,
859
+ price: {
860
+ type: 'recurring',
861
+ recurring: { interval: 'month', interval_count: 1 },
862
+ currency_id: 'eth',
863
+ currency_options: [
864
+ { currency_id: 'usd', unit_amount: '300' },
865
+ { currency_id: 'btc', unit_amount: '0.01' },
866
+ ],
867
+ },
868
+ },
869
+ ],
870
+ },
871
+ ];
872
+
873
+ const result = calculateRecommendedRechargeAmount(subscriptions, 'usd');
874
+ expect(result.cycle).toBe(30 * 24 * 60 * 60);
875
+ expect(result.desc).toBe('monthly');
876
+ expect(result.amount).toBe('300');
877
+ });
878
+ });
package/blocklet.yml CHANGED
@@ -14,7 +14,7 @@ repository:
14
14
  type: git
15
15
  url: git+https://github.com/blocklet/payment-kit.git
16
16
  specVersion: 1.2.8
17
- version: 1.18.17
17
+ version: 1.18.19
18
18
  logo: logo.png
19
19
  files:
20
20
  - dist
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payment-kit",
3
- "version": "1.18.17",
3
+ "version": "1.18.19",
4
4
  "scripts": {
5
5
  "dev": "blocklet dev --open",
6
6
  "eject": "vite eject",
@@ -46,18 +46,18 @@
46
46
  "@abtnode/cron": "^1.16.40",
47
47
  "@arcblock/did": "^1.19.15",
48
48
  "@arcblock/did-auth-storage-nedb": "^1.7.1",
49
- "@arcblock/did-connect": "^2.12.25",
49
+ "@arcblock/did-connect": "^2.12.36",
50
50
  "@arcblock/did-util": "^1.19.15",
51
51
  "@arcblock/jwt": "^1.19.15",
52
- "@arcblock/ux": "^2.12.25",
52
+ "@arcblock/ux": "^2.12.36",
53
53
  "@arcblock/validator": "^1.19.15",
54
54
  "@blocklet/js-sdk": "^1.16.40",
55
55
  "@blocklet/logger": "^1.16.40",
56
- "@blocklet/payment-react": "1.18.17",
56
+ "@blocklet/payment-react": "1.18.19",
57
57
  "@blocklet/sdk": "^1.16.40",
58
- "@blocklet/ui-react": "^2.12.25",
59
- "@blocklet/uploader": "^0.1.76",
60
- "@blocklet/xss": "^0.1.28",
58
+ "@blocklet/ui-react": "^2.12.36",
59
+ "@blocklet/uploader": "^0.1.79",
60
+ "@blocklet/xss": "^0.1.30",
61
61
  "@mui/icons-material": "^5.16.6",
62
62
  "@mui/lab": "^5.0.0-alpha.173",
63
63
  "@mui/material": "^5.16.6",
@@ -121,7 +121,7 @@
121
121
  "devDependencies": {
122
122
  "@abtnode/types": "^1.16.40",
123
123
  "@arcblock/eslint-config-ts": "^0.3.3",
124
- "@blocklet/payment-types": "1.18.17",
124
+ "@blocklet/payment-types": "1.18.19",
125
125
  "@types/cookie-parser": "^1.4.7",
126
126
  "@types/cors": "^2.8.17",
127
127
  "@types/debug": "^4.1.12",
@@ -167,5 +167,5 @@
167
167
  "parser": "typescript"
168
168
  }
169
169
  },
170
- "gitHead": "9d43381dd3744506455547260f5e621c6ad88e8f"
170
+ "gitHead": "eabd58598ad349a5177dca2e6302c82117d9b687"
171
171
  }
@@ -1,15 +1,22 @@
1
- import { Avatar, Stack, Typography } from '@mui/material';
1
+ import { Avatar, Stack, Typography, SxProps } from '@mui/material';
2
2
 
3
3
  type Props = {
4
4
  logo: string;
5
5
  name: string;
6
+ sx?: SxProps;
6
7
  };
7
8
 
8
- export default function Currency({ logo, name }: Props) {
9
+ export default function Currency({ logo, name, sx }: Props) {
9
10
  return (
10
- <Stack direction="row" alignItems="center" spacing={0.5}>
11
+ <Stack direction="row" alignItems="center" spacing={0.5} sx={sx}>
11
12
  <Avatar src={logo} alt={name} sx={{ width: 20, height: 20 }} />
12
- <Typography color="text.primary">{name}</Typography>
13
+ <Typography color="text.primary" className="currency-name">
14
+ {name}
15
+ </Typography>
13
16
  </Stack>
14
17
  );
15
18
  }
19
+
20
+ Currency.defaultProps = {
21
+ sx: {},
22
+ };