@akinon/next 1.59.0-rc.0 → 1.59.0-rc.2
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 +13 -0
- package/components/plugin-module.tsx +8 -3
- package/components/selected-payment-option-view.tsx +2 -1
- package/data/client/checkout.ts +18 -10
- package/hooks/use-payment-options.ts +2 -1
- package/middlewares/default.ts +193 -163
- package/middlewares/index.ts +3 -1
- package/middlewares/saved-card-redirection.ts +179 -0
- package/package.json +2 -2
- package/plugins.d.ts +5 -0
- package/plugins.js +2 -1
- package/redux/middlewares/checkout.ts +10 -6
- package/redux/reducers/index.ts +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @akinon/next
|
|
2
2
|
|
|
3
|
+
## 1.59.0-rc.2
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- c873740: ZERO-2903: add types
|
|
8
|
+
- 034b813: ZERO-2903: create saved card plugin
|
|
9
|
+
|
|
10
|
+
## 1.59.0-rc.1
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- 907813c: ZERO-2934: add payment-gateway/<gateway> redirect in middleware
|
|
15
|
+
|
|
3
16
|
## 1.59.0-rc.0
|
|
4
17
|
|
|
5
18
|
### Minor Changes
|
|
@@ -19,7 +19,8 @@ enum Plugin {
|
|
|
19
19
|
Masterpass = 'pz-masterpass',
|
|
20
20
|
B2B = 'pz-b2b',
|
|
21
21
|
Akifast = 'pz-akifast',
|
|
22
|
-
MultiBasket = 'pz-multi-basket'
|
|
22
|
+
MultiBasket = 'pz-multi-basket',
|
|
23
|
+
SavedCard = 'pz-saved-card'
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export enum Component {
|
|
@@ -43,7 +44,8 @@ export enum Component {
|
|
|
43
44
|
BasketB2B = 'BasketB2b',
|
|
44
45
|
AkifastQuickLoginButton = 'QuickLoginButton',
|
|
45
46
|
AkifastCheckoutButton = 'CheckoutButton',
|
|
46
|
-
MultiBasket = 'MultiBasket'
|
|
47
|
+
MultiBasket = 'MultiBasket',
|
|
48
|
+
SavedCard = 'SavedCardOption'
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
const PluginComponents = new Map([
|
|
@@ -75,7 +77,8 @@ const PluginComponents = new Map([
|
|
|
75
77
|
Plugin.Akifast,
|
|
76
78
|
[Component.AkifastQuickLoginButton, Component.AkifastCheckoutButton]
|
|
77
79
|
],
|
|
78
|
-
[Plugin.MultiBasket, [Component.MultiBasket]]
|
|
80
|
+
[Plugin.MultiBasket, [Component.MultiBasket]],
|
|
81
|
+
[Plugin.SavedCard, [Component.SavedCard]]
|
|
79
82
|
]);
|
|
80
83
|
|
|
81
84
|
const getPlugin = (component: Component) => {
|
|
@@ -138,6 +141,8 @@ export default function PluginModule({
|
|
|
138
141
|
promise = import(`${'@akinon/pz-akifast'}`);
|
|
139
142
|
} else if (plugin === Plugin.MultiBasket) {
|
|
140
143
|
promise = import(`${'@akinon/pz-multi-basket'}`);
|
|
144
|
+
} else if (plugin === Plugin.SavedCard) {
|
|
145
|
+
promise = import(`${'@akinon/pz-saved-card'}`);
|
|
141
146
|
}
|
|
142
147
|
} catch (error) {
|
|
143
148
|
logger.error(error);
|
|
@@ -17,7 +17,8 @@ const paymentTypeToView = {
|
|
|
17
17
|
loyalty_money: 'loyalty',
|
|
18
18
|
masterpass: 'credit-card',
|
|
19
19
|
pay_on_delivery: 'pay-on-delivery',
|
|
20
|
-
redirection: 'redirection'
|
|
20
|
+
redirection: 'redirection',
|
|
21
|
+
saved_card: 'saved-card'
|
|
21
22
|
// Add other mappings as needed
|
|
22
23
|
};
|
|
23
24
|
|
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 }) {
|
|
@@ -19,7 +19,8 @@ export const usePaymentOptions = () => {
|
|
|
19
19
|
bkm_express: 'pz-bkm',
|
|
20
20
|
credit_payment: 'pz-credit-payment',
|
|
21
21
|
masterpass: 'pz-masterpass',
|
|
22
|
-
gpay: 'pz-gpay'
|
|
22
|
+
gpay: 'pz-gpay',
|
|
23
|
+
saved_card: 'pz-saved-card'
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
const isInitialTypeIncluded = (type: string) => initialTypes.has(type);
|
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 '.';
|
|
@@ -140,6 +141,14 @@ const withPzDefault =
|
|
|
140
141
|
);
|
|
141
142
|
}
|
|
142
143
|
|
|
144
|
+
if (req.nextUrl.pathname.includes('/orders/saved-card-redirect')) {
|
|
145
|
+
return NextResponse.rewrite(
|
|
146
|
+
new URL(
|
|
147
|
+
`${encodeURI(Settings.commerceUrl)}/orders/saved-card-redirect/`
|
|
148
|
+
)
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
143
152
|
// If commerce redirects to /orders/checkout/ without locale
|
|
144
153
|
if (
|
|
145
154
|
req.nextUrl.pathname.match(new RegExp('^/orders/checkout/$')) &&
|
|
@@ -157,6 +166,25 @@ const withPzDefault =
|
|
|
157
166
|
return NextResponse.redirect(redirectUrlWithLocale, 303);
|
|
158
167
|
}
|
|
159
168
|
|
|
169
|
+
// Dynamically handle any payment gateway without specifying names
|
|
170
|
+
const paymentGatewayRegex = new RegExp('^/payment-gateway/([^/]+)/$');
|
|
171
|
+
const gatewayMatch = req.nextUrl.pathname.match(paymentGatewayRegex);
|
|
172
|
+
|
|
173
|
+
if (
|
|
174
|
+
gatewayMatch && // Check if the URL matches the /payment-gateway/<gateway> pattern
|
|
175
|
+
getUrlPathWithLocale(
|
|
176
|
+
`/payment-gateway/${gatewayMatch[1]}/`,
|
|
177
|
+
req.cookies.get('pz-locale')?.value
|
|
178
|
+
) !== req.nextUrl.pathname
|
|
179
|
+
) {
|
|
180
|
+
const redirectUrlWithLocale = `${url.origin}${getUrlPathWithLocale(
|
|
181
|
+
`/payment-gateway/${gatewayMatch[1]}/`,
|
|
182
|
+
req.cookies.get('pz-locale')?.value
|
|
183
|
+
)}?${req.nextUrl.searchParams.toString()}`;
|
|
184
|
+
|
|
185
|
+
return NextResponse.redirect(redirectUrlWithLocale);
|
|
186
|
+
}
|
|
187
|
+
|
|
160
188
|
if (req.nextUrl.pathname.startsWith('/orders/checkout-provider/')) {
|
|
161
189
|
try {
|
|
162
190
|
const data = await req.json();
|
|
@@ -204,128 +232,141 @@ const withPzDefault =
|
|
|
204
232
|
withUrlRedirection(
|
|
205
233
|
withCompleteGpay(
|
|
206
234
|
withCompleteMasterpass(
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
NextResponse
|
|
210
|
-
|
|
211
|
-
try {
|
|
212
|
-
const { locale, prettyUrl, currency } =
|
|
213
|
-
req.middlewareParams.rewrites;
|
|
214
|
-
const { defaultLocaleValue } =
|
|
215
|
-
Settings.localization;
|
|
216
|
-
const url = req.nextUrl.clone();
|
|
217
|
-
const pathnameWithoutLocale = url.pathname.replace(
|
|
218
|
-
urlLocaleMatcherRegex,
|
|
219
|
-
''
|
|
220
|
-
);
|
|
221
|
-
|
|
222
|
-
middlewareResult = (await middleware(
|
|
223
|
-
req,
|
|
224
|
-
event
|
|
225
|
-
)) as NextResponse | void;
|
|
226
|
-
|
|
227
|
-
let customRewriteUrlDiff = '';
|
|
228
|
-
|
|
229
|
-
if (
|
|
230
|
-
middlewareResult instanceof NextResponse &&
|
|
231
|
-
middlewareResult.headers.get(
|
|
232
|
-
'pz-override-response'
|
|
233
|
-
) &&
|
|
234
|
-
middlewareResult.headers.get(
|
|
235
|
-
'x-middleware-rewrite'
|
|
236
|
-
)
|
|
237
|
-
) {
|
|
238
|
-
const rewriteUrl = new URL(
|
|
239
|
-
middlewareResult.headers.get(
|
|
240
|
-
'x-middleware-rewrite'
|
|
241
|
-
)
|
|
242
|
-
);
|
|
243
|
-
const originalUrl = new URL(req.url);
|
|
244
|
-
customRewriteUrlDiff =
|
|
245
|
-
rewriteUrl.pathname.replace(
|
|
246
|
-
originalUrl.pathname,
|
|
247
|
-
''
|
|
248
|
-
);
|
|
249
|
-
}
|
|
235
|
+
withSavedCardRedirection(
|
|
236
|
+
async (req: PzNextRequest, event: NextFetchEvent) => {
|
|
237
|
+
let middlewareResult: NextResponse | void =
|
|
238
|
+
NextResponse.next();
|
|
250
239
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
Settings.customNotFoundEnabled
|
|
261
|
-
) {
|
|
262
|
-
let pathname = url.pathname
|
|
263
|
-
.replace(/\/+$/, '')
|
|
264
|
-
.split('/');
|
|
265
|
-
url.pathname = url.pathname.replace(
|
|
266
|
-
pathname.pop(),
|
|
267
|
-
'pz-not-found'
|
|
240
|
+
try {
|
|
241
|
+
const { locale, prettyUrl, currency } =
|
|
242
|
+
req.middlewareParams.rewrites;
|
|
243
|
+
const { defaultLocaleValue } =
|
|
244
|
+
Settings.localization;
|
|
245
|
+
const url = req.nextUrl.clone();
|
|
246
|
+
const pathnameWithoutLocale = url.pathname.replace(
|
|
247
|
+
urlLocaleMatcherRegex,
|
|
248
|
+
''
|
|
268
249
|
);
|
|
269
|
-
}
|
|
270
250
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
new RegExp(`^${value}/?$`).test(
|
|
276
|
-
pathnameWithoutLocale
|
|
277
|
-
)
|
|
278
|
-
)
|
|
279
|
-
) {
|
|
280
|
-
url.pathname =
|
|
281
|
-
url.pathname +
|
|
282
|
-
`searchparams|${url.searchParams.toString()}`;
|
|
283
|
-
}
|
|
251
|
+
middlewareResult = (await middleware(
|
|
252
|
+
req,
|
|
253
|
+
event
|
|
254
|
+
)) as NextResponse | void;
|
|
284
255
|
|
|
285
|
-
|
|
286
|
-
url.pathname = url.pathname.replace(
|
|
287
|
-
rewrite.source,
|
|
288
|
-
rewrite.destination
|
|
289
|
-
);
|
|
290
|
-
});
|
|
256
|
+
let customRewriteUrlDiff = '';
|
|
291
257
|
|
|
292
|
-
// if middleware.ts has a return value for current url
|
|
293
|
-
if (middlewareResult instanceof NextResponse) {
|
|
294
|
-
// pz-override-response header is used to prevent 404 page for custom responses.
|
|
295
258
|
if (
|
|
259
|
+
middlewareResult instanceof NextResponse &&
|
|
296
260
|
middlewareResult.headers.get(
|
|
297
261
|
'pz-override-response'
|
|
298
|
-
) !== 'true'
|
|
299
|
-
) {
|
|
300
|
-
middlewareResult.headers.set(
|
|
301
|
-
'x-middleware-rewrite',
|
|
302
|
-
url.href
|
|
303
|
-
);
|
|
304
|
-
} else if (
|
|
305
|
-
middlewareResult.headers.get(
|
|
306
|
-
'x-middleware-rewrite'
|
|
307
262
|
) &&
|
|
308
263
|
middlewareResult.headers.get(
|
|
309
|
-
'
|
|
310
|
-
)
|
|
264
|
+
'x-middleware-rewrite'
|
|
265
|
+
)
|
|
311
266
|
) {
|
|
312
|
-
|
|
267
|
+
const rewriteUrl = new URL(
|
|
268
|
+
middlewareResult.headers.get(
|
|
269
|
+
'x-middleware-rewrite'
|
|
270
|
+
)
|
|
271
|
+
);
|
|
272
|
+
const originalUrl = new URL(req.url);
|
|
273
|
+
customRewriteUrlDiff =
|
|
274
|
+
rewriteUrl.pathname.replace(
|
|
275
|
+
originalUrl.pathname,
|
|
276
|
+
''
|
|
277
|
+
);
|
|
313
278
|
}
|
|
314
|
-
} else {
|
|
315
|
-
// if middleware.ts doesn't have a return value.
|
|
316
|
-
// e.g. NextResponse.next() doesn't exist in middleware.ts
|
|
317
279
|
|
|
318
|
-
|
|
319
|
-
|
|
280
|
+
url.basePath = `/${commerceUrl}`;
|
|
281
|
+
url.pathname = `/${
|
|
282
|
+
locale.length ? `${locale}/` : ''
|
|
283
|
+
}${currency}/${customRewriteUrlDiff}${
|
|
284
|
+
prettyUrl ?? pathnameWithoutLocale
|
|
285
|
+
}`.replace(/\/+/g, '/');
|
|
286
|
+
|
|
287
|
+
if (
|
|
288
|
+
!req.middlewareParams.found &&
|
|
289
|
+
Settings.customNotFoundEnabled
|
|
290
|
+
) {
|
|
291
|
+
let pathname = url.pathname
|
|
292
|
+
.replace(/\/+$/, '')
|
|
293
|
+
.split('/');
|
|
294
|
+
url.pathname = url.pathname.replace(
|
|
295
|
+
pathname.pop(),
|
|
296
|
+
'pz-not-found'
|
|
297
|
+
);
|
|
298
|
+
}
|
|
320
299
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
300
|
+
if (
|
|
301
|
+
Settings.usePrettyUrlRoute &&
|
|
302
|
+
url.searchParams.toString().length > 0 &&
|
|
303
|
+
!Object.entries(ROUTES).find(([, value]) =>
|
|
304
|
+
new RegExp(`^${value}/?$`).test(
|
|
305
|
+
pathnameWithoutLocale
|
|
306
|
+
)
|
|
307
|
+
)
|
|
308
|
+
) {
|
|
309
|
+
url.pathname =
|
|
310
|
+
url.pathname +
|
|
311
|
+
`searchparams|${url.searchParams.toString()}`;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
Settings.rewrites.forEach((rewrite) => {
|
|
315
|
+
url.pathname = url.pathname.replace(
|
|
316
|
+
rewrite.source,
|
|
317
|
+
rewrite.destination
|
|
318
|
+
);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// if middleware.ts has a return value for current url
|
|
322
|
+
if (middlewareResult instanceof NextResponse) {
|
|
323
|
+
// pz-override-response header is used to prevent 404 page for custom responses.
|
|
324
|
+
if (
|
|
325
|
+
middlewareResult.headers.get(
|
|
326
|
+
'pz-override-response'
|
|
327
|
+
) !== 'true'
|
|
328
|
+
) {
|
|
329
|
+
middlewareResult.headers.set(
|
|
330
|
+
'x-middleware-rewrite',
|
|
331
|
+
url.href
|
|
332
|
+
);
|
|
333
|
+
} else if (
|
|
334
|
+
middlewareResult.headers.get(
|
|
335
|
+
'x-middleware-rewrite'
|
|
336
|
+
) &&
|
|
337
|
+
middlewareResult.headers.get(
|
|
338
|
+
'pz-override-response'
|
|
339
|
+
) === 'true'
|
|
340
|
+
) {
|
|
341
|
+
middlewareResult = NextResponse.rewrite(url);
|
|
342
|
+
}
|
|
343
|
+
} else {
|
|
344
|
+
// if middleware.ts doesn't have a return value.
|
|
345
|
+
// e.g. NextResponse.next() doesn't exist in middleware.ts
|
|
346
|
+
|
|
347
|
+
middlewareResult = NextResponse.rewrite(url);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (
|
|
351
|
+
!url.pathname.startsWith(`/${currency}/orders`)
|
|
352
|
+
) {
|
|
353
|
+
middlewareResult.cookies.set(
|
|
354
|
+
'pz-locale',
|
|
355
|
+
locale?.length > 0
|
|
356
|
+
? locale
|
|
357
|
+
: defaultLocaleValue,
|
|
358
|
+
{
|
|
359
|
+
sameSite: 'none',
|
|
360
|
+
secure: true,
|
|
361
|
+
expires: new Date(
|
|
362
|
+
Date.now() + 1000 * 60 * 60 * 24 * 7
|
|
363
|
+
) // 7 days
|
|
364
|
+
}
|
|
365
|
+
);
|
|
366
|
+
}
|
|
324
367
|
middlewareResult.cookies.set(
|
|
325
|
-
'pz-
|
|
326
|
-
|
|
327
|
-
? locale
|
|
328
|
-
: defaultLocaleValue,
|
|
368
|
+
'pz-currency',
|
|
369
|
+
currency,
|
|
329
370
|
{
|
|
330
371
|
sameSite: 'none',
|
|
331
372
|
secure: true,
|
|
@@ -334,76 +375,65 @@ const withPzDefault =
|
|
|
334
375
|
) // 7 days
|
|
335
376
|
}
|
|
336
377
|
);
|
|
337
|
-
}
|
|
338
|
-
middlewareResult.cookies.set(
|
|
339
|
-
'pz-currency',
|
|
340
|
-
currency,
|
|
341
|
-
{
|
|
342
|
-
sameSite: 'none',
|
|
343
|
-
secure: true,
|
|
344
|
-
expires: new Date(
|
|
345
|
-
Date.now() + 1000 * 60 * 60 * 24 * 7
|
|
346
|
-
) // 7 days
|
|
347
|
-
}
|
|
348
|
-
);
|
|
349
|
-
|
|
350
|
-
if (
|
|
351
|
-
req.cookies.get('pz-locale') &&
|
|
352
|
-
req.cookies.get('pz-locale').value !== locale
|
|
353
|
-
) {
|
|
354
|
-
logger.debug('Locale changed', {
|
|
355
|
-
locale,
|
|
356
|
-
oldLocale: req.cookies.get('pz-locale')?.value,
|
|
357
|
-
ip
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
middlewareResult.headers.set(
|
|
362
|
-
'pz-url',
|
|
363
|
-
req.nextUrl.toString()
|
|
364
|
-
);
|
|
365
378
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
'pz-
|
|
369
|
-
)
|
|
370
|
-
|
|
379
|
+
if (
|
|
380
|
+
req.cookies.get('pz-locale') &&
|
|
381
|
+
req.cookies.get('pz-locale').value !== locale
|
|
382
|
+
) {
|
|
383
|
+
logger.debug('Locale changed', {
|
|
384
|
+
locale,
|
|
385
|
+
oldLocale: req.cookies.get('pz-locale')?.value,
|
|
386
|
+
ip
|
|
387
|
+
});
|
|
388
|
+
}
|
|
371
389
|
|
|
372
|
-
if (process.env.ACC_APP_VERSION) {
|
|
373
390
|
middlewareResult.headers.set(
|
|
374
|
-
'
|
|
375
|
-
|
|
391
|
+
'pz-url',
|
|
392
|
+
req.nextUrl.toString()
|
|
376
393
|
);
|
|
377
|
-
}
|
|
378
394
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
395
|
+
if (req.cookies.get('pz-set-currency')) {
|
|
396
|
+
middlewareResult.cookies.delete(
|
|
397
|
+
'pz-set-currency'
|
|
398
|
+
);
|
|
399
|
+
}
|
|
382
400
|
|
|
383
|
-
if (
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
middlewareResult.cookies.set(
|
|
388
|
-
'csrftoken',
|
|
389
|
-
csrf_token
|
|
401
|
+
if (process.env.ACC_APP_VERSION) {
|
|
402
|
+
middlewareResult.headers.set(
|
|
403
|
+
'acc-app-version',
|
|
404
|
+
process.env.ACC_APP_VERSION
|
|
390
405
|
);
|
|
391
406
|
}
|
|
407
|
+
|
|
408
|
+
// Set CSRF token if not set
|
|
409
|
+
try {
|
|
410
|
+
const url = `${Settings.commerceUrl}${user.csrfToken}`;
|
|
411
|
+
|
|
412
|
+
if (!req.cookies.get('csrftoken')) {
|
|
413
|
+
const { csrf_token } = await (
|
|
414
|
+
await fetch(url)
|
|
415
|
+
).json();
|
|
416
|
+
middlewareResult.cookies.set(
|
|
417
|
+
'csrftoken',
|
|
418
|
+
csrf_token
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
} catch (error) {
|
|
422
|
+
logger.error('CSRF Error', {
|
|
423
|
+
error,
|
|
424
|
+
ip
|
|
425
|
+
});
|
|
426
|
+
}
|
|
392
427
|
} catch (error) {
|
|
393
|
-
logger.error('
|
|
428
|
+
logger.error('withPzDefault Error', {
|
|
394
429
|
error,
|
|
395
430
|
ip
|
|
396
431
|
});
|
|
397
432
|
}
|
|
398
|
-
} catch (error) {
|
|
399
|
-
logger.error('withPzDefault Error', {
|
|
400
|
-
error,
|
|
401
|
-
ip
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
433
|
|
|
405
|
-
|
|
406
|
-
|
|
434
|
+
return middlewareResult;
|
|
435
|
+
}
|
|
436
|
+
)
|
|
407
437
|
)
|
|
408
438
|
)
|
|
409
439
|
)
|
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 {
|
|
@@ -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: `osessionid=${req.cookies.get('osessionid')?.value ?? ''}`,
|
|
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;
|
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.59.0-rc.
|
|
4
|
+
"version": "1.59.0-rc.2",
|
|
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.59.0-rc.
|
|
33
|
+
"@akinon/eslint-plugin-projectzero": "1.59.0-rc.2",
|
|
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
package/plugins.js
CHANGED
|
@@ -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,15 +19,12 @@ import {
|
|
|
16
19
|
setPreOrder,
|
|
17
20
|
setRetailStores,
|
|
18
21
|
setShippingOptions,
|
|
19
|
-
|
|
20
|
-
setShippingStepCompleted,
|
|
21
|
-
setCreditPaymentOptions,
|
|
22
|
-
setAttributeBasedShippingOptions
|
|
22
|
+
setShippingStepCompleted
|
|
23
23
|
} from '../../redux/reducers/checkout';
|
|
24
24
|
import { RootState, TypedDispatch } from 'redux/store';
|
|
25
25
|
import { checkoutApi } from '../../data/client/checkout';
|
|
26
26
|
import { CheckoutContext, PreOrder } from '../../types';
|
|
27
|
-
import { getCookie
|
|
27
|
+
import { getCookie } from '../../utils';
|
|
28
28
|
import settings from 'settings';
|
|
29
29
|
import { LocaleUrlStrategy } from '../../localization';
|
|
30
30
|
import { showMobile3dIframe } from '../../utils/mobile-3d-iframe';
|
|
@@ -169,7 +169,11 @@ export const preOrderMiddleware: Middleware = ({
|
|
|
169
169
|
dispatch(apiEndpoints.setPaymentOption.initiate(paymentOptions[0].pk));
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
if (
|
|
172
|
+
if (
|
|
173
|
+
!preOrder.installment &&
|
|
174
|
+
preOrder.payment_option.payment_type !== 'saved_card' &&
|
|
175
|
+
installmentOptions.length > 0
|
|
176
|
+
) {
|
|
173
177
|
dispatch(
|
|
174
178
|
apiEndpoints.setInstallmentOption.initiate(installmentOptions[0].pk)
|
|
175
179
|
);
|
package/redux/reducers/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { api } from '../../data/client/api';
|
|
|
7
7
|
// Plugin reducers
|
|
8
8
|
import { masterpassReducer } from '@akinon/pz-masterpass';
|
|
9
9
|
import { otpReducer } from '@akinon/pz-otp';
|
|
10
|
+
import { savedCardReducer } from '@akinon/pz-saved-card';
|
|
10
11
|
|
|
11
12
|
const reducers = {
|
|
12
13
|
[api.reducerPath]: api.reducer,
|
|
@@ -15,7 +16,8 @@ const reducers = {
|
|
|
15
16
|
config: configReducer,
|
|
16
17
|
header: headerReducer,
|
|
17
18
|
masterpass: masterpassReducer,
|
|
18
|
-
otp: otpReducer
|
|
19
|
+
otp: otpReducer,
|
|
20
|
+
savedCard: savedCardReducer
|
|
19
21
|
};
|
|
20
22
|
|
|
21
23
|
export default reducers;
|