udp-react-enterprise-component-library 26.5.0-beta.12 → 26.5.0-beta.13

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 (100) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/utilities/form/paymentForm/BankAccountForm.d.ts +12 -29
  3. package/dist/utilities/form/paymentForm/BankAccountForm.d.ts.map +1 -1
  4. package/dist/utilities/form/paymentForm/BankAccountForm.js +64 -78
  5. package/dist/utilities/form/paymentForm/BankAccountForm.js.map +1 -1
  6. package/dist/utilities/form/paymentForm/BankAccountForm.mjs +77 -196
  7. package/dist/utilities/form/paymentForm/BankAccountForm.mjs.map +1 -1
  8. package/dist/utilities/form/paymentForm/CashForm.d.ts +1 -1
  9. package/dist/utilities/form/paymentForm/CashForm.d.ts.map +1 -1
  10. package/dist/utilities/form/paymentForm/CashForm.js +19 -51
  11. package/dist/utilities/form/paymentForm/CashForm.js.map +1 -1
  12. package/dist/utilities/form/paymentForm/CashForm.mjs +80 -119
  13. package/dist/utilities/form/paymentForm/CashForm.mjs.map +1 -1
  14. package/dist/utilities/form/paymentForm/CreditCardForm.d.ts +10 -21
  15. package/dist/utilities/form/paymentForm/CreditCardForm.d.ts.map +1 -1
  16. package/dist/utilities/form/paymentForm/CreditCardForm.js +81 -87
  17. package/dist/utilities/form/paymentForm/CreditCardForm.js.map +1 -1
  18. package/dist/utilities/form/paymentForm/CreditCardForm.mjs +84 -195
  19. package/dist/utilities/form/paymentForm/CreditCardForm.mjs.map +1 -1
  20. package/dist/utilities/form/paymentForm/CreditCardUtils.d.ts.map +1 -1
  21. package/dist/utilities/form/paymentForm/CreditCardUtils.js +5 -0
  22. package/dist/utilities/form/paymentForm/CreditCardUtils.js.map +1 -1
  23. package/dist/utilities/form/paymentForm/CreditCardUtils.mjs +1 -0
  24. package/dist/utilities/form/paymentForm/CreditCardUtils.mjs.map +1 -1
  25. package/dist/utilities/form/paymentForm/HostedPaymentError.d.ts +5 -16
  26. package/dist/utilities/form/paymentForm/HostedPaymentError.d.ts.map +1 -1
  27. package/dist/utilities/form/paymentForm/HostedPaymentError.js +6 -18
  28. package/dist/utilities/form/paymentForm/HostedPaymentError.js.map +1 -1
  29. package/dist/utilities/form/paymentForm/HostedPaymentError.mjs +18 -55
  30. package/dist/utilities/form/paymentForm/HostedPaymentError.mjs.map +1 -1
  31. package/dist/utilities/form/paymentForm/NewCreditCardForm.d.ts +12 -14
  32. package/dist/utilities/form/paymentForm/NewCreditCardForm.d.ts.map +1 -1
  33. package/dist/utilities/form/paymentForm/NewCreditCardForm.js +45 -58
  34. package/dist/utilities/form/paymentForm/NewCreditCardForm.js.map +1 -1
  35. package/dist/utilities/form/paymentForm/NewCreditCardForm.mjs +185 -262
  36. package/dist/utilities/form/paymentForm/NewCreditCardForm.mjs.map +1 -1
  37. package/dist/utilities/form/paymentForm/NewECheckForm.d.ts +9 -14
  38. package/dist/utilities/form/paymentForm/NewECheckForm.d.ts.map +1 -1
  39. package/dist/utilities/form/paymentForm/NewECheckForm.js +28 -68
  40. package/dist/utilities/form/paymentForm/NewECheckForm.js.map +1 -1
  41. package/dist/utilities/form/paymentForm/NewECheckForm.mjs +184 -227
  42. package/dist/utilities/form/paymentForm/NewECheckForm.mjs.map +1 -1
  43. package/dist/utilities/form/paymentForm/PaymentBankDetails.d.ts.map +1 -1
  44. package/dist/utilities/form/paymentForm/PaymentBankDetails.js +4 -2
  45. package/dist/utilities/form/paymentForm/PaymentBankDetails.js.map +1 -1
  46. package/dist/utilities/form/paymentForm/PaymentCardDetail.d.ts.map +1 -1
  47. package/dist/utilities/form/paymentForm/PaymentCardDetail.js +4 -2
  48. package/dist/utilities/form/paymentForm/PaymentCardDetail.js.map +1 -1
  49. package/dist/utilities/form/paymentForm/PaymentForm.d.ts +67 -3
  50. package/dist/utilities/form/paymentForm/PaymentForm.d.ts.map +1 -1
  51. package/dist/utilities/form/paymentForm/PaymentForm.js +265 -246
  52. package/dist/utilities/form/paymentForm/PaymentForm.js.map +1 -1
  53. package/dist/utilities/form/paymentForm/PaymentForm.mjs +275 -296
  54. package/dist/utilities/form/paymentForm/PaymentForm.mjs.map +1 -1
  55. package/dist/utilities/form/paymentForm/usePaymentContext.d.ts +22 -0
  56. package/dist/utilities/form/paymentForm/usePaymentContext.d.ts.map +1 -0
  57. package/dist/utilities/form/paymentForm/usePaymentContext.js +117 -0
  58. package/dist/utilities/form/paymentForm/usePaymentContext.js.map +1 -0
  59. package/dist/utilities/form/paymentForm/usePaymentContext.mjs +119 -0
  60. package/dist/utilities/form/paymentForm/usePaymentContext.mjs.map +1 -0
  61. package/dist/utilities/form/paymentForm/vendorsForms/FiservForm.d.ts.map +1 -1
  62. package/dist/utilities/form/paymentForm/vendorsForms/FiservForm.js +22 -28
  63. package/dist/utilities/form/paymentForm/vendorsForms/FiservForm.js.map +1 -1
  64. package/dist/utilities/form/paymentForm/vendorsForms/FiservForm.mjs +37 -32
  65. package/dist/utilities/form/paymentForm/vendorsForms/FiservForm.mjs.map +1 -1
  66. package/dist/utilities/form/paymentForm/vendorsForms/ICheckForm.d.ts.map +1 -1
  67. package/dist/utilities/form/paymentForm/vendorsForms/ICheckForm.js +25 -27
  68. package/dist/utilities/form/paymentForm/vendorsForms/ICheckForm.js.map +1 -1
  69. package/dist/utilities/form/paymentForm/vendorsForms/ICheckForm.mjs +40 -38
  70. package/dist/utilities/form/paymentForm/vendorsForms/ICheckForm.mjs.map +1 -1
  71. package/dist/utilities/form/paymentForm/vendorsForms/PaymentusForm.d.ts.map +1 -1
  72. package/dist/utilities/form/paymentForm/vendorsForms/PaymentusForm.js +25 -32
  73. package/dist/utilities/form/paymentForm/vendorsForms/PaymentusForm.js.map +1 -1
  74. package/dist/utilities/form/paymentForm/vendorsForms/PaymentusForm.mjs +38 -43
  75. package/dist/utilities/form/paymentForm/vendorsForms/PaymentusForm.mjs.map +1 -1
  76. package/package.json +6 -42
  77. package/dist/utilities/form/paymentForm/BankIcon.mjs +0 -56
  78. package/dist/utilities/form/paymentForm/BankIcon.mjs.map +0 -1
  79. package/dist/utilities/form/paymentForm/CashIcon.mjs +0 -55
  80. package/dist/utilities/form/paymentForm/CashIcon.mjs.map +0 -1
  81. package/dist/utilities/form/paymentForm/CheckIcon.mjs +0 -62
  82. package/dist/utilities/form/paymentForm/CheckIcon.mjs.map +0 -1
  83. package/dist/utilities/form/paymentForm/CreditCardIcon.mjs +0 -85
  84. package/dist/utilities/form/paymentForm/CreditCardIcon.mjs.map +0 -1
  85. package/dist/utilities/form/paymentForm/DisableOverlay.mjs +0 -44
  86. package/dist/utilities/form/paymentForm/DisableOverlay.mjs.map +0 -1
  87. package/dist/utilities/form/paymentForm/PaymentBankDetails.mjs +0 -161
  88. package/dist/utilities/form/paymentForm/PaymentBankDetails.mjs.map +0 -1
  89. package/dist/utilities/form/paymentForm/PaymentCard.d.ts +0 -13
  90. package/dist/utilities/form/paymentForm/PaymentCard.d.ts.map +0 -1
  91. package/dist/utilities/form/paymentForm/PaymentCard.js +0 -54
  92. package/dist/utilities/form/paymentForm/PaymentCard.js.map +0 -1
  93. package/dist/utilities/form/paymentForm/PaymentCard.mjs +0 -96
  94. package/dist/utilities/form/paymentForm/PaymentCard.mjs.map +0 -1
  95. package/dist/utilities/form/paymentForm/PaymentCardDetail.mjs +0 -284
  96. package/dist/utilities/form/paymentForm/PaymentCardDetail.mjs.map +0 -1
  97. package/dist/utilities/form/paymentForm/PaymentDropdown.mjs +0 -142
  98. package/dist/utilities/form/paymentForm/PaymentDropdown.mjs.map +0 -1
  99. package/dist/utilities/form/paymentForm/TabPanel.mjs +0 -74
  100. package/dist/utilities/form/paymentForm/TabPanel.mjs.map +0 -1
