@glomopay/react-native-sdk 4.0.0 → 5.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 (127) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/MIGRATION.md +112 -0
  3. package/README.md +173 -36
  4. package/lib/bridge/file-input-handler.d.ts +25 -0
  5. package/lib/bridge/file-input-handler.d.ts.map +1 -0
  6. package/lib/bridge/file-input-handler.js +38 -0
  7. package/lib/bridge/file-save-handler.d.ts +32 -0
  8. package/lib/bridge/file-save-handler.d.ts.map +1 -0
  9. package/lib/bridge/file-save-handler.js +92 -0
  10. package/lib/bridge/message-router.d.ts +44 -0
  11. package/lib/bridge/message-router.d.ts.map +1 -0
  12. package/lib/bridge/message-router.js +48 -0
  13. package/lib/bridge/payment-payload.d.ts +18 -0
  14. package/lib/bridge/payment-payload.d.ts.map +1 -0
  15. package/lib/bridge/payment-payload.js +31 -0
  16. package/lib/capabilities/file-save.d.ts +40 -0
  17. package/lib/capabilities/file-save.d.ts.map +1 -0
  18. package/lib/capabilities/file-save.js +101 -0
  19. package/lib/capabilities/index.d.ts +22 -0
  20. package/lib/capabilities/index.d.ts.map +1 -0
  21. package/lib/capabilities/index.js +34 -0
  22. package/lib/capabilities/use-capability-reporting.d.ts +24 -0
  23. package/lib/capabilities/use-capability-reporting.d.ts.map +1 -0
  24. package/lib/capabilities/use-capability-reporting.js +44 -0
  25. package/lib/config/base.d.ts +12 -0
  26. package/lib/config/base.d.ts.map +1 -1
  27. package/lib/config/base.js +13 -1
  28. package/lib/glomo-checkout.d.ts +9 -1
  29. package/lib/glomo-checkout.d.ts.map +1 -1
  30. package/lib/glomo-checkout.js +14 -5
  31. package/lib/glomo-lrs-checkout.d.ts +3 -0
  32. package/lib/glomo-lrs-checkout.d.ts.map +1 -1
  33. package/lib/glomo-lrs-checkout.js +34 -11
  34. package/lib/glomo-standard-checkout.d.ts +4 -1
  35. package/lib/glomo-standard-checkout.d.ts.map +1 -1
  36. package/lib/glomo-standard-checkout.js +21 -13
  37. package/lib/glomo-subscriptions-checkout.d.ts.map +1 -1
  38. package/lib/glomo-subscriptions-checkout.js +1 -1
  39. package/lib/index.d.ts +2 -2
  40. package/lib/index.d.ts.map +1 -1
  41. package/lib/index.js +4 -3
  42. package/lib/injections/index.d.ts +24 -0
  43. package/lib/injections/index.d.ts.map +1 -1
  44. package/lib/injections/index.js +41 -0
  45. package/lib/injections/webview-flow.injection.d.ts.map +1 -1
  46. package/lib/injections/webview-flow.injection.js +76 -0
  47. package/lib/injections/webview-main.injection.d.ts.map +1 -1
  48. package/lib/injections/webview-main.injection.js +31 -0
  49. package/lib/injections/webview-standard.injection.d.ts.map +1 -1
  50. package/lib/injections/webview-standard.injection.js +31 -0
  51. package/lib/services/file-saver.d.ts +109 -0
  52. package/lib/services/file-saver.d.ts.map +1 -0
  53. package/lib/services/file-saver.js +416 -0
  54. package/lib/types/checkout.d.ts +67 -6
  55. package/lib/types/checkout.d.ts.map +1 -1
  56. package/lib/types/checkout.js +24 -2
  57. package/lib/types/standard-checkout.d.ts +6 -6
  58. package/lib/types/standard-checkout.d.ts.map +1 -1
  59. package/lib/types/subscriptions-checkout.d.ts +5 -3
  60. package/lib/types/subscriptions-checkout.d.ts.map +1 -1
  61. package/lib/use-glomo-checkout.d.ts +2 -2
  62. package/lib/use-glomo-checkout.d.ts.map +1 -1
  63. package/lib/use-glomo-checkout.js +25 -4
  64. package/lib/use-lrs-checkout.d.ts +18 -6
  65. package/lib/use-lrs-checkout.d.ts.map +1 -1
  66. package/lib/use-lrs-checkout.js +330 -171
  67. package/lib/use-standard-checkout.d.ts +18 -13
  68. package/lib/use-standard-checkout.d.ts.map +1 -1
  69. package/lib/use-standard-checkout.js +282 -206
  70. package/lib/utils/analytics.d.ts +177 -51
  71. package/lib/utils/analytics.d.ts.map +1 -1
  72. package/lib/utils/analytics.js +299 -81
  73. package/lib/utils/device-compliance.d.ts +57 -12
  74. package/lib/utils/device-compliance.d.ts.map +1 -1
  75. package/lib/utils/device-compliance.js +101 -37
  76. package/lib/utils/event-projections.d.ts +56 -0
  77. package/lib/utils/event-projections.d.ts.map +1 -0
  78. package/lib/utils/event-projections.js +42 -0
  79. package/lib/utils/file-metadata.d.ts +52 -0
  80. package/lib/utils/file-metadata.d.ts.map +1 -0
  81. package/lib/utils/file-metadata.js +134 -0
  82. package/lib/utils/use-sdk-session.d.ts +12 -0
  83. package/lib/utils/use-sdk-session.d.ts.map +1 -0
  84. package/lib/utils/use-sdk-session.js +37 -0
  85. package/lib/utils/validation.d.ts +37 -3
  86. package/lib/utils/validation.d.ts.map +1 -1
  87. package/lib/utils/validation.js +64 -8
  88. package/lib/webview/capability-props.d.ts +27 -0
  89. package/lib/webview/capability-props.d.ts.map +1 -0
  90. package/lib/webview/capability-props.js +61 -0
  91. package/lib/webview/use-permission-request.d.ts +33 -0
  92. package/lib/webview/use-permission-request.d.ts.map +1 -0
  93. package/lib/webview/use-permission-request.js +89 -0
  94. package/package.json +13 -3
  95. package/src/bridge/file-input-handler.ts +66 -0
  96. package/src/bridge/file-save-handler.ts +141 -0
  97. package/src/bridge/message-router.ts +92 -0
  98. package/src/bridge/payment-payload.ts +31 -0
  99. package/src/capabilities/file-save.ts +124 -0
  100. package/src/capabilities/index.ts +34 -0
  101. package/src/capabilities/use-capability-reporting.ts +55 -0
  102. package/src/config/base.ts +13 -0
  103. package/src/glomo-checkout.tsx +28 -11
  104. package/src/glomo-lrs-checkout.tsx +49 -27
  105. package/src/glomo-standard-checkout.tsx +33 -34
  106. package/src/glomo-subscriptions-checkout.tsx +1 -0
  107. package/src/index.ts +4 -2
  108. package/src/injections/index.ts +43 -0
  109. package/src/injections/webview-flow.injection.ts +76 -0
  110. package/src/injections/webview-main.injection.ts +31 -0
  111. package/src/injections/webview-standard.injection.ts +31 -0
  112. package/src/services/file-saver.ts +497 -0
  113. package/src/types/checkout.ts +72 -6
  114. package/src/types/standard-checkout.ts +6 -6
  115. package/src/types/subscriptions-checkout.ts +9 -3
  116. package/src/use-glomo-checkout.tsx +32 -7
  117. package/src/use-lrs-checkout.tsx +684 -366
  118. package/src/use-standard-checkout.tsx +731 -465
  119. package/src/utils/analytics.ts +422 -117
  120. package/src/utils/device-compliance.ts +148 -38
  121. package/src/utils/event-projections.ts +74 -0
  122. package/src/utils/file-metadata.ts +140 -0
  123. package/src/utils/use-sdk-session.ts +41 -0
  124. package/src/utils/validation.ts +70 -10
  125. package/src/webview/capability-props.ts +86 -0
  126. package/src/webview/use-permission-request.ts +114 -0
  127. package/src/jail-monkey.d.ts +0 -19
