@akinon/next 2.0.0-beta.11 → 2.0.0-beta.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/CHANGELOG.md +286 -27
  2. package/api/auth.ts +99 -77
  3. package/api/cache.ts +41 -5
  4. package/api/client.ts +3 -3
  5. package/api/form.ts +85 -0
  6. package/api/image-proxy.ts +75 -0
  7. package/api/product-categories.ts +53 -0
  8. package/api/similar-product-list.ts +63 -0
  9. package/api/similar-products.ts +111 -0
  10. package/api/virtual-try-on.ts +382 -0
  11. package/bin/pz-generate-routes.js +105 -0
  12. package/bin/pz-prebuild.js +1 -1
  13. package/bin/pz-predev.js +1 -0
  14. package/components/accordion.tsx +21 -6
  15. package/components/button.tsx +1 -1
  16. package/components/file-input.tsx +65 -3
  17. package/components/input.tsx +2 -2
  18. package/components/modal.tsx +32 -16
  19. package/components/plugin-module.tsx +61 -3
  20. package/components/select.tsx +2 -2
  21. package/components/selected-payment-option-view.tsx +21 -0
  22. package/data/client/checkout.ts +130 -74
  23. package/data/server/category.ts +11 -9
  24. package/data/server/flatpage.ts +4 -1
  25. package/data/server/form.ts +4 -1
  26. package/data/server/landingpage.ts +4 -1
  27. package/data/server/list.ts +5 -4
  28. package/data/server/menu.ts +4 -1
  29. package/data/server/product.ts +97 -52
  30. package/data/server/seo.ts +4 -1
  31. package/data/server/special-page.ts +5 -4
  32. package/data/server/widget.ts +4 -1
  33. package/data/urls.ts +3 -2
  34. package/hocs/client/with-segment-defaults.tsx +2 -2
  35. package/hocs/server/with-segment-defaults.tsx +65 -20
  36. package/hooks/index.ts +1 -0
  37. package/hooks/use-loyalty-availability.ts +21 -0
  38. package/hooks/use-payment-options.ts +2 -1
  39. package/hooks/use-pz-params.ts +37 -0
  40. package/instrumentation/index.ts +0 -1
  41. package/instrumentation/node.ts +2 -20
  42. package/jest.config.js +7 -1
  43. package/lib/cache-handler.mjs +527 -15
  44. package/lib/cache.ts +260 -31
  45. package/localization/provider.tsx +2 -5
  46. package/middlewares/checkout-provider.ts +1 -1
  47. package/middlewares/complete-gpay.ts +33 -26
  48. package/middlewares/complete-masterpass.ts +34 -26
  49. package/middlewares/complete-wallet.ts +183 -0
  50. package/middlewares/default.ts +346 -235
  51. package/middlewares/index.ts +8 -2
  52. package/middlewares/locale.ts +0 -1
  53. package/middlewares/masterpass-rest-callback.ts +220 -0
  54. package/middlewares/pretty-url.ts +21 -8
  55. package/middlewares/redirection-payment.ts +33 -26
  56. package/middlewares/saved-card-redirection.ts +34 -26
  57. package/middlewares/three-d-redirection.ts +33 -26
  58. package/middlewares/url-redirection.ts +9 -15
  59. package/middlewares/wallet-complete-redirection.ts +207 -0
  60. package/package.json +20 -11
  61. package/plugins.d.ts +19 -4
  62. package/plugins.js +9 -1
  63. package/redux/actions.ts +47 -0
  64. package/redux/middlewares/checkout.ts +20 -8
  65. package/redux/middlewares/index.ts +12 -10
  66. package/redux/middlewares/pre-order/address.ts +1 -1
  67. package/redux/middlewares/pre-order/attribute-based-shipping-option.ts +1 -1
  68. package/redux/middlewares/pre-order/data-source-shipping-option.ts +1 -1
  69. package/redux/middlewares/pre-order/delivery-option.ts +1 -1
  70. package/redux/middlewares/pre-order/index.ts +3 -1
  71. package/redux/middlewares/pre-order/installment-option.ts +2 -1
  72. package/redux/middlewares/pre-order/payment-option-reset.ts +37 -0
  73. package/redux/middlewares/pre-order/payment-option.ts +1 -1
  74. package/redux/middlewares/pre-order/pre-order-validation.ts +4 -3
  75. package/redux/middlewares/pre-order/redirection.ts +2 -2
  76. package/redux/middlewares/pre-order/set-pre-order.ts +2 -2
  77. package/redux/middlewares/pre-order/shipping-option.ts +1 -1
  78. package/redux/middlewares/pre-order/shipping-step.ts +1 -1
  79. package/redux/reducers/checkout.ts +9 -1
  80. package/redux/reducers/index.ts +5 -1
  81. package/sentry/index.ts +54 -17
  82. package/types/commerce/checkout.ts +11 -1
  83. package/types/index.ts +96 -6
  84. package/types/next-auth.d.ts +2 -2
  85. package/utils/app-fetch.ts +2 -2
  86. package/utils/generate-commerce-search-params.ts +3 -2
  87. package/utils/get-checkout-path.ts +3 -0
  88. package/utils/index.ts +38 -11
  89. package/utils/override-middleware.ts +1 -0
  90. package/utils/pz-segments.ts +92 -0
  91. package/utils/redirect-ignore.ts +35 -0
  92. package/utils/redirect.ts +9 -3
  93. package/with-pz-config.js +10 -4
