@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
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { FC } from "react";
|
|
2
|
-
import { Subscription } from "../../../../../../projection/subscription/subscription";
|
|
3
|
-
interface CheckoutPaymentModalProps {
|
|
4
|
-
readonly coupon: string | null;
|
|
5
|
-
readonly orderNumber: number;
|
|
6
|
-
readonly isFirstOrder: boolean;
|
|
7
|
-
readonly subscription: Subscription;
|
|
8
|
-
readonly getAuthToken: () => Promise<string>;
|
|
9
|
-
readonly onCheckoutSubmitted?: () => void;
|
|
10
|
-
}
|
|
11
|
-
declare const CheckoutPaymentModal: FC<CheckoutPaymentModalProps>;
|
|
12
|
-
export { CheckoutPaymentModal };
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
-
import { useNavigate } from "react-router-native";
|
|
3
|
-
import { QueryStatus } from "@lookiero/messaging-react";
|
|
4
|
-
import { PaymentFlow } from "@lookiero/payments-front";
|
|
5
|
-
import { useLogger } from "@lookiero/sty-psp-logging";
|
|
6
|
-
import { CheckoutItemStatus } from "../../../../../../domain/checkoutItem/model/checkoutItem";
|
|
7
|
-
import { useViewFirstAvailableCheckoutByCustomerId } from "../../../../../projection/checkout/react/useViewFirstAvailableCheckoutByCustomerId";
|
|
8
|
-
import { useViewIsSizeChangeEnabledByCheckoutId } from "../../../../../projection/checkout/react/useViewIsSizeChangeEnabledByCheckoutId";
|
|
9
|
-
import { useViewPaymentFlowPayloadByCheckoutId } from "../../../../../projection/payment/react/useViewPaymentFlowPayloadByCheckoutId";
|
|
10
|
-
import { useViewPricingByCheckoutId } from "../../../../../projection/pricing/react/useViewPricingByCheckoutId";
|
|
11
|
-
import { TrackingPage } from "../../../../../tracking/tracking";
|
|
12
|
-
import { useTrackCheckout } from "../../../../../tracking/useTrackCheckout";
|
|
13
|
-
import { useStaticInfo } from "../../../../hooks/useStaticInfo";
|
|
14
|
-
import { useSubmitCheckout } from "../../../../hooks/useSubmitCheckout";
|
|
15
|
-
import { Routes } from "../../../../routing/routes";
|
|
16
|
-
const CheckoutPaymentModal = ({ coupon, isFirstOrder, subscription, orderNumber, getAuthToken, onCheckoutSubmitted, }) => {
|
|
17
|
-
const paymentFlowRef = useRef(null);
|
|
18
|
-
const logger = useLogger();
|
|
19
|
-
const { customer: { customerId, country, segment }, basePath, } = useStaticInfo();
|
|
20
|
-
const [checkout, checkoutStatus] = useViewFirstAvailableCheckoutByCustomerId({ customerId });
|
|
21
|
-
const [paymentFlowPayload] = useViewPaymentFlowPayloadByCheckoutId({
|
|
22
|
-
checkoutId: checkout?.id,
|
|
23
|
-
});
|
|
24
|
-
const [sizeChangeEnabled] = useViewIsSizeChangeEnabledByCheckoutId({ checkoutId: checkout?.id });
|
|
25
|
-
const [pricing] = useViewPricingByCheckoutId({
|
|
26
|
-
checkoutId: checkout?.id,
|
|
27
|
-
queryOptions: { refetchOnMount: true },
|
|
28
|
-
});
|
|
29
|
-
const [authToken, setAuthToken] = useState();
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
const loadAuthToken = async () => setAuthToken(await getAuthToken());
|
|
32
|
-
loadAuthToken();
|
|
33
|
-
}, [getAuthToken]);
|
|
34
|
-
const navigate = useNavigate();
|
|
35
|
-
const trackCheckout = useTrackCheckout({
|
|
36
|
-
page: TrackingPage.CHECKOUT,
|
|
37
|
-
checkoutId: checkout?.id,
|
|
38
|
-
country,
|
|
39
|
-
segment,
|
|
40
|
-
});
|
|
41
|
-
const handleOnSubmitCheckoutError = useCallback(() => navigate(`${basePath}/${Routes.CHECKOUT}`, { replace: true }), [basePath, navigate]);
|
|
42
|
-
const handleOnSubmitCheckoutSuccess = useCallback(() => {
|
|
43
|
-
const checkoutItemsKept = checkout?.items.filter((checkoutItem) => checkoutItem.status === CheckoutItemStatus.KEPT || checkoutItem.status === CheckoutItemStatus.REPLACED);
|
|
44
|
-
const totalReplacedFor = checkoutItemsKept?.filter(({ replacedFor }) => Boolean(replacedFor)).length || 0;
|
|
45
|
-
trackCheckout({
|
|
46
|
-
totalCheckoutItemsKept: checkoutItemsKept?.length || 0,
|
|
47
|
-
pendingToPay: pricing?.pendingToPay.amount / 100,
|
|
48
|
-
coupon,
|
|
49
|
-
currencyCode: pricing?.pendingToPay.currency,
|
|
50
|
-
isFirstOrder,
|
|
51
|
-
orderNumber,
|
|
52
|
-
totalReplacedFor,
|
|
53
|
-
subscription,
|
|
54
|
-
userId: customerId,
|
|
55
|
-
});
|
|
56
|
-
onCheckoutSubmitted?.();
|
|
57
|
-
}, [
|
|
58
|
-
checkout?.items,
|
|
59
|
-
coupon,
|
|
60
|
-
customerId,
|
|
61
|
-
isFirstOrder,
|
|
62
|
-
onCheckoutSubmitted,
|
|
63
|
-
orderNumber,
|
|
64
|
-
pricing?.pendingToPay.amount,
|
|
65
|
-
pricing?.pendingToPay.currency,
|
|
66
|
-
subscription,
|
|
67
|
-
trackCheckout,
|
|
68
|
-
]);
|
|
69
|
-
const [submitCheckout] = useSubmitCheckout({
|
|
70
|
-
checkoutId: checkout?.id,
|
|
71
|
-
checkoutBookingId: checkout?.checkoutBookingId,
|
|
72
|
-
paymentFlowRef,
|
|
73
|
-
onError: handleOnSubmitCheckoutError,
|
|
74
|
-
onSuccess: handleOnSubmitCheckoutSuccess,
|
|
75
|
-
logger,
|
|
76
|
-
});
|
|
77
|
-
useEffect(() => {
|
|
78
|
-
if (paymentFlowPayload && sizeChangeEnabled !== undefined && pricing !== undefined) {
|
|
79
|
-
submitCheckout({ paymentFlowPayload, sizeChangeEnabled });
|
|
80
|
-
}
|
|
81
|
-
}, [paymentFlowPayload, pricing, sizeChangeEnabled, submitCheckout]);
|
|
82
|
-
const dependenciesLoadedStatuses = [QueryStatus.ERROR, QueryStatus.SUCCESS];
|
|
83
|
-
const dependenciesLoaded = (dependenciesLoadedStatuses.includes(checkoutStatus) || checkout) && authToken;
|
|
84
|
-
if (!dependenciesLoaded)
|
|
85
|
-
return null;
|
|
86
|
-
return React.createElement(PaymentFlow, { ref: paymentFlowRef, token: authToken });
|
|
87
|
-
};
|
|
88
|
-
export { CheckoutPaymentModal };
|
|
@@ -1,297 +0,0 @@
|
|
|
1
|
-
import { act, renderHook, waitFor } from "@testing-library/react-native";
|
|
2
|
-
import { mock } from "jest-mock-extended";
|
|
3
|
-
import { RefObject } from "react";
|
|
4
|
-
import { CommandStatus } from "@lookiero/messaging-react";
|
|
5
|
-
import { PaymentFlowRef } from "@lookiero/payments-front";
|
|
6
|
-
import { ChargeStatus } from "@lookiero/payments-front/build/infrastructure/CheckoutAPI";
|
|
7
|
-
import { Logger } from "@lookiero/sty-psp-logging";
|
|
8
|
-
import { NotificationLevel, useCreateToastNotification } from "@lookiero/sty-psp-notifications";
|
|
9
|
-
import { CheckoutBookingProjection } from "../../../projection/checkoutBooking/checkoutBooking";
|
|
10
|
-
import { useSubmitCheckout } from "../../domain/checkout/react/useSubmitCheckout";
|
|
11
|
-
import { useBlockCheckoutBooking } from "../../domain/checkoutBooking/react/useBlockCheckoutBooking";
|
|
12
|
-
import { paymentFlowPayload as mockPaymentFlowPayload } from "../../projection/payment/paymentFlowPayload.mock";
|
|
13
|
-
import { useQueryBus } from "./useQueryBus";
|
|
14
|
-
import { useSubmitCheckout as sut } from "./useSubmitCheckout";
|
|
15
|
-
|
|
16
|
-
const checkoutId = "9c450400-0cd7-44a4-b0e3-e0002a9bf8df";
|
|
17
|
-
const checkoutBookingId = "07c996bb-a0b4-45f9-ae21-6bb9c784d12b";
|
|
18
|
-
const errorChargeStatuses = Object.values(ChargeStatus).filter((status) => status !== ChargeStatus.EXECUTED);
|
|
19
|
-
|
|
20
|
-
const logger = mock<Logger>();
|
|
21
|
-
|
|
22
|
-
jest.mock("@lookiero/sty-psp-notifications");
|
|
23
|
-
jest.mock("./useQueryBus");
|
|
24
|
-
jest.mock("../../domain/checkout/react/useSubmitCheckout");
|
|
25
|
-
jest.mock("../../domain/checkoutBooking/react/useBlockCheckoutBooking");
|
|
26
|
-
|
|
27
|
-
describe("useSubmitCheckout custom hook", () => {
|
|
28
|
-
test("returns success as the status right after calling 'submitCheckout'", async () => {
|
|
29
|
-
const mockBlockCheckoutBooking = jest.fn();
|
|
30
|
-
const mockSubmitCheckout = jest.fn();
|
|
31
|
-
const mockCreateToastNotification = jest.fn();
|
|
32
|
-
const mockOnError = jest.fn();
|
|
33
|
-
const mockOnSuccess = jest.fn();
|
|
34
|
-
const mockPaymentFlowRef: RefObject<PaymentFlowRef> = {
|
|
35
|
-
current: {
|
|
36
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
37
|
-
startLegacyBoxCheckout: jest.fn().mockImplementation((_payload, callback) => {
|
|
38
|
-
callback({ status: ChargeStatus.EXECUTED, final: true });
|
|
39
|
-
}),
|
|
40
|
-
startCheckout: jest.fn(),
|
|
41
|
-
},
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
(useQueryBus as jest.Mock).mockReturnValue(() => ({ isExpired: false }) as CheckoutBookingProjection);
|
|
45
|
-
(useBlockCheckoutBooking as jest.Mock).mockReturnValue([mockBlockCheckoutBooking, CommandStatus.SUCCESS]);
|
|
46
|
-
(useCreateToastNotification as jest.Mock).mockReturnValue([mockCreateToastNotification, CommandStatus.SUCCESS]);
|
|
47
|
-
(useSubmitCheckout as jest.Mock).mockReturnValue([mockSubmitCheckout, CommandStatus.SUCCESS]);
|
|
48
|
-
|
|
49
|
-
const { result } = renderHook(() =>
|
|
50
|
-
sut({
|
|
51
|
-
checkoutId,
|
|
52
|
-
checkoutBookingId,
|
|
53
|
-
paymentFlowRef: mockPaymentFlowRef,
|
|
54
|
-
onError: mockOnError,
|
|
55
|
-
onSuccess: mockOnSuccess,
|
|
56
|
-
logger,
|
|
57
|
-
}),
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
await act(async () => {
|
|
61
|
-
const [submitCheckout] = result.current;
|
|
62
|
-
await submitCheckout({ paymentFlowPayload: mockPaymentFlowPayload, sizeChangeEnabled: true });
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
await waitFor(() => {
|
|
66
|
-
const [, status] = result.current;
|
|
67
|
-
|
|
68
|
-
expect(mockBlockCheckoutBooking).toHaveBeenCalled();
|
|
69
|
-
expect(mockCreateToastNotification).not.toHaveBeenCalled();
|
|
70
|
-
expect(mockSubmitCheckout).toHaveBeenCalled();
|
|
71
|
-
|
|
72
|
-
expect(status).toBe("success");
|
|
73
|
-
expect(mockOnSuccess).toHaveBeenCalled();
|
|
74
|
-
expect(mockOnError).not.toHaveBeenCalled();
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
test("does not call blockCheckoutBooking if sizeChange is not enabled after calling 'submitCheckout'", async () => {
|
|
79
|
-
const mockBlockCheckoutBooking = jest.fn();
|
|
80
|
-
const mockSubmitCheckout = jest.fn();
|
|
81
|
-
const mockCreateToastNotification = jest.fn();
|
|
82
|
-
const mockOnError = jest.fn();
|
|
83
|
-
const mockPaymentFlowRef: RefObject<PaymentFlowRef> = {
|
|
84
|
-
current: {
|
|
85
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
86
|
-
startLegacyBoxCheckout: jest.fn().mockImplementation((_payload, callback) => {
|
|
87
|
-
callback({ status: ChargeStatus.EXECUTED, final: true });
|
|
88
|
-
}),
|
|
89
|
-
startCheckout: jest.fn(),
|
|
90
|
-
},
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
(useQueryBus as jest.Mock).mockReturnValue(() => ({ isExpired: false }) as CheckoutBookingProjection);
|
|
94
|
-
(useBlockCheckoutBooking as jest.Mock).mockReturnValue([mockBlockCheckoutBooking, CommandStatus.SUCCESS]);
|
|
95
|
-
(useCreateToastNotification as jest.Mock).mockReturnValue([mockCreateToastNotification, CommandStatus.SUCCESS]);
|
|
96
|
-
(useSubmitCheckout as jest.Mock).mockReturnValue([mockSubmitCheckout, CommandStatus.SUCCESS]);
|
|
97
|
-
|
|
98
|
-
const { result } = renderHook(() =>
|
|
99
|
-
sut({ checkoutId, checkoutBookingId, paymentFlowRef: mockPaymentFlowRef, onError: mockOnError, logger }),
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
await act(async () => {
|
|
103
|
-
const [submitCheckout] = result.current;
|
|
104
|
-
await submitCheckout({ paymentFlowPayload: mockPaymentFlowPayload, sizeChangeEnabled: false });
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
await waitFor(() => {
|
|
108
|
-
const [, status] = result.current;
|
|
109
|
-
|
|
110
|
-
expect(mockBlockCheckoutBooking).not.toHaveBeenCalled();
|
|
111
|
-
expect(mockCreateToastNotification).not.toHaveBeenCalled();
|
|
112
|
-
expect(mockSubmitCheckout).toHaveBeenCalled();
|
|
113
|
-
|
|
114
|
-
expect(status).toBe("success");
|
|
115
|
-
expect(mockOnError).not.toHaveBeenCalled();
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
test("returns blockCheckoutBooking error as the status right after calling 'submitCheckout'", async () => {
|
|
120
|
-
const mockBlockCheckoutBooking = jest.fn();
|
|
121
|
-
const mockSubmitCheckout = jest.fn();
|
|
122
|
-
const mockCreateToastNotification = jest.fn();
|
|
123
|
-
const mockOnError = jest.fn();
|
|
124
|
-
const mockPaymentFlowRef: RefObject<PaymentFlowRef> = {
|
|
125
|
-
current: {
|
|
126
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
127
|
-
startLegacyBoxCheckout: jest.fn().mockImplementation((_payload, callback) => {
|
|
128
|
-
callback({ status: ChargeStatus.EXECUTED, final: true });
|
|
129
|
-
}),
|
|
130
|
-
startCheckout: jest.fn(),
|
|
131
|
-
},
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
(useQueryBus as jest.Mock).mockReturnValue(() => ({ isExpired: false }) as CheckoutBookingProjection);
|
|
135
|
-
(useBlockCheckoutBooking as jest.Mock).mockReturnValue([mockBlockCheckoutBooking, CommandStatus.ERROR]);
|
|
136
|
-
(useCreateToastNotification as jest.Mock).mockReturnValue([mockCreateToastNotification, CommandStatus.IDLE]);
|
|
137
|
-
(useSubmitCheckout as jest.Mock).mockReturnValue([mockSubmitCheckout, CommandStatus.IDLE]);
|
|
138
|
-
|
|
139
|
-
const { result } = renderHook(() =>
|
|
140
|
-
sut({ checkoutId, checkoutBookingId, paymentFlowRef: mockPaymentFlowRef, onError: mockOnError, logger }),
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
await act(async () => {
|
|
144
|
-
const [submitCheckout] = result.current;
|
|
145
|
-
await submitCheckout({ paymentFlowPayload: mockPaymentFlowPayload, sizeChangeEnabled: true });
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
await waitFor(() => {
|
|
149
|
-
const [, status] = result.current;
|
|
150
|
-
|
|
151
|
-
expect(mockBlockCheckoutBooking).toHaveBeenCalled();
|
|
152
|
-
expect(mockCreateToastNotification).not.toHaveBeenCalled();
|
|
153
|
-
expect(status).toBe("error");
|
|
154
|
-
expect(mockOnError).toHaveBeenCalled();
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
test.each(errorChargeStatuses)(
|
|
159
|
-
"shows an 'error' notification when the checkout-flow fails (status %s)",
|
|
160
|
-
async (chargeStatus) => {
|
|
161
|
-
const mockBlockCheckoutBooking = jest.fn();
|
|
162
|
-
const mockSubmitCheckout = jest.fn();
|
|
163
|
-
const mockCreateToastNotification = jest.fn();
|
|
164
|
-
const mockOnError = jest.fn();
|
|
165
|
-
const mockPaymentFlowRef: RefObject<PaymentFlowRef> = {
|
|
166
|
-
current: {
|
|
167
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
168
|
-
startLegacyBoxCheckout: jest.fn().mockImplementation((_payload, callback) => {
|
|
169
|
-
callback({ status: chargeStatus, toaster: { id: `${chargeStatus}_toaster_id` }, final: true });
|
|
170
|
-
}),
|
|
171
|
-
startCheckout: jest.fn(),
|
|
172
|
-
},
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
(useQueryBus as jest.Mock).mockReturnValue(() => ({ isExpired: false }) as CheckoutBookingProjection);
|
|
176
|
-
(useBlockCheckoutBooking as jest.Mock).mockReturnValue([mockBlockCheckoutBooking, CommandStatus.SUCCESS]);
|
|
177
|
-
(useCreateToastNotification as jest.Mock).mockReturnValue([mockCreateToastNotification, CommandStatus.SUCCESS]);
|
|
178
|
-
(useSubmitCheckout as jest.Mock).mockReturnValue([mockSubmitCheckout, CommandStatus.IDLE]);
|
|
179
|
-
|
|
180
|
-
const { result } = renderHook(() =>
|
|
181
|
-
sut({ checkoutId, checkoutBookingId, paymentFlowRef: mockPaymentFlowRef, onError: mockOnError, logger }),
|
|
182
|
-
);
|
|
183
|
-
|
|
184
|
-
await act(async () => {
|
|
185
|
-
const [submitCheckout] = result.current;
|
|
186
|
-
await submitCheckout({ paymentFlowPayload: mockPaymentFlowPayload, sizeChangeEnabled: true });
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
await waitFor(() => {
|
|
190
|
-
const [, status] = result.current;
|
|
191
|
-
|
|
192
|
-
expect(mockBlockCheckoutBooking).toHaveBeenCalled();
|
|
193
|
-
expect(mockCreateToastNotification).toHaveBeenCalledWith(
|
|
194
|
-
expect.objectContaining({
|
|
195
|
-
level: NotificationLevel.ERROR,
|
|
196
|
-
bodyI18nKey: `${chargeStatus}_toaster_id`,
|
|
197
|
-
}),
|
|
198
|
-
);
|
|
199
|
-
expect(mockSubmitCheckout).not.toHaveBeenCalled();
|
|
200
|
-
|
|
201
|
-
expect(status).toBe("error");
|
|
202
|
-
expect(mockOnError).toHaveBeenCalled();
|
|
203
|
-
});
|
|
204
|
-
},
|
|
205
|
-
);
|
|
206
|
-
|
|
207
|
-
test("returns submitCheckout error as the status right after calling 'submitCheckout'", async () => {
|
|
208
|
-
const mockBlockCheckoutBooking = jest.fn();
|
|
209
|
-
const mockSubmitCheckout = jest.fn();
|
|
210
|
-
const mockCreateToastNotification = jest.fn();
|
|
211
|
-
const mockOnError = jest.fn();
|
|
212
|
-
const mockPaymentFlowRef: RefObject<PaymentFlowRef> = {
|
|
213
|
-
current: {
|
|
214
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
215
|
-
startLegacyBoxCheckout: jest.fn().mockImplementation((_payload, callback) => {
|
|
216
|
-
callback({ status: ChargeStatus.EXECUTED, final: true });
|
|
217
|
-
}),
|
|
218
|
-
startCheckout: jest.fn(),
|
|
219
|
-
},
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
(useQueryBus as jest.Mock).mockReturnValue(() => ({ isExpired: false }) as CheckoutBookingProjection);
|
|
223
|
-
(useBlockCheckoutBooking as jest.Mock).mockReturnValue([mockBlockCheckoutBooking, CommandStatus.SUCCESS]);
|
|
224
|
-
(useSubmitCheckout as jest.Mock).mockReturnValue([mockSubmitCheckout, CommandStatus.ERROR]);
|
|
225
|
-
(useCreateToastNotification as jest.Mock).mockReturnValue([mockCreateToastNotification, CommandStatus.SUCCESS]);
|
|
226
|
-
|
|
227
|
-
const { result } = renderHook(() =>
|
|
228
|
-
sut({ checkoutId, checkoutBookingId, paymentFlowRef: mockPaymentFlowRef, onError: mockOnError, logger }),
|
|
229
|
-
);
|
|
230
|
-
|
|
231
|
-
await act(async () => {
|
|
232
|
-
const [submitCheckout] = result.current;
|
|
233
|
-
submitCheckout({ paymentFlowPayload: mockPaymentFlowPayload, sizeChangeEnabled: true });
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
await waitFor(() => {
|
|
237
|
-
const [, status] = result.current;
|
|
238
|
-
|
|
239
|
-
expect(mockBlockCheckoutBooking).toHaveBeenCalled();
|
|
240
|
-
expect(mockCreateToastNotification).not.toHaveBeenCalled();
|
|
241
|
-
expect(mockSubmitCheckout).toHaveBeenCalled();
|
|
242
|
-
|
|
243
|
-
expect(status).toBe("error");
|
|
244
|
-
expect(mockOnError).toHaveBeenCalled();
|
|
245
|
-
});
|
|
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
|
-
});
|
|
297
|
-
});
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import { RefObject, useCallback, useMemo, useState } from "react";
|
|
2
|
-
import { CommandStatus } from "@lookiero/messaging-react";
|
|
3
|
-
import { PaymentFlowRef } from "@lookiero/payments-front";
|
|
4
|
-
import { Logger } from "@lookiero/sty-psp-logging";
|
|
5
|
-
import { NotificationLevel, useCreateToastNotification } from "@lookiero/sty-psp-notifications";
|
|
6
|
-
import {
|
|
7
|
-
ViewCheckoutBookingById,
|
|
8
|
-
viewCheckoutBookingById,
|
|
9
|
-
ViewCheckoutBookingByIdResult,
|
|
10
|
-
} from "../../../projection/checkoutBooking/viewCheckoutBookingById";
|
|
11
|
-
import { PaymentFlowPayloadProjection } from "../../../projection/payment/paymentFlowPayload";
|
|
12
|
-
import { MESSAGING_CONTEXT_ID } from "../../delivery/baseBootstrap";
|
|
13
|
-
import { useSubmitCheckout as useSubmitCheckoutCommand } from "../../domain/checkout/react/useSubmitCheckout";
|
|
14
|
-
import { useBlockCheckoutBooking } from "../../domain/checkoutBooking/react/useBlockCheckoutBooking";
|
|
15
|
-
import { I18nMessages } from "../i18n/i18n";
|
|
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
|
-
}
|
|
37
|
-
|
|
38
|
-
type Status = "idle" | "loading" | "success" | "error";
|
|
39
|
-
|
|
40
|
-
interface SubmitCheckoutFunctionArgs {
|
|
41
|
-
readonly paymentFlowPayload: PaymentFlowPayloadProjection;
|
|
42
|
-
readonly sizeChangeEnabled: boolean;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
interface SubmitCheckoutFunction {
|
|
46
|
-
(args: SubmitCheckoutFunctionArgs): Promise<void>;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
type UseSubmitCheckoutResult = [submitCheckout: SubmitCheckoutFunction, status: Status];
|
|
50
|
-
|
|
51
|
-
interface UseSubmitCheckoutFunctionArgs {
|
|
52
|
-
readonly checkoutId: string;
|
|
53
|
-
readonly checkoutBookingId: string;
|
|
54
|
-
readonly paymentFlowRef: RefObject<PaymentFlowRef>;
|
|
55
|
-
readonly onError: () => void;
|
|
56
|
-
readonly onSuccess?: () => void;
|
|
57
|
-
readonly logger: Logger;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
interface UseSubmitCheckoutFunction {
|
|
61
|
-
(args: UseSubmitCheckoutFunctionArgs): UseSubmitCheckoutResult;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const useSubmitCheckout: UseSubmitCheckoutFunction = ({
|
|
65
|
-
checkoutId,
|
|
66
|
-
checkoutBookingId,
|
|
67
|
-
paymentFlowRef,
|
|
68
|
-
onError,
|
|
69
|
-
onSuccess,
|
|
70
|
-
logger,
|
|
71
|
-
}) => {
|
|
72
|
-
const queryBus = useQueryBus();
|
|
73
|
-
const [submitCheckoutCommand, submitCheckoutCommandStatus] = useSubmitCheckoutCommand({ checkoutId, logger });
|
|
74
|
-
const [blockCheckoutBooking, blockCheckoutBookingStatus] = useBlockCheckoutBooking({ checkoutBookingId, logger });
|
|
75
|
-
const [createNotification] = useCreateToastNotification({ contextId: MESSAGING_CONTEXT_ID, logger });
|
|
76
|
-
const [checkoutBookingExpired, setCheckoutBookingExpired] = useState(false);
|
|
77
|
-
|
|
78
|
-
const [startLegacyBoxCheckoutStatus, setStartLegacyBoxCheckoutStatus] = useState<Status>("idle");
|
|
79
|
-
|
|
80
|
-
const submitCheckout: SubmitCheckoutFunction = useCallback(
|
|
81
|
-
async ({ paymentFlowPayload, sizeChangeEnabled }) => {
|
|
82
|
-
try {
|
|
83
|
-
sizeChangeEnabled && (await blockCheckoutBooking());
|
|
84
|
-
} catch (error) {
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const checkoutBooking = await queryBus<ViewCheckoutBookingById, ViewCheckoutBookingByIdResult>(
|
|
89
|
-
viewCheckoutBookingById({ checkoutBookingId }),
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
if (checkoutBooking?.isExpired) {
|
|
93
|
-
setCheckoutBookingExpired(true);
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
|
|
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
|
-
);
|
|
119
|
-
},
|
|
120
|
-
[
|
|
121
|
-
queryBus,
|
|
122
|
-
checkoutBookingId,
|
|
123
|
-
paymentFlowRef,
|
|
124
|
-
blockCheckoutBooking,
|
|
125
|
-
submitCheckoutCommand,
|
|
126
|
-
onSuccess,
|
|
127
|
-
createNotification,
|
|
128
|
-
],
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
const status: Status = useMemo(() => {
|
|
132
|
-
if (
|
|
133
|
-
blockCheckoutBookingStatus === CommandStatus.LOADING ||
|
|
134
|
-
startLegacyBoxCheckoutStatus === "loading" ||
|
|
135
|
-
submitCheckoutCommandStatus === CommandStatus.LOADING
|
|
136
|
-
) {
|
|
137
|
-
return "loading";
|
|
138
|
-
}
|
|
139
|
-
if (
|
|
140
|
-
blockCheckoutBookingStatus === CommandStatus.SUCCESS &&
|
|
141
|
-
startLegacyBoxCheckoutStatus === "success" &&
|
|
142
|
-
submitCheckoutCommandStatus === CommandStatus.SUCCESS
|
|
143
|
-
) {
|
|
144
|
-
return "success";
|
|
145
|
-
}
|
|
146
|
-
if (
|
|
147
|
-
blockCheckoutBookingStatus === CommandStatus.ERROR ||
|
|
148
|
-
startLegacyBoxCheckoutStatus === "error" ||
|
|
149
|
-
submitCheckoutCommandStatus === CommandStatus.ERROR ||
|
|
150
|
-
checkoutBookingExpired
|
|
151
|
-
) {
|
|
152
|
-
onError();
|
|
153
|
-
return "error";
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return "idle";
|
|
157
|
-
}, [
|
|
158
|
-
blockCheckoutBookingStatus,
|
|
159
|
-
startLegacyBoxCheckoutStatus,
|
|
160
|
-
submitCheckoutCommandStatus,
|
|
161
|
-
checkoutBookingExpired,
|
|
162
|
-
onError,
|
|
163
|
-
]);
|
|
164
|
-
|
|
165
|
-
return [submitCheckout, status];
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
export type { Status, SubmitCheckoutFunction };
|
|
169
|
-
export { useSubmitCheckout };
|