@akinon/next 1.118.0 → 1.119.0-rc.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,5 +1,31 @@
1
1
  # @akinon/next
2
2
 
3
+ ## 1.119.0-rc.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 31b4a266: ZERO-4102: Add support for post-checkout redirect in middleware
8
+ - d2c0e759: ZERO-3684: Handle cross-origin iframe access in iframeURLChange function
9
+ - aef81c5d: ZERO-4034: Refactor checkout API to use dynamic store imports for improved performance
10
+ - b55acb76: ZERO-2577: Fix pagination bug and update usePagination hook and ensure pagination controls rendering correctly
11
+ - 8a7fd0f4: ZERO-4065: Add '[segment]' to skipSegments in route generation
12
+ - 36143125: ZERO-3987: Add barcode scanner functionality with modal and button
13
+ - 0754c835: ZERO-4063: Add support for optional Redis password in cache handlers
14
+ - f7e0f646: ZERO-4032: Add bfcache-headers middleware, integrate it into the default chain, and introduce a new environment variable for control.
15
+ - 94a86fcc: ZERO-4065: Expand skipSegments array to include additional segments for route generation
16
+ - 143be2b9: ZERO-3457: Crop styles are customizable and logic improved for rendering similar products modal
17
+ - 9f8cd3bc: ZERO-3449: AI Search Active Filters & Crop Style changes have been implemented
18
+ - 49c82e1a: ZERO-4047: Remove Sentry configuration from default Next.js config
19
+ - a9f5cdb1: ZERO-4102: Fix post-checkout condition to additionally check for empty search parameters.
20
+ - d99a6a7d: ZERO-3457_1: Fixed the settings prop and made sure everything is customizable.
21
+ - d7e5178b: ZERO-3985: Add query string handling for orders redirection in middleware
22
+ - 591e345e: ZERO-3855: Enhance credit card payment handling in checkout middlewares
23
+ - 01ee41f1: ZERO-4102: Implement a post-checkout flow by dynamically determining checkout paths and managing a pz-post-checkout-flow cookie.
24
+ - 4de5303c: ZERO-2504: add cookie filter to api client request
25
+ - b59fdd1c: ZERO-4009: Add password reset token validation
26
+ - 95b139dc: ZERO-3795: Remove duplicate entry for SavedCard in PluginComponents map
27
+ - 3909d322: Edit the duplicate Plugin.SimilarProducts in the plugin-module.
28
+
3
29
  ## 1.118.0
4
30
 
5
31
  ### Minor Changes