@@ -8,10 +8,21 @@ const index_1 = require("./injections/index");
8
8
  const segment_1 = require("./config/segment");
9
9
  const base_1 = require("./config/base");
10
10
  const validation_1 = require("./utils/validation");
11
+ const payment_payload_1 = require("./bridge/payment-payload");
12
+ const event_projections_1 = require("./utils/event-projections");
11
13
  const device_compliance_1 = require("./utils/device-compliance");
14
+ const use_permission_request_1 = require("./webview/use-permission-request");
15
+ const message_router_1 = require("./bridge/message-router");
16
+ const file_save_handler_1 = require("./bridge/file-save-handler");
17
+ const file_input_handler_1 = require("./bridge/file-input-handler");
18
+ const index_2 = require("./capabilities/index");
19
+ const use_capability_reporting_1 = require("./capabilities/use-capability-reporting");
20
+ const use_sdk_session_1 = require("./utils/use-sdk-session");
12
21
  const analytics_1 = require("./utils/analytics");
13
- /** The main SDK hook for the GlomoPay LRS Checkout flow */
14
- function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymentFailure, onConnectionError, onPaymentTerminate, onSdkError, devMode = false, }, mockMode) {
22
+ const checkout_1 = require("./types/checkout");
23
+ /** Log prefix for everything this flow emits */
24
+ const LOG_PREFIX = "[Glomo-RN-SDK LRS]";
25
+ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymentFailure, onConnectionError, onPaymentTerminate, onSdkError, onUserRefusedDevicePermissions, devMode = false, }, mockMode, internalOptions) {
15
26
  // Refs for the WebViews
16
27
  const mainWebViewRef = (0, react_1.useRef)(null);
17
28
  const flowWebViewRef = (0, react_1.useRef)(null);
@@ -28,7 +39,7 @@ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymen
28
39
  // Building the query string manually since URLSearchParams is not fully supported in React Native
29
40
  const baseParams = [`orderId=${encodeURIComponent(orderId)}`];
30
41
  const mode = mockMode ? "mock" : "live";
31
- const params = baseParams.concat(`mode=${encodeURIComponent(mode)}`, `publicKey=${encodeURIComponent(publicKey)}`);
42
+ const params = baseParams.concat(`mode=${encodeURIComponent(mode)}`, `publicKey=${encodeURIComponent(publicKey)}`, `product=${encodeURIComponent(base_1.ANALYTICS_PRODUCT)}`, `surface=${encodeURIComponent(base_1.ANALYTICS_SURFACE)}`);
32
43
  const queryString = params.join("&");
33
44
  // Handling base URL with or without existing query parameters
34
45
  const separator = baseCheckoutUrl.includes("?") ? "&" : "?";
@@ -61,7 +72,9 @@ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymen
61
72
  const baseEducationCarouselUrl = serverConfig.baseUrl.lrsEducationCarousel;
62
73
  const separator = baseEducationCarouselUrl.includes("?") ? "&" : "?";
63
74
  const finalUrl = `${baseEducationCarouselUrl}${separator}orderId=${encodeURIComponent(orderId)}` +
64
- `&publicKey=${encodeURIComponent(publicKey)}`;
75
+ `&publicKey=${encodeURIComponent(publicKey)}` +
76
+ `&product=${encodeURIComponent(base_1.ANALYTICS_PRODUCT)}` +
77
+ `&surface=${encodeURIComponent(base_1.ANALYTICS_SURFACE)}`;
65
78
  if (!(0, validation_1.isValidUrl)(finalUrl)) {
66
79
  if (devMode) {
67
80
  console.error("[Glomo-RN-SDK LRS] Generated invalid education carousel URL:", finalUrl);
@@ -86,7 +99,17 @@ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymen
86
99
  const [showCheckout, setShowCheckout] = (0, react_1.useState)(false);
87
100
  const [showFlowWebView, setShowFlowWebView] = (0, react_1.useState)(false);
88
101
  const [flowWebViewUrl, setFlowWebViewUrl] = (0, react_1.useState)("");
89
- const [educationCarouselState, setEducationCarouselState] = (0, react_1.useState)("pending");
102
+ const [educationCarouselState, setEducationCarouselStateRaw] = (0, react_1.useState)("pending");
103
+ /** Wrapping the raw state setter to also fire the internal test callback */
104
+ const testCarouselCallbackRef = (0, react_1.useRef)(internalOptions === null || internalOptions === void 0 ? void 0 : internalOptions._onEducationCarouselStateChange);
105
+ testCarouselCallbackRef.current = internalOptions === null || internalOptions === void 0 ? void 0 : internalOptions._onEducationCarouselStateChange;
106
+ const setEducationCarouselState = (0, react_1.useCallback)((nextState) => {
107
+ var _a;
108
+ setEducationCarouselStateRaw(nextState);
109
+ if (__DEV__) {
110
+ (_a = testCarouselCallbackRef.current) === null || _a === void 0 ? void 0 : _a.call(testCarouselCallbackRef, nextState);
111
+ }
112
+ }, []);
90
113
  (0, react_1.useEffect)(() => {
91
114
  if (devMode) {
92
115
  console.log(`[Glomo-RN-SDK LRS] Carousel state snapshot: status=${status}, showCheckout=${showCheckout}, showFlowWebView=${showFlowWebView}, flowWebViewUrl=${flowWebViewUrl || "<empty>"}, educationCarouselState=${educationCarouselState}, educationCarouselUrl=${educationCarouselUrl || "<empty>"}`);
@@ -158,35 +181,62 @@ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymen
158
181
  }
159
182
  }
160
183
  }, [devMode]);
184
+ // Correlates every event from this mount, and keeps checkoutStatus current on all of them
185
+ (0, use_sdk_session_1.useSdkSession)(() => status);
186
+ // Missing optional dependencies reach the host here; start() warns on the console separately
187
+ (0, use_capability_reporting_1.useCapabilityReporting)({ onSdkError, devMode });
188
+ /**
189
+ * Handler for `file.input` - the user tapping a file input on a bank's page.
190
+ *
191
+ * Registered beside `file.save` on the same WebViews, for the same reason: uploads are not a
192
+ * property of one flow, and until now this message had no handler anywhere.
193
+ */
194
+ const fileInputHandler = (0, react_1.useMemo)(() => (0, file_input_handler_1.createFileInputHandler)({
195
+ flowType: "lrs",
196
+ orderId,
197
+ publicKey,
198
+ checkoutUrl,
199
+ mockMode,
200
+ devMode,
201
+ }), [orderId, publicKey, checkoutUrl, mockMode, devMode]);
202
+ /**
203
+ * Handler for `file.save` - the page asking the SDK to put a document on the device.
204
+ *
205
+ * One handler, registered on the main and flow WebViews both. Not on the education carousel:
206
+ * that renders our own content, which has nothing to gate, and the capability is not advertised
207
+ * there either.
208
+ */
209
+ const fileSaveHandler = (0, react_1.useMemo)(() => (0, file_save_handler_1.createFileSaveHandler)({
210
+ flowType: "lrs",
211
+ orderId,
212
+ publicKey,
213
+ checkoutUrl,
214
+ mockMode,
215
+ devMode,
216
+ onSdkError,
217
+ }), [orderId, publicKey, checkoutUrl, mockMode, devMode, onSdkError]);
161
218
  // Handler for the main WebView messages
162
- const handleMainWebViewMessage = (0, react_1.useCallback)((event) => {
163
- var _a;
164
- try {
165
- const data = (_a = event.nativeEvent) === null || _a === void 0 ? void 0 : _a.data;
166
- if (!data || typeof data !== "string") {
167
- if (devMode) {
168
- console.warn("[Glomo-RN-SDK LRS] Invalid message data from MainWebView");
169
- }
170
- // Tracking invalid message received
171
- (0, analytics_1.trackInvalidMessageReceived)("main", data, checkoutUrl, orderId, publicKey, devMode, mockMode, checkoutUrl);
172
- return;
173
- }
174
- const message = JSON.parse(data);
175
- if (devMode) {
176
- console.log("[Glomo-RN-SDK LRS] MainWebView Message received:", message.type, message);
177
- }
178
- if (message.type === "console") {
219
+ const handleMainWebViewMessage = (0, react_1.useMemo)(() => (0, message_router_1.createMessageRouter)({
220
+ logPrefix: LOG_PREFIX,
221
+ source: "MainWebView",
222
+ devMode,
223
+ onInvalidMessage: (data) => {
224
+ (0, analytics_1.trackInvalidMessageReceived)("main", data, checkoutUrl, orderId, publicKey, devMode, mockMode, checkoutUrl);
225
+ },
226
+ handlers: {
227
+ "file.save": fileSaveHandler,
228
+ "file.input": fileInputHandler,
229
+ console: (message) => {
179
230
  if (devMode) {
180
- const level = message.level || "log";
181
- console.log(`[Glomo-RN-SDK LRS] [WebView ${level.toUpperCase()}]`, message.message);
231
+ const level = (typeof message.level === "string" && message.level) || "log";
232
+ console.log(`${LOG_PREFIX} [WebView ${level.toUpperCase()}]`, message.message);
182
233
  }
183
- return;
184
- }
185
- if (message.type === "window.open") {
234
+ },
235
+ "window.open": (message) => {
186
236
  const url = message.url;
187
237
  if (url && typeof url === "string" && (0, validation_1.isValidUrl)(url)) {
188
238
  if (devMode) {
189
- console.log(`[Glomo-RN-SDK LRS] window.open called from MainWebView: ${url}`);
239
+ console.log(`${LOG_PREFIX} window.open called from MainWebView: ${url}`);
190
240
  }
191
241
  setFlowWebViewUrl(url);
192
242
  setShowFlowWebView(true);
@@ -196,32 +246,31 @@ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymen
196
246
  }
197
247
  else {
198
248
  if (devMode) {
199
- console.log("[Glomo-RN-SDK LRS] Invalid URL in window.open from MainWebView:", url, typeof url === "undefined" ? "undefined" : "");
249
+ console.log(`${LOG_PREFIX} Invalid URL in window.open from MainWebView:`, url, typeof url === "undefined" ? "undefined" : "");
200
250
  }
201
251
  }
202
- return;
203
- }
204
- if (message.type === "window.close") {
252
+ },
253
+ "window.close": () => {
205
254
  if (devMode) {
206
- console.log("[Glomo-RN-SDK LRS] Child window close requested from MainWebView");
255
+ console.log(`${LOG_PREFIX} Child window close requested from MainWebView`);
207
256
  }
208
257
  setShowFlowWebView(false);
209
258
  setFlowWebViewUrl("");
210
259
  setEducationCarouselState("pending");
211
260
  // Tracking window.close interception
212
261
  (0, analytics_1.trackWindowClose)("main", orderId, publicKey, devMode, mockMode, checkoutUrl);
213
- return;
214
- }
262
+ },
215
263
  // Handling merchant JS callbacks on flow completion
216
- if (message.type === "message" && message.message) {
264
+ message: (message) => {
217
265
  const messageData = message.message;
218
- const messageType = messageData === null || messageData === void 0 ? void 0 : messageData.type;
266
+ if (!messageData)
267
+ return;
268
+ const messageType = messageData.type;
219
269
  if (messageType === "payment.success" || messageType === "payment.complete") {
220
- const paymentMessage = messageData;
221
- const payload = paymentMessage === null || paymentMessage === void 0 ? void 0 : paymentMessage.payload;
270
+ const payload = messageData.payload;
222
271
  if ((0, validation_1.isValidPaymentPayload)(payload)) {
223
272
  if (devMode) {
224
- console.log("[Glomo-RN-SDK LRS] Payment successful via merchant callback!");
273
+ console.log(`${LOG_PREFIX} Payment successful via merchant callback!`);
225
274
  }
226
275
  setShowCheckout(false);
227
276
  setShowFlowWebView(false);
@@ -230,21 +279,32 @@ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymen
230
279
  setStatus("payment_successful");
231
280
  (0, validation_1.safeCallback)(onPaymentSuccess, [payload], "onPaymentSuccess", devMode);
232
281
  // Tracking payment success callback
233
- (0, analytics_1.trackPaymentSuccess)(payload.orderId, payload.paymentId, publicKey, devMode, mockMode, checkoutUrl);
282
+ (0, analytics_1.trackPaymentSuccess)((0, event_projections_1.toPaymentEventData)(payload), publicKey, devMode, mockMode, checkoutUrl);
283
+ // Fires alongside the callback, not instead of it - the outcome is real, the payload is thin
284
+ const missingFields = (0, validation_1.missingPaymentFields)(payload);
285
+ if (missingFields.length > 0) {
286
+ (0, analytics_1.trackIncompletePaymentPayload)("payment.success", missingFields, "main", payload.orderId, publicKey, devMode, mockMode, checkoutUrl, "lrs");
287
+ }
234
288
  }
235
289
  else {
290
+ /**
291
+ * Reported, not dropped. Before v5 this branch was a devMode-only console.error, so in a
292
+ * release build a terminal payment outcome the SDK could not route left no trace
293
+ * anywhere - no callback, no telemetry, nothing for support to find.
294
+ */
236
295
  if (devMode) {
237
- console.error("[Glomo-RN-SDK LRS] Invalid payment success payload:", payload);
296
+ console.error(`${LOG_PREFIX} Unroutable payment.success payload from main:`, payload);
238
297
  }
298
+ (0, analytics_1.trackDroppedPaymentPayload)("payment.success", (0, payment_payload_1.payloadKeysOf)(payload), "main", orderId, publicKey, devMode, mockMode, checkoutUrl, "lrs");
299
+ (0, validation_1.safeCallback)(onSdkError, [[(0, payment_payload_1.droppedPayloadSdkError)("payment.success")]], "onSdkError", devMode);
239
300
  }
240
301
  return;
241
302
  }
242
303
  if (messageType === "payment.failure" || messageType === "payment.error") {
243
- const paymentMessage = messageData;
244
- const payload = paymentMessage === null || paymentMessage === void 0 ? void 0 : paymentMessage.payload;
304
+ const payload = messageData.payload;
245
305
  if ((0, validation_1.isValidPaymentPayload)(payload)) {
246
306
  if (devMode) {
247
- console.error("[Glomo-RN-SDK LRS] Payment failed via merchant callback");
307
+ console.error(`${LOG_PREFIX} Payment failed via merchant callback`);
248
308
  }
249
309
  setShowCheckout(false);
250
310
  setShowFlowWebView(false);
@@ -253,65 +313,86 @@ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymen
253
313
  setStatus("payment_failed");
254
314
  (0, validation_1.safeCallback)(onPaymentFailure, [payload], "onPaymentFailure", devMode);
255
315
  // Tracking payment failure callback
256
- (0, analytics_1.trackPaymentFailure)(payload.orderId, payload.paymentId, publicKey, devMode, mockMode, checkoutUrl);
316
+ (0, analytics_1.trackPaymentFailure)((0, event_projections_1.toPaymentEventData)(payload), publicKey, devMode, mockMode, checkoutUrl);
317
+ // Fires alongside the callback, not instead of it - the outcome is real, the payload is thin
318
+ const missingFields = (0, validation_1.missingPaymentFields)(payload);
319
+ if (missingFields.length > 0) {
320
+ (0, analytics_1.trackIncompletePaymentPayload)("payment.failure", missingFields, "main", payload.orderId, publicKey, devMode, mockMode, checkoutUrl, "lrs");
321
+ }
257
322
  }
258
323
  else {
324
+ /**
325
+ * Reported, not dropped. Before v5 this branch was a devMode-only console.error, so in a
326
+ * release build a terminal payment outcome the SDK could not route left no trace
327
+ * anywhere - no callback, no telemetry, nothing for support to find.
328
+ */
259
329
  if (devMode) {
260
- console.error("[Glomo-RN-SDK LRS] Invalid payment failure payload:", payload);
330
+ console.error(`${LOG_PREFIX} Unroutable payment.failure payload from main:`, payload);
261
331
  }
332
+ (0, analytics_1.trackDroppedPaymentPayload)("payment.failure", (0, payment_payload_1.payloadKeysOf)(payload), "main", orderId, publicKey, devMode, mockMode, checkoutUrl, "lrs");
333
+ (0, validation_1.safeCallback)(onSdkError, [[(0, payment_payload_1.droppedPayloadSdkError)("payment.failure")]], "onSdkError", devMode);
262
334
  }
263
335
  return;
264
336
  }
265
337
  // Handling checkout.closed events
266
338
  if (messageType === "checkout.closed") {
267
339
  if (devMode) {
268
- console.log("[Glomo-RN-SDK LRS] Checkout closed event received");
340
+ console.log(`${LOG_PREFIX} Checkout closed event received`);
269
341
  }
270
342
  setShowCheckout(false);
271
343
  setShowFlowWebView(false);
272
344
  setFlowWebViewUrl("");
273
345
  setEducationCarouselState("pending");
274
- (0, validation_1.safeCallback)(onPaymentTerminate, [], "onPaymentTerminate", devMode);
275
- // Tracking payment terminate callback
276
- (0, analytics_1.trackPaymentTerminate)(orderId, "checkout_closed", publicKey, devMode, mockMode, checkoutUrl);
277
- return;
346
+ const termination = { source: checkout_1.TERMINATION_SOURCES.CHECKOUT_CLOSED };
347
+ (0, validation_1.safeCallback)(onPaymentTerminate, [termination], "onPaymentTerminate", devMode);
348
+ // One object to the host and to telemetry, so the two cannot drift
349
+ (0, analytics_1.trackPaymentTerminate)(orderId, (0, event_projections_1.toTerminationEventData)(termination), publicKey, devMode, mockMode, checkoutUrl);
278
350
  }
279
- }
280
- }
281
- catch (error) {
282
- if (devMode) {
283
- console.error("[Glomo-RN-SDK LRS] Error parsing MainWebView message:", error);
284
- }
285
- }
286
- }, [onPaymentSuccess, onPaymentFailure, onPaymentTerminate, devMode, mockMode, orderId, publicKey, checkoutUrl]);
351
+ },
352
+ },
353
+ }), [
354
+ onPaymentSuccess,
355
+ onPaymentFailure,
356
+ onPaymentTerminate,
357
+ devMode,
358
+ mockMode,
359
+ orderId,
360
+ publicKey,
361
+ checkoutUrl,
362
+ fileSaveHandler,
363
+ onSdkError,
364
+ fileInputHandler,
365
+ ]);
287
366
  // Handler for the flow WebView messages
288
- const handleFlowWebViewMessage = (0, react_1.useCallback)((event) => {
289
- var _a;
290
- try {
291
- const data = (_a = event.nativeEvent) === null || _a === void 0 ? void 0 : _a.data;
292
- if (!data || typeof data !== "string") {
293
- if (devMode) {
294
- console.warn("[Glomo-RN-SDK LRS] Invalid message data from FlowWebView");
295
- }
296
- // Tracking invalid message received
297
- (0, analytics_1.trackInvalidMessageReceived)("flow", data, flowWebViewUrl, orderId, publicKey, devMode, mockMode, checkoutUrl);
298
- return;
299
- }
300
- const message = JSON.parse(data);
301
- if (devMode) {
302
- console.log("[Glomo-RN-SDK LRS] FlowWebView Message received:", message.type, message);
303
- }
304
- if (message.type === "console") {
367
+ const handleFlowWebViewMessage = (0, react_1.useMemo)(() => (0, message_router_1.createMessageRouter)({
368
+ logPrefix: LOG_PREFIX,
369
+ source: "FlowWebView",
370
+ devMode,
371
+ onInvalidMessage: (data) => {
372
+ (0, analytics_1.trackInvalidMessageReceived)("flow", data, flowWebViewUrl, orderId, publicKey, devMode, mockMode, checkoutUrl);
373
+ },
374
+ handlers: {
375
+ /**
376
+ * Telemetry only, and deliberately not wired to onUserRefusedDevicePermissions.
377
+ * The flow injection sees this by watching getUserMedia, so coverage depends on
378
+ * whether a given bank uses it - partial coverage on a merchant callback is
379
+ * worse than none, because silence would stop being unambiguous.
380
+ */
381
+ "permission.denied": (message) => {
382
+ (0, analytics_1.trackDevicePermissionRefused)(typeof message.resource === "string" ? message.resource : "camera", "get_user_media", orderId, publicKey, devMode, mockMode, checkoutUrl, "lrs");
383
+ },
384
+ "file.save": fileSaveHandler,
385
+ "file.input": fileInputHandler,
386
+ console: (message) => {
305
387
  if (devMode) {
306
- console.log(`[Glomo-RN-SDK LRS] [Flow] ${message.message}`);
388
+ console.log(`${LOG_PREFIX} [Flow] ${message.message}`);
307
389
  }
308
- return;
309
- }
310
- if (message.type === "window.open") {
390
+ },
391
+ "window.open": (message) => {
311
392
  const url = message.url;
312
393
  if (url && typeof url === "string" && (0, validation_1.isValidUrl)(url)) {
313
394
  if (devMode) {
314
- console.log(`[Glomo-RN-SDK LRS] window.open called from FlowWebView: ${url}`);
395
+ console.log(`${LOG_PREFIX} window.open called from FlowWebView: ${url}`);
315
396
  }
316
397
  setFlowWebViewUrl(url);
317
398
  setEducationCarouselState("pending");
@@ -320,31 +401,31 @@ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymen
320
401
  }
321
402
  else {
322
403
  if (devMode) {
323
- console.log("[Glomo-RN-SDK LRS] Invalid URL in window.open from FlowWebView:", url, typeof url === "undefined" ? "undefined" : "");
404
+ console.log(`${LOG_PREFIX} Invalid URL in window.open from FlowWebView:`, url, typeof url === "undefined" ? "undefined" : "");
324
405
  }
325
406
  }
326
- return;
327
- }
328
- if (message.type === "window.close") {
407
+ },
408
+ "window.close": () => {
329
409
  if (devMode) {
330
- console.log("[Glomo-RN-SDK LRS] FlowWebView close requested");
410
+ console.log(`${LOG_PREFIX} FlowWebView close requested`);
331
411
  }
332
412
  setShowFlowWebView(false);
333
413
  setFlowWebViewUrl("");
334
414
  setEducationCarouselState("pending");
335
415
  // Tracking window.close interception
336
416
  (0, analytics_1.trackWindowClose)("flow", orderId, publicKey, devMode, mockMode, checkoutUrl);
337
- return;
338
- }
339
- if (message.type === "message" && message.message) {
417
+ },
418
+ // Handling merchant JS callbacks on flow completion
419
+ message: (message) => {
340
420
  const messageData = message.message;
341
- const messageType = messageData === null || messageData === void 0 ? void 0 : messageData.type;
421
+ if (!messageData)
422
+ return;
423
+ const messageType = messageData.type;
342
424
  if (messageType === "payment.success" || messageType === "payment.complete") {
343
- const paymentMessage = messageData;
344
- const payload = paymentMessage === null || paymentMessage === void 0 ? void 0 : paymentMessage.payload;
425
+ const payload = messageData.payload;
345
426
  if ((0, validation_1.isValidPaymentPayload)(payload)) {
346
427
  if (devMode) {
347
- console.log("[Glomo-RN-SDK LRS] Payment successful via FlowWebView callback!");
428
+ console.log(`${LOG_PREFIX} Payment successful via FlowWebView callback!`);
348
429
  }
349
430
  setShowFlowWebView(false);
350
431
  setShowCheckout(false);
@@ -353,21 +434,32 @@ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymen
353
434
  setStatus("payment_successful");
354
435
  (0, validation_1.safeCallback)(onPaymentSuccess, [payload], "onPaymentSuccess", devMode);
355
436
  // Tracking payment success callback
356
- (0, analytics_1.trackPaymentSuccess)(payload.orderId, payload.paymentId, publicKey, devMode, mockMode, checkoutUrl);
437
+ (0, analytics_1.trackPaymentSuccess)((0, event_projections_1.toPaymentEventData)(payload), publicKey, devMode, mockMode, checkoutUrl);
438
+ // Fires alongside the callback, not instead of it - the outcome is real, the payload is thin
439
+ const missingFields = (0, validation_1.missingPaymentFields)(payload);
440
+ if (missingFields.length > 0) {
441
+ (0, analytics_1.trackIncompletePaymentPayload)("payment.success", missingFields, "flow", payload.orderId, publicKey, devMode, mockMode, checkoutUrl, "lrs");
442
+ }
357
443
  }
358
444
  else {
445
+ /**
446
+ * Reported, not dropped. Before v5 this branch was a devMode-only console.error, so in a
447
+ * release build a terminal payment outcome the SDK could not route left no trace
448
+ * anywhere - no callback, no telemetry, nothing for support to find.
449
+ */
359
450
  if (devMode) {
360
- console.error("[Glomo-RN-SDK LRS] Invalid payment success payload from FlowWebView:", payload);
451
+ console.error(`${LOG_PREFIX} Unroutable payment.success payload from flow:`, payload);
361
452
  }
453
+ (0, analytics_1.trackDroppedPaymentPayload)("payment.success", (0, payment_payload_1.payloadKeysOf)(payload), "flow", orderId, publicKey, devMode, mockMode, checkoutUrl, "lrs");
454
+ (0, validation_1.safeCallback)(onSdkError, [[(0, payment_payload_1.droppedPayloadSdkError)("payment.success")]], "onSdkError", devMode);
362
455
  }
363
456
  return;
364
457
  }
365
458
  if (messageType === "payment.failure" || messageType === "payment.error") {
366
- const paymentMessage = messageData;
367
- const payload = paymentMessage === null || paymentMessage === void 0 ? void 0 : paymentMessage.payload;
459
+ const payload = messageData.payload;
368
460
  if ((0, validation_1.isValidPaymentPayload)(payload)) {
369
461
  if (devMode) {
370
- console.error("[Glomo-RN-SDK LRS] Payment failed via FlowWebView callback");
462
+ console.error(`${LOG_PREFIX} Payment failed via FlowWebView callback`);
371
463
  }
372
464
  setShowFlowWebView(false);
373
465
  setShowCheckout(false);
@@ -376,60 +468,61 @@ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymen
376
468
  setStatus("payment_failed");
377
469
  (0, validation_1.safeCallback)(onPaymentFailure, [payload], "onPaymentFailure", devMode);
378
470
  // Tracking payment failure callback
379
- (0, analytics_1.trackPaymentFailure)(payload.orderId, payload.paymentId, publicKey, devMode, mockMode, checkoutUrl);
471
+ (0, analytics_1.trackPaymentFailure)((0, event_projections_1.toPaymentEventData)(payload), publicKey, devMode, mockMode, checkoutUrl);
472
+ // Fires alongside the callback, not instead of it - the outcome is real, the payload is thin
473
+ const missingFields = (0, validation_1.missingPaymentFields)(payload);
474
+ if (missingFields.length > 0) {
475
+ (0, analytics_1.trackIncompletePaymentPayload)("payment.failure", missingFields, "flow", payload.orderId, publicKey, devMode, mockMode, checkoutUrl, "lrs");
476
+ }
380
477
  }
381
478
  else {
479
+ /**
480
+ * Reported, not dropped. Before v5 this branch was a devMode-only console.error, so in a
481
+ * release build a terminal payment outcome the SDK could not route left no trace
482
+ * anywhere - no callback, no telemetry, nothing for support to find.
483
+ */
382
484
  if (devMode) {
383
- console.error("[Glomo-RN-SDK LRS] Invalid payment failure payload from FlowWebView:", payload);
485
+ console.error(`${LOG_PREFIX} Unroutable payment.failure payload from flow:`, payload);
384
486
  }
487
+ (0, analytics_1.trackDroppedPaymentPayload)("payment.failure", (0, payment_payload_1.payloadKeysOf)(payload), "flow", orderId, publicKey, devMode, mockMode, checkoutUrl, "lrs");
488
+ (0, validation_1.safeCallback)(onSdkError, [[(0, payment_payload_1.droppedPayloadSdkError)("payment.failure")]], "onSdkError", devMode);
385
489
  }
386
- return;
387
490
  }
388
- }
389
- }
390
- catch (error) {
391
- if (devMode) {
392
- console.error("[Glomo-RN-SDK LRS] Error parsing FlowWebView message:", error);
393
- }
394
- }
395
- }, [onPaymentSuccess, onPaymentFailure, devMode, mockMode, orderId, publicKey, flowWebViewUrl, checkoutUrl]);
491
+ },
492
+ },
493
+ }), [
494
+ onPaymentSuccess,
495
+ onPaymentFailure,
496
+ devMode,
497
+ mockMode,
498
+ orderId,
499
+ publicKey,
500
+ flowWebViewUrl,
501
+ checkoutUrl,
502
+ fileSaveHandler,
503
+ onSdkError,
504
+ fileInputHandler,
505
+ ]);
396
506
  // Handler for the education carousel WebView messages
397
- const handleEducationCarouselWebViewMessage = (0, react_1.useCallback)((event) => {
398
- var _a;
399
- try {
400
- const data = (_a = event.nativeEvent) === null || _a === void 0 ? void 0 : _a.data;
401
- if (!data || typeof data !== "string") {
402
- if (devMode) {
403
- console.warn("[Glomo-RN-SDK LRS] Invalid message data from EducationCarouselWebView");
404
- }
405
- return;
406
- }
407
- const message = JSON.parse(data);
408
- if (devMode) {
409
- console.log("[Glomo-RN-SDK LRS] EducationCarouselWebView Message received:", message.type, message);
410
- }
411
- if (message.type === "console") {
507
+ const handleEducationCarouselWebViewMessage = (0, react_1.useMemo)(() => (0, message_router_1.createMessageRouter)({
508
+ logPrefix: LOG_PREFIX,
509
+ source: "EducationCarouselWebView",
510
+ devMode,
511
+ handlers: {
512
+ console: (message) => {
412
513
  if (devMode) {
413
- console.log(`[Glomo-RN-SDK LRS] [EducationCarousel] ${message.message}`);
514
+ console.log(`${LOG_PREFIX} [EducationCarousel] ${message.message}`);
414
515
  }
415
- return;
416
- }
417
- if (message.type === "lrs.has_education_steps") {
418
- const nextState = "hasContent";
516
+ },
517
+ "lrs.has_education_steps": (message) => {
419
518
  if (devMode) {
420
- console.log(`[Glomo-RN-SDK LRS] Education carousel signal received: value=${String(message.value)} source=${message.source || "unknown"} payload=${JSON.stringify(message.payload || {})}`);
519
+ console.log(`${LOG_PREFIX} Education carousel signal received: value=${String(message.value)} source=${message.source || "unknown"} payload=${JSON.stringify(message.payload || {})}`);
421
520
  }
422
- (0, analytics_1.trackEducationStepsShown)(orderId, publicKey, devMode, mockMode, checkoutUrl, message.source);
423
- setEducationCarouselState(nextState);
424
- return;
425
- }
426
- }
427
- catch (error) {
428
- if (devMode) {
429
- console.error("[Glomo-RN-SDK LRS] Error parsing EducationCarouselWebView message:", error);
430
- }
431
- }
432
- }, [checkoutUrl, devMode, mockMode, orderId, publicKey]);
521
+ (0, analytics_1.trackEducationStepsShown)(orderId, publicKey, devMode, mockMode, checkoutUrl, typeof message.source === "string" ? message.source : undefined);
522
+ setEducationCarouselState("hasContent");
523
+ },
524
+ },
525
+ }), [checkoutUrl, devMode, mockMode, orderId, publicKey]);
433
526
  // Handler for the navigation state changes
434
527
  const handleNavigationStateChange = (0, react_1.useCallback)((navState) => {
435
528
  if (devMode) {
@@ -460,10 +553,18 @@ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymen
460
553
  console.log("[Glomo-RN-SDK LRS] Ignoring checkout status update for: " + status);
461
554
  }
462
555
  }
463
- (0, validation_1.safeCallback)(onPaymentTerminate, [], "onPaymentTerminate", devMode);
464
- // Tracking payment terminate callback (user dismiss/back button)
465
- const terminateSource = react_native_1.Platform.OS === "ios" ? "user_dismiss" : "back_button";
466
- (0, analytics_1.trackPaymentTerminate)(orderId, terminateSource, publicKey, devMode, mockMode, checkoutUrl);
556
+ /**
557
+ * Inferred from the platform rather than read off an event, because `onRequestClose` is
558
+ * the only wiring here and the two platforms raise it for different gestures: Android
559
+ * for hardware back and the back gesture, iOS for swipe-down on the pageSheet. Nothing
560
+ * else routes through it, so the inference is sound - but it is an inference.
561
+ */
562
+ const termination = {
563
+ source: react_native_1.Platform.OS === "ios" ? checkout_1.TERMINATION_SOURCES.MODAL_DISMISS : checkout_1.TERMINATION_SOURCES.DEVICE_BACK_BUTTON,
564
+ };
565
+ (0, validation_1.safeCallback)(onPaymentTerminate, [termination], "onPaymentTerminate", devMode);
566
+ // One object to the host and to telemetry, so the two cannot drift
567
+ (0, analytics_1.trackPaymentTerminate)(orderId, (0, event_projections_1.toTerminationEventData)(termination), publicKey, devMode, mockMode, checkoutUrl);
467
568
  }
468
569
  catch (error) {
469
570
  if (devMode) {
@@ -471,6 +572,28 @@ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymen
471
572
  }
472
573
  }
473
574
  }, [showFlowWebView, devMode, onPaymentTerminate, status, orderId, publicKey, mockMode, checkoutUrl]);
