@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.
- package/es/contexts/payment.js +9 -1
- package/es/history/invoice/list.js +39 -9
- package/es/libs/util.js +22 -6
- package/es/locales/en.js +5 -1
- package/es/locales/zh.js +5 -1
- package/es/payment/form/index.js +4 -1
- package/lib/contexts/payment.js +4 -1
- package/lib/history/invoice/list.js +30 -8
- package/lib/libs/util.js +23 -6
- package/lib/locales/en.js +5 -1
- package/lib/locales/zh.js +5 -1
- package/lib/payment/form/index.js +1 -1
- package/package.json +8 -8
- package/src/contexts/payment.tsx +9 -1
- package/src/history/invoice/list.tsx +43 -9
- package/src/libs/util.ts +20 -4
- package/src/locales/en.tsx +4 -0
- package/src/locales/zh.tsx +4 -0
- package/src/payment/form/index.tsx +6 -1
package/es/contexts/payment.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
-
|
|
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",
|
|
327
|
+
subscription.on("invoice.paid", ({ response }) => {
|
|
297
328
|
if (response.customer_id === customer_id) {
|
|
298
|
-
|
|
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
|
|
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
|
|
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
|
|
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: {
|
package/es/payment/form/index.js
CHANGED
|
@@ -452,7 +452,10 @@ export default function PaymentForm({
|
|
|
452
452
|
state.customerLimited && /* @__PURE__ */ jsx(
|
|
453
453
|
ConfirmDialog,
|
|
454
454
|
{
|
|
455
|
-
onConfirm: () => window.open(
|
|
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"),
|
package/lib/contexts/payment.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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",
|
|
373
|
+
subscription.on("invoice.paid", ({
|
|
351
374
|
response
|
|
352
375
|
}) => {
|
|
353
376
|
if (response.customer_id === customer_id) {
|
|
354
|
-
|
|
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
|
|
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
|
|
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
|
|
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)(),
|
|
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.
|
|
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.
|
|
57
|
-
"@arcblock/ux": "^2.
|
|
58
|
-
"@arcblock/ws": "^1.18.
|
|
59
|
-
"@blocklet/ui-react": "^2.
|
|
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.
|
|
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.
|
|
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": "
|
|
126
|
+
"gitHead": "a033985b46d5c80b56f2ed9dc86d808fb60d9db1"
|
|
127
127
|
}
|
package/src/contexts/payment.tsx
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
-
|
|
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',
|
|
404
|
+
subscription.on('invoice.paid', ({ response }: { response: TInvoiceExpanded }) => {
|
|
370
405
|
if (response.customer_id === customer_id) {
|
|
371
|
-
|
|
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
|
|
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
|
|
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
|
|
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 {
|
package/src/locales/en.tsx
CHANGED
|
@@ -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: {
|
package/src/locales/zh.tsx
CHANGED
|
@@ -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={() =>
|
|
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')}
|