@akinon/next 1.111.0-snapshot-ZERO-3792-20251107072714 → 1.112.0-rc.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.
@@ -0,0 +1,218 @@
1
+ import { NextFetchEvent, NextMiddleware, NextResponse } from 'next/server';
2
+ import Settings from 'settings';
3
+ import logger from '../utils/log';
4
+ import { getUrlPathWithLocale } from '../utils/localization';
5
+ import { PzNextRequest } from '.';
6
+
7
+ const withMasterpassRestCallback =
8
+ (middleware: NextMiddleware) =>
9
+ async (req: PzNextRequest, event: NextFetchEvent) => {
10
+ const url = req.nextUrl.clone();
11
+ const ip = req.headers.get('x-forwarded-for') ?? '';
12
+ const sessionId = req.cookies.get('osessionid');
13
+
14
+ if (!url.pathname.includes('/orders/masterpass-rest-callback')) {
15
+ return middleware(req, event);
16
+ }
17
+
18
+ if (req.method !== 'POST') {
19
+ logger.warn('Invalid request method for masterpass REST callback', {
20
+ middleware: 'masterpass-rest-callback',
21
+ method: req.method,
22
+ ip
23
+ });
24
+
25
+ return NextResponse.redirect(
26
+ `${url.origin}${getUrlPathWithLocale(
27
+ '/orders/checkout/',
28
+ req.cookies.get('pz-locale')?.value
29
+ )}`,
30
+ 303
31
+ );
32
+ }
33
+
34
+ const responseCode = url.searchParams.get('responseCode');
35
+ const token = url.searchParams.get('token');
36
+
37
+ if (!responseCode || !token) {
38
+ logger.warn('Missing required parameters for masterpass REST callback', {
39
+ middleware: 'masterpass-rest-callback',
40
+ responseCode,
41
+ token,
42
+ ip
43
+ });
44
+
45
+ return NextResponse.redirect(
46
+ `${url.origin}${getUrlPathWithLocale(
47
+ '/orders/checkout/',
48
+ req.cookies.get('pz-locale')?.value
49
+ )}`,
50
+ 303
51
+ );
52
+ }
53
+
54
+ try {
55
+ const formData = await req.formData();
56
+ const body: Record<string, string> = {};
57
+
58
+ Array.from(formData.entries()).forEach(([key, value]) => {
59
+ body[key] = value.toString();
60
+ });
61
+
62
+ if (!sessionId) {
63
+ logger.warn(
64
+ 'Make sure that the SESSION_COOKIE_SAMESITE environment variable is set to None in Commerce.',
65
+ {
66
+ middleware: 'masterpass-rest-callback',
67
+ ip
68
+ }
69
+ );
70
+
71
+ return NextResponse.redirect(
72
+ `${url.origin}${getUrlPathWithLocale(
73
+ '/orders/checkout/',
74
+ req.cookies.get('pz-locale')?.value
75
+ )}`,
76
+ 303
77
+ );
78
+ }
79
+
80
+ const requestUrl = new URL('/orders/checkout/', Settings.commerceUrl);
81
+ requestUrl.searchParams.set('page', 'MasterpassRestCompletePage');
82
+ requestUrl.searchParams.set('responseCode', responseCode);
83
+ requestUrl.searchParams.set('token', token);
84
+ requestUrl.searchParams.set(
85
+ 'three_d_secure',
86
+ body.transactionType?.includes('3D') ? 'true' : 'false'
87
+ );
88
+ requestUrl.searchParams.set(
89
+ 'transactionType',
90
+ body.transactionType || ''
91
+ );
92
+
93
+ const requestHeaders = {
94
+ 'Content-Type': 'application/x-www-form-urlencoded',
95
+ 'X-Requested-With': 'XMLHttpRequest',
96
+ Cookie: req.headers.get('cookie') ?? '',
97
+ 'x-currency': req.cookies.get('pz-currency')?.value ?? '',
98
+ 'x-forwarded-for': ip,
99
+ 'User-Agent': req.headers.get('user-agent') ?? ''
100
+ };
101
+
102
+ const request = await fetch(requestUrl.toString(), {
103
+ method: 'POST',
104
+ headers: requestHeaders,
105
+ body: new URLSearchParams(body)
106
+ });
107
+
108
+ logger.info('Masterpass REST callback request', {
109
+ requestUrl: requestUrl.toString(),
110
+ status: request.status,
111
+ requestHeaders,
112
+ ip
113
+ });
114
+
115
+ const response = await request.json();
116
+
117
+ const { context_list: contextList, errors } = response;
118
+
119
+ let redirectUrl = response.redirect_url;
120
+
121
+ if (!redirectUrl && contextList && contextList.length > 0) {
122
+ for (const context of contextList) {
123
+ if (context.page_context && context.page_context.redirect_url) {
124
+ redirectUrl = context.page_context.redirect_url;
125
+ break;
126
+ }
127
+ }
128
+ }
129
+
130
+ if (errors && Object.keys(errors).length) {
131
+ logger.error('Error while processing masterpass REST callback', {
132
+ middleware: 'masterpass-rest-callback',
133
+ errors,
134
+ requestHeaders,
135
+ ip
136
+ });
137
+
138
+ return NextResponse.redirect(
139
+ `${url.origin}${getUrlPathWithLocale(
140
+ '/orders/checkout/',
141
+ req.cookies.get('pz-locale')?.value
142
+ )}`,
143
+ {
144
+ status: 303,
145
+ headers: {
146
+ 'Set-Cookie': `pz-pos-error=${JSON.stringify(errors)}; path=/;`
147
+ }
148
+ }
149
+ );
150
+ }
151
+
152
+ logger.info('Masterpass REST callback response', {
153
+ middleware: 'masterpass-rest-callback',
154
+ contextList,
155
+ redirectUrl,
156
+ ip
157
+ });
158
+
159
+ if (!redirectUrl) {
160
+ logger.warn(
161
+ 'No redirection url found in response. Redirecting to checkout page.',
162
+ {
163
+ middleware: 'masterpass-rest-callback',
164
+ requestHeaders,
165
+ response: JSON.stringify(response),
166
+ ip
167
+ }
168
+ );
169
+
170
+ const redirectUrlWithLocale = `${url.origin}${getUrlPathWithLocale(
171
+ '/orders/checkout/',
172
+ req.cookies.get('pz-locale')?.value
173
+ )}`;
174
+
175
+ return NextResponse.redirect(redirectUrlWithLocale, 303);
176
+ }
177
+
178
+ const redirectUrlWithLocale = `${url.origin}${getUrlPathWithLocale(
179
+ redirectUrl,
180
+ req.cookies.get('pz-locale')?.value
181
+ )}`;
182
+
183
+ logger.info('Redirecting after masterpass REST callback', {
184
+ middleware: 'masterpass-rest-callback',
185
+ redirectUrlWithLocale,
186
+ ip
187
+ });
188
+
189
+ const nextResponse = NextResponse.redirect(redirectUrlWithLocale, 303);
190
+
191
+ nextResponse.headers.set(
192
+ 'Set-Cookie',
193
+ request.headers.get('set-cookie') ?? ''
194
+ );
195
+
196
+ return nextResponse;
197
+ } catch (error) {
198
+ logger.error('Error while processing masterpass REST callback', {
199
+ middleware: 'masterpass-rest-callback',
200
+ error,
201
+ requestHeaders: {
202
+ Cookie: req.headers.get('cookie') ?? '',
203
+ 'x-currency': req.cookies.get('pz-currency')?.value ?? ''
204
+ },
205
+ ip
206
+ });
207
+
208
+ return NextResponse.redirect(
209
+ `${url.origin}${getUrlPathWithLocale(
210
+ '/orders/checkout/',
211
+ req.cookies.get('pz-locale')?.value
212
+ )}`,
213
+ 303
214
+ );
215
+ }
216
+ };
217
+
218
+ export default withMasterpassRestCallback;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@akinon/next",
3
3
  "description": "Core package for Project Zero Next",
