@blocklet/payment-react 1.26.2 → 1.26.3

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 (45) hide show
  1. package/es/checkout-v2/components/dialogs/checkout-dialogs.js +2 -0
  2. package/es/checkout-v2/components/left/promotion-input.d.ts +2 -1
  3. package/es/checkout-v2/components/left/promotion-input.js +4 -11
  4. package/es/checkout-v2/components/right/customer-info-card.d.ts +2 -0
  5. package/es/checkout-v2/components/right/customer-info-card.js +22 -14
  6. package/es/checkout-v2/components/right/status-feedback.js +1 -1
  7. package/es/checkout-v2/layouts/checkout-layout.js +13 -3
  8. package/es/checkout-v2/panels/right/payment-panel.js +3 -1
  9. package/es/checkout-v2/views/error-view.d.ts +1 -1
  10. package/es/checkout-v2/views/error-view.js +7 -0
  11. package/es/components/service-suspended-dialog.d.ts +4 -0
  12. package/es/components/service-suspended-dialog.js +61 -0
  13. package/es/locales/en.js +4 -0
  14. package/es/locales/zh.js +4 -0
  15. package/es/payment/form/index.js +17 -0
  16. package/es/payment/index.js +15 -4
  17. package/lib/checkout-v2/components/dialogs/checkout-dialogs.js +4 -0
  18. package/lib/checkout-v2/components/left/promotion-input.d.ts +2 -1
  19. package/lib/checkout-v2/components/left/promotion-input.js +5 -17
  20. package/lib/checkout-v2/components/right/customer-info-card.d.ts +2 -0
  21. package/lib/checkout-v2/components/right/customer-info-card.js +19 -13
  22. package/lib/checkout-v2/components/right/status-feedback.js +1 -1
  23. package/lib/checkout-v2/layouts/checkout-layout.js +28 -5
  24. package/lib/checkout-v2/panels/right/payment-panel.js +3 -1
  25. package/lib/checkout-v2/views/error-view.d.ts +1 -1
  26. package/lib/checkout-v2/views/error-view.js +7 -0
  27. package/lib/components/service-suspended-dialog.d.ts +4 -0
  28. package/lib/components/service-suspended-dialog.js +97 -0
  29. package/lib/locales/en.js +4 -0
  30. package/lib/locales/zh.js +4 -0
  31. package/lib/payment/form/index.js +23 -0
  32. package/lib/payment/index.js +15 -4
  33. package/package.json +4 -4
  34. package/src/checkout-v2/components/dialogs/checkout-dialogs.tsx +4 -0
  35. package/src/checkout-v2/components/left/promotion-input.tsx +6 -14
  36. package/src/checkout-v2/components/right/customer-info-card.tsx +29 -16
  37. package/src/checkout-v2/components/right/status-feedback.tsx +2 -2
  38. package/src/checkout-v2/layouts/checkout-layout.tsx +25 -10
  39. package/src/checkout-v2/panels/right/payment-panel.tsx +2 -0
  40. package/src/checkout-v2/views/error-view.tsx +9 -1
  41. package/src/components/service-suspended-dialog.tsx +64 -0
  42. package/src/locales/en.tsx +4 -0
  43. package/src/locales/zh.tsx +4 -0
  44. package/src/payment/form/index.tsx +20 -0
  45. package/src/payment/index.tsx +26 -4
