@lookiero/checkout 10.0.1 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/cypress/integration/checkout.spec.ts +0 -3
  2. package/dist/fake-dependencies/@lookiero/payments-front/index.d.ts +8 -7
  3. package/dist/fake-dependencies/@lookiero/payments-front/index.js +11 -3
  4. package/dist/index.d.ts +3 -3
  5. package/dist/src/ExpoRoot.js +17 -12
  6. package/dist/src/infrastructure/domain/checkoutBooking/react/useBlockCheckoutBooking.d.ts +1 -1
  7. package/dist/src/infrastructure/domain/checkoutBooking/react/useBlockCheckoutBooking.js +2 -0
  8. package/dist/src/infrastructure/projection/checkout/checkout.mock.d.ts +1 -0
  9. package/dist/src/infrastructure/projection/checkout/checkout.mock.js +3 -3
  10. package/dist/src/infrastructure/projection/pricing/react/useViewPricingByCheckoutId.d.ts +1 -1
  11. package/dist/src/infrastructure/projection/pricing/react/useViewPricingByCheckoutId.js +2 -1
  12. package/dist/src/infrastructure/tracking/tracking.d.ts +2 -2
  13. package/dist/src/infrastructure/tracking/useTrackCheckout.d.ts +10 -17
  14. package/dist/src/infrastructure/tracking/useTrackCheckout.js +27 -12
  15. package/dist/src/infrastructure/ui/Root.d.ts +6 -6
  16. package/dist/src/infrastructure/ui/Root.js +2 -3
  17. package/dist/src/infrastructure/ui/hooks/useCheckoutFlow.d.ts +26 -0
  18. package/dist/src/infrastructure/ui/hooks/useCheckoutFlow.js +127 -0
  19. package/dist/src/infrastructure/ui/hooks/usePaymentInstrumentEvents.d.ts +3 -2
  20. package/dist/src/infrastructure/ui/hooks/usePaymentInstrumentEvents.js +17 -26
  21. package/dist/src/infrastructure/ui/i18n/i18n.d.ts +1 -0
  22. package/dist/src/infrastructure/ui/i18n/i18n.js +1 -0
  23. package/dist/src/infrastructure/ui/routing/CheckoutMiddleware.js +1 -12
  24. package/dist/src/infrastructure/ui/routing/Routing.d.ts +5 -5
  25. package/dist/src/infrastructure/ui/routing/Routing.js +2 -10
  26. package/dist/src/infrastructure/ui/routing/routes.d.ts +0 -1
  27. package/dist/src/infrastructure/ui/routing/routes.js +0 -1
  28. package/dist/src/infrastructure/ui/views/App.js +5 -6
  29. package/dist/src/infrastructure/ui/views/checkout/Checkout.d.ts +7 -2
  30. package/dist/src/infrastructure/ui/views/checkout/Checkout.js +20 -9
  31. package/dist/src/infrastructure/ui/views/checkout/Checkout.style.d.ts +3 -0
  32. package/dist/src/infrastructure/ui/views/checkout/Checkout.style.js +3 -0
  33. package/dist/src/infrastructure/ui/views/checkout/components/paymentInstrument/PaymentInstrument.js +7 -7
  34. package/dist/src/projection/customer/customer.d.ts +2 -0
  35. package/dist/src/projection/order/order.d.ts +1 -1
  36. package/dist/src/projection/subscription/subscription.d.ts +1 -1
  37. package/dist/src/version.d.ts +1 -1
  38. package/dist/src/version.js +1 -1
  39. package/fake-dependencies/@lookiero/payments-front/index.tsx +36 -8
  40. package/index.ts +10 -3
  41. package/package.json +4 -4
  42. package/src/ExpoRoot.tsx +43 -36
  43. package/src/infrastructure/domain/checkoutBooking/react/useBlockCheckoutBooking.ts +4 -1
  44. package/src/infrastructure/projection/checkout/checkout.mock.ts +8 -3
  45. package/src/infrastructure/projection/pricing/react/useViewPricingByCheckoutId.ts +3 -2
  46. package/src/infrastructure/tracking/tracking.ts +2 -2
  47. package/src/infrastructure/tracking/useTrackCheckout.test.tsx +51 -24
  48. package/src/infrastructure/tracking/useTrackCheckout.ts +66 -56
  49. package/src/infrastructure/ui/Root.tsx +9 -9
  50. package/src/infrastructure/ui/components/templates/header/itemHeader/ItemHeader.tsx +1 -0
  51. package/src/infrastructure/ui/hooks/useCheckoutFlow.test.tsx +302 -0
  52. package/src/infrastructure/ui/hooks/useCheckoutFlow.tsx +203 -0
  53. package/src/infrastructure/ui/hooks/usePaymentInstrumentEvents.ts +18 -60
  54. package/src/infrastructure/ui/i18n/i18n.ts +1 -0
  55. package/src/infrastructure/ui/routing/CheckoutMiddleware.test.tsx +0 -11
  56. package/src/infrastructure/ui/routing/CheckoutMiddleware.tsx +1 -15
  57. package/src/infrastructure/ui/routing/Routing.tsx +14 -25
  58. package/src/infrastructure/ui/routing/routes.ts +0 -1
  59. package/src/infrastructure/ui/views/App.tsx +5 -13
  60. package/src/infrastructure/ui/views/checkout/Checkout.style.ts +3 -0
  61. package/src/infrastructure/ui/views/checkout/Checkout.test.tsx +51 -43
  62. package/src/infrastructure/ui/views/checkout/Checkout.tsx +51 -13
  63. package/src/infrastructure/ui/views/checkout/components/paymentInstrument/PaymentInstrument.tsx +8 -8
  64. package/src/projection/customer/customer.ts +2 -0
  65. package/src/projection/order/order.ts +1 -1
  66. package/src/projection/subscription/subscription.ts +1 -1
  67. package/dist/src/infrastructure/ui/hooks/useSubmitCheckout.d.ts +0 -27
  68. package/dist/src/infrastructure/ui/hooks/useSubmitCheckout.js +0 -97
  69. package/dist/src/infrastructure/ui/views/checkout/components/checkoutPaymentModal/CheckoutPaymentModal.d.ts +0 -12
  70. package/dist/src/infrastructure/ui/views/checkout/components/checkoutPaymentModal/CheckoutPaymentModal.js +0 -88
  71. package/src/infrastructure/ui/hooks/useSubmitCheckout.test.ts +0 -297
  72. package/src/infrastructure/ui/hooks/useSubmitCheckout.ts +0 -169
  73. package/src/infrastructure/ui/views/checkout/components/checkoutPaymentModal/CheckoutPaymentModal.test.tsx +0 -134
  74. package/src/infrastructure/ui/views/checkout/components/checkoutPaymentModal/CheckoutPaymentModal.tsx +0 -124
