@akinon/next 1.59.0-rc.1 → 1.59.0-rc.3

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 CHANGED
@@ -1,5 +1,14 @@
1
1
  # @akinon/next
2
2
 
3
+ ## 1.59.0-rc.3
4
+
5
+ ## 1.59.0-rc.2
6
+
7
+ ### Minor Changes
8
+
9
+ - c873740: ZERO-2903: add types
10
+ - 034b813: ZERO-2903: create saved card plugin
11
+
3
12
  ## 1.59.0-rc.1
4
13
 
5
14
  ### 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
 
@@ -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);
@@ -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/$')) &&
@@ -223,128 +232,141 @@ const withPzDefault =
223
232
  withUrlRedirection(
224
233
  withCompleteGpay(
225
234
  withCompleteMasterpass(
226
- async (req: PzNextRequest, event: NextFetchEvent) => {
227
- let middlewareResult: NextResponse | void =
228
- NextResponse.next();
229
-
230
- try {
231
- const { locale, prettyUrl, currency } =
232
- req.middlewareParams.rewrites;
233
- const { defaultLocaleValue } =
234
- Settings.localization;
235
- const url = req.nextUrl.clone();
236
- const pathnameWithoutLocale = url.pathname.replace(
237
- urlLocaleMatcherRegex,
238
- ''
239
- );
240
-
241
- middlewareResult = (await middleware(
242
- req,
243
- event
244
- )) as NextResponse | void;
245
-
246
- let customRewriteUrlDiff = '';
247
-
248
- if (
249
- middlewareResult instanceof NextResponse &&
250
- middlewareResult.headers.get(
251
- 'pz-override-response'
252
- ) &&
253
- middlewareResult.headers.get(
254
- 'x-middleware-rewrite'
255
- )
256
- ) {
257
- const rewriteUrl = new URL(
258
- middlewareResult.headers.get(
259
- 'x-middleware-rewrite'
260
- )
261
- );
262
- const originalUrl = new URL(req.url);
263
- customRewriteUrlDiff =
264
- rewriteUrl.pathname.replace(
265
- originalUrl.pathname,
266
- ''
267
- );
268
- }
235
+ withSavedCardRedirection(
236
+ async (req: PzNextRequest, event: NextFetchEvent) => {
237
+ let middlewareResult: NextResponse | void =
238
+ NextResponse.next();
269
239
 
270
- url.basePath = `/${commerceUrl}`;
271
- url.pathname = `/${
272
- locale.length ? `${locale}/` : ''
273
- }${currency}/${customRewriteUrlDiff}${
274
- prettyUrl ?? pathnameWithoutLocale
275
- }`.replace(/\/+/g, '/');
276
-
277
- if (
278
- !req.middlewareParams.found &&
279
- Settings.customNotFoundEnabled
280
- ) {
281
- let pathname = url.pathname
282
- .replace(/\/+$/, '')
283
- .split('/');
284
- url.pathname = url.pathname.replace(
285
- pathname.pop(),
286
- '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
+ ''
287
249
  );
288
- }
289
250
 
290
- if (
291
- Settings.usePrettyUrlRoute &&
292
- url.searchParams.toString().length > 0 &&
293
- !Object.entries(ROUTES).find(([, value]) =>
294
- new RegExp(`^${value}/?$`).test(
295
- pathnameWithoutLocale
296
- )
297
- )
298
- ) {
299
- url.pathname =
300
- url.pathname +
301
- `searchparams|${url.searchParams.toString()}`;
302
- }
251
+ middlewareResult = (await middleware(
252
+ req,
253
+ event
254
+ )) as NextResponse | void;
303
255
 
304
- Settings.rewrites.forEach((rewrite) => {
305
- url.pathname = url.pathname.replace(
306
- rewrite.source,
307
- rewrite.destination
308
- );
309
- });
256
+ let customRewriteUrlDiff = '';
310
257
 
311
- // if middleware.ts has a return value for current url
312
- if (middlewareResult instanceof NextResponse) {
313
- // pz-override-response header is used to prevent 404 page for custom responses.
314
258
  if (
259
+ middlewareResult instanceof NextResponse &&
315
260
  middlewareResult.headers.get(
316
261
  'pz-override-response'
317
- ) !== 'true'
318
- ) {
319
- middlewareResult.headers.set(
320
- 'x-middleware-rewrite',
321
- url.href
322
- );
323
- } else if (
324
- middlewareResult.headers.get(
325
- 'x-middleware-rewrite'
326
262
  ) &&
327
263
  middlewareResult.headers.get(
328
- 'pz-override-response'
329
- ) === 'true'
264
+ 'x-middleware-rewrite'
265
+ )
330
266
  ) {
331
- middlewareResult = NextResponse.rewrite(url);
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
+ );
332
278
  }
333
- } else {
334
- // if middleware.ts doesn't have a return value.
335
- // e.g. NextResponse.next() doesn't exist in middleware.ts
336
279
 
337
- middlewareResult = NextResponse.rewrite(url);
338
- }
280
+ url.basePath = `/${commerceUrl}`;
281
+ url.pathname = `/${
282
+ locale.length ? `${locale}/` : ''
283
+ }${currency}/${customRewriteUrlDiff}${
284
+ prettyUrl ?? pathnameWithoutLocale
285
+ }`.replace(/\/+/g, '/');
339
286
 
340
- if (
341
- !url.pathname.startsWith(`/${currency}/orders`)
342
- ) {
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
+ }
299
+
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
+ }
343
367
  middlewareResult.cookies.set(
344
- 'pz-locale',
345
- locale?.length > 0
346
- ? locale
347
- : defaultLocaleValue,
368
+ 'pz-currency',
369
+ currency,
348
370
  {
349
371
  sameSite: 'none',
350
372
  secure: true,
@@ -353,76 +375,65 @@ const withPzDefault =
353
375
  ) // 7 days
354
376
  }
355
377
  );
356
- }
357
- middlewareResult.cookies.set(
358
- 'pz-currency',
359
- currency,
360
- {
361
- sameSite: 'none',
362
- secure: true,
363
- expires: new Date(
364
- Date.now() + 1000 * 60 * 60 * 24 * 7
365
- ) // 7 days
366
- }
367
- );
368
-
369
- if (
370
- req.cookies.get('pz-locale') &&
371
- req.cookies.get('pz-locale').value !== locale
372
- ) {
373
- logger.debug('Locale changed', {
374
- locale,
375
- oldLocale: req.cookies.get('pz-locale')?.value,
376
- ip
377
- });
378
- }
379
-
380
- middlewareResult.headers.set(
381
- 'pz-url',
382
- req.nextUrl.toString()
383
- );
384
378
 
385
- if (req.cookies.get('pz-set-currency')) {
386
- middlewareResult.cookies.delete(
387
- 'pz-set-currency'
388
- );
389
- }
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
+ }
390
389
 
391
- if (process.env.ACC_APP_VERSION) {
392
390
  middlewareResult.headers.set(
393
- 'acc-app-version',
394
- process.env.ACC_APP_VERSION
391
+ 'pz-url',
392
+ req.nextUrl.toString()
395
393
  );
396
- }
397
394
 
398
- // Set CSRF token if not set
399
- try {
400
- const url = `${Settings.commerceUrl}${user.csrfToken}`;
395
+ if (req.cookies.get('pz-set-currency')) {
396
+ middlewareResult.cookies.delete(
397
+ 'pz-set-currency'
398
+ );
399
+ }
401
400
 
402
- if (!req.cookies.get('csrftoken')) {
403
- const { csrf_token } = await (
404
- await fetch(url)
405
- ).json();
406
- middlewareResult.cookies.set(
407
- 'csrftoken',
408
- csrf_token
401
+ if (process.env.ACC_APP_VERSION) {
402
+ middlewareResult.headers.set(
403
+ 'acc-app-version',
404
+ process.env.ACC_APP_VERSION
409
405
  );
410
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
+ }
411
427
  } catch (error) {
412
- logger.error('CSRF Error', {
428
+ logger.error('withPzDefault Error', {
413
429
  error,
414
430
  ip
415
431
  });
416
432
  }
417
- } catch (error) {
418
- logger.error('withPzDefault Error', {
419
- error,
420
- ip
421
- });
422
- }
423
433
 
424
- return middlewareResult;
425
- }
434
+ return middlewareResult;
435
+ }
436
+ )
426
437
  )
427
438
  )
428
439
  )
@@ -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.1",
4
+ "version": "1.59.0-rc.3",
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.1",
33
+ "@akinon/eslint-plugin-projectzero": "1.59.0-rc.3",
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
@@ -23,3 +23,8 @@ declare module '@akinon/pz-otp/src/redux/reducer' {
23
23
  export const showPopup: any;
24
24
  export const hidePopup: any;
25
25
  }
26
+
27
+ declare module '@akinon/pz-saved-card' {
28
+ export const savedCardReducer: any;
29
+ export const SavedCardOption: any;
30
+ }
package/plugins.js CHANGED
@@ -12,5 +12,6 @@ module.exports = [
12
12
  'pz-b2b',
13
13
  'pz-akifast',
14
14
  'pz-multi-basket',
15
- 'pz-tabby-extension'
15
+ 'pz-tabby-extension',
16
+ 'pz-saved-card'
16
17
  ];
@@ -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
- setDataSourceShippingOptions,
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, setCookie } from '../../utils';
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 (!preOrder.installment && installmentOptions.length > 0) {
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
  );
@@ -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;