@akinon/next 1.41.0-rc.1 → 1.42.0-rc.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 CHANGED
@@ -1,25 +1,35 @@
1
1
  # @akinon/next
2
2
 
3
- ## 1.41.0-rc.1
4
-
5
- ### Minor Changes
6
-
7
- - 98bb8dc: ZERO-2706: Cache getTranlations method
8
-
9
- ## 1.41.0-rc.0
3
+ ## 1.42.0-rc.0
10
4
 
11
5
  ### Minor Changes
12
6
 
7
+ - 90282b5: ZERO-2729: Audit packages for yarn and npm and also update app-template
8
+ - 572d2e8: ZERO-2667: Add iframe support for redirection payment methods
13
9
  - a4c8d6a: ZERO-2663: Fix the image url for gif and svgs and return them without options
14
10
  - c53ea3e: ZERO-2609: Reset additional form fields when selectedFormType is not company
15
11
  - c53ef7b: ZERO-2668: The Link component has been updated to improve the logic for handling href values. Previously, if the href was not a string or started with 'http', it would return the href as is. Now, if the href is not provided, it will default to '#' to prevent any potential errors. Additionally, if the href is a string and does not start with 'http', it will be formatted with the locale and pathname, based on the localeUrlStrategy and defaultLocaleValue. This ensures that the correct href is generated based on the localization settings.
12
+ - 0d3a913: ZERO-2725: Update decimal scale in Price component
16
13
  - 1448a96: ZERO-2612: add errors type in CheckoutState
14
+ - d3474c6: ZERO-2655: Add data source shipping option
17
15
  - 75080fd: ZERO-2630: Add max limit to postcode area
18
16
  - 91265bb: ZERO-2551: Improve pretty url and caching performance
19
17
  - bbe18b9: ZERO-2575: Fix build error
18
+ - 98bb8dc: ZERO-2706: Cache getTranlations method
19
+ - dcc8a15: ZERO-2694: added build step to RC branch pipeline
20
+ - fad2768: ZERO-2739: add gpay to payment plugin map
20
21
  - dff0d59: ZERO-2659: add formData support to proxy api requests
21
22
  - beb499e: ZERO-2551: Add new tsconfig paths
23
+ - c47be30: ZERO-2744: Update Order and OrderItem types
24
+ - e9a46ac: ZERO-2738: add CVC input to registered cards in Masterpass
22
25
  - f046f8e: ZERO-2575: update version for react-number-format
26
+ - 86d2531: ZERO-2693: resolve dependency collision warning for eslint-config-next
27
+
28
+ ## 1.41.0
29
+
30
+ ### Minor Changes
31
+
32
+ - 8f09473: ZERO-2686: create akifast plugin
23
33
 
24
34
  ## 1.40.0
25
35
 
