@blocklet/payment-react 1.21.16 → 1.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/.aigne/doc-smith/translation-cache.yaml +11 -0
  2. package/es/components/over-due-invoice-payment.js +86 -21
  3. package/es/components/payment-beneficiaries.js +3 -3
  4. package/es/components/stripe-payment-action.d.ts +16 -0
  5. package/es/components/stripe-payment-action.js +164 -0
  6. package/es/history/invoice/list.js +58 -2
  7. package/es/index.d.ts +3 -1
  8. package/es/index.js +2 -0
  9. package/es/libs/util.d.ts +2 -1
  10. package/es/libs/util.js +17 -1
  11. package/es/locales/en.js +18 -3
  12. package/es/locales/zh.js +12 -3
  13. package/es/payment/form/stripe/form.d.ts +4 -1
  14. package/es/payment/form/stripe/form.js +9 -5
  15. package/es/payment/product-item.js +1 -1
  16. package/es/payment/summary.js +1 -1
  17. package/lib/components/over-due-invoice-payment.js +99 -31
  18. package/lib/components/payment-beneficiaries.js +3 -2
  19. package/lib/components/stripe-payment-action.d.ts +16 -0
  20. package/lib/components/stripe-payment-action.js +191 -0
  21. package/lib/history/invoice/list.js +58 -10
  22. package/lib/index.d.ts +3 -1
  23. package/lib/index.js +8 -0
  24. package/lib/libs/util.d.ts +2 -1
  25. package/lib/libs/util.js +18 -1
  26. package/lib/locales/en.js +18 -3
  27. package/lib/locales/zh.js +12 -3
  28. package/lib/payment/form/stripe/form.d.ts +4 -1
  29. package/lib/payment/form/stripe/form.js +9 -5
  30. package/lib/payment/product-item.js +1 -1
  31. package/lib/payment/summary.js +1 -1
  32. package/package.json +9 -9
  33. package/src/components/over-due-invoice-payment.tsx +101 -29
  34. package/src/components/payment-beneficiaries.tsx +3 -3
  35. package/src/components/stripe-payment-action.tsx +220 -0
  36. package/src/history/invoice/list.tsx +67 -13
  37. package/src/index.ts +3 -0
  38. package/src/libs/util.ts +18 -1
  39. package/src/locales/en.tsx +16 -0
  40. package/src/locales/zh.tsx +10 -0
  41. package/src/payment/form/stripe/form.tsx +9 -2
  42. package/src/payment/product-item.tsx +1 -1
  43. package/src/payment/summary.tsx +1 -1
package/lib/libs/util.js CHANGED
@@ -14,6 +14,7 @@ exports.formatCouponTerms = void 0;
14
14
  exports.formatDateTime = formatDateTime;
15
15
  exports.formatError = void 0;
16
16
  exports.formatLineItemPricing = formatLineItemPricing;
17
+ exports.formatLinkWithLocale = formatLinkWithLocale;
17
18
  exports.formatLocale = void 0;
18
19
  exports.formatMeteredThen = formatMeteredThen;
19
20
  exports.formatNumber = formatNumber;
@@ -1059,8 +1060,8 @@ function formatTotalPrice({
1059
1060
  totalAmount: "0"
1060
1061
  };
1061
1062
  }
1062
- const unitValue = new _util.BN(price.custom_unit_amount || price.unit_amount);
1063
1063
  const currency = price?.currency ?? {};
1064
+ const unitValue = new _util.BN(getPriceUintAmountByCurrency(price, currency));
1064
1065
  const total = `${(0, _util.fromUnitToToken)(unitValue.mul(new _util.BN(quantity)), currency.decimal)} ${currency.symbol} `;
1065
1066
  const unit = `${(0, _util.fromUnitToToken)(unitValue, currency.decimal)} ${currency.symbol} `;
