@akinon/next 1.66.0 → 1.68.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 +21 -0
- package/api/client.ts +23 -2
- package/components/plugin-module.tsx +1 -1
- package/data/client/api.ts +1 -1
- package/data/client/basket.ts +6 -5
- package/data/client/checkout.ts +38 -10
- package/data/urls.ts +11 -3
- package/middlewares/complete-gpay.ts +1 -1
- package/middlewares/complete-masterpass.ts +1 -1
- package/middlewares/currency.ts +1 -0
- package/middlewares/default.ts +194 -165
- package/middlewares/index.ts +3 -1
- package/middlewares/oauth-login.ts +6 -1
- package/middlewares/saved-card-redirection.ts +179 -0
- package/middlewares/three-d-redirection.ts +1 -1
- package/package.json +2 -2
- package/plugins.d.ts +10 -3
- package/redux/middlewares/checkout.ts +38 -6
- package/redux/reducers/checkout.ts +16 -3
- package/redux/reducers/index.ts +3 -1
- package/types/commerce/checkout.ts +13 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @akinon/next
|
|
2
2
|
|
|
3
|
+
## 1.68.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 907813c: ZERO-2934: add payment-gateway/<gateway> redirect in middleware
|
|
8
|
+
- c873740: ZERO-2903: add types
|
|
9
|
+
- d899cc7: ZERO-2925: Login by checking the session id
|
|
10
|
+
- 57d1657: ZERO-2925: Update condation
|
|
11
|
+
- 3be7462: ZERO-2934: fix reset basket on redirection payment complete
|
|
12
|
+
- 034b813: ZERO-2903: create saved card plugin
|
|
13
|
+
- 48d508f: ZERO-2989: refine cookie header in payment middlewares
|
|
14
|
+
|
|
15
|
+
## 1.67.0
|
|
16
|
+
|
|
17
|
+
### Minor Changes
|
|
18
|
+
|
|
19
|
+
- bc2b411: ZERO-2825: Add attribute-based shipping options to checkout page
|
|
20
|
+
- 3d2212a: ZERO-2745: Add multi basket support
|
|
21
|
+
- 8f47cca: ZERO-2829: fix and add nested JSON support for useFormData in api/client route
|
|
22
|
+
- 9e25a64: ZERO-2835: Update category page layout with breadcrumb
|
|
23
|
+
|
|
3
24
|
## 1.66.0
|
|
4
25
|
|
|
5
26
|
### Minor Changes
|
package/api/client.ts
CHANGED
|
@@ -4,6 +4,7 @@ import settings from 'settings';
|
|
|
4
4
|
import logger from '../utils/log';
|
|
5
5
|
import formatCookieString from '../utils/format-cookie-string';
|
|
6
6
|
import cookieParser from 'set-cookie-parser';
|
|
7
|
+
import { cookies } from 'next/headers';
|
|
7
8
|
|
|
8
9
|
interface RouteParams {
|
|
9
10
|
params: {
|
|
@@ -80,11 +81,27 @@ async function proxyRequest(...args) {
|
|
|
80
81
|
}
|
|
81
82
|
} as RequestInit;
|
|
82
83
|
|
|
84
|
+
const nextCookies = cookies();
|
|
85
|
+
const segment = nextCookies.get('pz-segment')?.value;
|
|
86
|
+
const currency = nextCookies.get('pz-external-currency')?.value;
|
|
87
|
+
|
|
88
|
+
if (segment) {
|
|
89
|
+
fetchOptions.headers['X-Segment-Id'] = segment;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (currency) {
|
|
93
|
+
fetchOptions.headers = Object.assign({}, fetchOptions.headers, {
|
|
94
|
+
'x-currency': currency
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
83
98
|
if (options.contentType) {
|
|
84
99
|
fetchOptions.headers['Content-Type'] = options.contentType;
|
|
85
100
|
}
|
|
86
101
|
|
|
87
|
-
const isMultipartFormData = req.headers
|
|
102
|
+
const isMultipartFormData = req.headers
|
|
103
|
+
.get('content-type')
|
|
104
|
+
?.includes('multipart/form-data;');
|
|
88
105
|
|
|
89
106
|
if (req.method !== 'GET') {
|
|
90
107
|
let body: Record<string, any> | FormData = {};
|
|
@@ -108,7 +125,11 @@ async function proxyRequest(...args) {
|
|
|
108
125
|
|
|
109
126
|
Object.keys(body ?? {}).forEach((key) => {
|
|
110
127
|
if (body[key]) {
|
|
111
|
-
|
|
128
|
+
if (typeof body[key] === 'object' && body[key] !== null) {
|
|
129
|
+
formData.append(key, JSON.stringify(body[key]));
|
|
130
|
+
} else {
|
|
131
|
+
formData.append(key, body[key]);
|
|
132
|
+
}
|
|
112
133
|
}
|
|
113
134
|
});
|
|
114
135
|
|
package/data/client/api.ts
CHANGED
package/data/client/basket.ts
CHANGED
|
@@ -28,7 +28,7 @@ export const basketApi = api.injectEndpoints({
|
|
|
28
28
|
query: ({ namespace }) =>
|
|
29
29
|
buildClientRequestUrl(basket.getBasketDetail(namespace)),
|
|
30
30
|
transformResponse: (response: { basket: Basket }) => response.basket,
|
|
31
|
-
providesTags: ['
|
|
31
|
+
providesTags: ['MultiBasket']
|
|
32
32
|
}),
|
|
33
33
|
getAllBaskets: build.query<Basket[], void>({
|
|
34
34
|
query: () =>
|
|
@@ -36,7 +36,7 @@ export const basketApi = api.injectEndpoints({
|
|
|
36
36
|
contentType: 'application/json'
|
|
37
37
|
}),
|
|
38
38
|
transformResponse: (response: { baskets: Basket[] }) => response.baskets,
|
|
39
|
-
providesTags: ['
|
|
39
|
+
providesTags: ['MultiBasket']
|
|
40
40
|
}),
|
|
41
41
|
removeBasket: build.mutation<Basket, { pk: number }>({
|
|
42
42
|
query: ({ pk }) => ({
|
|
@@ -46,7 +46,7 @@ export const basketApi = api.injectEndpoints({
|
|
|
46
46
|
method: 'DELETE',
|
|
47
47
|
body: { pk }
|
|
48
48
|
}),
|
|
49
|
-
invalidatesTags: ['
|
|
49
|
+
invalidatesTags: ['MultiBasket', 'Basket']
|
|
50
50
|
}),
|
|
51
51
|
selectMainBasket: build.mutation<Basket, { pk: number }>({
|
|
52
52
|
query: ({ pk }) => ({
|
|
@@ -57,7 +57,7 @@ export const basketApi = api.injectEndpoints({
|
|
|
57
57
|
body: { pk }
|
|
58
58
|
}),
|
|
59
59
|
transformResponse: (response: { baskets: Basket }) => response.baskets,
|
|
60
|
-
invalidatesTags: ['
|
|
60
|
+
invalidatesTags: ['MultiBasket', 'Basket']
|
|
61
61
|
}),
|
|
62
62
|
updateQuantity: build.mutation<
|
|
63
63
|
UpdateQuantityResponse,
|
|
@@ -69,7 +69,8 @@ export const basketApi = api.injectEndpoints({
|
|
|
69
69
|
}),
|
|
70
70
|
method: 'PUT',
|
|
71
71
|
body
|
|
72
|
-
})
|
|
72
|
+
}),
|
|
73
|
+
invalidatesTags: ['MultiBasket', 'Basket']
|
|
73
74
|
}),
|
|
74
75
|
clearBasket: build.mutation<Basket, void>({
|
|
75
76
|
query: (body) => ({
|
package/data/client/checkout.ts
CHANGED
|
@@ -57,6 +57,7 @@ export interface CompleteCreditCardParams {
|
|
|
57
57
|
card_month: string;
|
|
58
58
|
card_year: string;
|
|
59
59
|
use_three_d?: boolean;
|
|
60
|
+
save?: boolean;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
interface GetContractResponse {
|
|
@@ -228,7 +229,8 @@ export const checkoutApi = api.injectEndpoints({
|
|
|
228
229
|
card_number,
|
|
229
230
|
card_month,
|
|
230
231
|
card_year,
|
|
231
|
-
use_three_d = true
|
|
232
|
+
use_three_d = true,
|
|
233
|
+
save = undefined
|
|
232
234
|
}) => {
|
|
233
235
|
const paymentOption =
|
|
234
236
|
store.getState().checkout?.preOrder?.payment_option;
|
|
@@ -242,20 +244,26 @@ export const checkoutApi = api.injectEndpoints({
|
|
|
242
244
|
};
|
|
243
245
|
}
|
|
244
246
|
|
|
247
|
+
const body: Record<string, string> = {
|
|
248
|
+
agreement: '1',
|
|
249
|
+
use_three_d: use_three_d ? '1' : '0',
|
|
250
|
+
card_cvv,
|
|
251
|
+
card_holder,
|
|
252
|
+
card_month,
|
|
253
|
+
card_number,
|
|
254
|
+
card_year
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
if (save !== undefined) {
|
|
258
|
+
body.save = save ? '1' : '0';
|
|
259
|
+
}
|
|
260
|
+
|
|
245
261
|
return {
|
|
246
262
|
url: buildClientRequestUrl(checkout.completeCreditCardPayment, {
|
|
247
263
|
useFormData: true
|
|
248
264
|
}),
|
|
249
265
|
method: 'POST',
|
|
250
|
-
body
|
|
251
|
-
agreement: '1',
|
|
252
|
-
use_three_d: use_three_d ? '1' : '0',
|
|
253
|
-
card_cvv,
|
|
254
|
-
card_holder,
|
|
255
|
-
card_month,
|
|
256
|
-
card_number,
|
|
257
|
-
card_year
|
|
258
|
-
}
|
|
266
|
+
body
|
|
259
267
|
};
|
|
260
268
|
},
|
|
261
269
|
async onQueryStarted(args, { dispatch, queryFulfilled }) {
|
|
@@ -697,6 +705,25 @@ export const checkoutApi = api.injectEndpoints({
|
|
|
697
705
|
};
|
|
698
706
|
}
|
|
699
707
|
}),
|
|
708
|
+
setAttributeBasedShippingOptions: build.mutation<
|
|
709
|
+
CheckoutResponse,
|
|
710
|
+
Record<string, number>
|
|
711
|
+
>({
|
|
712
|
+
query: (options) => ({
|
|
713
|
+
url: buildClientRequestUrl(checkout.setAttributeBasedShippingOption, {
|
|
714
|
+
useFormData: true
|
|
715
|
+
}),
|
|
716
|
+
method: 'POST',
|
|
717
|
+
body: {
|
|
718
|
+
attribute_based_shipping_options: JSON.stringify(options)
|
|
719
|
+
}
|
|
720
|
+
}),
|
|
721
|
+
async onQueryStarted(arg, { dispatch, queryFulfilled }) {
|
|
722
|
+
dispatch(setShippingStepBusy(true));
|
|
723
|
+
await queryFulfilled;
|
|
724
|
+
dispatch(setShippingStepBusy(false));
|
|
725
|
+
}
|
|
726
|
+
}),
|
|
700
727
|
setOrderSelectionPage: build.mutation<
|
|
701
728
|
CheckoutResponse,
|
|
702
729
|
{ extra_field: ExtraField }
|
|
@@ -747,5 +774,6 @@ export const {
|
|
|
747
774
|
usePayWithLoyaltyBalanceMutation,
|
|
748
775
|
useSetOrderNoteMutation,
|
|
749
776
|
useSetDeliveryBagsMutation,
|
|
777
|
+
useSetAttributeBasedShippingOptionsMutation,
|
|
750
778
|
useSetOrderSelectionPageMutation
|
|
751
779
|
} = checkoutApi;
|
package/data/urls.ts
CHANGED
|
@@ -17,16 +17,22 @@ export const account = {
|
|
|
17
17
|
page,
|
|
18
18
|
limit,
|
|
19
19
|
createdDate,
|
|
20
|
-
endDate
|
|
20
|
+
endDate,
|
|
21
|
+
shipping_option_slug
|
|
21
22
|
}: {
|
|
22
23
|
page?: number;
|
|
23
24
|
limit?: number;
|
|
24
25
|
createdDate?: string;
|
|
25
26
|
endDate?: string;
|
|
27
|
+
shipping_option_slug?: string;
|
|
26
28
|
}) =>
|
|
27
|
-
`/users/orders/?page=${page || 1}&limit=${limit || 12}
|
|
29
|
+
`/users/orders/?page=${page || 1}&limit=${limit || 12}${
|
|
28
30
|
createdDate ? `&created_date__gte=${createdDate}` : ''
|
|
29
|
-
}
|
|
31
|
+
}${endDate ? `&created_date__lte=${endDate}` : ''}${
|
|
32
|
+
shipping_option_slug
|
|
33
|
+
? `&shipping_option_slug=${shipping_option_slug}`
|
|
34
|
+
: ''
|
|
35
|
+
}`,
|
|
30
36
|
getQuotations: (page?: number, status?: string, limit?: number) =>
|
|
31
37
|
`/b2b/my-quotations/?page=${page || 1}` +
|
|
32
38
|
(status ? `&status=${status}` : '') +
|
|
@@ -111,6 +117,8 @@ export const checkout = {
|
|
|
111
117
|
loyaltyMoneyUsage: '/orders/checkout/?page=LoyaltyMoneyUsagePage',
|
|
112
118
|
setOrderNote: '/orders/checkout/?page=OrderNotePage',
|
|
113
119
|
couponSelectionPage: '/orders/checkout/?page=CouponSelectionPage',
|
|
120
|
+
setAttributeBasedShippingOption:
|
|
121
|
+
'/orders/checkout/?page=AttributeBasedShippingOptionSelectionPage',
|
|
114
122
|
deliveryBagsPage: '/orders/checkout/?page=DeliveryBagsPage',
|
|
115
123
|
setOrderSelectionPage: '/orders/checkout/?page=OrderSelectionPage'
|
|
116
124
|
};
|
|
@@ -43,7 +43,7 @@ const withCompleteGpay =
|
|
|
43
43
|
const requestHeaders = {
|
|
44
44
|
'X-Requested-With': 'XMLHttpRequest',
|
|
45
45
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
46
|
-
Cookie:
|
|
46
|
+
Cookie: req.headers.get('cookie') ?? '',
|
|
47
47
|
'x-currency': req.cookies.get('pz-currency')?.value ?? '',
|
|
48
48
|
'x-forwarded-for': ip
|
|
49
49
|
};
|
|
@@ -43,7 +43,7 @@ const withCompleteMasterpass =
|
|
|
43
43
|
const requestHeaders = {
|
|
44
44
|
'X-Requested-With': 'XMLHttpRequest',
|
|
45
45
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
46
|
-
Cookie:
|
|
46
|
+
Cookie: req.headers.get('cookie') ?? '',
|
|
47
47
|
'x-currency': req.cookies.get('pz-currency')?.value ?? '',
|
|
48
48
|
'x-forwarded-for': ip
|
|
49
49
|
};
|
package/middlewares/currency.ts
CHANGED
|
@@ -75,6 +75,7 @@ const withCurrency =
|
|
|
75
75
|
url.pathname.match(urlLocaleMatcherRegex) &&
|
|
76
76
|
!url.search.includes('mobile_app=true') &&
|
|
77
77
|
!url.search.includes('page=CreditCardThreeDSecurePage') &&
|
|
78
|
+
!url.search.includes('page=RedirectionPageCompletePage') &&
|
|
78
79
|
settings.resetBasketOnCurrencyChange !== false
|
|
79
80
|
) {
|
|
80
81
|
logger.info('Currency changed. Resetting basket...', {
|
package/middlewares/default.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
withOauthLogin,
|
|
10
10
|
withPrettyUrl,
|
|
11
11
|
withRedirectionPayment,
|
|
12
|
+
withSavedCardRedirection,
|
|
12
13
|
withThreeDRedirection,
|
|
13
14
|
withUrlRedirection
|
|
14
15
|
} from '.';
|
|
@@ -118,6 +119,14 @@ const withPzDefault =
|
|
|
118
119
|
);
|
|
119
120
|
}
|
|
120
121
|
|
|
122
|
+
if (req.nextUrl.pathname.includes('/orders/saved-card-redirect')) {
|
|
123
|
+
return NextResponse.rewrite(
|
|
124
|
+
new URL(
|
|
125
|
+
`${encodeURI(Settings.commerceUrl)}/orders/saved-card-redirect/`
|
|
126
|
+
)
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
121
130
|
// If commerce redirects to /orders/checkout/ without locale
|
|
122
131
|
if (
|
|
123
132
|
req.nextUrl.pathname.match(new RegExp('^/orders/checkout/$')) &&
|
|
@@ -135,6 +144,25 @@ const withPzDefault =
|
|
|
135
144
|
return NextResponse.redirect(redirectUrlWithLocale, 303);
|
|
136
145
|
}
|
|
137
146
|
|
|
147
|
+
// Dynamically handle any payment gateway without specifying names
|
|
148
|
+
const paymentGatewayRegex = new RegExp('^/payment-gateway/([^/]+)/$');
|
|
149
|
+
const gatewayMatch = req.nextUrl.pathname.match(paymentGatewayRegex);
|
|
150
|
+
|
|
151
|
+
if (
|
|
152
|
+
gatewayMatch && // Check if the URL matches the /payment-gateway/<gateway> pattern
|
|
153
|
+
getUrlPathWithLocale(
|
|
154
|
+
`/payment-gateway/${gatewayMatch[1]}/`,
|
|
155
|
+
req.cookies.get('pz-locale')?.value
|
|
156
|
+
) !== req.nextUrl.pathname
|
|
157
|
+
) {
|
|
158
|
+
const redirectUrlWithLocale = `${url.origin}${getUrlPathWithLocale(
|
|
159
|
+
`/payment-gateway/${gatewayMatch[1]}/`,
|
|
160
|
+
req.cookies.get('pz-locale')?.value
|
|
161
|
+
)}?${req.nextUrl.searchParams.toString()}`;
|
|
162
|
+
|
|
163
|
+
return NextResponse.redirect(redirectUrlWithLocale);
|
|
164
|
+
}
|
|
165
|
+
|
|
138
166
|
if (req.nextUrl.pathname.startsWith('/orders/checkout-provider/')) {
|
|
139
167
|
try {
|
|
140
168
|
const data = await req.json();
|
|
@@ -182,129 +210,140 @@ const withPzDefault =
|
|
|
182
210
|
withUrlRedirection(
|
|
183
211
|
withCompleteGpay(
|
|
184
212
|
withCompleteMasterpass(
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
NextResponse
|
|
188
|
-
|
|
189
|
-
try {
|
|
190
|
-
const { locale, prettyUrl, currency } =
|
|
191
|
-
req.middlewareParams.rewrites;
|
|
192
|
-
const { defaultLocaleValue } =
|
|
193
|
-
Settings.localization;
|
|
194
|
-
const url = req.nextUrl.clone();
|
|
195
|
-
const pathnameWithoutLocale = url.pathname.replace(
|
|
196
|
-
urlLocaleMatcherRegex,
|
|
197
|
-
''
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
middlewareResult = (await middleware(
|
|
201
|
-
req,
|
|
202
|
-
event
|
|
203
|
-
)) as NextResponse | void;
|
|
204
|
-
|
|
205
|
-
let customRewriteUrlDiff = '';
|
|
206
|
-
|
|
207
|
-
if (
|
|
208
|
-
middlewareResult instanceof NextResponse &&
|
|
209
|
-
middlewareResult.headers.get(
|
|
210
|
-
'pz-override-response'
|
|
211
|
-
) &&
|
|
212
|
-
middlewareResult.headers.get(
|
|
213
|
-
'x-middleware-rewrite'
|
|
214
|
-
)
|
|
215
|
-
) {
|
|
216
|
-
const rewriteUrl = new URL(
|
|
217
|
-
middlewareResult.headers.get(
|
|
218
|
-
'x-middleware-rewrite'
|
|
219
|
-
)
|
|
220
|
-
);
|
|
221
|
-
const originalUrl = new URL(req.url);
|
|
222
|
-
customRewriteUrlDiff =
|
|
223
|
-
rewriteUrl.pathname.replace(
|
|
224
|
-
originalUrl.pathname,
|
|
225
|
-
''
|
|
226
|
-
);
|
|
227
|
-
}
|
|
213
|
+
withSavedCardRedirection(
|
|
214
|
+
async (req: PzNextRequest, event: NextFetchEvent) => {
|
|
215
|
+
let middlewareResult: NextResponse | void =
|
|
216
|
+
NextResponse.next();
|
|
228
217
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
Settings.usePrettyUrlRoute &&
|
|
238
|
-
url.searchParams.toString().length > 0 &&
|
|
239
|
-
!Object.entries(ROUTES).find(([, value]) =>
|
|
240
|
-
new RegExp(`^${value}/?$`).test(
|
|
241
|
-
pathnameWithoutLocale
|
|
242
|
-
)
|
|
243
|
-
)
|
|
244
|
-
) {
|
|
245
|
-
url.pathname =
|
|
246
|
-
url.pathname +
|
|
247
|
-
(/\/$/.test(url.pathname) ? '' : '/') +
|
|
248
|
-
`searchparams|${url.searchParams.toString()}`;
|
|
249
|
-
}
|
|
218
|
+
try {
|
|
219
|
+
const { locale, prettyUrl, currency } =
|
|
220
|
+
req.middlewareParams.rewrites;
|
|
221
|
+
const { defaultLocaleValue } =
|
|
222
|
+
Settings.localization;
|
|
223
|
+
const url = req.nextUrl.clone();
|
|
224
|
+
const pathnameWithoutLocale =
|
|
225
|
+
url.pathname.replace(urlLocaleMatcherRegex, '');
|
|
250
226
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
let pathname = url.pathname
|
|
256
|
-
.replace(/\/+$/, '')
|
|
257
|
-
.split('/');
|
|
258
|
-
url.pathname = url.pathname.replace(
|
|
259
|
-
pathname.pop(),
|
|
260
|
-
'pz-not-found'
|
|
261
|
-
);
|
|
262
|
-
}
|
|
227
|
+
middlewareResult = (await middleware(
|
|
228
|
+
req,
|
|
229
|
+
event
|
|
230
|
+
)) as NextResponse | void;
|
|
263
231
|
|
|
264
|
-
|
|
265
|
-
url.pathname = url.pathname.replace(
|
|
266
|
-
rewrite.source,
|
|
267
|
-
rewrite.destination
|
|
268
|
-
);
|
|
269
|
-
});
|
|
232
|
+
let customRewriteUrlDiff = '';
|
|
270
233
|
|
|
271
|
-
// if middleware.ts has a return value for current url
|
|
272
|
-
if (middlewareResult instanceof NextResponse) {
|
|
273
|
-
// pz-override-response header is used to prevent 404 page for custom responses.
|
|
274
234
|
if (
|
|
235
|
+
middlewareResult instanceof NextResponse &&
|
|
275
236
|
middlewareResult.headers.get(
|
|
276
237
|
'pz-override-response'
|
|
277
|
-
) !== 'true'
|
|
278
|
-
) {
|
|
279
|
-
middlewareResult.headers.set(
|
|
280
|
-
'x-middleware-rewrite',
|
|
281
|
-
url.href
|
|
282
|
-
);
|
|
283
|
-
} else if (
|
|
284
|
-
middlewareResult.headers.get(
|
|
285
|
-
'x-middleware-rewrite'
|
|
286
238
|
) &&
|
|
287
239
|
middlewareResult.headers.get(
|
|
288
|
-
'
|
|
289
|
-
)
|
|
240
|
+
'x-middleware-rewrite'
|
|
241
|
+
)
|
|
290
242
|
) {
|
|
291
|
-
|
|
243
|
+
const rewriteUrl = new URL(
|
|
244
|
+
middlewareResult.headers.get(
|
|
245
|
+
'x-middleware-rewrite'
|
|
246
|
+
)
|
|
247
|
+
);
|
|
248
|
+
const originalUrl = new URL(req.url);
|
|
249
|
+
customRewriteUrlDiff =
|
|
250
|
+
rewriteUrl.pathname.replace(
|
|
251
|
+
originalUrl.pathname,
|
|
252
|
+
''
|
|
253
|
+
);
|
|
292
254
|
}
|
|
293
|
-
} else {
|
|
294
|
-
// if middleware.ts doesn't have a return value.
|
|
295
|
-
// e.g. NextResponse.next() doesn't exist in middleware.ts
|
|
296
255
|
|
|
297
|
-
|
|
298
|
-
|
|
256
|
+
url.basePath = `/${commerceUrl}`;
|
|
257
|
+
url.pathname = `/${
|
|
258
|
+
locale.length ? `${locale}/` : ''
|
|
259
|
+
}${currency}/${customRewriteUrlDiff}${
|
|
260
|
+
prettyUrl ?? pathnameWithoutLocale
|
|
261
|
+
}`.replace(/\/+/g, '/');
|
|
299
262
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
263
|
+
if (
|
|
264
|
+
Settings.usePrettyUrlRoute &&
|
|
265
|
+
url.searchParams.toString().length > 0 &&
|
|
266
|
+
!Object.entries(ROUTES).find(([, value]) =>
|
|
267
|
+
new RegExp(`^${value}/?$`).test(
|
|
268
|
+
pathnameWithoutLocale
|
|
269
|
+
)
|
|
270
|
+
)
|
|
271
|
+
) {
|
|
272
|
+
url.pathname =
|
|
273
|
+
url.pathname +
|
|
274
|
+
(/\/$/.test(url.pathname) ? '' : '/') +
|
|
275
|
+
`searchparams|${url.searchParams.toString()}`;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (
|
|
279
|
+
!req.middlewareParams.found &&
|
|
280
|
+
Settings.customNotFoundEnabled
|
|
281
|
+
) {
|
|
282
|
+
let pathname = url.pathname
|
|
283
|
+
.replace(/\/+$/, '')
|
|
284
|
+
.split('/');
|
|
285
|
+
url.pathname = url.pathname.replace(
|
|
286
|
+
pathname.pop(),
|
|
287
|
+
'pz-not-found'
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
Settings.rewrites.forEach((rewrite) => {
|
|
292
|
+
url.pathname = url.pathname.replace(
|
|
293
|
+
rewrite.source,
|
|
294
|
+
rewrite.destination
|
|
295
|
+
);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// if middleware.ts has a return value for current url
|
|
299
|
+
if (middlewareResult instanceof NextResponse) {
|
|
300
|
+
// pz-override-response header is used to prevent 404 page for custom responses.
|
|
301
|
+
if (
|
|
302
|
+
middlewareResult.headers.get(
|
|
303
|
+
'pz-override-response'
|
|
304
|
+
) !== 'true'
|
|
305
|
+
) {
|
|
306
|
+
middlewareResult.headers.set(
|
|
307
|
+
'x-middleware-rewrite',
|
|
308
|
+
url.href
|
|
309
|
+
);
|
|
310
|
+
} else if (
|
|
311
|
+
middlewareResult.headers.get(
|
|
312
|
+
'x-middleware-rewrite'
|
|
313
|
+
) &&
|
|
314
|
+
middlewareResult.headers.get(
|
|
315
|
+
'pz-override-response'
|
|
316
|
+
) === 'true'
|
|
317
|
+
) {
|
|
318
|
+
middlewareResult = NextResponse.rewrite(url);
|
|
319
|
+
}
|
|
320
|
+
} else {
|
|
321
|
+
// if middleware.ts doesn't have a return value.
|
|
322
|
+
// e.g. NextResponse.next() doesn't exist in middleware.ts
|
|
323
|
+
|
|
324
|
+
middlewareResult = NextResponse.rewrite(url);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (
|
|
328
|
+
!url.pathname.startsWith(`/${currency}/orders`)
|
|
329
|
+
) {
|
|
330
|
+
middlewareResult.cookies.set(
|
|
331
|
+
'pz-locale',
|
|
332
|
+
locale?.length > 0
|
|
333
|
+
? locale
|
|
334
|
+
: defaultLocaleValue,
|
|
335
|
+
{
|
|
336
|
+
sameSite: 'none',
|
|
337
|
+
secure: true,
|
|
338
|
+
expires: new Date(
|
|
339
|
+
Date.now() + 1000 * 60 * 60 * 24 * 7
|
|
340
|
+
) // 7 days
|
|
341
|
+
}
|
|
342
|
+
);
|
|
343
|
+
}
|
|
303
344
|
middlewareResult.cookies.set(
|
|
304
|
-
'pz-
|
|
305
|
-
|
|
306
|
-
? locale
|
|
307
|
-
: defaultLocaleValue,
|
|
345
|
+
'pz-currency',
|
|
346
|
+
currency,
|
|
308
347
|
{
|
|
309
348
|
sameSite: 'none',
|
|
310
349
|
secure: true,
|
|
@@ -313,76 +352,66 @@ const withPzDefault =
|
|
|
313
352
|
) // 7 days
|
|
314
353
|
}
|
|
315
354
|
);
|
|
316
|
-
}
|
|
317
|
-
middlewareResult.cookies.set(
|
|
318
|
-
'pz-currency',
|
|
319
|
-
currency,
|
|
320
|
-
{
|
|
321
|
-
sameSite: 'none',
|
|
322
|
-
secure: true,
|
|
323
|
-
expires: new Date(
|
|
324
|
-
Date.now() + 1000 * 60 * 60 * 24 * 7
|
|
325
|
-
) // 7 days
|
|
326
|
-
}
|
|
327
|
-
);
|
|
328
|
-
|
|
329
|
-
if (
|
|
330
|
-
req.cookies.get('pz-locale') &&
|
|
331
|
-
req.cookies.get('pz-locale').value !== locale
|
|
332
|
-
) {
|
|
333
|
-
logger.debug('Locale changed', {
|
|
334
|
-
locale,
|
|
335
|
-
oldLocale: req.cookies.get('pz-locale')?.value,
|
|
336
|
-
ip
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
middlewareResult.headers.set(
|
|
341
|
-
'pz-url',
|
|
342
|
-
req.nextUrl.toString()
|
|
343
|
-
);
|
|
344
355
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
'pz-
|
|
348
|
-
)
|
|
349
|
-
|
|
356
|
+
if (
|
|
357
|
+
req.cookies.get('pz-locale') &&
|
|
358
|
+
req.cookies.get('pz-locale').value !== locale
|
|
359
|
+
) {
|
|
360
|
+
logger.debug('Locale changed', {
|
|
361
|
+
locale,
|
|
362
|
+
oldLocale:
|
|
363
|
+
req.cookies.get('pz-locale')?.value,
|
|
364
|
+
ip
|
|
365
|
+
});
|
|
366
|
+
}
|
|
350
367
|
|
|
351
|
-
if (process.env.ACC_APP_VERSION) {
|
|
352
368
|
middlewareResult.headers.set(
|
|
353
|
-
'
|
|
354
|
-
|
|
369
|
+
'pz-url',
|
|
370
|
+
req.nextUrl.toString()
|
|
355
371
|
);
|
|
356
|
-
}
|
|
357
372
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
373
|
+
if (req.cookies.get('pz-set-currency')) {
|
|
374
|
+
middlewareResult.cookies.delete(
|
|
375
|
+
'pz-set-currency'
|
|
376
|
+
);
|
|
377
|
+
}
|
|
361
378
|
|
|
362
|
-
if (
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
middlewareResult.cookies.set(
|
|
367
|
-
'csrftoken',
|
|
368
|
-
csrf_token
|
|
379
|
+
if (process.env.ACC_APP_VERSION) {
|
|
380
|
+
middlewareResult.headers.set(
|
|
381
|
+
'acc-app-version',
|
|
382
|
+
process.env.ACC_APP_VERSION
|
|
369
383
|
);
|
|
370
384
|
}
|
|
385
|
+
|
|
386
|
+
// Set CSRF token if not set
|
|
387
|
+
try {
|
|
388
|
+
const url = `${Settings.commerceUrl}${user.csrfToken}`;
|
|
389
|
+
|
|
390
|
+
if (!req.cookies.get('csrftoken')) {
|
|
391
|
+
const { csrf_token } = await (
|
|
392
|
+
await fetch(url)
|
|
393
|
+
).json();
|
|
394
|
+
middlewareResult.cookies.set(
|
|
395
|
+
'csrftoken',
|
|
396
|
+
csrf_token
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
} catch (error) {
|
|
400
|
+
logger.error('CSRF Error', {
|
|
401
|
+
error,
|
|
402
|
+
ip
|
|
403
|
+
});
|
|
404
|
+
}
|
|
371
405
|
} catch (error) {
|
|
372
|
-
logger.error('
|
|
406
|
+
logger.error('withPzDefault Error', {
|
|
373
407
|
error,
|
|
374
408
|
ip
|
|
375
409
|
});
|
|
376
410
|
}
|
|
377
|
-
} catch (error) {
|
|
378
|
-
logger.error('withPzDefault Error', {
|
|
379
|
-
error,
|
|
380
|
-
ip
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
411
|
|
|
384
|
-
|
|
385
|
-
|
|
412
|
+
return middlewareResult;
|
|
413
|
+
}
|
|
414
|
+
)
|
|
386
415
|
)
|
|
387
416
|
)
|
|
388
417
|
)
|
package/middlewares/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ import withUrlRedirection from './url-redirection';
|
|
|
8
8
|
import withCompleteGpay from './complete-gpay';
|
|
9
9
|
import withCompleteMasterpass from './complete-masterpass';
|
|
10
10
|
import withCheckoutProvider from './checkout-provider';
|
|
11
|
+
import withSavedCardRedirection from './saved-card-redirection';
|
|
11
12
|
import { NextRequest } from 'next/server';
|
|
12
13
|
|
|
13
14
|
export {
|
|
@@ -20,7 +21,8 @@ export {
|
|
|
20
21
|
withUrlRedirection,
|
|
21
22
|
withCompleteGpay,
|
|
22
23
|
withCompleteMasterpass,
|
|
23
|
-
withCheckoutProvider
|
|
24
|
+
withCheckoutProvider,
|
|
25
|
+
withSavedCardRedirection
|
|
24
26
|
};
|
|
25
27
|
|
|
26
28
|
export interface PzNextRequest extends NextRequest {
|
|
@@ -58,7 +58,12 @@ const withOauthLogin =
|
|
|
58
58
|
return middleware(req, event);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
const currentSessionId = req.cookies.get('osessionid');
|
|
62
|
+
|
|
63
|
+
if (
|
|
64
|
+
req.cookies.get('messages')?.value.includes('Successfully signed in') ||
|
|
65
|
+
(currentSessionId && req.cookies.get('messages'))
|
|
66
|
+
) {
|
|
62
67
|
let redirectUrlWithLocale = `${url.origin}${getUrlPathWithLocale(
|
|
63
68
|
'/auth/oauth-login',
|
|
64
69
|
req.cookies.get('pz-locale')?.value
|
|
@@ -0,0 +1,179 @@
|
|
|
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
|
+
|
|
8
|
+
const streamToString = async (stream: ReadableStream<Uint8Array> | null) => {
|
|
9
|
+
if (stream) {
|
|
10
|
+
const chunks = [];
|
|
11
|
+
let result = '';
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
for await (const chunk of stream as any) {
|
|
15
|
+
chunks.push(Buffer.from(chunk));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
result = Buffer.concat(chunks).toString('utf-8');
|
|
19
|
+
} catch (error) {
|
|
20
|
+
logger.error('Error while reading body stream', {
|
|
21
|
+
middleware: 'saved-card-redirection',
|
|
22
|
+
error
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const withSavedCardRedirection =
|
|
32
|
+
(middleware: NextMiddleware) =>
|
|
33
|
+
async (req: PzNextRequest, event: NextFetchEvent) => {
|
|
34
|
+
const url = req.nextUrl.clone();
|
|
35
|
+
const ip = req.headers.get('x-forwarded-for') ?? '';
|
|
36
|
+
const sessionId = req.cookies.get('osessionid');
|
|
37
|
+
|
|
38
|
+
if (url.search.indexOf('SavedCardThreeDSecurePage') === -1) {
|
|
39
|
+
return middleware(req, event);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const requestUrl = `${Settings.commerceUrl}/orders/checkout/${url.search}`;
|
|
43
|
+
const requestHeaders = {
|
|
44
|
+
'X-Requested-With': 'XMLHttpRequest',
|
|
45
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
46
|
+
Cookie: req.headers.get('cookie') ?? '',
|
|
47
|
+
'x-currency': req.cookies.get('pz-currency')?.value ?? '',
|
|
48
|
+
'x-forwarded-for': ip
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
const body = await streamToString(req.body);
|
|
53
|
+
|
|
54
|
+
if (!sessionId) {
|
|
55
|
+
logger.warn(
|
|
56
|
+
'Make sure that the SESSION_COOKIE_SAMESITE environment variable is set to None in Commerce.',
|
|
57
|
+
{
|
|
58
|
+
middleware: 'saved-card-redirection',
|
|
59
|
+
ip
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
return NextResponse.redirect(
|
|
64
|
+
`${url.origin}${getUrlPathWithLocale(
|
|
65
|
+
'/orders/checkout/',
|
|
66
|
+
req.cookies.get('pz-locale')?.value
|
|
67
|
+
)}`,
|
|
68
|
+
303
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const request = await fetch(requestUrl, {
|
|
73
|
+
method: 'POST',
|
|
74
|
+
headers: requestHeaders,
|
|
75
|
+
body
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
logger.info('Complete 3D payment request', {
|
|
79
|
+
requestUrl,
|
|
80
|
+
status: request.status,
|
|
81
|
+
requestHeaders,
|
|
82
|
+
ip
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const response = await request.json();
|
|
86
|
+
|
|
87
|
+
const { context_list: contextList, errors } = response;
|
|
88
|
+
const redirectionContext = contextList?.find(
|
|
89
|
+
(context) => context.page_context?.redirect_url
|
|
90
|
+
);
|
|
91
|
+
const redirectUrl = redirectionContext?.page_context?.redirect_url;
|
|
92
|
+
|
|
93
|
+
if (errors && Object.keys(errors).length) {
|
|
94
|
+
logger.error('Error while completing 3D payment', {
|
|
95
|
+
middleware: 'saved-card-redirection',
|
|
96
|
+
errors,
|
|
97
|
+
requestHeaders,
|
|
98
|
+
ip
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return NextResponse.redirect(
|
|
102
|
+
`${url.origin}${getUrlPathWithLocale(
|
|
103
|
+
'/orders/checkout/',
|
|
104
|
+
req.cookies.get('pz-locale')?.value
|
|
105
|
+
)}`,
|
|
106
|
+
{
|
|
107
|
+
status: 303,
|
|
108
|
+
headers: {
|
|
109
|
+
'Set-Cookie': `pz-pos-error=${JSON.stringify(errors)}; path=/;`
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
logger.info('Order success page context list', {
|
|
116
|
+
middleware: 'saved-card-redirection',
|
|
117
|
+
contextList,
|
|
118
|
+
ip
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
if (!redirectUrl) {
|
|
122
|
+
logger.warn(
|
|
123
|
+
'No redirection url for order success page found in page_context. Redirecting to checkout page.',
|
|
124
|
+
{
|
|
125
|
+
middleware: 'saved-card-redirection',
|
|
126
|
+
requestHeaders,
|
|
127
|
+
response: JSON.stringify(response),
|
|
128
|
+
ip
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const redirectUrlWithLocale = `${url.origin}${getUrlPathWithLocale(
|
|
133
|
+
'/orders/checkout/',
|
|
134
|
+
req.cookies.get('pz-locale')?.value
|
|
135
|
+
)}`;
|
|
136
|
+
|
|
137
|
+
return NextResponse.redirect(redirectUrlWithLocale, 303);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const redirectUrlWithLocale = `${url.origin}${getUrlPathWithLocale(
|
|
141
|
+
redirectUrl,
|
|
142
|
+
req.cookies.get('pz-locale')?.value
|
|
143
|
+
)}`;
|
|
144
|
+
|
|
145
|
+
logger.info('Redirecting to order success page', {
|
|
146
|
+
middleware: 'saved-card-redirection',
|
|
147
|
+
redirectUrlWithLocale,
|
|
148
|
+
ip
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// Using POST method while redirecting causes an error,
|
|
152
|
+
// So we use 303 status code to change the method to GET
|
|
153
|
+
const nextResponse = NextResponse.redirect(redirectUrlWithLocale, 303);
|
|
154
|
+
|
|
155
|
+
nextResponse.headers.set(
|
|
156
|
+
'Set-Cookie',
|
|
157
|
+
request.headers.get('set-cookie') ?? ''
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
return nextResponse;
|
|
161
|
+
} catch (error) {
|
|
162
|
+
logger.error('Error while completing 3D payment', {
|
|
163
|
+
middleware: 'saved-card-redirection',
|
|
164
|
+
error,
|
|
165
|
+
requestHeaders,
|
|
166
|
+
ip
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
return NextResponse.redirect(
|
|
170
|
+
`${url.origin}${getUrlPathWithLocale(
|
|
171
|
+
'/orders/checkout/',
|
|
172
|
+
req.cookies.get('pz-locale')?.value
|
|
173
|
+
)}`,
|
|
174
|
+
303
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export default withSavedCardRedirection;
|
|
@@ -43,7 +43,7 @@ const withThreeDRedirection =
|
|
|
43
43
|
const requestHeaders = {
|
|
44
44
|
'X-Requested-With': 'XMLHttpRequest',
|
|
45
45
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
46
|
-
Cookie:
|
|
46
|
+
Cookie: req.headers.get('cookie') ?? '',
|
|
47
47
|
'x-currency': req.cookies.get('pz-currency')?.value ?? '',
|
|
48
48
|
'x-forwarded-for': ip
|
|
49
49
|
};
|
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.68.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"set-cookie-parser": "2.6.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@akinon/eslint-plugin-projectzero": "1.
|
|
33
|
+
"@akinon/eslint-plugin-projectzero": "1.68.0",
|
|
34
34
|
"@types/react-redux": "7.1.30",
|
|
35
35
|
"@types/set-cookie-parser": "2.4.7",
|
|
36
36
|
"@typescript-eslint/eslint-plugin": "6.7.4",
|
package/plugins.d.ts
CHANGED
|
@@ -24,7 +24,14 @@ declare module '@akinon/pz-otp/src/redux/reducer' {
|
|
|
24
24
|
export const hidePopup: any;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
declare module 'pz-saved-card' {
|
|
28
|
-
export const savedCardReducer:
|
|
29
|
-
export const
|
|
27
|
+
declare module '@akinon/pz-saved-card' {
|
|
28
|
+
export const savedCardReducer: any;
|
|
29
|
+
export const SavedCardOption: any;
|
|
30
|
+
export const savedCardReducer: any;
|
|
31
|
+
export const savedCardMiddleware: any;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
declare module '@akinon/pz-saved-card' {
|
|
35
|
+
export const iyzicoSavedCardReducer: any;
|
|
36
|
+
export const iyzicoSavedCardMiddleware: any;
|
|
30
37
|
}
|
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
import { Middleware } from '@reduxjs/toolkit';
|
|
4
4
|
import {
|
|
5
5
|
setAddressList,
|
|
6
|
+
setAttributeBasedShippingOptions,
|
|
6
7
|
setBankAccounts,
|
|
7
8
|
setCanGuestPurchase,
|
|
8
9
|
setCardType,
|
|
10
|
+
setCreditPaymentOptions,
|
|
11
|
+
setDataSourceShippingOptions,
|
|
9
12
|
setDeliveryOptions,
|
|
10
13
|
setErrors,
|
|
11
14
|
setHasGiftBox,
|
|
@@ -16,14 +19,12 @@ import {
|
|
|
16
19
|
setPreOrder,
|
|
17
20
|
setRetailStores,
|
|
18
21
|
setShippingOptions,
|
|
19
|
-
|
|
20
|
-
setShippingStepCompleted,
|
|
21
|
-
setCreditPaymentOptions
|
|
22
|
+
setShippingStepCompleted
|
|
22
23
|
} from '../../redux/reducers/checkout';
|
|
23
24
|
import { RootState, TypedDispatch } from 'redux/store';
|
|
24
25
|
import { checkoutApi } from '../../data/client/checkout';
|
|
25
26
|
import { CheckoutContext, PreOrder } from '../../types';
|
|
26
|
-
import { getCookie
|
|
27
|
+
import { getCookie } from '../../utils';
|
|
27
28
|
import settings from 'settings';
|
|
28
29
|
import { LocaleUrlStrategy } from '../../localization';
|
|
29
30
|
import { showMobile3dIframe } from '../../utils/mobile-3d-iframe';
|
|
@@ -77,7 +78,8 @@ export const preOrderMiddleware: Middleware = ({
|
|
|
77
78
|
shippingOptions,
|
|
78
79
|
dataSourceShippingOptions,
|
|
79
80
|
paymentOptions,
|
|
80
|
-
installmentOptions
|
|
81
|
+
installmentOptions,
|
|
82
|
+
attributeBasedShippingOptions
|
|
81
83
|
} = getState().checkout;
|
|
82
84
|
const { endpoints: apiEndpoints } = checkoutApi;
|
|
83
85
|
|
|
@@ -145,11 +147,33 @@ export const preOrderMiddleware: Middleware = ({
|
|
|
145
147
|
);
|
|
146
148
|
}
|
|
147
149
|
|
|
150
|
+
if (
|
|
151
|
+
Object.keys(attributeBasedShippingOptions).length > 0 &&
|
|
152
|
+
!preOrder.attribute_based_shipping_options
|
|
153
|
+
) {
|
|
154
|
+
const initialSelectedOptions: Record<string, number> = Object.fromEntries(
|
|
155
|
+
Object.entries(attributeBasedShippingOptions).map(([key, options]) => [
|
|
156
|
+
key,
|
|
157
|
+
options.attribute_based_shipping_options[0].pk
|
|
158
|
+
])
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
dispatch(
|
|
162
|
+
apiEndpoints.setAttributeBasedShippingOptions.initiate(
|
|
163
|
+
initialSelectedOptions
|
|
164
|
+
)
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
148
168
|
if (!preOrder.payment_option && paymentOptions.length > 0) {
|
|
149
169
|
dispatch(apiEndpoints.setPaymentOption.initiate(paymentOptions[0].pk));
|
|
150
170
|
}
|
|
151
171
|
|
|
152
|
-
if (
|
|
172
|
+
if (
|
|
173
|
+
!preOrder.installment &&
|
|
174
|
+
preOrder.payment_option?.payment_type !== 'saved_card' &&
|
|
175
|
+
installmentOptions.length > 0
|
|
176
|
+
) {
|
|
153
177
|
dispatch(
|
|
154
178
|
apiEndpoints.setInstallmentOption.initiate(installmentOptions[0].pk)
|
|
155
179
|
);
|
|
@@ -250,6 +274,14 @@ export const contextListMiddleware: Middleware = ({
|
|
|
250
274
|
);
|
|
251
275
|
}
|
|
252
276
|
|
|
277
|
+
if (context.page_context.attribute_based_shipping_options) {
|
|
278
|
+
dispatch(
|
|
279
|
+
setAttributeBasedShippingOptions(
|
|
280
|
+
context.page_context.attribute_based_shipping_options
|
|
281
|
+
)
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
253
285
|
if (context.page_context.payment_options) {
|
|
254
286
|
dispatch(setPaymentOptions(context.page_context.payment_options));
|
|
255
287
|
}
|
|
@@ -15,7 +15,8 @@ import {
|
|
|
15
15
|
PreOrder,
|
|
16
16
|
RetailStore,
|
|
17
17
|
ShippingOption,
|
|
18
|
-
DataSource
|
|
18
|
+
DataSource,
|
|
19
|
+
AttributeBasedShippingOption
|
|
19
20
|
} from '../../types';
|
|
20
21
|
|
|
21
22
|
export interface CheckoutState {
|
|
@@ -48,6 +49,8 @@ export interface CheckoutState {
|
|
|
48
49
|
selectedBankAccountPk: number;
|
|
49
50
|
loyaltyBalance?: string;
|
|
50
51
|
retailStores: RetailStore[];
|
|
52
|
+
attributeBasedShippingOptions: AttributeBasedShippingOption[];
|
|
53
|
+
selectedShippingOptions: Record<string, number>;
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
const initialState: CheckoutState = {
|
|
@@ -78,7 +81,9 @@ const initialState: CheckoutState = {
|
|
|
78
81
|
installmentOptions: [],
|
|
79
82
|
bankAccounts: [],
|
|
80
83
|
selectedBankAccountPk: null,
|
|
81
|
-
retailStores: []
|
|
84
|
+
retailStores: [],
|
|
85
|
+
attributeBasedShippingOptions: [],
|
|
86
|
+
selectedShippingOptions: {}
|
|
82
87
|
};
|
|
83
88
|
|
|
84
89
|
const checkoutSlice = createSlice({
|
|
@@ -156,6 +161,12 @@ const checkoutSlice = createSlice({
|
|
|
156
161
|
},
|
|
157
162
|
setRetailStores(state, { payload }) {
|
|
158
163
|
state.retailStores = payload;
|
|
164
|
+
},
|
|
165
|
+
setAttributeBasedShippingOptions(state, { payload }) {
|
|
166
|
+
state.attributeBasedShippingOptions = payload;
|
|
167
|
+
},
|
|
168
|
+
setSelectedShippingOptions: (state, { payload }) => {
|
|
169
|
+
state.selectedShippingOptions = payload;
|
|
159
170
|
}
|
|
160
171
|
}
|
|
161
172
|
});
|
|
@@ -184,7 +195,9 @@ export const {
|
|
|
184
195
|
setBankAccounts,
|
|
185
196
|
setSelectedBankAccountPk,
|
|
186
197
|
setLoyaltyBalance,
|
|
187
|
-
setRetailStores
|
|
198
|
+
setRetailStores,
|
|
199
|
+
setAttributeBasedShippingOptions,
|
|
200
|
+
setSelectedShippingOptions
|
|
188
201
|
} = checkoutSlice.actions;
|
|
189
202
|
|
|
190
203
|
export default checkoutSlice.reducer;
|
package/redux/reducers/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { api } from '../../data/client/api';
|
|
|
8
8
|
import { masterpassReducer } from '@akinon/pz-masterpass';
|
|
9
9
|
import { otpReducer } from '@akinon/pz-otp';
|
|
10
10
|
import { savedCardReducer } from '@akinon/pz-saved-card';
|
|
11
|
+
import { iyzicoSavedCardReducer } from '@akinon/pz-iyzico-saved-card';
|
|
11
12
|
|
|
12
13
|
const reducers = {
|
|
13
14
|
[api.reducerPath]: api.reducer,
|
|
@@ -17,7 +18,8 @@ const reducers = {
|
|
|
17
18
|
header: headerReducer,
|
|
18
19
|
masterpass: masterpassReducer,
|
|
19
20
|
otp: otpReducer,
|
|
20
|
-
|
|
21
|
+
savedCard: savedCardReducer,
|
|
22
|
+
iyzicoSavedCard: iyzicoSavedCardReducer
|
|
21
23
|
};
|
|
22
24
|
|
|
23
25
|
export default reducers;
|
|
@@ -86,6 +86,7 @@ export interface PreOrder {
|
|
|
86
86
|
total_amount?: string;
|
|
87
87
|
total_amount_with_interest?: string;
|
|
88
88
|
unpaid_amount?: string;
|
|
89
|
+
notes?: string;
|
|
89
90
|
user_phone_number?: string;
|
|
90
91
|
loyalty_money?: string;
|
|
91
92
|
currency_type_label?: string;
|
|
@@ -97,6 +98,7 @@ export interface PreOrder {
|
|
|
97
98
|
gift_box?: GiftBox;
|
|
98
99
|
credit_payment_option?: CheckoutCreditPaymentOption;
|
|
99
100
|
data_source_shipping_options: any;
|
|
101
|
+
attribute_based_shipping_options: any;
|
|
100
102
|
bags?: Product[];
|
|
101
103
|
bags_fee?: string;
|
|
102
104
|
extra_field?: ExtraField;
|
|
@@ -141,6 +143,7 @@ export interface CheckoutContext {
|
|
|
141
143
|
redirect_url?: string;
|
|
142
144
|
context_data?: any;
|
|
143
145
|
balance?: string;
|
|
146
|
+
attribute_based_shipping_options?: AttributeBasedShippingOption[];
|
|
144
147
|
[key: string]: any;
|
|
145
148
|
};
|
|
146
149
|
}
|
|
@@ -166,3 +169,13 @@ export interface BankAccount {
|
|
|
166
169
|
pk: number;
|
|
167
170
|
sort_order: number;
|
|
168
171
|
}
|
|
172
|
+
|
|
173
|
+
export interface AttributeBasedShippingOption {
|
|
174
|
+
pk: number;
|
|
175
|
+
shipping_amount: string | null;
|
|
176
|
+
shipping_option_name: string | null;
|
|
177
|
+
shipping_option_logo: string | null;
|
|
178
|
+
attribute_value: string | null;
|
|
179
|
+
attribute_key: string;
|
|
180
|
+
[key: string]: any;
|
|
181
|
+
}
|