@@ -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 };
@@ -1,134 +0,0 @@
1
- import { waitFor } from "@testing-library/react-native";
2
- import React from "react";
3
- import { QueryStatus } from "@lookiero/messaging-react";
4
- import { Country } from "@lookiero/sty-psp-locale";
5
- import { Segment } from "@lookiero/sty-psp-segment";
6
- import { CheckoutItemStatus } from "../../../../../../domain/checkoutItem/model/checkoutItem";
7
- import { checkout } from "../../../../../projection/checkout/checkout.mock";
8
- import { useViewFirstAvailableCheckoutByCustomerId } from "../../../../../projection/checkout/react/useViewFirstAvailableCheckoutByCustomerId";
9
- import { useViewIsSizeChangeEnabledByCheckoutId } from "../../../../../projection/checkout/react/useViewIsSizeChangeEnabledByCheckoutId";
10
- import { paymentFlowPayload as mockPaymentFlowPayload } from "../../../../../projection/payment/paymentFlowPayload.mock";
11
- import { useViewPaymentFlowPayloadByCheckoutId } from "../../../../../projection/payment/react/useViewPaymentFlowPayloadByCheckoutId";
12
- import { pricing } from "../../../../../projection/pricing/pricing.mock";
13
- import { useViewPricingByCheckoutId } from "../../../../../projection/pricing/react/useViewPricingByCheckoutId";
14
- import { useSubmitCheckout } from "../../../../hooks/useSubmitCheckout";
15
- import { Routes } from "../../../../routing/routes";
16
- import { render } from "../../../../test/render";
17
- import { CheckoutPaymentModal } from "./CheckoutPaymentModal";
18
-
19
- const customerId = "a8fff6d7-708c-41a7-b42a-58c5706d33df";
20
- const country = Country.ES;
21
- const segment = Segment.WOMEN;
22
- const orderNumber = 3691625;
23
- const isFirstOrder = false;
24
- const getAuthToken = () => Promise.resolve("token");
25
- const mockCheckout = checkout({
26
- items: [
27
- { status: CheckoutItemStatus.RETURNED },
28
- { status: CheckoutItemStatus.KEPT },
29
- { status: CheckoutItemStatus.KEPT },
30
- { status: CheckoutItemStatus.KEPT },
31
- { status: CheckoutItemStatus.REPLACED },
32
- ],
33
- });
34
- const mockPricing = pricing();
35
-
36
- jest.mock("../../../../hooks/useStaticInfo", () => ({
37
- useStaticInfo: () => ({
38
- customer: { customerId, country, segment },
39
- basePath: "",
40
- }),
41
- }));
42
-
43
- jest.mock("../../../../../projection/checkout/react/useViewFirstAvailableCheckoutByCustomerId");
44
- jest.mock("../../../../../projection/payment/react/useViewPaymentFlowPayloadByCheckoutId");
45
- jest.mock("../../../../../projection/checkout/react/useViewIsSizeChangeEnabledByCheckoutId");
46
- jest.mock("../../../../../projection/pricing/react/useViewPricingByCheckoutId");
47
- jest.mock("../../../../hooks/useSubmitCheckout");
48
- jest.mock("../../../../../tracking/useTrackCheckout");
49
-
50
- const mockStartLegacyBoxCheckout = jest.fn();
51
- jest.mock("@lookiero/payments-front", () => {
52
- // eslint-disable-next-line @typescript-eslint/no-var-requires
53
- const { useImperativeHandle, forwardRef } = require("react");
54
-
55
- return {
56
- CheckoutStatus: {
57
- REJECTED: "REJECTED",
58
- ERROR: "ERROR",
59
- FULFILLED: "FULFILLED",
60
- },
61
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
62
- // @ts-ignore
63
- // eslint-disable-next-line @typescript-eslint/naming-convention, react/display-name
64
- PaymentFlow: forwardRef((params, ref) => {
65
- useImperativeHandle(ref, () => ({
66
- startLegacyBoxCheckout: mockStartLegacyBoxCheckout,
67
- }));
68
-
69
- return null;
70
- }),
71
- PaymentMethod: {
72
- ["GOOGLE_PAY"]: "google_pay",
73
- },
74
- Section: {
75
- ["BOX_CHECKOUT"]: "box-checkout",
76
- },
77
- PaymentInstrumentSelect: jest.fn(() => <></>),
78
- };
79
- });
80
-
81
- const mockUseNavigate = jest.fn();
82
- jest.mock("react-router-native", () => ({
83
- ...jest.requireActual("react-router-native"),
84
- useNavigate: () => mockUseNavigate,
85
- }));
86
-
87
- describe("CheckoutPaymentModal component", () => {
88
- it("renders correctly", async () => {
89
- const mockSubmitCheckout = jest.fn();
90
- (useViewFirstAvailableCheckoutByCustomerId as jest.Mock).mockReturnValue([mockCheckout, QueryStatus.SUCCESS]);
91
- (useViewPaymentFlowPayloadByCheckoutId as jest.Mock).mockReturnValue([mockPaymentFlowPayload, QueryStatus.SUCCESS]);
92
- (useViewIsSizeChangeEnabledByCheckoutId as jest.Mock).mockReturnValue([true, QueryStatus.SUCCESS]);
93
- (useViewPricingByCheckoutId as jest.Mock).mockReturnValue([mockPricing, QueryStatus.SUCCESS]);
94
- (useSubmitCheckout as jest.Mock).mockReturnValue([mockSubmitCheckout, "success"]);
95
-
96
- await waitFor(() => {
97
- render(
98
- <CheckoutPaymentModal
99
- coupon={null}
100
- getAuthToken={getAuthToken}
101
- isFirstOrder={isFirstOrder}
102
- orderNumber={orderNumber}
103
- subscription="b"
104
- />,
105
- );
106
- expect(mockSubmitCheckout).toHaveBeenCalled();
107
- });
108
- });
109
-
110
- it("navigates back to /checkout when checkout-flow fails", async () => {
111
- (useViewFirstAvailableCheckoutByCustomerId as jest.Mock).mockReturnValue([mockCheckout, QueryStatus.SUCCESS]);
112
- (useViewPaymentFlowPayloadByCheckoutId as jest.Mock).mockReturnValue([mockPaymentFlowPayload, QueryStatus.SUCCESS]);
113
- (useViewIsSizeChangeEnabledByCheckoutId as jest.Mock).mockReturnValue([true, QueryStatus.SUCCESS]);
114
- (useViewPricingByCheckoutId as jest.Mock).mockReturnValue([mockPricing, QueryStatus.SUCCESS]);
115
- (useSubmitCheckout as jest.Mock).mockImplementation(({ onError }) => {
116
- const submitCheckout = () => onError();
117
-
118
- return [submitCheckout, "error"];
119
- });
120
-
121
- await waitFor(() => {
122
- render(
123
- <CheckoutPaymentModal
124
- coupon={null}
125
- getAuthToken={getAuthToken}
126
- isFirstOrder={isFirstOrder}
127
- orderNumber={orderNumber}
128
- subscription="b"
129
- />,
130
- );
131
- expect(mockUseNavigate).toHaveBeenCalledWith(`/${Routes.CHECKOUT}`, { replace: true });
132
- });
133
- });
134
- });