@@ -0,0 +1,64 @@
1
+ import { Box, Button, Dialog, DialogContent, Stack, Typography } from '@mui/material';
2
+ import { alpha } from '@mui/material/styles';
3
+ import PauseCircleOutlineIcon from '@mui/icons-material/PauseCircleOutline';
4
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
5
+
6
+ export default function ServiceSuspendedDialog({ open, onClose }: { open: boolean; onClose: () => void }) {
7
+ const { t } = useLocaleContext();
8
+
9
+ return (
10
+ <Dialog
11
+ open={open}
12
+ onClose={onClose}
13
+ PaperProps={{
14
+ sx: {
15
+ borderRadius: 3,
16
+ maxWidth: 400,
17
+ mx: 'auto',
18
+ overflow: 'hidden',
19
+ },
20
+ }}>
21
+ <DialogContent sx={{ p: 0 }}>
22
+ <Stack alignItems="center" sx={{ pt: 4, pb: 3, px: 4, textAlign: 'center' }}>
23
+ <Box
24
+ sx={{
25
+ width: 64,
26
+ height: 64,
27
+ borderRadius: '50%',
28
+ display: 'flex',
29
+ alignItems: 'center',
30
+ justifyContent: 'center',
31
+ bgcolor: (theme) => alpha(theme.palette.warning.main, 0.1),
32
+ mb: 2.5,
33
+ }}>
34
+ <PauseCircleOutlineIcon sx={{ fontSize: 36, color: 'warning.main' }} />
35
+ </Box>
36
+
37
+ <Typography sx={{ fontWeight: 700, fontSize: 18, mb: 1, color: 'text.primary' }}>
38
+ {t('payment.checkout.stopAcceptingOrders.title')}
39
+ </Typography>
40
+
41
+ <Typography sx={{ color: 'text.secondary', fontSize: 14, lineHeight: 1.6 }}>
42
+ {t('payment.checkout.stopAcceptingOrders.description')}
43
+ </Typography>
44
+ </Stack>
45
+
46
+ <Box sx={{ px: 4, pb: 3 }}>
47
+ <Button
48
+ fullWidth
49
+ variant="contained"
50
+ disableElevation
51
+ onClick={onClose}
52
+ sx={{
53
+ borderRadius: 2,
54
+ textTransform: 'none',
55
+ fontWeight: 600,
56
+ py: 1,
57
+ }}>
58
+ {t('common.know')}
59
+ </Button>
60
+ </Box>
61
+ </DialogContent>
62
+ </Dialog>
63
+ );
64
+ }
@@ -396,6 +396,10 @@ export default flat({
396
396
  title: 'Nothing to show here',
397
397
  description: 'It seems this checkout session is not configured properly',
398
398
  },
399
+ stopAcceptingOrders: {
400
+ title: 'Service Suspended',
401
+ description: 'New order placement is temporarily unavailable due to a system-level service suspension.',
402
+ },
399
403
  error: {
400
404
  title: 'Something went wrong',
401
405
  },
@@ -416,6 +416,10 @@ export default flat({
416
416
  title: '没有任何购买项目',
417
417
  description: '可能这个付款链接没有正确配置',
418
418
  },
419
+ stopAcceptingOrders: {
420
+ title: '暂停服务',
421
+ description: '因系统策略调整,当前已暂停新订单服务。',
422
+ },
419
423
  error: {
420
424
  title: '出了点问题',
421
425
  },
@@ -61,6 +61,7 @@ import LoadingButton from '../../components/loading-button';
61
61
  import OverdueInvoicePayment from '../../components/over-due-invoice-payment';
62
62
  import { saveCurrencyPreference } from '../../libs/currency';
63
63
  import ConfirmDialog from '../../components/confirm';
64
+ import ServiceSuspendedDialog from '../../components/service-suspended-dialog';
64
65
  import PriceChangeConfirm from '../../components/price-change-confirm';
65
66
  import { getFieldValidation, validatePostalCode } from '../../libs/validator';
66
67
 
@@ -267,6 +268,7 @@ export default function PaymentForm({
267
268
  };
268
269
  customer?: TCustomer;
269
270
  customerLimited?: boolean;
271
+ serviceSuspended?: boolean;
270
272
  stripePaying: boolean;
271
273
  fastCheckoutInfo: FastCheckoutInfo | null;
272
274
  creditInsufficientInfo: {
@@ -289,6 +291,7 @@ export default function PaymentForm({
289
291
  stripeContext: undefined,
290
292
  customer,
291
293
  customerLimited: false,
294
+ serviceSuspended: false,
292
295
  stripePaying: false,
293
296
  fastCheckoutInfo: null,
294
297
  creditInsufficientInfo: null,
@@ -1198,6 +1201,11 @@ export default function PaymentForm({
1198
1201
  shouldToast = false;
1199
1202
  setState({ customerLimited: true });
1200
1203
  }
1204
+
1205
+ if (errorCode === 'STOP_ACCEPTING_ORDERS') {
1206
+ shouldToast = false;
1207
+ setState({ serviceSuspended: true });
1208
+ }
1201
1209
  }
1202
1210
  if (shouldToast) {
1203
1211
  Toast.error(formatError(err));
@@ -1474,6 +1482,9 @@ export default function PaymentForm({
1474
1482
  }}
1475
1483
  />
1476
1484
  )}
1485
+ {state.serviceSuspended && (
1486
+ <ServiceSuspendedDialog open onClose={() => setState({ serviceSuspended: false })} />
1487
+ )}
1477
1488
  {FastCheckoutConfirmDialog}
1478
1489
  {CreditInsufficientDialog}
1479
1490
  {PriceUpdatedDialog}
@@ -1748,6 +1759,15 @@ export default function PaymentForm({
1748
1759
  }}
1749
1760
  />
1750
1761
  )}
1762
+ {state.serviceSuspended && (
1763
+ <ConfirmDialog
1764
+ onConfirm={() => setState({ serviceSuspended: false })}
1765
+ onCancel={() => setState({ serviceSuspended: false })}
1766
+ title={t('payment.checkout.stopAcceptingOrders.title')}
1767
+ message={t('payment.checkout.stopAcceptingOrders.description')}
1768
+ confirm={t('common.confirm')}
1769
+ />
1770
+ )}
1751
1771
  {FastCheckoutConfirmDialog}
1752
1772
  {CreditInsufficientDialog}
1753
1773
  {PriceUpdatedDialog}
@@ -125,6 +125,19 @@ function PaymentInner({
125
125
  }, [paymentMethods]);
126
126
 
127
127
  const defaultCurrencyId = useMemo(() => {
128
+ // Keep session currency stable when a promotion is already applied.
129
+ // Otherwise auto-picking from URL/local preference/no-wallet may switch currency
130
+ // on refresh and trigger a second recalculate-promotion that removes discount
131
+ // with `currency_incompatible`.
132
+ const hasAppliedDiscount = Boolean((state.checkoutSession as any)?.discounts?.length);
133
+ if (
134
+ hasAppliedDiscount &&
135
+ state.checkoutSession.currency_id &&
136
+ availableCurrencyIds.includes(state.checkoutSession.currency_id)
137
+ ) {
138
+ return state.checkoutSession.currency_id;
139
+ }
140
+
128
141
  // 1. first check url currencyId
129
142
  if (query.currencyId && availableCurrencyIds.includes(query.currencyId)) {
130
143
  return query.currencyId;
@@ -151,7 +164,7 @@ function PaymentInner({
151
164
  return state.checkoutSession.currency_id;
152
165
  }
153
166
  return availableCurrencyIds?.[0];
154
- }, [query.currencyId, availableCurrencyIds, session?.user, state.checkoutSession.currency_id, paymentMethods]);
167
+ }, [query.currencyId, availableCurrencyIds, session?.user, state.checkoutSession, paymentMethods]);
155
168
 
156
169
  const defaultMethodId = paymentMethods.find((m) => m.payment_currencies.some((c) => c.id === defaultCurrencyId))?.id;
157
170
  const hideSummaryCard = mode.endsWith('-minimal') || !showCheckoutSummary;
@@ -183,14 +196,23 @@ function PaymentInner({
183
196
  });
184
197
 
185
198
  useEffect(() => {
199
+ const hasAppliedDiscount = Boolean((state.checkoutSession as any)?.discounts?.length);
200
+ const currentCurrency = methods.getValues('payment_currency');
201
+ const currentMethod = methods.getValues('payment_method');
202
+
186
203
  if (defaultCurrencyId) {
187
- methods.setValue('payment_currency', defaultCurrencyId);
204
+ // Avoid overriding current currency on refresh when a promotion is already applied.
205
+ if (!hasAppliedDiscount || !currentCurrency) {
206
+ methods.setValue('payment_currency', defaultCurrencyId);
207
+ }
188
208
  }
189
209
  if (defaultMethodId) {
190
- methods.setValue('payment_method', defaultMethodId);
210
+ if (!hasAppliedDiscount || !currentMethod) {
211
+ methods.setValue('payment_method', defaultMethodId);
212
+ }
191
213
  }
192
214
  // eslint-disable-next-line react-hooks/exhaustive-deps
193
- }, [defaultCurrencyId, defaultMethodId]);
215
+ }, [defaultCurrencyId, defaultMethodId, state.checkoutSession.discounts]);
194
216
 
195
217
  useEffect(() => {
196
218
  if (!isMobileSafari()) {