575
+ /**
576
+ * Closing the checkout when the user refuses camera access, since the bank page cannot
577
+ * proceed without it. Kept here rather than inside usePermissionRequest because moving the
578
+ * status and notifying the merchant are flow concerns.
579
+ */
580
+ const handleDevicePermissionDenied = (0, react_1.useCallback)(() => {
581
+ setShowCheckout(false);
582
+ setShowFlowWebView(false);
583
+ setFlowWebViewUrl("");
584
+ setEducationCarouselState("pending");
585
+ if (status === "payment_in_progress") {
586
+ setStatus("payment_cancelled");
587
+ }
588
+ (0, validation_1.safeCallback)(onUserRefusedDevicePermissions, [], "onUserRefusedDevicePermissions", devMode);
589
+ // The permission hook only reaches here for camera; anything else is denied without notifying
590
+ (0, analytics_1.trackDevicePermissionRefused)("camera", "permission_request", orderId, publicKey, devMode, mockMode, checkoutUrl, "lrs");
591
+ }, [status, onUserRefusedDevicePermissions, devMode, orderId, publicKey, mockMode, checkoutUrl]);
592
+ const handlePermissionRequest = (0, use_permission_request_1.usePermissionRequest)({
593
+ logPrefix: "[Glomo-RN-SDK LRS]",
594
+ onDenied: handleDevicePermissionDenied,
595
+ devMode,
596
+ });
474
597
  /** Closes the flow WebView and returns to the main checkout */