@@ -69,7 +69,6 @@ const withLocale =
69
69
  req.method === 'GET'
70
70
  ) {
71
71
  // Redirect to existing or default locale
72
-
73
72
  url.pathname = getUrlPathWithLocale(
74
73
  url.pathname,
75
74
  req.cookies.get('pz-locale')?.value
@@ -0,0 +1,220 @@
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
+ import { getCheckoutPath } from '../utils';
7
+
8
+ const withMasterpassRestCallback =
9
+ (middleware: NextMiddleware) =>
10
+ async (req: PzNextRequest, event: NextFetchEvent) => {
11
+ const url = req.nextUrl.clone();
12
+ const ip = req.headers.get('x-forwarded-for') ?? '';
13
+ const sessionId = req.cookies.get('osessionid');
14
+
15
+ if (!url.pathname.includes('/orders/masterpass-rest-callback')) {
16
+ return middleware(req, event);
17
+ }
18
+
19
+ if (req.method !== 'POST') {
20
+ logger.warn('Invalid request method for masterpass REST callback', {
21
+ middleware: 'masterpass-rest-callback',
22
+ method: req.method,
23
+ ip
24
+ });
25
+
26
+ return NextResponse.redirect(
27
+ `${url.origin}${getUrlPathWithLocale(
28
+ '/orders/checkout/',
29
+ req.cookies.get('pz-locale')?.value
30
+ )}`,
31
+ 303
32
+ );
33
+ }
34
+
35
+ const responseCode = url.searchParams.get('responseCode');
36
+ const token = url.searchParams.get('token');
37
+
38
+ if (!responseCode || !token) {
39
+ logger.warn('Missing required parameters for masterpass REST callback', {
40
+ middleware: 'masterpass-rest-callback',
41
+ responseCode,
42
+ token,
43
+ ip
44
+ });
45
+
46
+ return NextResponse.redirect(
47
+ `${url.origin}${getUrlPathWithLocale(
48
+ '/orders/checkout/',
49
+ req.cookies.get('pz-locale')?.value
50
+ )}`,
51
+ 303
52
+ );
53
+ }
54
+
55
+ try {
56
+ const formData = await req.formData();
57
+ const body: Record<string, string> = {};
58
+
59
+ Array.from(formData.entries()).forEach(([key, value]) => {
60
+ body[key] = value.toString();
61
+ });
62
+
63
+ if (!sessionId) {
64
+ logger.warn(
65
+ 'Make sure that the SESSION_COOKIE_SAMESITE environment variable is set to None in Commerce.',
66
+ {
67
+ middleware: 'masterpass-rest-callback',
68
+ ip
69
+ }
70
+ );
71
+
72
+ return NextResponse.redirect(
73
+ `${url.origin}${getUrlPathWithLocale(
74
+ '/orders/checkout/',
75
+ req.cookies.get('pz-locale')?.value
76
+ )}`,
77
+ 303
78
+ );
79
+ }
80
+
81
+ const isPostCheckout = req.cookies.get('pz-post-checkout-flow')?.value === 'true';
82
+ const requestUrl = new URL(getCheckoutPath(isPostCheckout), Settings.commerceUrl);
83
+ requestUrl.searchParams.set('page', 'MasterpassRestCompletePage');
84
+ requestUrl.searchParams.set('responseCode', responseCode);
85
+ requestUrl.searchParams.set('token', token);
86
+ requestUrl.searchParams.set(
87
+ 'three_d_secure',
88
+ body.transactionType?.includes('3D') ? 'true' : 'false'
89
+ );
90
+ requestUrl.searchParams.set(
91
+ 'transactionType',
92
+ body.transactionType || ''
93
+ );
94
+
95
+ const requestHeaders = {
96
+ 'Content-Type': 'application/x-www-form-urlencoded',
97
+ 'X-Requested-With': 'XMLHttpRequest',
98
+ Cookie: req.headers.get('cookie') ?? '',
99
+ 'x-currency': req.cookies.get('pz-currency')?.value ?? '',
100
+ 'x-forwarded-for': ip,
101
+ 'User-Agent': req.headers.get('user-agent') ?? ''
102
+ };
103
+
104
+ const request = await fetch(requestUrl.toString(), {
105
+ method: 'POST',
106
+ headers: requestHeaders,
107
+ body: new URLSearchParams(body)
108
+ });
109
+
110
+ logger.info('Masterpass REST callback request', {
111
+ requestUrl: requestUrl.toString(),
112
+ status: request.status,
113
+ requestHeaders,
114
+ ip
115
+ });
116
+
117
+ const response = await request.json();
118
+
119
+ const { context_list: contextList, errors } = response;
120
+
121
+ let redirectUrl = response.redirect_url;
122
+
123
+ if (!redirectUrl && contextList && contextList.length > 0) {
124
+ for (const context of contextList) {
125
+ if (context.page_context && context.page_context.redirect_url) {
126
+ redirectUrl = context.page_context.redirect_url;
127
+ break;
128
+ }
129
+ }
130
+ }
131
+
132
+ if (errors && Object.keys(errors).length) {
133
+ logger.error('Error while processing masterpass REST callback', {
134
+ middleware: 'masterpass-rest-callback',
135
+ errors,
136
+ requestHeaders,
137
+ ip
138
+ });
139
+
140
+ return NextResponse.redirect(
141
+ `${url.origin}${getUrlPathWithLocale(
142
+ '/orders/checkout/',
143
+ req.cookies.get('pz-locale')?.value
144
+ )}`,
145
+ {
146
+ status: 303,
147
+ headers: {
148
+ 'Set-Cookie': `pz-pos-error=${JSON.stringify(errors)}; path=/;`
149
+ }
150
+ }
151
+ );
152
+ }
153
+
154
+ logger.info('Masterpass REST callback response', {
155
+ middleware: 'masterpass-rest-callback',
156
+ contextList,
157
+ redirectUrl,
158
+ ip
159
+ });
160
+
161
+ if (!redirectUrl) {
162
+ logger.warn(
163
+ 'No redirection url found in response. Redirecting to checkout page.',
164
+ {
165
+ middleware: 'masterpass-rest-callback',
166
+ requestHeaders,
167
+ response: JSON.stringify(response),
168
+ ip
169
+ }
170
+ );
171
+
172
+ const redirectUrlWithLocale = `${url.origin}${getUrlPathWithLocale(
173
+ '/orders/checkout/',
174
+ req.cookies.get('pz-locale')?.value
175
+ )}`;
176
+
177
+ return NextResponse.redirect(redirectUrlWithLocale, 303);
178
+ }
179
+
180
+ const redirectUrlWithLocale = `${url.origin}${getUrlPathWithLocale(
181
+ redirectUrl,
182
+ req.cookies.get('pz-locale')?.value
183
+ )}`;
184
+
185
+ logger.info('Redirecting after masterpass REST callback', {
186
+ middleware: 'masterpass-rest-callback',
187
+ redirectUrlWithLocale,
188
+ ip
189
+ });
190
+
191
+ const nextResponse = NextResponse.redirect(redirectUrlWithLocale, 303);
192
+
193
+ nextResponse.headers.set(
194
+ 'Set-Cookie',
195
+ request.headers.get('set-cookie') ?? ''
196
+ );
197
+
198
+ return nextResponse;
199
+ } catch (error) {
200
+ logger.error('Error while processing masterpass REST callback', {
201
+ middleware: 'masterpass-rest-callback',
202
+ error,
203
+ requestHeaders: {
204
+ Cookie: req.headers.get('cookie') ?? '',
205
+ 'x-currency': req.cookies.get('pz-currency')?.value ?? ''
206
+ },
207
+ ip
208
+ });
209
+
210
+ return NextResponse.redirect(
211
+ `${url.origin}${getUrlPathWithLocale(
212
+ '/orders/checkout/',
213
+ req.cookies.get('pz-locale')?.value
214
+ )}`,
215
+ 303
216
+ );
217
+ }
218
+ };
219
+
220
+ export default withMasterpassRestCallback;
@@ -1,17 +1,33 @@
1
1
  import { Cache, CacheKey } from '../lib/cache';
2
- import { NextFetchEvent, NextMiddleware, NextRequest } from 'next/server';
3
- import { ROUTES } from 'routes';
2
+ import { NextFetchEvent, NextMiddleware } from 'next/server';
4
3
  import { URLS } from '../data/urls';
5
4
  import Settings from 'settings';
6
5
  import { urlLocaleMatcherRegex } from '../utils';
7
6
  import { PzNextRequest } from '.';
8
7
  import logger from '../utils/log';
8
+ import { ROUTES } from 'routes';
9
9
 
10
10
  type PrettyUrlResult = {
11
11
  matched: boolean;
12
12
  path?: string;
13
13
  };
14
14
 
15
+ let APP_ROUTES: string[] = [];
16
+
17
+ const legacyRoutes = Object.values(ROUTES);
18
+
19
+ try {
20
+ const generatedRoutes = require('routes/generated-routes');
21
+ const allRoutes = [...legacyRoutes, ...(generatedRoutes || [])];
22
+ APP_ROUTES = Array.from(new Set(allRoutes));
23
+ logger.debug('Loaded merged routes (legacy + generated)', {
24
+ count: APP_ROUTES.length
25
+ });
26
+ } catch (error) {
27
+ APP_ROUTES = legacyRoutes;
28
+ logger.debug('Loaded only legacy routes', { count: APP_ROUTES.length });
29
+ }
30
+
15
31
  const resolvePrettyUrlHandler =
16
32
  (pathname: string, ip: string | null) => async () => {
17
33
  let results = <{ old_path: string }[]>[];
@@ -53,7 +69,8 @@ const resolvePrettyUrl = async (
53
69
  locale,
54
70
  resolvePrettyUrlHandler(pathname, ip),
55
71
  {
56
- useProxy: true
72
+ useProxy: true,
73
+ compressed: true
57
74
  }
58
75
  );
59
76
  };
@@ -73,9 +90,7 @@ const withPrettyUrl =
73
90
  const isValidPrettyUrlPath = (pathname: string) => {
74
91
  return (
75
92
  new RegExp(/^\/[a-zA-Z0-9/]+(?:-[a-zA-Z0-9/]+)*$/).test(pathname) &&
76
- !Object.entries(ROUTES).find(([, value]) =>
77
- new RegExp(`^${value}$`).test(pathname)
78
- )
93
+ !APP_ROUTES.some((route) => new RegExp(`^${route}$`).test(pathname))
79
94
  );
80
95
  };
81
96
  const ip = req.headers.get('x-forwarded-for') ?? '';
@@ -108,8 +123,6 @@ const withPrettyUrl =
108
123
  return middleware(req, event);
109
124
  }
110
125
 
111
- req.middlewareParams.found = false;
112
-
113
126
  return middleware(req, event);
114
127
  };
