@blocklet/payment-react 1.18.15 → 1.18.17

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.
@@ -4,19 +4,27 @@ type DialogProps = {
4
4
  onClose?: () => void;
5
5
  title?: string;
6
6
  };
7
+ type DetailLinkOptions = {
8
+ enabled?: boolean;
9
+ onClick?: (e: React.MouseEvent) => void;
10
+ title?: string;
11
+ };
7
12
  type Props = {
8
- subscriptionId: string;
13
+ subscriptionId?: string;
14
+ customerId?: string;
9
15
  mode?: 'default' | 'custom';
10
- onPaid?: (subscriptionId: string, currencyId: string) => void;
16
+ onPaid?: (id: string, currencyId: string, type: 'subscription' | 'customer') => void;
11
17
  dialogProps?: DialogProps;
12
- inSubscriptionDetail?: boolean;
18
+ detailLinkOptions?: DetailLinkOptions;
19
+ successToast?: boolean;
13
20
  children?: (handlePay: (item: SummaryItem) => void, data: {
14
- subscription: Subscription;
21
+ subscription?: Subscription;
15
22
  summary: {
16
23
  [key: string]: SummaryItem;
17
24
  };
18
25
  invoices: Invoice[];
19
- subscriptionUrl: string;
26
+ subscriptionCount?: number;
27
+ detailUrl: string;
20
28
  }) => React.ReactNode;
21
29
  };
