@akinon/next 2.0.0-beta.2 → 2.0.0-beta.20
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/.eslintrc.js +12 -0
- package/CHANGELOG.md +377 -7
- package/__tests__/next-config.test.ts +83 -0
- package/__tests__/tsconfig.json +23 -0
- package/api/auth.ts +133 -44
- package/api/barcode-search.ts +59 -0
- package/api/cache.ts +41 -5
- package/api/client.ts +21 -4
- package/api/form.ts +85 -0
- package/api/image-proxy.ts +75 -0
- package/api/product-categories.ts +53 -0
- package/api/similar-product-list.ts +63 -0
- package/api/similar-products.ts +111 -0
- package/api/virtual-try-on.ts +382 -0
- package/assets/styles/index.scss +84 -0
- package/babel.config.js +6 -0
- package/bin/pz-generate-routes.js +115 -0
- package/bin/pz-prebuild.js +1 -0
- package/bin/pz-predev.js +1 -0
- package/bin/pz-run-tests.js +99 -0
- package/bin/run-prebuild-tests.js +46 -0
- package/components/accordion.tsx +20 -5
- package/components/button.tsx +51 -36
- package/components/client-root.tsx +138 -2
- package/components/file-input.tsx +65 -3
- package/components/index.ts +1 -0
- package/components/input.tsx +1 -1
- package/components/link.tsx +46 -16
- package/components/logger-popup.tsx +213 -0
- package/components/modal.tsx +32 -16
- package/components/plugin-module.tsx +62 -3
- package/components/price.tsx +2 -2
- package/components/select.tsx +1 -1
- package/components/selected-payment-option-view.tsx +21 -0
- package/components/theme-editor/blocks/accordion-block.tsx +136 -0
- package/components/theme-editor/blocks/block-renderer-registry.tsx +77 -0
- package/components/theme-editor/blocks/button-block.tsx +593 -0
- package/components/theme-editor/blocks/counter-block.tsx +348 -0
- package/components/theme-editor/blocks/divider-block.tsx +20 -0
- package/components/theme-editor/blocks/embed-block.tsx +208 -0
- package/components/theme-editor/blocks/group-block.tsx +116 -0
- package/components/theme-editor/blocks/hotspot-block.tsx +147 -0
- package/components/theme-editor/blocks/icon-block.tsx +230 -0
- package/components/theme-editor/blocks/image-block.tsx +137 -0
- package/components/theme-editor/blocks/image-gallery-block.tsx +269 -0
- package/components/theme-editor/blocks/input-block.tsx +123 -0
- package/components/theme-editor/blocks/link-block.tsx +216 -0
- package/components/theme-editor/blocks/lottie-block.tsx +325 -0
- package/components/theme-editor/blocks/map-block.tsx +89 -0
- package/components/theme-editor/blocks/slider-block.tsx +595 -0
- package/components/theme-editor/blocks/tab-block.tsx +10 -0
- package/components/theme-editor/blocks/text-block.tsx +52 -0
- package/components/theme-editor/blocks/video-block.tsx +122 -0
- package/components/theme-editor/components/action-toolbar.tsx +305 -0
- package/components/theme-editor/components/designer-overlay.tsx +74 -0
- package/components/theme-editor/components/with-designer-features.tsx +142 -0
- package/components/theme-editor/dynamic-font-loader.tsx +79 -0
- package/components/theme-editor/hooks/use-designer-features.tsx +100 -0
- package/components/theme-editor/hooks/use-external-designer.tsx +95 -0
- package/components/theme-editor/hooks/use-native-widget-data.ts +188 -0
- package/components/theme-editor/hooks/use-visibility-context.ts +27 -0
- package/components/theme-editor/placeholder-registry.ts +31 -0
- package/components/theme-editor/sections/before-after-section.tsx +245 -0
- package/components/theme-editor/sections/contact-form-section.tsx +563 -0
- package/components/theme-editor/sections/countdown-campaign-banner-section.tsx +433 -0
- package/components/theme-editor/sections/coupon-banner-section.tsx +710 -0
- package/components/theme-editor/sections/divider-section.tsx +62 -0
- package/components/theme-editor/sections/featured-product-spotlight-section.tsx +507 -0
- package/components/theme-editor/sections/find-in-store-section.tsx +1995 -0
- package/components/theme-editor/sections/hover-showcase-section.tsx +326 -0
- package/components/theme-editor/sections/image-hotspot-section.tsx +142 -0
- package/components/theme-editor/sections/installment-options-section.tsx +1065 -0
- package/components/theme-editor/sections/notification-banner-section.tsx +173 -0
- package/components/theme-editor/sections/order-tracking-lookup-section.tsx +1379 -0
- package/components/theme-editor/sections/posts-slider-section.tsx +472 -0
- package/components/theme-editor/sections/pre-order-launch-banner-section.tsx +663 -0
- package/components/theme-editor/sections/section-renderer-registry.tsx +89 -0
- package/components/theme-editor/sections/section-wrapper.tsx +135 -0
- package/components/theme-editor/sections/shipping-threshold-progress-section.tsx +586 -0
- package/components/theme-editor/sections/stats-counter-section.tsx +486 -0
- package/components/theme-editor/sections/tabs-section.tsx +578 -0
- package/components/theme-editor/theme-block.tsx +102 -0
- package/components/theme-editor/theme-placeholder-client.tsx +218 -0
- package/components/theme-editor/theme-placeholder-wrapper.tsx +732 -0
- package/components/theme-editor/theme-placeholder.tsx +288 -0
- package/components/theme-editor/theme-section.tsx +1224 -0
- package/components/theme-editor/theme-settings-context.tsx +13 -0
- package/components/theme-editor/utils/index.ts +792 -0
- package/components/theme-editor/utils/iterator-utils.ts +234 -0
- package/components/theme-editor/utils/publish-window.ts +86 -0
- package/components/theme-editor/utils/visibility-rules.ts +188 -0
- package/data/client/account.ts +17 -2
- package/data/client/api.ts +2 -0
- package/data/client/basket.ts +66 -5
- package/data/client/checkout.ts +391 -99
- package/data/client/misc.ts +38 -2
- package/data/client/product.ts +19 -2
- package/data/client/user.ts +16 -8
- package/data/server/category.ts +11 -9
- package/data/server/flatpage.ts +11 -4
- package/data/server/form.ts +15 -4
- package/data/server/landingpage.ts +11 -4
- package/data/server/list.ts +5 -4
- package/data/server/menu.ts +11 -3
- package/data/server/product.ts +111 -55
- package/data/server/seo.ts +14 -4
- package/data/server/special-page.ts +5 -4
- package/data/server/widget.ts +90 -5
- package/data/urls.ts +16 -5
- package/hocs/client/with-segment-defaults.tsx +2 -2
- package/hocs/server/with-segment-defaults.tsx +65 -20
- package/hooks/index.ts +4 -0
- package/hooks/use-localization.ts +24 -10
- package/hooks/use-logger-context.tsx +114 -0
- package/hooks/use-logger.ts +92 -0
- package/hooks/use-loyalty-availability.ts +21 -0
- package/hooks/use-payment-options.ts +2 -1
- package/hooks/use-pz-params.ts +37 -0
- package/hooks/use-router.ts +51 -14
- package/hooks/use-sentry-uncaught-errors.ts +24 -0
- package/instrumentation/index.ts +10 -1
- package/instrumentation/node.ts +2 -20
- package/jest.config.js +25 -0
- package/lib/cache-handler.mjs +534 -16
- package/lib/cache.ts +272 -37
- package/localization/index.ts +2 -1
- package/localization/provider.tsx +2 -5
- package/middlewares/bfcache-headers.ts +18 -0
- package/middlewares/checkout-provider.ts +1 -1
- package/middlewares/complete-gpay.ts +32 -26
- package/middlewares/complete-masterpass.ts +33 -26
- package/middlewares/complete-wallet.ts +182 -0
- package/middlewares/default.ts +360 -215
- package/middlewares/index.ts +10 -2
- package/middlewares/locale.ts +34 -11
- package/middlewares/masterpass-rest-callback.ts +230 -0
- package/middlewares/oauth-login.ts +200 -57
- package/middlewares/pretty-url.ts +21 -8
- package/middlewares/redirection-payment.ts +32 -26
- package/middlewares/saved-card-redirection.ts +33 -26
- package/middlewares/three-d-redirection.ts +32 -26
- package/middlewares/url-redirection.ts +11 -1
- package/middlewares/wallet-complete-redirection.ts +206 -0
- package/package.json +25 -10
- package/plugins.d.ts +19 -4
- package/plugins.js +10 -1
- package/redux/actions.ts +47 -0
- package/redux/middlewares/checkout.ts +63 -138
- package/redux/middlewares/index.ts +14 -10
- package/redux/middlewares/pre-order/address.ts +7 -2
- package/redux/middlewares/pre-order/attribute-based-shipping-option.ts +7 -1
- package/redux/middlewares/pre-order/data-source-shipping-option.ts +7 -1
- package/redux/middlewares/pre-order/delivery-option.ts +7 -1
- package/redux/middlewares/pre-order/index.ts +16 -10
- package/redux/middlewares/pre-order/installment-option.ts +8 -1
- package/redux/middlewares/pre-order/payment-option-reset.ts +37 -0
- package/redux/middlewares/pre-order/payment-option.ts +7 -1
- package/redux/middlewares/pre-order/pre-order-validation.ts +8 -3
- package/redux/middlewares/pre-order/redirection.ts +8 -2
- package/redux/middlewares/pre-order/set-pre-order.ts +6 -2
- package/redux/middlewares/pre-order/shipping-option.ts +7 -1
- package/redux/middlewares/pre-order/shipping-step.ts +5 -1
- package/redux/reducers/checkout.ts +23 -3
- package/redux/reducers/index.ts +11 -3
- package/redux/reducers/root.ts +7 -2
- package/redux/reducers/widget.ts +80 -0
- package/sentry/index.ts +69 -13
- package/tailwind/content.js +16 -0
- package/types/commerce/account.ts +5 -1
- package/types/commerce/checkout.ts +35 -1
- package/types/commerce/widget.ts +33 -0
- package/types/index.ts +101 -6
- package/types/next-auth.d.ts +2 -2
- package/types/widget.ts +80 -0
- package/utils/app-fetch.ts +7 -2
- package/utils/generate-commerce-search-params.ts +3 -2
- package/utils/get-checkout-path.ts +3 -0
- package/utils/get-root-hostname.ts +28 -0
- package/utils/index.ts +64 -10
- package/utils/localization.ts +4 -0
- package/utils/mobile-3d-iframe.ts +8 -2
- package/utils/override-middleware.ts +7 -12
- package/utils/pz-segments.ts +92 -0
- package/utils/redirect-ignore.ts +35 -0
- package/utils/redirect.ts +9 -3
- package/utils/redirection-iframe.ts +8 -2
- package/utils/widget-styles.ts +107 -0
- package/views/error-page.tsx +93 -0
- package/with-pz-config.js +13 -6
|
@@ -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
|
|
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
|
|
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:
|
|
78
|
+
status: fetchResponse.status,
|
|
82
79
|
requestHeaders,
|
|
83
80
|
ip
|
|
84
81
|
});
|
|
85
82
|
|
|
86
|
-
const
|
|
83
|
+
const responseData = await fetchResponse.json();
|
|
87
84
|
|
|
88
|
-
const { context_list: contextList, errors } =
|
|
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,26 @@ const withRedirectionPayment =
|
|
|
99
96
|
ip
|
|
100
97
|
});
|
|
101
98
|
|
|
102
|
-
|
|
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
|
|
113
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.cookies.set('pz-pos-error', JSON.stringify(errors), {
|
|
115
|
+
path: '/'
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
return errorResponse;
|
|
114
119
|
}
|
|
115
120
|
|
|
116
121
|
logger.info('Order success page context list', {
|
|
@@ -125,7 +130,7 @@ const withRedirectionPayment =
|
|
|
125
130
|
{
|
|
126
131
|
middleware: 'redirection-payment',
|
|
127
132
|
requestHeaders,
|
|
128
|
-
response: JSON.stringify(
|
|
133
|
+
response: JSON.stringify(responseData),
|
|
129
134
|
ip
|
|
130
135
|
}
|
|
131
136
|
);
|
|
@@ -153,10 +158,11 @@ const withRedirectionPayment =
|
|
|
153
158
|
// So we use 303 status code to change the method to GET
|
|
154
159
|
const nextResponse = NextResponse.redirect(redirectUrlWithLocale, 303);
|
|
155
160
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
+
});
|
|
160
166
|
|
|
161
167
|
return nextResponse;
|
|
162
168
|
} 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
|
|
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
|
|
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:
|
|
78
|
+
status: fetchResponse.status,
|
|
81
79
|
requestHeaders,
|
|
82
80
|
ip
|
|
83
81
|
});
|
|
84
82
|
|
|
85
|
-
const
|
|
83
|
+
const responseData = await fetchResponse.json();
|
|
86
84
|
|
|
87
|
-
const { context_list: contextList, errors } =
|
|
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,26 @@ const withSavedCardRedirection =
|
|
|
98
96
|
ip
|
|
99
97
|
});
|
|
100
98
|
|
|
101
|
-
|
|
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
|
|
112
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.cookies.set('pz-pos-error', JSON.stringify(errors), {
|
|
115
|
+
path: '/'
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
return errorResponse;
|
|
113
119
|
}
|
|
114
120
|
|
|
115
121
|
logger.info('Order success page context list', {
|
|
@@ -124,7 +130,7 @@ const withSavedCardRedirection =
|
|
|
124
130
|
{
|
|
125
131
|
middleware: 'saved-card-redirection',
|
|
126
132
|
requestHeaders,
|
|
127
|
-
response: JSON.stringify(
|
|
133
|
+
response: JSON.stringify(responseData),
|
|
128
134
|
ip
|
|
129
135
|
}
|
|
130
136
|
);
|
|
@@ -152,10 +158,11 @@ const withSavedCardRedirection =
|
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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) {
|
|
@@ -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
|
|
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
|
|
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:
|
|
77
|
+
status: fetchResponse.status,
|
|
81
78
|
requestHeaders,
|
|
82
79
|
ip
|
|
83
80
|
});
|
|
84
81
|
|
|
85
|
-
const
|
|
82
|
+
const responseData = await fetchResponse.json();
|
|
86
83
|
|
|
87
|
-
const { context_list: contextList, errors } =
|
|
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,26 @@ const withThreeDRedirection =
|
|
|
98
95
|
ip
|
|
99
96
|
});
|
|
100
97
|
|
|
101
|
-
|
|
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
|
|
112
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.cookies.set('pz-pos-error', JSON.stringify(errors), {
|
|
114
|
+
path: '/'
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
return errorResponse;
|
|
113
118
|
}
|
|
114
119
|
|
|
115
120
|
logger.info('Order success page context list', {
|
|
@@ -124,7 +129,7 @@ const withThreeDRedirection =
|
|
|
124
129
|
{
|
|
125
130
|
middleware: 'three-d-redirection',
|
|
126
131
|
requestHeaders,
|
|
127
|
-
response: JSON.stringify(
|
|
132
|
+
response: JSON.stringify(responseData),
|
|
128
133
|
ip
|
|
129
134
|
}
|
|
130
135
|
);
|
|
@@ -152,10 +157,11 @@ const withThreeDRedirection =
|
|
|
152
157
|
// So we use 303 status code to change the method to GET
|
|
153
158
|
const nextResponse = NextResponse.redirect(redirectUrlWithLocale, 303);
|
|
154
159
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
160
|
+
// Forward all set-cookie headers from the upstream response
|
|
161
|
+
const setCookies = fetchResponse.headers.getSetCookie?.() ?? [];
|
|
162
|
+
setCookies.forEach((cookie) => {
|
|
163
|
+
nextResponse.headers.append('Set-Cookie', cookie);
|
|
164
|
+
});
|
|
159
165
|
|
|
160
166
|
return nextResponse;
|
|
161
167
|
} catch (error) {
|
|
@@ -4,6 +4,7 @@ import { PzNextRequest } from '.';
|
|
|
4
4
|
import logger from '../utils/log';
|
|
5
5
|
import { urlLocaleMatcherRegex } from '../utils';
|
|
6
6
|
import { getUrlPathWithLocale } from '../utils/localization';
|
|
7
|
+
import { shouldIgnoreRedirect } from '../utils/redirect-ignore';
|
|
7
8
|
import { ROUTES } from 'routes';
|
|
8
9
|
|
|
9
10
|
// This middleware is used to handle url redirections set in Omnitron
|
|
@@ -50,7 +51,7 @@ const withUrlRedirection =
|
|
|
50
51
|
const location = request.headers.get('location');
|
|
51
52
|
const redirectUrl = new URL(
|
|
52
53
|
request.headers.get('location'),
|
|
53
|
-
location.startsWith('http') ? '' :
|
|
54
|
+
location.startsWith('http') ? '' : url.origin
|
|
54
55
|
);
|
|
55
56
|
|
|
56
57
|
redirectUrl.pathname = getUrlPathWithLocale(
|
|
@@ -60,6 +61,15 @@ const withUrlRedirection =
|
|
|
60
61
|
|
|
61
62
|
const setCookies = request.headers.getSetCookie();
|
|
62
63
|
|
|
64
|
+
if (
|
|
65
|
+
shouldIgnoreRedirect(
|
|
66
|
+
url.pathname,
|
|
67
|
+
req.middlewareParams.rewrites.locale
|
|
68
|
+
)
|
|
69
|
+
) {
|
|
70
|
+
return middleware(req, event);
|
|
71
|
+
}
|
|
72
|
+
|
|
63
73
|
const response = NextResponse.redirect(redirectUrl.toString(), {
|
|
64
74
|
status: request.status
|
|
65
75
|
});
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { NextFetchEvent, NextMiddleware, NextResponse } from 'next/server';
|
|
2
|
+
import Settings from 'settings';
|
|
3
|
+
import { Buffer } from 'buffer';
|
|
4
|
+
import logger from '../utils/log';
|
|
5
|
+
import { getUrlPathWithLocale } from '../utils/localization';
|
|
6
|
+
import { PzNextRequest } from '.';
|
|
7
|
+
import { getCheckoutPath } from '../utils';
|
|
8
|
+
|
|
9
|
+
const streamToString = async (stream: ReadableStream<Uint8Array> | null) => {
|
|
10
|
+
if (stream) {
|
|
11
|
+
const chunks = [];
|
|
12
|
+
let result = '';
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
for await (const chunk of stream as any) {
|
|
16
|
+
chunks.push(Buffer.from(chunk));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
result = Buffer.concat(chunks).toString('utf-8');
|
|
20
|
+
} catch (error) {
|
|
21
|
+
logger.error('Error while reading body stream', {
|
|
22
|
+
middleware: 'wallet-complete-redirection',
|
|
23
|
+
error
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const withWalletCompleteRedirection =
|
|
33
|
+
(middleware: NextMiddleware) =>
|
|
34
|
+
async (req: PzNextRequest, event: NextFetchEvent) => {
|
|
35
|
+
const url = req.nextUrl.clone();
|
|
36
|
+
const ip = req.headers.get('x-forwarded-for') ?? '';
|
|
37
|
+
const sessionId = req.cookies.get('osessionid');
|
|
38
|
+
|
|
39
|
+
if (url.search.indexOf('WalletCompletePage') === -1) {
|
|
40
|
+
return middleware(req, event);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const isPostCheckout = req.cookies.get('pz-post-checkout-flow')?.value === 'true';
|
|
44
|
+
const requestUrl = `${Settings.commerceUrl}${getCheckoutPath(isPostCheckout)}${url.search}`;
|
|
45
|
+
const requestHeaders = {
|
|
46
|
+
'X-Requested-With': 'XMLHttpRequest',
|
|
47
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
48
|
+
Cookie: req.headers.get('cookie') ?? '',
|
|
49
|
+
'x-currency': req.cookies.get('pz-currency')?.value ?? '',
|
|
50
|
+
'x-forwarded-for': ip
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const existingBody = await streamToString(req.body);
|
|
55
|
+
const queryParams = new URLSearchParams(url.search);
|
|
56
|
+
const bodyParams = new URLSearchParams();
|
|
57
|
+
|
|
58
|
+
if (existingBody) {
|
|
59
|
+
try {
|
|
60
|
+
const existingParams = new URLSearchParams(existingBody);
|
|
61
|
+
|
|
62
|
+
existingParams.forEach((value, key) => {
|
|
63
|
+
bodyParams.append(key, value);
|
|
64
|
+
});
|
|
65
|
+
} catch {
|
|
66
|
+
logger.error('Error parsing existing body as URL-encoded data', {
|
|
67
|
+
middleware: 'wallet-complete-redirection',
|
|
68
|
+
existingBody,
|
|
69
|
+
ip
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
queryParams.forEach((value, key) => {
|
|
75
|
+
bodyParams.append(key, value);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const body = bodyParams.toString();
|
|
79
|
+
|
|
80
|
+
if (!sessionId) {
|
|
81
|
+
logger.warn(
|
|
82
|
+
'Make sure that the SESSION_COOKIE_SAMESITE environment variable is set to None in Commerce.',
|
|
83
|
+
{
|
|
84
|
+
middleware: 'wallet-complete-redirection',
|
|
85
|
+
ip
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const fetchResponse = await fetch(requestUrl, {
|
|
91
|
+
method: 'POST',
|
|
92
|
+
headers: requestHeaders,
|
|
93
|
+
body
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
logger.info('Complete wallet payment request', {
|
|
97
|
+
requestUrl,
|
|
98
|
+
status: fetchResponse.status,
|
|
99
|
+
requestHeaders,
|
|
100
|
+
ip
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const responseData = await fetchResponse.json();
|
|
104
|
+
|
|
105
|
+
const { context_list: contextList, errors } = responseData;
|
|
106
|
+
const redirectionContext = contextList?.find(
|
|
107
|
+
(context) => context.page_context?.redirect_url
|
|
108
|
+
);
|
|
109
|
+
const redirectUrl = redirectionContext?.page_context?.redirect_url;
|
|
110
|
+
|
|
111
|
+
if (errors && Object.keys(errors).length) {
|
|
112
|
+
logger.error('Error while completing wallet payment', {
|
|
113
|
+
middleware: 'wallet-complete-redirection',
|
|
114
|
+
errors,
|
|
115
|
+
requestHeaders,
|
|
116
|
+
ip
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const errorResponse = NextResponse.redirect(
|
|
120
|
+
`${url.origin}${getUrlPathWithLocale(
|
|
121
|
+
'/orders/checkout/',
|
|
122
|
+
req.cookies.get('pz-locale')?.value
|
|
123
|
+
)}`,
|
|
124
|
+
303
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
// Forward set-cookie headers from the upstream response
|
|
128
|
+
const setCookies = fetchResponse.headers.getSetCookie?.() ?? [];
|
|
129
|
+
setCookies.forEach((cookie) => {
|
|
130
|
+
errorResponse.headers.append('Set-Cookie', cookie);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Add error cookie
|
|
134
|
+
errorResponse.cookies.set('pz-pos-error', JSON.stringify(errors), {
|
|
135
|
+
path: '/'
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
return errorResponse;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
logger.info('Order success page context list', {
|
|
142
|
+
middleware: 'wallet-complete-redirection',
|
|
143
|
+
contextList,
|
|
144
|
+
ip
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
if (!redirectUrl) {
|
|
148
|
+
logger.warn(
|
|
149
|
+
'No redirection url for order success page found in page_context. Redirecting to checkout page.',
|
|
150
|
+
{
|
|
151
|
+
middleware: 'wallet-complete-redirection',
|
|
152
|
+
requestHeaders,
|
|
153
|
+
response: JSON.stringify(responseData),
|
|
154
|
+
ip
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
const redirectUrlWithLocale = `${url.origin}${getUrlPathWithLocale(
|
|
159
|
+
'/orders/checkout/',
|
|
160
|
+
req.cookies.get('pz-locale')?.value
|
|
161
|
+
)}`;
|
|
162
|
+
|
|
163
|
+
return NextResponse.redirect(redirectUrlWithLocale, 303);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const redirectUrlWithLocale = `${url.origin}${getUrlPathWithLocale(
|
|
167
|
+
redirectUrl,
|
|
168
|
+
req.cookies.get('pz-locale')?.value
|
|
169
|
+
)}`;
|
|
170
|
+
|
|
171
|
+
logger.info('Redirecting to order success page', {
|
|
172
|
+
middleware: 'wallet-complete-redirection',
|
|
173
|
+
redirectUrlWithLocale,
|
|
174
|
+
ip
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Using POST method while redirecting causes an error,
|
|
178
|
+
// So we use 303 status code to change the method to GET
|
|
179
|
+
const nextResponse = NextResponse.redirect(redirectUrlWithLocale, 303);
|
|
180
|
+
|
|
181
|
+
// Forward all set-cookie headers from the upstream response
|
|
182
|
+
const setCookies = fetchResponse.headers.getSetCookie?.() ?? [];
|
|
183
|
+
setCookies.forEach((cookie) => {
|
|
184
|
+
nextResponse.headers.append('Set-Cookie', cookie);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
return nextResponse;
|
|
188
|
+
} catch (error) {
|
|
189
|
+
logger.error('Error while completing wallet payment', {
|
|
190
|
+
middleware: 'wallet-complete-redirection',
|
|
191
|
+
error,
|
|
192
|
+
requestHeaders,
|
|
193
|
+
ip
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
return NextResponse.redirect(
|
|
197
|
+
`${url.origin}${getUrlPathWithLocale(
|
|
198
|
+
'/orders/checkout/',
|
|
199
|
+
req.cookies.get('pz-locale')?.value
|
|
200
|
+
)}`,
|
|
201
|
+
303
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
export default withWalletCompleteRedirection;
|
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": "2.0.0-beta.
|
|
4
|
+
"version": "2.0.0-beta.20",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -13,30 +13,45 @@
|
|
|
13
13
|
"pz-predev": "bin/pz-predev.js",
|
|
14
14
|
"pz-postdev": "bin/pz-postdev.js"
|
|
15
15
|
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"test": "jest"
|
|
18
|
+
},
|
|
16
19
|
"dependencies": {
|
|
20
|
+
"@mongodb-js/zstd": "^2.0.1",
|
|
21
|
+
"@neshca/cache-handler": "1.9.0",
|
|
17
22
|
"@opentelemetry/exporter-trace-otlp-http": "0.46.0",
|
|
18
23
|
"@opentelemetry/resources": "1.19.0",
|
|
19
24
|
"@opentelemetry/sdk-node": "0.46.0",
|
|
20
25
|
"@opentelemetry/sdk-trace-node": "1.19.0",
|
|
21
26
|
"@opentelemetry/semantic-conventions": "1.19.0",
|
|
22
|
-
"@reduxjs/toolkit": "
|
|
23
|
-
"@
|
|
27
|
+
"@reduxjs/toolkit": "2.11.2",
|
|
28
|
+
"@sentry/nextjs": "10.39.0",
|
|
24
29
|
"cross-spawn": "7.0.3",
|
|
25
30
|
"generic-pool": "3.9.0",
|
|
26
|
-
"
|
|
31
|
+
"lottie-web": "^5.13.0",
|
|
32
|
+
"react-multi-carousel": "2.8.4",
|
|
33
|
+
"react-redux": "9.2.0",
|
|
27
34
|
"react-string-replace": "1.1.1",
|
|
28
35
|
"redis": "4.6.13",
|
|
29
36
|
"semver": "7.6.2",
|
|
30
37
|
"set-cookie-parser": "2.6.0"
|
|
31
38
|
},
|
|
32
39
|
"devDependencies": {
|
|
33
|
-
"@akinon/eslint-plugin-projectzero": "2.0.0-beta.
|
|
40
|
+
"@akinon/eslint-plugin-projectzero": "2.0.0-beta.20",
|
|
41
|
+
"@babel/core": "7.26.10",
|
|
42
|
+
"@babel/preset-env": "7.26.9",
|
|
43
|
+
"@babel/preset-typescript": "7.27.0",
|
|
44
|
+
"@types/jest": "29.5.14",
|
|
34
45
|
"@types/react-redux": "7.1.30",
|
|
35
46
|
"@types/set-cookie-parser": "2.4.7",
|
|
36
|
-
"@typescript-eslint/eslint-plugin": "
|
|
37
|
-
"@typescript-eslint/parser": "
|
|
38
|
-
"
|
|
39
|
-
"eslint
|
|
40
|
-
"eslint-config-
|
|
47
|
+
"@typescript-eslint/eslint-plugin": "6.7.4",
|
|
48
|
+
"@typescript-eslint/parser": "6.7.4",
|
|
49
|
+
"babel-jest": "29.7.0",
|
|
50
|
+
"eslint": "8.56.0",
|
|
51
|
+
"eslint-config-next": "16.1.6",
|
|
52
|
+
"eslint-config-prettier": "8.5.0",
|
|
53
|
+
"jest": "29.7.0",
|
|
54
|
+
"ts-jest": "29.3.2",
|
|
55
|
+
"typescript": "5.9.3"
|
|
41
56
|
}
|
|
42
57
|
}
|