@akinon/next 1.111.0-snapshot-ZERO-3792-20251107072714 → 1.111.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,10 +1,11 @@
1
1
  # @akinon/next
2
2
 
3
- ## 1.111.0-snapshot-ZERO-3792-20251107072714
3
+ ## 1.111.0
4
4
 
5
5
  ### Minor Changes
6
6
 
7
- - 888fdec: ZERO-3792: Virtual Try On new features are implemented and also basket support implemented.
7
+ - c026300: ZERO-3833: fix masterpass rest conflicts
8
+ - b47b9d8: ZERO-3414: create masterpass-rest package
8
9
 
9
10
  ## 1.110.0
10
11
 
@@ -122,55 +122,6 @@ export async function GET(request: NextRequest) {
122
122
  'X-Virtual-Try-On-Cache-Status': 'MISS'
123
123
  }
124
124
  });
125
- } else if (endpoint === 'job-status') {
126
- const referenceUrl = searchParams.get('reference_url');
127
- if (!referenceUrl) {
128
- return NextResponse.json(
129
- { error: 'reference_url is required' },
130
- { status: 400 }
131
- );
132
- }
133
-
134
- const externalUrl = `${VIRTUAL_TRY_ON_API_URL}/api/async/v1/job-status?reference_url=${encodeURIComponent(referenceUrl)}`;
135
- const clientIP = getClientIP(request);
136
- const locale = getLocaleFromRequest(request);
137
-
138
- const headersToSend = {
139
- Accept: 'application/json',
140
- ...(locale && { 'Accept-Language': locale }),
141
- ...(request.headers.get('authorization') && {
142
- Authorization: request.headers.get('authorization')!
143
- }),
144
- ...(clientIP && {
145
- 'X-Forwarded-For': clientIP
146
- })
147
- };
148
-
149
- const response = await fetch(externalUrl, {
150
- method: 'GET',
151
- headers: headersToSend
152
- });
153
-
154
- let responseData: any;
155
- const responseText = await response.text();
156
-
157
- try {
158
- responseData = responseText ? JSON.parse(responseText) : {};
159
- } catch (parseError) {
160
- return NextResponse.json(
161
- { error: 'Invalid JSON response from job status API' },
162
- { status: 500 }
163
- );
164
- }
165
-
166
- return NextResponse.json(responseData, {
167
- status: response.status,
168
- headers: {
169
- 'Access-Control-Allow-Origin': '*',
170
- 'Access-Control-Allow-Methods': 'GET, POST, PUT, OPTIONS',
171
- 'Access-Control-Allow-Headers': 'Content-Type, Accept, Authorization'
172
- }
173
- });
174
125
  }
175
126
 
176
127
  return NextResponse.json(
@@ -190,32 +141,13 @@ export async function POST(request: NextRequest) {
190
141
  const body = await request.json();
191
142
 
192
143
  let externalUrl: string;
193
- let httpMethod = 'POST';
194
-
195
144
  if (endpoint === 'feedback') {
196
- if (!body.url || typeof body.url !== 'string' || !body.url.trim()) {
197
- return NextResponse.json(
198
- { status: 'error', message: 'URL is required for feedback' },
199
- { status: 400 }
200
- );
201
- }
202
- if (typeof body.feedback !== 'boolean') {
203
- return NextResponse.json(
204
- { status: 'error', message: 'Feedback must be a boolean value' },
205
- { status: 400 }
206
- );
207
- }
208
145
  externalUrl = `${VIRTUAL_TRY_ON_API_URL}/api/v1/feedback`;
209
- httpMethod = 'PUT';
210
- } else if (endpoint === 'async-multiple-try-on') {
211
- externalUrl = `${VIRTUAL_TRY_ON_API_URL}/api/async/v1/multiple-virtual-try-on`;
212
146
  } else {
213
- return NextResponse.json(
214
- { error: 'Invalid endpoint specified' },
215
- { status: 400 }
216
- );
147
+ externalUrl = `${VIRTUAL_TRY_ON_API_URL}/api/v1/virtual-try-on`;
217
148
  }
218
149
 
150
+ const httpMethod = endpoint === 'feedback' ? 'PUT' : 'POST';
219
151
  const clientIP = getClientIP(request);
220
152
  const locale = getLocaleFromRequest(request);
221
153
 
@@ -231,18 +163,13 @@ export async function POST(request: NextRequest) {
231
163
  })
232
164
  };
