@akinon/pz-masterpass-rest 2.0.55-rc.0 → 2.0.56-beta.0

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/CHANGELOG.md CHANGED
@@ -1,14 +1,8 @@
1
1
  # @akinon/pz-masterpass-rest
2
2
 
3
- ## 2.0.55-rc.0
3
+ ## 2.0.56-beta.0
4
4
 
5
- ### Patch Changes
6
-
7
- - 119f0c11: ZERO-4629: Refactor token decoding to use decodeMasterpassToken utility and add wait-for-utils-ready functionality
8
- - 823a4449a: ZERO-4193: Upgrade TypeScript to version 5.2.2 across multiple packages
9
- - 5da13fff: ZERO-4358: fix masterpass-rest guest checkout flow
10
- - fb4aa9b4: ZERO-4333: update maxLength for cardNumber input and enhance button styling in installment list
11
- - 63a720003: ZERO-3895: enhance payment processing with additional fields support
5
+ ## 2.0.55
12
6
 
13
7
  ## 2.0.54
14
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akinon/pz-masterpass-rest",
3
- "version": "2.0.55-rc.0",
3
+ "version": "2.0.56-beta.0",
4
4
  "main": "src/index.ts",
5
5
  "types": "src/index.d.ts",
6
6
  "description": "Modern React-based Masterpass REST API integration package for ProjectZero e-commerce platform",
@@ -31,6 +31,6 @@
31
31
  "devDependencies": {
32
32
  "@types/react": "18.2.27",
33
33
  "@types/react-dom": "18.2.12",
34
- "typescript": "^5.2.2"
34
+ "typescript": "5.2.2"
35
35
  }
36
36
  }
@@ -184,7 +184,7 @@ const CreditCardForm: React.FC<CreditCardFormProps> = ({
184
184
  setValue('cardNumber', formatted, { shouldValidate: true });
185
185
  }}
186
186
  error={errors.cardNumber}
187
- maxLength={19}
187
+ maxLength={23}
188
188
  className="pr-20"
189
189
  />