1066
1067
  const appendUnit = (v, alt) => {
@@ -1278,4 +1279,20 @@ function showStaking(method, currency, noStake) {
1278
1279
  return currency.type !== "credit";
1279
1280
  }
1280
1281
  return false;
1282
+ }
1283
+ function formatLinkWithLocale(url, locale) {
1284
+ if (!locale || !url) {
1285
+ return url;
1286
+ }
1287
+ try {
1288
+ const urlObj = new URL(url);
1289
+ urlObj.searchParams.set("locale", locale);
1290
+ return urlObj.toString();
1291
+ } catch (error) {
1292
+ if (/[?&]locale=[^&]*/.test(url)) {
1293
+ return url.replace(/([?&])locale=[^&]*/, `$1locale=${locale}`);
1294
+ }
1295
+ const separator = url.includes("?") ? "&" : "?";
1296
+ return `${url}${separator}locale=${locale}`;
1297
+ }
1281
1298
  }
package/lib/locales/en.js CHANGED
@@ -115,7 +115,8 @@ module.exports = (0, _flat.default)({
115
115
  cancel: "Cancel"
116
116
  },
117
117
  paymentMethod: "Payment Method",
118
- viewInvoice: "View Invoice"
118
+ viewInvoice: "View Invoice",
119
+ submit: "Submit"
119
120
  },