@@ -1,76 +1,21 @@
1
1
  import React, { useRef, useEffect, useMemo, useState, useCallback } from 'react';
2
- import { makeStyles, useTheme, Tabs, Tab, CardContent, Card, LinearProgress, Badge, Tooltip, Grow } from '@material-ui/core';
3
- import { Box } from '../../../UI/Box';
4
- import { Typography } from '../../../UI/dataDisplay/typography/Typography';
5
- import { CircularProgress } from '../../../UI/loading/CircularProgress';
6
2
  import { Form } from '../Form';
7
3
  import { useTranslation } from 'react-i18next';
8
- import { FluentButton } from '../../../UI/inputs/buttons/FluentButton';
4
+ import { UdpFlexbox } from '../../../UI/fluent-web-components/UdpFlexbox';
5
+ import { UdpButton } from '../../../UI/fluent-web-components/UdpButton';
6
+ import { UdpProgressBar } from '../../../UI/fluent-web-components/UdpProgressBar';
7
+ import { UdpMessageBar } from '../../../UI/fluent-web-components/UdpMessageBar';
8
+ import { UdpRadioGroup } from '../../../UI/fluent-web-components/UdpRadioGroup';
9
+ import { UdpTablist } from '../../../UI/fluent-web-components/UdpTablist';
9
10
  import { CreditCardForm } from './CreditCardForm';
10
11
  import { BankAccountForm } from './BankAccountForm';
11
- import { CreditCardIcon } from './CreditCardIcon';
12
- import { BankIcon } from './BankIcon';
13
12
  import { CashForm } from './CashForm';
14
- import { CashIcon } from './CashIcon';
15
- import { DisabledOverlay } from './DisableOverlay';
16
- import { CheckIcon } from './CheckIcon';
17
- import { LayoutUnit } from '../../../layout/LayoutUnit';
18
- import { TabPanel } from './TabPanel';
19
- import { useAxiosGet } from '../../useAxiosGet';
20
13
  import { ConfigService } from '../../../configService';
21
- import { stringToProps } from '../../stringUtils';
22
14
  import { apiMutate } from '../../useAxiosMutate';
23
15
  import { useUser } from '../../auth/useUser';
24
16
  import { PaymentVendorFactoryEnum } from '../../../enums/paymentVendorFactoryEnum';
25
17
  import { PaymentMethodTypeEnum } from '../../../enums/paymentMethodTypeEnum';
