@lookiero/checkout 9.9.0-beta.1 → 9.9.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 (144) hide show
  1. package/dist/index.js +2 -2
  2. package/dist/src/ExpoRoot.js +5 -7
  3. package/dist/src/infrastructure/ui/Root.d.ts +2 -0
  4. package/dist/src/infrastructure/ui/Root.js +4 -2
  5. package/dist/src/infrastructure/ui/components/organisms/checkoutQuestions/components/textareaCheckoutQuestionItem/TextareaCheckoutQuestionItem.js +1 -1
  6. package/dist/src/infrastructure/ui/components/organisms/checkoutQuestions/components/textareaCheckoutQuestionItem/TextareaCheckoutQuestionItem.style.d.ts +0 -1
  7. package/dist/src/infrastructure/ui/components/organisms/checkoutQuestions/components/textareaCheckoutQuestionItem/TextareaCheckoutQuestionItem.style.js +0 -1
  8. package/dist/src/infrastructure/ui/components/organisms/returnQuestions/components/textareaReturnQuestionItem/TextareaReturnQuestionItem.js +1 -1
  9. package/dist/src/infrastructure/ui/components/organisms/returnQuestions/components/textareaReturnQuestionItem/TextareaReturnQuestionItem.style.d.ts +0 -1
  10. package/dist/src/infrastructure/ui/components/organisms/returnQuestions/components/textareaReturnQuestionItem/TextareaReturnQuestionItem.style.js +0 -1
  11. package/dist/src/infrastructure/ui/hooks/usePaymentInstrumentEvents.d.ts +2 -3
  12. package/dist/src/infrastructure/ui/hooks/usePaymentInstrumentEvents.js +26 -17
  13. package/dist/src/infrastructure/ui/hooks/useQueryBus.d.ts +9 -0
  14. package/dist/src/infrastructure/ui/hooks/useQueryBus.js +10 -0
  15. package/dist/src/infrastructure/ui/hooks/useSubmitCheckout.d.ts +1 -1
  16. package/dist/src/infrastructure/ui/hooks/useSubmitCheckout.js +55 -22
  17. package/dist/src/infrastructure/ui/i18n/i18n.d.ts +0 -1
  18. package/dist/src/infrastructure/ui/i18n/i18n.js +0 -1
  19. package/dist/src/infrastructure/ui/views/checkout/Checkout.js +2 -5
  20. package/dist/src/infrastructure/ui/views/checkout/Checkout.style.d.ts +0 -3
  21. package/dist/src/infrastructure/ui/views/checkout/Checkout.style.js +0 -3
  22. package/dist/src/infrastructure/ui/views/checkout/components/checkoutPaymentModal/CheckoutPaymentModal.js +2 -2
  23. package/dist/src/infrastructure/ui/views/checkout/components/paymentInstrument/PaymentInstrument.js +7 -7
  24. package/dist/src/projection/customer/customer.d.ts +0 -2
  25. package/dist/src/version.d.ts +1 -1
  26. package/dist/src/version.js +1 -1
  27. package/index.ts +2 -2
  28. package/package.json +7 -7
  29. package/src/ExpoRoot.tsx +5 -7
  30. package/src/infrastructure/ui/Root.tsx +30 -17
  31. package/src/infrastructure/ui/components/organisms/checkoutQuestions/components/textareaCheckoutQuestionItem/TextareaCheckoutQuestionItem.style.ts +0 -1
  32. package/src/infrastructure/ui/components/organisms/checkoutQuestions/components/textareaCheckoutQuestionItem/TextareaCheckoutQuestionItem.tsx +1 -0
  33. package/src/infrastructure/ui/components/organisms/returnQuestions/components/textareaReturnQuestionItem/TextareaReturnQuestionItem.style.ts +0 -1
  34. package/src/infrastructure/ui/components/organisms/returnQuestions/components/textareaReturnQuestionItem/TextareaReturnQuestionItem.tsx +1 -0
  35. package/src/infrastructure/ui/hooks/usePaymentInstrumentEvents.ts +60 -18
  36. package/src/infrastructure/ui/hooks/useQueryBus.test.tsx +23 -0
  37. package/src/infrastructure/ui/hooks/useQueryBus.tsx +27 -0
  38. package/src/infrastructure/ui/hooks/useSubmitCheckout.test.ts +58 -0
  39. package/src/infrastructure/ui/hooks/useSubmitCheckout.ts +77 -33
  40. package/src/infrastructure/ui/i18n/i18n.ts +0 -1
  41. package/src/infrastructure/ui/views/checkout/Checkout.style.ts +0 -3
  42. package/src/infrastructure/ui/views/checkout/Checkout.tsx +3 -12
  43. package/src/infrastructure/ui/views/checkout/components/checkoutPaymentModal/CheckoutPaymentModal.tsx +2 -2
  44. package/src/infrastructure/ui/views/checkout/components/paymentInstrument/PaymentInstrument.tsx +8 -8
  45. package/src/projection/customer/customer.ts +0 -2
  46. package/dist/pact.config.d.ts +0 -21
  47. package/dist/pact.config.js +0 -16
  48. package/dist/public/public/assets/adaptive-icon.png +0 -0
  49. package/dist/public/public/assets/favicon.png +0 -0
  50. package/dist/public/public/assets/icon.png +0 -0
  51. package/dist/public/public/assets/splash.png +0 -0
  52. package/dist/public/public/images/not-found.png +0 -0
  53. package/dist/src/infrastructure/projection/bookedProductsVariants/httpBookedProductsVariantsForCheckoutItemView.pact.d.ts +0 -1
  54. package/dist/src/infrastructure/projection/bookedProductsVariants/httpBookedProductsVariantsForCheckoutItemView.pact.js +0 -55
  55. package/dist/src/infrastructure/projection/checkout/httpCheckoutByIdView.pact.d.ts +0 -1
  56. package/dist/src/infrastructure/projection/checkout/httpCheckoutByIdView.pact.js +0 -116
  57. package/dist/src/infrastructure/projection/checkout/httpFirstAvailableCheckoutByCustomerIdView.pact.d.ts +0 -1
  58. package/dist/src/infrastructure/projection/checkout/httpFirstAvailableCheckoutByCustomerIdView.pact.js +0 -56
  59. package/dist/src/infrastructure/projection/checkout/httpFiveItemsDiscountByCustomerIdView.pact.d.ts +0 -1
  60. package/dist/src/infrastructure/projection/checkout/httpFiveItemsDiscountByCustomerIdView.pact.js +0 -51
  61. package/dist/src/infrastructure/projection/checkout/httpIsCheckoutEnabledByCustomerIdView.pact.d.ts +0 -1
  62. package/dist/src/infrastructure/projection/checkout/httpIsCheckoutEnabledByCustomerIdView.pact.js +0 -51
  63. package/dist/src/infrastructure/projection/checkout/httpIsSizeChangeEnabledByCheckoutIdView.pact.d.ts +0 -1
  64. package/dist/src/infrastructure/projection/checkout/httpIsSizeChangeEnabledByCheckoutIdView.pact.js +0 -51
  65. package/dist/src/infrastructure/projection/checkout/react/useViewIsCheckoutAccessibleByCustomerId.d.ts +0 -13
  66. package/dist/src/infrastructure/projection/checkout/react/useViewIsCheckoutAccessibleByCustomerId.js +0 -19
  67. package/dist/src/infrastructure/projection/checkoutBooking/httpCheckoutBookingByIdView.pact.d.ts +0 -1
  68. package/dist/src/infrastructure/projection/checkoutBooking/httpCheckoutBookingByIdView.pact.js +0 -56
  69. package/dist/src/infrastructure/projection/checkoutItem/httpCheckoutItemByIdView.pact.d.ts +0 -1
  70. package/dist/src/infrastructure/projection/checkoutItem/httpCheckoutItemByIdView.pact.js +0 -57
  71. package/dist/src/infrastructure/projection/checkoutQuestion/httpCheckoutQuestionsByCheckoutIdView.pact.d.ts +0 -1
  72. package/dist/src/infrastructure/projection/checkoutQuestion/httpCheckoutQuestionsByCheckoutIdView.pact.js +0 -55
  73. package/dist/src/infrastructure/projection/payment/httpPaymentFlowPayloadByCheckoutIdView.pact.d.ts +0 -1
  74. package/dist/src/infrastructure/projection/payment/httpPaymentFlowPayloadByCheckoutIdView.pact.js +0 -120
  75. package/dist/src/infrastructure/projection/pricing/httpPricingByCheckoutIdView.pact.d.ts +0 -1
  76. package/dist/src/infrastructure/projection/pricing/httpPricingByCheckoutIdView.pact.js +0 -55
  77. package/dist/src/infrastructure/projection/returnQuestion/httpReturnQuestionsByCheckoutItemIdView.pact.d.ts +0 -1
  78. package/dist/src/infrastructure/projection/returnQuestion/httpReturnQuestionsByCheckoutItemIdView.pact.js +0 -55
  79. package/dist/src/infrastructure/ui/components/layouts/layout/Layout.d.ts +0 -22
  80. package/dist/src/infrastructure/ui/components/layouts/layout/Layout.js +0 -1
  81. package/dist/src/infrastructure/ui/components/layouts/layout/components/footer/Footer.d.ts +0 -7
  82. package/dist/src/infrastructure/ui/components/layouts/layout/components/footer/Footer.js +0 -5
  83. package/dist/src/infrastructure/ui/components/layouts/layout/components/header/Header.d.ts +0 -7
  84. package/dist/src/infrastructure/ui/components/layouts/layout/components/header/Header.js +0 -6
  85. package/dist/src/infrastructure/ui/components/layouts/layout/components/header/Header.style.d.ts +0 -4
  86. package/dist/src/infrastructure/ui/components/layouts/layout/components/header/Header.style.js +0 -20
  87. package/dist/src/infrastructure/ui/components/layouts/layout/dummyLayout/DummyLayout.d.ts +0 -4
  88. package/dist/src/infrastructure/ui/components/layouts/layout/dummyLayout/DummyLayout.js +0 -43
  89. package/dist/src/infrastructure/ui/components/layouts/layout/dummyLayout/DummyLayout.style.d.ts +0 -12
  90. package/dist/src/infrastructure/ui/components/layouts/layout/dummyLayout/DummyLayout.style.js +0 -14
  91. package/dist/src/infrastructure/ui/components/templates/header/defaultHeader/DefaultHeader.d.ts +0 -7
  92. package/dist/src/infrastructure/ui/components/templates/header/defaultHeader/DefaultHeader.js +0 -21
  93. package/dist/src/infrastructure/ui/components/templates/header/defaultHeader/DefaultHeader.style.d.ts +0 -8
  94. package/dist/src/infrastructure/ui/components/templates/header/defaultHeader/DefaultHeader.style.js +0 -12
  95. package/dist/src/infrastructure/ui/hooks/useNewFeedbackExperiment.d.ts +0 -12
  96. package/dist/src/infrastructure/ui/hooks/useNewFeedbackExperiment.js +0 -64
  97. package/dist/src/infrastructure/ui/i18n/fetchTranslations.d.ts +0 -10
  98. package/dist/src/infrastructure/ui/i18n/fetchTranslations.js +0 -17
  99. package/dist/src/infrastructure/ui/i18n/translationEndpoint.d.ts +0 -20
  100. package/dist/src/infrastructure/ui/i18n/translationEndpoint.js +0 -27
  101. package/dist/src/infrastructure/ui/routing/CheckoutAccessibilityMiddleware.d.ts +0 -10
  102. package/dist/src/infrastructure/ui/routing/CheckoutAccessibilityMiddleware.js +0 -27
  103. package/dist/src/infrastructure/ui/views/App.style.d.ts +0 -6
  104. package/dist/src/infrastructure/ui/views/App.style.js +0 -8
  105. package/dist/src/infrastructure/ui/views/feedback/components/checkoutQuestionsForm/CheckoutQuestionsForm.style.d.ts +0 -7
  106. package/dist/src/infrastructure/ui/views/feedback/components/checkoutQuestionsForm/CheckoutQuestionsForm.style.js +0 -11
  107. package/dist/src/infrastructure/ui/views/item/components/returnQuestionsForm/ReturnQuestionsForm.d.ts +0 -12
  108. package/dist/src/infrastructure/ui/views/item/components/returnQuestionsForm/ReturnQuestionsForm.js +0 -64
  109. package/dist/src/infrastructure/ui/views/item/components/returnQuestionsForm/ReturnQuestionsForm.style.d.ts +0 -12
  110. package/dist/src/infrastructure/ui/views/item/components/returnQuestionsForm/ReturnQuestionsForm.style.js +0 -16
  111. package/dist/src/infrastructure/ui/views/return/Return.style.d.ts +0 -40
  112. package/dist/src/infrastructure/ui/views/return/Return.style.js +0 -44
  113. package/dist/src/infrastructure/ui/views/summary/components/stickyPricing/StickyPricing.d.ts +0 -14
  114. package/dist/src/infrastructure/ui/views/summary/components/stickyPricing/StickyPricing.js +0 -28
  115. package/dist/src/infrastructure/ui/views/summary/components/stickyPricing/StickyPricing.style.d.ts +0 -7
  116. package/dist/src/infrastructure/ui/views/summary/components/stickyPricing/StickyPricing.style.js +0 -11
  117. package/dist/src/projection/checkout/viewIsCheckoutAccessibleByCustomerId.d.ts +0 -26
  118. package/dist/src/projection/checkout/viewIsCheckoutAccessibleByCustomerId.js +0 -21
  119. package/dist/src/projection/shared/country.d.ts +0 -14
  120. package/dist/src/projection/shared/country.js +0 -15
  121. package/dist/src/projection/shared/customer.d.ts +0 -9
  122. package/dist/src/projection/shared/customer.js +0 -1
  123. package/dist/src/projection/shared/locale.d.ts +0 -12
  124. package/dist/src/projection/shared/locale.js +0 -13
  125. package/dist/src/projection/shared/order.d.ts +0 -6
  126. package/dist/src/projection/shared/order.js +0 -1
  127. package/dist/src/projection/shared/price.d.ts +0 -11
  128. package/dist/src/projection/shared/price.js +0 -1
  129. package/dist/src/projection/shared/size.d.ts +0 -21
  130. package/dist/src/projection/shared/size.js +0 -4
  131. package/dist/src/projection/shared/subscription.d.ts +0 -2
  132. package/dist/src/projection/shared/subscription.js +0 -1
  133. package/dist/src/shared/ui/components/atoms/aspectRatioView/AspectRatioView.d.ts +0 -15
  134. package/dist/src/shared/ui/components/atoms/aspectRatioView/AspectRatioView.js +0 -16
  135. package/dist/src/shared/ui/components/atoms/error/Error.d.ts +0 -11
  136. package/dist/src/shared/ui/components/atoms/error/Error.js +0 -6
  137. package/dist/src/shared/ui/components/atoms/field/Field.d.ts +0 -14
  138. package/dist/src/shared/ui/components/atoms/field/Field.js +0 -29
  139. package/dist/src/shared/ui/components/atoms/field/Field.style.d.ts +0 -16
  140. package/dist/src/shared/ui/components/atoms/field/Field.style.js +0 -19
  141. package/dist/src/shared/ui/components/molecules/inputField/InputField.d.ts +0 -24
  142. package/dist/src/shared/ui/components/molecules/inputField/InputField.js +0 -28
  143. package/dist/src/shared/ui/components/molecules/inputField/InputField.style.d.ts +0 -29
  144. package/dist/src/shared/ui/components/molecules/inputField/InputField.style.js +0 -37
