@glomopay/react-native-sdk 2.0.2 → 3.0.1

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 (70) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/README.md +261 -310
  3. package/lib/config/base.d.ts +17 -11
  4. package/lib/config/base.d.ts.map +1 -1
  5. package/lib/config/base.js +19 -7
  6. package/lib/config/segment.js +3 -3
  7. package/lib/glomo-checkout.d.ts +8 -0
  8. package/lib/glomo-checkout.d.ts.map +1 -0
  9. package/lib/glomo-checkout.js +69 -0
  10. package/lib/glomo-lrs-checkout.js +9 -9
  11. package/lib/glomo-standard-checkout.d.ts +5 -0
  12. package/lib/glomo-standard-checkout.d.ts.map +1 -0
  13. package/lib/glomo-standard-checkout.js +218 -0
  14. package/lib/index.d.ts +5 -4
  15. package/lib/index.d.ts.map +1 -1
  16. package/lib/index.js +7 -5
  17. package/lib/injections/index.d.ts +1 -0
  18. package/lib/injections/index.d.ts.map +1 -1
  19. package/lib/injections/index.js +2 -0
  20. package/lib/injections/webview-flow.injection.d.ts.map +1 -1
  21. package/lib/injections/webview-flow.injection.js +106 -69
  22. package/lib/injections/webview-main.injection.d.ts.map +1 -1
  23. package/lib/injections/webview-main.injection.js +112 -77
  24. package/lib/injections/webview-standard.injection.d.ts +3 -0
  25. package/lib/injections/webview-standard.injection.d.ts.map +1 -0
  26. package/lib/injections/webview-standard.injection.js +214 -0
  27. package/lib/services/order-type-fetcher.d.ts +28 -0
  28. package/lib/services/order-type-fetcher.d.ts.map +1 -0
  29. package/lib/services/order-type-fetcher.js +99 -0
  30. package/lib/types/checkout.d.ts +53 -0
  31. package/lib/types/checkout.d.ts.map +1 -0
  32. package/lib/types/checkout.js +3 -0
  33. package/lib/types/standard-checkout.d.ts +40 -0
  34. package/lib/types/standard-checkout.d.ts.map +1 -0
  35. package/lib/types/standard-checkout.js +3 -0
  36. package/lib/use-glomo-checkout.d.ts +24 -0
  37. package/lib/use-glomo-checkout.d.ts.map +1 -0
  38. package/lib/use-glomo-checkout.js +182 -0
  39. package/lib/use-lrs-checkout.d.ts +9 -4
  40. package/lib/use-lrs-checkout.d.ts.map +1 -1
  41. package/lib/use-lrs-checkout.js +76 -93
  42. package/lib/use-standard-checkout.d.ts +65 -0
  43. package/lib/use-standard-checkout.d.ts.map +1 -0
  44. package/lib/use-standard-checkout.js +832 -0
  45. package/lib/utils/analytics.d.ts +102 -1
  46. package/lib/utils/analytics.d.ts.map +1 -1
  47. package/lib/utils/analytics.js +294 -21
  48. package/lib/utils/device-compliance.js +3 -3
  49. package/lib/utils/validation.d.ts.map +1 -1
  50. package/lib/utils/validation.js +7 -6
  51. package/package.json +3 -2
  52. package/src/config/base.ts +36 -17
  53. package/src/config/segment.ts +3 -3
  54. package/src/glomo-checkout.tsx +73 -0
  55. package/src/glomo-lrs-checkout.tsx +9 -9
  56. package/src/glomo-standard-checkout.tsx +324 -0
  57. package/src/index.ts +13 -7
  58. package/src/injections/index.ts +2 -0
  59. package/src/injections/webview-flow.injection.ts +106 -69
  60. package/src/injections/webview-main.injection.ts +112 -77
  61. package/src/injections/webview-standard.injection.ts +211 -0
  62. package/src/services/order-type-fetcher.ts +86 -0
  63. package/src/types/checkout.ts +65 -0
  64. package/src/types/standard-checkout.ts +49 -0
  65. package/src/use-glomo-checkout.tsx +228 -0
  66. package/src/use-lrs-checkout.tsx +91 -111
  67. package/src/use-standard-checkout.tsx +1185 -0
  68. package/src/utils/analytics.ts +431 -22
  69. package/src/utils/device-compliance.ts +3 -3
  70. package/src/utils/validation.ts +7 -8