233
165
 
234
- const fetchOptions: RequestInit = {
166
+ const response = await fetch(externalUrl, {
235
167
  method: httpMethod,
236
- headers: headersToSend
237
- };
238
-
239
- if (httpMethod !== 'GET') {
240
- fetchOptions.body = JSON.stringify(body);
241
- }
242
-
243
- const response = await fetch(externalUrl, fetchOptions);
168
+ headers: headersToSend,
169
+ body: JSON.stringify(body)
170
+ });
244
171
 
245
- let responseData: Record<string, any>;
172
+ let responseData: any;
246
173
  const responseText = await response.text();
247
174
 
248
175
  if (endpoint === 'feedback') {
@@ -346,7 +273,7 @@ export async function PUT(request: NextRequest) {
346
273
  body: JSON.stringify(body)
347
274
  });
348
275
 
349
- const responseData = response?.json() || {};
276
+ const responseData = await response.json();
350
277
 
351
278
  return NextResponse.json(responseData, {
352
279
  status: response.status,
@@ -360,8 +287,7 @@ export async function PUT(request: NextRequest) {
360
287
  return NextResponse.json(
361
288
  {
362
289
  status: 'error',
363
- message: 'Internal server error occurred during feedback submission',
364
- error: (error as Error).message
290
+ message: 'Internal server error occurred during feedback submission'
365
291
  },
366
292
  { status: 500 }
367
293
  );
@@ -23,7 +23,8 @@ enum Plugin {
23
23
  SavedCard = 'pz-saved-card',
24
24
  FlowPayment = 'pz-flow-payment',
25
25
  VirtualTryOn = 'pz-virtual-try-on',
26
- Hepsipay = 'pz-hepsipay'
26
+ Hepsipay = 'pz-hepsipay',
27
+ MasterpassRest = 'pz-masterpass-rest'
27
28
  }
28
29
 
29
30
  export enum Component {
@@ -50,10 +51,10 @@ export enum Component {
50
51
  MultiBasket = 'MultiBasket',
51
52
  SavedCard = 'SavedCardOption',
52
53
  VirtualTryOnPlugin = 'VirtualTryOnPlugin',
53
- BasketVirtualTryOn = 'BasketVirtualTryOn',
54
54
  IyzicoSavedCard = 'IyzicoSavedCardOption',
55
55
  Hepsipay = 'Hepsipay',
56
- FlowPayment = 'FlowPayment'
56
+ FlowPayment = 'FlowPayment',
57
+ MasterpassRest = 'MasterpassRestOption'
57
58
  }
58
59
 
59
60
  const PluginComponents = new Map([
@@ -89,11 +90,9 @@ const PluginComponents = new Map([
89
90
  [Plugin.SavedCard, [Component.SavedCard, Component.IyzicoSavedCard]],
90
91
  [Plugin.SavedCard, [Component.SavedCard]],
91
92
  [Plugin.FlowPayment, [Component.FlowPayment]],
92
- [
93
- Plugin.VirtualTryOn,
94
- [Component.VirtualTryOnPlugin, Component.BasketVirtualTryOn]
95
- ],
96
- [Plugin.Hepsipay, [Component.Hepsipay]]
93
+ [Plugin.VirtualTryOn, [Component.VirtualTryOnPlugin]],
94
+ [Plugin.Hepsipay, [Component.Hepsipay]],
95
+ [Plugin.MasterpassRest, [Component.MasterpassRest]]
97
96
  ]);
98
97
 
99
98
  const getPlugin = (component: Component) => {
@@ -105,10 +104,10 @@ const getPlugin = (component: Component) => {
105
104
  };
106
105
 
107
106
  export default function PluginModule({
108
- component,
109
- props,
110
- children
111
- }: {
107
+ component,
108
+ props,
109
+ children
110
+ }: {
112
111
  component: Component;
113
112
  props?: any;
114
113
  children?: React.ReactNode;
@@ -164,6 +163,8 @@ export default function PluginModule({
164
163
  promise = import(`${'@akinon/pz-flow-payment'}`);
165
164
  } else if (plugin === Plugin.VirtualTryOn) {
166
165
  promise = import(`${'@akinon/pz-virtual-try-on'}`);
166
+ } else if (plugin === Plugin.MasterpassRest) {
167
+ promise = import(`${'@akinon/pz-masterpass-rest'}`);
167
168
  }
168
169
  } catch (error) {
169
170
  logger.error(error);
@@ -16,6 +16,7 @@ const paymentTypeToView = {
16
16
  gpay: 'gpay',
17
17
  loyalty_money: 'loyalty',
18
18
  masterpass: 'credit-card',
19
+ masterpass_rest: 'masterpass-rest',
19
20
  pay_on_delivery: 'pay-on-delivery',
20
21
  redirection: 'redirection',
21
22
  saved_card: 'saved-card'
@@ -21,7 +21,8 @@ export const usePaymentOptions = () => {
21
21
  credit_payment: 'pz-credit-payment',
22
22
  masterpass: 'pz-masterpass',
23
23
  saved_card: 'pz-saved-card',
24
- gpay: 'pz-gpay'
24
+ gpay: 'pz-gpay',
25
+ masterpass_rest: 'pz-masterpass-rest'
25
26
  };
26
27
 
27
28
  const isInitialTypeIncluded = (type: string) => initialTypes.has(type);
@@ -13,7 +13,8 @@ import {
13
13
  withThreeDRedirection,
14
14
  withUrlRedirection,
15
15
  withCompleteWallet,
16
- withWalletCompleteRedirection
16
+ withWalletCompleteRedirection,
17
+ withMasterpassRestCallback
17
18
  } from '.';
18
19
  import { urlLocaleMatcherRegex } from '../utils';
19
20
  import withCurrency from './currency';
@@ -230,226 +231,235 @@ const withPzDefault =
230
231
  withCompleteMasterpass(
231
232
  withSavedCardRedirection(
232
233
  withCompleteWallet(
233
- withWalletCompleteRedirection(
234
- async (
235
- req: PzNextRequest,
236
- event: NextFetchEvent
237
- ) => {
238
- let middlewareResult: NextResponse | void =
239
- NextResponse.next();
240
-
241
- try {
242
- const { locale, prettyUrl, currency } =
243
- req.middlewareParams.rewrites;
244
- const { defaultLocaleValue } =
245
- Settings.localization;
246
- const url = req.nextUrl.clone();
247
- const pathnameWithoutLocale =
248
- url.pathname.replace(
249
- urlLocaleMatcherRegex,
250
- ''
251
- );
252
-
253
- middlewareResult = (await middleware(
254
- req,
255
- event
256
- )) as NextResponse | void;
257
-
258
- let customRewriteUrlDiff = '';
259
-
260
- if (
261
- middlewareResult instanceof NextResponse &&
262
- middlewareResult.headers.get(
263
- 'pz-override-response'
264
- ) &&
265
- middlewareResult.headers.get(
266
- 'x-middleware-rewrite'
267
- )
268
- ) {
269
- const rewriteUrl = new URL(
270
- middlewareResult.headers.get(
271
- 'x-middleware-rewrite'
272
- )
273
- );
274
- const originalUrl = new URL(req.url);
275
- customRewriteUrlDiff =
276
- rewriteUrl.pathname.replace(
277
- originalUrl.pathname,
278
- ''
279
- );
280
- }
234
+ withWalletCompleteRedirection(
235
+ withMasterpassRestCallback(
236
+ async (
237
+ req: PzNextRequest,
238
+ event: NextFetchEvent
239
+ ) => {
240
+ let middlewareResult: NextResponse | void =
241
+ NextResponse.next();
242
+
243
+ try {
244
+ const { locale, prettyUrl, currency } =
245
+ req.middlewareParams.rewrites;
246
+ const { defaultLocaleValue } =
247
+ Settings.localization;
248
+ const url = req.nextUrl.clone();
249
+ const pathnameWithoutLocale =
250
+ url.pathname.replace(
251
+ urlLocaleMatcherRegex,
252
+ ''
253
+ );
254
+
255
+ middlewareResult = (await middleware(
256
+ req,
257
+ event
258
+ )) as NextResponse | void;
259
+
260
+ let customRewriteUrlDiff = '';
261
+
262
+ if (
263
+ middlewareResult instanceof
264
+ NextResponse &&
265
+ middlewareResult.headers.get(
266
+ 'pz-override-response'
267
+ ) &&
268
+ middlewareResult.headers.get(
269
+ 'x-middleware-rewrite'
270
+ )
271
+ ) {
272
+ const rewriteUrl = new URL(
273
+ middlewareResult.headers.get(
274
+ 'x-middleware-rewrite'
275
+ )
276
+ );
277
+ const originalUrl = new URL(req.url);
278
+ customRewriteUrlDiff =
279
+ rewriteUrl.pathname.replace(
280
+ originalUrl.pathname,
281
+ ''
282
+ );
283
+ }
281
284
 
282
- url.basePath = `/${commerceUrl}`;
283
- url.pathname = `/${
284
- locale.length ? `${locale}/` : ''
285
- }${currency}/${customRewriteUrlDiff}${
286
- prettyUrl ?? pathnameWithoutLocale
287
- }`.replace(/\/+/g, '/');
288
-
289
- if (
290
- Settings.usePrettyUrlRoute &&
291
- url.searchParams.toString().length > 0 &&
292
- !Object.entries(ROUTES).find(([, value]) =>
293
- new RegExp(`^${value}/?$`).test(
294
- pathnameWithoutLocale
295
- )
296
- )
297
- ) {
298
- url.pathname =
299
- url.pathname +
300
- (/\/$/.test(url.pathname) ? '' : '/') +
301
- `searchparams|${encodeURIComponent(
302
- url.searchParams.toString()
303
- )}`;
304
- }
285
+ url.basePath = `/${commerceUrl}`;
286
+ url.pathname = `/${
287
+ locale.length ? `${locale}/` : ''
288
+ }${currency}/${customRewriteUrlDiff}${
289
+ prettyUrl ?? pathnameWithoutLocale
290
+ }`.replace(/\/+/g, '/');
291
+
292
+ if (
293
+ Settings.usePrettyUrlRoute &&
294
+ url.searchParams.toString().length > 0 &&
295
+ !Object.entries(ROUTES).find(
296
+ ([, value]) =>
297
+ new RegExp(`^${value}/?$`).test(
298
+ pathnameWithoutLocale
299
+ )
300
+ )
301
+ ) {
302
+ url.pathname =
303
+ url.pathname +
304
+ (/\/$/.test(url.pathname) ? '' : '/') +
305
+ `searchparams|${encodeURIComponent(
306
+ url.searchParams.toString()
307
+ )}`;
308
+ }
305
309
 
306
- Settings.rewrites.forEach((rewrite) => {
307
- url.pathname = url.pathname.replace(
308
- rewrite.source,
309
- rewrite.destination
310
- );
311
- });
312
-
313
- // if middleware.ts has a return value for current url
314
- if (middlewareResult instanceof NextResponse) {
315
- // pz-override-response header is used to prevent 404 page for custom responses.
316
- if (
317
- middlewareResult.headers.get(
318
- 'pz-override-response'
319
- ) !== 'true'
320
- ) {
321
- middlewareResult.headers.set(
322
- 'x-middleware-rewrite',
323
- url.href
324
- );
325
- } else if (
326
- middlewareResult.headers.get(
327
- 'x-middleware-rewrite'
328
- ) &&
329
- middlewareResult.headers.get(
330
- 'pz-override-response'
331
- ) === 'true'
332
- ) {
333
- middlewareResult =
334
- NextResponse.rewrite(url);
335
- }
336
- } else {
337
- // if middleware.ts doesn't have a return value.
338
- // e.g. NextResponse.next() doesn't exist in middleware.ts
310
+ Settings.rewrites.forEach((rewrite) => {
311
+ url.pathname = url.pathname.replace(
312
+ rewrite.source,
313
+ rewrite.destination
314
+ );
315
+ });
316
+
317
+ // if middleware.ts has a return value for current url
318
+ if (
319
+ middlewareResult instanceof NextResponse
320
+ ) {
321
+ // pz-override-response header is used to prevent 404 page for custom responses.
322
+ if (
323
+ middlewareResult.headers.get(
324
+ 'pz-override-response'
325
+ ) !== 'true'
326
+ ) {
327
+ middlewareResult.headers.set(
328
+ 'x-middleware-rewrite',
329
+ url.href
330
+ );
331
+ } else if (
332
+ middlewareResult.headers.get(
333
+ 'x-middleware-rewrite'
334
+ ) &&
335
+ middlewareResult.headers.get(
336
+ 'pz-override-response'
337
+ ) === 'true'
338
+ ) {
339
+ middlewareResult =
340
+ NextResponse.rewrite(url);
341
+ }
342
+ } else {
343
+ // if middleware.ts doesn't have a return value.
344
+ // e.g. NextResponse.next() doesn't exist in middleware.ts
339
345
 
340
- middlewareResult = NextResponse.rewrite(url);
341
- }
346
+ middlewareResult =
347
+ NextResponse.rewrite(url);
348
+ }
342
349
 
343
- const { localeUrlStrategy } =
344
- Settings.localization;
345
-
346
- const fallbackHost =
347
- req.headers.get('x-forwarded-host') ||
348
- req.headers.get('host');
349
- const hostname =
350
- process.env.NEXT_PUBLIC_URL ||
351
- `https://${fallbackHost}`;
352
- const rootHostname =
353
- localeUrlStrategy ===
354
- LocaleUrlStrategy.Subdomain
355
- ? getRootHostname(hostname)
356
- : null;
357
-
358
- if (
359
- !url.pathname.startsWith(
360
- `/${currency}/orders`
361
- )
362
- ) {
363
- middlewareResult.cookies.set(
364
- 'pz-locale',
365
- locale?.length > 0
366
- ? locale
367
- : defaultLocaleValue,
368
- {
369
- domain: rootHostname,
370
- sameSite: 'none',
371
- secure: true,
372
- expires: new Date(
373
- Date.now() + 1000 * 60 * 60 * 24 * 7
374
- ) // 7 days
350
+ const { localeUrlStrategy } =
351
+ Settings.localization;
352
+
353
+ const fallbackHost =
354
+ req.headers.get('x-forwarded-host') ||
355
+ req.headers.get('host');
356
+ const hostname =
357
+ process.env.NEXT_PUBLIC_URL ||
358
+ `https://${fallbackHost}`;
359
+ const rootHostname =
360
+ localeUrlStrategy ===
361
+ LocaleUrlStrategy.Subdomain
362
+ ? getRootHostname(hostname)
363
+ : null;
364
+
365
+ if (
366
+ !url.pathname.startsWith(
367
+ `/${currency}/orders`
368
+ )
369
+ ) {
370
+ middlewareResult.cookies.set(
371
+ 'pz-locale',
372
+ locale?.length > 0
373
+ ? locale
374
+ : defaultLocaleValue,
375
+ {
376
+ domain: rootHostname,
377
+ sameSite: 'none',
378
+ secure: true,
379
+ expires: new Date(
380
+ Date.now() + 1000 * 60 * 60 * 24 * 7
381
+ ) // 7 days
382
+ }
383
+ );
375
384
  }
376
- );
377
- }
378
385
 
379
- middlewareResult.cookies.set(
380
- 'pz-currency',
381
- currency,
382
- {
383
- domain: rootHostname,
384
- sameSite: 'none',
385
- secure: true,
386
- expires: new Date(
387
- Date.now() + 1000 * 60 * 60 * 24 * 7
388
- ) // 7 days
389
- }
390
- );
391
-
392
- if (
393
- req.cookies.get('pz-locale') &&
394
- req.cookies.get('pz-locale').value !== locale
395
- ) {
396
- logger.debug('Locale changed', {
397
- locale,
398
- oldLocale:
399
- req.cookies.get('pz-locale')?.value,
400
- ip
401
- });
402
- }
386
+ middlewareResult.cookies.set(
387
+ 'pz-currency',
388
+ currency,
389
+ {
390
+ domain: rootHostname,
391
+ sameSite: 'none',
392
+ secure: true,
393
+ expires: new Date(
394
+ Date.now() + 1000 * 60 * 60 * 24 * 7
395
+ ) // 7 days
396
+ }
397
+ );
403
398
 
404
- middlewareResult.headers.set(
405
- 'pz-url',
406
- req.nextUrl.toString()
407
- );
399
+ if (
400
+ req.cookies.get('pz-locale') &&
401
+ req.cookies.get('pz-locale').value !==
402
+ locale
403
+ ) {
404
+ logger.debug('Locale changed', {
405
+ locale,
406
+ oldLocale:
407
+ req.cookies.get('pz-locale')?.value,
408
+ ip
409
+ });
410
+ }
408
411
 
409
- if (req.cookies.get('pz-set-currency')) {
410
- middlewareResult.cookies.delete(
411
- 'pz-set-currency'
412
- );
413
- }
412
+ middlewareResult.headers.set(
413
+ 'pz-url',
414
+ req.nextUrl.toString()
415
+ );
414
416
 
415
- if (process.env.ACC_APP_VERSION) {
416
- middlewareResult.headers.set(
417
- 'acc-app-version',
418
- process.env.ACC_APP_VERSION
419
- );
420
- }
417
+ if (req.cookies.get('pz-set-currency')) {
418
+ middlewareResult.cookies.delete(
419
+ 'pz-set-currency'
420
+ );
421
+ }
421
422
 
422
- // Set CSRF token if not set
423
- try {
424
- const url = `${Settings.commerceUrl}${user.csrfToken}`;
423
+ if (process.env.ACC_APP_VERSION) {
424
+ middlewareResult.headers.set(
425
+ 'acc-app-version',
426
+ process.env.ACC_APP_VERSION
427
+ );
428
+ }
425
429
 
426
- if (!req.cookies.get('csrftoken')) {
427
- const { csrf_token } = await (
428
- await fetch(url)
429
- ).json();
430
- middlewareResult.cookies.set(
431
- 'csrftoken',
432
- csrf_token,
433
- {
434
- domain: rootHostname
430
+ // Set CSRF token if not set
431
+ try {
432
+ const url = `${Settings.commerceUrl}${user.csrfToken}`;
433
+
434
+ if (!req.cookies.get('csrftoken')) {
435
+ const { csrf_token } = await (
436
+ await fetch(url)
437
+ ).json();
438
+ middlewareResult.cookies.set(
439
+ 'csrftoken',
440
+ csrf_token,
441
+ {
442
+ domain: rootHostname
443
+ }
444
+ );
435
445
  }
436
- );
446
+ } catch (error) {
447
+ logger.error('CSRF Error', {
448
+ error,
449
+ ip
450
+ });
451
+ }
452
+ } catch (error) {
453
+ logger.error('withPzDefault Error', {
454
+ error,
455
+ ip
456
+ });
437
457
  }
438
- } catch (error) {
439
- logger.error('CSRF Error', {
440
- error,
441
- ip
442
- });
458
+
459
+ return middlewareResult;
443
460
  }
444
- } catch (error) {
445
- logger.error('withPzDefault Error', {
446
- error,
447
- ip
448
- });
449
- }
450
-
451
- return middlewareResult;
452
- }
461
+ )
462
+ )
453
463
  )
454
464
  )
455
465
  )
@@ -461,7 +471,6 @@ const withPzDefault =
461
471
  )
462
472
  )
463
473
  )
464
- )
465
474
  )(req, event);
466
475
  };
467
476
 
@@ -11,6 +11,7 @@ import withCheckoutProvider from './checkout-provider';
11
11
  import withSavedCardRedirection from './saved-card-redirection';
12
12
  import withCompleteWallet from './complete-wallet';
13
13
  import withWalletCompleteRedirection from './wallet-complete-redirection';
14
+ import withMasterpassRestCallback from './masterpass-rest-callback';
14
15
  import { NextRequest } from 'next/server';
15
16
 
16
17
  export {
@@ -26,7 +27,8 @@ export {
26
27
  withCheckoutProvider,
27
28
  withSavedCardRedirection,
28
29
  withCompleteWallet,
29
- withWalletCompleteRedirection
30
+ withWalletCompleteRedirection,
31
+ withMasterpassRestCallback
30
32
  };
31
33
 
32
34
  export interface PzNextRequest extends NextRequest {
@@ -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.111.0",
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.111.0",
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
@@ -46,3 +46,8 @@ declare module '@akinon/pz-cybersource-uc' {
46
46
  }
47
47
 
48
48
  declare module '@akinon/pz-flow-payment' {}
49
+
50
+ declare module '@akinon/pz-masterpass-rest' {
51
+ export const MasterpassRestOption: React.FC;
52
+ export const masterpassRestReducer: any;
53
+ }
package/plugins.js CHANGED
@@ -19,5 +19,6 @@ module.exports = [
19
19
  'pz-cybersource-uc',
20
20
  'pz-hepsipay',
21
21
  'pz-flow-payment',
22
- 'pz-virtual-try-on'
22
+ 'pz-virtual-try-on',
23
+ 'pz-masterpass-rest'
23
24
  ];
@@ -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;