@@ -3,6 +3,7 @@ import React, { ComponentType, useCallback } from "react";
3
3
  import { Platform } from "react-native";
4
4
  import { useRoutes as reactRouterUseRoutes } from "react-router-native";
5
5
  import { I18n } from "@lookiero/i18n-react";
6
+ import { QueryBus } from "@lookiero/messaging";
6
7
  import { MessagingRoot } from "@lookiero/messaging-react/bootstrap";
7
8
  import { Locale } from "@lookiero/sty-psp-locale";
8
9
  import { SentryEnvironment, SentryLoggerFunctionArgs, sentryLogger, sentryLoggerHOC } from "@lookiero/sty-psp-logging";
@@ -11,11 +12,13 @@ import { Customer } from "../../projection/customer/customer";
11
12
  import { Order } from "../../projection/order/order";
12
13
  import { Subscription } from "../../projection/subscription/subscription";
13
14
  import { KameleoonEnvironment } from "../ab-testing/kameleoonEnvironment";
15
+ import { QueryBusProvider } from "./hooks/useQueryBus";
14
16
  import { Routing } from "./routing/Routing";
15
17
 
16
18
  interface RootFunctionArgs {
17
19
  readonly Messaging: MessagingRoot;
18
20
  readonly I18n: I18n;
21
+ readonly queryBus: QueryBus;
19
22
  readonly development?: boolean;
20
23
  readonly sentry: () => SentryEnvironment;
21
24
  readonly getAuthToken: () => Promise<string>;
@@ -39,7 +42,15 @@ interface RootProps {
39
42
  readonly useRoutes?: typeof reactRouterUseRoutes;
40
43
  }
41
44
 
42
- const root: RootFunction = ({ Messaging, I18n, getAuthToken, development, sentry, kameleoon: kameleoonConfig }) => {
45
+ const root: RootFunction = ({
46
+ Messaging,
47
+ I18n,
48
+ queryBus,
49
+ getAuthToken,
50
+ development,
51
+ sentry,
52
+ kameleoon: kameleoonConfig,
53
+ }) => {
43
54
  const logger = sentryLogger(sentry);
44
55
  const kameleoon = kameleoonConfig();
45
56
 
@@ -60,22 +71,24 @@ const root: RootFunction = ({ Messaging, I18n, getAuthToken, development, sentry
60
71
 
61
72
  return (
62
73
  <Messaging includeReactQueryDevTools={Platform.OS === "web"}>
63
- <Routing
64
- I18n={I18n}
65
- basePath={basePath}
66
- customer={customer}
67
- getAuthToken={getAuthToken}
68
- kameleoon={kameleoon}
69
- layout={layout}
70
- locale={locale}
71
- order={order}
72
- subscription={subscription}
73
- useRedirect={useRedirect}
74
- useRoutes={useRoutes}
75
- onCheckoutSubmitted={onCheckoutSubmitted}
76
- onI18nError={development ? undefined : handleOnI18nError}
77
- onNotAccessible={onNotAccessible}
78
- />
74
+ <QueryBusProvider queryBus={queryBus}>
75
+ <Routing
76
+ I18n={I18n}
77
+ basePath={basePath}
78
+ customer={customer}
79
+ getAuthToken={getAuthToken}
80
+ kameleoon={kameleoon}
81
+ layout={layout}
82
+ locale={locale}
83
+ order={order}
84
+ subscription={subscription}
85
+ useRedirect={useRedirect}
86
+ useRoutes={useRoutes}
87
+ onCheckoutSubmitted={onCheckoutSubmitted}
88
+ onI18nError={development ? undefined : handleOnI18nError}
89
+ onNotAccessible={onNotAccessible}
90
+ />
91
+ </QueryBusProvider>
79
92
  </Messaging>
80
93
  );
81
94
  };
@@ -11,7 +11,6 @@ const style = StyleSheet.create({
11
11
  ...Platform.select({
12
12
  android: {
13
13
  paddingTop: space2,
14
- height: "auto",
15
14
  },
16
15
  }),
17
16
  },
@@ -32,6 +32,7 @@ const TextareaCheckoutQuestionItem: CheckoutQuestionItem = ({
32
32
  placeholder={placeholderText}
33
33
  style={style}
34
34
  testID={testID}
35
+ textAlignVertical="top"
35
36
  value={feedback}
36
37
  multiline
37
38
  onBlur={onBlur}
@@ -11,7 +11,6 @@ const style = StyleSheet.create({
11
11
  ...Platform.select({
12
12
  android: {
13
13
  paddingTop: space2,
14
- height: "auto",
15
14
  },
16
15
  }),
17
16
  },
@@ -37,6 +37,7 @@ const TextareaReturnQuestionItem: ReturnQuestionItem<ReturnQuestionType.TEXTAREA
37
37
  placeholder={placeholderText}
38
38
  style={style}
39
39
  testID={testID}
40
+ textAlignVertical="top"
40
41
  value={feedback}
41
42
  multiline
42
43
  onBlur={onBlur}
@@ -1,33 +1,75 @@
1
- import { useEffect } from "react";
2
- import { PaymentPayload, Section, usePaymentStatusManager } from "@lookiero/payments-front";
1
+ import { useCallback, useEffect } from "react";
2
+ import { useEvent } from "@lookiero/event";
3
+ import { Logger } from "@lookiero/sty-psp-logging";
4
+ import { NotificationLevel, useCreateToastNotification } from "@lookiero/sty-psp-notifications";
5
+ import { MESSAGING_CONTEXT_ID } from "../../delivery/baseBootstrap";
6
+ import { I18nMessages } from "../i18n/i18n";
7
+
8
+ const PAYMENT_ERROR = "ERROR";
9
+ const PAYMENT_SUCCESS = "PAYMENT_INSTRUMENT_UPDATED";
10
+
11
+ interface Message {
12
+ readonly id: string;
13
+ }
14
+ interface OnSuccessFunctionArgs {
15
+ readonly message: Message;
16
+ }
17
+ interface OnSuccessFunction {
18
+ (args: OnSuccessFunctionArgs): void;
19
+ }
20
+
21
+ interface Toaster {
22
+ readonly id: string;
23
+ }
24
+ interface Error {
25
+ readonly toaster?: Toaster;
26
+ }
27
+ interface OnErrorFunctionArgs {
28
+ readonly error: Error;
29
+ }
30
+ interface OnErrorFunction {
31
+ (args: OnErrorFunctionArgs): void;
32
+ }
3
33
 
4
34
  interface UsePaymentInstrumentEventsFunctionArgs {
5
- readonly onSuccess: (payload: PaymentPayload) => void;
6
- readonly onError: (payload: PaymentPayload) => void;
35
+ readonly logger: Logger;
7
36
  }
8
37
 
9
38
  interface UsePaymentInstrumentEventsFunction {
10
39
  (args: UsePaymentInstrumentEventsFunctionArgs): void;
11
40
  }
12
41
 
13
- const usePaymentInstrumentEvents: UsePaymentInstrumentEventsFunction = ({ onSuccess, onError }) => {
14
- const refreshStatus = usePaymentStatusManager(Section.BOX_CHECKOUT);
42
+ const usePaymentInstrumentEvents: UsePaymentInstrumentEventsFunction = ({ logger }) => {
43
+ const { subscribe, unsubscribe } = useEvent();
15
44
 
16
- useEffect(() => {
17
- const { isLoading, consumePayload } = refreshStatus;
45
+ const [createNotification] = useCreateToastNotification({ contextId: MESSAGING_CONTEXT_ID, logger });
18
46
 
19
- if (isLoading) {
20
- return;
21
- }
47
+ const onSuccess: OnSuccessFunction = useCallback(
48
+ ({ message }) => createNotification({ bodyI18nKey: message.id, level: NotificationLevel.SUCCESS }),
49
+ [createNotification],
50
+ );
22
51
 
23
- consumePayload((payload) => {
24
- if (payload.success) {
25
- onSuccess(payload);
26
- } else {
27
- onError(payload);
52
+ const onError: OnErrorFunction = useCallback(
53
+ ({ error }) => {
54
+ if (error.toaster) {
55
+ createNotification({
56
+ bodyI18nKey: error.toaster.id || I18nMessages.CHECKOUT_TOAST_PAYMENT_ERROR,
57
+ level: NotificationLevel.ERROR,
58
+ });
28
59
  }
29
- });
30
- }, [onError, onSuccess, refreshStatus]);
60
+ },
61
+ [createNotification],
62
+ );
63
+
64
+ useEffect(() => {
65
+ subscribe({ event: PAYMENT_ERROR }, onError);
66
+ subscribe({ event: PAYMENT_SUCCESS }, onSuccess);
67
+
68
+ return () => {
69
+ unsubscribe({ event: PAYMENT_ERROR }, onError);
70
+ unsubscribe({ event: PAYMENT_SUCCESS }, onSuccess);
71
+ };
72
+ }, [subscribe, unsubscribe, createNotification, onError, onSuccess]);
31
73
  };
32
74
 
33
75
  export { usePaymentInstrumentEvents };
@@ -0,0 +1,23 @@
1
+ import { renderHook, waitFor } from "@testing-library/react-native";
2
+ import { mockFn } from "jest-mock-extended";
3
+ import React, { FC } from "react";
4
+ import { QueryBus } from "@lookiero/messaging";
5
+ import { QueryBusProvider, useQueryBus as sut } from "./useQueryBus";
6
+
7
+ const mockQueryBus = mockFn<QueryBus>();
8
+
9
+ interface WrapperProps {
10
+ readonly children: JSX.Element;
11
+ }
12
+
13
+ const Wrapper: FC<WrapperProps> = ({ children }) => (
14
+ <QueryBusProvider queryBus={mockQueryBus}>{children}</QueryBusProvider>
15
+ );
16
+
17
+ describe("useQueryBus hook", () => {
18
+ it("returns the QueryBusProvider provided queryBus", async () => {
19
+ const { result } = renderHook(() => sut(), { wrapper: Wrapper });
20
+
21
+ await waitFor(() => expect(result.current).toEqual(mockQueryBus));
22
+ });
23
+ });
@@ -0,0 +1,27 @@
1
+ import React, { createContext, FC, ReactNode, useContext } from "react";
2
+ import invariant from "tiny-invariant";
3
+ import { QueryBus } from "@lookiero/messaging";
4
+
5
+ const QueryBusContext = createContext<QueryBus>(null as unknown as QueryBus);
6
+
7
+ interface QueryBusProviderProps {
8
+ readonly children: ReactNode;
9
+ readonly queryBus: QueryBus;
10
+ }
11
+
12
+ const QueryBusProvider: FC<QueryBusProviderProps> = ({ children, queryBus }) => (
13
+ <QueryBusContext.Provider value={queryBus}>{children}</QueryBusContext.Provider>
14
+ );
15
+
16
+ const useQueryBus = () => {
17
+ const queryBus = useContext(QueryBusContext);
18
+
19
+ invariant(
20
+ queryBus,
21
+ "Your are trying to use the useQueryBus hook without wrapping your app with the <QueryBusProvider>.",
22
+ );
23
+
24
+ return queryBus;
25
+ };
26
+
27
+ export { useQueryBus, QueryBusProvider };
@@ -6,9 +6,11 @@ import { PaymentFlowRef } from "@lookiero/payments-front";
6
6
  import { ChargeStatus } from "@lookiero/payments-front/build/infrastructure/CheckoutAPI";
7
7
  import { Logger } from "@lookiero/sty-psp-logging";
8
8
  import { NotificationLevel, useCreateToastNotification } from "@lookiero/sty-psp-notifications";
9
+ import { CheckoutBookingProjection } from "../../../projection/checkoutBooking/checkoutBooking";
9
10
  import { useSubmitCheckout } from "../../domain/checkout/react/useSubmitCheckout";
10
11
  import { useBlockCheckoutBooking } from "../../domain/checkoutBooking/react/useBlockCheckoutBooking";
11
12
  import { paymentFlowPayload as mockPaymentFlowPayload } from "../../projection/payment/paymentFlowPayload.mock";
13
+ import { useQueryBus } from "./useQueryBus";
12
14
  import { useSubmitCheckout as sut } from "./useSubmitCheckout";
13
15
 
14
16
  const checkoutId = "9c450400-0cd7-44a4-b0e3-e0002a9bf8df";
@@ -18,6 +20,7 @@ const errorChargeStatuses = Object.values(ChargeStatus).filter((status) => statu
18
20
  const logger = mock<Logger>();
19
21
 
20
22
  jest.mock("@lookiero/sty-psp-notifications");
23
+ jest.mock("./useQueryBus");
21
24
  jest.mock("../../domain/checkout/react/useSubmitCheckout");
22
25
  jest.mock("../../domain/checkoutBooking/react/useBlockCheckoutBooking");
23
26
 
@@ -38,6 +41,7 @@ describe("useSubmitCheckout custom hook", () => {
38
41
  },
39
42
  };
40
43
 
44
+ (useQueryBus as jest.Mock).mockReturnValue(() => ({ isExpired: false }) as CheckoutBookingProjection);
41
45
  (useBlockCheckoutBooking as jest.Mock).mockReturnValue([mockBlockCheckoutBooking, CommandStatus.SUCCESS]);
42
46
  (useCreateToastNotification as jest.Mock).mockReturnValue([mockCreateToastNotification, CommandStatus.SUCCESS]);
43
47
  (useSubmitCheckout as jest.Mock).mockReturnValue([mockSubmitCheckout, CommandStatus.SUCCESS]);
@@ -86,6 +90,7 @@ describe("useSubmitCheckout custom hook", () => {
86
90
  },
87
91
  };
88
92
 
93
+ (useQueryBus as jest.Mock).mockReturnValue(() => ({ isExpired: false }) as CheckoutBookingProjection);
89
94
  (useBlockCheckoutBooking as jest.Mock).mockReturnValue([mockBlockCheckoutBooking, CommandStatus.SUCCESS]);
90
95
  (useCreateToastNotification as jest.Mock).mockReturnValue([mockCreateToastNotification, CommandStatus.SUCCESS]);
91
96
  (useSubmitCheckout as jest.Mock).mockReturnValue([mockSubmitCheckout, CommandStatus.SUCCESS]);
@@ -126,6 +131,7 @@ describe("useSubmitCheckout custom hook", () => {
126
131
  },
127
132
  };
128
133
 
134
+ (useQueryBus as jest.Mock).mockReturnValue(() => ({ isExpired: false }) as CheckoutBookingProjection);
129
135
  (useBlockCheckoutBooking as jest.Mock).mockReturnValue([mockBlockCheckoutBooking, CommandStatus.ERROR]);
130
136
  (useCreateToastNotification as jest.Mock).mockReturnValue([mockCreateToastNotification, CommandStatus.IDLE]);
131
137
  (useSubmitCheckout as jest.Mock).mockReturnValue([mockSubmitCheckout, CommandStatus.IDLE]);
@@ -166,6 +172,7 @@ describe("useSubmitCheckout custom hook", () => {
166
172
  },
167
173
  };
168
174
 
175
+ (useQueryBus as jest.Mock).mockReturnValue(() => ({ isExpired: false }) as CheckoutBookingProjection);
169
176
  (useBlockCheckoutBooking as jest.Mock).mockReturnValue([mockBlockCheckoutBooking, CommandStatus.SUCCESS]);
170
177
  (useCreateToastNotification as jest.Mock).mockReturnValue([mockCreateToastNotification, CommandStatus.SUCCESS]);
171
178
  (useSubmitCheckout as jest.Mock).mockReturnValue([mockSubmitCheckout, CommandStatus.IDLE]);
@@ -212,6 +219,7 @@ describe("useSubmitCheckout custom hook", () => {
212
219
  },
213
220
  };
214
221
 
222
+ (useQueryBus as jest.Mock).mockReturnValue(() => ({ isExpired: false }) as CheckoutBookingProjection);
215
223
  (useBlockCheckoutBooking as jest.Mock).mockReturnValue([mockBlockCheckoutBooking, CommandStatus.SUCCESS]);
216
224
  (useSubmitCheckout as jest.Mock).mockReturnValue([mockSubmitCheckout, CommandStatus.ERROR]);
217
225
  (useCreateToastNotification as jest.Mock).mockReturnValue([mockCreateToastNotification, CommandStatus.SUCCESS]);
@@ -236,4 +244,54 @@ describe("useSubmitCheckout custom hook", () => {
236
244
  expect(mockOnError).toHaveBeenCalled();
237
245
  });
238
246
  });
247
+
248
+ test("returns submitCheckout error as the status right after calling 'submitCheckout' if CheckoutBooking isExpired", async () => {
249
+ const mockBlockCheckoutBooking = jest.fn();
250
+ const mockSubmitCheckout = jest.fn();
251
+ const mockCreateToastNotification = jest.fn();
252
+ const mockOnError = jest.fn();
253
+ const mockOnSuccess = jest.fn();
254
+ const mockPaymentFlowRef: RefObject<PaymentFlowRef> = {
255
+ current: {
256
+ // eslint-disable-next-line @typescript-eslint/naming-convention
257
+ startLegacyBoxCheckout: jest.fn().mockImplementation((_payload, callback) => {
258
+ callback({ status: ChargeStatus.EXECUTED, final: true });
259
+ }),
260
+ startCheckout: jest.fn(),
261
+ },
262
+ };
263
+
264
+ (useQueryBus as jest.Mock).mockReturnValue(() => ({ isExpired: true }) as CheckoutBookingProjection);
265
+ (useBlockCheckoutBooking as jest.Mock).mockReturnValue([mockBlockCheckoutBooking, CommandStatus.SUCCESS]);
266
+ (useCreateToastNotification as jest.Mock).mockReturnValue([mockCreateToastNotification, CommandStatus.SUCCESS]);
267
+ (useSubmitCheckout as jest.Mock).mockReturnValue([mockSubmitCheckout, CommandStatus.SUCCESS]);
268
+
269
+ const { result } = renderHook(() =>
270
+ sut({
271
+ checkoutId,
272
+ checkoutBookingId,
273
+ paymentFlowRef: mockPaymentFlowRef,
274
+ onError: mockOnError,
275
+ onSuccess: mockOnSuccess,
276
+ logger,
277
+ }),
278
+ );
279
+
280
+ await act(async () => {
281
+ const [submitCheckout] = result.current;
282
+ await submitCheckout({ paymentFlowPayload: mockPaymentFlowPayload, sizeChangeEnabled: true });
283
+ });
284
+
285
+ await waitFor(() => {
286
+ const [, status] = result.current;
287
+
288
+ expect(mockBlockCheckoutBooking).toHaveBeenCalled();
289
+ expect(mockCreateToastNotification).not.toHaveBeenCalled();
290
+ expect(mockSubmitCheckout).not.toHaveBeenCalled();
291
+
292
+ expect(status).toBe("error");
293
+ expect(mockOnSuccess).not.toHaveBeenCalled();
294
+ expect(mockOnError).toHaveBeenCalled();
295
+ });
296
+ });
239
297
  });
@@ -1,14 +1,39 @@
1
1
  import { RefObject, useCallback, useMemo, useState } from "react";
2
2
  import { CommandStatus } from "@lookiero/messaging-react";
3
- import { PaymentFlowRef, PaymentPayload } from "@lookiero/payments-front";
3
+ import { PaymentFlowRef } from "@lookiero/payments-front";
4
4
  import { Logger } from "@lookiero/sty-psp-logging";
5
5
  import { NotificationLevel, useCreateToastNotification } from "@lookiero/sty-psp-notifications";
6
+ import {
7
+ ViewCheckoutBookingById,
8
+ viewCheckoutBookingById,
9
+ ViewCheckoutBookingByIdResult,
10
+ } from "../../../projection/checkoutBooking/viewCheckoutBookingById";
6
11
  import { PaymentFlowPayloadProjection } from "../../../projection/payment/paymentFlowPayload";
7
12
  import { MESSAGING_CONTEXT_ID } from "../../delivery/baseBootstrap";
8
13
  import { useSubmitCheckout as useSubmitCheckoutCommand } from "../../domain/checkout/react/useSubmitCheckout";
9
14
  import { useBlockCheckoutBooking } from "../../domain/checkoutBooking/react/useBlockCheckoutBooking";
10
15
  import { I18nMessages } from "../i18n/i18n";
11
- import { usePaymentInstrumentEvents } from "./usePaymentInstrumentEvents";
16
+ import { useQueryBus } from "./useQueryBus";
17
+
18
+ enum ChargeStatus {
19
+ EXECUTED = "EXECUTED",
20
+ REQUIRES_ACTION = "REQUIRES_ACTION",
21
+ REQUIRED_ACTION_CANCELLED = "REQUIRED_ACTION_CANCELLED",
22
+ REJECTED = "REJECTED",
23
+ CANCELLED = "CANCELLED",
24
+ TO_CONFIRM = "TO_CONFIRM",
25
+ ERROR = "ERROR",
26
+ UNKNOWN = "UNKNOWN",
27
+ }
28
+ interface LegacyBoxCheckout {
29
+ readonly status: ChargeStatus;
30
+ readonly toaster: {
31
+ id: string;
32
+ } | null;
33
+ readonly final: boolean;
34
+ readonly id?: string;
35
+ readonly translation?: string;
36
+ }
12
37
 
13
38
  type Status = "idle" | "loading" | "success" | "error";
14
39
 
@@ -28,7 +53,7 @@ interface UseSubmitCheckoutFunctionArgs {
28
53
  readonly checkoutBookingId: string;
29
54
  readonly paymentFlowRef: RefObject<PaymentFlowRef>;
30
55
  readonly onError: () => void;
31
- readonly onSuccess: () => void;
56
+ readonly onSuccess?: () => void;
32
57
  readonly logger: Logger;
33
58
  }
34
59
 
@@ -44,9 +69,11 @@ const useSubmitCheckout: UseSubmitCheckoutFunction = ({
44
69
  onSuccess,
45
70
  logger,
46
71
  }) => {
72
+ const queryBus = useQueryBus();
47
73
  const [submitCheckoutCommand, submitCheckoutCommandStatus] = useSubmitCheckoutCommand({ checkoutId, logger });
48
74
  const [blockCheckoutBooking, blockCheckoutBookingStatus] = useBlockCheckoutBooking({ checkoutBookingId, logger });
49
75
  const [createNotification] = useCreateToastNotification({ contextId: MESSAGING_CONTEXT_ID, logger });
76
+ const [checkoutBookingExpired, setCheckoutBookingExpired] = useState(false);
50
77
 
51
78
  const [startLegacyBoxCheckoutStatus, setStartLegacyBoxCheckoutStatus] = useState<Status>("idle");
52
79
 
@@ -58,37 +85,48 @@ const useSubmitCheckout: UseSubmitCheckoutFunction = ({
58
85
  return;
59
86
  }
60
87
 
61
- setStartLegacyBoxCheckoutStatus("loading");
62
-
63
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
64
- // @ts-ignore
65
- paymentFlowRef.current?.startLegacyBoxCheckout(paymentFlowPayload);
66
- },
67
- [paymentFlowRef, blockCheckoutBooking],
68
- );
88
+ const checkoutBooking = await queryBus<ViewCheckoutBookingById, ViewCheckoutBookingByIdResult>(
89
+ viewCheckoutBookingById({ checkoutBookingId }),
90
+ );
69
91
 
70
- const onPaymentSuccess = useCallback(async () => {
71
- setStartLegacyBoxCheckoutStatus("success");
72
-
73
- await submitCheckoutCommand();
74
-
75
- createNotification({
76
- bodyI18nKey: I18nMessages.CHECKOUT_TOAST_PAYMENT_SUCCESS,
77
- level: NotificationLevel.SUCCESS,
78
- });
79
- }, [createNotification, submitCheckoutCommand]);
80
- const onPaymentError = useCallback(
81
- (payload: PaymentPayload) => {
82
- setStartLegacyBoxCheckoutStatus("error");
92
+ if (checkoutBooking?.isExpired) {
93
+ setCheckoutBookingExpired(true);
94
+ return;
95
+ }
83
96
 
84
- createNotification({
85
- bodyI18nKey: payload.metadata?.toaster?.id || I18nMessages.CHECKOUT_TOAST_PAYMENT_ERROR,
86
- level: NotificationLevel.ERROR,
87
- });
97
+ paymentFlowRef.current?.startLegacyBoxCheckout(
98
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
99
+ // @ts-ignore
100
+ paymentFlowPayload,
101
+ async ({ status, toaster, final }: LegacyBoxCheckout) => {
102
+ setStartLegacyBoxCheckoutStatus("loading");
103
+
104
+ if (final) {
105
+ if (status === ChargeStatus.EXECUTED) {
106
+ setStartLegacyBoxCheckoutStatus("success");
107
+ await submitCheckoutCommand();
108
+ onSuccess?.();
109
+ } else {
110
+ createNotification({
111
+ level: NotificationLevel.ERROR,
112
+ bodyI18nKey: toaster?.id || I18nMessages.CHECKOUT_TOAST_PAYMENT_ERROR,
113
+ });
114
+ setStartLegacyBoxCheckoutStatus("error");
115
+ }
116
+ }
117
+ },
118
+ );
88
119
  },
89
- [createNotification],
120
+ [
121
+ queryBus,
122
+ checkoutBookingId,
123
+ paymentFlowRef,
124
+ blockCheckoutBooking,
125
+ submitCheckoutCommand,
126
+ onSuccess,
127
+ createNotification,
128
+ ],
90
129
  );
91
- usePaymentInstrumentEvents({ onSuccess: onPaymentSuccess, onError: onPaymentError });
92
130
 
93
131
  const status: Status = useMemo(() => {
94
132
  if (
@@ -103,20 +141,26 @@ const useSubmitCheckout: UseSubmitCheckoutFunction = ({
103
141
  startLegacyBoxCheckoutStatus === "success" &&
104
142
  submitCheckoutCommandStatus === CommandStatus.SUCCESS
105
143
  ) {
106
- onSuccess();
107
144
  return "success";
108
145
  }
109
146
  if (
110
147
  blockCheckoutBookingStatus === CommandStatus.ERROR ||
111
148
  startLegacyBoxCheckoutStatus === "error" ||
112
- submitCheckoutCommandStatus === CommandStatus.ERROR
149
+ submitCheckoutCommandStatus === CommandStatus.ERROR ||
150
+ checkoutBookingExpired
113
151
  ) {
114
152
  onError();
115
153
  return "error";
116
154
  }
117
155
 
118
156
  return "idle";
119
- }, [blockCheckoutBookingStatus, startLegacyBoxCheckoutStatus, submitCheckoutCommandStatus, onSuccess, onError]);
157
+ }, [
158
+ blockCheckoutBookingStatus,
159
+ startLegacyBoxCheckoutStatus,
160
+ submitCheckoutCommandStatus,
161
+ checkoutBookingExpired,
162
+ onError,
163
+ ]);
120
164
 
121
165
  return [submitCheckout, status];
122
166
  };
@@ -48,7 +48,6 @@ enum I18nMessages {
48
48
  CHECKOUT_TITLE = "checkout.title",
49
49
  CHECKOUT_PAY_BUTTON = "checkout.pay_button",
50
50
  CHECKOUT_TOAST_PAYMENT_ERROR = "checkout.toast_payment_error",
51
- CHECKOUT_TOAST_PAYMENT_SUCCESS = "checkout.toast_payment_success",
52
51
  CHECKOUT_SUCCESS_MODAL_TITLE = "checkout.success_modal_title",
53
52
  CHECKOUT_SUCCESS_MODAL_DESCRIPTION = "checkout.success_modal_description",
54
53
  CHECKOUT_SUCCESS_MODAL_BUTTON = "checkout.success_modal_button",
@@ -30,9 +30,6 @@ const style = StyleSheet.create({
30
30
  paymentSelector: {
31
31
  marginTop: space6,
32
32
  },
33
- paymentSelectorNL: {
34
- marginBottom: space6,
35
- },
36
33
  princingWrapper: {
37
34
  padding: space6,
38
35
  },
@@ -4,7 +4,6 @@ import { useNavigate } from "react-router-native";
4
4
  import { Box, Button, Layout as AuroraLayout, Spinner, Text, useDevice } from "@lookiero/aurora";
5
5
  import { useI18nMessage } from "@lookiero/i18n-react";
6
6
  import { QueryStatus } from "@lookiero/messaging-react";
7
- import { Country } from "@lookiero/sty-psp-locale";
8
7
  import { Layout as UiLayout, Sticky } from "@lookiero/sty-psp-ui";
9
8
  import { CheckoutItemStatus } from "../../../../domain/checkoutItem/model/checkoutItem";
10
9
  import { useViewFirstAvailableCheckoutByCustomerId } from "../../../projection/checkout/react/useViewFirstAvailableCheckoutByCustomerId";
@@ -117,12 +116,6 @@ const Checkout: FC<CheckoutProps> = ({ children, layout: Layout, useRedirect })
117
116
  >
118
117
  <Box size={{ L: "2/3" }} style={screen.L && style.desktopListSpacing}>
119
118
  <View style={[style.contentWrapper, screen.L && style.desktopContentWrapper]}>
120
- {country === Country.NL && (
121
- <View style={style.paymentSelectorNL}>
122
- <PaymentInstrument useRedirect={useRedirect} />
123
- </View>
124
- )}
125
-
126
119
  <Text level={3} style={style.title} heading>
127
120
  {titleText}
128
121
  </Text>
@@ -146,11 +139,9 @@ const Checkout: FC<CheckoutProps> = ({ children, layout: Layout, useRedirect })
146
139
  </View>
147
140
  ))}
148
141
 
149
- {country !== Country.NL && (
150
- <View style={style.paymentSelector}>
151
- <PaymentInstrument useRedirect={useRedirect} />
152
- </View>
153
- )}
142
+ <View style={style.paymentSelector}>
143
+ <PaymentInstrument useRedirect={useRedirect} />
144
+ </View>
154
145
  </View>
155
146
  </Box>
156
147
 
@@ -1,7 +1,7 @@
1
1
  import React, { FC, useCallback, useEffect, useRef, useState } from "react";
2
2
  import { useNavigate } from "react-router-native";
3
3
  import { QueryStatus } from "@lookiero/messaging-react";
4
- import { PaymentFlow, PaymentFlowRef, Section } from "@lookiero/payments-front";
4
+ import { PaymentFlow, PaymentFlowRef } from "@lookiero/payments-front";
5
5
  import { useLogger } from "@lookiero/sty-psp-logging";
6
6
  import { CheckoutItemStatus } from "../../../../../../domain/checkoutItem/model/checkoutItem";
7
7
  import { Currency } from "../../../../../../domain/checkoutItem/model/currency";
@@ -119,7 +119,7 @@ const CheckoutPaymentModal: FC<CheckoutPaymentModalProps> = ({
119
119
 
120
120
  if (!dependenciesLoaded) return null;
121
121
 
122
- return <PaymentFlow ref={paymentFlowRef} section={Section.BOX_CHECKOUT} token={authToken} />;
122
+ return <PaymentFlow ref={paymentFlowRef} token={authToken} />;
123
123
  };
124
124
 
125
125
  export { CheckoutPaymentModal };
@@ -1,24 +1,24 @@
1
- import React, { FC } from "react";
1
+ import React, { FC, useRef } from "react";
2
2
  import { PaymentInstrumentSelect, Section } from "@lookiero/payments-front";
3
- import { useStaticInfo } from "../../../../hooks/useStaticInfo";
3
+ import { useLogger } from "@lookiero/sty-psp-logging";
4
+ import { usePaymentInstrumentEvents } from "../../../../hooks/usePaymentInstrumentEvents";
4
5
 
5
6
  interface PaymentInstrumentProps {
6
7
  readonly useRedirect: () => Record<string, string>;
7
8
  }
8
9
  const PaymentInstrument: FC<PaymentInstrumentProps> = ({ useRedirect }) => {
10
+ const paymentInstrumentSelectRef = useRef(null);
9
11
  const { returnUrl } = useRedirect();
10
- const { customer } = useStaticInfo();
12
+ const logger = useLogger();
13
+
14
+ usePaymentInstrumentEvents({ logger });
11
15
 
12
16
  return (
13
17
  <PaymentInstrumentSelect
18
+ ref={paymentInstrumentSelectRef}
14
19
  beforeRedirect={returnUrl ? () => Promise.resolve(returnUrl) : undefined}
15
20
  hasError={false}
16
21
  section={Section.BOX_CHECKOUT}
17
- userInformation={{
18
- email: customer.email,
19
- name: customer.name,
20
- }}
21
- showSingleUsePaymentMethods
22
22
  />
23
23
  );
24
24
  };
@@ -5,8 +5,6 @@ interface Customer {
5
5
  readonly customerId: string;
6
6
  readonly country: Country;
7
7
  readonly segment: Segment;
8
- readonly name: string;
9
- readonly email: string;
10
8
  }
11
9
 
12
10
  export type { Customer };