120
121
  payment: {
121
122
  checkout: {
@@ -453,7 +454,11 @@ module.exports = (0, _flat.default)({
453
454
  amountApplied: "Applied Credit",
454
455
  pay: "Pay this invoice",
455
456
  paySuccess: "You have successfully paid the invoice",
457
+ payProcessing: "Payment is being processed, please refresh in a moment",
456
458
  payError: "Failed to pay the invoice",
459
+ sync: "Sync Status",
460
+ syncing: "Syncing...",
461
+ syncSuccess: "Synced successfully",
457
462
  renew: "Renew the subscription",
458
463
  renewSuccess: "You have successfully renewed the subscription",
459
464
  renewError: "Failed to renew the subscription",
@@ -462,7 +467,16 @@ module.exports = (0, _flat.default)({
462
467
  invoiceNumber: "Invoice Number",
463
468
  emptyList: "No Invoices",
464
469
  noPaymentRequired: "No Payment Required",
465
- payBatch: "Pay Due Invoices"
470
+ payBatch: "Pay Due Invoices",
471
+ stripePayDescription: "Complete payment using your saved payment method or add a new one.",
472
+ amount: "Amount",
473
+ paymentConfirmTitle: "Payment Confirmation",
474
+ paymentConfirmDescription: "After completing this payment, the payment method you use will be automatically set as the default for this subscription. Additionally, we will retry payment for any other unpaid invoices associated with this subscription.",
475
+ continue: "Continue"
476
+ },
477
+ overduePayment: {
478
+ setupPaymentDescription: "Use your saved card or add a new one to complete payment via Stripe.",
479
+ totalAmount: "Total Amount"
466
480
  },
467
481
  payment: {
468
482
  empty: "There are no payments",
@@ -531,7 +545,8 @@ module.exports = (0, _flat.default)({
531
545
  list: "Past Due Invoices:",
532
546
  empty: "There are no overdue invoices for your subscription {name}.",
533
547
  retry: "Retry",
534
- paid: "Paid"
548
+ paid: "Paid",
549
+ processing: "Processing"
535
550
  }
536
551
  }
537
552
  },
package/lib/locales/zh.js CHANGED
@@ -115,7 +115,8 @@ module.exports = (0, _flat.default)({
115
115
  cancel: "\u53D6\u6D88"
116
116
  },
117
117
  paymentMethod: "\u652F\u4ED8\u65B9\u5F0F",
118
- viewInvoice: "\u67E5\u770B\u8D26\u5355"
118
+ viewInvoice: "\u67E5\u770B\u8D26\u5355",
119
+ submit: "\u63D0\u4EA4"
119
120
  },
120
121
  payment: {
121
122
  checkout: {
@@ -457,7 +458,11 @@ module.exports = (0, _flat.default)({
457
458
  amountApplied: "\u4F59\u989D\u53D8\u66F4",
458
459
  pay: "\u652F\u4ED8\u6B64\u8D26\u5355",
459
460
  paySuccess: "\u652F\u4ED8\u6210\u529F",
461
+ payProcessing: "\u652F\u4ED8\u5904\u7406\u4E2D\uFF0C\u8BF7\u7A0D\u5019\u5237\u65B0\u67E5\u770B",
460
462
  payError: "\u652F\u4ED8\u5931\u8D25",
463
+ sync: "\u540C\u6B65\u72B6\u6001",
464
+ syncing: "\u540C\u6B65\u4E2D...",
465
+ syncSuccess: "\u540C\u6B65\u6210\u529F",
461
466
  renew: "\u6062\u590D\u8BA2\u9605",
462
467
  renewSuccess: "\u8BA2\u9605\u6062\u590D\u6210\u529F",
463
468
  renewError: "\u8BA2\u9605\u6062\u590D\u5931\u8D25",
@@ -466,7 +471,10 @@ module.exports = (0, _flat.default)({
466
471
  invoiceNumber: "\u8D26\u5355\u7F16\u53F7",
467
472
  emptyList: "\u6CA1\u6709\u8D26\u5355",
468
473
  noPaymentRequired: "\u65E0\u9700\u652F\u4ED8",
469
- payBatch: "\u652F\u4ED8\u6B20\u6B3E"
474
+ payBatch: "\u652F\u4ED8\u6B20\u6B3E",
475
+ paymentConfirmTitle: "\u652F\u4ED8\u786E\u8BA4",
476
+ paymentConfirmDescription: "\u5B8C\u6210\u672C\u6B21\u652F\u4ED8\u540E\uFF0C\u60A8\u4F7F\u7528\u7684\u652F\u4ED8\u65B9\u5F0F\u5C06\u81EA\u52A8\u8BBE\u7F6E\u4E3A\u8BE5\u8BA2\u9605\u7684\u9ED8\u8BA4\u652F\u4ED8\u65B9\u5F0F\u3002\u6B64\u5916\uFF0C\u6211\u4EEC\u8FD8\u5C06\u5BF9\u8BE5\u8BA2\u9605\u7684\u5176\u4ED6\u6B20\u8D39\u8D26\u5355\u8FDB\u884C\u91CD\u8BD5\u6536\u8D39\u3002",
477
+ continue: "\u7EE7\u7EED"
470
478
  },
471
479
  payment: {
472
480
  empty: "\u6CA1\u6709\u652F\u4ED8\u8BB0\u5F55",
@@ -535,7 +543,8 @@ module.exports = (0, _flat.default)({
535
543
  list: "\u6B20\u8D39\u8D26\u5355\uFF1A",
536
544
  empty: "\u60A8\u7684\u3010{name}\u3011\u8BA2\u9605\u5F53\u524D\u6CA1\u6709\u6B20\u8D39\u8D26\u5355",
537
545
  retry: "\u91CD\u65B0\u652F\u4ED8",
538
- paid: "\u5DF2\u652F\u4ED8"
546
+ paid: "\u5DF2\u652F\u4ED8",
547
+ processing: "\u652F\u4ED8\u4E2D"
539
548
  }
540
549
  }
541
550
  },
@@ -6,6 +6,7 @@ export type StripeCheckoutFormProps = {
6
6
  mode: string;
7
7
  onConfirm: Function;
8
8
  returnUrl?: string;
9
+ submitButtonText?: string;
9
10
  };
10
11
  export type StripeCheckoutProps = {
11
12
  clientSecret: string;
@@ -16,5 +17,7 @@ export type StripeCheckoutProps = {
16
17
  onConfirm: Function;
17
18
  onCancel: Function;
18
19
  returnUrl?: string;
20
+ title?: string;
21
+ submitButtonText?: string;
19
22
  };
20
- export default function StripeCheckout({ clientSecret, intentType, publicKey, mode, customer, onConfirm, onCancel, returnUrl, }: StripeCheckoutProps): import("react").JSX.Element;
23
+ export default function StripeCheckout({ clientSecret, intentType, publicKey, mode, customer, onConfirm, onCancel, returnUrl, title, submitButtonText, }: StripeCheckoutProps): import("react").JSX.Element;
@@ -37,7 +37,8 @@ function StripeCheckoutForm({
37
37
  customer,
38
38
  mode,
39
39
  onConfirm,
40
- returnUrl = ""
40
+ returnUrl = "",
41
+ submitButtonText = ""
41
42
  }) {
42
43
  const stripe = useStripe();
43
44
  const elements = useElements();
@@ -247,7 +248,7 @@ function StripeCheckoutForm({
247
248
  variant: "contained",
248
249
  color: "primary",
249
250
  size: "large",
250
- children: t("payment.checkout.continue", {
251
+ children: submitButtonText || t("payment.checkout.continue", {
251
252
  action: t(`payment.checkout.${mode}`)
252
253
  })
253
254
  }), state.message && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
@@ -276,7 +277,9 @@ function StripeCheckout({
276
277
  customer,
277
278
  onConfirm,
278
279
  onCancel,
279
- returnUrl = ""
280
+ returnUrl = "",
281
+ title = "",
282
+ submitButtonText = ""
280
283
  }) {
281
284
  const stripePromise = loadStripe(publicKey);
282
285
  const {
@@ -301,7 +304,7 @@ function StripeCheckout({
301
304
  onCancel();
302
305
  };
303
306
  return /* @__PURE__ */(0, _jsxRuntime.jsx)(_Dialog.default, {
304
- title: t("payment.checkout.cardPay", {
307
+ title: title || t("payment.checkout.cardPay", {
305
308
  action: t(`payment.checkout.${mode}`)
306
309
  }),
307
310
  showCloseButton: state.closable,
@@ -352,7 +355,8 @@ function StripeCheckout({
352
355
  mode,
353
356
  customer,
354
357
  onConfirm,
355
- returnUrl
358
+ returnUrl,
359
+ submitButtonText
356
360
  })
357
361
  })
358
362
  });
@@ -204,7 +204,7 @@ function ProductItem({
204
204
  return t(getLocaleKey("normal", type2), buildBaseParams());
205
205
  }
206
206
  const pendingAmountBN = new _util.BN(pendingAmount || "0");
207
- const creditAmountBN = (0, _util.fromTokenToUnit)(new _util.BN(creditAmount), creditCurrency?.decimal || 2);
207
+ const creditAmountBN = (0, _util.fromTokenToUnit)(creditAmount, creditCurrency?.decimal || 2);
208
208
  const minQuantityNeeded = Math.ceil(pendingAmountBN.mul(new _util.BN(100)).div(creditAmountBN).toNumber() / 100);
209
209
  const currentPurchaseCreditBN = creditAmountBN.mul(new _util.BN(localQuantity || 0));
210
210
  const actualAvailable = currentPurchaseCreditBN.sub(pendingAmountBN).toString();
@@ -446,7 +446,7 @@ function PaymentSummary({
446
446
  sx: {
447
447
  justifyContent: "space-between",
448
448
  alignItems: "center",
449
- ...(staking > 0 && {
449
+ ...(+staking > 0 && {
450
450
  borderTop: "1px solid",
451
451
  borderColor: "divider",
452
452
  pt: 1,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/payment-react",
3
- "version": "1.21.16",
3
+ "version": "1.22.0",
4
4
  "description": "Reusable react components for payment kit v2",
5
5
  "keywords": [
6
6
  "react",
@@ -54,16 +54,16 @@
54
54
  }
55
55
  },
56
56
  "dependencies": {
57
- "@arcblock/did-connect-react": "^3.1.46",
58
- "@arcblock/ux": "^3.1.46",
59
- "@arcblock/ws": "^1.25.6",
60
- "@blocklet/theme": "^3.1.46",
61
- "@blocklet/ui-react": "^3.1.46",
57
+ "@arcblock/did-connect-react": "^3.1.52",
58
+ "@arcblock/ux": "^3.1.52",
59
+ "@arcblock/ws": "^1.26.3",
60
+ "@blocklet/theme": "^3.1.52",
61
+ "@blocklet/ui-react": "^3.1.52",
62
62
  "@mui/icons-material": "^7.1.2",
63
63
  "@mui/lab": "7.0.0-beta.14",
64
64
  "@mui/material": "^7.1.2",
65
65
  "@mui/system": "^7.1.1",
66
- "@ocap/util": "^1.25.6",
66
+ "@ocap/util": "^1.26.3",
67
67
  "@stripe/react-stripe-js": "^2.9.0",
68
68
  "@stripe/stripe-js": "^2.4.0",
69
69
  "@vitejs/plugin-legacy": "^7.0.0",
@@ -94,7 +94,7 @@
94
94
  "@babel/core": "^7.27.4",
95
95
  "@babel/preset-env": "^7.27.2",
96
96
  "@babel/preset-react": "^7.27.1",
97
- "@blocklet/payment-types": "1.21.16",
97
+ "@blocklet/payment-types": "1.22.0",
98
98
  "@storybook/addon-essentials": "^7.6.20",
99
99
  "@storybook/addon-interactions": "^7.6.20",
100
100
  "@storybook/addon-links": "^7.6.20",
@@ -125,5 +125,5 @@
125
125
  "vite-plugin-babel": "^1.3.1",
126
126
  "vite-plugin-node-polyfills": "^0.23.0"
127
127
  },
128
- "gitHead": "16509d9abd2da2f52587972c863c79ba9e4cd49d"
128
+ "gitHead": "d69619c7d669bc6de8620273ff0f3f7b5d56383f"
129
129
  }
@@ -22,6 +22,7 @@ import { formatAmount, formatError, getPrefix, isCrossOrigin } from '../libs/uti
22
22
  import { useSubscription } from '../hooks/subscription';
23
23
  import api from '../libs/api';
24
24
  import LoadingButton from './loading-button';
25
+ import StripePaymentAction from './stripe-payment-action';
25
26
 
26
27
  type DialogProps = {
27
28
  open?: boolean;
@@ -112,12 +113,20 @@ function OverdueInvoicePayment({
112
113
  const [payLoading, setPayLoading] = useState(false);
113
114
  const [dialogOpen, setDialogOpen] = useState(dialogProps.open || false);
114
115
  const [processedCurrencies, setProcessedCurrencies] = useState<{ [key: string]: number }>({});
115
- const [paymentStatus, setPaymentStatus] = useState<{ [key: string]: 'success' | 'error' | 'idle' }>({});
116
+ const [paymentStatus, setPaymentStatus] = useState<{ [key: string]: 'success' | 'error' | 'idle' | 'processing' }>(
117
+ {}
118
+ );
119
+ const [stripePaymentInProgress, setStripePaymentInProgress] = useState<{ [key: string]: boolean }>({});
120
+ const stripePaymentInProgressRef = useRef(stripePaymentInProgress);
116
121
 
117
122
  const sourceType = subscriptionId ? 'subscription' : 'customer';
118
123
  const effectiveCustomerId = customerId || session?.user?.did;
119
124
  const sourceId = subscriptionId || effectiveCustomerId;
120
125
  const customerIdRef = useRef(effectiveCustomerId);
126
+
127
+ useEffect(() => {
128
+ stripePaymentInProgressRef.current = stripePaymentInProgress;
129
+ }, [stripePaymentInProgress]);
121
130
  const {
122
131
  data = {
123
132
  summary: {},
@@ -154,6 +163,36 @@ function OverdueInvoicePayment({
154
163
 
155
164
  const debouncedHandleInvoicePaid = debounce(
156
165
  async (currencyId: string) => {
166
+ // If Stripe payment is in progress, check if it's complete before refreshing
167
+ if (stripePaymentInProgressRef.current[currencyId]) {
168
+ try {
169
+ const checkData = await fetchOverdueInvoices({
170
+ subscriptionId,
171
+ customerId: effectiveCustomerId,
172
+ authToken,
173
+ });
174
+
175
+ const hasRemainingInvoices = checkData.invoices?.some((inv: Invoice) => inv.currency_id === currencyId);
176
+
177
+ // Only refresh UI when all invoices are paid
178
+ if (hasRemainingInvoices) {
179
+ return;
180
+ }
181
+
182
+ // Clear Stripe payment state
183
+ setStripePaymentInProgress((prev) => {
184
+ const newState = { ...prev };
185
+ delete newState[currencyId];
186
+ return newState;
187
+ });
188
+ setPaymentStatus((prev) => ({ ...prev, [currencyId]: 'success' }));
189
+ } catch (err) {
190
+ console.error('Error checking Stripe payment completion:', err);
191
+ return;
192
+ }
193
+ }
194
+
195
+ // Now refresh and update UI
157
196
  if (successToast) {
158
197
  Toast.close();
159
198
  Toast.success(t('payment.customer.invoice.paySuccess'));
@@ -189,7 +228,7 @@ function OverdueInvoicePayment({
189
228
  return isPaid;
190
229
  };
191
230
 
192
- const handleConnected = async () => {
231
+ const handleConnected = async (currencyId: string, isStripe = false) => {
193
232
  if (isCrossOriginRequest) {
194
233
  try {
195
234
  const paid = await waitForInvoiceAllPaid();
@@ -199,32 +238,43 @@ function OverdueInvoicePayment({
199
238
  }
200
239
  if (paid) {
201
240
  setDialogOpen(false);
202
- onPaid(sourceId as string, selectCurrencyId, sourceType as 'subscription' | 'customer');
241
+ onPaid(sourceId as string, currencyId, sourceType as 'subscription' | 'customer');
203
242
  }
204
243
  } catch (err) {
205
244
  console.error('Check payment status failed:', err);
206
245
  }
246
+ } else if (isStripe) {
247
+ setStripePaymentInProgress((prev) => ({ ...prev, [currencyId]: true }));
248
+ setPaymentStatus((prev) => ({ ...prev, [currencyId]: 'processing' }));
207
249
  }
208
250
  };
209
251
 
210
252
  useEffect(() => {
211
- if (subscription && !isCrossOriginRequest) {
212
- subscription.on('invoice.paid', ({ response }: { response: TInvoiceExpanded }) => {
213
- const relevantId = subscriptionId || response.customer_id;
214
- const uniqueKey = `${relevantId}-${response.currency_id}`;
215
-
216
- if (
217
- (subscriptionId && response.subscription_id === subscriptionId) ||
218
- (effectiveCustomerId && effectiveCustomerId === response.customer_id) ||
219
- (customerIdRef.current && customerIdRef.current === response.customer_id)
220
- ) {
221
- if (!processedCurrencies[uniqueKey]) {
222
- setProcessedCurrencies((prev) => ({ ...prev, [uniqueKey]: 1 }));
223
- debouncedHandleInvoicePaid(response.currency_id);
224
- }
225
- }
226
- });
253
+ if (!subscription || isCrossOriginRequest) {
254
+ return undefined;
227
255
  }
256
+
257
+ const handleInvoicePaid = ({ response }: { response: TInvoiceExpanded }) => {
258
+ const relevantId = subscriptionId || response.customer_id;
259
+ const uniqueKey = `${relevantId}-${response.currency_id}`;
260
+
261
+ if (
262
+ (subscriptionId && response.subscription_id === subscriptionId) ||
263
+ (effectiveCustomerId && effectiveCustomerId === response.customer_id) ||
264
+ (customerIdRef.current && customerIdRef.current === response.customer_id)
265
+ ) {
266
+ if (!processedCurrencies[uniqueKey]) {
267
+ setProcessedCurrencies((prev) => ({ ...prev, [uniqueKey]: 1 }));
268
+ debouncedHandleInvoicePaid(response.currency_id);
269
+ }
270
+ }
271
+ };
272
+
273
+ subscription.on('invoice.paid', handleInvoicePaid);
274
+
275
+ return () => {
276
+ subscription.off('invoice.paid', handleInvoicePaid);
277
+ };
228
278
  // eslint-disable-next-line react-hooks/exhaustive-deps
229
279
  }, [subscription, subscriptionId, effectiveCustomerId]);
230
280
 
@@ -268,7 +318,7 @@ function OverdueInvoicePayment({
268
318
  } as any,
269
319
  onSuccess: () => {
270
320
  connect.close();
271
- handleConnected();
321
+ handleConnected(currency.id);
272
322
  setPayLoading(false);
273
323
  setPaymentStatus((prev) => ({
274
324
  ...prev,
@@ -325,7 +375,7 @@ function OverdueInvoicePayment({
325
375
  const renderPayButton = (
326
376
  item: SummaryItem,
327
377
  primaryButton = true,
328
- props: {
378
+ options: {
329
379
  variant?: 'contained' | 'text';
330
380
  sx?: SxProps;
331
381
  } = {
@@ -339,8 +389,7 @@ function OverdueInvoicePayment({
339
389
  if (status === 'success') {
340
390
  return (
341
391
  <Button
342
- // eslint-disable-next-line react/prop-types
343
- variant={props?.variant || 'contained'}
392
+ variant={options?.variant || 'contained'}
344
393
  size="small"
345
394
  {...(primaryButton
346
395
  ? {}
@@ -355,7 +404,7 @@ function OverdueInvoicePayment({
355
404
 
356
405
  if (status === 'error') {
357
406
  return (
358
- <Button variant="contained" size="small" onClick={() => handlePay(item)} {...props}>
407
+ <Button variant={options?.variant || 'contained'} size="small" onClick={() => handlePay(item)} sx={options?.sx}>
359
408
  {t('payment.subscription.overdue.retry')}
360
409
  </Button>
361
410
  );
@@ -363,19 +412,42 @@ function OverdueInvoicePayment({
363
412
 
364
413
  if (item.method.type === 'stripe') {
365
414
  return (
366
- <Button variant="contained" color="primary" onClick={() => window.open(detailUrl, '_blank')} {...props}>
367
- {t('payment.subscription.overdue.viewNow')}
368
- </Button>
415
+ <StripePaymentAction
416
+ subscriptionId={subscriptionId}
417
+ customerId={!subscriptionId ? effectiveCustomerId : undefined}
418
+ currencyId={currency.id}
419
+ paymentMethod={item.method}
420
+ onSuccess={() => {
421
+ handleConnected(currency.id, true);
422
+ }}
423
+ onError={() => {
424
+ setPaymentStatus((prev) => ({ ...prev, [currency.id]: 'error' }));
425
+ setStripePaymentInProgress((prev) => ({ ...prev, [currency.id]: false }));
426
+ }}>
427
+ {(onPay: () => void, paying: boolean) => (
428
+ <LoadingButton
429
+ variant={options?.variant || 'contained'}
430
+ size="small"
431
+ disabled={paying || status === 'processing'}
432
+ loading={paying || status === 'processing'}
433
+ onClick={onPay}
434
+ sx={options?.sx}>
435
+ {status === 'processing'
436
+ ? t('payment.subscription.overdue.processing')
437
+ : t('payment.subscription.overdue.payNow')}
438
+ </LoadingButton>
439
+ )}
440
+ </StripePaymentAction>
369
441
  );
370
442
  }
371
443
  return (
372
444
  <LoadingButton
373
- variant="contained"
445
+ variant={options?.variant || 'contained'}
374
446
  size="small"
375
447
  disabled={inProcess}
376
448
  loading={inProcess}
377
449
  onClick={() => handlePay(item)}
378
- {...props}>
450
+ sx={options?.sx}>
379
451
  {t('payment.subscription.overdue.payNow')}
380
452
  </LoadingButton>
381
453
  );
@@ -3,7 +3,7 @@ import { Avatar, Box, Stack, Typography } from '@mui/material';
3
3
  import { BN } from '@ocap/util';
4
4
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
5
5
  import DID from '@arcblock/ux/lib/DID';
6
- import { formatBNStr } from '../libs/util';
6
+ import { formatBNStr, formatLinkWithLocale } from '../libs/util';
7
7
 
8
8
  export interface TBeneficiary {
9
9
  name: string;
@@ -26,7 +26,7 @@ interface BenefitsProps {
26
26
  }
27
27
 
28
28
  export default function PaymentBeneficiaries({ data, currency, totalAmount = '0' }: BenefitsProps) {
29
- const { t } = useLocaleContext();
29
+ const { t, locale } = useLocaleContext();
30
30
  return (
31
31
  <Stack spacing={2}>
32
32
  <Typography
@@ -78,7 +78,7 @@ export default function PaymentBeneficiaries({ data, currency, totalAmount = '0'
78
78
  variant="subtitle2"
79
79
  onClick={() => {
80
80
  if (item.url) {
81
- window.open(item.url, '_blank');
81
+ window.open(formatLinkWithLocale(item.url, locale), '_blank');
82
82
  }
83
83
  }}
84
84
  sx={{