@lookiero/checkout 10.0.0-beta.4 → 10.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.
- package/dist/fake-dependencies/@lookiero/payments-front/index.d.ts +6 -8
- package/dist/fake-dependencies/@lookiero/payments-front/index.js +4 -7
- package/dist/index.d.ts +4 -3
- package/dist/src/ExpoRoot.js +13 -17
- package/dist/src/infrastructure/domain/checkoutBooking/react/useBlockCheckoutBooking.d.ts +1 -1
- package/dist/src/infrastructure/domain/checkoutBooking/react/useBlockCheckoutBooking.js +0 -2
- package/dist/src/infrastructure/projection/pricing/react/useViewPricingByCheckoutId.d.ts +1 -1
- package/dist/src/infrastructure/projection/pricing/react/useViewPricingByCheckoutId.js +1 -2
- package/dist/src/infrastructure/tracking/tracking.d.ts +2 -2
- package/dist/src/infrastructure/tracking/useTrackCheckout.d.ts +17 -10
- package/dist/src/infrastructure/tracking/useTrackCheckout.js +12 -27
- package/dist/src/infrastructure/ui/Root.d.ts +9 -7
- package/dist/src/infrastructure/ui/Root.js +3 -2
- package/dist/src/infrastructure/ui/hooks/usePaymentInstrumentEvents.d.ts +2 -3
- package/dist/src/infrastructure/ui/hooks/usePaymentInstrumentEvents.js +26 -17
- package/dist/src/infrastructure/ui/hooks/useStaticInfo.d.ts +3 -0
- package/dist/src/infrastructure/ui/hooks/useStaticInfo.js +7 -2
- package/dist/src/infrastructure/ui/i18n/i18n.d.ts +0 -1
- package/dist/src/infrastructure/ui/i18n/i18n.js +0 -1
- package/dist/src/infrastructure/ui/routing/CheckoutMiddleware.js +12 -1
- package/dist/src/infrastructure/ui/routing/Routing.d.ts +7 -5
- package/dist/src/infrastructure/ui/routing/Routing.js +11 -3
- package/dist/src/infrastructure/ui/routing/routes.d.ts +1 -0
- package/dist/src/infrastructure/ui/routing/routes.js +1 -0
- package/dist/src/infrastructure/ui/views/App.js +6 -5
- package/dist/src/infrastructure/ui/views/checkout/Checkout.d.ts +2 -7
- package/dist/src/infrastructure/ui/views/checkout/Checkout.js +9 -20
- package/dist/src/infrastructure/ui/views/checkout/Checkout.style.d.ts +0 -3
- package/dist/src/infrastructure/ui/views/checkout/Checkout.style.js +0 -3
- package/dist/src/infrastructure/ui/views/checkout/components/checkoutPaymentModal/CheckoutPaymentModal.js +1 -3
- package/dist/src/infrastructure/ui/views/checkout/components/paymentInstrument/PaymentInstrument.js +7 -7
- package/dist/src/infrastructure/ui/views/summary/Summary.js +1 -2
- package/dist/src/projection/customer/customer.d.ts +0 -2
- 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 +9 -32
- package/index.ts +4 -10
- package/package.json +4 -3
- package/src/ExpoRoot.tsx +37 -42
- package/src/infrastructure/domain/checkoutBooking/react/useBlockCheckoutBooking.ts +1 -4
- package/src/infrastructure/projection/pricing/react/useViewPricingByCheckoutId.ts +2 -3
- package/src/infrastructure/tracking/tracking.ts +2 -2
- package/src/infrastructure/tracking/useTrackCheckout.ts +56 -66
- package/src/infrastructure/ui/Root.tsx +14 -10
- package/src/infrastructure/ui/components/templates/header/itemHeader/ItemHeader.test.tsx +6 -0
- package/src/infrastructure/ui/components/templates/header/itemHeader/ItemHeader.tsx +0 -1
- package/src/infrastructure/ui/hooks/usePaymentInstrumentEvents.ts +60 -18
- package/src/infrastructure/ui/hooks/useStaticInfo.test.tsx +6 -1
- package/src/infrastructure/ui/hooks/useStaticInfo.tsx +13 -2
- package/src/infrastructure/ui/hooks/useSubmitCheckout.test.ts +297 -0
- package/src/infrastructure/ui/hooks/useSubmitCheckout.ts +169 -0
- package/src/infrastructure/ui/i18n/i18n.ts +0 -1
- package/src/infrastructure/ui/routing/CheckoutMiddleware.test.tsx +9 -1
- package/src/infrastructure/ui/routing/CheckoutMiddleware.tsx +15 -1
- package/src/infrastructure/ui/routing/Routing.tsx +29 -15
- package/src/infrastructure/ui/routing/routes.ts +1 -0
- package/src/infrastructure/ui/views/App.tsx +13 -5
- package/src/infrastructure/ui/views/checkout/Checkout.style.ts +0 -3
- package/src/infrastructure/ui/views/checkout/Checkout.test.tsx +43 -51
- package/src/infrastructure/ui/views/checkout/Checkout.tsx +13 -51
- package/src/infrastructure/ui/views/checkout/components/checkoutPaymentModal/CheckoutPaymentModal.test.tsx +134 -0
- package/src/infrastructure/ui/views/checkout/components/checkoutPaymentModal/CheckoutPaymentModal.tsx +124 -0
- package/src/infrastructure/ui/views/checkout/components/paymentInstrument/PaymentInstrument.tsx +8 -8
- package/src/infrastructure/ui/views/return/components/returnQuestionsForm/ReturnQuestionsForm.test.tsx +6 -0
- package/src/infrastructure/ui/views/summary/Summary.tsx +1 -1
- package/src/infrastructure/ui/views/summaryTabs/SummaryTabs.test.tsx +4 -1
- package/src/projection/customer/customer.ts +0 -2
- package/src/projection/order/order.ts +1 -1
- package/src/projection/subscription/subscription.ts +1 -1
- package/dist/public/public/assets/adaptive-icon.png +0 -0
- package/dist/public/public/assets/favicon.png +0 -0
- package/dist/public/public/assets/icon.png +0 -0
- package/dist/public/public/assets/splash.png +0 -0
- package/dist/public/public/images/not-found.png +0 -0
- package/dist/src/infrastructure/ui/hooks/useCheckoutFlow.d.ts +0 -26
- package/dist/src/infrastructure/ui/hooks/useCheckoutFlow.js +0 -127
- package/dist/src/infrastructure/ui/routing/useBasePath.d.ts +0 -8
- package/dist/src/infrastructure/ui/routing/useBasePath.js +0 -9
- package/src/infrastructure/ui/hooks/useCheckoutFlow.test.tsx +0 -302
- package/src/infrastructure/ui/hooks/useCheckoutFlow.tsx +0 -203
|
@@ -0,0 +1,297 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,169 @@
|
|
|
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 };
|
|
@@ -50,7 +50,6 @@ enum I18nMessages {
|
|
|
50
50
|
CHECKOUT_TITLE = "checkout.title",
|
|
51
51
|
CHECKOUT_PAY_BUTTON = "checkout.pay_button",
|
|
52
52
|
CHECKOUT_TOAST_PAYMENT_ERROR = "checkout.toast_payment_error",
|
|
53
|
-
CHECKOUT_TOAST_PAYMENT_SUCCESS = "checkout.toast_payment_success",
|
|
54
53
|
CHECKOUT_SUCCESS_MODAL_TITLE = "checkout.success_modal_title",
|
|
55
54
|
CHECKOUT_SUCCESS_MODAL_DESCRIPTION = "checkout.success_modal_description",
|
|
56
55
|
CHECKOUT_SUCCESS_MODAL_BUTTON = "checkout.success_modal_button",
|
|
@@ -7,8 +7,11 @@ import { generatePath } from "react-router-native";
|
|
|
7
7
|
import { createMemoryRouter, RouterProvider } from "react-router-native";
|
|
8
8
|
import { CommandStatus, QueryStatus } from "@lookiero/messaging-react";
|
|
9
9
|
import { Logger } from "@lookiero/sty-psp-logging";
|
|
10
|
+
import { Tradename } from "@lookiero/sty-sp-tradename";
|
|
10
11
|
import { CheckoutStatus } from "../../../domain/checkout/model/checkout";
|
|
11
12
|
import { CheckoutItemStatus } from "../../../domain/checkoutItem/model/checkoutItem";
|
|
13
|
+
import { Customer } from "../../../projection/customer/customer";
|
|
14
|
+
import { KameleoonEnvironment } from "../../ab-testing/kameleoonEnvironment";
|
|
12
15
|
import { useStartCheckout } from "../../domain/checkout/react/useStartCheckout";
|
|
13
16
|
import { checkout } from "../../projection/checkout/checkout.mock";
|
|
14
17
|
import { useViewFirstAvailableCheckoutByCustomerId } from "../../projection/checkout/react/useViewFirstAvailableCheckoutByCustomerId";
|
|
@@ -91,7 +94,12 @@ const renderCheckoutMiddleware = ({
|
|
|
91
94
|
{
|
|
92
95
|
path: "/*",
|
|
93
96
|
element: (
|
|
94
|
-
<StaticInfoProvider
|
|
97
|
+
<StaticInfoProvider
|
|
98
|
+
basePath=""
|
|
99
|
+
customer={{} as Customer}
|
|
100
|
+
kameleoon={{} as KameleoonEnvironment}
|
|
101
|
+
tradename={Tradename.LOOKIERO}
|
|
102
|
+
>
|
|
95
103
|
<CheckoutMiddleware
|
|
96
104
|
customerId={customerId}
|
|
97
105
|
loader={<Text>{loaderText}</Text>}
|
|
@@ -48,6 +48,12 @@ const CheckoutMiddleware: FC<CheckoutMiddlewareProps> = ({
|
|
|
48
48
|
const feedbackRouteMatch = useMatch(`${basePath}/${Routes.FEEDBACK}`);
|
|
49
49
|
const feedbackRouteMatchRef = useRef(feedbackRouteMatch);
|
|
50
50
|
feedbackRouteMatchRef.current = feedbackRouteMatch;
|
|
51
|
+
const checkoutPaymentRouteMatch = useMatch(`${basePath}/${Routes.CHECKOUT}/${Routes.CHECKOUT_PAYMENT}`);
|
|
52
|
+
const checkoutPaymentRouteMatchRef = useRef(checkoutPaymentRouteMatch);
|
|
53
|
+
checkoutPaymentRouteMatchRef.current = checkoutPaymentRouteMatch;
|
|
54
|
+
|
|
55
|
+
const checkoutShown = useRef(false);
|
|
56
|
+
checkoutShown.current = checkoutShown.current || (Boolean(checkoutRouteMatch) && !Boolean(checkoutPaymentRouteMatch));
|
|
51
57
|
|
|
52
58
|
const [checkout] = useViewFirstAvailableCheckoutByCustomerId({ customerId });
|
|
53
59
|
const checkoutItemsRef = useRef<CheckoutItemProjection[]>();
|
|
@@ -93,7 +99,8 @@ const CheckoutMiddleware: FC<CheckoutMiddlewareProps> = ({
|
|
|
93
99
|
summaryRouteMatchRef.current ||
|
|
94
100
|
summaryTabsRouteMatchRef.current ||
|
|
95
101
|
itemDetailRouteMatchRef.current ||
|
|
96
|
-
checkoutRouteMatchRef.current
|
|
102
|
+
checkoutRouteMatchRef.current ||
|
|
103
|
+
checkoutPaymentRouteMatchRef.current
|
|
97
104
|
)
|
|
98
105
|
) {
|
|
99
106
|
navigateRef.current(`${basePath}/${Routes.SUMMARY}`, { replace: true });
|
|
@@ -120,6 +127,13 @@ const CheckoutMiddleware: FC<CheckoutMiddlewareProps> = ({
|
|
|
120
127
|
return null;
|
|
121
128
|
}
|
|
122
129
|
|
|
130
|
+
/* Prevent direct payment access */
|
|
131
|
+
if (checkoutPaymentRouteMatch && !checkoutShown.current) {
|
|
132
|
+
onNotAccessible();
|
|
133
|
+
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
|
|
123
137
|
return children;
|
|
124
138
|
};
|
|
125
139
|
|
|
@@ -5,13 +5,15 @@ import { I18n } from "@lookiero/i18n-react";
|
|
|
5
5
|
import { Kameleoon } from "@lookiero/sty-psp-ab-testing";
|
|
6
6
|
import { Locale } from "@lookiero/sty-psp-locale";
|
|
7
7
|
import { Layout } from "@lookiero/sty-psp-ui";
|
|
8
|
+
import { Tradename } from "@lookiero/sty-sp-tradename";
|
|
8
9
|
import { Customer } from "../../../projection/customer/customer";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
10
|
+
import { Order } from "../../../projection/order/order";
|
|
11
|
+
import { Subscription } from "../../../projection/subscription/subscription";
|
|
11
12
|
import { KameleoonEnvironment } from "../../ab-testing/kameleoonEnvironment";
|
|
12
13
|
import { StaticInfoProvider } from "../hooks/useStaticInfo";
|
|
13
14
|
import { App } from "../views/App";
|
|
14
15
|
import { Checkout } from "../views/checkout/Checkout";
|
|
16
|
+
import { CheckoutPaymentModal } from "../views/checkout/components/checkoutPaymentModal/CheckoutPaymentModal";
|
|
15
17
|
import { Feedback } from "../views/feedback/Feedback";
|
|
16
18
|
import { Item } from "../views/item/Item";
|
|
17
19
|
import { Return } from "../views/return/Return";
|
|
@@ -23,15 +25,16 @@ import { Routes } from "./routes";
|
|
|
23
25
|
interface RoutingProps {
|
|
24
26
|
readonly basePath?: string;
|
|
25
27
|
readonly customer: Customer;
|
|
26
|
-
readonly order:
|
|
27
|
-
readonly subscription:
|
|
28
|
+
readonly order: Order | undefined;
|
|
29
|
+
readonly subscription: Subscription | undefined;
|
|
28
30
|
readonly locale: Locale;
|
|
29
31
|
readonly I18n: I18n;
|
|
30
32
|
readonly kameleoon: KameleoonEnvironment;
|
|
31
33
|
readonly layout: Layout;
|
|
34
|
+
readonly tradename: Tradename;
|
|
32
35
|
readonly getAuthToken: () => Promise<string>;
|
|
33
36
|
readonly onNotAccessible: () => void;
|
|
34
|
-
readonly
|
|
37
|
+
readonly onCheckoutSubmitted?: () => void;
|
|
35
38
|
readonly onI18nError?: (err: Error) => void;
|
|
36
39
|
readonly useRedirect: () => Record<string, string>;
|
|
37
40
|
readonly useRoutes: typeof reactRouterUseRoutes;
|
|
@@ -46,10 +49,11 @@ const Routing: FC<RoutingProps> = ({
|
|
|
46
49
|
I18n,
|
|
47
50
|
kameleoon,
|
|
48
51
|
layout,
|
|
52
|
+
tradename,
|
|
49
53
|
getAuthToken,
|
|
50
54
|
onI18nError,
|
|
51
55
|
onNotAccessible,
|
|
52
|
-
|
|
56
|
+
onCheckoutSubmitted,
|
|
53
57
|
useRedirect,
|
|
54
58
|
useRoutes = reactRouterUseRoutes,
|
|
55
59
|
}) => {
|
|
@@ -57,7 +61,7 @@ const Routing: FC<RoutingProps> = ({
|
|
|
57
61
|
{
|
|
58
62
|
path: "",
|
|
59
63
|
element: (
|
|
60
|
-
<StaticInfoProvider basePath={basePath} customer={customer} kameleoon={kameleoon}>
|
|
64
|
+
<StaticInfoProvider basePath={basePath} customer={customer} kameleoon={kameleoon} tradename={tradename}>
|
|
61
65
|
<I18n loader={<Spinner />} locale={locale} onError={onI18nError}>
|
|
62
66
|
<Kameleoon loader={<Spinner />} siteCode={kameleoon.siteCode}>
|
|
63
67
|
<CheckoutMiddleware customerId={customer?.customerId as string} onNotAccessible={onNotAccessible}>
|
|
@@ -110,16 +114,26 @@ const Routing: FC<RoutingProps> = ({
|
|
|
110
114
|
path: Routes.CHECKOUT,
|
|
111
115
|
element: (
|
|
112
116
|
<Suspense fallback={<Spinner />}>
|
|
113
|
-
<Checkout
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
order={order}
|
|
117
|
-
subscription={subscription}
|
|
118
|
-
useRedirect={useRedirect}
|
|
119
|
-
onCheckoutFlowSuccess={onCheckoutFlowSuccess}
|
|
120
|
-
/>
|
|
117
|
+
<Checkout layout={layout} useRedirect={useRedirect}>
|
|
118
|
+
<Outlet />
|
|
119
|
+
</Checkout>
|
|
121
120
|
</Suspense>
|
|
122
121
|
),
|
|
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
|
+
],
|
|
123
137
|
},
|
|
124
138
|
{
|
|
125
139
|
path: Routes.FEEDBACK,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PortalProvider } 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";
|
|
5
6
|
import { Notifications } from "@lookiero/sty-psp-notifications";
|
|
6
7
|
import { theme } from "@lookiero/sty-psp-ui";
|
|
7
8
|
import { MESSAGING_CONTEXT_ID } from "../../delivery/baseBootstrap";
|
|
@@ -15,10 +16,17 @@ interface AppProps {
|
|
|
15
16
|
|
|
16
17
|
const App: FC<AppProps> = ({ children }) => (
|
|
17
18
|
<SafeAreaProvider>
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
<PortalProvider rootHostName="Checkout">
|
|
20
|
+
<StatusBar backgroundColor={colorBgBase} barStyle="dark-content" translucent />
|
|
21
|
+
<Notifications contextId={MESSAGING_CONTEXT_ID} domain={DOMAIN} portalHostName="Checkout" />
|
|
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>
|
|
22
30
|
</SafeAreaProvider>
|
|
23
31
|
);
|
|
24
32
|
|