4
- "version": "1.111.0-snapshot-ZERO-3792-20251107072714",
4
+ "version": "1.112.0-rc.17",
5
5
  "private": false,
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -35,7 +35,7 @@
35
35
  "set-cookie-parser": "2.6.0"
36
36
  },
37
37
  "devDependencies": {
38
- "@akinon/eslint-plugin-projectzero": "1.111.0-snapshot-ZERO-3792-20251107072714",
38
+ "@akinon/eslint-plugin-projectzero": "1.112.0-rc.17",
39
39
  "@babel/core": "7.26.10",
40
40
  "@babel/preset-env": "7.26.9",
41
41
  "@babel/preset-typescript": "7.27.0",
package/plugins.d.ts CHANGED
@@ -37,6 +37,16 @@ declare module '@akinon/pz-cybersource-uc/src/redux/middleware' {
37
37
  export default middleware as any;
38
38
  }
39
39
 
40
+ declare module '@akinon/pz-apple-pay' {}
41
+
42
+ declare module '@akinon/pz-similar-products' {
43
+ export const SimilarProductsModal: any;
44
+ export const SimilarProductsFilterSidebar: any;
45
+ export const SimilarProductsResultsGrid: any;
46
+ export const SimilarProductsPlugin: any;
47
+ export const SimilarProductsButtonPlugin: any;
48
+ }
49
+
40
50
  declare module '@akinon/pz-cybersource-uc/src/redux/reducer' {
41
51
  export default reducer as any;
42
52
  }
@@ -46,3 +56,8 @@ declare module '@akinon/pz-cybersource-uc' {
46
56
  }
47
57
 
48
58
  declare module '@akinon/pz-flow-payment' {}
59
+
60
+ declare module '@akinon/pz-masterpass-rest' {
61
+ export const MasterpassRestOption: React.FC;
62
+ export const masterpassRestReducer: any;
63
+ }
package/plugins.js CHANGED
@@ -16,8 +16,10 @@ module.exports = [
16
16
  'pz-tabby-extension',
17
17
  'pz-apple-pay',
18
18
  'pz-tamara-extension',
19
+ 'pz-similar-products',
19
20
  'pz-cybersource-uc',
20
21
  'pz-hepsipay',
21
22
  'pz-flow-payment',
22
- 'pz-virtual-try-on'
23
+ 'pz-virtual-try-on',
24
+ 'pz-masterpass-rest'
23
25
  ];
@@ -208,7 +208,10 @@ export const contextListMiddleware: Middleware = ({
208
208
  dispatch(setCardType(context.page_context.card_type));
209
209
  }
210
210
 
211
- if (context.page_context.installments) {
211
+ if (
212
+ context.page_context.installments &&
213
+ preOrder?.payment_option?.payment_type !== 'masterpass_rest'
214
+ ) {
212
215
  dispatch(setInstallmentOptions(context.page_context.installments));
213
216
  }
214
217
  }
@@ -20,6 +20,7 @@ export const installmentOptionMiddleware: Middleware = ({
20
20
  if (
21
21
  !preOrder?.installment &&
22
22
  preOrder?.payment_option?.payment_type !== 'saved_card' &&
23
+ preOrder?.payment_option?.payment_type !== 'masterpass_rest' &&
23
24
  installmentOptions.length > 0
24
25
  ) {
25
26
  const firstInstallmentOptionPk = installmentOptions[0]?.pk;
@@ -9,6 +9,7 @@ import { masterpassReducer } from '@akinon/pz-masterpass';
9
9
  import { otpReducer } from '@akinon/pz-otp';
10
10
  import { savedCardReducer } from '@akinon/pz-saved-card';
11
11
  import cyberSourceUcReducer from '@akinon/pz-cybersource-uc/src/redux/reducer';
12
+ import { masterpassRestReducer } from '@akinon/pz-masterpass-rest';
12
13
 
13
14
  const fallbackReducer = (state = {}) => state;
14
15
 
@@ -21,7 +22,8 @@ const reducers = {
21
22
  masterpass: masterpassReducer || fallbackReducer,
22
23
  otp: otpReducer || fallbackReducer,
23
24
  savedCard: savedCardReducer || fallbackReducer,
24
- cybersource_uc: cyberSourceUcReducer || fallbackReducer
25
+ cybersource_uc: cyberSourceUcReducer || fallbackReducer,
26
+ masterpassRest: masterpassRestReducer || fallbackReducer
25
27
  };
26
28
 
27
29
  export default reducers;
package/types/index.ts CHANGED
@@ -83,6 +83,12 @@ export interface Settings {
83
83
  };
84
84
  usePrettyUrlRoute?: boolean;
85
85
  commerceUrl: string;
86
+ /**
87
+ * This option allows you to track Sentry events on the client side, in addition to server and edge environments.
88
+ *
89
+ * It overrides process.env.NEXT_PUBLIC_SENTRY_DSN and process.env.SENTRY_DSN.
90
+ */
91
+ sentryDsn?: string;
86
92
  redis: {
87
93
  defaultExpirationTime: number;
88
94
  };
@@ -1,8 +1,14 @@
1
1
  const iframeURLChange = (iframe, callback) => {
2
2
  iframe.addEventListener('load', () => {
3
3
  setTimeout(() => {
4
- if (iframe?.contentWindow?.location) {
5
- callback(iframe.contentWindow.location);
4
+ try {
5
+ if (iframe?.contentWindow?.location) {
6
+ const iframeLocation = iframe.contentWindow.location;
7
+
8
+ callback(iframeLocation);
9
+ }
10
+ } catch (error) {
11
+ // Expected: browser blocks cross-origin iframe access for security
6
12
  }
7
13
  }, 0);
8
14
  });
@@ -1,8 +1,14 @@
1
1
  const iframeURLChange = (iframe, callback) => {
2
2
  iframe.addEventListener('load', () => {
3
3
  setTimeout(() => {
4
- if (iframe?.contentWindow?.location) {
5
- callback(iframe.contentWindow.location);
4
+ try {
5
+ if (iframe?.contentWindow?.location) {
6
+ const iframeLocation = iframe.contentWindow.location;
7
+
8
+ callback(iframeLocation);
9
+ }
10
+ } catch (error) {
11
+ // Expected: browser blocks cross-origin iframe access for security
6
12
  }
7
13
  }, 0);
8
14
  });