190
190
  {cardType !== 'unknown' && (
@@ -118,7 +118,7 @@ const InstallmentList: React.FC<InstallmentListProps> = ({
118
118
  <Button
119
119
  onClick={onProceedToPayment}
120
120
  disabled={isLoading || paymentLoading}
121
- className="w-full cursor-pointer bg-[#000000] text-[#ffffff] hover:bg-[#ffffff] hover:text-[#000000] border border-solid border-[#000000] disabled:opacity-50 disabled:cursor-not-allowed"
121
+ className="w-full cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"
122
122
  >
123
123
  {paymentLoading ? (
124
124
  <div className="flex items-center justify-center">
@@ -204,21 +204,15 @@ const OTPModal: React.FC<OTPModalProps> = ({
204
204
  >
205
205
  <div className="px-6">
206
206
  {getTitle() && (
207
- <h3 className="text-center mt-4 text-lg font-semibold">
208
- {getTitle()}
209
- </h3>
207
+ <h3 className="text-center mt-4 text-lg font-semibold">{getTitle()}</h3>
210
208
  )}
211
- <p className="text-center mt-2 text-sm text-gray-600">
212
- {getDescription()}
213
- </p>
209
+ <p className="text-center mt-2 text-sm text-gray-600">{getDescription()}</p>
214
210
  <div className="flex flex-col gap-3 p-5 w-3/4 m-auto">
215
211
  <Input
216
212
  type="text"
217
213
  value={otp}
218
214
  onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
219
- setOtp(
220
- e.target.value.replace(/\D/g, '').slice(0, getMaxLength())
221
- );
215
+ setOtp(e.target.value.replace(/\D/g, '').slice(0, getMaxLength()));
222
216
  setError(null);
223
217
  }}
224
218
  maxLength={getMaxLength()}
@@ -228,9 +222,7 @@ const OTPModal: React.FC<OTPModalProps> = ({
228
222
  placeholder={getPlaceholder()}
229
223
  />
230
224
  {getHelperText() && (
231
- <p className="text-xs text-gray-500 text-center">
232
- {getHelperText()}
233
- </p>
225
+ <p className="text-xs text-gray-500 text-center">{getHelperText()}</p>
234
226
  )}
235
227
  {error && (
236
228
  <p className="text-[#d72b01] text-xs text-center">{error}</p>
@@ -24,7 +24,6 @@ import type {
24
24
  CardModel
25
25
  } from '../types/account.types';
26
26
  import { useMasterpassToken } from './useMasterpassToken';
27
- import { decodeMasterpassToken } from '../utils/wait-for-utils-ready';
28
27
 
29
28
  export const useMasterpassAccount = () => {
30
29
  const dispatch = useDispatch();
@@ -41,16 +40,7 @@ export const useMasterpassAccount = () => {
41
40
 
42
41
  const initializeAccount = useCallback(
43
42
  async (newTokenData: any, rawToken?: string) => {
44
- if (!newTokenData?.AccountKey) {
45
- dispatch(
46
- setAccountStatus({
47
- isAccountNotFound: false,
48
- isAccountNotLinked: true,
49
- shouldShowDirectForm: true
50
- })
51
- );
52
- return;
53
- }
43
+ if (!newTokenData?.AccountKey) return;
54
44
 
55
45
  const accountService = new AccountService(rawToken || token, newTokenData?.MerchantId);
56
46
 
@@ -136,19 +126,13 @@ export const useMasterpassAccount = () => {
136
126
  dispatch(resetState());
137
127
  }, [dispatch]);
138
128
 
139
- const refreshToken = async (options?: {
140
- three_d?: boolean;
141
- skipReset?: boolean;
142
- }) => {
129
+ const refreshToken = async (options?: { three_d?: boolean; skipReset?: boolean }) => {
143
130
  if (options?.three_d !== false && !options?.skipReset) {
144
131
  resetData();
145
132
  }
146
133
  const result = await refetch(options);
147
134
  if (result?.data?.token) {
148
- const decodedToken = decodeMasterpassToken(result.data.token);
149
- if (!decodedToken) {
150
- return null;
151
- }
135
+ const decodedToken = window.Utils.decodeJwt(result.data.token);
152
136
  return {
153
137
  tokenData: decodedToken,
154
138
  token: result.data.token,
@@ -234,12 +218,8 @@ export const useMasterpassAccount = () => {
234
218
  informationModalData: {
235
219
  type: 'warning',
236
220
  title: texts?.sessionExpiredTitle || 'Session Expired',
237
- message:
238
- texts?.sessionExpiredMessage ||
239
- 'Your session has expired due to inactivity. Please restart the process to continue.',
240
- secondaryMessage:
241
- texts?.sessionExpiredSecondaryMessage ||
242
- 'For security reasons, verification codes are only valid for a limited time.',
221
+ message: texts?.sessionExpiredMessage || 'Your session has expired due to inactivity. Please restart the process to continue.',
222
+ secondaryMessage: texts?.sessionExpiredSecondaryMessage || 'For security reasons, verification codes are only valid for a limited time.',
243
223
  buttonText: texts?.sessionExpiredButton || 'Start Again'
244
224
  }
245
225
  })
@@ -355,7 +335,7 @@ export const useMasterpassAccount = () => {
355
335
  const activeMerchantId = freshResult?.merchantId || tokenData?.MerchantId;
356
336
 
357
337
  if (!activeTokenData?.AccountKey) {
358
- return { success: false, message: '' };
338
+ throw new Error('Token data not available');
359
339
  }
360
340
 
361
341
  const accountService = new AccountService(activeToken, activeMerchantId);
@@ -25,7 +25,6 @@ import {
25
25
  } from '../redux/reducer';
26
26
  import { useMasterpassToken } from './useMasterpassToken';
27
27
  import { PaymentService } from '../services/payment';
28
- import { decodeMasterpassToken } from '../utils/wait-for-utils-ready';
29
28
  import {
30
29
  createPaymentRequest,
31
30
  createDirectPaymentRequest,
@@ -94,10 +93,7 @@ export const useMasterpassPayment = (
94
93
  const refreshTokenForPayment = useCallback(async () => {
95
94
  const result = await refetchToken({ skipReset: true });
96
95
  if (result?.data?.token) {
97
- const decodedToken = decodeMasterpassToken(result.data.token);
98
- if (!decodedToken) {
99
- return null;
100
- }
96
+ const decodedToken = window.Utils.decodeJwt(result.data.token);
101
97
  return {
102
98
  tokenData: decodedToken,
103
99
  token: result.data.token,
@@ -351,6 +347,10 @@ export const useMasterpassPayment = (
351
347
  const activeToken = freshResult?.token;
352
348
  const activeMerchantId = freshResult?.merchantId || tokenData?.MerchantId;
353
349
 
350
+ if (!activeTokenData?.AccountKey) {
351
+ return { success: false, message: 'Token data not available' };
352
+ }
353
+
354
354
  const prepareResult = await prepareMasterpassOrder({
355
355
  use_three_d: paymentState.useThreeD
356
356
  }).unwrap();
@@ -364,13 +364,13 @@ export const useMasterpassPayment = (
364
364
  cardHolderName: newCardFormData.cardholderName || '',
365
365
  cardAlias: newCardFormData.cardAlias || '',
366
366
  expiryDate: newCardFormData.expiryDate,
367
- accountKey: activeTokenData?.AccountKey || '',
367
+ accountKey: activeTokenData.AccountKey,
368
368
  amount: prepareResult.pre_order?.total_amount_with_interest,
369
369
  orderNo: prepareResult.context_list?.[0]?.page_context?.order_no,
370
370
  currencyCode: currency,
371
371
  installmentCount: selectedInstallment.installment_count,
372
- authenticationMethod: activeTokenData?.AuthenticationMethod,
373
- secure3DModel: activeTokenData?.Secure3dType,
372
+ authenticationMethod: activeTokenData.AuthenticationMethod,
373
+ secure3DModel: activeTokenData.Secure3dType,
374
374
  terminalGroupId,
375
375
  acquirerIcaNumber: bankIca,
376
376
  additionalFields:
@@ -14,10 +14,6 @@ import {
14
14
  updatePaymentState
15
15
  } from '../redux/reducer';
16
16
  import { isTokenExpired } from '../utils/payment-utils';
17
- import {
18
- waitForUtilsReady,
19
- decodeMasterpassToken
20
- } from '../utils/wait-for-utils-ready';
21
17
 
22
18
  interface UseMasterpassTokenProps {
23
19
  useThreeD: boolean;
@@ -69,37 +65,12 @@ export const useMasterpassToken = ({
69
65
  return;
70
66
  }
71
67
 
72
- if (!(data?.token && data.token !== reduxToken)) {
73
- return;
74
- }
75
-
76
- const token = data.token;
77
- const extras = data.extras;
78
- let cancelled = false;
79
-
80
- // The token request can resolve before the Masterpass SDK script has
81
- // loaded (common on slow/3G networks). Wait for `window.Utils.decodeJwt`
82
- // to exist before decoding, otherwise the checkout crashes with
83
- // `Cannot read properties of undefined (reading 'decodeJwt')`.
84
- //
85
- // Redux writes are deferred until after the wait resolves: dispatching
86
- // `setMasterpassRestToken` synchronously would change the `reduxToken`
87
- // dependency, re-run this effect and cancel the in-flight decode.
88
- (async () => {
89
- const ready = await waitForUtilsReady();
90
- if (cancelled) {
91
- return;
92
- }
93
-
94
- dispatch(setMasterpassRestToken(token));
95
- dispatch(setMasterpassTerminalGroupId(extras.terminal_group_id));
96
- dispatch(setMasterpassBankIca(extras.bank_ica));
97
-
98
- const decodedToken = ready ? decodeMasterpassToken(token) : undefined;
99
- if (!decodedToken) {
100
- return;
101
- }
68
+ if (data?.token && data.token !== reduxToken) {
69
+ dispatch(setMasterpassRestToken(data.token));
70
+ dispatch(setMasterpassTerminalGroupId(data.extras.terminal_group_id));
71
+ dispatch(setMasterpassBankIca(data.extras.bank_ica));
102
72
 
73
+ const decodedToken = window.Utils.decodeJwt(data.token);
103
74
  dispatch(setMasterpassRestTokenData(decodedToken));
104
75
 
105
76
  if (decodedToken?.AuthenticationMethod?.toLowerCase() === 'none') {
@@ -107,11 +78,7 @@ export const useMasterpassToken = ({
107
78
  }
108
79
 
109
80
  stableOnTokenReady(decodedToken);
110
- })();
111
-
112
- return () => {
113
- cancelled = true;
114
- };
81
+ }
115
82
  }, [data?.token, reduxToken, dispatch, stableOnTokenReady]);
116
83
 
117
84
  const refetchToken = useCallback(
@@ -127,19 +94,15 @@ export const useMasterpassToken = ({
127
94
  );
128
95
  dispatch(setMasterpassBankIca(result.data.extras.bank_ica));
129
96
 
130
- await waitForUtilsReady();
131
- const decodedToken = decodeMasterpassToken(result.data.token);
132
-
133
- if (decodedToken) {
134
- dispatch(setMasterpassRestTokenData(decodedToken));
97
+ const decodedToken = window.Utils.decodeJwt(result.data.token);
98
+ dispatch(setMasterpassRestTokenData(decodedToken));
135
99
 
136
- if (decodedToken?.AuthenticationMethod?.toLowerCase() === 'none') {
137
- dispatch(updatePaymentState({ useThreeD: false }));
138
- }
100
+ if (decodedToken?.AuthenticationMethod?.toLowerCase() === 'none') {
101
+ dispatch(updatePaymentState({ useThreeD: false }));
102
+ }
139
103
 
140
- if (options?.three_d !== false && !options?.skipReset) {
141
- stableOnTokenReady(decodedToken);
142
- }
104
+ if (options?.three_d !== false && !options?.skipReset) {
105
+ stableOnTokenReady(decodedToken);
143
106
  }
144
107
  }
145
108
 
package/src/index.ts CHANGED
@@ -19,7 +19,6 @@ export * from './utils/reward-utils';
19
19
  export * from './utils/card-utils';
20
20
  export * from './utils/validation-schemas';
21
21
  export * from './utils/masterpass-sdk';
22
- export * from './utils/wait-for-utils-ready';
23
22
  export type { MasterpassEnvironment } from './utils/constants';
24
23
 
25
24
  export * from './types/payment.types';
@@ -9,7 +9,7 @@ export const formatCardNumber = (value: string): string => {
9
9
 
10
10
  const formatted = cleanValue.replace(/(\d{4})(?=\d)/g, '$1 ');
11
11
 
12
- return formatted.substring(0, 19);
12
+ return formatted.substring(0, 23);
13
13
  };
14
14
 
15
15
  export const formatExpiryDate = (value: string): string => {
@@ -1,64 +0,0 @@
1
- const DEFAULT_TIMEOUT_MS = 10000;
2
- const POLL_INTERVAL_MS = 100;
3
-
4
- interface MasterpassUtils {
5
- decodeJwt: (token: string) => any;
6
- [key: string]: any;
7
- }
8
-
9
- // `window.Utils` is attached by the Masterpass REST SDK and is undefined until
10
- // that script finishes loading, so it is resolved through this guarded getter
11
- // rather than accessed directly.
12
- const getMasterpassUtils = (): MasterpassUtils | undefined =>
13
- (window as unknown as { Utils?: MasterpassUtils }).Utils;
14
-
15
- // The Masterpass REST SDK (`/masterpass-javascript-sdk-web.min.js`) attaches
16
- // both `window.Masterpass` and `window.Utils` when it finishes loading. That
17
- // script is loaded asynchronously (see `useMasterpassScript`), so on slow
18
- // networks the `/orders/masterpass-token` response can arrive before the SDK
19
- // is ready. Calling `window.Utils.decodeJwt(...)` at that point crashes the
20
- // checkout with `TypeError: Cannot read properties of undefined (reading
21
- // 'decodeJwt')` (ZERO-4362 — same class of bug already fixed in the non-rest
22
- // `@akinon/pz-masterpass` package).
23
- //
24
- // This helper polls until the SDK has exposed `window.Utils.decodeJwt`, so
25
- // callers can await readiness before decoding instead of crashing.
26
- export const waitForUtilsReady = (
27
- timeoutMs: number = DEFAULT_TIMEOUT_MS
28
- ): Promise<boolean> =>
29
- new Promise((resolve) => {
30
- const start = Date.now();
31
-
32
- const check = () => {
33
- if (typeof getMasterpassUtils()?.decodeJwt === 'function') {
34
- resolve(true);
35
- return;
36
- }
37
-
38
- if (Date.now() - start >= timeoutMs) {
39
- resolve(false);
40
- return;
41
- }
42
-
43
- setTimeout(check, POLL_INTERVAL_MS);
44
- };
45
-
46
- check();
47
- });
48
-
49
- // Guarded `decodeJwt` wrapper. Returns `undefined` (instead of throwing) when
50
- // the SDK is not yet available, so every callsite degrades gracefully.
51
- export const decodeMasterpassToken = <T = any>(
52
- token: string
53
- ): T | undefined => {
54
- const utils = getMasterpassUtils();
55
-
56
- if (typeof utils?.decodeJwt !== 'function') {
57
- console.error(
58
- '[pz-masterpass-rest] window.Utils.decodeJwt is not available'
59
- );
60
- return undefined;
61
- }
62
-
63
- return utils.decodeJwt(token);
64
- };