26
- import grey from '@material-ui/core/colors/grey';
27
- const useStyles = makeStyles((theme) => ({
28
- tab: {
29
- color: grey[800],
30
- '&.Mui-selected': {
31
- color: theme.palette.secondary.main,
32
- },
33
- textTransform: 'none',
34
- minWidth: 115,
35
- width: 115,
36
- },
37
- cardFullWidth: {
38
- width: "100%",
39
- minWidth: "100%",
40
- padding: 0,
41
- },
42
- paddingZero: {
43
- padding: 0,
44
- },
45
- submitControl: {
46
- marginLeft: '50px',
47
- },
48
- buttonProgress: {
49
- color: theme.palette.secondary.main,
50
- position: 'absolute',
51
- top: '50%',
52
- left: '50%',
53
- marginTop: -12,
54
- marginLeft: -12,
55
- },
56
- dropDownPaymentsCard: {
57
- padding: '20px 12px 0px 12px',
58
- },
59
- assetTypeLabel: {
60
- top: theme.spacing(-0.7),
61
- },
62
- formControl: {
63
- width: '100%',
64
- marginTop: '-15px',
65
- },
66
- }));
67
- /** paymentMethodIcons @type {*} */
68
- const paymentMethodIcons = {
69
- 'Credit Card': CreditCardIcon,
70
- 'Cash': CashIcon,
71
- 'eCheck': BankIcon,
72
- 'Debit Card': CheckIcon,
73
- };
18
+ import { usePaymentContext } from './usePaymentContext';
74
19
  const DEFAULT_ACCOUNT_TYPES = [
75
20
  { name: 'Checking', value: 'checking' },
76
21
  { name: 'Savings', value: 'savings' },
@@ -81,25 +26,6 @@ const PAYMENT_METHODS = {
81
26
  E_CHECK: 'echeck',
82
27
  DEBIT_CARD: 'debit card'
83
28
  };
84
- /**
85
- * getPaymentMethodIcon
86
- *
87
- * @param {string} paymentMethodName
88
- * @param {boolean} isSelected
89
- * @param {boolean} isEnabled
90
- * @param {*} theme
91
- * @return {*}
92
- */
93
- const getPaymentMethodIcon = (paymentMethodName, isSelected, isEnabled, theme) => {
94
- // Determine the icon color based on the selection and enablement status
95
- const color = isEnabled ? (isSelected ? theme.palette.secondary.main : grey[800]) : grey[400];
96
- // Find the corresponding icon component from the mapping
97
- const IconComponent = paymentMethodIcons[paymentMethodName];
98
- // Return null if no matching icon is found (unknown payment method)
99
- if (!IconComponent)
100
- return null;
101
- return (React.createElement(Badge, { color: "secondary", badgeContent: !isEnabled ? "!" : null }, React.createElement(IconComponent, { color: color })));
102
- };
103
29
  /**
104
30
  * PaymentForm Component
105
31
  *
@@ -156,44 +82,60 @@ const getPaymentMethodIcon = (paymentMethodName, isSelected, isEnabled, theme) =
156
82
  * This component should be used within a React environment that supports hooks and functional components.
157
83
  * It relies on Material-UI for UI components and theming.
158
84
  */
159
- export const PaymentForm = ({ id, activeTab = 0, externalId, paymentAccounts, paymentDetails, bankAccountTypes = DEFAULT_ACCOUNT_TYPES, loading, presetDataRequest, onTabChange, onFormStatusChange, onSubmitCompleted, submit, children, childrenPosition = "before", actionLabel, render }) => {
160
- const classes = useStyles();
161
- const theme = useTheme();
85
+ export const PaymentForm = ({ id, activeTab = 0, externalId,
86
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
87
+ paymentAccounts, paymentDetails, bankAccountTypes = DEFAULT_ACCOUNT_TYPES, loading, presetDataRequest, onTabChange, onFormStatusChange, onSubmitCompleted, submit, children, childrenPosition = "before", actionLabel, render, showSandboxBanner = true, s2sToken, s2sTenantId, refreshS2sToken, allowSavePaymentMethod = true, methodPicker = 'radio', actionsStart, onSubmittingChange, onReadyChange, }) => {
88
+ var _a;
162
89
  const user = useUser();
163
90
  const { t } = useTranslation();
164
91
  const [value, setValue] = useState(activeTab);
165
92
  const formRef = useRef(null);
93
+ const methodBoxRef = useRef(null);
94
+ const methodBoxReleaseTimer = useRef(null);
95
+ /**
96
+ * Freeze the method-form area's current height while switching methods.
97
+ * The outgoing form unmounts synchronously but the incoming form's web
98
+ * components hydrate asynchronously, so the area collapses to ~zero height
99
+ * for a few frames. Inside a scroll container that collapse clamps the
100
+ * scroll position (the page appears to jump to the top). Holding the
101
+ * previous height through the swap keeps the scroll position stable.
102
+ */
103
+ const freezeMethodBoxHeight = () => {
104
+ const box = methodBoxRef.current;
105
+ if (!box)
106
+ return;
107
+ box.style.minHeight = `${box.offsetHeight}px`;
108
+ if (methodBoxReleaseTimer.current) {
109
+ window.clearTimeout(methodBoxReleaseTimer.current);
110
+ }
111
+ methodBoxReleaseTimer.current = window.setTimeout(() => {
112
+ box.style.minHeight = '';
113
+ methodBoxReleaseTimer.current = null;
114
+ }, 400);
115
+ };
116
+ useEffect(() => () => {
117
+ if (methodBoxReleaseTimer.current) {
118
+ window.clearTimeout(methodBoxReleaseTimer.current);
119
+ }
120
+ }, []);
121
+ // Submit-path calls read the token through this ref so refreshS2sToken can
122
+ // swap in a fresh one mid-submit without waiting for a re-render.
123
+ const s2sTokenRef = useRef(s2sToken);
124
+ useEffect(() => {
125
+ s2sTokenRef.current = s2sToken;
126
+ }, [s2sToken]);
166
127
  const [paymentFormStatus, setPaymentFormStatus] = useState(null);
167
- const [paymentMethodsArray, setPaymentMethodsArray] = useState();
168
- const [selectedHostedPayment, setSelectedHostedPayment] = useState(null);
128
+ const [, setSelectedHostedPayment] = useState(null);
169
129
  const [selectedPaymentType, setSelectedPaymentType] = useState(PaymentMethodTypeEnum.CreditCard);
170
130
  const [loadingProcessPayment, setLoadingProcessPayment] = useState(false);
171
131
  const [selectedPaymentAccount, setSelectedPaymentAccount] = useState(null);
172
132
  // #region [API Calls]
173
133
  /**
174
- * API call to get the payment accounts (Credit cards, Bank accounts) associated with the account
175
- */
176
- const [{ data: paymentMethods, loading: loadingPaymentMethods }] = useAxiosGet(ConfigService.pgV1ApiUrl, `PaymentMethod`, {}, false);
177
- /**
178
- * API call to get the payment method information and determine if the payment method is enabled or not
179
- *
180
- * @param {*} paymentMethodID : Credit card: 4001, Bank account: 4002, eCheck: 4004
181
- * @return {*}
134
+ * Discovery: resolved list of payment methods + vendor/hosted-page info.
135
+ * When the backend ships a single payment-context endpoint, replace the
136
+ * usePaymentContext implementation no other change in this file is needed.
182
137
  */
183
- const getPaymentMethodInfo = useCallback(async (paymentMethodID) => {
184
- if (!paymentMethodID || !(user === null || user === void 0 ? void 0 : user.currentTenantId)) {
185
- return;
186
- }
187
- try {
188
- const result = await apiMutate(ConfigService.pgV1ApiUrl, `AppPaymentVendor/PaymentMethod/${paymentMethodID}`, {
189
- method: 'GET',
190
- }, {});
191
- return result;
192
- }
193
- catch (error) {
194
- throw new Error(`Error getting payment method info: ${error === null || error === void 0 ? void 0 : error.message}`);
195
- }
196
- }, [user]);
138
+ const { methods: paymentMethodsArray, isLoading: loadingPaymentMethods } = usePaymentContext(s2sToken, s2sTenantId);
197
139
  /**
198
140
  * API call to fetchPaymentMethod
199
141
  *
@@ -205,10 +147,15 @@ export const PaymentForm = ({ id, activeTab = 0, externalId, paymentAccounts, pa
205
147
  if (!currentTenantID || !paymentMethodId) {
206
148
  return null;
207
149
  }
208
- return apiMutate(ConfigService.pgV1ApiUrl, `AppPaymentVendor/PaymentMethod/${paymentMethodId}`, {
209
- method: 'GET',
210
- }, {});
211
- }, []);
150
+ const token = s2sTokenRef.current;
151
+ const path = token
152
+ ? `AppPaymentVendor/PaymentMethod/${paymentMethodId}/S2S`
153
+ : `AppPaymentVendor/PaymentMethod/${paymentMethodId}`;
154
+ const config = token
155
+ ? { method: 'GET', headers: { Authorization: `Bearer ${token}`, currentTenantId: s2sTenantId } }
156
+ : { method: 'GET' };
157
+ return apiMutate(ConfigService.pgV1ApiUrl, path, config, {});
158
+ }, [s2sTenantId]);
212
159
  /**
213
160
  * fetchPaymentMethodDetails
214
161
  *
@@ -235,11 +182,16 @@ export const PaymentForm = ({ id, activeTab = 0, externalId, paymentAccounts, pa
235
182
  if (!paymentType || !requestBody) {
236
183
  return null;
237
184
  }
238
- return apiMutate(ConfigService.pgV1ApiUrl, `ProcessPayment/${paymentType}`, {
239
- method: 'POST',
240
- data: requestBody,
241
- }, {});
242
- }, []);
185
+ const token = s2sTokenRef.current;
186
+ const path = token
187
+ ? `ProcessPayment/${paymentType}/S2S`
188
+ : `ProcessPayment/${paymentType}`;
189
+ const body = requestBody;
190
+ const config = token
191
+ ? { method: 'POST', data: body, headers: { Authorization: `Bearer ${token}`, currentTenantId: s2sTenantId } }
192
+ : { method: 'POST', data: body };
193
+ return apiMutate(ConfigService.pgV1ApiUrl, path, config, {});
194
+ }, [s2sTenantId]);
243
195
  /**
244
196
  * executeHostedPaymentPG
245
197
  *
@@ -251,14 +203,16 @@ export const PaymentForm = ({ id, activeTab = 0, externalId, paymentAccounts, pa
251
203
  if (!paymentType || !requestBody) {
252
204
  return null;
253
205
  }
254
- const path = (requestBody === null || requestBody === void 0 ? void 0 : requestBody.paymentTokenId) ?
255
- `ProcessPayment/${paymentType}Profile` :
256
- `ProcessHostedPaymentPagePayment/${paymentType}`;
257
- return apiMutate(ConfigService.pgV1ApiUrl, path, {
258
- method: 'POST',
259
- data: requestBody,
260
- }, {});
261
- }, []);
206
+ const body = requestBody;
207
+ const token = s2sTokenRef.current;
208
+ const basePath = (requestBody === null || requestBody === void 0 ? void 0 : requestBody.paymentTokenId)
209
+ ? `ProcessPayment/${paymentType}Profile`
210
+ : `ProcessHostedPaymentPagePayment/${paymentType}`;
211
+ const path = token ? `${basePath}/S2S` : basePath;
212
+ return apiMutate(ConfigService.pgV1ApiUrl, path, token
213
+ ? { method: 'POST', data: body, headers: { Authorization: `Bearer ${token}`, currentTenantId: s2sTenantId } }
214
+ : { method: 'POST', data: body }, {});
215
+ }, [s2sTenantId]);
262
216
  // #endregion
263
217
  // #region [Methods]
264
218
  const paymentMethodLabels = useMemo(() => ({
@@ -274,46 +228,80 @@ export const PaymentForm = ({ id, activeTab = 0, externalId, paymentAccounts, pa
274
228
  * @param {React.ChangeEvent<{}>} event
275
229
  * @param {number} newValue
276
230
  */
231
+ /**
232
+ * Update the active method by index into the enabled list.
233
+ * `event` is forwarded to `onTabChange` for back-compat with consumers that
234
+ * expected the legacy MUI tab-change signature.
235
+ */
277
236
  const handleChange = (event, newValue) => {
278
237
  var _a;
279
- event.preventDefault();
238
+ freezeMethodBoxHeight();
280
239
  setValue(newValue);
281
- const selectedPaymentMethod = paymentMethodsArray === null || paymentMethodsArray === void 0 ? void 0 : paymentMethodsArray[newValue];
282
- // Set selected payment method and hosted payment type
240
+ const selectedPaymentMethod = enabledMethods === null || enabledMethods === void 0 ? void 0 : enabledMethods[newValue];
283
241
  setSelectedHostedPayment((_a = selectedPaymentMethod === null || selectedPaymentMethod === void 0 ? void 0 : selectedPaymentMethod.paymentVendorConnection) === null || _a === void 0 ? void 0 : _a.paymentVendorFactoryID);
284
242
  setSelectedPaymentType(selectedPaymentMethod === null || selectedPaymentMethod === void 0 ? void 0 : selectedPaymentMethod.paymentMethodID);
285
243
  onTabChange(event, selectedPaymentMethod);
286
244
  };
245
+ // onFormStatusChange is fired by the useEffect on `paymentFormStatus` below;
246
+ // do NOT also call it here or consumers receive the same status twice per change.
287
247
  const handleStatusChange = (paymentFormStatus) => {
288
248
  setPaymentFormStatus(paymentFormStatus);
289
- onFormStatusChange === null || onFormStatusChange === void 0 ? void 0 : onFormStatusChange(paymentFormStatus);
290
249
  };
291
250
  /**
292
- * useCallback()
293
- * renderPaymentMethodForm
251
+ * Render the form for the currently-selected method. The picker only ever
252
+ * surfaces enabled methods, so no disabled-overlay branch is needed.
294
253
  *
295
- * @param {any} method
296
- * @return {*}
254
+ * @param method the active payment method
255
+ * @param isFormDisabled when true, locks every input in the rendered form.
256
+ * Driven by RFF `submitting` + our own
257
+ * `loadingProcessPayment` so the user can't change
258
+ * values mid-submit.
297
259
  */
298
- const renderPaymentMethodForm = useCallback((method) => {
299
- var _a, _b, _c, _d;
300
- const isDisabled = !method.enabled;
301
- const overlayMessage = t("This Payment method is currently unavailable");
302
- return (React.createElement("div", { style: { position: 'relative' } }, isDisabled && React.createElement(DisabledOverlay, { message: overlayMessage }), React.createElement("div", { style: { opacity: isDisabled ? 0.5 : 1 } }, ((_a = method === null || method === void 0 ? void 0 : method.name) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === PAYMENT_METHODS.CREDIT_CARD && React.createElement(CreditCardForm, Object.assign({ onStatusChange: handleStatusChange, paymentDetails: paymentDetails, externalId: externalId, setSelectedPaymentAccount: setSelectedPaymentAccount, actionLabel: actionLabel }, method)), ((_b = method === null || method === void 0 ? void 0 : method.name) === null || _b === void 0 ? void 0 : _b.toLowerCase()) === PAYMENT_METHODS.CASH && React.createElement(CashForm, Object.assign({ onStatusChange: handleStatusChange, paymentDetails: paymentDetails }, method)), ((_c = method === null || method === void 0 ? void 0 : method.name) === null || _c === void 0 ? void 0 : _c.toLowerCase()) === PAYMENT_METHODS.E_CHECK && React.createElement(BankAccountForm, Object.assign({ bankAccountTypes: bankAccountTypes, onStatusChange: handleStatusChange, paymentDetails: paymentDetails, externalId: externalId, setSelectedPaymentAccount: setSelectedPaymentAccount, actionLabel: actionLabel }, method)), ((_d = method === null || method === void 0 ? void 0 : method.name) === null || _d === void 0 ? void 0 : _d.toLowerCase()) === PAYMENT_METHODS.DEBIT_CARD && React.createElement(CreditCardForm, Object.assign({ onStatusChange: handleStatusChange, paymentDetails: paymentDetails }, method)), " ")));
303
- }, [paymentDetails, bankAccountTypes]);
260
+ const renderPaymentMethodForm = useCallback((method, isFormDisabled) => {
261
+ var _a;
262
+ const name = (_a = method === null || method === void 0 ? void 0 : method.name) === null || _a === void 0 ? void 0 : _a.toLowerCase();
263
+ if (name === PAYMENT_METHODS.CREDIT_CARD) {
264
+ return (React.createElement(CreditCardForm, Object.assign({ onStatusChange: handleStatusChange, paymentDetails: paymentDetails, externalId: externalId !== undefined ? String(externalId) : undefined, setSelectedPaymentAccount: setSelectedPaymentAccount, actionLabel: actionLabel, isFormDisabled: isFormDisabled, allowSaveMethod: allowSavePaymentMethod }, method)));
265
+ }
266
+ if (name === PAYMENT_METHODS.CASH) {
267
+ return (React.createElement(CashForm, Object.assign({ onStatusChange: handleStatusChange, paymentDetails: paymentDetails, isFormDisabled: isFormDisabled }, method)));
268
+ }
269
+ if (name === PAYMENT_METHODS.E_CHECK) {
270
+ return (React.createElement(BankAccountForm, Object.assign({ bankAccountTypes: bankAccountTypes, onStatusChange: handleStatusChange, paymentDetails: paymentDetails, externalId: externalId !== undefined ? String(externalId) : undefined, setSelectedPaymentAccount: setSelectedPaymentAccount, actionLabel: actionLabel, isFormDisabled: isFormDisabled, allowSaveMethod: allowSavePaymentMethod }, method)));
271
+ }
272
+ if (name === PAYMENT_METHODS.DEBIT_CARD) {
273
+ // TODO: Implement Debit Card form. Falls back to CreditCardForm until then.
274
+ return (React.createElement(CreditCardForm, Object.assign({ onStatusChange: handleStatusChange, paymentDetails: paymentDetails, isFormDisabled: isFormDisabled, allowSaveMethod: allowSavePaymentMethod }, method)));
275
+ }
276
+ return null;
277
+ }, [paymentDetails, bankAccountTypes, externalId, actionLabel, allowSavePaymentMethod]);
304
278
  /**
305
- * isLiveMode
306
- *
307
- * @return {*}
279
+ * Enabled (configured) methods only. Unconfigured methods are hidden from the
280
+ * picker entirely — today's behaviour of "show a disabled tab with a !
281
+ * badge" was confusing and inaccessible.
282
+ */
283
+ const enabledMethods = useMemo(() => {
284
+ var _a;
285
+ return (_a = paymentMethodsArray === null || paymentMethodsArray === void 0 ? void 0 : paymentMethodsArray.filter((m) => m.enabled)) !== null && _a !== void 0 ? _a : [];
286
+ }, [paymentMethodsArray]);
287
+ const activeMethod = (_a = enabledMethods[value]) !== null && _a !== void 0 ? _a : enabledMethods[0];
288
+ /**
289
+ * Sandbox mode for the currently-selected method's vendor connection.
308
290
  */
309
291
  const isSandBoxMode = useMemo(() => {
310
- var _a, _b;
311
- const isLiveMode = ((_b = (_a = paymentMethodsArray === null || paymentMethodsArray === void 0 ? void 0 : paymentMethodsArray[value]) === null || _a === void 0 ? void 0 : _a.paymentVendorConnection) === null || _b === void 0 ? void 0 : _b.isLiveMode);
312
- if (isLiveMode === undefined) {
313
- return false;
292
+ var _a;
293
+ const isLiveMode = (_a = activeMethod === null || activeMethod === void 0 ? void 0 : activeMethod.paymentVendorConnection) === null || _a === void 0 ? void 0 : _a.isLiveMode;
294
+ return isLiveMode === false;
295
+ }, [activeMethod]);
296
+ // Clamp `value` if the enabled-methods list shrinks (e.g. discovery
297
+ // returned fewer methods than expected).
298
+ useEffect(() => {
299
+ if (enabledMethods.length === 0)
300
+ return;
301
+ if (value >= enabledMethods.length) {
302
+ setValue(0);
314
303
  }
315
- return !isLiveMode;
316
- }, [paymentMethodsArray, value]);
304
+ }, [enabledMethods.length, value]);
317
305
  /**
318
306
  * getPaymentMethodLabel
319
307
  *
@@ -326,64 +314,6 @@ export const PaymentForm = ({ id, activeTab = 0, externalId, paymentAccounts, pa
326
314
  const methodLabel = (paymentMethodLabels === null || paymentMethodLabels === void 0 ? void 0 : paymentMethodLabels[normalizedMethodName]) || method.name;
327
315
  return methodLabel;
328
316
  };
329
- /**
330
- * setPaymentMethodsEnabled
331
- * take every paymentMethod (eCheck, credit card, etc) and verify if the payment methods are enabled or not
332
- * @return {*}
333
- */
334
- const setPaymentMethodsEnabled = useCallback(async () => {
335
- var _a;
336
- // Filter out any falsy paymentMethod values first
337
- const validPaymentMethods = paymentMethods === null || paymentMethods === void 0 ? void 0 : paymentMethods.filter(Boolean);
338
- // Use Promise.allSettled to handle both fulfilled and rejected promises
339
- const results = await Promise.allSettled(validPaymentMethods === null || validPaymentMethods === void 0 ? void 0 : validPaymentMethods.map(async (paymentMethod) => {
340
- var _a;
341
- try {
342
- const paymentMethodEnable = await getPaymentMethodInfo(paymentMethod.paymentMethodID);
343
- const paymentVendorConnection = ((_a = paymentMethodEnable === null || paymentMethodEnable === void 0 ? void 0 : paymentMethodEnable.data) === null || _a === void 0 ? void 0 : _a.paymentVendorConnection) || null;
344
- const providerType = paymentVendorConnection === null || paymentVendorConnection === void 0 ? void 0 : paymentVendorConnection.paymentVendorFactoryID;
345
- const hostedPaymentPageSetup = (paymentVendorConnection === null || paymentVendorConnection === void 0 ? void 0 : paymentVendorConnection.hostedPaymentPageSetup.find((x) => x.useHostedPaymentPage)) || null;
346
- const isHostedPaymentPage = (hostedPaymentPageSetup === null || hostedPaymentPageSetup === void 0 ? void 0 : hostedPaymentPageSetup.useHostedPaymentPage) || false;
347
- let providerProps = {};
348
- let hasReadPropertiesError = false;
349
- let readPropertiesMessageError = null;
350
- if (isHostedPaymentPage) {
351
- const { error, attributes } = stringToProps(hostedPaymentPageSetup === null || hostedPaymentPageSetup === void 0 ? void 0 : hostedPaymentPageSetup.hostedPaymentPageConfig);
352
- providerProps = Object.assign({ customCss: hostedPaymentPageSetup === null || hostedPaymentPageSetup === void 0 ? void 0 : hostedPaymentPageSetup.customCSS, attributes }, ((attributes === null || attributes === void 0 ? void 0 : attributes.baseUrl)
353
- ? { baseUrl: attributes === null || attributes === void 0 ? void 0 : attributes.baseUrl }
354
- : null));
355
- hasReadPropertiesError = error ? true : false;
356
- readPropertiesMessageError = error;
357
- }
358
- // Initialize returnObject with common properties
359
- let returnObject = {
360
- paymentMethodID: paymentMethod.paymentMethodID,
361
- name: paymentMethod.name,
362
- enabled: (paymentMethodEnable === null || paymentMethodEnable === void 0 ? void 0 : paymentMethodEnable.status) === 200,
363
- data: (paymentMethodEnable === null || paymentMethodEnable === void 0 ? void 0 : paymentMethodEnable.status) === 200
364
- ? paymentMethodEnable.data
365
- : null,
366
- paymentVendorConnection,
367
- isHostedPaymentPage,
368
- providerType,
369
- providerProps,
370
- hasReadPropertiesError,
371
- readPropertiesMessageError,
372
- };
373
- return returnObject;
374
- }
375
- catch (error) {
376
- console.error(`Error fetching payment method info for ${paymentMethod === null || paymentMethod === void 0 ? void 0 : paymentMethod.paymentMethodID}:`, error);
377
- return null;
378
- }
379
- }));
380
- // Filter out null results in case of errors
381
- const filteredResults = results
382
- .filter(result => result.status === 'fulfilled' && result.value !== null)
383
- .map((result) => result.value) || null;
384
- setPaymentMethodsArray(filteredResults);
385
- setSelectedHostedPayment(((_a = filteredResults === null || filteredResults === void 0 ? void 0 : filteredResults.find((x) => x.enabled)) === null || _a === void 0 ? void 0 : _a.providerType) || null);
386
- }, [paymentMethods]);
387
317
  /**
388
318
  * createHostedPaymentRequest
389
319
  *
@@ -517,28 +447,85 @@ export const PaymentForm = ({ id, activeTab = 0, externalId, paymentAccounts, pa
517
447
  */
518
448
  const submitPaymentForm = useCallback(async (formData) => {
519
449
  var _a, _b, _c;
450
+ var _d;
520
451
  if (!formData)
521
452
  return;
453
+ // Discovery still in flight or returned no payable methods — no method
454
+ // fields are mounted yet, so nothing validated and formData is empty.
455
+ // The internal button is disabled in this window, but external
456
+ // form-registry submit buttons can still land here.
457
+ if (loadingPaymentMethods || enabledMethods.length === 0)
458
+ return;
522
459
  setLoadingProcessPayment(true);
523
- const currentTenantID = user === null || user === void 0 ? void 0 : user.currentTenantId;
460
+ // Payment-link tokens can expire between page load and submit; re-mint
461
+ // when the host provides a way to. Failure keeps the existing token.
462
+ if (refreshS2sToken) {
463
+ try {
464
+ const freshToken = await refreshS2sToken();
465
+ if (freshToken) {
466
+ s2sTokenRef.current = freshToken;
467
+ }
468
+ }
469
+ catch (error) {
470
+ console.warn('refreshS2sToken failed; submitting with the existing token', error);
471
+ }
472
+ }
473
+ // Reactive backstop to the proactive refresh above: when a call is
474
+ // rejected with a 403 (how the Payment Portal reports an expired S2S
475
+ // token), re-mint the token and re-issue that call once. Never retries
476
+ // other statuses — a 500 from ProcessPayment is a real error and may
477
+ // mean the charge already went through.
478
+ const retryOnAuthError = async (call) => {
479
+ var _a;
480
+ try {
481
+ return await call();
482
+ }
483
+ catch (error) {
484
+ if (!refreshS2sToken || ((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status) !== 403)
485
+ throw error;
486
+ let freshToken;
487
+ try {
488
+ freshToken = await refreshS2sToken();
489
+ }
490
+ catch (_b) {
491
+ throw error;
492
+ }
493
+ if (!freshToken)
494
+ throw error;
495
+ s2sTokenRef.current = freshToken;
496
+ return call();
497
+ }
498
+ };
499
+ const currentTenantID = s2sTokenRef.current ? s2sTenantId : user === null || user === void 0 ? void 0 : user.currentTenantId;
524
500
  const paymentMethodId = selectedPaymentType;
525
501
  if (!currentTenantID || !paymentMethodId) {
526
- onSubmitCompleted === null || onSubmitCompleted === void 0 ? void 0 : onSubmitCompleted(null, { error: true });
502
+ onSubmitCompleted === null || onSubmitCompleted === void 0 ? void 0 : onSubmitCompleted(null, {
503
+ error: true,
504
+ message: 'Missing tenant ID or payment method ID',
505
+ });
506
+ setLoadingProcessPayment(false);
507
+ return;
527
508
  }
528
509
  try {
529
- const paymentMethod = await fetchPaymentMethodDetails(currentTenantID, paymentMethodId);
510
+ const paymentMethod = await retryOnAuthError(() => fetchPaymentMethodDetails(currentTenantID, paymentMethodId));
530
511
  const isHostedPayment = ((_c = (_b = (_a = paymentMethod.paymentVendorConnection) === null || _a === void 0 ? void 0 : _a.hostedPaymentPageSetup) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.useHostedPaymentPage) || false;
531
512
  const dataRequest = createDataRequest(formData, paymentMethod, isHostedPayment, paymentMethodId, selectedPaymentAccount);
532
- const paymentResult = await executePayment(paymentMethodId, dataRequest, isHostedPayment);
513
+ const paymentResult = await retryOnAuthError(() => executePayment(paymentMethodId, dataRequest, isHostedPayment));
533
514
  onSubmitCompleted === null || onSubmitCompleted === void 0 ? void 0 : onSubmitCompleted(formData, paymentResult);
534
515
  }
535
516
  catch (error) {
536
- throw new Error("Error processing payment: " + (error === null || error === void 0 ? void 0 : error.message));
517
+ // Route failures through onSubmitCompleted so consumers can react
518
+ // (previous behaviour threw from an async event handler -> unhandled rejection).
519
+ console.error('Error processing payment:', error);
520
+ onSubmitCompleted === null || onSubmitCompleted === void 0 ? void 0 : onSubmitCompleted(formData, {
521
+ error: true,
522
+ message: (_d = error === null || error === void 0 ? void 0 : error.message) !== null && _d !== void 0 ? _d : 'Error processing payment',
523
+ });
537
524
  }
538
525
  finally {
539
526
  setLoadingProcessPayment(false);
540
527
  }
541
- }, [paymentFormStatus, createDataRequest, selectedPaymentType, user, selectedPaymentAccount]);
528
+ }, [createDataRequest, selectedPaymentType, user, selectedPaymentAccount, s2sTenantId, refreshS2sToken, executePayment, fetchPaymentMethodDetails, onSubmitCompleted, loadingPaymentMethods, enabledMethods]);
542
529
  /**
543
530
  * Handles the form submission for iCheck payments.
544
531
  *
@@ -548,51 +535,83 @@ export const PaymentForm = ({ id, activeTab = 0, externalId, paymentAccounts, pa
548
535
  *
549
536
  */
550
537
  const handleICheckPaymentFormStatus = useCallback(() => {
551
- var _a;
552
- if ((paymentFormStatus === null || paymentFormStatus === void 0 ? void 0 : paymentFormStatus.selectedHostedPayment) === PaymentVendorFactoryEnum.ICheck ||
553
- (paymentFormStatus === null || paymentFormStatus === void 0 ? void 0 : paymentFormStatus.selectedHostedPayment) === PaymentVendorFactoryEnum.Paymentus && (formRef === null || formRef === void 0 ? void 0 : formRef.current)) {
554
- (_a = formRef.current) === null || _a === void 0 ? void 0 : _a.submit();
538
+ const vendor = paymentFormStatus === null || paymentFormStatus === void 0 ? void 0 : paymentFormStatus.selectedHostedPayment;
539
+ const isAutoSubmitVendor = vendor === PaymentVendorFactoryEnum.ICheck ||
540
+ vendor === PaymentVendorFactoryEnum.Paymentus;
541
+ if (isAutoSubmitVendor && (formRef === null || formRef === void 0 ? void 0 : formRef.current)) {
542
+ formRef.current.submit();
555
543
  }
556
544
  }, [paymentFormStatus]);
557
545
  // #endregion
558
546
  // #region [Effects]
547
+ // Default the selected vendor to the first enabled method once discovery
548
+ // completes. Matches the previous behaviour from setPaymentMethodsEnabled.
559
549
  useEffect(() => {
560
- setPaymentMethodsEnabled();
561
- }, [paymentMethods]);
550
+ var _a;
551
+ if (!paymentMethodsArray)
552
+ return;
553
+ const firstEnabled = paymentMethodsArray.find((m) => m.enabled);
554
+ setSelectedHostedPayment((_a = firstEnabled === null || firstEnabled === void 0 ? void 0 : firstEnabled.providerType) !== null && _a !== void 0 ? _a : null);
555
+ }, [paymentMethodsArray]);
562
556
  useEffect(() => {
563
557
  onFormStatusChange === null || onFormStatusChange === void 0 ? void 0 : onFormStatusChange(paymentFormStatus);
564
558
  handleICheckPaymentFormStatus();
565
559
  }, [paymentFormStatus]);
560
+ // onSubmittingChange is intentionally omitted from deps; consumers may pass
561
+ // unstable references and we only want to fire when the window changes.
562
+ useEffect(() => {
563
+ onSubmittingChange === null || onSubmittingChange === void 0 ? void 0 : onSubmittingChange(loadingProcessPayment);
564
+ // eslint-disable-next-line react-hooks/exhaustive-deps
565
+ }, [loadingProcessPayment]);
566
+ // Mirrors the internal submit button's disabled conditions so hosts with
567
+ // external submit buttons can gate them identically. Callback omitted from
568
+ // deps for the same unstable-reference reason as above.
569
+ const isReadyToSubmit = !loadingPaymentMethods &&
570
+ enabledMethods.length > 0 &&
571
+ (paymentFormStatus === null || paymentFormStatus === void 0 ? void 0 : paymentFormStatus.isValid) !== false;
572
+ useEffect(() => {
573
+ onReadyChange === null || onReadyChange === void 0 ? void 0 : onReadyChange(isReadyToSubmit);
574
+ // eslint-disable-next-line react-hooks/exhaustive-deps
575
+ }, [isReadyToSubmit]);
566
576
  // #endregion
567
- return (React.createElement(React.Fragment, null, loadingProcessPayment && (React.createElement(Box, { sx: {
568
- position: 'fixed',
569
- top: 0,
570
- left: 0,
571
- width: '100%',
572
- height: '100%',
573
- backgroundColor: 'rgba(255, 255, 255, 0.8)',
574
- zIndex: 9999,
575
- display: 'flex',
576
- alignItems: 'center',
577
- justifyContent: 'center',
578
- } }, React.createElement(CircularProgress, { size: "3rem", color: "primary" }))), React.createElement(Form, { id: id, onSubmit: submitPaymentForm, autocomplete: "off", render: (params) => {
577
+ return (React.createElement(React.Fragment, null, React.createElement(Form, { id: id, onSubmit: submitPaymentForm, render: (params) => {
578
+ var _a, _b;
579
579
  formRef.current = params === null || params === void 0 ? void 0 : params.form;
580
- return (React.createElement(React.Fragment, null, React.createElement(Box, { display: "flex", flexDirection: "column", style: { gap: '10px', flex: 1 } }, childrenPosition === "before" && children, React.createElement(Box, { display: "flex", flexDirection: "column", padding: "0" }, React.createElement(Tabs, { value: value, onChange: handleChange, "aria-label": t("(payment method tabs") }, paymentMethodsArray === null || paymentMethodsArray === void 0 ? void 0 : paymentMethodsArray.map((method, index) => (
581
- // @ts-ignore
582
- React.createElement(Tooltip, { key: method.paymentMethodID, title: !method.enabled ? t("This Payment method is currently unavailable") : "" }, React.createElement(Tab, { key: method.paymentMethodID,
583
- //@ts-ignore
584
- icon: getPaymentMethodIcon(method.name, value === index, method.enabled, theme), label: getPaymentMethodLabel(method), "aria-label": getPaymentMethodLabel(method), className: classes.tab }))))), React.createElement(Card, { className: classes.cardFullWidth }, React.createElement(CardContent, { className: classes.paddingZero }, (loading || loadingPaymentMethods) && React.createElement(LinearProgress, null), React.createElement(Grow, Object.assign({ in: isSandBoxMode, style: { transformOrigin: '0 0 0', display: isSandBoxMode ? 'flex' : 'none' } }, (true ? { timeout: 800 } : {})), React.createElement(Box, { display: "flex", flexDirection: "row", justifyContent: "space-between", alignItems: "center", style: {
585
- gap: '8px',
586
- boxSizing: 'border-box',
587
- background: '#FFF2CD',
588
- borderWidth: '1px 0px',
589
- borderStyle: 'solid',
590
- borderColor: 'rgba(0, 0, 0, 0.12)',
591
- padding: '10px',
592
- } }, React.createElement(Typography, { variant: "subtitle2", style: { width: '100%', textAlign: 'center' } }, t('You are currently in test mode using a Sandbox account')))), React.createElement(LayoutUnit, { unit: "xxs" }), paymentMethodsArray === null || paymentMethodsArray === void 0 ? void 0 : paymentMethodsArray.map((method, index) => (
593
- // @ts-ignore
594
- React.createElement(TabPanel, { value: value, index: index, key: method.paymentMethodID }, renderPaymentMethodForm(method)))), submit &&
595
- React.createElement(FluentButton, { variant: "contained", color: "secondary", disabled: false, type: "submit", className: classes.submitControl }, loading && React.createElement(CircularProgress, { size: 24, className: classes.buttonProgress }), React.createElement(Typography, null, t('Save')))))), childrenPosition === "after" && children, typeof render === 'function' ? render(params) : render), childrenPosition === "outside" && children));
580
+ const hasMultipleMethods = enabledMethods.length > 1;
581
+ // Discovery has completed and there are no enabled methods.
582
+ const hasNoMethods = paymentMethodsArray !== undefined &&
583
+ enabledMethods.length === 0 &&
584
+ !loadingPaymentMethods;
585
+ // RFF `submitting` flips while the Promise returned from
586
+ // submitPaymentForm is in flight. loadingProcessPayment is our own
587
+ // signal for the same window. Either way, lock the UI so the user
588
+ // can't change values or switch methods mid-submit.
589
+ const isSubmitting = Boolean(params === null || params === void 0 ? void 0 : params.submitting) || loadingProcessPayment;
590
+ return (React.createElement(React.Fragment, null, React.createElement(UdpFlexbox, { direction: "column", gap: "md", wrap: "nowrap" }, childrenPosition === "before" && children, (loading || loadingPaymentMethods) && React.createElement(UdpProgressBar, null), hasNoMethods && (React.createElement(UdpMessageBar, { intent: "error" }, t("No payment methods are available for this account"))), showSandboxBanner && isSandBoxMode && (React.createElement(UdpMessageBar, { intent: "warning" }, t("You are currently in test mode using a Sandbox account"))), hasMultipleMethods && methodPicker === 'tabs' && (React.createElement(UdpTablist, { size: "small", disabled: isSubmitting, activeId: String((_a = activeMethod === null || activeMethod === void 0 ? void 0 : activeMethod.paymentMethodID) !== null && _a !== void 0 ? _a : ''), tabList: enabledMethods.map((m) => ({
591
+ id: String(m.paymentMethodID),
592
+ label: getPaymentMethodLabel(m),
593
+ })), onTabChange: (e) => {
594
+ const id = String(e.detail);
595
+ const nextIdx = enabledMethods.findIndex((m) => String(m.paymentMethodID) === id);
596
+ if (nextIdx >= 0) {
597
+ handleChange(e, nextIdx);
598
+ }
599
+ } })), hasMultipleMethods && methodPicker === 'radio' && (React.createElement(UdpRadioGroup, { label: t("Payment method"), name: "paymentMethod", disabled: isSubmitting, includeErrorPadding: false, value: String((_b = activeMethod === null || activeMethod === void 0 ? void 0 : activeMethod.paymentMethodID) !== null && _b !== void 0 ? _b : ''), items: enabledMethods.map((m) => ({
600
+ label: getPaymentMethodLabel(m),
601
+ value: String(m.paymentMethodID),
602
+ })), onChange: (e) => {
603
+ const id = e.target.value;
604
+ const nextIdx = enabledMethods.findIndex((m) => String(m.paymentMethodID) === id);
605
+ if (nextIdx >= 0) {
606
+ handleChange(e, nextIdx);
607
+ }
608
+ } })), React.createElement("div", { ref: methodBoxRef }, activeMethod && renderPaymentMethodForm(activeMethod, isSubmitting)), submit && (React.createElement(UdpFlexbox, { direction: "row", gap: "sm", align: "center", wrap: "nowrap" }, actionsStart, React.createElement(UdpButton, { type: "submit", form: id, appearance: "primary", loading: loading || loadingProcessPayment,
609
+ // Not actionable until discovery resolves a payable method
610
+ // (no fields mounted yet — a click would submit an empty
611
+ // form), nor in hosted-iframe mode until the vendor iframe
612
+ // reports a token (isValid). Tokenization is driven inside
613
+ // the iframe, so this cannot deadlock the flow.
614
+ disabled: loadingPaymentMethods || hasNoMethods || (paymentFormStatus === null || paymentFormStatus === void 0 ? void 0 : paymentFormStatus.isValid) === false }, actionLabel || t('Save')))), childrenPosition === "after" && children, typeof render === 'function' ? render(params) : render), childrenPosition === "outside" && children));
596
615
  } })));
597
616
  };
598
617
  //# sourceMappingURL=PaymentForm.js.map