22
30
  type SummaryItem = {
@@ -24,7 +32,7 @@ type SummaryItem = {
24
32
  currency: PaymentCurrency;
25
33
  method: PaymentMethod;
26
34
  };
27
- declare function OverdueInvoicePayment({ subscriptionId, mode, dialogProps, children, onPaid, inSubscriptionDetail, }: Props): import("react").JSX.Element | null;
35
+ declare function OverdueInvoicePayment({ subscriptionId, customerId, mode, dialogProps, children, onPaid, detailLinkOptions, successToast, }: Props): import("react").JSX.Element | null;
28
36
  declare namespace OverdueInvoicePayment {
29
37
  var defaultProps: {
30
38
  mode: string;
@@ -33,7 +41,12 @@ declare namespace OverdueInvoicePayment {
33
41
  open: boolean;
34
42
  };
35
43
  children: null;
36
- inSubscriptionDetail: boolean;
44
+ detailLinkOptions: {
45
+ enabled: boolean;
46
+ };
47
+ subscriptionId: undefined;
48
+ customerId: undefined;
49
+ successToast: boolean;
37
50
  };
38
51
  }
39
52
  export default OverdueInvoicePayment;
@@ -12,22 +12,38 @@ var _Toast = _interopRequireDefault(require("@arcblock/ux/lib/Toast"));
12
12
  var _ufo = require("ufo");
13
13
  var _ahooks = require("ahooks");
14
14
  var _ux = require("@arcblock/ux");
15
+ var _iconsMaterial = require("@mui/icons-material");
16
+ var _debounce = _interopRequireDefault(require("lodash/debounce"));
15
17
  var _payment = require("../contexts/payment");
16
18
  var _util = require("../libs/util");
17
19
  var _subscription = require("../hooks/subscription");
18
20
  var _api = _interopRequireDefault(require("../libs/api"));
21
+ var _loadingButton = _interopRequireDefault(require("./loading-button"));
19
22
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
20
- const fetchOverdueInvoices = async subscriptionId => {
21
- const res = await _api.default.get(`/api/subscriptions/${subscriptionId}/overdue/invoices`);
23
+ const fetchOverdueInvoices = async params => {
24
+ if (!params.subscriptionId && !params.customerId) {
25
+ throw new Error("Either subscriptionId or customerId must be provided");
26
+ }
27
+ let url;
28
+ if (params.subscriptionId) {
29
+ url = `/api/subscriptions/${params.subscriptionId}/overdue/invoices`;
30
+ } else {
31
+ url = `/api/customers/${params.customerId}/overdue/invoices`;
32
+ }
33
+ const res = await _api.default.get(url);
22
34
  return res.data;
23
35
  };
24
36
  function OverdueInvoicePayment({
25
37
  subscriptionId,
38
+ customerId,
26
39
  mode = "default",
27
40
  dialogProps = {},
28
41
  children,
29
42
  onPaid = () => {},
30
- inSubscriptionDetail = false
43
+ detailLinkOptions = {
44
+ enabled: true
45
+ },
46
+ successToast = true
31
47
  }) {
32
48
  const {
33
49
  t
@@ -39,47 +55,74 @@ function OverdueInvoicePayment({
39
55
  const [payLoading, setPayLoading] = (0, _react.useState)(false);
40
56
  const [dialogOpen, setDialogOpen] = (0, _react.useState)(dialogProps.open || false);
41
57
  const [processedCurrencies, setProcessedCurrencies] = (0, _react.useState)({});
58
+ const [paymentStatus, setPaymentStatus] = (0, _react.useState)({});
59
+ const sourceType = subscriptionId ? "subscription" : "customer";
60
+ const sourceId = subscriptionId || customerId;
42
61
  const {
43
62
  data = {
44
- subscription: {},
45
63
  summary: {},
46
64
  invoices: []
47
65
  },
48
66
  error,
49
67
  loading,
50
68
  runAsync: refresh
51
- } = (0, _ahooks.useRequest)(() => fetchOverdueInvoices(subscriptionId));
52
- const subscriptionUrl = (0, _ufo.joinURL)((0, _util.getPrefix)(), `/customer/subscription/${subscriptionId}`);
69
+ } = (0, _ahooks.useRequest)(() => fetchOverdueInvoices({
70
+ subscriptionId,
71
+ customerId
72
+ }), {
73
+ ready: !!subscriptionId || !!customerId
74
+ });
75
+ const detailUrl = (0, _react.useMemo)(() => {
76
+ if (subscriptionId) {
77
+ return (0, _ufo.joinURL)((0, _util.getPrefix)(), `/customer/subscription/${subscriptionId}`);
78
+ }
79
+ if (customerId) {
80
+ return (0, _ufo.joinURL)((0, _util.getPrefix)(), "/customer/invoice/past-due");
81
+ }
82
+ return "";
83
+ }, [subscriptionId, customerId]);
53
84
  const summaryList = (0, _react.useMemo)(() => {
54
85
  if (!data?.summary) {
55
86
  return [];
56
87
  }
57
88
  return Object.values(data.summary);
58
89
  }, [data?.summary]);
90
+ const debouncedHandleInvoicePaid = (0, _debounce.default)(async currencyId => {
91
+ if (successToast) {
92
+ _Toast.default.close();
93
+ _Toast.default.success(t("payment.customer.invoice.paySuccess"));
94
+ }
95
+ setPayLoading(false);
96
+ const res = await refresh();
97
+ if (res.invoices?.length === 0) {
98
+ setDialogOpen(false);
99
+ onPaid(sourceId, currencyId, sourceType);
100
+ }
101
+ }, 1e3, {
102
+ leading: false,
103
+ trailing: true,
104
+ maxWait: 5e3
105
+ });
59
106
  const subscription = (0, _subscription.useSubscription)("events");
60
107
  (0, _react.useEffect)(() => {
61
108
  if (subscription) {
62
109
  subscription.on("invoice.paid", ({
63
110
  response
64
111
  }) => {
65
- const uniqueKey = `${response.subscription_id}-${response.currency_id}`;
66
- if (response.subscription_id === subscriptionId && !processedCurrencies[uniqueKey]) {
67
- _Toast.default.success(t("payment.customer.invoice.paySuccess"));
68
- setPayLoading(false);
69
- setProcessedCurrencies({
70
- ...processedCurrencies,
71
- [uniqueKey]: 1
72
- });
73
- refresh().then(res => {
74
- if (res.invoices?.length === 0) {
75
- setDialogOpen(false);
76
- onPaid(subscriptionId, response.currency_id);
77
- }
78
- });
112
+ const relevantId = subscriptionId || response.customer_id;
113
+ const uniqueKey = `${relevantId}-${response.currency_id}`;
114
+ if (subscriptionId && response.subscription_id === subscriptionId || customerId && response.customer_id === customerId) {
115
+ if (!processedCurrencies[uniqueKey]) {
116
+ setProcessedCurrencies(prev => ({
117
+ ...prev,
118
+ [uniqueKey]: 1
119
+ }));
120
+ debouncedHandleInvoicePaid(response.currency_id);
121
+ }
79
122
  }
80
123
  });
81
124
  }
82
- }, [subscription]);
125
+ }, [subscription, subscriptionId, customerId]);
83
126
  const handlePay = item => {
84
127
  const {
85
128
  currency,
@@ -94,18 +137,32 @@ function OverdueInvoicePayment({
94
137
  }
95
138
  setSelectCurrencyId(currency.id);
96
139
  setPayLoading(true);
140
+ setPaymentStatus(prev => ({
141
+ ...prev,
142
+ [currency.id]: "idle"
143
+ }));
97
144
  if (["arcblock", "ethereum", "base"].includes(method.type)) {
145
+ const extraParams = {
146
+ currencyId: currency.id
147
+ };
148
+ if (subscriptionId) {
149
+ extraParams.subscriptionId = subscriptionId;
150
+ } else if (customerId) {
151
+ extraParams.customerId = customerId;
152
+ }
98
153
  connect.open({
99
154
  containerEl: void 0,
100
155
  saveConnect: false,
101
156
  action: "collect-batch",
102
157
  prefix: (0, _ufo.joinURL)((0, _util.getPrefix)(), "/api/did"),
103
- extraParams: {
104
- currencyId: currency.id,
105
- subscriptionId
106
- },
158
+ extraParams,
107
159
  onSuccess: () => {
108
160
  connect.close();
161
+ setPayLoading(false);
162
+ setPaymentStatus(prev => ({
163
+ ...prev,
164
+ [currency.id]: "success"
165
+ }));
109
166
  },
110
167
  onClose: () => {
111
168
  connect.close();
@@ -113,6 +170,10 @@ function OverdueInvoicePayment({
113
170
  },
114
171
  onError: err => {
115
172
  _Toast.default.error((0, _util.formatError)(err));
173
+ setPaymentStatus(prev => ({
174
+ ...prev,
175
+ [currency.id]: "error"
176
+ }));
116
177
  setPayLoading(false);
117
178
  }
118
179
  });
@@ -123,7 +184,10 @@ function OverdueInvoicePayment({
123
184
  dialogProps.onClose?.();
124
185
  };
125
186
  const handleViewDetailClick = e => {
126
- if (inSubscriptionDetail) {
187
+ if (detailLinkOptions.onClick) {
188
+ e.preventDefault();
189
+ detailLinkOptions.onClick(e);
190
+ } else if (!detailLinkOptions.enabled) {
127
191
  e.preventDefault();
128
192
  handleClose();
129
193
  }
@@ -131,39 +195,120 @@ function OverdueInvoicePayment({
131
195
  if (loading) {
132
196
  return null;
133
197
  }
134
- const renderPayButton = (item, props) => {
135
- const isPayLoading = payLoading && item.currency.id === selectCurrencyId;
198
+ const getDetailLinkText = () => {
199
+ if (detailLinkOptions.title) {
200
+ return detailLinkOptions.title;
201
+ }
202
+ if (subscriptionId) {
203
+ return t("payment.subscription.overdue.view");
204
+ }
205
+ return t("payment.customer.pastDue.view");
206
+ };
207
+ const renderPayButton = (item, primaryButton = true, props = {
208
+ variant: "contained"
209
+ }) => {
210
+ const {
211
+ currency
212
+ } = item;
213
+ const inProcess = payLoading && selectCurrencyId === currency.id;
214
+ const status = paymentStatus[currency.id] || "idle";
215
+ if (status === "success") {
216
+ return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Button, {
217
+ variant: props?.variant || "contained",
218
+ size: "small",
219
+ ...(primaryButton ? {} : {
220
+ color: "success",
221
+ startIcon: /* @__PURE__ */(0, _jsxRuntime.jsx)(_iconsMaterial.CheckCircle, {})
222
+ }),
223
+ children: t("payment.subscription.overdue.paid")
224
+ });
225
+ }
226
+ if (status === "error") {
227
+ return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Button, {
228
+ variant: "contained",
229
+ size: "small",
230
+ onClick: () => handlePay(item),
231
+ ...props,
232
+ children: t("payment.subscription.overdue.retry")
233
+ });
234
+ }
136
235
  if (item.method.type === "stripe") {
137
236
  return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Button, {
138
237
  variant: "contained",
139
238
  color: "primary",
140
- onClick: () => window.open(subscriptionUrl, "_blank"),
239
+ onClick: () => window.open(detailUrl, "_blank"),
141
240
  ...props,
142
241
  children: t("payment.subscription.overdue.viewNow")
143
242
  });
144
243
  }
145
- return /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Button, {
244
+ return /* @__PURE__ */(0, _jsxRuntime.jsx)(_loadingButton.default, {
146
245
  variant: "contained",
147
- color: "primary",
246
+ size: "small",
247
+ disabled: inProcess,
248
+ loading: inProcess,
148
249
  onClick: () => handlePay(item),
149
250
  ...props,
150
- disabled: isPayLoading,
151
- children: [isPayLoading && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.CircularProgress, {
152
- size: 14,
153
- sx: {
154
- mr: 1,
155
- color: "text.lighter"
156
- }
157
- }), t("payment.subscription.overdue.payNow")]
251
+ children: t("payment.subscription.overdue.payNow")
158
252
  });
159
253
  };
254
+ const getMethodText = method => {
255
+ if (method.name && method.type !== "arcblock") {
256
+ return ` (${method.name})`;
257
+ }
258
+ return "";
259
+ };
260
+ const getOverdueTitle = () => {
261
+ if (subscriptionId && data.subscription) {
262
+ if (summaryList.length === 1) {
263
+ return t("payment.subscription.overdue.title", {
264
+ name: data.subscription?.description,
265
+ count: data.invoices?.length,
266
+ total: (0, _util.formatAmount)(summaryList[0]?.amount, summaryList[0]?.currency?.decimal),
267
+ symbol: summaryList[0]?.currency?.symbol,
268
+ method: getMethodText(summaryList[0]?.method)
269
+ });
270
+ }
271
+ return t("payment.subscription.overdue.simpleTitle", {
272
+ name: data.subscription?.description,
273
+ count: data.invoices?.length
274
+ });
275
+ }
276
+ if (customerId) {
277
+ if (summaryList.length === 1) {
278
+ return t("payment.customer.overdue.title", {
279
+ subscriptionCount: data.subscriptionCount || 0,
280
+ count: data.invoices?.length,
281
+ total: (0, _util.formatAmount)(summaryList[0]?.amount, summaryList[0]?.currency?.decimal),
282
+ symbol: summaryList[0]?.currency?.symbol,
283
+ method: getMethodText(summaryList[0]?.method)
284
+ });
285
+ }
286
+ return t("payment.customer.overdue.simpleTitle", {
287
+ subscriptionCount: data.subscriptionCount || 0,
288
+ count: data.invoices?.length
289
+ });
290
+ }
291
+ return "";
292
+ };
293
+ const getEmptyStateMessage = () => {
294
+ if (subscriptionId && data.subscription) {
295
+ return t("payment.subscription.overdue.empty", {
296
+ name: data.subscription?.description
297
+ });
298
+ }
299
+ if (customerId) {
300
+ return t("payment.customer.overdue.empty");
301
+ }
302
+ return "";
303
+ };
160
304
  if (mode === "custom" && children && typeof children === "function") {
161
305
  return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Stack, {
162
306
  children: children(handlePay, {
163
307
  subscription: data?.subscription,
164
- subscriptionUrl,
165
308
  summary: data?.summary,
166
- invoices: data?.invoices
309
+ invoices: data?.invoices,
310
+ subscriptionCount: data?.subscriptionCount,
311
+ detailUrl
167
312
  })
168
313
  });
169
314
  }
@@ -190,10 +335,7 @@ function OverdueInvoicePayment({
190
335
  children: [summaryList.length === 0 && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
191
336
  children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Alert, {
192
337
  severity: "success",
193
- children: t("payment.subscription.overdue.empty", {
194
- // @ts-ignore
195
- name: data?.subscription?.description
196
- })
338
+ children: getEmptyStateMessage()
197
339
  }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Stack, {
198
340
  direction: "row",
199
341
  justifyContent: "flex-end",
@@ -212,21 +354,17 @@ function OverdueInvoicePayment({
212
354
  children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Typography, {
213
355
  color: "text.secondary",
214
356
  variant: "body1",
215
- children: [t("payment.subscription.overdue.title", {
216
- // @ts-ignore
217
- name: data?.subscription?.description,
218
- count: data?.invoices?.length,
219
- total: (0, _util.formatAmount)(summaryList[0]?.amount, summaryList[0]?.currency?.decimal),
220
- symbol: summaryList[0]?.currency?.symbol
221
- }), /* @__PURE__ */(0, _jsxRuntime.jsx)("br", {}), t("payment.subscription.overdue.description"), /* @__PURE__ */(0, _jsxRuntime.jsx)("a", {
222
- href: subscriptionUrl,
223
- target: "_blank",
224
- onClick: handleViewDetailClick,
225
- rel: "noreferrer",
226
- style: {
227
- color: "var(--foregrounds-fg-interactive, 0086FF)"
228
- },
229
- children: t("payment.subscription.overdue.view")
357
+ children: [getOverdueTitle(), detailLinkOptions.enabled && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
358
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)("br", {}), t("payment.subscription.overdue.description"), /* @__PURE__ */(0, _jsxRuntime.jsx)("a", {
359
+ href: detailUrl,
360
+ target: "_blank",
361
+ onClick: handleViewDetailClick,
362
+ rel: "noreferrer",
363
+ style: {
364
+ color: "var(--foregrounds-fg-interactive, 0086FF)"
365
+ },
366
+ children: getDetailLinkText()
367
+ })]
230
368
  })]
231
369
  }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
232
370
  direction: "row",
@@ -244,19 +382,17 @@ function OverdueInvoicePayment({
244
382
  children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Typography, {
245
383
  color: "text.secondary",
246
384
  variant: "body1",
247
- children: [t("payment.subscription.overdue.simpleTitle", {
248
- // @ts-ignore
249
- name: data?.subscription?.description,
250
- count: data?.invoices?.length
251
- }), /* @__PURE__ */(0, _jsxRuntime.jsx)("br", {}), t("payment.subscription.overdue.description"), /* @__PURE__ */(0, _jsxRuntime.jsx)("a", {
252
- href: subscriptionUrl,
253
- target: "_blank",
254
- rel: "noreferrer",
255
- onClick: handleViewDetailClick,
256
- style: {
257
- color: "var(--foregrounds-fg-interactive, 0086FF)"
258
- },
259
- children: t("payment.subscription.overdue.view")
385
+ children: [getOverdueTitle(), detailLinkOptions.enabled && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
386
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)("br", {}), t("payment.subscription.overdue.description"), /* @__PURE__ */(0, _jsxRuntime.jsx)("a", {
387
+ href: detailUrl,
388
+ target: "_blank",
389
+ rel: "noreferrer",
390
+ onClick: handleViewDetailClick,
391
+ style: {
392
+ color: "var(--foregrounds-fg-interactive, 0086FF)"
393
+ },
394
+ children: getDetailLinkText()
395
+ })]
260
396
  })]
261
397
  }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
262
398
  color: "text.secondary",
@@ -279,9 +415,10 @@ function OverdueInvoicePayment({
279
415
  children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
280
416
  children: t("payment.subscription.overdue.total", {
281
417
  total: (0, _util.formatAmount)(item?.amount, item?.currency?.decimal),
282
- currency: item?.currency?.symbol
418
+ currency: item?.currency?.symbol,
419
+ method: getMethodText(item?.method)
283
420
  })
284
- }), renderPayButton(item, {
421
+ }), renderPayButton(item, false, {
285
422
  variant: "text",
286
423
  sx: {
287
424
  color: "text.link"
@@ -300,6 +437,11 @@ OverdueInvoicePayment.defaultProps = {
300
437
  open: true
301
438
  },
302
439
  children: null,
303
- inSubscriptionDetail: false
440
+ detailLinkOptions: {
441
+ enabled: true
442
+ },
443
+ subscriptionId: void 0,
444
+ customerId: void 0,
445
+ successToast: true
304
446
  };
305
447
  module.exports = OverdueInvoicePayment;
@@ -15,6 +15,19 @@ var _api = _interopRequireDefault(require("../libs/api"));
15
15
  var _util = require("../libs/util");
16
16
  var _cachedRequest = require("../libs/cached-request");
17
17
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
18
+ const formatData = data => {
19
+ if (!data) {
20
+ return {
21
+ paymentMethods: [],
22
+ baseCurrency: {}
23
+ };
24
+ }
25
+ return {
26
+ ...data,
27
+ paymentMethods: data.paymentMethods || [],
28
+ baseCurrency: data.baseCurrency || {}
29
+ };
30
+ };
18
31
  const PaymentContext = exports.PaymentContext = (0, _react.createContext)({
19
32
  api: _api.default
20
33
  });
@@ -80,7 +93,7 @@ function PaymentProvider({
80
93
  connect,
81
94
  prefix,
82
95
  livemode: !!livemode,
83
- settings: data,
96
+ settings: formatData(data),
84
97
  getCurrency: currencyId => getCurrency(currencyId, data?.paymentMethods || []),
85
98
  getMethod: methodId => getMethod(methodId, data?.paymentMethods || []),
86
99
  refresh: run,
@@ -11,6 +11,7 @@ type Props = {
11
11
  action?: string;
12
12
  type?: 'list' | 'table';
13
13
  onTableDataChange?: Function;
14
+ relatedSubscription?: boolean;
14
15
  };
15
16
  declare function CustomerInvoiceList(props: Props): JSX.Element;
16
17
  declare namespace CustomerInvoiceList {
@@ -26,6 +27,7 @@ declare namespace CustomerInvoiceList {
26
27
  action: string;
27
28
  type: string;
28
29
  onTableDataChange: () => void;
30
+ relatedSubscription: boolean;
29
31
  };
30
32
  }
31
33
  export default CustomerInvoiceList;