@lookiero/checkout 10.1.0 → 11.1.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/cypress/integration/checkout.spec.ts +0 -3
- package/dist/fake-dependencies/@lookiero/payments-front/index.d.ts +8 -7
- package/dist/fake-dependencies/@lookiero/payments-front/index.js +11 -3
- package/dist/index.d.ts +3 -3
- package/dist/src/Expo.js +8 -2
- package/dist/src/ExpoRoot.d.ts +5 -1
- package/dist/src/ExpoRoot.js +26 -17
- package/dist/src/infrastructure/domain/checkoutBooking/react/useBlockCheckoutBooking.d.ts +1 -1
- package/dist/src/infrastructure/domain/checkoutBooking/react/useBlockCheckoutBooking.js +2 -0
- package/dist/src/infrastructure/projection/checkout/checkout.mock.d.ts +1 -0
- package/dist/src/infrastructure/projection/checkout/checkout.mock.js +3 -3
- package/dist/src/infrastructure/projection/pricing/react/useViewPricingByCheckoutId.d.ts +1 -1
- package/dist/src/infrastructure/projection/pricing/react/useViewPricingByCheckoutId.js +2 -1
- package/dist/src/infrastructure/tracking/tracking.d.ts +2 -2
- package/dist/src/infrastructure/tracking/useTrackCheckout.d.ts +10 -17
- package/dist/src/infrastructure/tracking/useTrackCheckout.js +27 -12
- package/dist/src/infrastructure/ui/Root.d.ts +6 -6
- package/dist/src/infrastructure/ui/Root.js +2 -3
- package/dist/src/infrastructure/ui/hooks/useCheckoutFlow.d.ts +26 -0
- package/dist/src/infrastructure/ui/hooks/useCheckoutFlow.js +127 -0
- package/dist/src/infrastructure/ui/hooks/usePaymentInstrumentEvents.d.ts +3 -2
- package/dist/src/infrastructure/ui/hooks/usePaymentInstrumentEvents.js +17 -26
- package/dist/src/infrastructure/ui/i18n/i18n.d.ts +1 -0
- package/dist/src/infrastructure/ui/i18n/i18n.js +1 -0
- package/dist/src/infrastructure/ui/routing/CheckoutMiddleware.js +1 -12
- package/dist/src/infrastructure/ui/routing/Routing.d.ts +5 -5
- package/dist/src/infrastructure/ui/routing/Routing.js +2 -10
- package/dist/src/infrastructure/ui/routing/routes.d.ts +0 -1
- package/dist/src/infrastructure/ui/routing/routes.js +0 -1
- package/dist/src/infrastructure/ui/views/App.js +5 -6
- package/dist/src/infrastructure/ui/views/checkout/Checkout.d.ts +7 -2
- package/dist/src/infrastructure/ui/views/checkout/Checkout.js +20 -9
- package/dist/src/infrastructure/ui/views/checkout/Checkout.style.d.ts +3 -0
- package/dist/src/infrastructure/ui/views/checkout/Checkout.style.js +3 -0
- package/dist/src/infrastructure/ui/views/checkout/components/paymentInstrument/PaymentInstrument.js +7 -7
- package/dist/src/projection/customer/customer.d.ts +2 -0
- package/dist/src/projection/order/order.d.ts +1 -1
- package/dist/src/projection/subscription/subscription.d.ts +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/fake-dependencies/@lookiero/payments-front/index.tsx +36 -8
- package/index.ts +10 -3
- package/package.json +3 -4
- package/src/Expo.tsx +10 -2
- package/src/ExpoRoot.tsx +58 -43
- package/src/infrastructure/domain/checkoutBooking/react/useBlockCheckoutBooking.ts +4 -1
- package/src/infrastructure/projection/checkout/checkout.mock.ts +8 -3
- package/src/infrastructure/projection/pricing/react/useViewPricingByCheckoutId.ts +3 -2
- package/src/infrastructure/tracking/tracking.ts +2 -2
- package/src/infrastructure/tracking/useTrackCheckout.test.tsx +51 -24
- package/src/infrastructure/tracking/useTrackCheckout.ts +66 -56
- package/src/infrastructure/ui/Root.tsx +9 -9
- package/src/infrastructure/ui/components/templates/header/itemHeader/ItemHeader.tsx +1 -0
- package/src/infrastructure/ui/hooks/useCheckoutFlow.test.tsx +302 -0
- package/src/infrastructure/ui/hooks/useCheckoutFlow.tsx +203 -0
- package/src/infrastructure/ui/hooks/usePaymentInstrumentEvents.ts +18 -60
- package/src/infrastructure/ui/i18n/i18n.ts +1 -0
- package/src/infrastructure/ui/routing/CheckoutMiddleware.test.tsx +0 -11
- package/src/infrastructure/ui/routing/CheckoutMiddleware.tsx +1 -15
- package/src/infrastructure/ui/routing/Routing.tsx +14 -25
- package/src/infrastructure/ui/routing/routes.ts +0 -1
- package/src/infrastructure/ui/views/App.tsx +5 -13
- package/src/infrastructure/ui/views/checkout/Checkout.style.ts +3 -0
- package/src/infrastructure/ui/views/checkout/Checkout.test.tsx +51 -43
- package/src/infrastructure/ui/views/checkout/Checkout.tsx +51 -13
- package/src/infrastructure/ui/views/checkout/components/paymentInstrument/PaymentInstrument.tsx +8 -8
- package/src/infrastructure/ui/views/item/components/itemActions/__snapshots__/ItemActions.test.tsx.snap +8 -0
- package/src/infrastructure/ui/views/item/components/selectModal/__snapshots__/SelecModal.test.tsx.snap +8 -0
- package/src/infrastructure/ui/views/item/components/sizeWithoutStockModal/__snapshots__/SizeWithoutStockModal.test.tsx.snap +8 -0
- package/src/projection/customer/customer.ts +2 -0
- package/src/projection/order/order.ts +1 -1
- package/src/projection/subscription/subscription.ts +1 -1
- package/dist/src/infrastructure/ui/hooks/useSubmitCheckout.d.ts +0 -27
- package/dist/src/infrastructure/ui/hooks/useSubmitCheckout.js +0 -97
- package/dist/src/infrastructure/ui/views/checkout/components/checkoutPaymentModal/CheckoutPaymentModal.d.ts +0 -12
- package/dist/src/infrastructure/ui/views/checkout/components/checkoutPaymentModal/CheckoutPaymentModal.js +0 -88
- package/src/infrastructure/ui/hooks/useSubmitCheckout.test.ts +0 -297
- package/src/infrastructure/ui/hooks/useSubmitCheckout.ts +0 -169
- package/src/infrastructure/ui/views/checkout/components/checkoutPaymentModal/CheckoutPaymentModal.test.tsx +0 -134
- package/src/infrastructure/ui/views/checkout/components/checkoutPaymentModal/CheckoutPaymentModal.tsx +0 -124
|
@@ -7,13 +7,12 @@ import { Locale } from "@lookiero/sty-psp-locale";
|
|
|
7
7
|
import { Layout } from "@lookiero/sty-psp-ui";
|
|
8
8
|
import { Tradename } from "@lookiero/sty-sp-tradename";
|
|
9
9
|
import { Customer } from "../../../projection/customer/customer";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { OrderProjection } from "../../../projection/order/order";
|
|
11
|
+
import { SubscriptionProjection } from "../../../projection/subscription/subscription";
|
|
12
12
|
import { KameleoonEnvironment } from "../../ab-testing/kameleoonEnvironment";
|
|
13
13
|
import { StaticInfoProvider } from "../hooks/useStaticInfo";
|
|
14
14
|
import { App } from "../views/App";
|
|
15
15
|
import { Checkout } from "../views/checkout/Checkout";
|
|
16
|
-
import { CheckoutPaymentModal } from "../views/checkout/components/checkoutPaymentModal/CheckoutPaymentModal";
|
|
17
16
|
import { Feedback } from "../views/feedback/Feedback";
|
|
18
17
|
import { Item } from "../views/item/Item";
|
|
19
18
|
import { Return } from "../views/return/Return";
|
|
@@ -25,8 +24,8 @@ import { Routes } from "./routes";
|
|
|
25
24
|
interface RoutingProps {
|
|
26
25
|
readonly basePath?: string;
|
|
27
26
|
readonly customer: Customer;
|
|
28
|
-
readonly order:
|
|
29
|
-
readonly subscription:
|
|
27
|
+
readonly order: OrderProjection;
|
|
28
|
+
readonly subscription: SubscriptionProjection;
|
|
30
29
|
readonly locale: Locale;
|
|
31
30
|
readonly I18n: I18n;
|
|
32
31
|
readonly kameleoon: KameleoonEnvironment;
|
|
@@ -34,7 +33,7 @@ interface RoutingProps {
|
|
|
34
33
|
readonly tradename: Tradename;
|
|
35
34
|
readonly getAuthToken: () => Promise<string>;
|
|
36
35
|
readonly onNotAccessible: () => void;
|
|
37
|
-
readonly
|
|
36
|
+
readonly onCheckoutFlowSuccess: () => void;
|
|
38
37
|
readonly onI18nError?: (err: Error) => void;
|
|
39
38
|
readonly useRedirect: () => Record<string, string>;
|
|
40
39
|
readonly useRoutes: typeof reactRouterUseRoutes;
|
|
@@ -53,7 +52,7 @@ const Routing: FC<RoutingProps> = ({
|
|
|
53
52
|
getAuthToken,
|
|
54
53
|
onI18nError,
|
|
55
54
|
onNotAccessible,
|
|
56
|
-
|
|
55
|
+
onCheckoutFlowSuccess,
|
|
57
56
|
useRedirect,
|
|
58
57
|
useRoutes = reactRouterUseRoutes,
|
|
59
58
|
}) => {
|
|
@@ -114,26 +113,16 @@ const Routing: FC<RoutingProps> = ({
|
|
|
114
113
|
path: Routes.CHECKOUT,
|
|
115
114
|
element: (
|
|
116
115
|
<Suspense fallback={<Spinner />}>
|
|
117
|
-
<Checkout
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
<Checkout
|
|
117
|
+
getAuthToken={getAuthToken}
|
|
118
|
+
layout={layout}
|
|
119
|
+
order={order}
|
|
120
|
+
subscription={subscription}
|
|
121
|
+
useRedirect={useRedirect}
|
|
122
|
+
onCheckoutFlowSuccess={onCheckoutFlowSuccess}
|
|
123
|
+
/>
|
|
120
124
|
</Suspense>
|
|
121
125
|
),
|
|
122
|
-
children: [
|
|
123
|
-
{
|
|
124
|
-
path: Routes.CHECKOUT_PAYMENT,
|
|
125
|
-
element: (
|
|
126
|
-
<CheckoutPaymentModal
|
|
127
|
-
coupon={order?.coupon || null}
|
|
128
|
-
getAuthToken={getAuthToken}
|
|
129
|
-
isFirstOrder={order?.isFirstOrder as boolean}
|
|
130
|
-
orderNumber={order?.orderNumber as number}
|
|
131
|
-
subscription={subscription as Subscription}
|
|
132
|
-
onCheckoutSubmitted={onCheckoutSubmitted}
|
|
133
|
-
/>
|
|
134
|
-
),
|
|
135
|
-
},
|
|
136
|
-
],
|
|
137
126
|
},
|
|
138
127
|
{
|
|
139
128
|
path: Routes.FEEDBACK,
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PortalHost } from "@gorhom/portal";
|
|
2
2
|
import React, { FC } from "react";
|
|
3
3
|
import { StatusBar } from "react-native";
|
|
4
4
|
import { SafeAreaProvider } from "react-native-safe-area-context";
|
|
5
|
-
import { PortalProvider as AuroraPortalProvider } from "@lookiero/aurora";
|
|
6
5
|
import { Notifications } from "@lookiero/sty-psp-notifications";
|
|
7
6
|
import { theme } from "@lookiero/sty-psp-ui";
|
|
8
7
|
import { MESSAGING_CONTEXT_ID } from "../../delivery/baseBootstrap";
|
|
@@ -16,17 +15,10 @@ interface AppProps {
|
|
|
16
15
|
|
|
17
16
|
const App: FC<AppProps> = ({ children }) => (
|
|
18
17
|
<SafeAreaProvider>
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
We are using the Aurora's PortalProvider at this level for notifications to work properly.
|
|
24
|
-
|
|
25
|
-
PaymentInstrumentSelect uses Aurora's Portal, and if we rely on UAF's Portal (injected by <Aurora>)
|
|
26
|
-
notifications would be displayed in a layer below Portal's one (not visible).
|
|
27
|
-
*/}
|
|
28
|
-
<AuroraPortalProvider>{children}</AuroraPortalProvider>
|
|
29
|
-
</PortalProvider>
|
|
18
|
+
<StatusBar backgroundColor={colorBgBase} barStyle="dark-content" translucent />
|
|
19
|
+
<Notifications contextId={MESSAGING_CONTEXT_ID} domain={DOMAIN} portalHostName="Checkout" />
|
|
20
|
+
<PortalHost name="Checkout" />
|
|
21
|
+
{children}
|
|
30
22
|
</SafeAreaProvider>
|
|
31
23
|
);
|
|
32
24
|
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { fireEvent } from "@testing-library/react-native";
|
|
2
|
-
import React from "react";
|
|
2
|
+
import React, { ReactNode } from "react";
|
|
3
|
+
import { View } from "react-native";
|
|
3
4
|
import { QueryStatus } from "@lookiero/messaging-react";
|
|
4
5
|
import { Country } from "@lookiero/sty-psp-locale";
|
|
5
6
|
import { Segment } from "@lookiero/sty-psp-segment";
|
|
6
7
|
import { DummyLayout } from "@lookiero/sty-psp-ui";
|
|
7
8
|
import { CheckoutItemStatus } from "../../../../domain/checkoutItem/model/checkoutItem";
|
|
9
|
+
import { OrderProjection } from "../../../../projection/order/order";
|
|
10
|
+
import { SubscriptionProjection } from "../../../../projection/subscription/subscription";
|
|
8
11
|
import { checkout } from "../../../projection/checkout/checkout.mock";
|
|
9
12
|
import { useViewFirstAvailableCheckoutByCustomerId } from "../../../projection/checkout/react/useViewFirstAvailableCheckoutByCustomerId";
|
|
10
13
|
import { pricing } from "../../../projection/pricing/pricing.mock";
|
|
@@ -14,7 +17,9 @@ import { Routes } from "../../routing/routes";
|
|
|
14
17
|
import { render } from "../../test/render";
|
|
15
18
|
import { Checkout } from "./Checkout";
|
|
16
19
|
|
|
20
|
+
const getAuthToken = () => Promise.resolve("token");
|
|
17
21
|
const customerId = "a8fff6d7-708c-41a7-b42a-58c5706d33df";
|
|
22
|
+
const basePath = "/checkout";
|
|
18
23
|
const country = Country.ES;
|
|
19
24
|
const segment = Segment.WOMEN;
|
|
20
25
|
const mockCheckout = checkout({
|
|
@@ -26,49 +31,29 @@ const mockCheckout = checkout({
|
|
|
26
31
|
{ status: CheckoutItemStatus.REPLACED },
|
|
27
32
|
],
|
|
28
33
|
});
|
|
34
|
+
const order: OrderProjection = {
|
|
35
|
+
orderNumber: 12345,
|
|
36
|
+
isFirstOrder: false,
|
|
37
|
+
coupon: null,
|
|
38
|
+
};
|
|
39
|
+
const subscription: SubscriptionProjection = "o";
|
|
29
40
|
const mockUseRedirect = jest.fn(() => ({ returnUrl: "https://web2.dev.aws.lookiero.es/user/" }));
|
|
30
41
|
|
|
31
|
-
jest.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
42
|
+
const mockOnSuccess = jest.fn();
|
|
43
|
+
const mockCheckoutFlow = jest.fn();
|
|
44
|
+
const paymentFlowTestId = "payment-flow";
|
|
45
|
+
const mockPaymentFlowComponent: ReactNode = <View testID={paymentFlowTestId}>PaymentFlow</View>;
|
|
46
|
+
jest.mock("../../hooks/useCheckoutFlow", () => ({
|
|
47
|
+
useCheckoutFlow: () => [mockCheckoutFlow, QueryStatus.SUCCESS, mockPaymentFlowComponent],
|
|
36
48
|
}));
|
|
37
49
|
|
|
50
|
+
jest.mock("../../hooks/useStaticInfo", () => ({
|
|
51
|
+
useStaticInfo: () => ({ customer: { customerId, country, segment }, basePath }),
|
|
52
|
+
}));
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
54
|
+
jest.mock("./components/paymentInstrument/PaymentInstrument", () => ({ PaymentInstrument: () => null }));
|
|
38
55
|
jest.mock("../../../projection/checkout/react/useViewFirstAvailableCheckoutByCustomerId");
|
|
39
56
|
jest.mock("../../../projection/pricing/react/useViewPricingByCheckoutId");
|
|
40
|
-
jest.mock("../../hooks/usePaymentInstrumentEvents");
|
|
41
|
-
|
|
42
|
-
const mockStartLegacyBoxCheckout = jest.fn();
|
|
43
|
-
jest.mock("@lookiero/payments-front", () => {
|
|
44
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
45
|
-
const { useImperativeHandle, forwardRef } = require("react");
|
|
46
|
-
|
|
47
|
-
return {
|
|
48
|
-
CheckoutStatus: {
|
|
49
|
-
REJECTED: "REJECTED",
|
|
50
|
-
ERROR: "ERROR",
|
|
51
|
-
FULFILLED: "FULFILLED",
|
|
52
|
-
},
|
|
53
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
54
|
-
// @ts-ignore
|
|
55
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention, react/display-name
|
|
56
|
-
PaymentFlow: forwardRef((params, ref) => {
|
|
57
|
-
useImperativeHandle(ref, () => ({
|
|
58
|
-
startLegacyBoxCheckout: mockStartLegacyBoxCheckout,
|
|
59
|
-
}));
|
|
60
|
-
|
|
61
|
-
return null;
|
|
62
|
-
}),
|
|
63
|
-
PaymentMethod: {
|
|
64
|
-
["GOOGLE_PAY"]: "google_pay",
|
|
65
|
-
},
|
|
66
|
-
Section: {
|
|
67
|
-
["BOX_CHECKOUT"]: "box-checkout",
|
|
68
|
-
},
|
|
69
|
-
PaymentInstrumentSelect: jest.fn(() => <></>),
|
|
70
|
-
};
|
|
71
|
-
});
|
|
72
57
|
|
|
73
58
|
const mockTrackPressContinue = jest.fn();
|
|
74
59
|
jest.mock("../../../tracking/useTrackPressContinue", () => ({
|
|
@@ -87,13 +72,25 @@ jest.mock("react-router-native", () => ({
|
|
|
87
72
|
useNavigate: () => mockUseNavigate,
|
|
88
73
|
}));
|
|
89
74
|
|
|
75
|
+
beforeEach(() => {
|
|
76
|
+
mockCheckoutFlow.mockClear();
|
|
77
|
+
mockTrackPressContinue.mockClear();
|
|
78
|
+
mockTrackPressBack.mockClear();
|
|
79
|
+
});
|
|
80
|
+
|
|
90
81
|
describe("Checkout view", () => {
|
|
91
82
|
it("renders correctly", async () => {
|
|
92
83
|
(useViewFirstAvailableCheckoutByCustomerId as jest.Mock).mockReturnValue([mockCheckout, QueryStatus.SUCCESS]);
|
|
93
84
|
(useViewPricingByCheckoutId as jest.Mock).mockReturnValue([pricing(), QueryStatus.SUCCESS]);
|
|
94
|
-
|
|
95
85
|
const { findByText, getAllByTestId, getByText, getByTestId } = render(
|
|
96
|
-
<Checkout
|
|
86
|
+
<Checkout
|
|
87
|
+
getAuthToken={getAuthToken}
|
|
88
|
+
layout={DummyLayout}
|
|
89
|
+
order={order}
|
|
90
|
+
subscription={subscription}
|
|
91
|
+
useRedirect={mockUseRedirect}
|
|
92
|
+
onCheckoutFlowSuccess={mockOnSuccess}
|
|
93
|
+
/>,
|
|
97
94
|
);
|
|
98
95
|
|
|
99
96
|
expect(await findByText(I18nMessages.CHECKOUT_TITLE)).toBeTruthy();
|
|
@@ -114,15 +111,17 @@ describe("Checkout view", () => {
|
|
|
114
111
|
expect(getByText(I18nMessages.SUMMARY_FEE)).toBeTruthy();
|
|
115
112
|
expect(getByText("-€10.00")).toBeTruthy();
|
|
116
113
|
|
|
114
|
+
expect(getByTestId(paymentFlowTestId)).toBeTruthy();
|
|
115
|
+
|
|
117
116
|
expect(getByText(I18nMessages.CHECKOUT_PAY_BUTTON)).toBeTruthy();
|
|
118
117
|
fireEvent.press(getByText(I18nMessages.CHECKOUT_PAY_BUTTON));
|
|
119
118
|
expect(mockTrackPressContinue).toHaveBeenCalled();
|
|
120
|
-
expect(
|
|
119
|
+
expect(mockCheckoutFlow).toHaveBeenCalled();
|
|
121
120
|
|
|
122
121
|
expect(getByTestId("checkout-header")).toBeTruthy();
|
|
123
122
|
fireEvent.press(getByTestId("arrow-left-button-icon"));
|
|
124
123
|
expect(mockTrackPressBack).toHaveBeenCalled();
|
|
125
|
-
expect(mockUseNavigate).toHaveBeenCalledWith(
|
|
124
|
+
expect(mockUseNavigate).toHaveBeenCalledWith(`${basePath}/${Routes.SUMMARY}`);
|
|
126
125
|
});
|
|
127
126
|
|
|
128
127
|
it("does not render a delivery banner", async () => {
|
|
@@ -138,7 +137,16 @@ describe("Checkout view", () => {
|
|
|
138
137
|
(useViewFirstAvailableCheckoutByCustomerId as jest.Mock).mockReturnValue([mockCheckout, QueryStatus.SUCCESS]);
|
|
139
138
|
(useViewPricingByCheckoutId as jest.Mock).mockReturnValue([pricing(), QueryStatus.SUCCESS]);
|
|
140
139
|
|
|
141
|
-
const { findByText, queryByText } = render(
|
|
140
|
+
const { findByText, queryByText } = render(
|
|
141
|
+
<Checkout
|
|
142
|
+
getAuthToken={getAuthToken}
|
|
143
|
+
layout={DummyLayout}
|
|
144
|
+
order={order}
|
|
145
|
+
subscription={subscription}
|
|
146
|
+
useRedirect={mockUseRedirect}
|
|
147
|
+
onCheckoutFlowSuccess={mockOnSuccess}
|
|
148
|
+
/>,
|
|
149
|
+
);
|
|
142
150
|
|
|
143
151
|
expect(await findByText(I18nMessages.CHECKOUT_TITLE)).toBeTruthy();
|
|
144
152
|
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import React, { FC,
|
|
1
|
+
import React, { FC, useCallback, useMemo, useState } from "react";
|
|
2
2
|
import { LayoutRectangle, Platform, ScrollView, View } from "react-native";
|
|
3
3
|
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";
|
|
7
8
|
import { Layout as UiLayout, Sticky } from "@lookiero/sty-psp-ui";
|
|
8
9
|
import { CheckoutItemStatus } from "../../../../domain/checkoutItem/model/checkoutItem";
|
|
10
|
+
import { OrderProjection } from "../../../../projection/order/order";
|
|
11
|
+
import { SubscriptionProjection } from "../../../../projection/subscription/subscription";
|
|
9
12
|
import { useViewFirstAvailableCheckoutByCustomerId } from "../../../projection/checkout/react/useViewFirstAvailableCheckoutByCustomerId";
|
|
10
13
|
import { useViewPricingByCheckoutId } from "../../../projection/pricing/react/useViewPricingByCheckoutId";
|
|
11
14
|
import { TrackingPage } from "../../../tracking/tracking";
|
|
@@ -14,6 +17,7 @@ import { useTrackPressBack } from "../../../tracking/useTrackPressBack";
|
|
|
14
17
|
import { useTrackPressContinue } from "../../../tracking/useTrackPressContinue";
|
|
15
18
|
import { Body } from "../../components/layouts/body/Body";
|
|
16
19
|
import { CheckoutHeader } from "../../components/templates/header/checkoutHeader/CheckoutHeader";
|
|
20
|
+
import { useCheckoutFlow } from "../../hooks/useCheckoutFlow";
|
|
17
21
|
import { useStaticInfo } from "../../hooks/useStaticInfo";
|
|
18
22
|
import { DOMAIN, I18nMessages } from "../../i18n/i18n";
|
|
19
23
|
import { Routes } from "../../routing/routes";
|
|
@@ -24,12 +28,22 @@ import { DeliveryBanner } from "./components/deliveryBanner/DeliveryBanner";
|
|
|
24
28
|
import { PaymentInstrument } from "./components/paymentInstrument/PaymentInstrument";
|
|
25
29
|
|
|
26
30
|
interface CheckoutProps {
|
|
27
|
-
readonly children?: ReactNode;
|
|
28
31
|
readonly layout: UiLayout;
|
|
32
|
+
readonly order: OrderProjection;
|
|
33
|
+
readonly subscription: SubscriptionProjection;
|
|
34
|
+
readonly getAuthToken: () => Promise<string>;
|
|
35
|
+
readonly onCheckoutFlowSuccess: () => void;
|
|
29
36
|
readonly useRedirect: () => Record<string, string>;
|
|
30
37
|
}
|
|
31
38
|
|
|
32
|
-
const Checkout: FC<CheckoutProps> = ({
|
|
39
|
+
const Checkout: FC<CheckoutProps> = ({
|
|
40
|
+
layout: Layout,
|
|
41
|
+
order,
|
|
42
|
+
subscription,
|
|
43
|
+
getAuthToken,
|
|
44
|
+
useRedirect,
|
|
45
|
+
onCheckoutFlowSuccess,
|
|
46
|
+
}) => {
|
|
33
47
|
const {
|
|
34
48
|
customer: { customerId, country, segment },
|
|
35
49
|
basePath,
|
|
@@ -43,6 +57,14 @@ const Checkout: FC<CheckoutProps> = ({ children, layout: Layout, useRedirect })
|
|
|
43
57
|
const [checkout, checkoutStatus] = useViewFirstAvailableCheckoutByCustomerId({ customerId });
|
|
44
58
|
const [pricing, pricingStatus] = useViewPricingByCheckoutId({ checkoutId: checkout?.id as string });
|
|
45
59
|
|
|
60
|
+
const [checkoutFlow, checkoutFlowStatus, paymentFlowComponent] = useCheckoutFlow({
|
|
61
|
+
checkout,
|
|
62
|
+
order,
|
|
63
|
+
subscription,
|
|
64
|
+
getAuthToken,
|
|
65
|
+
onSuccess: onCheckoutFlowSuccess,
|
|
66
|
+
});
|
|
67
|
+
|
|
46
68
|
useTrackPageView({
|
|
47
69
|
page: TrackingPage.CHECKOUT,
|
|
48
70
|
country,
|
|
@@ -51,17 +73,16 @@ const Checkout: FC<CheckoutProps> = ({ children, layout: Layout, useRedirect })
|
|
|
51
73
|
});
|
|
52
74
|
|
|
53
75
|
const navigate = useNavigate();
|
|
54
|
-
|
|
55
76
|
const trackPressContinue = useTrackPressContinue({
|
|
56
77
|
page: TrackingPage.CHECKOUT,
|
|
57
78
|
country,
|
|
58
79
|
segment,
|
|
59
80
|
checkoutId: checkout?.id,
|
|
60
81
|
});
|
|
61
|
-
const handleOnSubmit = useCallback(() => {
|
|
82
|
+
const handleOnSubmit = useCallback(async () => {
|
|
62
83
|
trackPressContinue();
|
|
63
|
-
|
|
64
|
-
}, [
|
|
84
|
+
await checkoutFlow();
|
|
85
|
+
}, [checkoutFlow, trackPressContinue]);
|
|
65
86
|
|
|
66
87
|
const checkoutItemsKept = useMemo(
|
|
67
88
|
() =>
|
|
@@ -115,6 +136,12 @@ const Checkout: FC<CheckoutProps> = ({ children, layout: Layout, useRedirect })
|
|
|
115
136
|
>
|
|
116
137
|
<Box size={{ L: "2/3" }} style={screen.L && style.desktopListSpacing}>
|
|
117
138
|
<View style={[style.contentWrapper, screen.L && style.desktopContentWrapper]}>
|
|
139
|
+
{country === Country.NL && (
|
|
140
|
+
<View style={style.paymentSelectorNL}>
|
|
141
|
+
<PaymentInstrument useRedirect={useRedirect} />
|
|
142
|
+
</View>
|
|
143
|
+
)}
|
|
144
|
+
|
|
118
145
|
<Text level={3} style={style.title} heading>
|
|
119
146
|
{titleText}
|
|
120
147
|
</Text>
|
|
@@ -138,9 +165,11 @@ const Checkout: FC<CheckoutProps> = ({ children, layout: Layout, useRedirect })
|
|
|
138
165
|
</View>
|
|
139
166
|
))}
|
|
140
167
|
|
|
141
|
-
|
|
142
|
-
<
|
|
143
|
-
|
|
168
|
+
{country !== Country.NL && (
|
|
169
|
+
<View style={style.paymentSelector}>
|
|
170
|
+
<PaymentInstrument useRedirect={useRedirect} />
|
|
171
|
+
</View>
|
|
172
|
+
)}
|
|
144
173
|
</View>
|
|
145
174
|
</Box>
|
|
146
175
|
|
|
@@ -150,7 +179,11 @@ const Checkout: FC<CheckoutProps> = ({ children, layout: Layout, useRedirect })
|
|
|
150
179
|
<Pricing pricing={pricing} totalCheckoutItemsKept={checkoutItemsKept?.length || 0} />
|
|
151
180
|
|
|
152
181
|
{screen.L ? (
|
|
153
|
-
<Button
|
|
182
|
+
<Button
|
|
183
|
+
busy={checkoutFlowStatus === "loading"}
|
|
184
|
+
testID="confirm-checkout-button"
|
|
185
|
+
onPress={handleOnSubmit}
|
|
186
|
+
>
|
|
154
187
|
{submitButtonText}
|
|
155
188
|
</Button>
|
|
156
189
|
) : null}
|
|
@@ -163,14 +196,19 @@ const Checkout: FC<CheckoutProps> = ({ children, layout: Layout, useRedirect })
|
|
|
163
196
|
{pricing && !screen.L ? (
|
|
164
197
|
<Sticky style={style.sticky} onLayout={Platform.OS !== "web" ? handleOnPricingLayout : undefined}>
|
|
165
198
|
<Body>
|
|
166
|
-
<Button
|
|
199
|
+
<Button
|
|
200
|
+
busy={checkoutFlowStatus === "loading"}
|
|
201
|
+
testID="confirm-checkout-button"
|
|
202
|
+
small
|
|
203
|
+
onPress={handleOnSubmit}
|
|
204
|
+
>
|
|
167
205
|
{submitButtonText}
|
|
168
206
|
</Button>
|
|
169
207
|
</Body>
|
|
170
208
|
</Sticky>
|
|
171
209
|
) : null}
|
|
172
210
|
|
|
173
|
-
{
|
|
211
|
+
{paymentFlowComponent}
|
|
174
212
|
</Layout>
|
|
175
213
|
);
|
|
176
214
|
};
|
package/src/infrastructure/ui/views/checkout/components/paymentInstrument/PaymentInstrument.tsx
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import React, { FC
|
|
1
|
+
import React, { FC } from "react";
|
|
2
2
|
import { PaymentInstrumentSelect, Section } from "@lookiero/payments-front";
|
|
3
|
-
import {
|
|
4
|
-
import { usePaymentInstrumentEvents } from "../../../../hooks/usePaymentInstrumentEvents";
|
|
3
|
+
import { useStaticInfo } from "../../../../hooks/useStaticInfo";
|
|
5
4
|
|
|
6
5
|
interface PaymentInstrumentProps {
|
|
7
6
|
readonly useRedirect: () => Record<string, string>;
|
|
8
7
|
}
|
|
9
8
|
const PaymentInstrument: FC<PaymentInstrumentProps> = ({ useRedirect }) => {
|
|
10
|
-
const paymentInstrumentSelectRef = useRef(null);
|
|
11
9
|
const { returnUrl } = useRedirect();
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
usePaymentInstrumentEvents({ logger });
|
|
10
|
+
const { customer } = useStaticInfo();
|
|
15
11
|
|
|
16
12
|
return (
|
|
17
13
|
<PaymentInstrumentSelect
|
|
18
|
-
ref={paymentInstrumentSelectRef}
|
|
19
14
|
beforeRedirect={returnUrl ? () => Promise.resolve(returnUrl) : undefined}
|
|
20
15
|
hasError={false}
|
|
21
16
|
section={Section.BOX_CHECKOUT}
|
|
17
|
+
userInformation={{
|
|
18
|
+
email: customer.email,
|
|
19
|
+
name: customer.name,
|
|
20
|
+
}}
|
|
21
|
+
showSingleUsePaymentMethods
|
|
22
22
|
/>
|
|
23
23
|
);
|
|
24
24
|
};
|
|
@@ -793,6 +793,14 @@ exports[`ItemActions component matches the snapshot 1`] = `
|
|
|
793
793
|
"fontStyle": "normal",
|
|
794
794
|
"fontWeight": "normal",
|
|
795
795
|
"height": 24,
|
|
796
|
+
"letterSpacing": -0.2,
|
|
797
|
+
"lineHeight": 24,
|
|
798
|
+
"minHeight": 24,
|
|
799
|
+
"minWidth": 24,
|
|
800
|
+
"paddingBottom": 0,
|
|
801
|
+
"paddingLeft": 0,
|
|
802
|
+
"paddingRight": 0,
|
|
803
|
+
"paddingTop": 0,
|
|
796
804
|
"width": 24,
|
|
797
805
|
},
|
|
798
806
|
]
|
|
@@ -219,6 +219,14 @@ exports[`SelectField component matches the snapshot 1`] = `
|
|
|
219
219
|
"fontStyle": "normal",
|
|
220
220
|
"fontWeight": "normal",
|
|
221
221
|
"height": 24,
|
|
222
|
+
"letterSpacing": -0.2,
|
|
223
|
+
"lineHeight": 24,
|
|
224
|
+
"minHeight": 24,
|
|
225
|
+
"minWidth": 24,
|
|
226
|
+
"paddingBottom": 0,
|
|
227
|
+
"paddingLeft": 0,
|
|
228
|
+
"paddingRight": 0,
|
|
229
|
+
"paddingTop": 0,
|
|
222
230
|
"width": 24,
|
|
223
231
|
},
|
|
224
232
|
]
|
|
@@ -219,6 +219,14 @@ exports[`SizeWithoutStockModal component matches the snapshot 1`] = `
|
|
|
219
219
|
"fontStyle": "normal",
|
|
220
220
|
"fontWeight": "normal",
|
|
221
221
|
"height": 24,
|
|
222
|
+
"letterSpacing": -0.2,
|
|
223
|
+
"lineHeight": 24,
|
|
224
|
+
"minHeight": 24,
|
|
225
|
+
"minWidth": 24,
|
|
226
|
+
"paddingBottom": 0,
|
|
227
|
+
"paddingLeft": 0,
|
|
228
|
+
"paddingRight": 0,
|
|
229
|
+
"paddingTop": 0,
|
|
222
230
|
"width": 24,
|
|
223
231
|
},
|
|
224
232
|
]
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { RefObject } from "react";
|
|
2
|
-
import { PaymentFlowRef } from "@lookiero/payments-front";
|
|
3
|
-
import { Logger } from "@lookiero/sty-psp-logging";
|
|
4
|
-
import { PaymentFlowPayloadProjection } from "../../../projection/payment/paymentFlowPayload";
|
|
5
|
-
type Status = "idle" | "loading" | "success" | "error";
|
|
6
|
-
interface SubmitCheckoutFunctionArgs {
|
|
7
|
-
readonly paymentFlowPayload: PaymentFlowPayloadProjection;
|
|
8
|
-
readonly sizeChangeEnabled: boolean;
|
|
9
|
-
}
|
|
10
|
-
interface SubmitCheckoutFunction {
|
|
11
|
-
(args: SubmitCheckoutFunctionArgs): Promise<void>;
|
|
12
|
-
}
|
|
13
|
-
type UseSubmitCheckoutResult = [submitCheckout: SubmitCheckoutFunction, status: Status];
|
|
14
|
-
interface UseSubmitCheckoutFunctionArgs {
|
|
15
|
-
readonly checkoutId: string;
|
|
16
|
-
readonly checkoutBookingId: string;
|
|
17
|
-
readonly paymentFlowRef: RefObject<PaymentFlowRef>;
|
|
18
|
-
readonly onError: () => void;
|
|
19
|
-
readonly onSuccess?: () => void;
|
|
20
|
-
readonly logger: Logger;
|
|
21
|
-
}
|
|
22
|
-
interface UseSubmitCheckoutFunction {
|
|
23
|
-
(args: UseSubmitCheckoutFunctionArgs): UseSubmitCheckoutResult;
|
|
24
|
-
}
|
|
25
|
-
declare const useSubmitCheckout: UseSubmitCheckoutFunction;
|
|
26
|
-
export type { Status, SubmitCheckoutFunction };
|
|
27
|
-
export { useSubmitCheckout };
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { useCallback, useMemo, useState } from "react";
|
|
2
|
-
import { CommandStatus } from "@lookiero/messaging-react";
|
|
3
|
-
import { NotificationLevel, useCreateToastNotification } from "@lookiero/sty-psp-notifications";
|
|
4
|
-
import { viewCheckoutBookingById, } from "../../../projection/checkoutBooking/viewCheckoutBookingById";
|
|
5
|
-
import { MESSAGING_CONTEXT_ID } from "../../delivery/baseBootstrap";
|
|
6
|
-
import { useSubmitCheckout as useSubmitCheckoutCommand } from "../../domain/checkout/react/useSubmitCheckout";
|
|
7
|
-
import { useBlockCheckoutBooking } from "../../domain/checkoutBooking/react/useBlockCheckoutBooking";
|
|
8
|
-
import { I18nMessages } from "../i18n/i18n";
|
|
9
|
-
import { useQueryBus } from "./useQueryBus";
|
|
10
|
-
var ChargeStatus;
|
|
11
|
-
(function (ChargeStatus) {
|
|
12
|
-
ChargeStatus["EXECUTED"] = "EXECUTED";
|
|
13
|
-
ChargeStatus["REQUIRES_ACTION"] = "REQUIRES_ACTION";
|
|
14
|
-
ChargeStatus["REQUIRED_ACTION_CANCELLED"] = "REQUIRED_ACTION_CANCELLED";
|
|
15
|
-
ChargeStatus["REJECTED"] = "REJECTED";
|
|
16
|
-
ChargeStatus["CANCELLED"] = "CANCELLED";
|
|
17
|
-
ChargeStatus["TO_CONFIRM"] = "TO_CONFIRM";
|
|
18
|
-
ChargeStatus["ERROR"] = "ERROR";
|
|
19
|
-
ChargeStatus["UNKNOWN"] = "UNKNOWN";
|
|
20
|
-
})(ChargeStatus || (ChargeStatus = {}));
|
|
21
|
-
const useSubmitCheckout = ({ checkoutId, checkoutBookingId, paymentFlowRef, onError, onSuccess, logger, }) => {
|
|
22
|
-
const queryBus = useQueryBus();
|
|
23
|
-
const [submitCheckoutCommand, submitCheckoutCommandStatus] = useSubmitCheckoutCommand({ checkoutId, logger });
|
|
24
|
-
const [blockCheckoutBooking, blockCheckoutBookingStatus] = useBlockCheckoutBooking({ checkoutBookingId, logger });
|
|
25
|
-
const [createNotification] = useCreateToastNotification({ contextId: MESSAGING_CONTEXT_ID, logger });
|
|
26
|
-
const [checkoutBookingExpired, setCheckoutBookingExpired] = useState(false);
|
|
27
|
-
const [startLegacyBoxCheckoutStatus, setStartLegacyBoxCheckoutStatus] = useState("idle");
|
|
28
|
-
const submitCheckout = useCallback(async ({ paymentFlowPayload, sizeChangeEnabled }) => {
|
|
29
|
-
try {
|
|
30
|
-
sizeChangeEnabled && (await blockCheckoutBooking());
|
|
31
|
-
}
|
|
32
|
-
catch (error) {
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
const checkoutBooking = await queryBus(viewCheckoutBookingById({ checkoutBookingId }));
|
|
36
|
-
if (checkoutBooking?.isExpired) {
|
|
37
|
-
setCheckoutBookingExpired(true);
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
paymentFlowRef.current?.startLegacyBoxCheckout(
|
|
41
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
42
|
-
// @ts-ignore
|
|
43
|
-
paymentFlowPayload, async ({ status, toaster, final }) => {
|
|
44
|
-
setStartLegacyBoxCheckoutStatus("loading");
|
|
45
|
-
if (final) {
|
|
46
|
-
if (status === ChargeStatus.EXECUTED) {
|
|
47
|
-
setStartLegacyBoxCheckoutStatus("success");
|
|
48
|
-
await submitCheckoutCommand();
|
|
49
|
-
onSuccess?.();
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
createNotification({
|
|
53
|
-
level: NotificationLevel.ERROR,
|
|
54
|
-
bodyI18nKey: toaster?.id || I18nMessages.CHECKOUT_TOAST_PAYMENT_ERROR,
|
|
55
|
-
});
|
|
56
|
-
setStartLegacyBoxCheckoutStatus("error");
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
}, [
|
|
61
|
-
queryBus,
|
|
62
|
-
checkoutBookingId,
|
|
63
|
-
paymentFlowRef,
|
|
64
|
-
blockCheckoutBooking,
|
|
65
|
-
submitCheckoutCommand,
|
|
66
|
-
onSuccess,
|
|
67
|
-
createNotification,
|
|
68
|
-
]);
|
|
69
|
-
const status = useMemo(() => {
|
|
70
|
-
if (blockCheckoutBookingStatus === CommandStatus.LOADING ||
|
|
71
|
-
startLegacyBoxCheckoutStatus === "loading" ||
|
|
72
|
-
submitCheckoutCommandStatus === CommandStatus.LOADING) {
|
|
73
|
-
return "loading";
|
|
74
|
-
}
|
|
75
|
-
if (blockCheckoutBookingStatus === CommandStatus.SUCCESS &&
|
|
76
|
-
startLegacyBoxCheckoutStatus === "success" &&
|
|
77
|
-
submitCheckoutCommandStatus === CommandStatus.SUCCESS) {
|
|
78
|
-
return "success";
|
|
79
|
-
}
|
|
80
|
-
if (blockCheckoutBookingStatus === CommandStatus.ERROR ||
|
|
81
|
-
startLegacyBoxCheckoutStatus === "error" ||
|
|
82
|
-
submitCheckoutCommandStatus === CommandStatus.ERROR ||
|
|
83
|
-
checkoutBookingExpired) {
|
|
84
|
-
onError();
|
|
85
|
-
return "error";
|
|
86
|
-
}
|
|
87
|
-
return "idle";
|
|
88
|
-
}, [
|
|
89
|
-
blockCheckoutBookingStatus,
|
|
90
|
-
startLegacyBoxCheckoutStatus,
|
|
91
|
-
submitCheckoutCommandStatus,
|
|
92
|
-
checkoutBookingExpired,
|
|
93
|
-
onError,
|
|
94
|
-
]);
|
|
95
|
-
return [submitCheckout, status];
|
|
96
|
-
};
|
|
97
|
-
export { useSubmitCheckout };
|