115
128
 
@@ -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) {
@@ -35,18 +36,22 @@ const withRedirectionPayment =
35
36
  const searchParams = new URLSearchParams(url.search);
36
37
  const ip = req.headers.get('x-forwarded-for') ?? '';
37
38
  const sessionId = req.cookies.get('osessionid');
39
+ const currentLocale = req.middlewareParams?.rewrites?.locale;
38
40
 
39
41
  if (searchParams.get('page') !== 'RedirectionPageCompletePage') {
40
42
  return middleware(req, event);
41
43
  }
42
44
 
43
- 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}`;
44
47
  const requestHeaders = {
45
48
  'X-Requested-With': 'XMLHttpRequest',
46
49
  'Content-Type': 'application/x-www-form-urlencoded',
47
50
  Cookie: req.headers.get('cookie') ?? '',
48
51
  'x-currency': req.cookies.get('pz-currency')?.value ?? '',
49
- 'x-forwarded-for': ip
52
+ 'x-forwarded-for': ip,
53
+ 'Accept-Language':
54
+ currentLocale ?? req.cookies.get('pz-locale')?.value ?? ''
50
55
  };
51
56
 
52
57
  try {
@@ -60,17 +65,9 @@ const withRedirectionPayment =
60
65
  ip
61
66
  }
62
67
  );
63
-
64
- return NextResponse.redirect(
65
- `${url.origin}${getUrlPathWithLocale(
66
- '/orders/checkout/',
67
- req.cookies.get('pz-locale')?.value
68
- )}`,
69
- 303
70
- );
71
68
  }
72
69
 
73
- const request = await fetch(requestUrl, {
70
+ const fetchResponse = await fetch(requestUrl, {
74
71
  method: 'POST',
75
72
  headers: requestHeaders,
76
73
  body
@@ -78,14 +75,14 @@ const withRedirectionPayment =
78
75
 
79
76
  logger.info('Complete redirection payment request', {
80
77
  requestUrl,
81
- status: request.status,
78
+ status: fetchResponse.status,
82
79
  requestHeaders,
83
80
  ip
84
81
  });
85
82
 
86
- const response = await request.json();
83
+ const responseData = await fetchResponse.json();
87
84
 
88
- const { context_list: contextList, errors } = response;
85
+ const { context_list: contextList, errors } = responseData;
89
86
  const redirectionContext = contextList?.find(
90
87
  (context) => context.page_context?.redirect_url
91
88
  );
@@ -99,18 +96,27 @@ const withRedirectionPayment =
99
96
  ip
100
97
  });
101
98
 
102
- return NextResponse.redirect(
99
+ const errorResponse = NextResponse.redirect(
103
100
  `${url.origin}${getUrlPathWithLocale(
104
101
  '/orders/checkout/',
105
102
  req.cookies.get('pz-locale')?.value
106
103
  )}`,
107
- {
108
- status: 303,
109
- headers: {
110
- 'Set-Cookie': `pz-pos-error=${JSON.stringify(errors)}; path=/;`
111
- }
112
- }
104
+ 303
105
+ );
106
+
107
+ // Forward set-cookie headers from the upstream response
108
+ const setCookies = fetchResponse.headers.getSetCookie?.() ?? [];
109
+ setCookies.forEach((cookie) => {
110
+ errorResponse.headers.append('Set-Cookie', cookie);
111
+ });
112
+
113
+ // Add error cookie
114
+ errorResponse.headers.append(
115
+ 'Set-Cookie',
116
+ `pz-pos-error=${JSON.stringify(errors)}; path=/;`
113
117
  );
118
+
119
+ return errorResponse;
114
120
  }
115
121
 
116
122
  logger.info('Order success page context list', {
@@ -125,7 +131,7 @@ const withRedirectionPayment =
125
131
  {
126
132
  middleware: 'redirection-payment',
127
133
  requestHeaders,
128
- response: JSON.stringify(response),
134
+ response: JSON.stringify(responseData),
129
135
  ip
130
136
  }
131
137
  );
@@ -153,10 +159,11 @@ const withRedirectionPayment =
153
159
  // So we use 303 status code to change the method to GET
154
160
  const nextResponse = NextResponse.redirect(redirectUrlWithLocale, 303);
155
161
 
156
- nextResponse.headers.set(
157
- 'Set-Cookie',
158
- request.headers.get('set-cookie') ?? ''
159
- );
162
+ // Forward all set-cookie headers from the upstream response
163
+ const setCookies = fetchResponse.headers.getSetCookie?.() ?? [];
164
+ setCookies.forEach((cookie) => {
165
+ nextResponse.headers.append('Set-Cookie', cookie);
166
+ });
160
167
 
161
168
  return nextResponse;
162
169
  } catch (error) {
@@ -4,6 +4,8 @@ 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 { ServerVariables } from '../utils/server-variables';
8
+ import { getCheckoutPath } from '../utils';
7
9
 
8
10
  const streamToString = async (stream: ReadableStream<Uint8Array> | null) => {
9
11
  if (stream) {
@@ -34,18 +36,22 @@ const withSavedCardRedirection =
34
36
  const url = req.nextUrl.clone();
35
37
  const ip = req.headers.get('x-forwarded-for') ?? '';
36
38
  const sessionId = req.cookies.get('osessionid');
39
+ const currentLocale = req.middlewareParams?.rewrites?.locale;
37
40
 
38
41
  if (url.search.indexOf('SavedCardThreeDSecurePage') === -1) {
39
42
  return middleware(req, event);
40
43
  }
41
44
 
42
- 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}`;
43
47
  const requestHeaders = {
44
48
  'X-Requested-With': 'XMLHttpRequest',
45
49
  'Content-Type': 'application/x-www-form-urlencoded',
46
50
  Cookie: req.headers.get('cookie') ?? '',
47
51
  'x-currency': req.cookies.get('pz-currency')?.value ?? '',
48
- 'x-forwarded-for': ip
52
+ 'x-forwarded-for': ip,
53
+ 'Accept-Language':
54
+ currentLocale ?? req.cookies.get('pz-locale')?.value ?? ''
49
55
  };