@@ -0,0 +1,49 @@
1
+ .checkout-payment-iframe-wrapper {
2
+ position: fixed;
3
+ top: 0;
4
+ left: 0;
5
+ width: 100%;
6
+ height: 100%;
7
+ border: none;
8
+ z-index: 1000;
9
+ background-color: white;
10
+ }
11
+ .checkout-payment-iframe-wrapper iframe {
12
+ width: 100%;
13
+ height: 100%;
14
+ border: none;
15
+ background-color: white;
16
+ }
17
+ .checkout-payment-iframe-wrapper .close-button {
18
+ position: fixed;
19
+ top: 16px;
20
+ right: 16px;
21
+ width: 32px;
22
+ height: 32px;
23
+ display: flex;
24
+ align-items: center;
25
+ justify-content: center;
26
+ z-index: 1001;
27
+ }
28
+
29
+ .checkout-payment-redirection-iframe-wrapper {
30
+ width: 100%;
31
+ position: relative;
32
+ }
33
+ .checkout-payment-redirection-iframe-wrapper iframe {
34
+ width: 100%;
35
+ height: 100%;
36
+ border: none;
37
+ background-color: white;
38
+ }
39
+ .checkout-payment-redirection-iframe-wrapper .close-button {
40
+ position: absolute;
41
+ top: 16px;
42
+ right: 16px;
43
+ width: 32px;
44
+ height: 32px;
45
+ display: flex;
46
+ align-items: center;
47
+ justify-content: center;
48
+ z-index: 1001;
49
+ }/*# sourceMappingURL=index.css.map */
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["index.scss","index.css"],"names":[],"mappings":"AAAA;EACE,eAAA;EACA,MAAA;EACA,OAAA;EACA,WAAA;EACA,YAAA;EACA,YAAA;EACA,aAAA;EACA,uBAAA;ACCF;ADCE;EACE,WAAA;EACA,YAAA;EACA,YAAA;EACA,uBAAA;ACCJ;ADEE;EACE,eAAA;EACA,SAAA;EACA,WAAA;EACA,WAAA;EACA,YAAA;EACA,aAAA;EACA,mBAAA;EACA,uBAAA;EACA,aAAA;ACAJ;;ADIA;EACE,WAAA;EACA,kBAAA;ACDF;ADGE;EACE,WAAA;EACA,YAAA;EACA,YAAA;EACA,uBAAA;ACDJ;ADIE;EACE,kBAAA;EACA,SAAA;EACA,WAAA;EACA,WAAA;EACA,YAAA;EACA,aAAA;EACA,mBAAA;EACA,uBAAA;EACA,aAAA;ACFJ","file":"index.css"}
@@ -1,29 +1,53 @@
1
1
  .checkout-payment-iframe-wrapper {
2
- position: fixed;
3
- top: 0;
4
- left: 0;
5
- width: 100%;
6
- height: 100%;
7
- border: none;
8
- z-index: 1000;
9
- background-color: white;
2
+ position: fixed;
3
+ top: 0;
4
+ left: 0;
5
+ width: 100%;
6
+ height: 100%;
7
+ border: none;
8
+ z-index: 1000;
9
+ background-color: white;
10
10
 
11
- iframe {
12
- width: 100%;
13
- height: 100%;
14
- border: none;
15
- background-color: white;
16
- }
11
+ iframe {
12
+ width: 100%;
13
+ height: 100%;
14
+ border: none;
15
+ background-color: white;
16
+ }
17
17
 
18
- .close-button {
19
- position: fixed;
20
- top: 16px;
21
- right: 16px;
22
- width: 32px;
23
- height: 32px;
24
- display: flex;
25
- align-items: center;
26
- justify-content: center;
27
- z-index: 1001;
28
- }
29
- }
18
+ .close-button {
19
+ position: fixed;
20
+ top: 16px;
21
+ right: 16px;
22
+ width: 32px;
23
+ height: 32px;
24
+ display: flex;
25
+ align-items: center;
26
+ justify-content: center;
27
+ z-index: 1001;
28
+ }
29
+ }
30
+
31
+ .checkout-payment-redirection-iframe-wrapper {
32
+ width: 100%;
33
+ position: relative;
34
+
35
+ iframe {
36
+ width: 100%;
37
+ height: 100%;
38
+ border: none;
39
+ background-color: white;
40
+ }
41
+
42
+ .close-button {
43
+ position: absolute;
44
+ top: 16px;
45
+ right: 16px;
46
+ width: 32px;
47
+ height: 32px;
48
+ display: flex;
49
+ align-items: center;
50
+ justify-content: center;
51
+ z-index: 1001;
52
+ }
53
+ }
@@ -4,6 +4,7 @@ import dynamic from 'next/dynamic';
4
4
  import plugins from 'plugins';
5
5
  import logger from '../utils/log';
6
6
  import { useMemo } from 'react';
7
+ import settings from 'settings';
7
8
 
