@akinon/next 2.0.0-beta.8 → 2.0.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.
Files changed (121) hide show
  1. package/CHANGELOG.md +438 -21
  2. package/__tests__/next-config.test.ts +83 -0
  3. package/__tests__/tsconfig.json +23 -0
  4. package/api/auth.ts +367 -63
  5. package/api/barcode-search.ts +59 -0
  6. package/api/cache.ts +41 -5
  7. package/api/client.ts +21 -4
  8. package/api/form.ts +85 -0
  9. package/api/image-proxy.ts +75 -0
  10. package/api/product-categories.ts +53 -0
  11. package/api/similar-product-list.ts +63 -0
  12. package/api/similar-products.ts +111 -0
  13. package/api/virtual-try-on.ts +382 -0
  14. package/assets/styles/index.scss +84 -0
  15. package/babel.config.js +6 -0
  16. package/bin/pz-generate-routes.js +115 -0
  17. package/bin/pz-install-plugins.js +1 -1
  18. package/bin/pz-prebuild.js +1 -0
  19. package/bin/pz-predev.js +1 -0
  20. package/bin/pz-run-tests.js +99 -0
  21. package/components/accordion.tsx +21 -6
  22. package/components/client-root.tsx +119 -3
  23. package/components/file-input.tsx +65 -3
  24. package/components/index.ts +1 -0
  25. package/components/input.tsx +2 -2
  26. package/components/link.tsx +46 -16
  27. package/components/logger-popup.tsx +213 -0
  28. package/components/modal.tsx +32 -16
  29. package/components/plugin-module.tsx +62 -3
  30. package/components/price.tsx +2 -2
  31. package/components/select.tsx +3 -3
  32. package/components/selected-payment-option-view.tsx +21 -0
  33. package/data/client/account.ts +17 -2
  34. package/data/client/basket.ts +39 -0
  35. package/data/client/checkout.ts +336 -99
  36. package/data/client/misc.ts +13 -1
  37. package/data/server/category.ts +11 -9
  38. package/data/server/flatpage.ts +4 -1
  39. package/data/server/form.ts +4 -1
  40. package/data/server/landingpage.ts +4 -1
  41. package/data/server/list.ts +5 -4
  42. package/data/server/menu.ts +4 -1
  43. package/data/server/product.ts +97 -52
  44. package/data/server/seo.ts +4 -1
  45. package/data/server/special-page.ts +5 -4
  46. package/data/server/widget.ts +71 -1
  47. package/data/urls.ts +6 -3
  48. package/hocs/client/with-segment-defaults.tsx +2 -2
  49. package/hocs/server/with-segment-defaults.tsx +81 -20
  50. package/hooks/index.ts +3 -0
  51. package/hooks/use-localization.ts +24 -10
  52. package/hooks/use-logger-context.tsx +114 -0
  53. package/hooks/use-logger.ts +92 -0
  54. package/hooks/use-loyalty-availability.ts +21 -0
  55. package/hooks/use-payment-options.ts +2 -1
  56. package/hooks/use-pz-params.ts +37 -0
  57. package/hooks/use-router.ts +53 -19
  58. package/instrumentation/index.ts +0 -1
  59. package/instrumentation/node.ts +2 -20
  60. package/jest.config.js +25 -0
  61. package/lib/cache-handler.mjs +534 -16
  62. package/lib/cache.ts +269 -34
  63. package/localization/provider.tsx +2 -5
  64. package/middlewares/bfcache-headers.ts +18 -0
  65. package/middlewares/checkout-provider.ts +1 -1
  66. package/middlewares/complete-gpay.ts +32 -26
  67. package/middlewares/complete-masterpass.ts +33 -26
  68. package/middlewares/complete-wallet.ts +182 -0
  69. package/middlewares/default.ts +357 -203
  70. package/middlewares/index.ts +10 -2
  71. package/middlewares/locale.ts +5 -3
  72. package/middlewares/masterpass-rest-callback.ts +230 -0
  73. package/middlewares/oauth-login.ts +200 -57
  74. package/middlewares/pretty-url.ts +21 -8
  75. package/middlewares/redirection-payment.ts +32 -26
  76. package/middlewares/saved-card-redirection.ts +33 -26
  77. package/middlewares/three-d-redirection.ts +32 -26
  78. package/middlewares/url-redirection.ts +9 -15
  79. package/middlewares/wallet-complete-redirection.ts +206 -0
  80. package/package.json +24 -10
  81. package/plugins.d.ts +19 -4
  82. package/plugins.js +9 -1
  83. package/redux/actions.ts +47 -0
  84. package/redux/middlewares/checkout.ts +61 -8
  85. package/redux/middlewares/index.ts +14 -10
  86. package/redux/middlewares/pre-order/address.ts +1 -1
  87. package/redux/middlewares/pre-order/attribute-based-shipping-option.ts +1 -1
  88. package/redux/middlewares/pre-order/data-source-shipping-option.ts +1 -1
  89. package/redux/middlewares/pre-order/delivery-option.ts +1 -1
  90. package/redux/middlewares/pre-order/index.ts +3 -1
  91. package/redux/middlewares/pre-order/installment-option.ts +2 -1
  92. package/redux/middlewares/pre-order/payment-option-reset.ts +37 -0
  93. package/redux/middlewares/pre-order/payment-option.ts +1 -1
  94. package/redux/middlewares/pre-order/pre-order-validation.ts +4 -3
  95. package/redux/middlewares/pre-order/redirection.ts +2 -2
  96. package/redux/middlewares/pre-order/set-pre-order.ts +2 -2
  97. package/redux/middlewares/pre-order/shipping-option.ts +1 -1
  98. package/redux/middlewares/pre-order/shipping-step.ts +1 -1
  99. package/redux/reducers/checkout.ts +15 -1
  100. package/redux/reducers/index.ts +7 -1
  101. package/redux/reducers/widget.ts +80 -0
  102. package/sentry/index.ts +54 -17
  103. package/tailwind/content.js +16 -0
  104. package/types/commerce/checkout.ts +26 -1
  105. package/types/commerce/widget.ts +33 -0
  106. package/types/index.ts +114 -5
  107. package/types/next-auth.d.ts +2 -2
  108. package/types/widget.ts +80 -0
  109. package/utils/app-fetch.ts +7 -2
  110. package/utils/generate-commerce-search-params.ts +3 -2
  111. package/utils/get-checkout-path.ts +3 -0
  112. package/utils/get-root-hostname.ts +28 -0
  113. package/utils/index.ts +69 -18
  114. package/utils/mobile-3d-iframe.ts +8 -2
  115. package/utils/override-middleware.ts +1 -0
  116. package/utils/pz-segments.ts +92 -0
  117. package/utils/redirect-ignore.ts +35 -0
  118. package/utils/redirect.ts +9 -3
  119. package/utils/redirection-iframe.ts +8 -2
  120. package/utils/widget-styles.ts +107 -0
  121. package/with-pz-config.js +20 -7