50
56
 
51
57
  try {
@@ -59,17 +65,9 @@ const withSavedCardRedirection =
59
65
  ip
60
66
  }
61
67
  );
62
-
63
- return NextResponse.redirect(
64
- `${url.origin}${getUrlPathWithLocale(
65
- '/orders/checkout/',
66
- req.cookies.get('pz-locale')?.value
67
- )}`,
68
- 303
69
- );
70
68
  }
71
69
 
72
- const request = await fetch(requestUrl, {
70
+ const fetchResponse = await fetch(requestUrl, {
73
71
  method: 'POST',
74
72
  headers: requestHeaders,
75
73
  body
@@ -77,14 +75,14 @@ const withSavedCardRedirection =
77
75
 
78
76
  logger.info('Complete 3D payment request', {
79
77
  requestUrl,
80
- status: request.status,
78
+ status: fetchResponse.status,
81
79
  requestHeaders,
82
80
  ip
83
81
  });
84
82
 
85
- const response = await request.json();
83
+ const responseData = await fetchResponse.json();
86
84
 
87
- const { context_list: contextList, errors } = response;
85
+ const { context_list: contextList, errors } = responseData;
88
86
  const redirectionContext = contextList?.find(
89
87
  (context) => context.page_context?.redirect_url
90
88
  );
@@ -98,18 +96,27 @@ const withSavedCardRedirection =
98
96
  ip
99
97
  });
100
98
 
101
- return NextResponse.redirect(
99
+ const errorResponse = NextResponse.redirect(
102
100
  `${url.origin}${getUrlPathWithLocale(
103
101
  '/orders/checkout/',
104
102
  req.cookies.get('pz-locale')?.value
105
103
  )}`,
106
- {
107
- status: 303,
108
- headers: {
109
- 'Set-Cookie': `pz-pos-error=${JSON.stringify(errors)}; path=/;`
110
- }
111
- }
104
+ 303
105
+ );
106
+
107
+ // Forward set-cookie headers from the upstream response
108
+ const setCookies = fetchResponse.headers.getSetCookie?.() ?? [];
109
+ setCookies.forEach((cookie) => {
110
+ errorResponse.headers.append('Set-Cookie', cookie);
111
+ });
112
+
113
+ // Add error cookie
114
+ errorResponse.headers.append(
115
+ 'Set-Cookie',
116
+ `pz-pos-error=${JSON.stringify(errors)}; path=/;`
112
117
  );
118
+
119
+ return errorResponse;
113
120
  }
114
121
 
115
122
  logger.info('Order success page context list', {
@@ -124,7 +131,7 @@ const withSavedCardRedirection =
124
131
  {
125
132
  middleware: 'saved-card-redirection',
126
133
  requestHeaders,
127
- response: JSON.stringify(response),
134
+ response: JSON.stringify(responseData),
128
135
  ip
129
136
  }
130
137
  );
@@ -152,10 +159,11 @@ const withSavedCardRedirection =
152
159
  // So we use 303 status code to change the method to GET
153
160
  const nextResponse = NextResponse.redirect(redirectUrlWithLocale, 303);
154
161
 
155
- nextResponse.headers.set(
156
- 'Set-Cookie',
157
- request.headers.get('set-cookie') ?? ''
158
- );
162
+ // Forward all set-cookie headers from the upstream response
163
+ const setCookies = fetchResponse.headers.getSetCookie?.() ?? [];
164
+ setCookies.forEach((cookie) => {
165
+ nextResponse.headers.append('Set-Cookie', cookie);
166
+ });
159
167
 
160
168
  return nextResponse;
161
169
  } catch (error) {
@@ -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) {
@@ -34,18 +35,22 @@ const withThreeDRedirection =
34
35
  const url = req.nextUrl.clone();
35
36
  const ip = req.headers.get('x-forwarded-for') ?? '';
36
37
  const sessionId = req.cookies.get('osessionid');
38
+ const currentLocale = req.middlewareParams?.rewrites?.locale;
37
39
 
38
40
  if (url.search.indexOf('CreditCardThreeDSecurePage') === -1) {
39
41
  return middleware(req, event);
40
42
  }
41
43
 
42
- 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}`;
43
46
  const requestHeaders = {
44
47
  'X-Requested-With': 'XMLHttpRequest',
45
48
  'Content-Type': 'application/x-www-form-urlencoded',
46
49
  Cookie: req.headers.get('cookie') ?? '',
47
50
  'x-currency': req.cookies.get('pz-currency')?.value ?? '',
48
- 'x-forwarded-for': ip
51
+ 'x-forwarded-for': ip,
52
+ 'Accept-Language':
53
+ currentLocale ?? req.cookies.get('pz-locale')?.value ?? ''
49
54
  };
50
55
 
51
56
  try {
@@ -59,17 +64,9 @@ const withThreeDRedirection =
59
64
  ip
60
65
  }
61
66
  );
62
-
63
- return NextResponse.redirect(
64
- `${url.origin}${getUrlPathWithLocale(
65
- '/orders/checkout/',
66
- req.cookies.get('pz-locale')?.value
67
- )}`,
68
- 303
69
- );
70
67
  }
71
68
 
72
- const request = await fetch(requestUrl, {
69
+ const fetchResponse = await fetch(requestUrl, {
73
70
  method: 'POST',
74
71
  headers: requestHeaders,
75
72
  body
@@ -77,14 +74,14 @@ const withThreeDRedirection =
77
74
 
78
75
  logger.info('Complete 3D payment request', {
79
76
  requestUrl,
80
- status: request.status,
77
+ status: fetchResponse.status,
81
78
  requestHeaders,
82
79
  ip
83
80
  });
84
81
 
85
- const response = await request.json();
82
+ const responseData = await fetchResponse.json();
86
83
 
87
- const { context_list: contextList, errors } = response;
84
+ const { context_list: contextList, errors } = responseData;
88
85
  const redirectionContext = contextList?.find(
89
86
  (context) => context.page_context?.redirect_url
90
87
  );
@@ -98,18 +95,27 @@ const withThreeDRedirection =
98
95
  ip
99
96
  });
100
97
 
101
- return NextResponse.redirect(
98
+ const errorResponse = NextResponse.redirect(
102
99
  `${url.origin}${getUrlPathWithLocale(
103
100
  '/orders/checkout/',
104
101
  req.cookies.get('pz-locale')?.value
105
102
  )}`,
106
- {
107
- status: 303,
108
- headers: {
109
- 'Set-Cookie': `pz-pos-error=${JSON.stringify(errors)}; path=/;`
110
- }
111
- }
103
+ 303
104
+ );
105
+
106
+ // Forward set-cookie headers from the upstream response
107
+ const setCookies = fetchResponse.headers.getSetCookie?.() ?? [];
108
+ setCookies.forEach((cookie) => {
109
+ errorResponse.headers.append('Set-Cookie', cookie);
110
+ });
111
+
112
+ // Add error cookie
113
+ errorResponse.headers.append(
114
+ 'Set-Cookie',
115
+ `pz-pos-error=${JSON.stringify(errors)}; path=/;`
112
116
  );
117
+
118
+ return errorResponse;
113
119
  }
114
120
 
115
121
  logger.info('Order success page context list', {
@@ -124,7 +130,7 @@ const withThreeDRedirection =
124
130
  {
125
131
  middleware: 'three-d-redirection',
126
132
  requestHeaders,
127
- response: JSON.stringify(response),
133
+ response: JSON.stringify(responseData),
128
134
  ip
129
135
  }
130
136
  );
@@ -152,10 +158,11 @@ const withThreeDRedirection =
152
158
  // So we use 303 status code to change the method to GET
153
159
  const nextResponse = NextResponse.redirect(redirectUrlWithLocale, 303);
154
160
 
155
- nextResponse.headers.set(
156
- 'Set-Cookie',
157
- request.headers.get('set-cookie') ?? ''
158
- );
161
+ // Forward all set-cookie headers from the upstream response
162
+ const setCookies = fetchResponse.headers.getSetCookie?.() ?? [];
163
+ setCookies.forEach((cookie) => {
164
+ nextResponse.headers.append('Set-Cookie', cookie);
165
+ });
159
166
 
160
167
  return nextResponse;
161
168
  } catch (error) {