@@ -0,0 +1,832 @@
1
+ "use strict";
2
+ /** Main SDK hook for the GlomoPay Standard Checkout flow */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.useStandardCheckout = useStandardCheckout;
5
+ const react_1 = require("react");
6
+ const react_native_1 = require("react-native");
7
+ const index_1 = require("./injections/index");
8
+ const segment_1 = require("./config/segment");
9
+ const base_1 = require("./config/base");
10
+ const validation_1 = require("./utils/validation");
11
+ const device_compliance_1 = require("./utils/device-compliance");
12
+ const analytics_1 = require("./utils/analytics");
13
+ /** The main SDK hook for the GlomoPay Standard Checkout flow */
14
+ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymentFailure, onConnectionError, onPaymentTerminate, onSdkError, onBankTransferSubmitted, onPayViaBankCompleted, onPayViaBankBankConnectionSuccessful, onUserRefusedCameraPermissions, devMode = false, }, mockMode) {
15
+ // Refs for the WebViews
16
+ const mainWebViewRef = (0, react_1.useRef)(null);
17
+ const flowWebViewRef = (0, react_1.useRef)(null);
18
+ // Deduplication guard - prevents firing onPayViaBankCompleted twice when both main and flow WebViews emit the event
19
+ const payViaBankCompletedHandledRef = (0, react_1.useRef)(false);
20
+ // Resolving checkout URL from the selected SDK server
21
+ const serverConfig = (0, react_1.useMemo)(() => (0, base_1.resolveServerConfig)(server), [server]);
22
+ const baseCheckoutUrl = serverConfig.baseUrl.standardCheckout;
23
+ /**
24
+ * The main checkout URL for the standard flow
25
+ */
26
+ const checkoutUrl = (0, react_1.useMemo)(() => {
27
+ try {
28
+ // Building the query string manually since URLSearchParams is not fully supported in React Native
29
+ const baseParams = [`orderId=${encodeURIComponent(orderId)}`];
30
+ const mode = mockMode ? "mock" : "live";
31
+ const params = baseParams.concat(`mode=${encodeURIComponent(mode)}`, `publicKey=${encodeURIComponent(publicKey)}`);
32
+ const queryString = params.join("&");
33
+ // Handling base URL with or without existing query parameters
34
+ const separator = baseCheckoutUrl.includes("?") ? "&" : "?";
35
+ const finalUrl = `${baseCheckoutUrl}${separator}${queryString}`;
36
+ // Validating the final URL
37
+ if (!(0, validation_1.isValidUrl)(finalUrl)) {
38
+ if (devMode) {
39
+ console.error("[Glomo-RN-SDK Standard] Generated invalid checkout URL:", finalUrl);
40
+ }
41
+ return "";
42
+ }
43
+ if (devMode) {
44
+ console.log("[Glomo-RN-SDK Standard] Checkout URL:", finalUrl);
45
+ }
46
+ return finalUrl;
47
+ }
48
+ catch (error) {
49
+ if (devMode) {
50
+ console.error("[Glomo-RN-SDK Standard] Error building checkout URL:", error);
51
+ }
52
+ return "";
53
+ }
54
+ }, [baseCheckoutUrl, orderId, publicKey, mockMode, devMode]);
55
+ // Checkout status state
56
+ const [status, setStatus] = (0, react_1.useState)("ready");
57
+ // Visibility state for the main checkout modal
58
+ const [showCheckout, setShowCheckout] = (0, react_1.useState)(false);
59
+ const [showFlowWebView, setShowFlowWebView] = (0, react_1.useState)(false);
60
+ const [flowWebViewUrl, setFlowWebViewUrl] = (0, react_1.useState)("");
61
+ (0, react_1.useEffect)(() => {
62
+ if (devMode) {
63
+ console.log(`[Glomo-RN-SDK Standard] State snapshot: status=${status}, showCheckout=${showCheckout}, showFlowWebView=${showFlowWebView}, flowWebViewUrl=${flowWebViewUrl || "<empty>"}`);
64
+ }
65
+ }, [devMode, status, showCheckout, showFlowWebView, flowWebViewUrl]);
66
+ // Handler for the WebView errors
67
+ const handleError = (0, react_1.useCallback)((syntheticEvent) => {
68
+ try {
69
+ const { nativeEvent } = syntheticEvent;
70
+ // Now checking for connection errors
71
+ // iOS: NSURLErrorDomain errors
72
+ const isIOSConnectionError = (nativeEvent === null || nativeEvent === void 0 ? void 0 : nativeEvent.domain) === "NSURLErrorDomain" &&
73
+ ((nativeEvent === null || nativeEvent === void 0 ? void 0 : nativeEvent.code) === -1004 || // Could not connect to the server
74
+ (nativeEvent === null || nativeEvent === void 0 ? void 0 : nativeEvent.code) === -1009 || // The Internet connection appears to be offline
75
+ (nativeEvent === null || nativeEvent === void 0 ? void 0 : nativeEvent.code) === -1001 || // The request timed out
76
+ (nativeEvent === null || nativeEvent === void 0 ? void 0 : nativeEvent.code) === -1003); // Cannot find host
77
+ // Android: Network errors
78
+ const description = (nativeEvent === null || nativeEvent === void 0 ? void 0 : nativeEvent.description) || "";
79
+ const isAndroidConnectionError = typeof description === "string" &&
80
+ (description.includes("ERR_EMPTY_RESPONSE") || // Server unreachable
81
+ description.includes("ERR_CONNECTION_REFUSED") || // Connection refused
82
+ description.includes("ERR_NAME_NOT_RESOLVED") || // DNS resolution failed
83
+ description.includes("ERR_INTERNET_DISCONNECTED") || // No internet
84
+ description.includes("ERR_CONNECTION_TIMED_OUT") || // Connection timeout
85
+ description.includes("ERR_NETWORK_CHANGED")); // Network changed
86
+ if (isIOSConnectionError || isAndroidConnectionError) {
87
+ if (devMode) {
88
+ console.error("[Glomo-RN-SDK Standard] Connection error:", nativeEvent);
89
+ }
90
+ // Closing the checkout modal
91
+ setShowCheckout(false);
92
+ setShowFlowWebView(false);
93
+ setFlowWebViewUrl("");
94
+ // Calling onConnectionError callback if provided
95
+ (0, validation_1.safeCallback)(onConnectionError, [nativeEvent], "onConnectionError", devMode);
96
+ // Tracking connection error
97
+ (0, analytics_1.trackStandardConnectionError)(orderId, nativeEvent === null || nativeEvent === void 0 ? void 0 : nativeEvent.code, publicKey, devMode, mockMode, checkoutUrl);
98
+ }
99
+ else {
100
+ if (devMode) {
101
+ console.error("[Glomo-RN-SDK Standard] WebView error:", nativeEvent);
102
+ }
103
+ }
104
+ }
105
+ catch (error) {
106
+ if (devMode) {
107
+ console.error("[Glomo-RN-SDK Standard] Error in handleError:", error);
108
+ }
109
+ }
110
+ }, [devMode, onConnectionError, checkoutUrl, orderId, publicKey, mockMode]);
111
+ // Handler for the WebView HTTP errors
112
+ const handleHttpError = (0, react_1.useCallback)((syntheticEvent) => {
113
+ try {
114
+ const { nativeEvent } = syntheticEvent;
115
+ if (devMode) {
116
+ console.error("[Glomo-RN-SDK Standard] WebView HTTP error:", nativeEvent);
117
+ }
118
+ }
119
+ catch (error) {
120
+ if (devMode) {
121
+ console.error("[Glomo-RN-SDK Standard] Error in handleHttpError:", error);
122
+ }
123
+ }
124
+ }, [devMode]);
125
+ /**
126
+ * Handler for the main WebView messages.
127
+ * The main WebView hosts the checkout page. Messages arrive as JSON strings from the injected
128
+ * JavaScript bridge and include console logs, window.open/close interceptions, and payment
129
+ * lifecycle events (success, failure, terminate, bank transfer, pay via bank).
130
+ */
131
+ const handleMainWebViewMessage = (0, react_1.useCallback)((event) => {
132
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
133
+ try {
134
+ // Extracting and validating the raw message data from the WebView event
135
+ const data = (_a = event.nativeEvent) === null || _a === void 0 ? void 0 : _a.data;
136
+ if (!data || typeof data !== "string") {
137
+ if (devMode) {
138
+ console.warn("[Glomo-RN-SDK Standard] Invalid message data from MainWebView");
139
+ }
140
+ // Tracking invalid message received
141
+ (0, analytics_1.trackStandardInvalidMessageReceived)("main", data, checkoutUrl, orderId, publicKey, devMode, mockMode, checkoutUrl);
142
+ return;
143
+ }
144
+ const message = JSON.parse(data);
145
+ if (devMode) {
146
+ console.log("[Glomo-RN-SDK Standard] MainWebView Message received:", message.type, message);
147
+ }
148
+ // Forwarding console messages from the injected script to the React Native console
149
+ if (message.type === "console") {
150
+ if (devMode) {
151
+ const level = message.level || "log";
152
+ console.log(`[Glomo-RN-SDK Standard] [WebView ${level.toUpperCase()}]`, message.message);
153
+ }
154
+ return;
155
+ }
156
+ // Intercepting window.open calls - opening the target URL in the flow WebView overlay
157
+ if (message.type === "window.open") {
158
+ const url = message.url;
159
+ if (url && typeof url === "string" && (0, validation_1.isValidUrl)(url)) {
160
+ if (devMode) {
161
+ console.log(`[Glomo-RN-SDK Standard] window.open called from MainWebView: ${url}`);
162
+ }
163
+ setFlowWebViewUrl(url);
164
+ setShowFlowWebView(true);
165
+ // Tracking window.open interception
166
+ (0, analytics_1.trackStandardWindowOpen)("main", url, orderId, publicKey, devMode, mockMode, checkoutUrl);
167
+ }
168
+ else {
169
+ if (devMode) {
170
+ console.log("[Glomo-RN-SDK Standard] Invalid URL in window.open from MainWebView:", url, typeof url === "undefined" ? "undefined" : "");
171
+ }
172
+ }
173
+ return;
174
+ }
175
+ // Intercepting window.close calls - closing the flow WebView overlay
176
+ if (message.type === "window.close") {
177
+ if (devMode) {
178
+ console.log("[Glomo-RN-SDK Standard] Child window close requested from MainWebView");
179
+ }
180
+ setShowFlowWebView(false);
181
+ setFlowWebViewUrl("");
182
+ // Tracking window.close interception
183
+ (0, analytics_1.trackStandardWindowClose)("main", orderId, publicKey, devMode, mockMode, checkoutUrl);
184
+ return;
185
+ }
186
+ // Handling merchant JS callbacks on flow completion (payment events, lifecycle events)
187
+ if (message.type === "message" && message.message) {
188
+ const messageData = message.message;
189
+ const messageType = messageData === null || messageData === void 0 ? void 0 : messageData.type;
190
+ // Handling payment success - closing checkout and notifying the merchant
191
+ if (messageType === "payment.success" || messageType === "payment.complete") {
192
+ const paymentMessage = messageData;
193
+ const payload = paymentMessage === null || paymentMessage === void 0 ? void 0 : paymentMessage.payload;
194
+ if ((0, validation_1.isValidPaymentPayload)(payload)) {
195
+ if (devMode) {
196
+ console.log("[Glomo-RN-SDK Standard] Payment successful via merchant callback!");
197
+ }
198
+ setShowCheckout(false);
199
+ setShowFlowWebView(false);
200
+ setFlowWebViewUrl("");
201
+ setStatus("payment_successful");
202
+ (0, validation_1.safeCallback)(onPaymentSuccess, [payload], "onPaymentSuccess", devMode);
203
+ // Tracking payment success callback
204
+ (0, analytics_1.trackStandardPaymentSuccess)(payload.orderId, payload.paymentId, publicKey, devMode, mockMode, checkoutUrl);
205
+ }
206
+ else {
207
+ if (devMode) {
208
+ console.error("[Glomo-RN-SDK Standard] Invalid payment success payload:", payload);
209
+ }
210
+ }
211
+ return;
212
+ }
213
+ // Handling payment failure - closing checkout and notifying the merchant
214
+ if (messageType === "payment.failure" || messageType === "payment.error") {
215
+ const paymentMessage = messageData;
216
+ const payload = paymentMessage === null || paymentMessage === void 0 ? void 0 : paymentMessage.payload;
217
+ if ((0, validation_1.isValidPaymentPayload)(payload)) {
218
+ if (devMode) {
219
+ console.error("[Glomo-RN-SDK Standard] Payment failed via merchant callback");
220
+ }
221
+ setShowCheckout(false);
222
+ setShowFlowWebView(false);
223
+ setFlowWebViewUrl("");
224
+ setStatus("payment_failed");
225
+ (0, validation_1.safeCallback)(onPaymentFailure, [payload], "onPaymentFailure", devMode);
226
+ // Tracking payment failure callback
227
+ (0, analytics_1.trackStandardPaymentFailure)(payload.orderId, payload.paymentId, publicKey, devMode, mockMode, checkoutUrl);
228
+ }
229
+ else {
230
+ if (devMode) {
231
+ console.error("[Glomo-RN-SDK Standard] Invalid payment failure payload:", payload);
232
+ }
233
+ }
234
+ return;
235
+ }
236
+ // Handling bank transfer submission - user has submitted transfer details
237
+ if (messageType === "payment.bank_transfer_submitted") {
238
+ const payload = messageData === null || messageData === void 0 ? void 0 : messageData.payload;
239
+ if (devMode) {
240
+ console.log("[Glomo-RN-SDK Standard] Bank transfer submitted:", payload);
241
+ }
242
+ setShowCheckout(false);
243
+ setShowFlowWebView(false);
244
+ setFlowWebViewUrl("");
245
+ setStatus("bank_transfer_submitted");
246
+ (0, validation_1.safeCallback)(onBankTransferSubmitted, [
247
+ {
248
+ orderId: (_b = payload === null || payload === void 0 ? void 0 : payload.orderId) !== null && _b !== void 0 ? _b : "",
249
+ senderAccountNumber: (_c = payload === null || payload === void 0 ? void 0 : payload.senderAccountNumber) !== null && _c !== void 0 ? _c : "",
250
+ transactionReference: (_d = payload === null || payload === void 0 ? void 0 : payload.transactionReference) !== null && _d !== void 0 ? _d : "",
251
+ },
252
+ ], "onBankTransferSubmitted", devMode);
253
+ (0, analytics_1.trackStandardBankTransferSubmitted)(orderId, publicKey, devMode, mockMode, checkoutUrl);
254
+ return;
255
+ }
256
+ // Handling pay via bank - bank connection successful event (informational, no status change)
257
+ if (messageType === "pay_via_bank.bank.connection_successful") {
258
+ const payload = messageData === null || messageData === void 0 ? void 0 : messageData.payload;
259
+ if (devMode) {
260
+ console.log("[Glomo-RN-SDK Standard] Pay via bank - bank connection successful:", payload);
261
+ }
262
+ (0, validation_1.safeCallback)(onPayViaBankBankConnectionSuccessful, [
263
+ {
264
+ bankIdentifier: (_e = payload === null || payload === void 0 ? void 0 : payload.bankIdentifier) !== null && _e !== void 0 ? _e : "",
265
+ cooldownPeriodInMinutes: (_f = payload === null || payload === void 0 ? void 0 : payload.cooldownPeriodInMinutes) !== null && _f !== void 0 ? _f : 0,
266
+ bankName: (_g = payload === null || payload === void 0 ? void 0 : payload.bankName) !== null && _g !== void 0 ? _g : "",
267
+ bankImageSrc: (_h = payload === null || payload === void 0 ? void 0 : payload.bankImageSrc) !== null && _h !== void 0 ? _h : "",
268
+ bankImageAlt: (_j = payload === null || payload === void 0 ? void 0 : payload.bankImageAlt) !== null && _j !== void 0 ? _j : "",
269
+ },
270
+ ], "onPayViaBankBankConnectionSuccessful", devMode);
271
+ (0, analytics_1.trackStandardPayViaBankBankConnectionSuccessful)(orderId, publicKey, (_k = payload === null || payload === void 0 ? void 0 : payload.bankIdentifier) !== null && _k !== void 0 ? _k : "", devMode, mockMode, checkoutUrl);
272
+ return;
273
+ }
274
+ /**
275
+ * Handling the generic glomoCheckoutJourneyTerminate event.
276
+ * This event is fired by the checkout page when the pay via bank (lean/open finance)
277
+ * flow completes. The deduplication ref prevents firing the callback twice when both
278
+ * main and flow WebViews emit this event.
279
+ */
280
+ if (messageType === "glomoCheckoutJourneyTerminate") {
281
+ // Deduplication - both main and flow WebViews can emit this event
282
+ if (payViaBankCompletedHandledRef.current) {
283
+ if (devMode) {
284
+ console.log("[Glomo-RN-SDK Standard] Pay via bank completed already handled, ignoring duplicate");
285
+ }
286
+ return;
287
+ }
288
+ payViaBankCompletedHandledRef.current = true;
289
+ const payload = messageData === null || messageData === void 0 ? void 0 : messageData.payload;
290
+ if (devMode) {
291
+ console.log("[Glomo-RN-SDK Standard] Pay via bank completed via MainWebView:", payload);
292
+ }
293
+ setShowFlowWebView(false);
294
+ setShowCheckout(false);
295
+ setFlowWebViewUrl("");
296
+ setStatus("pay_via_bank_completed");
297
+ (0, validation_1.safeCallback)(onPayViaBankCompleted, [{ status: (_l = payload === null || payload === void 0 ? void 0 : payload.status) !== null && _l !== void 0 ? _l : "unknown" }], "onPayViaBankCompleted", devMode);
298
+ (0, analytics_1.trackStandardPayViaBankCompleted)(orderId, publicKey, (_m = payload === null || payload === void 0 ? void 0 : payload.status) !== null && _m !== void 0 ? _m : "unknown", devMode, mockMode, checkoutUrl);
299
+ return;
300
+ }
301
+ // Handling checkout.closed events
302
+ if (messageType === "checkout.closed") {
303
+ if (devMode) {
304
+ console.log("[Glomo-RN-SDK Standard] Checkout closed event received");
305
+ }
306
+ setShowCheckout(false);
307
+ setShowFlowWebView(false);
308
+ setFlowWebViewUrl("");
309
+ (0, validation_1.safeCallback)(onPaymentTerminate, [], "onPaymentTerminate", devMode);
310
+ // Tracking payment terminate callback
311
+ (0, analytics_1.trackStandardPaymentTerminate)(orderId, "checkout_closed", publicKey, devMode, mockMode, checkoutUrl);
312
+ return;
313
+ }
314
+ }
315
+ }
316
+ catch (error) {
317
+ if (devMode) {
318
+ console.error("[Glomo-RN-SDK Standard] Error parsing MainWebView message:", error);
319
+ }
320
+ }
321
+ }, [
322
+ onPaymentSuccess,
323
+ onPaymentFailure,
324
+ onPaymentTerminate,
325
+ onBankTransferSubmitted,
326
+ onPayViaBankCompleted,
327
+ onPayViaBankBankConnectionSuccessful,
328
+ devMode,
329
+ mockMode,
330
+ orderId,
331
+ publicKey,
332
+ checkoutUrl,
333
+ ]);
334
+ /**
335
+ * Handler for the flow WebView messages.
336
+ * The flow WebView hosts child pages opened via window.open (e.g. 3DS, bank redirects).
337
+ * Message structure mirrors the main WebView handler but operates on the overlay WebView.
338
+ */
339
+ const handleFlowWebViewMessage = (0, react_1.useCallback)((event) => {
340
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
341
+ try {
342
+ // Extracting and validating the raw message data from the flow WebView event
343
+ const data = (_a = event.nativeEvent) === null || _a === void 0 ? void 0 : _a.data;
344
+ if (!data || typeof data !== "string") {
345
+ if (devMode) {
346
+ console.warn("[Glomo-RN-SDK Standard] Invalid message data from FlowWebView");
347
+ }
348
+ // Tracking invalid message received
349
+ (0, analytics_1.trackStandardInvalidMessageReceived)("flow", data, flowWebViewUrl, orderId, publicKey, devMode, mockMode, checkoutUrl);
350
+ return;
351
+ }
352
+ const message = JSON.parse(data);
353
+ if (devMode) {
354
+ console.log("[Glomo-RN-SDK Standard] FlowWebView Message received:", message.type, message);
355
+ }
356
+ // Forwarding console messages from the flow WebView's injected script
357
+ if (message.type === "console") {
358
+ if (devMode) {
359
+ console.log(`[Glomo-RN-SDK Standard] [Flow] ${message.message}`);
360
+ }
361
+ return;
362
+ }
363
+ // Intercepting window.open calls from the flow WebView - navigating within the same overlay
364
+ if (message.type === "window.open") {
365
+ const url = message.url;
366
+ if (url && typeof url === "string" && (0, validation_1.isValidUrl)(url)) {
367
+ if (devMode) {
368
+ console.log(`[Glomo-RN-SDK Standard] window.open called from FlowWebView: ${url}`);
369
+ }
370
+ setFlowWebViewUrl(url);
371
+ // Tracking window.open interception
372
+ (0, analytics_1.trackStandardWindowOpen)("flow", url, orderId, publicKey, devMode, mockMode, checkoutUrl);
373
+ }
374
+ else {
375
+ if (devMode) {
376
+ console.log("[Glomo-RN-SDK Standard] Invalid URL in window.open from FlowWebView:", url, typeof url === "undefined" ? "undefined" : "");
377
+ }
378
+ }
379
+ return;
380
+ }
381
+ // Intercepting window.close calls from the flow WebView - closing the overlay
382
+ if (message.type === "window.close") {
383
+ if (devMode) {
384
+ console.log("[Glomo-RN-SDK Standard] FlowWebView close requested");
385
+ }
386
+ setShowFlowWebView(false);
387
+ setFlowWebViewUrl("");
388
+ // Tracking window.close interception
389
+ (0, analytics_1.trackStandardWindowClose)("flow", orderId, publicKey, devMode, mockMode, checkoutUrl);
390
+ return;
391
+ }
392
+ // Handling merchant JS callbacks from the flow WebView (payment events, lifecycle events)
393
+ if (message.type === "message" && message.message) {
394
+ const messageData = message.message;
395
+ const messageType = messageData === null || messageData === void 0 ? void 0 : messageData.type;
396
+ // Handling payment success from the flow WebView
397
+ if (messageType === "payment.success" || messageType === "payment.complete") {
398
+ const paymentMessage = messageData;
399
+ const payload = paymentMessage === null || paymentMessage === void 0 ? void 0 : paymentMessage.payload;
400
+ if ((0, validation_1.isValidPaymentPayload)(payload)) {
401
+ if (devMode) {
402
+ console.log("[Glomo-RN-SDK Standard] Payment successful via FlowWebView callback!");
403
+ }
404
+ setShowFlowWebView(false);
405
+ setShowCheckout(false);
406
+ setFlowWebViewUrl("");
407
+ setStatus("payment_successful");
408
+ (0, validation_1.safeCallback)(onPaymentSuccess, [payload], "onPaymentSuccess", devMode);
409
+ // Tracking payment success callback
410
+ (0, analytics_1.trackStandardPaymentSuccess)(payload.orderId, payload.paymentId, publicKey, devMode, mockMode, checkoutUrl);
411
+ }
412
+ else {
413
+ if (devMode) {
414
+ console.error("[Glomo-RN-SDK Standard] Invalid payment success payload from FlowWebView:", payload);
415
+ }
416
+ }
417
+ return;
418
+ }
419
+ // Handling payment failure from the flow WebView
420
+ if (messageType === "payment.failure" || messageType === "payment.error") {
421
+ const paymentMessage = messageData;
422
+ const payload = paymentMessage === null || paymentMessage === void 0 ? void 0 : paymentMessage.payload;
423
+ if ((0, validation_1.isValidPaymentPayload)(payload)) {
424
+ if (devMode) {
425
+ console.error("[Glomo-RN-SDK Standard] Payment failed via FlowWebView callback");
426
+ }
427
+ setShowFlowWebView(false);
428
+ setShowCheckout(false);
429
+ setFlowWebViewUrl("");
430
+ setStatus("payment_failed");
431
+ (0, validation_1.safeCallback)(onPaymentFailure, [payload], "onPaymentFailure", devMode);
432
+ // Tracking payment failure callback
433
+ (0, analytics_1.trackStandardPaymentFailure)(payload.orderId, payload.paymentId, publicKey, devMode, mockMode, checkoutUrl);
434
+ }
435
+ else {
436
+ if (devMode) {
437
+ console.error("[Glomo-RN-SDK Standard] Invalid payment failure payload from FlowWebView:", payload);
438
+ }
439
+ }
440
+ return;
441
+ }
442
+ // Handling pay via bank - bank connection successful event from the flow WebView
443
+ if (messageType === "pay_via_bank.bank.connection_successful") {
444
+ const payload = messageData === null || messageData === void 0 ? void 0 : messageData.payload;
445
+ if (devMode) {
446
+ console.log("[Glomo-RN-SDK Standard] Pay via bank - bank connection successful via FlowWebView:", payload);
447
+ }
448
+ (0, validation_1.safeCallback)(onPayViaBankBankConnectionSuccessful, [
449
+ {
450
+ bankIdentifier: (_b = payload === null || payload === void 0 ? void 0 : payload.bankIdentifier) !== null && _b !== void 0 ? _b : "",
451
+ cooldownPeriodInMinutes: (_c = payload === null || payload === void 0 ? void 0 : payload.cooldownPeriodInMinutes) !== null && _c !== void 0 ? _c : 0,
452
+ bankName: (_d = payload === null || payload === void 0 ? void 0 : payload.bankName) !== null && _d !== void 0 ? _d : "",
453
+ bankImageSrc: (_e = payload === null || payload === void 0 ? void 0 : payload.bankImageSrc) !== null && _e !== void 0 ? _e : "",
454
+ bankImageAlt: (_f = payload === null || payload === void 0 ? void 0 : payload.bankImageAlt) !== null && _f !== void 0 ? _f : "",
455
+ },
456
+ ], "onPayViaBankBankConnectionSuccessful", devMode);
457
+ (0, analytics_1.trackStandardPayViaBankBankConnectionSuccessful)(orderId, publicKey, (_g = payload === null || payload === void 0 ? void 0 : payload.bankIdentifier) !== null && _g !== void 0 ? _g : "", devMode, mockMode, checkoutUrl);
458
+ return;
459
+ }
460
+ // Handling glomoCheckoutJourneyTerminate from the flow WebView (pay via bank completion)
461
+ if (messageType === "glomoCheckoutJourneyTerminate") {
462
+ // Deduplication - both main and flow WebViews can emit this event
463
+ if (payViaBankCompletedHandledRef.current) {
464
+ if (devMode) {
465
+ console.log("[Glomo-RN-SDK Standard] Pay via bank completed already handled, ignoring duplicate");
466
+ }
467
+ return;
468
+ }
469
+ payViaBankCompletedHandledRef.current = true;
470
+ const payload = messageData === null || messageData === void 0 ? void 0 : messageData.payload;
471
+ if (devMode) {
472
+ console.log("[Glomo-RN-SDK Standard] Pay via bank completed via FlowWebView:", payload);
473
+ }
474
+ setShowFlowWebView(false);
475
+ setShowCheckout(false);
476
+ setFlowWebViewUrl("");
477
+ setStatus("pay_via_bank_completed");
478
+ (0, validation_1.safeCallback)(onPayViaBankCompleted, [{ status: (_h = payload === null || payload === void 0 ? void 0 : payload.status) !== null && _h !== void 0 ? _h : "unknown" }], "onPayViaBankCompleted", devMode);
479
+ (0, analytics_1.trackStandardPayViaBankCompleted)(orderId, publicKey, (_j = payload === null || payload === void 0 ? void 0 : payload.status) !== null && _j !== void 0 ? _j : "unknown", devMode, mockMode, checkoutUrl);
480
+ return;
481
+ }
482
+ }
483
+ }
484
+ catch (error) {
485
+ if (devMode) {
486
+ console.error("[Glomo-RN-SDK Standard] Error parsing FlowWebView message:", error);
487
+ }
488
+ }
489
+ }, [
490
+ onPaymentSuccess,
491
+ onPaymentFailure,
492
+ onPayViaBankCompleted,
493
+ onPayViaBankBankConnectionSuccessful,
494
+ devMode,
495
+ mockMode,
496
+ orderId,
497
+ publicKey,
498
+ flowWebViewUrl,
499
+ checkoutUrl,
500
+ ]);
501
+ // Handler for the navigation state changes
502
+ const handleNavigationStateChange = (0, react_1.useCallback)((navState) => {
503
+ if (devMode) {
504
+ console.log("[Glomo-RN-SDK Standard] Navigation state:", navState);
505
+ }
506
+ }, [devMode]);
507
+ // Handler for the modal back button press events
508
+ const handleModalBackButton = (0, react_1.useCallback)(() => {
509
+ try {
510
+ if (devMode) {
511
+ console.log("[Glomo-RN-SDK Standard] Modal dismissed/back button pressed");
512
+ }
513
+ if (showFlowWebView) {
514
+ setShowFlowWebView(false);
515
+ setFlowWebViewUrl("");
516
+ }
517
+ setShowCheckout(false);
518
+ if (status === "payment_in_progress") {
519
+ if (devMode) {
520
+ console.log("[Glomo-RN-SDK Standard] In progress payment cancelled");
521
+ }
522
+ setStatus("payment_cancelled");
523
+ }
524
+ else {
525
+ if (devMode) {
526
+ console.log("[Glomo-RN-SDK Standard] Ignoring checkout status update for: " + status);
527
+ }
528
+ }
529
+ (0, validation_1.safeCallback)(onPaymentTerminate, [], "onPaymentTerminate", devMode);
530
+ // Tracking payment terminate callback (user dismiss/back button)
531
+ const terminateSource = react_native_1.Platform.OS === "ios" ? "user_dismiss" : "back_button";
532
+ (0, analytics_1.trackStandardPaymentTerminate)(orderId, terminateSource, publicKey, devMode, mockMode, checkoutUrl);
533
+ }
534
+ catch (error) {
535
+ if (devMode) {
536
+ console.error("[Glomo-RN-SDK Standard] Error in handleModalBackButton:", error);
537
+ }
538
+ }
539
+ }, [showFlowWebView, devMode, onPaymentTerminate, status, orderId, publicKey, mockMode, checkoutUrl]);
540
+ /** Closes the flow WebView and returns to the main checkout */
541
+ const handleFlowBack = (0, react_1.useCallback)(() => {
542
+ if (devMode) {
543
+ console.log("[Glomo-RN-SDK Standard] Flow WebView back button pressed");
544
+ }
545
+ setShowFlowWebView(false);
546
+ setFlowWebViewUrl("");
547
+ (0, analytics_1.trackStandardWindowClose)("flow", orderId, publicKey, devMode, mockMode, checkoutUrl);
548
+ }, [devMode, orderId, publicKey, mockMode, checkoutUrl]);
549
+ /**
550
+ * Handler for WebView permission requests (e.g. camera access for bank authentication).
551
+ * On Android, prompts the user for camera permission via PermissionsAndroid.
552
+ * On iOS, grants camera access automatically (iOS handles its own permission prompt).
553
+ * Non-camera permissions are denied by default.
554
+ */
555
+ const handlePermissionRequest = (0, react_1.useCallback)(async (request) => {
556
+ try {
557
+ const { resources } = request.nativeEvent;
558
+ // Checking if the requested permission is camera-related
559
+ const isCameraPermissionRequest = resources.some((resource) => resource === "camera" || resource.toLowerCase().includes("camera"));
560
+ if (!isCameraPermissionRequest) {
561
+ if (devMode) {
562
+ console.log("[Glomo-RN-SDK Standard] Non-camera permission requested, denying:", resources);
563
+ }
564
+ request.deny();
565
+ return;
566
+ }
567
+ if (devMode) {
568
+ console.log("[Glomo-RN-SDK Standard] Camera permission requested by webpage");
569
+ }
570
+ if (react_native_1.Platform.OS === "android") {
571
+ try {
572
+ const granted = await react_native_1.PermissionsAndroid.request(react_native_1.PermissionsAndroid.PERMISSIONS.CAMERA, {
573
+ title: "Camera Permission",
574
+ message: "This app needs camera access for identity verification during checkout",
575
+ buttonNeutral: "Ask Me Later",
576
+ buttonNegative: "Cancel",
577
+ buttonPositive: "OK",
578
+ });
579
+ if (granted === react_native_1.PermissionsAndroid.RESULTS.GRANTED) {
580
+ if (devMode) {
581
+ console.log("[Glomo-RN-SDK Standard] Camera permission granted");
582
+ }
583
+ request.grant(resources);
584
+ }
585
+ else {
586
+ if (devMode) {
587
+ console.log("[Glomo-RN-SDK Standard] Camera permission denied by user");
588
+ }
589
+ setShowCheckout(false);
590
+ if (status === "payment_in_progress") {
591
+ setStatus("payment_cancelled");
592
+ }
593
+ (0, validation_1.safeCallback)(onUserRefusedCameraPermissions, [], "onUserRefusedCameraPermissions", devMode);
594
+ request.deny();
595
+ }
596
+ }
597
+ catch (err) {
598
+ if (devMode) {
599
+ console.error("[Glomo-RN-SDK Standard] Error requesting camera permission:", err);
600
+ }
601
+ setShowCheckout(false);
602
+ if (status === "payment_in_progress") {
603
+ setStatus("payment_cancelled");
604
+ }
605
+ (0, validation_1.safeCallback)(onUserRefusedCameraPermissions, [], "onUserRefusedCameraPermissions", devMode);
606
+ request.deny();
607
+ }
608
+ }
609
+ else if (react_native_1.Platform.OS === "ios") {
610
+ if (devMode) {
611
+ console.log("[Glomo-RN-SDK Standard] Granting camera permission request on iOS");
612
+ }
613
+ request.grant(resources);
614
+ }
615
+ else {
616
+ if (devMode) {
617
+ console.warn("[Glomo-RN-SDK Standard] Unsupported platform for camera permissions");
618
+ }
619
+ request.deny();
620
+ }
621
+ }
622
+ catch (error) {
623
+ if (devMode) {
624
+ console.error("[Glomo-RN-SDK Standard] Error in handlePermissionRequest:", error);
625
+ }
626
+ request.deny();
627
+ }
628
+ }, [devMode, status, onUserRefusedCameraPermissions]);
629
+ /**
630
+ * Initiator method for the Standard Checkout flow.
631
+ * Using a ref, the standard flow can be started by calling this method.
632
+ * Example:
633
+ * const checkoutRef = useRef<GlomoStandardCheckoutRef>(null);
634
+ * checkoutRef.current?.start();
635
+ *
636
+ * The standard checkout can only be started if
637
+ * - The status is "ready", "payment_cancelled", or "payment_failed"
638
+ * - The status is NOT "payment_successful"
639
+ * - Input validation passes
640
+ */
641
+ const start = (0, react_1.useCallback)(() => {
642
+ /**
643
+ * Checking device security compliance (if peerDependencies are available)
644
+ * A null value indicates that recommended security dependencies were not installed and compliance checks will be skipped in this case
645
+ */
646
+ const deviceComplianceCheck = (0, device_compliance_1.checkDeviceCompliance)(devMode);
647
+ const jailbreakDetectionLibraryInstalled = (0, device_compliance_1.isComplianceCheckAvailable)();
648
+ const jailbreakDetectionInfo = {
649
+ jailbreakDetectionLibraryInstalled,
650
+ jailbreakDetected: deviceComplianceCheck,
651
+ };
652
+ if (devMode) {
653
+ console.log(`[Glomo-RN-SDK Standard] Attempting to start checkout of ${checkoutUrl} from status: ${status}`);
654
+ }
655
+ // Tracking start attempt with current status
656
+ (0, analytics_1.trackStandardStartAttempt)(status, orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
657
+ if (deviceComplianceCheck === true) {
658
+ // Device detected as compromised
659
+ if (devMode) {
660
+ console.error("[Glomo-RN-SDK Standard] Cannot start checkout. Device is rooted or jailbroken.");
661
+ }
662
+ const errors = [
663
+ {
664
+ type: "device_forbidden",
665
+ message: "Standard checkout is not allowed on rooted or jailbroken devices.",
666
+ },
667
+ ];
668
+ (0, validation_1.safeCallback)(onSdkError, [errors], "onSdkError", devMode);
669
+ // Tracking start() failure
670
+ (0, analytics_1.trackStandardStartFailure)("device_forbidden", orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
671
+ setShowCheckout(false);
672
+ return false;
673
+ }
674
+ else if (deviceComplianceCheck === null) {
675
+ // This warning was intentionally not gated behind a devMode check to ensure merchants are aware of the strong recommendation to install jail-monkey for enhanced security compliance.
676
+ console.warn("[Glomo-RN-SDK Standard] Device security library not installed - the SDK won't be able to automatically detect rooted/jailbroken devices. It is HIGHLY recommended to install jail-monkey as per the README, and prevent standard checkouts from compromised devices.");
677
+ }
678
+ // Enforcing devMode requirement for development servers
679
+ const resolvedServer = server || "prod";
680
+ if (!devMode && resolvedServer !== "prod") {
681
+ const errors = [
682
+ {
683
+ type: "validation_error",
684
+ message: "Development servers can only be used when devMode is enabled.",
685
+ },
686
+ ];
687
+ (0, validation_1.safeCallback)(onSdkError, [errors], "onSdkError", devMode);
688
+ (0, analytics_1.trackStandardSdkError)(errors, orderId, publicKey, devMode, mockMode, checkoutUrl);
689
+ (0, analytics_1.trackStandardStartFailure)("validation_error", orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
690
+ setShowCheckout(false);
691
+ return false;
692
+ }
693
+ // Checking validation errors with detailed field-level messages
694
+ const errors = [];
695
+ if (!(0, validation_1.isValidPublicKey)(publicKey)) {
696
+ const error = {
697
+ type: "validation_error",
698
+ message: "Invalid publicKey: must start with 'live_', 'mock_', or 'test_' and be a valid string",
699
+ field: "publicKey",
700
+ };
701
+ errors.push(error);
702
+ }
703
+ if (!(0, validation_1.isValidOrderId)(orderId)) {
704
+ const error = {
705
+ type: "validation_error",
706
+ message: "Invalid orderId: must start with 'order_' and be a valid string",
707
+ field: "orderId",
708
+ };
709
+ errors.push(error);
710
+ }
711
+ if (!(0, validation_1.isValidUrl)(checkoutUrl)) {
712
+ const error = !(0, validation_1.isValidUrl)(baseCheckoutUrl)
713
+ ? {
714
+ type: "validation_error",
715
+ message: "Invalid baseCheckoutUrl: must be a valid HTTP or HTTPS URL",
716
+ field: "baseCheckoutUrl",
717
+ }
718
+ : {
719
+ type: "validation_error",
720
+ message: "Invalid checkoutUrl: must be a valid HTTP or HTTPS URL",
721
+ field: "generatedCheckoutUrl",
722
+ };
723
+ errors.push(error);
724
+ }
725
+ if (errors.length > 0) {
726
+ if (devMode) {
727
+ console.error(`[Glomo-RN-SDK Standard] Cannot start checkout. Validation failed.`);
728
+ }
729
+ (0, validation_1.safeCallback)(onSdkError, [errors], "onSdkError", devMode);
730
+ // Tracking start() failure
731
+ (0, analytics_1.trackStandardSdkError)(errors, orderId, publicKey, devMode, mockMode, checkoutUrl);
732
+ (0, analytics_1.trackStandardStartFailure)("validation_error", orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
733
+ setShowCheckout(false);
734
+ return false;
735
+ }
736
+ if (status === "payment_successful") {
737
+ if (devMode) {
738
+ console.log(`[Glomo-RN-SDK Standard] Cannot start checkout. Current status: ${status}. Payment already successful.`);
739
+ }
740
+ // Tracking start() failure
741
+ (0, analytics_1.trackStandardStartFailure)("payment_successful", orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
742
+ setShowCheckout(false);
743
+ return false;
744
+ }
745
+ if (status !== "ready" &&
746
+ status !== "payment_cancelled" &&
747
+ status !== "payment_failed" &&
748
+ status !== "payment_in_progress" &&
749
+ status !== "bank_transfer_submitted" &&
750
+ status !== "pay_via_bank_completed") {
751
+ if (devMode) {
752
+ console.log(`[Glomo-RN-SDK Standard] Cannot start checkout. Unknown status: ${status}.`);
753
+ }
754
+ // Tracking start() failure
755
+ (0, analytics_1.trackStandardStartFailure)("invalid_status", orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
756
+ setShowCheckout(false);
757
+ return false;
758
+ }
759
+ if (!checkoutUrl) {
760
+ if (devMode) {
761
+ console.error("[Glomo-RN-SDK Standard] Cannot start checkout. Invalid checkout URL.");
762
+ }
763
+ // Tracking start() failure
764
+ (0, analytics_1.trackStandardStartFailure)("invalid_url", orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
765
+ setShowCheckout(false);
766
+ return false;
767
+ }
768
+ if (devMode) {
769
+ console.log("[Glomo-RN-SDK Standard] Starting checkout from previous state: " + status);
770
+ }
771
+ // Resetting any stale state from a previous checkout attempt before starting a new one
772
+ setShowFlowWebView(false);
773
+ setFlowWebViewUrl("");
774
+ payViaBankCompletedHandledRef.current = false;
775
+ setStatus("payment_in_progress");
776
+ // Tracking successful start
777
+ (0, analytics_1.trackStandardStartSuccess)(orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
778
+ // Opening the checkout modal
779
+ setShowCheckout(true);
780
+ return true;
781
+ }, [devMode, status, publicKey, orderId, baseCheckoutUrl, checkoutUrl, mockMode, onSdkError, server]);
782
+ // Initializing Segment analytics
783
+ (0, react_1.useEffect)(() => {
784
+ (0, segment_1.initializeSegment)(devMode);
785
+ }, [devMode]);
786
+ // Resetting checkout flow if orderId or publicKey are changed
787
+ (0, react_1.useEffect)(() => {
788
+ if (orderId && publicKey) {
789
+ // Only resetting the checkout flow if the current status is that of a terminal state
790
+ if (status === "payment_successful" ||
791
+ status === "payment_failed" ||
792
+ status === "payment_cancelled" ||
793
+ status === "bank_transfer_submitted" ||
794
+ status === "pay_via_bank_completed") {
795
+ if (devMode) {
796
+ console.log(`[Glomo-RN-SDK Standard] Resetting checkout status ${status} for new orderId ${orderId} and publicKey ${publicKey}`);
797
+ }
798
+ setStatus("ready");
799
+ }
800
+ }
801
+ }, [orderId, publicKey, status, devMode]);
802
+ // Cleaning up on unmount
803
+ (0, react_1.useEffect)(() => {
804
+ return () => {
805
+ // Cleaning up - resetting state on unmount
806
+ setStatus("ready");
807
+ setShowCheckout(false);
808
+ setShowFlowWebView(false);
809
+ setFlowWebViewUrl("");
810
+ };
811
+ }, []);
812
+ return {
813
+ start,
814
+ status,
815
+ showCheckout,
816
+ flowWebViewUrl,
817
+ showFlowWebView,
818
+ checkoutUrl,
819
+ mainWebViewRef,
820
+ flowWebViewRef,
821
+ handleMainWebViewMessage,
822
+ handleFlowWebViewMessage,
823
+ handleError,
824
+ handleHttpError,
825
+ handlePermissionRequest,
826
+ handleNavigationStateChange,
827
+ handleModalBackButton,
828
+ handleFlowBack,
829
+ injectedMain: index_1.InjectionScripts.standard,
830
+ injectedFlow: index_1.InjectionScripts.flow,
831
+ };
832
+ }