@@ -10,6 +10,7 @@ import {
10
10
  } from '../../redux/reducers/checkout';
11
11
  import {
12
12
  CheckoutContext,
13
+ AccountUsage,
13
14
  ExtraField,
14
15
  GuestLoginFormParams,
15
16
  Order,
@@ -17,10 +18,10 @@ import {
17
18
  SendSmsType,
18
19
  VerifySmsType
19
20
  } from '../../types';
20
- import { buildClientRequestUrl } from '../../utils';
21
+ import { buildClientRequestUrl, getCookie } from '../../utils';
21
22
  import { api } from './api';
22
23
  import { checkout } from '../urls';
23
- import { AppDispatch, AppStore, store } from 'redux/store';
24
+ import type { AppDispatch, AppStore } from 'redux/store';
24
25
  import settings from 'settings';
25
26
  import { showMobile3dIframe } from '../../utils/mobile-3d-iframe';
26
27
  import {
@@ -32,6 +33,21 @@ import {
32
33
  buildDirectPurchaseForm,
33
34
  buildPurchaseForm
34
35
  } from '@akinon/pz-masterpass/src/utils';
36
+ import { devLogger } from '@akinon/next/hooks/use-logger-context';
37
+ import { LogLevel } from '@akinon/next/hooks/use-logger';
38
+
39
+ const getLatestState = async (getState: () => any): Promise<any> => {
40
+ await new Promise((resolve) => setTimeout(resolve, 250));
41
+
42
+ return getState();
43
+ };
44
+
45
+ const recentLogMessages = new Map<string, number>();
46
+
47
+ const getStore = async (): Promise<AppStore> => {
48
+ const { store } = await import('redux/store');
49
+ return store;
50
+ };
35
51
 
36
52
  interface CheckoutResponse {
37
53
  pre_order?: PreOrder;
@@ -43,6 +59,53 @@ interface CheckoutResponse {
43
59
  redirect_url?: string;
44
60
  }
45
61
 
62
+ const validateCheckoutState = (
63
+ state: any,
64
+ validations: Array<{
65
+ condition: (state: any) => boolean;
66
+ errorMessage: string;
67
+ severity?: LogLevel;
68
+ data?: any;
69
+ action?: () => void;
70
+ }>
71
+ ) => {
72
+ validations.forEach(
73
+ ({
74
+ condition,
75
+ errorMessage,
76
+ severity = 'error',
77
+ data: logData,
78
+ action
79
+ }) => {
80
+ if (condition(state)) {
81
+ const now = Date.now();
82
+ const lastLogged = recentLogMessages.get(errorMessage) || 0;
83
+
84
+ if (now - lastLogged > 2000) {
85
+ recentLogMessages.set(errorMessage, now);
86
+
87
+ switch (severity) {
88
+ case 'error':
89
+ devLogger.error(
90
+ errorMessage,
91
+ logData || state.checkout?.preOrder
92
+ );
93
+ action?.();
94
+ break;
95
+ case 'warn':
96
+ devLogger.warn(errorMessage, logData || state.checkout?.preOrder);
97
+ action?.();
98
+ break;
99
+ default:
100
+ devLogger.info(errorMessage, logData || state.checkout?.preOrder);
101
+ action?.();
102
+ }
103
+ }
104
+ }
105
+ }
106
+ );
107
+ };
108
+
46
109
  interface SetAddressesParams {
47
110
  shippingAddressPk: number;
48
111
  billingAddressPk: number;
@@ -82,6 +145,18 @@ export interface PayOnDeliveryParams {
82
145
  paymentType: string;
83
146
  }
84
147
 
148
+ const buildCheckoutRequestUrl = (
149
+ path: string,
150
+ options?: Parameters<typeof buildClientRequestUrl>[1]
151
+ ) => {
152
+ const effectivePath =
153
+ getCookie('pz-post-checkout-flow') === 'true' &&
154
+ /^\/orders\/checkout(\/|\?|$)/.test(path)
155
+ ? path.replace('/orders/checkout', '/orders/post-checkout')
156
+ : path;
157
+ return buildClientRequestUrl(effectivePath, options);
158
+ };
159
+
85
160
  const completeMasterpassPayment = async (
86
161
  params: CompleteCreditCardParams,
87
162
  dispatch: AppDispatch,
@@ -177,45 +252,59 @@ const completeMasterpassPayment = async (
177
252
  });
178
253
  };
179
254
 
255
+ let checkoutAbortController = new AbortController();
256
+
257
+ export const getCheckoutAbortSignal = () => {
258
+ if (checkoutAbortController.signal.aborted) {
259
+ checkoutAbortController = new AbortController();
260
+ }
261
+ return checkoutAbortController.signal;
262
+ };
263
+
264
+ const abortCheckout = () => {
265
+ checkoutAbortController.abort();
266
+ checkoutAbortController = new AbortController();
267
+ };
268
+
180
269
  export const checkoutApi = api.injectEndpoints({
181
270
  endpoints: (build) => ({
182
271
  fetchCheckout: build.query<CheckoutResponse, void>({
183
272
  query: () => ({
184
- url: buildClientRequestUrl(checkout.fetchCheckout, {})
273
+ url: buildCheckoutRequestUrl(checkout.fetchCheckout, {})
185
274
  }),
186
275
  providesTags: ['Checkout']
187
276
  }),
188
277
  resetCheckoutState: build.query<CheckoutResponse, void>({
189
278
  query: () => ({
190
- url: buildClientRequestUrl(checkout.guestLogin, {})
279
+ url: buildCheckoutRequestUrl(checkout.guestLogin, {})
191
280
  })
192
281
  }),
193
282
  fetchCheckoutResult: build.query<{ order: Order }, string>({
194
283
  query: (token: string) =>
195
- buildClientRequestUrl(checkout.fetchCheckoutResult(token))
284
+ buildCheckoutRequestUrl(checkout.fetchCheckoutResult(token))
196
285
  }),
197
286
  get3dRedirectForm: build.query<{ result: string }, void>({
198
287
  query: () =>
199
- buildClientRequestUrl(checkout.get3dRedirectForm, {
288
+ buildCheckoutRequestUrl(checkout.get3dRedirectForm, {
200
289
  responseType: 'text'
201
290
  })
202
291
  }),
203
292
  getContract: build.query<GetContractResponse, string>({
204
293
  query: (slug: string) =>
205
- buildClientRequestUrl(checkout.getContract(slug), {
294
+ buildCheckoutRequestUrl(checkout.getContract(slug), {
206
295
  responseType: 'text'
207
296
  })
208
297
  }),
209
298
  getCoupons: build.query<CheckoutResponse, void>({
210
299
  query: () => ({
211
- url: buildClientRequestUrl(checkout.couponSelectionPage)
300
+ url: buildCheckoutRequestUrl(checkout.couponSelectionPage)
212
301
  })
213
302
  }),
214
303
 
215
304
  setCoupon: build.mutation<CheckoutResponse, { pk: number; action: string }>(
216
305
  {
217
306
  query: ({ pk, action }) => ({
218
- url: buildClientRequestUrl(checkout.couponSelectionPage, {
307
+ url: buildCheckoutRequestUrl(checkout.couponSelectionPage, {
219
308
  useFormData: true
220
309
  }),
221
310
  method: 'POST',
@@ -231,25 +320,32 @@ export const checkoutApi = api.injectEndpoints({
231
320
  CheckoutResponse,
232
321
  CompleteCreditCardParams
233
322
  >({
234
- query: ({
235
- card_holder,
236
- card_cvv,
237
- card_number,
238
- card_month,
239
- card_year,
240
- use_three_d = true,
241
- save = undefined
242
- }) => {
323
+ async queryFn(
324
+ {
325
+ card_holder,
326
+ card_cvv,
327
+ card_number,
328
+ card_month,
329
+ card_year,
330
+ use_three_d = true,
331
+ save = undefined
332
+ },
333
+ _queryApi,
334
+ _extraOptions,
335
+ baseQuery
336
+ ) {
337
+ const reduxStore = await getStore();
243
338
  const paymentOption =
244
- store.getState().checkout?.preOrder?.payment_option;
339
+ reduxStore.getState().checkout?.preOrder?.payment_option;
245
340
 
246
341
  if (paymentOption?.payment_type === 'masterpass') {
247
- return {
248
- url: buildClientRequestUrl(checkout.getMasterpassOrderNo, {
342
+ const result = await baseQuery({
343
+ url: buildCheckoutRequestUrl(checkout.getMasterpassOrderNo, {
249
344
  useFormData: true
250
345
  }),
251
346
  method: 'POST'
252
- };
347
+ });
348
+ return result as { data: CheckoutResponse } | { error: any };
253
349
  }
254
350
 
255
351
  const body: Record<string, string> = {
@@ -266,18 +362,19 @@ export const checkoutApi = api.injectEndpoints({
266
362
  body.save = save ? '1' : '0';
267
363
  }
268
364
 
269
- return {
270
- url: buildClientRequestUrl(checkout.completeCreditCardPayment, {
365
+ const result = await baseQuery({
366
+ url: buildCheckoutRequestUrl(checkout.completeCreditCardPayment, {
271
367
  useFormData: true
272
368
  }),
273
369
  method: 'POST',
274
370
  body
275
- };
371
+ });
372
+ return result as { data: CheckoutResponse } | { error: any };
276
373
  },
277
374
  async onQueryStarted(args, { dispatch, queryFulfilled }) {
278
375
  dispatch(setPaymentStepBusy(true));
279
376
  const { data } = await queryFulfilled;
280
- const reduxStore = (await import('redux/store')).store;
377
+ const reduxStore = await getStore();
281
378
  const completePaymentContext = data?.context_list?.find(
282
379
  (context) => context?.page_name === 'MasterpassCompletePage'
283
380
  );
@@ -302,7 +399,7 @@ export const checkoutApi = api.injectEndpoints({
302
399
  }
303
400
  >({
304
401
  query: ({ token }) => ({
305
- url: buildClientRequestUrl(checkout.completeMasterpassPayment, {
402
+ url: buildCheckoutRequestUrl(checkout.completeMasterpassPayment, {
306
403
  useFormData: true
307
404
  }),
308
405
  method: 'POST',
@@ -319,7 +416,7 @@ export const checkoutApi = api.injectEndpoints({
319
416
  }),
320
417
  completeFundsTransfer: build.mutation<CheckoutResponse, void>({
321
418
  query: () => ({
322
- url: buildClientRequestUrl(checkout.completeFundsTransfer, {
419
+ url: buildCheckoutRequestUrl(checkout.completeFundsTransfer, {
323
420
  useFormData: true
324
421
  }),
325
422
  method: 'POST',
@@ -336,7 +433,7 @@ export const checkoutApi = api.injectEndpoints({
336
433
  }),
337
434
  guestLogin: build.mutation<CheckoutResponse, GuestLoginFormParams>({
338
435
  query: ({ user_email, phone_number }) => ({
339
- url: buildClientRequestUrl(checkout.guestLogin, {
436
+ url: buildCheckoutRequestUrl(checkout.guestLogin, {
340
437
  useFormData: true
341
438
  }),
342
439
  method: 'POST',
@@ -349,56 +446,132 @@ export const checkoutApi = api.injectEndpoints({
349
446
  }),
350
447
  setDeliveryOption: build.mutation<CheckoutResponse, number>({
351
448
  query: (pk: number) => ({
352
- url: buildClientRequestUrl(checkout.setDeliveryOption, {
449
+ url: buildCheckoutRequestUrl(checkout.setDeliveryOption, {
353
450
  useFormData: true
354
451
  }),
355
452
  method: 'POST',
356
453
  body: {
357
454
  delivery_option: String(pk)
358
- }
455
+ },
456
+ signal: getCheckoutAbortSignal()
359
457
  }),
360
- async onQueryStarted(arg, { dispatch, queryFulfilled }) {
458
+ async onQueryStarted(arg, { dispatch, queryFulfilled, getState }) {
361
459
  dispatch(setShippingStepBusy(true));
362
- await queryFulfilled;
363
- dispatch(setShippingStepBusy(false));
460
+
461
+ const state = await getLatestState(getState);
462
+
463
+ validateCheckoutState(state, [
464
+ {
465
+ condition: (state) => {
466
+ const preOrder = state.checkout?.preOrder;
467
+
468
+ return preOrder?.basket?.basketitem_set?.length === 0;
469
+ },
470
+ errorMessage:
471
+ 'Your shopping basket is empty. Please add items to your basket before selecting a delivery option.',
472
+ action: () => abortCheckout()
473
+ }
474
+ ]);
475
+
476
+ try {
477
+ await queryFulfilled;
478
+ dispatch(setShippingStepBusy(false));
479
+ } catch (error) {
480
+ dispatch(setShippingStepBusy(false));
481
+ }
364
482
  }
365
483
  }),
366
484
  setAddresses: build.mutation<CheckoutResponse, SetAddressesParams>({
367
485
  query: ({ shippingAddressPk, billingAddressPk }) => ({
368
- url: buildClientRequestUrl(checkout.setAddresses, {
486
+ url: buildCheckoutRequestUrl(checkout.setAddresses, {
369
487
  useFormData: true
370
488
  }),
371
489
  method: 'POST',
372
490
  body: {
373
491
  shipping_address: String(shippingAddressPk),
374
492
  billing_address: String(billingAddressPk)
375
- }
493
+ },
494
+ signal: getCheckoutAbortSignal()
376
495
  }),
377
- async onQueryStarted(arg, { dispatch, queryFulfilled }) {
496
+ async onQueryStarted(arg, { dispatch, queryFulfilled, getState }) {
378
497
  dispatch(setShippingStepBusy(true));
379
- await queryFulfilled;
380
- dispatch(setShippingStepBusy(false));
498
+
499
+ const state = await getLatestState(getState);
500
+
501
+ validateCheckoutState(state, [
502
+ {
503
+ condition: (state) => {
504
+ const deliveryOptions = state.checkout?.deliveryOptions;
505
+ const preOrder = state.checkout?.preOrder;
506
+
507
+ return deliveryOptions?.length > 0
508
+ ? preOrder && !preOrder.delivery_option?.pk
509
+ : false;
510
+ },
511
+ errorMessage:
512
+ 'You need to select a delivery option before setting your addresses. Dispatch setAddresses action after delivery option selection.',
513
+ action: () => abortCheckout()
514
+ }
515
+ ]);
516
+
517
+ try {
518
+ await queryFulfilled;
519
+ dispatch(setShippingStepBusy(false));
520
+ } catch (error) {
521
+ dispatch(setShippingStepBusy(false));
522
+ }
381
523
  }
382
524
  }),
383
525
  setShippingOption: build.mutation<CheckoutResponse, number>({
384
526
  query: (pk: number) => ({
385
- url: buildClientRequestUrl(checkout.setShippingOption, {
527
+ url: buildCheckoutRequestUrl(checkout.setShippingOption, {
386
528
  useFormData: true
387
529
  }),
388
530
  method: 'POST',
389
531
  body: {
390
532
  shipping_option: String(pk)
391
- }
533
+ },
534
+ signal: getCheckoutAbortSignal()
392
535
  }),
393
- async onQueryStarted(arg, { dispatch, queryFulfilled }) {
536
+ async onQueryStarted(arg, { dispatch, queryFulfilled, getState }) {
394
537
  dispatch(setShippingStepBusy(true));
395
- await queryFulfilled;
396
- dispatch(setShippingStepBusy(false));
538
+
539
+ const state = await getLatestState(getState);
540
+
541
+ validateCheckoutState(state, [
542
+ {
543
+ condition: (state) => {
544
+ const preOrder = state.checkout?.preOrder;
545
+
546
+ return !preOrder?.billing_address;
547
+ },
548
+ errorMessage:
549
+ 'You need to provide a billing address before selecting a shipping option. Dispatch setShippingOption action after billing address selection.',
550
+ action: () => abortCheckout()
551
+ },
552
+ {
553
+ condition: (state) => {
554
+ const preOrder = state.checkout?.preOrder;
555
+
556
+ return !preOrder?.shipping_address;
557
+ },
558
+ errorMessage:
559
+ 'You need to provide a shipping address before selecting a shipping option. Dispatch setShippingOption action after shipping address selection.',
560
+ action: () => abortCheckout()
561
+ }
562
+ ]);
563
+
564
+ try {
565
+ await queryFulfilled;
566
+ dispatch(setShippingStepBusy(false));
567
+ } catch (error) {
568
+ dispatch(setShippingStepBusy(false));
569
+ }
397
570
  }
398
571
  }),
399
572
  setDataSourceShippingOptions: build.mutation<CheckoutResponse, number[]>({
400
573
  query: (pks) => ({
401
- url: buildClientRequestUrl(checkout.setDataSourceShippingOption, {
574
+ url: buildCheckoutRequestUrl(checkout.setDataSourceShippingOption, {
402
575
  useFormData: true
403
576
  }),
404
577
  method: 'POST',
@@ -414,7 +587,7 @@ export const checkoutApi = api.injectEndpoints({
414
587
  }),
415
588
  setRetailStore: build.mutation<CheckoutResponse, SetRetailStoreParams>({
416
589
  query: ({ retailStorePk, billingAddressPk }) => ({
417
- url: buildClientRequestUrl(
590
+ url: buildCheckoutRequestUrl(
418
591
  '/orders/checkout?page=RetailStoreSelectionPage',
419
592
  {
420
593
  useFormData: true
@@ -429,28 +602,49 @@ export const checkoutApi = api.injectEndpoints({
429
602
  }),
430
603
  fetchPaymentOptions: build.query<CheckoutResponse, void>({
431
604
  query: () => ({
432
- url: buildClientRequestUrl(checkout.setPaymentOption)
605
+ url: buildCheckoutRequestUrl(checkout.setPaymentOption)
433
606
  }),
434
607
  providesTags: ['PaymentOptions']
435
608
  }),
436
609
  setPaymentOption: build.mutation<CheckoutResponse, number>({
437
610
  query: (pk: number) => ({
438
- url: buildClientRequestUrl(checkout.setPaymentOption, {
611
+ url: buildCheckoutRequestUrl(checkout.setPaymentOption, {
439
612
  useFormData: true
440
613
  }),
441
614
  method: 'POST',
442
615
  body: {
443
616
  payment_option: String(pk)
444
- }
617
+ },
618
+ signal: getCheckoutAbortSignal()
445
619
  }),
446
- async onQueryStarted(arg, { dispatch, queryFulfilled }) {
620
+ async onQueryStarted(arg, { dispatch, queryFulfilled, getState }) {
447
621
  dispatch(setPaymentStepBusy(true));
448
622
  dispatch(setInstallmentOptions([]));
449
623
  dispatch(setBankAccounts([]));
450
624
  dispatch(setSelectedBankAccountPk(null));
451
625
  dispatch(setCardType(null));
452
- await queryFulfilled;
453
- dispatch(setPaymentStepBusy(false));
626
+
627
+ const state = await getLatestState(getState);
628
+
629
+ validateCheckoutState(state, [
630
+ {
631
+ condition: (state) => {
632
+ const preOrder = state.checkout?.preOrder;
633
+
634
+ return !preOrder?.shipping_option?.pk;
635
+ },
636
+ errorMessage:
637
+ 'You need to select a shipping option before choosing a payment method. Dispatch setPaymentOption action after shipping option selection.',
638
+ action: () => abortCheckout()
639
+ }
640
+ ]);
641
+
642
+ try {
643
+ await queryFulfilled;
644
+ dispatch(setPaymentStepBusy(false));
645
+ } catch (error) {
646
+ dispatch(setPaymentStepBusy(false));
647
+ }
454
648
  }
455
649
  }),
456
650
  setWalletSelectionPage: build.mutation<
@@ -461,7 +655,7 @@ export const checkoutApi = api.injectEndpoints({
461
655
  }
462
656
  >({
463
657
  query: ({ payment_option: pk, validationURL }) => ({
464
- url: buildClientRequestUrl(checkout.setWalletSelectionPage, {
658
+ url: buildCheckoutRequestUrl(checkout.setWalletSelectionPage, {
465
659
  useFormData: true
466
660
  }),
467
661
  method: 'POST',
@@ -485,7 +679,7 @@ export const checkoutApi = api.injectEndpoints({
485
679
  }
486
680
  >({
487
681
  query: (requestBody) => ({
488
- url: buildClientRequestUrl(checkout.setWalletPaymentPage, {
682
+ url: buildCheckoutRequestUrl(checkout.setWalletPaymentPage, {
489
683
  useFormData: true
490
684
  }),
491
685
  method: 'POST',
@@ -497,12 +691,22 @@ export const checkoutApi = api.injectEndpoints({
497
691
  dispatch(setPaymentStepBusy(false));
498
692
  }
499
693
  }),
500
- setWalletCompletePage: build.mutation<CheckoutResponse, boolean>({
501
- query: (success: boolean) => ({
502
- url: buildClientRequestUrl(checkout.setWalletCompletePage),
694
+ setWalletCompletePage: build.mutation<
695
+ CheckoutResponse,
696
+ {
697
+ success: boolean;
698
+ cko_payment_id?: string;
699
+ [key: string]: any;
700
+ }
701
+ >({
702
+ query: ({ success, ...additionalParams }) => ({
703
+ url: buildCheckoutRequestUrl(checkout.setWalletCompletePage, {
704
+ useFormData: true
705
+ }),
503
706
  method: 'POST',
504
707
  body: {
505
- success
708
+ success,
709
+ ...additionalParams
506
710
  }
507
711
  }),
508
712
  async onQueryStarted(arg, { dispatch, queryFulfilled }) {
@@ -512,23 +716,25 @@ export const checkoutApi = api.injectEndpoints({
512
716
  }
513
717
  }),
514
718
  setBinNumber: build.mutation<CheckoutResponse, string>({
515
- query: (binNumber: string) => {
719
+ async queryFn(binNumber, _queryApi, _extraOptions, baseQuery) {
720
+ const reduxStore = await getStore();
516
721
  const paymentOption =
517
- store.getState().checkout?.preOrder?.payment_option;
722
+ reduxStore.getState().checkout?.preOrder?.payment_option;
518
723
  const binNumberUrl =
519
724
  paymentOption?.payment_type === 'masterpass'
520
725
  ? checkout.setMasterpassBinNumber
521
726
  : checkout.setBinNumber;
522
727
 
523
- return {
524
- url: buildClientRequestUrl(binNumberUrl, {
728
+ const result = await baseQuery({
729
+ url: buildCheckoutRequestUrl(binNumberUrl, {
525
730
  useFormData: true
526
731
  }),
527
732
  method: 'POST',
528
733
  body: {
529
734
  bin_number: binNumber
530
735
  }
531
- };
736
+ });
737
+ return result as { data: CheckoutResponse } | { error: any };
532
738
  },
533
739
  async onQueryStarted(arg, { dispatch, queryFulfilled }) {
534
740
  dispatch(setPaymentStepBusy(true));
@@ -538,22 +744,25 @@ export const checkoutApi = api.injectEndpoints({
538
744
  }
539
745
  }),
540
746
  setInstallmentOption: build.mutation<CheckoutResponse, number>({
541
- query: (pk: number) => {
747
+ async queryFn(pk, _queryApi, _extraOptions, baseQuery) {
748
+ const reduxStore = await getStore();
542
749
  const paymentOption =
543
- store.getState().checkout?.preOrder?.payment_option;
750
+ reduxStore.getState().checkout?.preOrder?.payment_option;
544
751
  const installmentOption =
545
752
  paymentOption?.payment_type === 'masterpass'
546
753
  ? checkout.setMasterPassInstallmentOption
547
754
  : checkout.setInstallmentOption;
548
- return {
549
- url: buildClientRequestUrl(installmentOption, {
755
+
756
+ const result = await baseQuery({
757
+ url: buildCheckoutRequestUrl(installmentOption, {
550
758
  useFormData: true
551
759
  }),
552
760
  method: 'POST',
553
761
  body: {
554
762
  installment: String(pk)
555
763
  }
556
- };
764
+ });
765
+ return result as { data: CheckoutResponse } | { error: any };
557
766
  },
558
767
  async onQueryStarted(arg, { dispatch, queryFulfilled }) {
559
768
  dispatch(setPaymentStepBusy(true));
@@ -563,7 +772,7 @@ export const checkoutApi = api.injectEndpoints({
563
772
  }),
564
773
  setFundsTransferOption: build.mutation<CheckoutResponse, number>({
565
774
  query: (pk: number) => ({
566
- url: buildClientRequestUrl(checkout.setFundsTransferOption, {
775
+ url: buildCheckoutRequestUrl(checkout.setFundsTransferOption, {
567
776
  useFormData: true
568
777
  }),
569
778
  method: 'POST',
@@ -580,7 +789,7 @@ export const checkoutApi = api.injectEndpoints({
580
789
  }),
581
790
  setCreditPaymentOption: build.mutation<CheckoutResponse, number>({
582
791
  query: (pk: number) => ({
583
- url: buildClientRequestUrl(checkout.setCreditPaymentOption, {
792
+ url: buildCheckoutRequestUrl(checkout.setCreditPaymentOption, {
584
793
  useFormData: true
585
794
  }),
586
795
  method: 'POST',
@@ -597,7 +806,7 @@ export const checkoutApi = api.injectEndpoints({
597
806
  }),
598
807
  confirmationCreditPayment: build.mutation<CheckoutResponse, void>({
599
808
  query: () => ({
600
- url: buildClientRequestUrl(checkout.confirmationCreditPayment, {
809
+ url: buildCheckoutRequestUrl(checkout.confirmationCreditPayment, {
601
810
  useFormData: true
602
811
  }),
603
812
  method: 'POST',
@@ -613,7 +822,7 @@ export const checkoutApi = api.injectEndpoints({
613
822
  }),
614
823
  completeRedirectionPayment: build.mutation<CheckoutResponse, void>({
615
824
  query: () => ({
616
- url: buildClientRequestUrl(checkout.completeRedirectionPayment, {
825
+ url: buildCheckoutRequestUrl(checkout.completeRedirectionPayment, {
617
826
  useFormData: true
618
827
  }),
619
828
  method: 'POST',
@@ -629,7 +838,7 @@ export const checkoutApi = api.injectEndpoints({
629
838
  }),
630
839
  applePaymentSelect: build.mutation<CheckoutResponse, ApplePaySelectParams>({
631
840
  query: ({ agreement, validationURL }) => ({
632
- url: buildClientRequestUrl(checkout.confirmationPaymentSelect, {
841
+ url: buildCheckoutRequestUrl(checkout.confirmationPaymentSelect, {
633
842
  useFormData: true
634
843
  }),
635
844
  method: 'POST',
@@ -646,7 +855,7 @@ export const checkoutApi = api.injectEndpoints({
646
855
  }),
647
856
  appleQuery: build.mutation<CheckoutResponse, ApplePayQueryParams>({
648
857
  query: ({ agreement, paymentToken }) => ({
649
- url: buildClientRequestUrl(checkout.confirmationQuery, {
858
+ url: buildCheckoutRequestUrl(checkout.confirmationQuery, {
650
859
  useFormData: true
651
860
  }),
652
861
  method: 'POST',
@@ -663,7 +872,7 @@ export const checkoutApi = api.injectEndpoints({
663
872
  }),
664
873
  completeConfirmation: build.mutation<CheckoutResponse, void>({
665
874
  query: () => ({
666
- url: buildClientRequestUrl(checkout.confirmationComplete, {
875
+ url: buildCheckoutRequestUrl(checkout.confirmationComplete, {
667
876
  useFormData: true
668
877
  }),
669
878
  method: 'POST',
@@ -682,7 +891,7 @@ export const checkoutApi = api.injectEndpoints({
682
891
  PayOnDeliveryParams
683
892
  >({
684
893
  query: ({ paymentType }) => ({
685
- url: buildClientRequestUrl(
894
+ url: buildCheckoutRequestUrl(
686
895
  `${checkout.fetchCheckout}?page=PayOnDeliveryPage`,
687
896
  { useFormData: true }
688
897
  ),
@@ -700,7 +909,7 @@ export const checkoutApi = api.injectEndpoints({
700
909
  }),
701
910
  setPayOnDeliveryChoice: build.mutation<CheckoutResponse, string>({
702
911
  query: (body) => ({
703
- url: buildClientRequestUrl(
912
+ url: buildCheckoutRequestUrl(
704
913
  `${checkout.fetchCheckout}?page=PayOnDeliveryPaymentChoicePage`,
705
914
  { useFormData: true }
706
915
  ),
@@ -717,7 +926,7 @@ export const checkoutApi = api.injectEndpoints({
717
926
  }),
718
927
  completeLoyaltyPayment: build.mutation<CheckoutResponse, void>({
719
928
  query: () => ({
720
- url: buildClientRequestUrl(checkout.completeLoyaltyPayment, {
929
+ url: buildCheckoutRequestUrl(checkout.completeLoyaltyPayment, {
721
930
  useFormData: true
722
931
  }),
723
932
  method: 'POST',
@@ -733,19 +942,31 @@ export const checkoutApi = api.injectEndpoints({
733
942
  }),
734
943
  getCheckoutLoyaltyBalance: build.query<CheckoutResponse, void>({
735
944
  query: () => ({
736
- url: buildClientRequestUrl(checkout.loyaltyMoneyUsage)
945
+ url: buildCheckoutRequestUrl(checkout.loyaltyMoneyUsage)
737
946
  })
738
947
  }),
739
- payWithLoyaltyBalance: build.mutation<any, string>({
740
- query: (amount) => ({
741
- url: buildClientRequestUrl(checkout.loyaltyMoneyUsage, {
742
- useFormData: true
743
- }),
744
- method: 'POST',
745
- body: {
746
- loyalty_amount_to_use: amount
747
- }
748
- }),
948
+ payWithLoyaltyBalance: build.mutation<
949
+ any,
950
+ string | { account_usages: AccountUsage[] }
951
+ >({
952
+ query: (params) => {
953
+ const isAccountUsages =
954
+ typeof params === 'object' && 'account_usages' in params;
955
+
956
+ return {
957
+ url: buildCheckoutRequestUrl(checkout.loyaltyMoneyUsage, {
958
+ useFormData: true
959
+ }),
960
+ method: 'POST',
961
+ body: isAccountUsages
962
+ ? {
963
+ account_usages: JSON.stringify(params.account_usages)
964
+ }
965
+ : {
966
+ loyalty_amount_to_use: params
967
+ }
968
+ };
969
+ },
749
970
  async onQueryStarted(arg, { dispatch, queryFulfilled }) {
750
971
  dispatch(setPaymentStepBusy(true));
751
972
  dispatch(setPaymentOptions([]));
@@ -769,7 +990,7 @@ export const checkoutApi = api.injectEndpoints({
769
990
  }),
770
991
  setOrderNote: build.mutation<CheckoutResponse, string>({
771
992
  query: (notes) => ({
772
- url: buildClientRequestUrl(checkout.setOrderNote, {
993
+ url: buildCheckoutRequestUrl(checkout.setOrderNote, {
773
994
  useFormData: true
774
995
  }),
775
996
  method: 'POST',
@@ -789,7 +1010,7 @@ export const checkoutApi = api.injectEndpoints({
789
1010
  };
790
1011
 
791
1012
  return {
792
- url: buildClientRequestUrl(checkout.deliveryBagsPage, {
1013
+ url: buildCheckoutRequestUrl(checkout.deliveryBagsPage, {
793
1014
  useFormData: true
794
1015
  }),
795
1016
  method: 'POST',
@@ -802,7 +1023,7 @@ export const checkoutApi = api.injectEndpoints({
802
1023
  Record<string, number>
803
1024
  >({
804
1025
  query: (options) => ({
805
- url: buildClientRequestUrl(checkout.setAttributeBasedShippingOption, {
1026
+ url: buildCheckoutRequestUrl(checkout.setAttributeBasedShippingOption, {
806
1027
  useFormData: true
807
1028
  }),
808
1029
  method: 'POST',
@@ -821,7 +1042,7 @@ export const checkoutApi = api.injectEndpoints({
821
1042
  { extra_field: ExtraField }
822
1043
  >({
823
1044
  query: ({ extra_field }) => ({
824
- url: buildClientRequestUrl(checkout.setOrderSelectionPage, {
1045
+ url: buildCheckoutRequestUrl(checkout.setOrderSelectionPage, {
825
1046
  useFormData: true
826
1047
  }),
827
1048
  method: 'POST',
@@ -832,16 +1053,16 @@ export const checkoutApi = api.injectEndpoints({
832
1053
  }),
833
1054
  fetchLoyaltyData: build.query<CheckoutResponse, void>({
834
1055
  query: () => ({
835
- url: buildClientRequestUrl(checkout.loyaltyCardPage, {
1056
+ url: buildCheckoutRequestUrl(checkout.loyaltyCardPage, {
836
1057
  accept: 'application/json',
837
1058
  contentType: 'application/json'
838
1059
  }),
839
1060
  method: 'GET'
840
1061
  })
841
1062
  }),
842
- setLoyaltyData: build.mutation<CheckoutResponse, number>({
1063
+ setLoyaltyData: build.mutation<CheckoutResponse, number | string>({
843
1064
  query: (amount) => ({
844
- url: buildClientRequestUrl(checkout.loyaltyCardPage, {
1065
+ url: buildCheckoutRequestUrl(checkout.loyaltyCardPage, {
845
1066
  useFormData: true
846
1067
  }),
847
1068
  method: 'POST',
@@ -852,7 +1073,7 @@ export const checkoutApi = api.injectEndpoints({
852
1073
  }),
853
1074
  sendSms: build.mutation<CheckoutResponse, SendSmsType>({
854
1075
  query: (body) => ({
855
- url: buildClientRequestUrl(checkout.sendSmsPage, {
1076
+ url: buildCheckoutRequestUrl(checkout.sendSmsPage, {
856
1077
  useFormData: true
857
1078
  }),
858
1079
  method: 'POST',
@@ -861,12 +1082,27 @@ export const checkoutApi = api.injectEndpoints({
861
1082
  }),
862
1083
  verifySms: build.mutation<CheckoutResponse, VerifySmsType>({
863
1084
  query: (body) => ({
864
- url: buildClientRequestUrl(checkout.verifySmsPage, {
1085
+ url: buildCheckoutRequestUrl(checkout.verifySmsPage, {
865
1086
  useFormData: true
866
1087
  }),
867
1088
  method: 'POST',
868
1089
  body
869
1090
  })
1091
+ }),
1092
+ saveSampleProducts: build.mutation<CheckoutResponse, number[] | undefined>({
1093
+ query: (products = []) => {
1094
+ const formData = new FormData();
1095
+
1096
+ products.forEach((product) => {
1097
+ formData.append('sample_products', String(product));
1098
+ });
1099
+
1100
+ return {
1101
+ url: buildCheckoutRequestUrl(checkout.saveSampleProducts),
1102
+ method: 'POST',
1103
+ body: formData
1104
+ };
1105
+ }
870
1106
  })
871
1107
  }),
872
1108
  overrideExisting: false
@@ -914,5 +1150,6 @@ export const {
914
1150
  useSetWalletCompletePageMutation,
915
1151
  useSendSmsMutation,
916
1152
  useVerifySmsMutation,
917
- useResetCheckoutStateQuery
1153
+ useResetCheckoutStateQuery,
1154
+ useSaveSampleProductsMutation
918
1155
  } = checkoutApi;