@blocklet/payment-react 1.16.17 → 1.16.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.
@@ -38,7 +38,15 @@ function PaymentProvider({ session, connect, children, baseUrl }) {
38
38
  window.__PAYMENT_KIT_BASE_URL = baseUrl;
39
39
  }
40
40
  const [livemode, setLivemode] = useLocalStorageState("livemode", { defaultValue: true });
41
- const { data, error, run, loading } = useRequest(getSettings, {
41
+ const {
42
+ data = {
43
+ paymentMethods: [],
44
+ baseCurrency: {}
45
+ },
46
+ error,
47
+ run,
48
+ loading
49
+ } = useRequest(getSettings, {
42
50
  refreshDeps: [livemode]
43
51
  });
44
52
  const prefix = getPrefix();
@@ -5,8 +5,9 @@ import { OpenInNewOutlined } from "@mui/icons-material";
5
5
  import { Box, Button, CircularProgress, Hidden, Stack, Typography } from "@mui/material";
6
6
  import { styled } from "@mui/system";
7
7
  import { useInfiniteScroll, useRequest, useSetState } from "ahooks";
8
- import React, { useEffect, useState } from "react";
8
+ import React, { useEffect, useRef, useState } from "react";
9
9
  import { joinURL } from "ufo";
10
+ import debounce from "lodash/debounce";
10
11
  import Status from "../../components/status.js";
11
12
  import { usePaymentContext } from "../../contexts/payment.js";
12
13
  import { useSubscription } from "../../hooks/subscription.js";
@@ -100,18 +101,32 @@ const InvoiceTable = React.memo((props) => {
100
101
  refreshDeps: [search, status, customer_id, currency_id, subscription_id, include_staking, include_recovered_from]
101
102
  }
102
103
  );
104
+ const prevData = useRef(data);
103
105
  useEffect(() => {
104
106
  if (onTableDataChange) {
105
- onTableDataChange(data);
107
+ onTableDataChange(data, prevData.current);
108
+ prevData.current = data;
106
109
  }
107
110
  }, [data]);
108
111
  const subscription = useSubscription("events");
112
+ const debouncedHandleInvoicePaid = debounce(
113
+ async () => {
114
+ Toast.close();
115
+ Toast.success(t("payment.customer.invoice.paySuccess"));
116
+ await refresh();
117
+ },
118
+ 1e3,
119
+ {
120
+ leading: false,
121
+ trailing: true,
122
+ maxWait: 5e3
123
+ }
124
+ );
109
125
  useEffect(() => {
110
126
  if (subscription && customer_id) {
111
127
  subscription.on("invoice.paid", ({ response }) => {
112
128
  if (response.customer_id === customer_id) {
113
- Toast.success(t("payment.customer.invoice.paySuccess"));
114
- refresh();
129
+ debouncedHandleInvoicePaid();
115
130
  }
116
131
  });
117
132
  }
@@ -186,7 +201,8 @@ const InvoiceTable = React.memo((props) => {
186
201
  customBodyRenderLite: (val, index) => {
187
202
  const invoice = data?.list[index];
188
203
  const link = getInvoiceLink(invoice, action);
189
- if (action) {
204
+ const hidePay = invoice.billing_reason === "overdraft-protection";
205
+ if (action && !hidePay) {
190
206
  return link.connect ? /* @__PURE__ */ jsx(Button, { variant: "text", size: "small", onClick: () => onPay(invoice.id), sx: { color: "text.link" }, children: t("payment.customer.invoice.pay") }) : /* @__PURE__ */ jsx(
191
207
  Button,
192
208
  {
@@ -286,17 +302,31 @@ const InvoiceList = React.memo((props) => {
286
302
  reloadDeps: [customer_id, subscription_id, status, include_staking, include_recovered_from]
287
303
  }
288
304
  );
305
+ const prevData = useRef(data);
289
306
  useEffect(() => {
290
307
  if (onTableDataChange) {
291
- onTableDataChange(data);
308
+ onTableDataChange(data, prevData.current);
309
+ prevData.current = data;
292
310
  }
293
311
  }, [data]);
312
+ const debouncedHandleInvoicePaid = debounce(
313
+ async () => {
314
+ Toast.close();
315
+ Toast.success(t("payment.customer.invoice.paySuccess"));
316
+ await reloadAsync();
317
+ },
318
+ 1e3,
319
+ {
320
+ leading: false,
321
+ trailing: true,
322
+ maxWait: 5e3
323
+ }
324
+ );
294
325
  useEffect(() => {
295
326
  if (subscription && customer_id) {
296
- subscription.on("invoice.paid", async ({ response }) => {
327
+ subscription.on("invoice.paid", ({ response }) => {
297
328
  if (response.customer_id === customer_id) {
298
- Toast.success(t("payment.customer.invoice.paySuccess"));
299
- await reloadAsync();
329
+ debouncedHandleInvoicePaid();
300
330
  }
301
331
  });
302
332
  }
package/es/libs/util.js CHANGED
@@ -105,6 +105,9 @@ export function formatNumber(n, precision = 6, trim = true) {
105
105
  return right ? [left, trimEnd(right, "0")].filter(Boolean).join(".") : left;
106
106
  }
107
107
  export const formatPrice = (price, currency, unit_label, quantity = 1, bn = true, locale = "en") => {
108
+ if (!currency) {
109
+ return "";
110
+ }
108
111
  if (price.custom_unit_amount) {
109
112
  return `Custom (${currency.symbol})`;
110
113
  }
@@ -123,6 +126,9 @@ export const formatPrice = (price, currency, unit_label, quantity = 1, bn = true
123
126
  return `${amount} ${currency.symbol}`;
124
127
  };
125
128
  export const formatPriceAmount = (price, currency, unit_label, quantity = 1, bn = true) => {
129
+ if (!currency) {
130
+ return "";
131
+ }
126
132
  const unit = getPriceUintAmountByCurrency(price, currency);
127
133
  const amount = bn ? fromUnitToToken(new BN(unit).mul(new BN(quantity)), currency.decimal).toString() : +unit * quantity;
128
134
  if (price?.type === "recurring" && price.recurring) {
@@ -174,20 +180,20 @@ export function formatRecurring(recurring, translate = true, separator = "per",
174
180
  }
175
181
  export function getPriceUintAmountByCurrency(price, currency) {
176
182
  const options = getPriceCurrencyOptions(price);
177
- const option = options.find((x) => x.currency_id === currency.id);
183
+ const option = options.find((x) => x.currency_id === currency?.id);
178
184
  if (option) {
179
185
  if (option.custom_unit_amount) {
180
186
  return option.custom_unit_amount.preset || option.custom_unit_amount.presets[0];
181
187
  }
182
188
  return option.unit_amount;
183
189
  }
184
- if (price.currency_id === currency.id) {
190
+ if (price.currency_id === currency?.id) {
185
191
  if (price.custom_unit_amount) {
186
192
  return price.custom_unit_amount.preset || price.custom_unit_amount.presets[0];
187
193
  }
188
194
  return price.unit_amount;
189
195
  }
190
- console.warn(`Currency ${currency.id} not configured for price`, price);
196
+ console.warn(`Currency ${currency?.id} not configured for price`, price);
191
197
  return "0";
192
198
  }
193
199
  export function getPriceCurrencyOptions(price) {
@@ -204,6 +210,9 @@ export function getPriceCurrencyOptions(price) {
204
210
  ];
205
211
  }
206
212
  export function formatLineItemPricing(item, currency, { trialEnd, trialInDays }, locale = "en") {
213
+ if (!currency) {
214
+ return { primary: "", secondary: "", quantity: "" };
215
+ }
207
216
  const price = item.upsell_price || item.price;
208
217
  let quantity = t("common.qty", locale, { count: item.quantity });
209
218
  if (price.recurring?.usage_type === "metered" || +item.quantity === 1) {
@@ -863,10 +872,12 @@ export function getInvoiceDescriptionAndReason(invoice, locale = "en") {
863
872
  slash_stake: t("payment.invoice.reason.slashStake", locale),
864
873
  stake: t("payment.invoice.reason.stake", locale),
865
874
  return_stake: t("payment.invoice.reason.returnStake", locale),
866
- recharge: t("payment.invoice.reason.recharge", locale)
875
+ recharge: t("payment.invoice.reason.recharge", locale),
876
+ stake_overdraft_protection: t("payment.invoice.reason.stake", locale),
877
+ overdraft_protection: t("payment.invoice.reason.fee", locale)
867
878
  };
868
879
  let invoiceType = t("payment.invoice.reason.payment", locale);
869
- if (reason.includes("stake") || reason.includes("recharge")) {
880
+ if (reason.includes("stake") || reason.includes("recharge") || reason === "overdraft_protection") {
870
881
  invoiceType = reasonMap[reason];
871
882
  }
872
883
  if (description?.startsWith("Subscription ") || description?.startsWith("Slash stake")) {
@@ -882,7 +893,12 @@ export function getInvoiceDescriptionAndReason(invoice, locale = "en") {
882
893
  "Stake for subscription": t("payment.invoice.reason.staking", locale),
883
894
  "Return Subscription staking": t("payment.invoice.reason.returnStake", locale),
884
895
  "Recharge for subscription": t("payment.invoice.reason.rechargeForSubscription", locale),
885
- "Add funds for subscription": t("payment.invoice.reason.rechargeForSubscription", locale)
896
+ "Add funds for subscription": t("payment.invoice.reason.rechargeForSubscription", locale),
897
+ "Overdraft protection": t("payment.invoice.reason.overdraftProtection", locale),
898
+ "Stake for subscription overdraft protection": t(
899
+ "payment.invoice.reason.stakeForSubscriptionOverdraftProtection",
900
+ locale
901
+ )
886
902
  };
887
903
  return {
888
904
  description: descMap[description] || description,
package/es/locales/en.js CHANGED
@@ -315,7 +315,11 @@ export default flat({
315
315
  stakeForChangePlan: "Subscription plan update",
316
316
  stakeForChangePayment: "Subscription payment method update",
317
317
  recharge: "Add funds",
318
- rechargeForSubscription: "Add funds for subscription"
318
+ rechargeForSubscription: "Add funds for subscription",
319
+ overdraftProtection: "Overdraft protection Fee",
320
+ stakeForSubscriptionOverdraftProtection: "Subscription overdraft protection",
321
+ gas: "Gas",
322
+ fee: "Fee"
319
323
  }
320
324
  },
321
325
  subscription: {
package/es/locales/zh.js CHANGED
@@ -315,7 +315,11 @@ export default flat({
315
315
  stakeForChangePlan: "\u8BA2\u9605\u5957\u9910\u66F4\u65B0",
316
316
  stakeForChangePayment: "\u8BA2\u9605\u652F\u4ED8\u65B9\u5F0F\u66F4\u65B0",
317
317
  recharge: "\u5145\u503C",
318
- rechargeForSubscription: "\u8BA2\u9605\u5145\u503C"
318
+ rechargeForSubscription: "\u8BA2\u9605\u5145\u503C",
319
+ overdraftProtection: "\u900F\u652F\u4FDD\u62A4\u8D39",
320
+ stakeForSubscriptionOverdraftProtection: "\u8BA2\u9605\u900F\u652F\u4FDD\u62A4",
321
+ gas: "\u624B\u7EED\u8D39",
322
+ fee: "\u670D\u52A1\u8D39"
319
323
  }
320
324
  },
321
325
  subscription: {
@@ -452,7 +452,10 @@ export default function PaymentForm({
452
452
  state.customerLimited && /* @__PURE__ */ jsx(
453
453
  ConfirmDialog,
454
454
  {
455
- onConfirm: () => window.open(joinURL(getPrefix(), "/customer/invoice/past-due"), "_blank"),
455
+ onConfirm: () => window.open(
456
+ joinURL(getPrefix(), `/customer/invoice/past-due?referer=${encodeURIComponent(window.location.href)}`),
457
+ "_self"
458
+ ),
456
459
  onCancel: () => setState({ customerLimited: false }),
457
460
  confirm: t("payment.customer.pastDue.alert.confirm"),
458
461
  title: t("payment.customer.pastDue.alert.title"),
@@ -68,7 +68,10 @@ function PaymentProvider({
68
68
  defaultValue: true
69
69
  });
70
70
  const {
71
- data,
71
+ data = {
72
+ paymentMethods: [],
73
+ baseCurrency: {}
74
+ },
72
75
  error,
73
76
  run,
74
77
  loading
@@ -13,6 +13,7 @@ var _system = require("@mui/system");
13
13
  var _ahooks = require("ahooks");
14
14
  var _react = _interopRequireWildcard(require("react"));
15
15
  var _ufo = require("ufo");
16
+ var _debounce = _interopRequireDefault(require("lodash/debounce"));
16
17
  var _status = _interopRequireDefault(require("../../components/status"));
17
18
  var _payment = require("../../contexts/payment");
18
19
  var _subscription = require("../../hooks/subscription");
@@ -100,20 +101,30 @@ const InvoiceTable = _react.default.memo(props => {
100
101
  }), {
101
102
  refreshDeps: [search, status, customer_id, currency_id, subscription_id, include_staking, include_recovered_from]
102
103
  });
104
+ const prevData = (0, _react.useRef)(data);
103
105
  (0, _react.useEffect)(() => {
104
106
  if (onTableDataChange) {
105
- onTableDataChange(data);
107
+ onTableDataChange(data, prevData.current);
108
+ prevData.current = data;
106
109
  }
107
110
  }, [data]);
108
111
  const subscription = (0, _subscription.useSubscription)("events");
112
+ const debouncedHandleInvoicePaid = (0, _debounce.default)(async () => {
113
+ _Toast.default.close();
114
+ _Toast.default.success(t("payment.customer.invoice.paySuccess"));
115
+ await refresh();
116
+ }, 1e3, {
117
+ leading: false,
118
+ trailing: true,
119
+ maxWait: 5e3
120
+ });
109
121
  (0, _react.useEffect)(() => {
110
122
  if (subscription && customer_id) {
111
123
  subscription.on("invoice.paid", ({
112
124
  response
113
125
  }) => {
114
126
  if (response.customer_id === customer_id) {
115
- _Toast.default.success(t("payment.customer.invoice.paySuccess"));
116
- refresh();
127
+ debouncedHandleInvoicePaid();
117
128
  }
118
129
  });
119
130
  }
@@ -207,7 +218,8 @@ const InvoiceTable = _react.default.memo(props => {
207
218
  customBodyRenderLite: (val, index) => {
208
219
  const invoice = data?.list[index];
209
220
  const link = getInvoiceLink(invoice, action);
210
- if (action) {
221
+ const hidePay = invoice.billing_reason === "overdraft-protection";
222
+ if (action && !hidePay) {
211
223
  return link.connect ? /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Button, {
212
224
  variant: "text",
213
225
  size: "small",
@@ -340,19 +352,29 @@ const InvoiceList = _react.default.memo(props => {
340
352
  }, {
341
353
  reloadDeps: [customer_id, subscription_id, status, include_staking, include_recovered_from]
342
354
  });
355
+ const prevData = (0, _react.useRef)(data);
343
356
  (0, _react.useEffect)(() => {
344
357
  if (onTableDataChange) {
345
- onTableDataChange(data);
358
+ onTableDataChange(data, prevData.current);
359
+ prevData.current = data;
346
360
  }
347
361
  }, [data]);
362
+ const debouncedHandleInvoicePaid = (0, _debounce.default)(async () => {
363
+ _Toast.default.close();
364
+ _Toast.default.success(t("payment.customer.invoice.paySuccess"));
365
+ await reloadAsync();
366
+ }, 1e3, {
367
+ leading: false,
368
+ trailing: true,
369
+ maxWait: 5e3
370
+ });
348
371
  (0, _react.useEffect)(() => {
349
372
  if (subscription && customer_id) {
350
- subscription.on("invoice.paid", async ({
373
+ subscription.on("invoice.paid", ({
351
374
  response
352
375
  }) => {
353
376
  if (response.customer_id === customer_id) {
354
- _Toast.default.success(t("payment.customer.invoice.paySuccess"));
355
- await reloadAsync();
377
+ debouncedHandleInvoicePaid();
356
378
  }
357
379
  });
358
380
  }
package/lib/libs/util.js CHANGED
@@ -174,6 +174,9 @@ function formatNumber(n, precision = 6, trim = true) {
174
174
  return right ? [left, (0, _trimEnd.default)(right, "0")].filter(Boolean).join(".") : left;
175
175
  }
176
176
  const formatPrice = (price, currency, unit_label, quantity = 1, bn = true, locale = "en") => {
177
+ if (!currency) {
178
+ return "";
179
+ }
177
180
  if (price.custom_unit_amount) {
178
181
  return `Custom (${currency.symbol})`;
179
182
  }
@@ -193,6 +196,9 @@ const formatPrice = (price, currency, unit_label, quantity = 1, bn = true, local
193
196
  };
194
197
  exports.formatPrice = formatPrice;
195
198
  const formatPriceAmount = (price, currency, unit_label, quantity = 1, bn = true) => {
199
+ if (!currency) {
200
+ return "";
201
+ }
196
202
  const unit = getPriceUintAmountByCurrency(price, currency);
197
203
  const amount = bn ? (0, _util.fromUnitToToken)(new _util.BN(unit).mul(new _util.BN(quantity)), currency.decimal).toString() : +unit * quantity;
198
204
  if (price?.type === "recurring" && price.recurring) {
@@ -247,20 +253,20 @@ function formatRecurring(recurring, translate = true, separator = "per", locale
247
253
  }
248
254
  function getPriceUintAmountByCurrency(price, currency) {
249
255
  const options = getPriceCurrencyOptions(price);
250
- const option = options.find(x => x.currency_id === currency.id);
256
+ const option = options.find(x => x.currency_id === currency?.id);
251
257
  if (option) {
252
258
  if (option.custom_unit_amount) {
253
259
  return option.custom_unit_amount.preset || option.custom_unit_amount.presets[0];
254
260
  }
255
261
  return option.unit_amount;
256
262
  }
257
- if (price.currency_id === currency.id) {
263
+ if (price.currency_id === currency?.id) {
258
264
  if (price.custom_unit_amount) {
259
265
  return price.custom_unit_amount.preset || price.custom_unit_amount.presets[0];
260
266
  }
261
267
  return price.unit_amount;
262
268
  }
263
- console.warn(`Currency ${currency.id} not configured for price`, price);
269
+ console.warn(`Currency ${currency?.id} not configured for price`, price);
264
270
  return "0";
265
271
  }
266
272
  function getPriceCurrencyOptions(price) {
@@ -278,6 +284,13 @@ function formatLineItemPricing(item, currency, {
278
284
  trialEnd,
279
285
  trialInDays
280
286
  }, locale = "en") {
287
+ if (!currency) {
288
+ return {
289
+ primary: "",
290
+ secondary: "",
291
+ quantity: ""
292
+ };
293
+ }
281
294
  const price = item.upsell_price || item.price;
282
295
  let quantity = (0, _locales.t)("common.qty", locale, {
283
296
  count: item.quantity
@@ -1046,10 +1059,12 @@ function getInvoiceDescriptionAndReason(invoice, locale = "en") {
1046
1059
  slash_stake: (0, _locales.t)("payment.invoice.reason.slashStake", locale),
1047
1060
  stake: (0, _locales.t)("payment.invoice.reason.stake", locale),
1048
1061
  return_stake: (0, _locales.t)("payment.invoice.reason.returnStake", locale),
1049
- recharge: (0, _locales.t)("payment.invoice.reason.recharge", locale)
1062
+ recharge: (0, _locales.t)("payment.invoice.reason.recharge", locale),
1063
+ stake_overdraft_protection: (0, _locales.t)("payment.invoice.reason.stake", locale),
1064
+ overdraft_protection: (0, _locales.t)("payment.invoice.reason.fee", locale)
1050
1065
  };
1051
1066
  let invoiceType = (0, _locales.t)("payment.invoice.reason.payment", locale);
1052
- if (reason.includes("stake") || reason.includes("recharge")) {
1067
+ if (reason.includes("stake") || reason.includes("recharge") || reason === "overdraft_protection") {
1053
1068
  invoiceType = reasonMap[reason];
1054
1069
  }
1055
1070
  if (description?.startsWith("Subscription ") || description?.startsWith("Slash stake")) {
@@ -1065,7 +1080,9 @@ function getInvoiceDescriptionAndReason(invoice, locale = "en") {
1065
1080
  "Stake for subscription": (0, _locales.t)("payment.invoice.reason.staking", locale),
1066
1081
  "Return Subscription staking": (0, _locales.t)("payment.invoice.reason.returnStake", locale),
1067
1082
  "Recharge for subscription": (0, _locales.t)("payment.invoice.reason.rechargeForSubscription", locale),
1068
- "Add funds for subscription": (0, _locales.t)("payment.invoice.reason.rechargeForSubscription", locale)
1083
+ "Add funds for subscription": (0, _locales.t)("payment.invoice.reason.rechargeForSubscription", locale),
1084
+ "Overdraft protection": (0, _locales.t)("payment.invoice.reason.overdraftProtection", locale),
1085
+ "Stake for subscription overdraft protection": (0, _locales.t)("payment.invoice.reason.stakeForSubscriptionOverdraftProtection", locale)
1069
1086
  };
1070
1087
  return {
1071
1088
  description: descMap[description] || description,
package/lib/locales/en.js CHANGED
@@ -322,7 +322,11 @@ module.exports = (0, _flat.default)({
322
322
  stakeForChangePlan: "Subscription plan update",
323
323
  stakeForChangePayment: "Subscription payment method update",
324
324
  recharge: "Add funds",
325
- rechargeForSubscription: "Add funds for subscription"
325
+ rechargeForSubscription: "Add funds for subscription",
326
+ overdraftProtection: "Overdraft protection Fee",
327
+ stakeForSubscriptionOverdraftProtection: "Subscription overdraft protection",
328
+ gas: "Gas",
329
+ fee: "Fee"
326
330
  }
327
331
  },
328
332
  subscription: {
package/lib/locales/zh.js CHANGED
@@ -322,7 +322,11 @@ module.exports = (0, _flat.default)({
322
322
  stakeForChangePlan: "\u8BA2\u9605\u5957\u9910\u66F4\u65B0",
323
323
  stakeForChangePayment: "\u8BA2\u9605\u652F\u4ED8\u65B9\u5F0F\u66F4\u65B0",
324
324
  recharge: "\u5145\u503C",
325
- rechargeForSubscription: "\u8BA2\u9605\u5145\u503C"
325
+ rechargeForSubscription: "\u8BA2\u9605\u5145\u503C",
326
+ overdraftProtection: "\u900F\u652F\u4FDD\u62A4\u8D39",
327
+ stakeForSubscriptionOverdraftProtection: "\u8BA2\u9605\u900F\u652F\u4FDD\u62A4",
328
+ gas: "\u624B\u7EED\u8D39",
329
+ fee: "\u670D\u52A1\u8D39"
326
330
  }
327
331
  },
328
332
  subscription: {
@@ -521,7 +521,7 @@ function PaymentForm({
521
521
  })]
522
522
  })
523
523
  }), state.customerLimited && /* @__PURE__ */(0, _jsxRuntime.jsx)(_confirm.default, {
524
- onConfirm: () => window.open((0, _ufo.joinURL)((0, _util.getPrefix)(), "/customer/invoice/past-due"), "_blank"),
524
+ onConfirm: () => window.open((0, _ufo.joinURL)((0, _util.getPrefix)(), `/customer/invoice/past-due?referer=${encodeURIComponent(window.location.href)}`), "_self"),
525
525
  onCancel: () => setState({
526
526
  customerLimited: false
527
527
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/payment-react",
3
- "version": "1.16.17",
3
+ "version": "1.16.19",
4
4
  "description": "Reusable react components for payment kit v2",
5
5
  "keywords": [
6
6
  "react",
@@ -53,15 +53,15 @@
53
53
  }
54
54
  },
55
55
  "dependencies": {
56
- "@arcblock/did-connect": "^2.10.88",
57
- "@arcblock/ux": "^2.10.88",
58
- "@arcblock/ws": "^1.18.161",
59
- "@blocklet/ui-react": "^2.10.88",
56
+ "@arcblock/did-connect": "^2.11.15",
57
+ "@arcblock/ux": "^2.11.15",
58
+ "@arcblock/ws": "^1.18.165",
59
+ "@blocklet/ui-react": "^2.11.15",
60
60
  "@mui/icons-material": "^5.16.6",
61
61
  "@mui/lab": "^5.0.0-alpha.173",
62
62
  "@mui/material": "^5.16.6",
63
63
  "@mui/system": "^5.16.6",
64
- "@ocap/util": "^1.18.161",
64
+ "@ocap/util": "^1.18.165",
65
65
  "@stripe/react-stripe-js": "^2.7.3",
66
66
  "@stripe/stripe-js": "^2.4.0",
67
67
  "@vitejs/plugin-legacy": "^5.4.1",
@@ -92,7 +92,7 @@
92
92
  "@babel/core": "^7.25.2",
93
93
  "@babel/preset-env": "^7.25.2",
94
94
  "@babel/preset-react": "^7.24.7",
95
- "@blocklet/payment-types": "1.16.17",
95
+ "@blocklet/payment-types": "1.16.19",
96
96
  "@storybook/addon-essentials": "^7.6.20",
97
97
  "@storybook/addon-interactions": "^7.6.20",
98
98
  "@storybook/addon-links": "^7.6.20",
@@ -123,5 +123,5 @@
123
123
  "vite-plugin-babel": "^1.2.0",
124
124
  "vite-plugin-node-polyfills": "^0.21.0"
125
125
  },
126
- "gitHead": "38b1da1771951e6a30f3cb643fadb108aceed5b6"
126
+ "gitHead": "a033985b46d5c80b56f2ed9dc86d808fb60d9db1"
127
127
  }
@@ -81,7 +81,15 @@ function PaymentProvider({ session, connect, children, baseUrl }: PaymentContext
81
81
 
82
82
  const [livemode, setLivemode] = useLocalStorageState('livemode', { defaultValue: true });
83
83
 
84
- const { data, error, run, loading } = useRequest(getSettings, {
84
+ const {
85
+ data = {
86
+ paymentMethods: [],
87
+ baseCurrency: {},
88
+ },
89
+ error,
90
+ run,
91
+ loading,
92
+ } = useRequest(getSettings, {
85
93
  refreshDeps: [livemode],
86
94
  });
87
95
  const prefix = getPrefix();
@@ -11,9 +11,10 @@ import { OpenInNewOutlined } from '@mui/icons-material';
11
11
  import { Box, Button, CircularProgress, Hidden, Stack, Typography } from '@mui/material';
12
12
  import { styled } from '@mui/system';
13
13
  import { useInfiniteScroll, useRequest, useSetState } from 'ahooks';
14
- import React, { useEffect, useState } from 'react';
14
+ import React, { useEffect, useRef, useState } from 'react';
15
15
  import { joinURL } from 'ufo';
16
16
 
17
+ import debounce from 'lodash/debounce';
17
18
  import Status from '../../components/status';
18
19
  import { usePaymentContext } from '../../contexts/payment';
19
20
  import { useSubscription } from '../../hooks/subscription';
@@ -131,21 +132,38 @@ const InvoiceTable = React.memo((props: Props & { onPay: (invoiceId: string) =>
131
132
  refreshDeps: [search, status, customer_id, currency_id, subscription_id, include_staking, include_recovered_from],
132
133
  }
133
134
  );
135
+
136
+ const prevData = useRef(data);
137
+
134
138
  useEffect(() => {
135
139
  if (onTableDataChange) {
136
- onTableDataChange(data);
140
+ onTableDataChange(data, prevData.current);
141
+ prevData.current = data;
137
142
  }
138
143
  // eslint-disable-next-line react-hooks/exhaustive-deps
139
144
  }, [data]);
140
145
 
141
146
  const subscription = useSubscription('events');
142
147
 
148
+ const debouncedHandleInvoicePaid = debounce(
149
+ async () => {
150
+ Toast.close();
151
+ Toast.success(t('payment.customer.invoice.paySuccess'));
152
+ await refresh();
153
+ },
154
+ 1000,
155
+ {
156
+ leading: false,
157
+ trailing: true,
158
+ maxWait: 5000,
159
+ }
160
+ );
161
+
143
162
  useEffect(() => {
144
163
  if (subscription && customer_id) {
145
164
  subscription.on('invoice.paid', ({ response }: { response: TInvoiceExpanded }) => {
146
165
  if (response.customer_id === customer_id) {
147
- Toast.success(t('payment.customer.invoice.paySuccess'));
148
- refresh();
166
+ debouncedHandleInvoicePaid();
149
167
  }
150
168
  });
151
169
  }
@@ -241,7 +259,8 @@ const InvoiceTable = React.memo((props: Props & { onPay: (invoiceId: string) =>
241
259
  customBodyRenderLite: (val: string, index: number) => {
242
260
  const invoice = data?.list[index] as TInvoiceExpanded;
243
261
  const link = getInvoiceLink(invoice, action);
244
- if (action) {
262
+ const hidePay = invoice.billing_reason === 'overdraft-protection';
263
+ if (action && !hidePay) {
245
264
  return link.connect ? (
246
265
  <Button variant="text" size="small" onClick={() => onPay(invoice.id)} sx={{ color: 'text.link' }}>
247
266
  {t('payment.customer.invoice.pay')}
@@ -356,20 +375,35 @@ const InvoiceList = React.memo((props: Props & { onPay: (invoiceId: string) => v
356
375
  }
357
376
  );
358
377
 
378
+ const prevData = useRef(data);
379
+
359
380
  useEffect(() => {
360
381
  if (onTableDataChange) {
361
- onTableDataChange(data);
382
+ onTableDataChange(data, prevData.current);
383
+ prevData.current = data;
362
384
  }
363
385
  // eslint-disable-next-line react-hooks/exhaustive-deps
364
386
  }, [data]);
365
387
 
388
+ const debouncedHandleInvoicePaid = debounce(
389
+ async () => {
390
+ Toast.close();
391
+ Toast.success(t('payment.customer.invoice.paySuccess'));
392
+ await reloadAsync();
393
+ },
394
+ 1000,
395
+ {
396
+ leading: false,
397
+ trailing: true,
398
+ maxWait: 5000,
399
+ }
400
+ );
366
401
  // Listen to invoice.paid event and refresh data
367
402
  useEffect(() => {
368
403
  if (subscription && customer_id) {
369
- subscription.on('invoice.paid', async ({ response }: { response: TInvoiceExpanded }) => {
404
+ subscription.on('invoice.paid', ({ response }: { response: TInvoiceExpanded }) => {
370
405
  if (response.customer_id === customer_id) {
371
- Toast.success(t('payment.customer.invoice.paySuccess'));
372
- await reloadAsync();
406
+ debouncedHandleInvoicePaid();
373
407
  }
374
408
  });
375
409
  }
package/src/libs/util.ts CHANGED
@@ -159,6 +159,9 @@ export const formatPrice = (
159
159
  bn: boolean = true,
160
160
  locale: string = 'en'
161
161
  ) => {
162
+ if (!currency) {
163
+ return '';
164
+ }
162
165
  if (price.custom_unit_amount) {
163
166
  return `Custom (${currency.symbol})`;
164
167
  }
@@ -190,6 +193,9 @@ export const formatPriceAmount = (
190
193
  quantity: number = 1,
191
194
  bn: boolean = true
192
195
  ) => {
196
+ if (!currency) {
197
+ return '';
198
+ }
193
199
  const unit = getPriceUintAmountByCurrency(price, currency);
194
200
  const amount = bn
195
201
  ? fromUnitToToken(new BN(unit).mul(new BN(quantity)), currency.decimal).toString()
@@ -259,7 +265,7 @@ export function formatRecurring(
259
265
 
260
266
  export function getPriceUintAmountByCurrency(price: TPrice, currency: TPaymentCurrency) {
261
267
  const options = getPriceCurrencyOptions(price);
262
- const option = options.find((x) => x.currency_id === currency.id);
268
+ const option = options.find((x) => x.currency_id === currency?.id);
263
269
  if (option) {
264
270
  if (option.custom_unit_amount) {
265
271
  return option.custom_unit_amount.preset || option.custom_unit_amount.presets[0];
@@ -267,14 +273,14 @@ export function getPriceUintAmountByCurrency(price: TPrice, currency: TPaymentCu
267
273
  return option.unit_amount;
268
274
  }
269
275
 
270
- if (price.currency_id === currency.id) {
276
+ if (price.currency_id === currency?.id) {
271
277
  if (price.custom_unit_amount) {
272
278
  return price.custom_unit_amount.preset || price.custom_unit_amount.presets[0];
273
279
  }
274
280
  return price.unit_amount;
275
281
  }
276
282
 
277
- console.warn(`Currency ${currency.id} not configured for price`, price);
283
+ console.warn(`Currency ${currency?.id} not configured for price`, price);
278
284
  return '0';
279
285
  }
280
286
 
@@ -299,6 +305,9 @@ export function formatLineItemPricing(
299
305
  { trialEnd, trialInDays }: { trialEnd: number; trialInDays: number },
300
306
  locale: string = 'en'
301
307
  ): { primary: string; secondary?: string; quantity: string } {
308
+ if (!currency) {
309
+ return { primary: '', secondary: '', quantity: '' };
310
+ }
302
311
  const price = item.upsell_price || item.price;
303
312
 
304
313
  let quantity = t('common.qty', locale, { count: item.quantity });
@@ -1121,9 +1130,11 @@ export function getInvoiceDescriptionAndReason(invoice: TInvoiceExpanded, locale
1121
1130
  stake: t('payment.invoice.reason.stake', locale),
1122
1131
  return_stake: t('payment.invoice.reason.returnStake', locale),
1123
1132
  recharge: t('payment.invoice.reason.recharge', locale),
1133
+ stake_overdraft_protection: t('payment.invoice.reason.stake', locale),
1134
+ overdraft_protection: t('payment.invoice.reason.fee', locale),
1124
1135
  };
1125
1136
  let invoiceType = t('payment.invoice.reason.payment', locale);
1126
- if (reason.includes('stake') || reason.includes('recharge')) {
1137
+ if (reason.includes('stake') || reason.includes('recharge') || reason === 'overdraft_protection') {
1127
1138
  invoiceType = reasonMap[reason as keyof typeof reasonMap];
1128
1139
  }
1129
1140
  if (description?.startsWith('Subscription ') || description?.startsWith('Slash stake')) {
@@ -1140,6 +1151,11 @@ export function getInvoiceDescriptionAndReason(invoice: TInvoiceExpanded, locale
1140
1151
  'Return Subscription staking': t('payment.invoice.reason.returnStake', locale),
1141
1152
  'Recharge for subscription': t('payment.invoice.reason.rechargeForSubscription', locale),
1142
1153
  'Add funds for subscription': t('payment.invoice.reason.rechargeForSubscription', locale),
1154
+ 'Overdraft protection': t('payment.invoice.reason.overdraftProtection', locale),
1155
+ 'Stake for subscription overdraft protection': t(
1156
+ 'payment.invoice.reason.stakeForSubscriptionOverdraftProtection',
1157
+ locale
1158
+ ),
1143
1159
  };
1144
1160
 
1145
1161
  return {
@@ -329,6 +329,10 @@ export default flat({
329
329
  stakeForChangePayment: 'Subscription payment method update',
330
330
  recharge: 'Add funds',
331
331
  rechargeForSubscription: 'Add funds for subscription',
332
+ overdraftProtection: 'Overdraft protection Fee',
333
+ stakeForSubscriptionOverdraftProtection: 'Subscription overdraft protection',
334
+ gas: 'Gas',
335
+ fee: 'Fee',
332
336
  },
333
337
  },
334
338
  subscription: {
@@ -319,6 +319,10 @@ export default flat({
319
319
  stakeForChangePayment: '订阅支付方式更新',
320
320
  recharge: '充值',
321
321
  rechargeForSubscription: '订阅充值',
322
+ overdraftProtection: '透支保护费',
323
+ stakeForSubscriptionOverdraftProtection: '订阅透支保护',
324
+ gas: '手续费',
325
+ fee: '服务费',
322
326
  },
323
327
  },
324
328
  subscription: {
@@ -523,7 +523,12 @@ export default function PaymentForm({
523
523
  </Fade>
524
524
  {state.customerLimited && (
525
525
  <ConfirmDialog
526
- onConfirm={() => window.open(joinURL(getPrefix(), '/customer/invoice/past-due'), '_blank')}
526
+ onConfirm={() =>
527
+ window.open(
528
+ joinURL(getPrefix(), `/customer/invoice/past-due?referer=${encodeURIComponent(window.location.href)}`),
529
+ '_self'
530
+ )
531
+ }
527
532
  onCancel={() => setState({ customerLimited: false })}
528
533
  confirm={t('payment.customer.pastDue.alert.confirm')}
529
534
  title={t('payment.customer.pastDue.alert.title')}