475
598
  const handleFlowBack = (0, react_1.useCallback)(() => {
476
599
  if (devMode) {
@@ -479,7 +602,7 @@ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymen
479
602
  setShowFlowWebView(false);
480
603
  setFlowWebViewUrl("");
481
604
  setEducationCarouselState("pending");
482
- (0, analytics_1.trackWindowClose)("flow", orderId, publicKey, devMode, mockMode, checkoutUrl);
605
+ (0, analytics_1.trackBackNavigationViaSdkBackButton)(orderId, publicKey, devMode, mockMode, checkoutUrl, "lrs");
483
606
  }, [devMode, orderId, publicKey, mockMode, checkoutUrl]);
484
607
  /**
485
608
  * Initiator method for the LRS Checkout flow.
@@ -498,38 +621,62 @@ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymen
498
621
  * Checking device security compliance (if peerDependencies are available)
499
622
  * A null value indicates that recommended security dependencies were not installed and compliance checks will be skipped in this case
500
623
  */
501
- const deviceComplianceCheck = (0, device_compliance_1.checkDeviceCompliance)(devMode);
502
- const jailbreakDetectionLibraryInstalled = (0, device_compliance_1.isComplianceCheckAvailable)();
503
- const jailbreakDetectionInfo = {
504
- jailbreakDetectionLibraryInstalled,
505
- jailbreakDetected: deviceComplianceCheck,
624
+ const compliance = (0, device_compliance_1.resolveDeviceCompliance)(devMode);
625
+ const complianceLibraryInstalled = (0, device_compliance_1.isComplianceCheckAvailable)();
626
+ const fileSave = (0, index_2.resolveFileSaveAvailability)();
627
+ const dependencyInfo = {
628
+ jailbreakDetectionLibraryInstalled: complianceLibraryInstalled,
629
+ jailbreakDetected: compliance.compromised,
630
+ complianceSource: compliance.source,
631
+ complianceSignals: compliance.trippedSignals,
632
+ fileSaveAvailable: fileSave.available,
633
+ missingFileSavePackages: fileSave.missing,
506
634
  };
507
635
  if (devMode) {
508
636
  console.log(`[Glomo-RN-SDK LRS] Attempting to start checkout of ${checkoutUrl} from status: ${status}`);
509
637
  }
510
638
  // Tracking start attempt with current status
511
- (0, analytics_1.trackStartAttempt)(status, orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
512
- if (deviceComplianceCheck === true) {
513
- // Device detected as compromised
639
+ (0, analytics_1.trackStartAttempt)(status, orderId, publicKey, devMode, mockMode, checkoutUrl, dependencyInfo);
640
+ /**
641
+ * Ungated on purpose, both of them. An absent optional dependency degrades silently by
642
+ * design, so a console warning is the only thing that reaches a merchant who has not
643
+ * noticed - and the compliance one now precedes a hard block on live keys.
644
+ */
645
+ if (!complianceLibraryInstalled) {
646
+ console.warn("[Glomo-RN-SDK LRS] jail-monkey is not installed or its native module is not linked, so " +
647
+ "rooted and jailbroken devices cannot be detected. As of v5 this BLOCKS live checkouts. " +
648
+ "Install jail-monkey and rebuild the app - see the README.");
649
+ }
650
+ if (!fileSave.available) {
651
+ console.warn(`[Glomo-RN-SDK LRS] Document downloads are disabled: ${fileSave.missing.join(" and ")} ` +
652
+ "could not be loaded. Install the package(s) and rebuild the app, or ignore this if your " +
653
+ "checkout does not offer downloads - the page falls back to its own behaviour.");
654
+ }
655
+ if ((0, device_compliance_1.blocksCheckout)(compliance, publicKey)) {
656
+ /**
657
+ * Two conditions, one refusal. `compromised === true` is a rooted or jailbroken device;
658
+ * `null` is a device we could not check, which on RN overwhelmingly means jail-monkey is
659
+ * absent rather than that a platform probe failed. Both fail closed on a live key, and
660
+ * neither fires on a test or mock key.
661
+ */
662
+ const unknown = compliance.compromised === null;
514
663
  if (devMode) {
515
- console.error("[Glomo-RN-SDK LRS] Cannot start checkout. Device is rooted or jailbroken.");
664
+ console.error(`[Glomo-RN-SDK LRS] Cannot start checkout. ${unknown ? "Device security could not be verified." : "Device is rooted or jailbroken."}`);
516
665
  }
517
666
  const errors = [
518
667
  {
519
668
  type: "device_forbidden",
520
- message: "LRS checkout is not allowed on rooted or jailbroken devices.",
669
+ message: unknown
670
+ ? "LRS checkout could not verify this device's security. Install jail-monkey and rebuild the app."
671
+ : "LRS checkout is not allowed on rooted or jailbroken devices.",
521
672
  },
522
673
  ];
523
674
  (0, validation_1.safeCallback)(onSdkError, [errors], "onSdkError", devMode);
524
675
  // Tracking start() failure
525
- (0, analytics_1.trackStartFailure)("device_forbidden", orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
676
+ (0, analytics_1.trackStartFailure)(unknown ? "device_compliance_unknown" : "device_forbidden", orderId, publicKey, devMode, mockMode, checkoutUrl, dependencyInfo);
526
677
  setShowCheckout(false);
527
678
  return false;
528
679
  }
529
- else if (deviceComplianceCheck === null) {
530
- // 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.
531
- console.warn("[Glomo-RN-SDK LRS] 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 LRS checkouts from compromised devices.");
532
- }
533
680
  // Enforcing devMode requirement for development servers
534
681
  const resolvedServer = server || "prod";
535
682
  if (!devMode && resolvedServer !== "prod") {
@@ -541,7 +688,7 @@ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymen
541
688
  ];
542
689
  (0, validation_1.safeCallback)(onSdkError, [errors], "onSdkError", devMode);
543
690
  (0, analytics_1.trackSdkError)(errors, orderId, publicKey, devMode, mockMode, checkoutUrl);
544
- (0, analytics_1.trackStartFailure)("validation_error", orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
691
+ (0, analytics_1.trackStartFailure)("validation_error", orderId, publicKey, devMode, mockMode, checkoutUrl, dependencyInfo);
545
692
  setShowCheckout(false);
546
693
  return false;
547
694
  }
@@ -584,7 +731,7 @@ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymen
584
731
  (0, validation_1.safeCallback)(onSdkError, [errors], "onSdkError", devMode);
585
732
  // Tracking start() failure
586
733
  (0, analytics_1.trackSdkError)(errors, orderId, publicKey, devMode, mockMode, checkoutUrl);
587
- (0, analytics_1.trackStartFailure)("validation_error", orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
734
+ (0, analytics_1.trackStartFailure)("validation_error", orderId, publicKey, devMode, mockMode, checkoutUrl, dependencyInfo);
588
735
  setShowCheckout(false);
589
736
  return false;
590
737
  }
@@ -593,7 +740,7 @@ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymen
593
740
  console.log(`[Glomo-RN-SDK LRS] Cannot start checkout. Current status: ${status}. Payment already successful.`);
594
741
  }
595
742
  // Tracking start() failure
596
- (0, analytics_1.trackStartFailure)("payment_successful", orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
743
+ (0, analytics_1.trackStartFailure)("payment_successful", orderId, publicKey, devMode, mockMode, checkoutUrl, dependencyInfo);
597
744
  setShowCheckout(false);
598
745
  return false;
599
746
  }
@@ -607,7 +754,7 @@ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymen
607
754
  console.log(`[Glomo-RN-SDK LRS] Cannot start checkout. Unknown status: ${status}.`);
608
755
  }
609
756
  // Tracking start() failure
610
- (0, analytics_1.trackStartFailure)("invalid_status", orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
757
+ (0, analytics_1.trackStartFailure)("invalid_status", orderId, publicKey, devMode, mockMode, checkoutUrl, dependencyInfo);
611
758
  setShowCheckout(false);
612
759
  return false;
613
760
  }
@@ -616,7 +763,7 @@ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymen
616
763
  console.error("[Glomo-RN-SDK LRS] Cannot start checkout. Invalid checkout URL.");
617
764
  }
618
765
  // Tracking start() failure
619
- (0, analytics_1.trackStartFailure)("invalid_url", orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
766
+ (0, analytics_1.trackStartFailure)("invalid_url", orderId, publicKey, devMode, mockMode, checkoutUrl, dependencyInfo);
620
767
  setShowCheckout(false);
621
768
  return false;
622
769
  }
@@ -628,7 +775,7 @@ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymen
628
775
  setEducationCarouselState("pending");
629
776
  setStatus("payment_in_progress");
630
777
  // Tracking successful start
631
- (0, analytics_1.trackStartSuccess)(orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
778
+ (0, analytics_1.trackStartSuccess)(orderId, publicKey, devMode, mockMode, checkoutUrl, dependencyInfo);
632
779
  setShowCheckout(true);
633
780
  return true;
634
781
  }, [devMode, status, publicKey, orderId, baseCheckoutUrl, checkoutUrl, mockMode, onSdkError, server]);
@@ -663,6 +810,13 @@ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymen
663
810
  setEducationCarouselState("pending");
664
811
  };