@@ -0,0 +1,59 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import Settings from 'settings';
3
+
4
+ export async function GET(request: NextRequest) {
5
+ try {
6
+ const { searchParams } = new URL(request.url);
7
+ const barcode = searchParams.get('search_text');
8
+
9
+ if (!barcode) {
10
+ return NextResponse.json(
11
+ { error: 'Missing search_text parameter (barcode)' },
12
+ { status: 400 }
13
+ );
14
+ }
15
+
16
+ if (Settings.commerceUrl === 'default') {
17
+ return NextResponse.json(
18
+ { error: 'Commerce URL is not configured' },
19
+ { status: 500 }
20
+ );
21
+ }
22
+
23
+ const queryParams = new URLSearchParams();
24
+ queryParams.append('search_text', barcode);
25
+
26
+ searchParams.forEach((value, key) => {
27
+ if (key !== 'search_text') {
28
+ queryParams.append(key, value);
29
+ }
30
+ });
31
+
32
+ const apiUrl = `${Settings.commerceUrl}/list/?${queryParams.toString()}`;
33
+
34
+ const headers: Record<string, string> = {
35
+ Accept: 'application/json',
36
+ 'Content-Type': 'application/json'
37
+ };
38
+
39
+ const response = await fetch(apiUrl, {
40
+ method: 'GET',
41
+ headers
42
+ });
43
+
44
+ if (!response.ok) {
45
+ return NextResponse.json(
46
+ { error: `API request failed with status: ${response.status}` },
47
+ { status: response.status }
48
+ );
49
+ }
50
+
51
+ const data = await response.json();
52
+ return NextResponse.json(data);
53
+ } catch (error) {
54
+ return NextResponse.json(
55
+ { error: (error as Error).message },
56
+ { status: 500 }
57
+ );
58
+ }
59
+ }
@@ -25,7 +25,16 @@ const generateRoutes = () => {
25
25
  const routes = [];
26
26
  const excludedDirs = ['api', 'pz-not-found'];
27
27
 
28
- const skipSegments = ['[commerce]', '[locale]', '[currency]'];
28
+ const skipSegments = [
29
+ '[commerce]',
30
+ '[locale]',
31
+ '[currency]',
32
+ '[session]',
33
+ '[segment]',
34
+ '[url]',
35
+ '[theme]',
36
+ '[member_type]'
37
+ ];
29
38
  const skipCatchAllRoutes = ['[...prettyurl]', '[...not_found]'];
30
39
 
31
40
  const walkDirectory = (dir, basePath = '') => {
@@ -55,6 +55,7 @@ export enum Component {
55
55
  SavedCard = 'SavedCardOption',
56
56
  VirtualTryOnPlugin = 'VirtualTryOnPlugin',
57
57
  BasketVirtualTryOn = 'BasketVirtualTryOn',
58
+ BarcodeScannerPlugin = 'BarcodeScannerPlugin',
58
59
  IyzicoSavedCard = 'IyzicoSavedCardOption',
59
60
  Hepsipay = 'Hepsipay',
60
61
  FlowPayment = 'FlowPayment',
@@ -113,11 +114,10 @@ const PluginComponents = new Map([
113
114
  ]
114
115
  ],
115
116
  [Plugin.SavedCard, [Component.SavedCard, Component.IyzicoSavedCard]],
116
- [Plugin.SavedCard, [Component.SavedCard]],
117
117
  [Plugin.FlowPayment, [Component.FlowPayment]],
118
118
  [
119
119
  Plugin.VirtualTryOn,
120
- [Component.VirtualTryOnPlugin, Component.BasketVirtualTryOn]
120
+ [Component.VirtualTryOnPlugin, Component.BasketVirtualTryOn, Component.BarcodeScannerPlugin]
121
121
  ],
122
122
  [Plugin.Hepsipay, [Component.Hepsipay]],
123
123
  [Plugin.MasterpassRest, [Component.MasterpassRest]],
@@ -77,6 +77,10 @@ interface LoyaltyTransactions {
77
77
  }[];
78
78
  }
79
79
 
80
+ interface PasswordResetValidateResponse {
81
+ validlink: boolean;
82
+ }
83
+
80
84
  const accountApi = api.injectEndpoints({
81
85
  endpoints: (builder) => ({
82
86
  updatePassword: builder.mutation<void, AccountChangePasswordFormType>({
@@ -221,6 +225,12 @@ const accountApi = api.injectEndpoints({
221
225
  }),
222
226
  getLoyaltyTransactions: builder.query<LoyaltyTransactions, void>({
223
227
  query: () => buildClientRequestUrl(account.loyaltyTransactions)
228
+ }),
229
+ getValidatePasswordResetToken: builder.query<
230
+ PasswordResetValidateResponse,
231
+ string
232
+ >({
233
+ query: (slug) => buildClientRequestUrl(account.passwordReset(slug))
224
234
  })
225
235
  }),
226
236
  overrideExisting: true
@@ -247,5 +257,6 @@ export const {
247
257
  usePasswordResetMutation,
248
258
  useAnonymizeMutation,
249
259
  useGetLoyaltyBalanceQuery,
250
- useGetLoyaltyTransactionsQuery
260
+ useGetLoyaltyTransactionsQuery,
261
+ useGetValidatePasswordResetTokenQuery
251
262
  } = accountApi;
@@ -17,10 +17,10 @@ import {
17
17
  SendSmsType,
18
18
  VerifySmsType
19
19
  } from '../../types';
20
- import { buildClientRequestUrl } from '../../utils';
20
+ import { buildClientRequestUrl, getCookie } from '../../utils';
21
21
  import { api } from './api';
22
22
  import { checkout } from '../urls';
23
- import { AppDispatch, AppStore, store } from 'redux/store';
23
+ import type { AppDispatch, AppStore } from 'redux/store';
24
24
  import settings from 'settings';
25
25
  import { showMobile3dIframe } from '../../utils/mobile-3d-iframe';
26
26
  import {
@@ -33,6 +33,11 @@ import {
33
33
  buildPurchaseForm
34
34
  } from '@akinon/pz-masterpass/src/utils';
35
35
 
36
+ const getStore = async (): Promise<AppStore> => {
37
+ const { store } = await import('redux/store');
38
+ return store;
39
+ };
40
+
36
41
  interface CheckoutResponse {
37
42
  pre_order?: PreOrder;
38
43
  errors: {
@@ -82,6 +87,18 @@ export interface PayOnDeliveryParams {
82
87
  paymentType: string;
83
88
  }
84
89
 
90
+ const buildCheckoutRequestUrl = (
91
+ path: string,
92
+ options?: Parameters<typeof buildClientRequestUrl>[1]
93
+ ) => {
94
+ const effectivePath =
95
+ getCookie('pz-post-checkout-flow') === 'true' &&
96
+ /^\/orders\/checkout(\/|\?|$)/.test(path)
97
+ ? path.replace('/orders/checkout', '/orders/post-checkout')
98
+ : path;
99
+ return buildClientRequestUrl(effectivePath, options);
100
+ };
101
+
85
102
  const completeMasterpassPayment = async (
86
103
  params: CompleteCreditCardParams,
87
104
  dispatch: AppDispatch,
@@ -181,41 +198,41 @@ export const checkoutApi = api.injectEndpoints({
181
198
  endpoints: (build) => ({
182
199
  fetchCheckout: build.query<CheckoutResponse, void>({
183
200
  query: () => ({
184
- url: buildClientRequestUrl(checkout.fetchCheckout, {})
201
+ url: buildCheckoutRequestUrl(checkout.fetchCheckout, {})
185
202
  }),
186
203
  providesTags: ['Checkout']
187
204
  }),
188
205
  resetCheckoutState: build.query<CheckoutResponse, void>({
189
206
  query: () => ({
190
- url: buildClientRequestUrl(checkout.guestLogin, {})
207
+ url: buildCheckoutRequestUrl(checkout.guestLogin, {})
191
208
  })
192
209
  }),
193
210
  fetchCheckoutResult: build.query<{ order: Order }, string>({
194
211
  query: (token: string) =>
195
- buildClientRequestUrl(checkout.fetchCheckoutResult(token))
212
+ buildCheckoutRequestUrl(checkout.fetchCheckoutResult(token))
196
213
  }),
197
214
  get3dRedirectForm: build.query<{ result: string }, void>({
198
215
  query: () =>
199
- buildClientRequestUrl(checkout.get3dRedirectForm, {
216
+ buildCheckoutRequestUrl(checkout.get3dRedirectForm, {
200
217
  responseType: 'text'
201
218
  })
202
219
  }),
203
220
  getContract: build.query<GetContractResponse, string>({
204
221
  query: (slug: string) =>
205
- buildClientRequestUrl(checkout.getContract(slug), {
222
+ buildCheckoutRequestUrl(checkout.getContract(slug), {
206
223
  responseType: 'text'
207
224
  })
208
225
  }),
209
226
  getCoupons: build.query<CheckoutResponse, void>({
210
227
  query: () => ({
211
- url: buildClientRequestUrl(checkout.couponSelectionPage)
228
+ url: buildCheckoutRequestUrl(checkout.couponSelectionPage)
212
229
  })
213
230
  }),
214
231
 
215
232
  setCoupon: build.mutation<CheckoutResponse, { pk: number; action: string }>(
216
233
  {
217
234
  query: ({ pk, action }) => ({
218
- url: buildClientRequestUrl(checkout.couponSelectionPage, {
235
+ url: buildCheckoutRequestUrl(checkout.couponSelectionPage, {
219
236
  useFormData: true
220
237
  }),
221
238
  method: 'POST',
@@ -231,25 +248,32 @@ export const checkoutApi = api.injectEndpoints({
231
248
  CheckoutResponse,
232
249
  CompleteCreditCardParams
233
250
  >({
234
- query: ({
235
- card_holder,
236
- card_cvv,
237
- card_number,
238
- card_month,
239
- card_year,
240
- use_three_d = true,
241
- save = undefined
242
- }) => {
251
+ async queryFn(
252
+ {
253
+ card_holder,
254
+ card_cvv,
255
+ card_number,
256
+ card_month,
257
+ card_year,
258
+ use_three_d = true,
259
+ save = undefined
260
+ },
261
+ _queryApi,
262
+ _extraOptions,
263
+ baseQuery
264
+ ) {
265
+ const reduxStore = await getStore();
243
266
  const paymentOption =
244
- store.getState().checkout?.preOrder?.payment_option;
267
+ reduxStore.getState().checkout?.preOrder?.payment_option;
245
268
 
246
269
  if (paymentOption?.payment_type === 'masterpass') {
247
- return {
248
- url: buildClientRequestUrl(checkout.getMasterpassOrderNo, {
270
+ const result = await baseQuery({
271
+ url: buildCheckoutRequestUrl(checkout.getMasterpassOrderNo, {
249
272
  useFormData: true
250
273
  }),
251
274
  method: 'POST'
252
- };
275
+ });
276
+ return result as { data: CheckoutResponse } | { error: any };
253
277
  }
254
278
 
255
279
  const body: Record<string, string> = {
@@ -266,18 +290,19 @@ export const checkoutApi = api.injectEndpoints({
266
290
  body.save = save ? '1' : '0';
267
291
  }
268
292
 
269
- return {
270
- url: buildClientRequestUrl(checkout.completeCreditCardPayment, {
293
+ const result = await baseQuery({
294
+ url: buildCheckoutRequestUrl(checkout.completeCreditCardPayment, {
271
295
  useFormData: true
272
296
  }),
273
297
  method: 'POST',
274
298
  body
275
- };
299
+ });
300
+ return result as { data: CheckoutResponse } | { error: any };
276
301
  },
277
302
  async onQueryStarted(args, { dispatch, queryFulfilled }) {
278
303
  dispatch(setPaymentStepBusy(true));
279
304
  const { data } = await queryFulfilled;
280
- const reduxStore = (await import('redux/store')).store;
305
+ const reduxStore = await getStore();
281
306
  const completePaymentContext = data?.context_list?.find(
282
307
  (context) => context?.page_name === 'MasterpassCompletePage'
283
308
  );
@@ -302,7 +327,7 @@ export const checkoutApi = api.injectEndpoints({
302
327
  }
303
328
  >({
304
329
  query: ({ token }) => ({
305
- url: buildClientRequestUrl(checkout.completeMasterpassPayment, {
330
+ url: buildCheckoutRequestUrl(checkout.completeMasterpassPayment, {
306
331
  useFormData: true
307
332
  }),
308
333
  method: 'POST',
@@ -319,7 +344,7 @@ export const checkoutApi = api.injectEndpoints({
319
344
  }),
320
345
  completeFundsTransfer: build.mutation<CheckoutResponse, void>({
321
346
  query: () => ({
322
- url: buildClientRequestUrl(checkout.completeFundsTransfer, {
347
+ url: buildCheckoutRequestUrl(checkout.completeFundsTransfer, {
323
348
  useFormData: true
324
349
  }),
325
350
  method: 'POST',
@@ -336,7 +361,7 @@ export const checkoutApi = api.injectEndpoints({
336
361
  }),
337
362
  guestLogin: build.mutation<CheckoutResponse, GuestLoginFormParams>({
338
363
  query: ({ user_email, phone_number }) => ({
339
- url: buildClientRequestUrl(checkout.guestLogin, {
364
+ url: buildCheckoutRequestUrl(checkout.guestLogin, {
340
365
  useFormData: true
341
366
  }),
342
367
  method: 'POST',
@@ -349,7 +374,7 @@ export const checkoutApi = api.injectEndpoints({
349
374
  }),
350
375
  setDeliveryOption: build.mutation<CheckoutResponse, number>({
351
376
  query: (pk: number) => ({
352
- url: buildClientRequestUrl(checkout.setDeliveryOption, {
377
+ url: buildCheckoutRequestUrl(checkout.setDeliveryOption, {
353
378
  useFormData: true
354
379
  }),
355
380
  method: 'POST',
@@ -365,7 +390,7 @@ export const checkoutApi = api.injectEndpoints({
365
390
  }),
366
391
  setAddresses: build.mutation<CheckoutResponse, SetAddressesParams>({
367
392
  query: ({ shippingAddressPk, billingAddressPk }) => ({
368
- url: buildClientRequestUrl(checkout.setAddresses, {
393
+ url: buildCheckoutRequestUrl(checkout.setAddresses, {
369
394
  useFormData: true
370
395
  }),
371
396
  method: 'POST',
@@ -382,7 +407,7 @@ export const checkoutApi = api.injectEndpoints({
382
407
  }),
383
408
  setShippingOption: build.mutation<CheckoutResponse, number>({
384
409
  query: (pk: number) => ({
385
- url: buildClientRequestUrl(checkout.setShippingOption, {
410
+ url: buildCheckoutRequestUrl(checkout.setShippingOption, {
386
411
  useFormData: true
387
412
  }),
388
413
  method: 'POST',
@@ -398,7 +423,7 @@ export const checkoutApi = api.injectEndpoints({
398
423
  }),
399
424
  setDataSourceShippingOptions: build.mutation<CheckoutResponse, number[]>({
400
425
  query: (pks) => ({
401
- url: buildClientRequestUrl(checkout.setDataSourceShippingOption, {
426
+ url: buildCheckoutRequestUrl(checkout.setDataSourceShippingOption, {
402
427
  useFormData: true
403
428
  }),
404
429
  method: 'POST',
@@ -414,7 +439,7 @@ export const checkoutApi = api.injectEndpoints({
414
439
  }),
415
440
  setRetailStore: build.mutation<CheckoutResponse, SetRetailStoreParams>({
416
441
  query: ({ retailStorePk, billingAddressPk }) => ({
417
- url: buildClientRequestUrl(
442
+ url: buildCheckoutRequestUrl(
418
443
  '/orders/checkout?page=RetailStoreSelectionPage',
419
444
  {
420
445
  useFormData: true
@@ -429,13 +454,13 @@ export const checkoutApi = api.injectEndpoints({
429
454
  }),
430
455
  fetchPaymentOptions: build.query<CheckoutResponse, void>({
431
456
  query: () => ({
432
- url: buildClientRequestUrl(checkout.setPaymentOption)
457
+ url: buildCheckoutRequestUrl(checkout.setPaymentOption)
433
458
  }),
434
459
  providesTags: ['PaymentOptions']
435
460
  }),
436
461
  setPaymentOption: build.mutation<CheckoutResponse, number>({
437
462
  query: (pk: number) => ({
438
- url: buildClientRequestUrl(checkout.setPaymentOption, {
463
+ url: buildCheckoutRequestUrl(checkout.setPaymentOption, {
439
464
  useFormData: true
440
465
  }),
441
466
  method: 'POST',
@@ -461,7 +486,7 @@ export const checkoutApi = api.injectEndpoints({
461
486
  }
462
487
  >({
463
488
  query: ({ payment_option: pk, validationURL }) => ({
464
- url: buildClientRequestUrl(checkout.setWalletSelectionPage, {
489
+ url: buildCheckoutRequestUrl(checkout.setWalletSelectionPage, {
465
490
  useFormData: true
466
491
  }),
467
492
  method: 'POST',
@@ -485,7 +510,7 @@ export const checkoutApi = api.injectEndpoints({
485
510
  }
486
511
  >({
487
512
  query: (requestBody) => ({
488
- url: buildClientRequestUrl(checkout.setWalletPaymentPage, {
513
+ url: buildCheckoutRequestUrl(checkout.setWalletPaymentPage, {
489
514
  useFormData: true
490
515
  }),
491
516
  method: 'POST',
@@ -506,7 +531,7 @@ export const checkoutApi = api.injectEndpoints({
506
531
  }
507
532
  >({
508
533
  query: ({ success, ...additionalParams }) => ({
509
- url: buildClientRequestUrl(checkout.setWalletCompletePage, {
534
+ url: buildCheckoutRequestUrl(checkout.setWalletCompletePage, {
510
535
  useFormData: true
511
536
  }),
512
537
  method: 'POST',
@@ -522,23 +547,25 @@ export const checkoutApi = api.injectEndpoints({
522
547
  }
523
548
  }),
524
549
  setBinNumber: build.mutation<CheckoutResponse, string>({
525
- query: (binNumber: string) => {
550
+ async queryFn(binNumber, _queryApi, _extraOptions, baseQuery) {
551
+ const reduxStore = await getStore();
526
552
  const paymentOption =
527
- store.getState().checkout?.preOrder?.payment_option;
553
+ reduxStore.getState().checkout?.preOrder?.payment_option;
528
554
  const binNumberUrl =
529
555
  paymentOption?.payment_type === 'masterpass'
530
556
  ? checkout.setMasterpassBinNumber
531
557
  : checkout.setBinNumber;
532
558
 
533
- return {
534
- url: buildClientRequestUrl(binNumberUrl, {
559
+ const result = await baseQuery({
560
+ url: buildCheckoutRequestUrl(binNumberUrl, {
535
561
  useFormData: true
536
562
  }),
537
563
  method: 'POST',
538
564
  body: {
539
565
  bin_number: binNumber
540
566
  }
541
- };
567
+ });
568
+ return result as { data: CheckoutResponse } | { error: any };
542
569
  },
543
570
  async onQueryStarted(arg, { dispatch, queryFulfilled }) {
544
571
  dispatch(setPaymentStepBusy(true));
@@ -548,22 +575,25 @@ export const checkoutApi = api.injectEndpoints({
548
575
  }
549
576
  }),
550
577
  setInstallmentOption: build.mutation<CheckoutResponse, number>({
551
- query: (pk: number) => {
578
+ async queryFn(pk, _queryApi, _extraOptions, baseQuery) {
579
+ const reduxStore = await getStore();
552
580
  const paymentOption =
553
- store.getState().checkout?.preOrder?.payment_option;
581
+ reduxStore.getState().checkout?.preOrder?.payment_option;
554
582
  const installmentOption =
555
583
  paymentOption?.payment_type === 'masterpass'
556
584
  ? checkout.setMasterPassInstallmentOption
557
585
  : checkout.setInstallmentOption;
558
- return {
559
- url: buildClientRequestUrl(installmentOption, {
586
+
587
+ const result = await baseQuery({
588
+ url: buildCheckoutRequestUrl(installmentOption, {
560
589
  useFormData: true
561
590
  }),
562
591
  method: 'POST',
563
592
  body: {
564
593
  installment: String(pk)
565
594
  }
566
- };
595
+ });
596
+ return result as { data: CheckoutResponse } | { error: any };
567
597
  },
568
598
  async onQueryStarted(arg, { dispatch, queryFulfilled }) {
569
599
  dispatch(setPaymentStepBusy(true));
@@ -573,7 +603,7 @@ export const checkoutApi = api.injectEndpoints({
573
603
  }),
574
604
  setFundsTransferOption: build.mutation<CheckoutResponse, number>({
575
605
  query: (pk: number) => ({
576
- url: buildClientRequestUrl(checkout.setFundsTransferOption, {
606
+ url: buildCheckoutRequestUrl(checkout.setFundsTransferOption, {
577
607
  useFormData: true
578
608
  }),
579
609
  method: 'POST',
@@ -590,7 +620,7 @@ export const checkoutApi = api.injectEndpoints({
590
620
  }),
591
621
  setCreditPaymentOption: build.mutation<CheckoutResponse, number>({
592
622
  query: (pk: number) => ({
593
- url: buildClientRequestUrl(checkout.setCreditPaymentOption, {
623
+ url: buildCheckoutRequestUrl(checkout.setCreditPaymentOption, {
594
624
  useFormData: true
595
625
  }),
596
626
  method: 'POST',
@@ -607,7 +637,7 @@ export const checkoutApi = api.injectEndpoints({
607
637
  }),
608
638
  confirmationCreditPayment: build.mutation<CheckoutResponse, void>({
609
639
  query: () => ({
610
- url: buildClientRequestUrl(checkout.confirmationCreditPayment, {
640
+ url: buildCheckoutRequestUrl(checkout.confirmationCreditPayment, {
611
641
  useFormData: true
612
642
  }),
613
643
  method: 'POST',
@@ -623,7 +653,7 @@ export const checkoutApi = api.injectEndpoints({
623
653
  }),
624
654
  completeRedirectionPayment: build.mutation<CheckoutResponse, void>({
625
655
  query: () => ({
626
- url: buildClientRequestUrl(checkout.completeRedirectionPayment, {
656
+ url: buildCheckoutRequestUrl(checkout.completeRedirectionPayment, {
627
657
  useFormData: true
628
658
  }),
629
659
  method: 'POST',
@@ -639,7 +669,7 @@ export const checkoutApi = api.injectEndpoints({
639
669
  }),
640
670
  applePaymentSelect: build.mutation<CheckoutResponse, ApplePaySelectParams>({
641
671
  query: ({ agreement, validationURL }) => ({
642
- url: buildClientRequestUrl(checkout.confirmationPaymentSelect, {
672
+ url: buildCheckoutRequestUrl(checkout.confirmationPaymentSelect, {
643
673
  useFormData: true
644
674
  }),
645
675
  method: 'POST',
@@ -656,7 +686,7 @@ export const checkoutApi = api.injectEndpoints({
656
686
  }),
657
687
  appleQuery: build.mutation<CheckoutResponse, ApplePayQueryParams>({
658
688
  query: ({ agreement, paymentToken }) => ({
659
- url: buildClientRequestUrl(checkout.confirmationQuery, {
689
+ url: buildCheckoutRequestUrl(checkout.confirmationQuery, {
660
690
  useFormData: true
661
691
  }),
662
692
  method: 'POST',
@@ -673,7 +703,7 @@ export const checkoutApi = api.injectEndpoints({
673
703
  }),
674
704
  completeConfirmation: build.mutation<CheckoutResponse, void>({
675
705
  query: () => ({
676
- url: buildClientRequestUrl(checkout.confirmationComplete, {
706
+ url: buildCheckoutRequestUrl(checkout.confirmationComplete, {
677
707
  useFormData: true
678
708
  }),
679
709
  method: 'POST',
@@ -692,7 +722,7 @@ export const checkoutApi = api.injectEndpoints({
692
722
  PayOnDeliveryParams
693
723
  >({
694
724
  query: ({ paymentType }) => ({
695
- url: buildClientRequestUrl(
725
+ url: buildCheckoutRequestUrl(
696
726
  `${checkout.fetchCheckout}?page=PayOnDeliveryPage`,
697
727
  { useFormData: true }
698
728
  ),
@@ -710,7 +740,7 @@ export const checkoutApi = api.injectEndpoints({
710
740
  }),
711
741
  setPayOnDeliveryChoice: build.mutation<CheckoutResponse, string>({
712
742
  query: (body) => ({
713
- url: buildClientRequestUrl(
743
+ url: buildCheckoutRequestUrl(
714
744
  `${checkout.fetchCheckout}?page=PayOnDeliveryPaymentChoicePage`,
715
745
  { useFormData: true }
716
746
  ),
@@ -727,7 +757,7 @@ export const checkoutApi = api.injectEndpoints({
727
757
  }),
728
758
  completeLoyaltyPayment: build.mutation<CheckoutResponse, void>({
729
759
  query: () => ({
730
- url: buildClientRequestUrl(checkout.completeLoyaltyPayment, {
760
+ url: buildCheckoutRequestUrl(checkout.completeLoyaltyPayment, {
731
761
  useFormData: true
732
762
  }),
733
763
  method: 'POST',
@@ -743,12 +773,12 @@ export const checkoutApi = api.injectEndpoints({
743
773
  }),
744
774
  getCheckoutLoyaltyBalance: build.query<CheckoutResponse, void>({
745
775
  query: () => ({
746
- url: buildClientRequestUrl(checkout.loyaltyMoneyUsage)
776
+ url: buildCheckoutRequestUrl(checkout.loyaltyMoneyUsage)
747
777
  })
748
778
  }),
749
779
  payWithLoyaltyBalance: build.mutation<any, string>({
750
780
  query: (amount) => ({
751
- url: buildClientRequestUrl(checkout.loyaltyMoneyUsage, {
781
+ url: buildCheckoutRequestUrl(checkout.loyaltyMoneyUsage, {
752
782
  useFormData: true
753
783
  }),
754
784
  method: 'POST',
@@ -779,7 +809,7 @@ export const checkoutApi = api.injectEndpoints({
779
809
  }),
780
810
  setOrderNote: build.mutation<CheckoutResponse, string>({
781
811
  query: (notes) => ({
782
- url: buildClientRequestUrl(checkout.setOrderNote, {
812
+ url: buildCheckoutRequestUrl(checkout.setOrderNote, {
783
813
  useFormData: true
784
814
  }),
785
815
  method: 'POST',
@@ -799,7 +829,7 @@ export const checkoutApi = api.injectEndpoints({
799
829
  };
800
830
 
801
831
  return {
802
- url: buildClientRequestUrl(checkout.deliveryBagsPage, {
832
+ url: buildCheckoutRequestUrl(checkout.deliveryBagsPage, {
803
833
  useFormData: true
804
834
  }),
805
835
  method: 'POST',
@@ -812,7 +842,7 @@ export const checkoutApi = api.injectEndpoints({
812
842
  Record<string, number>
813
843
  >({
814
844
  query: (options) => ({
815
- url: buildClientRequestUrl(checkout.setAttributeBasedShippingOption, {
845
+ url: buildCheckoutRequestUrl(checkout.setAttributeBasedShippingOption, {
816
846
  useFormData: true
817
847
  }),
818
848
  method: 'POST',
@@ -831,7 +861,7 @@ export const checkoutApi = api.injectEndpoints({
831
861
  { extra_field: ExtraField }
832
862
  >({
833
863
  query: ({ extra_field }) => ({
834
- url: buildClientRequestUrl(checkout.setOrderSelectionPage, {
864
+ url: buildCheckoutRequestUrl(checkout.setOrderSelectionPage, {
835
865
  useFormData: true
836
866
  }),
837
867
  method: 'POST',
@@ -842,7 +872,7 @@ export const checkoutApi = api.injectEndpoints({
842
872
  }),
843
873
  fetchLoyaltyData: build.query<CheckoutResponse, void>({
844
874
  query: () => ({
845
- url: buildClientRequestUrl(checkout.loyaltyCardPage, {
875
+ url: buildCheckoutRequestUrl(checkout.loyaltyCardPage, {
846
876
  accept: 'application/json',
847
877
  contentType: 'application/json'
848
878
  }),
@@ -851,7 +881,7 @@ export const checkoutApi = api.injectEndpoints({
851
881
  }),
852
882
  setLoyaltyData: build.mutation<CheckoutResponse, number | string>({
853
883
  query: (amount) => ({
854
- url: buildClientRequestUrl(checkout.loyaltyCardPage, {
884
+ url: buildCheckoutRequestUrl(checkout.loyaltyCardPage, {
855
885
  useFormData: true
856
886
  }),
857
887
  method: 'POST',
@@ -862,7 +892,7 @@ export const checkoutApi = api.injectEndpoints({
862
892
  }),
863
893
  sendSms: build.mutation<CheckoutResponse, SendSmsType>({
864
894
  query: (body) => ({
865
- url: buildClientRequestUrl(checkout.sendSmsPage, {
895
+ url: buildCheckoutRequestUrl(checkout.sendSmsPage, {
866
896
  useFormData: true
867
897
  }),
868
898
  method: 'POST',
@@ -871,7 +901,7 @@ export const checkoutApi = api.injectEndpoints({
871
901
  }),
872
902
  verifySms: build.mutation<CheckoutResponse, VerifySmsType>({
873
903
  query: (body) => ({
874
- url: buildClientRequestUrl(checkout.verifySmsPage, {
904
+ url: buildCheckoutRequestUrl(checkout.verifySmsPage, {
875
905
  useFormData: true
876
906
  }),
877
907
  method: 'POST',
@@ -887,7 +917,7 @@ export const checkoutApi = api.injectEndpoints({
887
917
  });
888
918
 
889
919
  return {
890
- url: buildClientRequestUrl(checkout.saveSampleProducts),
920
+ url: buildCheckoutRequestUrl(checkout.saveSampleProducts),
891
921
  method: 'POST',
892
922
  body: formData
893
923
  };
package/data/urls.ts CHANGED
@@ -183,7 +183,11 @@ export const product = {
183
183
  breadcrumbUrl: (menuitemmodel: string) =>
184
184
  `/menus/generate_breadcrumb/?item=${menuitemmodel}&generator_name=menu_item`,
185
185
  bundleProduct: (productPk: string, queryString: string) =>
186
- `/bundle-product/${productPk}/?${queryString}`
186
+ `/bundle-product/${productPk}/?${queryString}`,
187
+ similarProducts: (params?: string) =>
188
+ `/similar-products${params ? `?${params}` : ''}`,
189
+ similarProductsList: (params?: string) =>
190
+ `/similar-product-list${params ? `?${params}` : ''}`
187
191
  };
188
192
 
189
193
  export const wishlist = {
@@ -364,11 +364,17 @@ CacheHandler.onCreation(async () => {
364
364
  };
365
365
  }
366
366
 
367
- const redisHandler = createRedisHandler({
367
+ const redisOptions = {
368
368
  client,
369
369
  timeoutMs: CACHE_CONFIG.redis.timeoutMs,
370
370
  keyExpirationStrategy: 'EXPIREAT'
371
- });
371
+ };
372
+
373
+ if (process.env.CACHE_PASSWORD) {
374
+ redisOptions.password = process.env.CACHE_PASSWORD;
375
+ }
376
+
377
+ const redisHandler = createRedisHandler(redisOptions);
372
378
 
373
379
  const localHandler = createLruHandler(CACHE_CONFIG.lru);
374
380
 
package/lib/cache.ts CHANGED
@@ -155,9 +155,14 @@ export class Cache {
155
155
  process.env.CACHE_PORT
156
156
  }/${process.env.CACHE_BUCKET ?? '0'}`;
157
157
 
158
- const client: RedisClientType = createClient({
159
- url: redisUrl
160
- });
158
+ const options = {
159
+ url: redisUrl,
160
+ ...(process.env.CACHE_PASSWORD && {
161
+ password: process.env.CACHE_PASSWORD
162
+ })
163
+ };
164
+
165
+ const client: RedisClientType = createClient(options);
161
166
 
162
167
  client.on('error', (error) => {
163
168
  logger.error('Redis client error', { redisUrl, error });
@@ -0,0 +1,18 @@
1
+ import { NextMiddleware } from 'next/server';
2
+
3
+ const withBfcacheHeaders = (middleware: NextMiddleware): NextMiddleware => {
4
+ return async (req, event) => {
5
+ const response = await middleware(req, event);
6
+
7
+ if (process.env.BF_CACHE === 'true' && response) {
8
+ response.headers.set(
9
+ 'Cache-Control',
10
+ 'private, no-cache, max-age=0, must-revalidate'
11
+ );
12
+ }
13
+
14
+ return response;
15
+ };
16
+ };
17
+
18
+ export default withBfcacheHeaders;
@@ -4,6 +4,7 @@ import { Buffer } from 'buffer';
4
4
  import logger from '../utils/log';
5
5
  import { getUrlPathWithLocale } from '../utils/localization';
6
6
  import { PzNextRequest } from '.';
7
+ import { getCheckoutPath } from '../utils';
7
8
 
8
9
  const streamToString = async (stream: ReadableStream<Uint8Array> | null) => {
9
10
  if (stream) {
@@ -40,7 +41,8 @@ const withCompleteGpay =
40
41
  return middleware(req, event);
41
42
  }
42
43
 
43
- const requestUrl = `${Settings.commerceUrl}/orders/checkout/${url.search}`;
44
+ const isPostCheckout = req.cookies.get('pz-post-checkout-flow')?.value === 'true';
45
+ const requestUrl = `${Settings.commerceUrl}${getCheckoutPath(isPostCheckout)}${url.search}`;
44
46
  const requestHeaders = {
45
47
  'X-Requested-With': 'XMLHttpRequest',
46
48
  'Content-Type': 'application/x-www-form-urlencoded',
@@ -5,6 +5,7 @@ import logger from '../utils/log';
5
5
  import { getUrlPathWithLocale } from '../utils/localization';
6
6
  import { PzNextRequest } from '.';
7
7
  import { ServerVariables } from '../utils/server-variables';
8
+ import { getCheckoutPath } from '../utils';
8
9
 
9
10
  const streamToString = async (stream: ReadableStream<Uint8Array> | null) => {
10
11
  if (stream) {
@@ -41,7 +42,8 @@ const withCompleteMasterpass =
41
42
  return middleware(req, event);
42
43
  }
43
44
 
44
- const requestUrl = `${Settings.commerceUrl}/orders/checkout/${url.search}`;
45
+ const isPostCheckout = req.cookies.get('pz-post-checkout-flow')?.value === 'true';
46
+ const requestUrl = `${Settings.commerceUrl}${getCheckoutPath(isPostCheckout)}${url.search}`;
45
47
  const requestHeaders = {
46
48
  'X-Requested-With': 'XMLHttpRequest',
47
49
  'Content-Type': 'application/x-www-form-urlencoded',
@@ -4,6 +4,7 @@ import { Buffer } from 'buffer';
4
4
  import logger from '../utils/log';
5
5
  import { getUrlPathWithLocale } from '../utils/localization';
6
6
  import { PzNextRequest } from '.';
7
+ import { getCheckoutPath } from '../utils';
7
8
 
8
9
  const streamToString = async (stream: ReadableStream<Uint8Array> | null) => {
9
10
  if (stream) {
@@ -39,7 +40,8 @@ const withCompleteWallet =
39
40
  return middleware(req, event);
40
41
  }
41
42
 
42
- const requestUrl = `${Settings.commerceUrl}/orders/checkout/${url.search}`;
43
+ const isPostCheckout = req.cookies.get('pz-post-checkout-flow')?.value === 'true';
44
+ const requestUrl = `${Settings.commerceUrl}${getCheckoutPath(isPostCheckout)}${url.search}`;
43
45
  const requestHeaders = {
44
46
  'X-Requested-With': 'XMLHttpRequest',
45
47
  'Content-Type': 'application/x-www-form-urlencoded',
@@ -14,7 +14,8 @@ import {
14
14
  withUrlRedirection,
15
15
  withCompleteWallet,
16
16
  withWalletCompleteRedirection,
17
- withMasterpassRestCallback
17
+ withMasterpassRestCallback,
18
+ withBfcacheHeaders
18
19
  } from '.';
19
20
  import { urlLocaleMatcherRegex } from '../utils';
20
21
  import withCurrency from './currency';
@@ -25,6 +26,8 @@ import { getUrlPathWithLocale } from '../utils/localization';
25
26
  import getRootHostname from '../utils/get-root-hostname';
26
27
  import { LocaleUrlStrategy } from '../localization';
27
28
 
29
+ const POST_CHECKOUT_COOKIE_MAX_AGE_MS = 1000 * 60 * 30; // 30 minutes
30
+
28
31
  const withPzDefault =
29
32
  (middleware: NextMiddleware) =>
30
33
  async (req: PzNextRequest, event: NextFetchEvent) => {
@@ -101,6 +104,7 @@ const withPzDefault =
101
104
  if (
102
105
  req.nextUrl.pathname.includes('/orders/hooks/') ||
103
106
  req.nextUrl.pathname.includes('/orders/checkout-with-token/') ||
107
+ req.nextUrl.pathname.includes('/orders/post-checkout-redirect/') ||
104
108
  req.nextUrl.pathname.includes('/hooks/cash_register/complete/') ||
105
109
  req.nextUrl.pathname.includes('/hooks/cash_register/pre_order/')
106
110
  ) {
@@ -128,8 +132,13 @@ const withPzDefault =
128
132
  }
129
133
 
130
134
  if (req.nextUrl.pathname.startsWith('/orders/redirection/')) {
135
+ const queryString = searchParams.toString();
131
136
  return NextResponse.rewrite(
132
- new URL(`${encodeURI(Settings.commerceUrl)}/orders/redirection/`)
137
+ new URL(
138
+ `${encodeURI(Settings.commerceUrl)}/orders/redirection/${
139
+ queryString ? `?${queryString}` : ''
140
+ }`
141
+ )
133
142
  );
134
143
  }
135
144
 
@@ -147,21 +156,35 @@ const withPzDefault =
147
156
  );
148
157
  }
149
158
 
150
- // If commerce redirects to /orders/checkout/ without locale
159
+ // If commerce redirects to /orders/checkout/ or /orders/post-checkout/ without locale
160
+ const isPostCheckout = !!req.nextUrl.pathname.match(
161
+ new RegExp('^/orders/post-checkout/$')
162
+ );
163
+ const checkoutLocalePath = getUrlPathWithLocale(
164
+ '/orders/checkout/',
165
+ req.cookies.get('pz-locale')?.value
166
+ );
151
167
  if (
152
- req.nextUrl.pathname.match(new RegExp('^/orders/checkout/$')) &&
153
- req.nextUrl.searchParams.size === 0 &&
154
- getUrlPathWithLocale(
155
- '/orders/checkout/',
156
- req.cookies.get('pz-locale')?.value
157
- ) !== req.nextUrl.pathname
168
+ (isPostCheckout && req.nextUrl.searchParams.size === 0) ||
169
+ (req.nextUrl.pathname.match(new RegExp('^/orders/checkout/$')) &&
170
+ req.nextUrl.searchParams.size === 0 &&
171
+ checkoutLocalePath !== req.nextUrl.pathname)
158
172
  ) {
159
- const redirectUrlWithLocale = `${url.origin}${getUrlPathWithLocale(
160
- '/orders/checkout/',
161
- req.cookies.get('pz-locale')?.value
162
- )}`;
173
+ const response = NextResponse.redirect(
174
+ `${url.origin}${checkoutLocalePath}`,
175
+ 303
176
+ );
177
+
178
+ if (isPostCheckout) {
179
+ response.cookies.set('pz-post-checkout-flow', 'true', {
180
+ path: '/',
181
+ sameSite: 'none',
182
+ secure: true,
183
+ expires: new Date(Date.now() + POST_CHECKOUT_COOKIE_MAX_AGE_MS)
184
+ });
185
+ }
163
186
 
164
- return NextResponse.redirect(redirectUrlWithLocale, 303);
187
+ return response;
165
188
  }
166
189
 
167
190
  // Dynamically handle any payment gateway without specifying names
@@ -233,10 +256,11 @@ const withPzDefault =
233
256
  withCompleteWallet(
234
257
  withWalletCompleteRedirection(
235
258
  withMasterpassRestCallback(
236
- async (
237
- req: PzNextRequest,
238
- event: NextFetchEvent
239
- ) => {
259
+ withBfcacheHeaders(
260
+ async (
261
+ req: PzNextRequest,
262
+ event: NextFetchEvent
263
+ ) => {
240
264
  let middlewareResult: NextResponse | void =
241
265
  NextResponse.next();
242
266
 
@@ -420,6 +444,25 @@ const withPzDefault =
420
444
  );
421
445
  }
422
446
 
447
+ if (
448
+ req.cookies.get(
449
+ 'pz-post-checkout-flow'
450
+ )
451
+ ) {
452
+ if (
453
+ pathnameWithoutLocale.startsWith(
454
+ '/orders/completed/'
455
+ ) ||
456
+ pathnameWithoutLocale.startsWith(
457
+ '/basket'
458
+ )
459
+ ) {
460
+ middlewareResult.cookies.delete(
461
+ 'pz-post-checkout-flow'
462
+ );
463
+ }
464
+ }
465
+
423
466
  if (process.env.ACC_APP_VERSION) {
424
467
  middlewareResult.headers.set(
425
468
  'acc-app-version',
@@ -457,7 +500,7 @@ const withPzDefault =
457
500
  }
458
501
 
459
502
  return middlewareResult;
460
- }
503
+ })
461
504
  )
462
505
  )
463
506
  )
@@ -12,6 +12,7 @@ import withSavedCardRedirection from './saved-card-redirection';
12
12
  import withCompleteWallet from './complete-wallet';
13
13
  import withWalletCompleteRedirection from './wallet-complete-redirection';
14
14
  import withMasterpassRestCallback from './masterpass-rest-callback';
15
+ import withBfcacheHeaders from './bfcache-headers';
15
16
  import { NextRequest } from 'next/server';
16
17
 
17
18
  export {
@@ -28,7 +29,8 @@ export {
28
29
  withSavedCardRedirection,
29
30
  withCompleteWallet,
30
31
  withWalletCompleteRedirection,
31
- withMasterpassRestCallback
32
+ withMasterpassRestCallback,
33
+ withBfcacheHeaders
32
34
  };
33
35
 
34
36
  export interface PzNextRequest extends NextRequest {
@@ -3,6 +3,7 @@ import Settings from 'settings';
3
3
  import logger from '../utils/log';
4
4
  import { getUrlPathWithLocale } from '../utils/localization';
5
5
  import { PzNextRequest } from '.';
6
+ import { getCheckoutPath } from '../utils';
6
7
 
7
8
  const withMasterpassRestCallback =
8
9
  (middleware: NextMiddleware) =>
@@ -19,7 +20,9 @@ const withMasterpassRestCallback =
19
20
  }
20
21
 
21
22
  try {
22
- const requestUrl = new URL('/orders/checkout/', Settings.commerceUrl);
23
+ const isPostCheckout = req.cookies.get('pz-post-checkout-flow')?.value === 'true';
24
+ const requestUrl = new URL(getCheckoutPath(isPostCheckout), Settings.commerceUrl);
25
+
23
26
  url.searchParams.forEach((value, key) => {
24
27
  requestUrl.searchParams.set(key, value);
25
28
  });
@@ -4,6 +4,7 @@ import Settings from 'settings';
4
4
  import logger from '../utils/log';
5
5
  import { PzNextRequest } from '.';
6
6
  import { getUrlPathWithLocale } from '../utils/localization';
7
+ import { getCheckoutPath } from '../utils';
7
8
 
8
9
  const streamToString = async (stream: ReadableStream<Uint8Array> | null) => {
9
10
  if (stream) {
@@ -41,7 +42,8 @@ const withRedirectionPayment =
41
42
  return middleware(req, event);
42
43
  }
43
44
 
44
- const requestUrl = `${Settings.commerceUrl}/orders/checkout/${url.search}`;
45
+ const isPostCheckout = req.cookies.get('pz-post-checkout-flow')?.value === 'true';
46
+ const requestUrl = `${Settings.commerceUrl}${getCheckoutPath(isPostCheckout)}${url.search}`;
45
47
  const requestHeaders = {
46
48
  'X-Requested-With': 'XMLHttpRequest',
47
49
  'Content-Type': 'application/x-www-form-urlencoded',
@@ -5,6 +5,7 @@ import logger from '../utils/log';
5
5
  import { getUrlPathWithLocale } from '../utils/localization';
6
6
  import { PzNextRequest } from '.';
7
7
  import { ServerVariables } from '../utils/server-variables';
8
+ import { getCheckoutPath } from '../utils';
8
9
 
9
10
  const streamToString = async (stream: ReadableStream<Uint8Array> | null) => {
10
11
  if (stream) {
@@ -41,7 +42,8 @@ const withSavedCardRedirection =
41
42
  return middleware(req, event);
42
43
  }
43
44
 
44
- const requestUrl = `${Settings.commerceUrl}/orders/checkout/${url.search}`;
45
+ const isPostCheckout = req.cookies.get('pz-post-checkout-flow')?.value === 'true';
46
+ const requestUrl = `${Settings.commerceUrl}${getCheckoutPath(isPostCheckout)}${url.search}`;
45
47
  const requestHeaders = {
46
48
  'X-Requested-With': 'XMLHttpRequest',
47
49
  'Content-Type': 'application/x-www-form-urlencoded',
@@ -4,6 +4,7 @@ import { Buffer } from 'buffer';
4
4
  import logger from '../utils/log';
5
5
  import { getUrlPathWithLocale } from '../utils/localization';
6
6
  import { PzNextRequest } from '.';
7
+ import { getCheckoutPath } from '../utils';
7
8
 
8
9
  const streamToString = async (stream: ReadableStream<Uint8Array> | null) => {
9
10
  if (stream) {
@@ -40,7 +41,8 @@ const withThreeDRedirection =
40
41
  return middleware(req, event);
41
42
  }
42
43
 
43
- const requestUrl = `${Settings.commerceUrl}/orders/checkout/${url.search}`;
44
+ const isPostCheckout = req.cookies.get('pz-post-checkout-flow')?.value === 'true';
45
+ const requestUrl = `${Settings.commerceUrl}${getCheckoutPath(isPostCheckout)}${url.search}`;
44
46
  const requestHeaders = {
45
47
  'X-Requested-With': 'XMLHttpRequest',
46
48
  'Content-Type': 'application/x-www-form-urlencoded',
@@ -4,6 +4,7 @@ import { Buffer } from 'buffer';
4
4
  import logger from '../utils/log';
5
5
  import { getUrlPathWithLocale } from '../utils/localization';
6
6
  import { PzNextRequest } from '.';
7
+ import { getCheckoutPath } from '../utils';
7
8
 
8
9
  const streamToString = async (stream: ReadableStream<Uint8Array> | null) => {
9
10
  if (stream) {
@@ -39,7 +40,8 @@ const withWalletCompleteRedirection =
39
40
  return middleware(req, event);
40
41
  }
41
42
 
42
- const requestUrl = `${Settings.commerceUrl}/orders/checkout/${url.search}`;
43
+ const isPostCheckout = req.cookies.get('pz-post-checkout-flow')?.value === 'true';
44
+ const requestUrl = `${Settings.commerceUrl}${getCheckoutPath(isPostCheckout)}${url.search}`;
43
45
  const requestHeaders = {
44
46
  'X-Requested-With': 'XMLHttpRequest',
45
47
  'Content-Type': 'application/x-www-form-urlencoded',
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.118.0",
4
+ "version": "1.119.0-rc.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.118.0",
38
+ "@akinon/eslint-plugin-projectzero": "1.119.0-rc.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
@@ -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
  }
package/plugins.js CHANGED
@@ -16,6 +16,7 @@ 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',
@@ -204,15 +204,25 @@ export const contextListMiddleware: Middleware = ({
204
204
  (ctx) => ctx.page_name === 'DeliveryOptionSelectionPage'
205
205
  )
206
206
  ) {
207
+ const isCreditCardPayment =
208
+ preOrder?.payment_option?.payment_type === 'credit_card' ||
209
+ preOrder?.payment_option?.payment_type === 'masterpass';
210
+
207
211
  if (context.page_context.card_type) {
208
212
  dispatch(setCardType(context.page_context.card_type));
213
+ } else if (isCreditCardPayment) {
214
+ dispatch(setCardType(null));
209
215
  }
210
216
 
211
217
  if (
212
218
  context.page_context.installments &&
213
219
  preOrder?.payment_option?.payment_type !== 'masterpass_rest'
214
220
  ) {
215
- dispatch(setInstallmentOptions(context.page_context.installments));
221
+ if (!isCreditCardPayment || context.page_context.card_type) {
222
+ dispatch(
223
+ setInstallmentOptions(context.page_context.installments)
224
+ );
225
+ }
216
226
  }
217
227
  }
218
228
 
@@ -14,9 +14,17 @@ export const installmentOptionMiddleware: Middleware = ({
14
14
  return result;
15
15
  }
16
16
 
17
- const { installmentOptions } = getState().checkout;
17
+ const { installmentOptions, cardType } = getState().checkout;
18
18
  const { endpoints: apiEndpoints } = checkoutApi;
19
19
 
20
+ const isCreditCardPayment =
21
+ preOrder?.payment_option?.payment_type === 'credit_card' ||
22
+ preOrder?.payment_option?.payment_type === 'masterpass';
23
+
24
+ if (isCreditCardPayment && !cardType) {
25
+ return result;
26
+ }
27
+
20
28
  if (
21
29
  !preOrder?.installment &&
22
30
  preOrder?.payment_option?.payment_type !== 'saved_card' &&
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
  };
@@ -0,0 +1,3 @@
1
+ export const getCheckoutPath = (isPostCheckout: boolean): string => {
2
+ return isPostCheckout ? '/orders/post-checkout/' : '/orders/checkout/';
3
+ };
package/utils/index.ts CHANGED
@@ -7,6 +7,7 @@ export * from './get-currency';
7
7
  export * from './menu-generator';
8
8
  export * from './generate-commerce-search-params';
9
9
  export * from './get-currency-label';
10
+ export * from './get-checkout-path';
10
11
 
11
12
  export function getCookie(name: string) {
12
13
  if (typeof document === 'undefined') {
@@ -204,6 +205,23 @@ export const getPosError = () => {
204
205
  return error;
205
206
  };
206
207
 
208
+ export const checkPaymentWillRedirect = (response: {
209
+ context_list?: Array<{
210
+ page_name: string;
211
+ page_context?: { context_data?: { redirect_url?: string } };
212
+ }>;
213
+ redirect_url?: string;
214
+ errors?: unknown;
215
+ }): boolean => {
216
+ if (!response) return false;
217
+
218
+ const hasThankYouPage = response.context_list?.some(
219
+ (c) => c.page_name === 'ThankYouPage'
220
+ );
221
+
222
+ return Boolean(hasThankYouPage || response.redirect_url);
223
+ };
224
+
207
225
  export const urlSchemes = [
208
226
  'http',
209
227
  'tel:',
@@ -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
  });
package/with-pz-config.js CHANGED
@@ -65,10 +65,7 @@ const defaultConfig = {
65
65
  translations: false
66
66
  };
67
67
  return config;
68
- },
69
- sentry: {
70
- hideSourceMaps: true
71
- } // TODO: This section will be reviewed again in the Sentry 8 update.
68
+ }
72
69
  };
73
70
 
74
71
  const withPzConfig = (