8
9
  enum Plugin {
9
10
  BasketGiftPack = 'pz-basket-gift-pack',
@@ -16,13 +17,15 @@ enum Plugin {
16
17
  BKMExpress = 'pz-bkm',
17
18
  CreditPayment = 'pz-credit-payment',
18
19
  Masterpass = 'pz-masterpass',
19
- B2B = 'pz-b2b'
20
+ B2B = 'pz-b2b',
21
+ Akifast = 'pz-akifast'
20
22
  }
21
23
 
22
24
  export enum Component {
23
25
  BasketGiftPack = 'BasketGiftPack',
24
26
  ClickCollect = 'ClickCollect',
25
27
  OneClickCheckoutButtons = 'OneClickCheckoutButtons',
28
+ OneClickProviderButton = 'OneClickCheckoutButton',
26
29
  PayOnDelivery = 'PayOnDelivery',
27
30
  CheckoutGiftPack = 'CheckoutGiftPack',
28
31
  GPay = 'GPayOption',
@@ -36,13 +39,18 @@ export enum Component {
36
39
  MasterpassOtpModal = 'MasterpassOtpModal',
37
40
  MasterpassLinkModal = 'MasterpassLinkModal',
38
41
  MyQuotationsB2B = 'AccountMyQuotations',
39
- BasketB2B = 'BasketB2b'
42
+ BasketB2B = 'BasketB2b',
43
+ AkifastQuickLoginButton = 'QuickLoginButton',
44
+ AkifastCheckoutButton = 'CheckoutButton'
40
45
  }
41
46
 
42
47
  const PluginComponents = new Map([
43
48
  [Plugin.BasketGiftPack, [Component.BasketGiftPack]],
44
49
  [Plugin.ClickCollect, [Component.ClickCollect]],
45
- [Plugin.OneClickCheckout, [Component.OneClickCheckoutButtons]],
50
+ [
51
+ Plugin.OneClickCheckout,
52
+ [Component.OneClickCheckoutButtons, Component.OneClickProviderButton]
53
+ ],
46
54
  [Plugin.PayOnDelivery, [Component.PayOnDelivery]],
47
55
  [Plugin.CheckoutGiftPack, [Component.CheckoutGiftPack]],
48
56
  [Plugin.GPay, [Component.GPay]],
@@ -60,7 +68,11 @@ const PluginComponents = new Map([
60
68
  Component.MasterpassLinkModal
61
69
  ]
62
70
  ],
63
- [Plugin.B2B, [Component.MyQuotationsB2B, Component.BasketB2B]]
71
+ [Plugin.B2B, [Component.MyQuotationsB2B, Component.BasketB2B]],
72
+ [
73
+ Plugin.Akifast,
74
+ [Component.AkifastQuickLoginButton, Component.AkifastCheckoutButton]
75
+ ]
64
76
  ]);
65
77
 
66
78
  const getPlugin = (component: Component) => {
@@ -81,6 +93,14 @@ export default function PluginModule({
81
93
  children?: React.ReactNode;
82
94
  }) {
83
95
  const plugin = getPlugin(component);
96
+ const pluginSettings = settings.plugins?.[plugin];
97
+
98
+ if (pluginSettings) {
99
+ props = {
100
+ ...props,
101
+ settings: pluginSettings
102
+ };
103
+ }
84
104
 
85
105
  const Component = useMemo(
86
106
  () =>
@@ -111,6 +131,8 @@ export default function PluginModule({
111
131
  promise = import(`${'@akinon/pz-masterpass'}`);
112
132
  } else if (plugin === Plugin.B2B) {
113
133
  promise = import(`${'@akinon/pz-b2b'}`);
134
+ } else if (plugin === Plugin.Akifast) {
135
+ promise = import(`${'@akinon/pz-akifast'}`);
114
136
  }
115
137
  } catch (error) {
116
138
  logger.error(error);
@@ -5,6 +5,7 @@ import { getCurrency } from '@akinon/next/utils';
5
5
 
6
6
  import { useLocalization } from '@akinon/next/hooks';
7
7
  import { PriceProps } from '../types';
8
+ import Settings from '@theme/settings';
8
9
 
9
10
  export const Price = (props: NumericFormatProps & PriceProps) => {
10
11
  const {
@@ -28,6 +29,10 @@ export const Price = (props: NumericFormatProps & PriceProps) => {
28
29
  // TODO: This is very bad practice. It broke decimalScale.
29
30
  const _value = value?.toString().replace('.', ',');
30
31
 
32
+ const currentCurrencyDecimalScale = Settings.localization.currencies.find(
33
+ (currency) => currency.code === currencyCode_
34
+ ).decimalScale;
35
+
31
36
  const currency = useMemo(
32
37
  () =>
33
38
  getCurrency({
@@ -47,7 +52,7 @@ export const Price = (props: NumericFormatProps & PriceProps) => {
47
52
  }}
48
53
  displayType={displayType}
49
54
  thousandSeparator={thousandSeparator}
50
- decimalScale={decimalScale}
55
+ decimalScale={currentCurrencyDecimalScale ?? decimalScale}
51
56
  decimalSeparator={decimalSeparator}
52
57
  fixedDecimalScale={fixedDecimalScale}
53
58
  {...rest}
@@ -6,6 +6,21 @@ import dynamic from 'next/dynamic';
6
6
  import { PaymentOptionViews } from 'views/checkout/steps/payment';
7
7
  import { useMemo } from 'react';
8
8
 
9
+ const fallbackView = () => <div />;
10
+
11
+ const paymentTypeToView = {
12
+ bkm_express: 'bkm',
13
+ credit_card: 'credit-card',
14
+ credit_payment: 'credit-payment',
15
+ funds_transfer: 'funds-transfer',
16
+ gpay: 'gpay',
17
+ loyalty_money: 'loyalty',
18
+ masterpass: 'credit-card',
19
+ pay_on_delivery: 'pay-on-delivery',
20
+ redirection: 'redirection'
21
+ // Add other mappings as needed
22
+ };
23
+
9
24
  export default function SelectedPaymentOptionView() {
10
25
  const { payment_option } = useAppSelector(
11
26
  (state: RootState) => state.checkout.preOrder
@@ -14,7 +29,7 @@ export default function SelectedPaymentOptionView() {
14
29
  const Component = useMemo(
15
30
  () =>
16
31
  dynamic(
17
- () => {
32
+ async () => {
18
33
  const customOption = PaymentOptionViews.find(
19
34
  (opt) => opt.slug === payment_option.slug
20
35
  );
@@ -29,46 +44,19 @@ export default function SelectedPaymentOptionView() {
29
44
  });
30
45
  }
31
46
 
32
- let promise: any;
33
-
34
- try {
35
- if (payment_option.payment_type === 'credit_card') {
36
- promise = import(
37
- `views/checkout/steps/payment/options/credit-card`
38
- );
39
- } else if (payment_option.payment_type === 'funds_transfer') {
40
- promise = import(
41
- `views/checkout/steps/payment/options/funds-transfer`
42
- );
43
- } else if (payment_option.payment_type === 'redirection') {
44
- promise = import(
45
- `views/checkout/steps/payment/options/redirection`
46
- );
47
- } else if (payment_option.payment_type === 'pay_on_delivery') {
48
- promise = import(
49
- `views/checkout/steps/payment/options/pay-on-delivery`
50
- );
51
- } else if (payment_option.payment_type === 'loyalty_money') {
52
- promise = import(`views/checkout/steps/payment/options/loyalty`);
53
- } else if (payment_option.payment_type === 'masterpass') {
54
- promise = import(
55
- `views/checkout/steps/payment/options/credit-card`
47
+ const view = paymentTypeToView[payment_option.payment_type];
48
+ if (view) {
49
+ try {
50
+ const mod = await import(
51
+ `views/checkout/steps/payment/options/${view}`
56
52
  );
53
+ return mod.default || fallbackView;
54
+ } catch (error) {
55
+ return fallbackView;
57
56
  }
58
- // else if (payment_option.payment_type === 'credit_payment') {
59
- // promise = import(`views/checkout/steps/payment/options/credit-payment`);
60
- // }
61
- // else if (payment_option.payment_type === 'gpay') {
62
- // promise = import(`views/checkout/steps/payment/options/gpay`);
63
- // }
64
- // else if (payment_option.payment_type === 'bkm_express') {
65
- // promise = import(`views/checkout/steps/payment/options/bkm`);
66
- // }
67
- } catch (error) {}
57
+ }
68
58
 
69
- return promise
70
- ? promise.then((mod) => mod.default).catch(() => () => null)
71
- : new Promise<any>((resolve) => resolve(() => null));
59
+ return fallbackView;
72
60
  },
73
61
  { ssr: false }
74
62
  ),
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  setBankAccounts,
3
+ setCardType,
3
4
  setInstallmentOptions,
4
5
  setPaymentStepBusy,
5
6
  setSelectedBankAccountPk,
6
7
  setSelectedCreditPaymentPk,
7
- setShippingStepBusy,
8
- setCardType
8
+ setShippingStepBusy
9
9
  } from '../../redux/reducers/checkout';
10
10
  import {
11
11
  CheckoutContext,
@@ -20,6 +20,7 @@ import { AppDispatch, AppStore, store } from 'redux/store';
20
20
  import settings from 'settings';
21
21
  import { showMobile3dIframe } from '../../utils/mobile-3d-iframe';
22
22
  import {
23
+ setCvcRequired,
23
24
  setError,
24
25
  setOtpModalVisible
25
26
  } from '@akinon/pz-masterpass/src/redux/reducer';
@@ -113,7 +114,8 @@ const completeMasterpassPayment = async (
113
114
  ? await buildDirectPurchaseForm(commonFormValues, params)
114
115
  : await buildPurchaseForm({
115
116
  ...commonFormValues,
116
- selectedCard
117
+ selectedCard,
118
+ cardCvc: params?.card_cvv
117
119
  });
118
120
 
119
121
  window.MFS?.[
@@ -151,6 +153,10 @@ const completeMasterpassPayment = async (
151
153
  return;
152
154
  }
153
155
 
156
+ if (['5013', '5182'].includes(response.responseCode)) {
157
+ dispatch(setCvcRequired(true));
158
+ }
159
+
154
160
  if (
155
161
  response.token &&
156
162
  (response.responseCode == '0000' || response.responseCode == '')
@@ -373,6 +379,22 @@ export const checkoutApi = api.injectEndpoints({
373
379
  dispatch(setShippingStepBusy(false));
374
380
  }
375
381
  }),
382
+ setDataSourceShippingOptions: build.mutation<CheckoutResponse, number[]>({
383
+ query: (pks) => ({
384
+ url: buildClientRequestUrl(checkout.setDataSourceShippingOption, {
385
+ useFormData: true
386
+ }),
387
+ method: 'POST',
388
+ body: {
389
+ data_source_shipping_options: JSON.stringify(pks)
390
+ }
391
+ }),
392
+ async onQueryStarted(arg, { dispatch, queryFulfilled }) {
393
+ dispatch(setShippingStepBusy(true));
394
+ await queryFulfilled;
395
+ dispatch(setShippingStepBusy(false));
396
+ }
397
+ }),
376
398
  setRetailStore: build.mutation<CheckoutResponse, SetRetailStoreParams>({
377
399
  query: ({ retailStorePk, billingAddressPk }) => ({
378
400
  url: buildClientRequestUrl(
@@ -672,6 +694,7 @@ export const {
672
694
  useSetDeliveryOptionMutation,
673
695
  useSetAddressesMutation,
674
696
  useSetShippingOptionMutation,
697
+ useSetDataSourceShippingOptionsMutation,
675
698
  useSetPaymentOptionMutation,
676
699
  useSetBinNumberMutation,
677
700
  useSetInstallmentOptionMutation,
package/data/urls.ts CHANGED
@@ -79,6 +79,8 @@ export const checkout = {
79
79
  setDeliveryOption: '/orders/checkout/?page=DeliveryOptionSelectionPage',
80
80
  setAddresses: '/orders/checkout/?page=AddressSelectionPage',
81
81
  setShippingOption: '/orders/checkout/?page=ShippingOptionSelectionPage',
82
+ setDataSourceShippingOption:
83
+ '/orders/checkout/?page=DataSourceShippingOptionSelectionPage',
82
84
  setPaymentOption: '/orders/checkout/?page=PaymentOptionSelectionPage',
83
85
  setBinNumber: '/orders/checkout/?page=BinNumberPage',
84
86
  setMasterpassBinNumber: '/orders/checkout/?page=MasterpassBinNumberPage',
package/hooks/index.ts CHANGED
@@ -8,4 +8,5 @@ export * from './use-media-query';
8
8
  export * from './use-on-click-outside';
9
9
  export * from './use-mobile-iframe-handler';
10
10
  export * from './use-payment-options';
11
- export * from './use-pagination';
11
+ export * from './use-pagination';
12
+ export * from './use-message-listener';
@@ -0,0 +1,24 @@
1
+ import { useEffect } from 'react';
2
+
3
+ export const useMessageListener = () => {
4
+ useEffect(() => {
5
+ const handleMessage = (event: MessageEvent) => {
6
+ if (event.origin !== window.location.origin) {
7
+ return;
8
+ }
9
+
10
+ if (event.data && typeof event.data === 'string') {
11
+ const messageData = JSON.parse(event.data);
12
+ if (messageData?.url) {
13
+ window.location.href = messageData.url;
14
+ }
15
+ }
16
+ };
17
+
18
+ window.addEventListener('message', handleMessage);
19
+
20
+ return () => {
21
+ window.removeEventListener('message', handleMessage);
22
+ };
23
+ }, []);
24
+ };
@@ -18,7 +18,8 @@ export const usePaymentOptions = () => {
18
18
  pay_on_delivery: 'pz-pay-on-delivery',
19
19
  bkm_express: 'pz-bkm',
20
20
  credit_payment: 'pz-credit-payment',
21
- masterpass: 'pz-masterpass'
21
+ masterpass: 'pz-masterpass',
22
+ gpay: 'pz-gpay'
22
23
  };
23
24
 
24
25
  const isInitialTypeIncluded = (type: string) => initialTypes.has(type);
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.41.0-rc.1",
4
+ "version": "1.42.0-rc.0",
5
5
  "private": false,
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -26,16 +26,16 @@
26
26
  "react-redux": "8.1.3",
27
27
  "react-string-replace": "1.1.1",
28
28
  "redis": "4.6.13",
29
- "semver": "7.5.4",
29
+ "semver": "7.6.2",
30
30
  "set-cookie-parser": "2.6.0"
31
31
  },
32
32
  "devDependencies": {
33
+ "@akinon/eslint-plugin-projectzero": "1.42.0-rc.0",
33
34
  "@types/react-redux": "7.1.30",
34
35
  "@types/set-cookie-parser": "2.4.7",
35
36
  "@typescript-eslint/eslint-plugin": "6.7.4",
36
37
  "@typescript-eslint/parser": "6.7.4",
37
38
  "eslint": "^8.14.0",
38
- "@akinon/eslint-plugin-projectzero": "1.41.0-rc.1",
39
39
  "eslint-config-prettier": "8.5.0"
40
40
  }
41
41
  }
package/plugins.d.ts CHANGED
@@ -12,6 +12,7 @@ declare module '@akinon/pz-masterpass/src/utils' {
12
12
  declare module '@akinon/pz-masterpass/src/redux/reducer' {
13
13
  export const setError: any;
14
14
  export const setOtpModalVisible: any;
15
+ export const setCvcRequired: any;
15
16
  }
16
17
 
17
18
  declare module '@akinon/pz-otp' {
package/plugins.js CHANGED
@@ -9,5 +9,6 @@ module.exports = [
9
9
  'pz-bkm',
10
10
  'pz-credit-payment',
11
11
  'pz-masterpass',
12
- 'pz-b2b'
12
+ 'pz-b2b',
13
+ 'pz-akifast'
13
14
  ];
@@ -16,6 +16,7 @@ import {
16
16
  setPreOrder,
17
17
  setRetailStores,
18
18
  setShippingOptions,
19
+ setDataSourceShippingOptions,
19
20
  setShippingStepCompleted,
20
21
  setCreditPaymentOptions
21
22
  } from '../../redux/reducers/checkout';
@@ -26,6 +27,7 @@ import { getCookie, setCookie } from '../../utils';
26
27
  import settings from 'settings';
27
28
  import { LocaleUrlStrategy } from '../../localization';
28
29
  import { showMobile3dIframe } from '../../utils/mobile-3d-iframe';
30
+ import { showRedirectionIframe } from '../../utils/redirection-iframe';
29
31
 
30
32
  interface CheckoutResult {
31
33
  payload: {
@@ -73,6 +75,7 @@ export const preOrderMiddleware: Middleware = ({
73
75
  deliveryOptions,
74
76
  addressList: addresses,
75
77
  shippingOptions,
78
+ dataSourceShippingOptions,
76
79
  paymentOptions,
77
80
  installmentOptions
78
81
  } = getState().checkout;
@@ -125,6 +128,22 @@ export const preOrderMiddleware: Middleware = ({
125
128
  dispatch(apiEndpoints.setShippingOption.initiate(shippingOptions[0].pk));
126
129
  }
127
130
 
131
+ if (
132
+ dataSourceShippingOptions.length > 0 &&
133
+ !preOrder.data_source_shipping_options
134
+ ) {
135
+ const selectedDataSourceShippingOptionsPks =
136
+ dataSourceShippingOptions.map(
137
+ (opt) => opt.data_source_shipping_options[0].pk
138
+ );
139
+
140
+ dispatch(
141
+ apiEndpoints.setDataSourceShippingOptions.initiate(
142
+ selectedDataSourceShippingOptionsPks
143
+ )
144
+ );
145
+ }
146
+
128
147
  if (!preOrder.payment_option && paymentOptions.length > 0) {
129
148
  dispatch(apiEndpoints.setPaymentOption.initiate(paymentOptions[0].pk));
130
149
  }
@@ -163,6 +182,7 @@ export const contextListMiddleware: Middleware = ({
163
182
  if (result?.payload?.context_list) {
164
183
  result.payload.context_list.forEach((context) => {
165
184
  const redirectUrl = context.page_context.redirect_url;
185
+ const isIframe = context.page_context.is_frame ?? false;
166
186
 
167
187
  if (redirectUrl) {
168
188
  const currentLocale = getCookie('pz-locale');
@@ -181,16 +201,19 @@ export const contextListMiddleware: Middleware = ({
181
201
  }
182
202
 
183
203
  const urlObj = new URL(url, window.location.origin);
184
- urlObj.searchParams.set('t', new Date().getTime().toString());
185
-
186
- if (
187
- (isMobileApp ||
188
- /iPad|iPhone|iPod|Android/i.test(navigator.userAgent)) &&
204
+ const isMobileDevice =
205
+ isMobileApp ||
206
+ /iPad|iPhone|iPod|Android/i.test(navigator.userAgent);
207
+ const isIframePaymentOptionExcluded =
189
208
  !settings.checkout?.iframeExcludedPaymentOptions?.includes(
190
209
  result.payload?.pre_order?.payment_option?.slug
191
- )
192
- ) {
210
+ );
211
+ urlObj.searchParams.set('t', new Date().getTime().toString());
212
+
213
+ if (isMobileDevice && !isIframePaymentOptionExcluded) {
193
214
  showMobile3dIframe(urlObj.toString());
215
+ } else if (isIframe && !isIframePaymentOptionExcluded) {
216
+ showRedirectionIframe(urlObj.toString());
194
217
  } else {
195
218
  window.location.href = urlObj.toString();
196
219
  }
@@ -220,12 +243,20 @@ export const contextListMiddleware: Middleware = ({
220
243
  dispatch(setShippingOptions(context.page_context.shipping_options));
221
244
  }
222
245
 
246
+ if (context.page_context.data_sources) {
247
+ dispatch(
248
+ setDataSourceShippingOptions(context.page_context.data_sources)
249
+ );
250
+ }
251
+
223
252
  if (context.page_context.payment_options) {
224
253
  dispatch(setPaymentOptions(context.page_context.payment_options));
225
254
  }
226
255
 
227
256
  if (context.page_context.credit_payment_options) {
228
- dispatch(setCreditPaymentOptions(context.page_context.credit_payment_options));
257
+ dispatch(
258
+ setCreditPaymentOptions(context.page_context.credit_payment_options)
259
+ );
229
260
  }
230
261
 
231
262
  if (context.page_context.payment_choices) {
@@ -14,7 +14,8 @@ import {
14
14
  CheckoutCreditPaymentOption,
15
15
  PreOrder,
16
16
  RetailStore,
17
- ShippingOption
17
+ ShippingOption,
18
+ DataSource
18
19
  } from '../../types';
19
20
 
20
21
  export interface CheckoutState {
@@ -36,6 +37,7 @@ export interface CheckoutState {
36
37
  addressList: Address[];
37
38
  deliveryOptions: DeliveryOption[];
38
39
  shippingOptions: ShippingOption[];
40
+ dataSourceShippingOptions: DataSource[];
39
41
  paymentOptions: PaymentOption[];
40
42
  creditPaymentOptions: CheckoutCreditPaymentOption[];
41
43
  selectedCreditPaymentPk: number;
@@ -67,6 +69,7 @@ const initialState: CheckoutState = {
67
69
  addressList: [],
68
70
  deliveryOptions: [],
69
71
  shippingOptions: [],
72
+ dataSourceShippingOptions: [],
70
73
  paymentOptions: [],
71
74
  creditPaymentOptions: [],
72
75
  selectedCreditPaymentPk: null,
@@ -121,6 +124,9 @@ const checkoutSlice = createSlice({
121
124
  setShippingOptions(state, { payload }) {
122
125
  state.shippingOptions = payload;
123
126
  },
127
+ setDataSourceShippingOptions(state, { payload }) {
128
+ state.dataSourceShippingOptions = payload;
129
+ },
124
130
  setPaymentOptions(state, { payload }) {
125
131
  state.paymentOptions = payload;
126
132
  },
@@ -168,6 +174,7 @@ export const {
168
174
  setAddressList,
169
175
  setDeliveryOptions,
170
176
  setShippingOptions,
177
+ setDataSourceShippingOptions,
171
178
  setPaymentOptions,
172
179
  setCreditPaymentOptions,
173
180
  setSelectedCreditPaymentPk,
@@ -33,6 +33,23 @@ export interface ShippingOption {
33
33
  slug: string;
34
34
  }
35
35
 
36
+ export interface DataSource {
37
+ pk: number;
38
+ name: string;
39
+ data_source_shipping_options: DataSourceShippingOption[];
40
+ }
41
+
42
+ export interface DataSourceShippingOption {
43
+ pk: number;
44
+ shipping_amount: string;
45
+ shipping_option_name: string;
46
+ shipping_option_logo: string | null;
47
+ data_source: {
48
+ pk: number;
49
+ title: string | null;
50
+ };
51
+ }
52
+
36
53
  export interface CheckoutAddressType {
37
54
  label: string;
38
55
  text: string;
@@ -78,6 +95,7 @@ export interface PreOrder {
78
95
  retail_store?: RetailStore;
79
96
  gift_box?: GiftBox;
80
97
  credit_payment_option?: CheckoutCreditPaymentOption;
98
+ data_source_shipping_options: any;
81
99
  }
82
100
 
83
101
  export interface GuestLoginFormParams {
@@ -52,6 +52,10 @@ export interface OrderItem {
52
52
  quantity: string;
53
53
  unit_price: string;
54
54
  total_amount: string;
55
+ attributes: {
56
+ gift_note: string;
57
+ [key: string]: any;
58
+ };
55
59
  }
56
60
 
57
61
  export interface Order {
@@ -105,6 +109,9 @@ export interface Order {
105
109
  shipping_option: number;
106
110
  tracking_number: string;
107
111
  tracking_url: string;
112
+ bank: {
113
+ logo: string;
114
+ };
108
115
  }
109
116
 
110
117
  export interface Quotations {
package/types/index.ts CHANGED
@@ -8,6 +8,7 @@ declare global {
8
8
  // we did it like this because declare types needs to be identical, if not it will fail
9
9
  // eslint-disable-next-line @typescript-eslint/ban-types
10
10
  dataLayer?: Object[];
11
+
11
12
  [key: string]: any;
12
13
  }
13
14
  }
@@ -67,6 +68,13 @@ export interface Currency {
67
68
  * @see https://en.wikipedia.org/wiki/ISO_4217
68
69
  */
69
70
  code: string;
71
+ /**
72
+ * Number of decimal places to display.
73
+ *
74
+ * @example
75
+ * decimalScale: 3
76
+ */
77
+ decimalScale?: number;
70
78
  }
71
79
 
72
80
  export interface Settings {
@@ -183,6 +191,7 @@ export interface Settings {
183
191
  masterpassJsUrl?: string;
184
192
  };
185
193
  customNotFoundEnabled: boolean;
194
+ plugins?: Record<string, Record<string, any>>;
186
195
  }
187
196
 
188
197
  export interface CacheOptions {
@@ -278,3 +287,7 @@ export interface AccordionProps {
278
287
  titleClassName?: string;
279
288
  dataTestId?: string;
280
289
  }
290
+
291
+ export interface PluginModuleComponentProps {
292
+ settings?: Record<string, any>;
293
+ }
@@ -0,0 +1,85 @@
1
+ const iframeURLChange = (iframe, callback) => {
2
+ iframe.addEventListener('load', () => {
3
+ setTimeout(() => {
4
+ if (iframe?.contentWindow?.location) {
5
+ callback(iframe.contentWindow.location);
6
+ }
7
+ }, 0);
8
+ });
9
+ };
10
+
11
+ const removeIframe = async () => {
12
+ const iframeSelector = document.querySelector(
13
+ '.checkout-payment-redirection-iframe-wrapper'
14
+ );
15
+
16
+ const redirectionPaymentWrapper = document.querySelector(
17
+ '.checkout-redirection-payment-wrapper'
18
+ );
19
+
20
+ const form = redirectionPaymentWrapper.querySelector('form') as HTMLElement;
21
+
22
+ if (!iframeSelector) {
23
+ return;
24
+ }
25
+
26
+ iframeSelector.remove();
27
+
28
+ if (form) {
29
+ form.style.display = 'block';
30
+ }
31
+
32
+ location.reload();
33
+ };
34
+
35
+ export const showRedirectionIframe = (redirectUrl: string) => {
36
+ const iframeWrapper = document.createElement('div');
37
+ const iframe = document.createElement('iframe');
38
+ const closeButton = document.createElement('div');
39
+ const redirectionPaymentWrapper = document.querySelector(
40
+ '.checkout-redirection-payment-wrapper'
41
+ );
42
+ const form = redirectionPaymentWrapper.querySelector('form') as HTMLElement;
43
+
44
+ iframeWrapper.className = 'checkout-payment-redirection-iframe-wrapper';
45
+ closeButton.className = 'close-button';
46
+
47
+ iframe.setAttribute('src', redirectUrl);
48
+ closeButton.innerHTML = '&#x2715';
49
+ closeButton.addEventListener('click', removeIframe);
50
+
51
+ iframeWrapper.append(iframe, closeButton);
52
+
53
+ if (form) {
54
+ form.style.display = 'none';
55
+ }
56
+
57
+ redirectionPaymentWrapper.appendChild(iframeWrapper);
58
+
59
+ iframeURLChange(iframe, (location) => {
60
+ if (location.origin !== window.location.origin) {
61
+ return false;
62
+ }
63
+
64
+ const searchParams = new URLSearchParams(location.search);
65
+ const isOrderCompleted = location.href.includes('/orders/completed');
66
+
67
+ if (isOrderCompleted) {
68
+ (window.parent as any)?.postMessage?.(
69
+ JSON.stringify({
70
+ url: location.pathname
71
+ })
72
+ );
73
+ }
74
+
75
+ if (
76
+ searchParams.has('success') ||
77
+ isOrderCompleted ||
78
+ location.href.includes('/orders/checkout')
79
+ ) {
80
+ setTimeout(() => {
81
+ removeIframe();
82
+ }, 0);
83
+ }
84
+ });
85
+ };