665
812
  }, []);
813
+ /**
814
+ * Scripts injected into each WebView, carrying what this build can advertise.
815
+ *
816
+ * Resolved once per mount rather than at module load, so a merchant who never opens a
817
+ * checkout never pays for the dependency probe.
818
+ */
819
+ const injectionScripts = (0, react_1.useMemo)(() => (0, index_1.buildInjectionScripts)((0, index_2.resolveCapabilities)()), []);
666
820
  return {
667
821
  start,
668
822
  status,
@@ -683,8 +837,13 @@ function useLrsCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymen
683
837
  handleNavigationStateChange,
684
838
  handleModalBackButton,
685
839
  handleFlowBack,
686
- injectedMain: index_1.InjectionScripts.main,
687
- injectedFlow: index_1.InjectionScripts.flow,
688
- injectedEducationCarousel: index_1.InjectionScripts.educationCarousel,
840
+ handlePermissionRequest,
841
+ injectedMain: __DEV__ && (internalOptions === null || internalOptions === void 0 ? void 0 : internalOptions._testInjectionScript)
842
+ ? injectionScripts.main + internalOptions._testInjectionScript
843
+ : injectionScripts.main,
844
+ injectedFlow: injectionScripts.flow,
845
+ injectedEducationCarousel: __DEV__ && (internalOptions === null || internalOptions === void 0 ? void 0 : internalOptions._testCarouselInjectionScript)
846
+ ? injectionScripts.educationCarousel + internalOptions._testCarouselInjectionScript
847
+ : injectionScripts.educationCarousel,
689
848
  };
690
849
  }