@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 +26 -0
- package/api/barcode-search.ts +59 -0
- package/bin/pz-generate-routes.js +10 -1
- package/components/plugin-module.tsx +2 -2
- package/data/client/account.ts +12 -1
- package/data/client/checkout.ts +100 -70
- package/data/urls.ts +5 -1
- package/lib/cache-handler.mjs +8 -2
- package/lib/cache.ts +8 -3
- package/middlewares/bfcache-headers.ts +18 -0
- package/middlewares/complete-gpay.ts +3 -1
- package/middlewares/complete-masterpass.ts +3 -1
- package/middlewares/complete-wallet.ts +3 -1
- package/middlewares/default.ts +62 -19
- package/middlewares/index.ts +3 -1
- package/middlewares/masterpass-rest-callback.ts +4 -1
- package/middlewares/redirection-payment.ts +3 -1
- package/middlewares/saved-card-redirection.ts +3 -1
- package/middlewares/three-d-redirection.ts +3 -1
- package/middlewares/wallet-complete-redirection.ts +3 -1
- package/package.json +2 -2
- package/plugins.d.ts +10 -0
- package/plugins.js +1 -0
- package/redux/middlewares/checkout.ts +11 -1
- package/redux/middlewares/pre-order/installment-option.ts +9 -1
- package/types/index.ts +6 -0
- package/utils/get-checkout-path.ts +3 -0
- package/utils/index.ts +18 -0
- package/utils/mobile-3d-iframe.ts +8 -2
- package/utils/redirection-iframe.ts +8 -2
- package/with-pz-config.js +1 -4
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 = [
|
|
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]],
|
package/data/client/account.ts
CHANGED
|
@@ -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;
|
package/data/client/checkout.ts
CHANGED
|
@@ -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
|
|
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:
|
|
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:
|
|
207
|
+
url: buildCheckoutRequestUrl(checkout.guestLogin, {})
|
|
191
208
|
})
|
|
192
209
|
}),
|
|
193
210
|
fetchCheckoutResult: build.query<{ order: Order }, string>({
|
|
194
211
|
query: (token: string) =>
|
|
195
|
-
|
|
212
|
+
buildCheckoutRequestUrl(checkout.fetchCheckoutResult(token))
|
|
196
213
|
}),
|
|
197
214
|
get3dRedirectForm: build.query<{ result: string }, void>({
|
|
198
215
|
query: () =>
|
|
199
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
-
|
|
267
|
+
reduxStore.getState().checkout?.preOrder?.payment_option;
|
|
245
268
|
|
|
246
269
|
if (paymentOption?.payment_type === 'masterpass') {
|
|
247
|
-
|
|
248
|
-
url:
|
|
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
|
-
|
|
270
|
-
url:
|
|
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 =
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
550
|
+
async queryFn(binNumber, _queryApi, _extraOptions, baseQuery) {
|
|
551
|
+
const reduxStore = await getStore();
|
|
526
552
|
const paymentOption =
|
|
527
|
-
|
|
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
|
-
|
|
534
|
-
url:
|
|
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
|
-
|
|
578
|
+
async queryFn(pk, _queryApi, _extraOptions, baseQuery) {
|
|
579
|
+
const reduxStore = await getStore();
|
|
552
580
|
const paymentOption =
|
|
553
|
-
|
|
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
|
-
|
|
559
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
776
|
+
url: buildCheckoutRequestUrl(checkout.loyaltyMoneyUsage)
|
|
747
777
|
})
|
|
748
778
|
}),
|
|
749
779
|
payWithLoyaltyBalance: build.mutation<any, string>({
|
|
750
780
|
query: (amount) => ({
|
|
751
|
-
url:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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 = {
|
package/lib/cache-handler.mjs
CHANGED
|
@@ -364,11 +364,17 @@ CacheHandler.onCreation(async () => {
|
|
|
364
364
|
};
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
-
const
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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/middlewares/default.ts
CHANGED
|
@@ -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(
|
|
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.
|
|
153
|
-
req.nextUrl.
|
|
154
|
-
|
|
155
|
-
|
|
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
|
|
160
|
-
|
|
161
|
-
|
|
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
|
|
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
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
)
|
package/middlewares/index.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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
|
@@ -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
|
-
|
|
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
|
};
|
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
|
-
|
|
5
|
-
|
|
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
|
-
|
|
5
|
-
|
|
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