@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,14 +8,26 @@ 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 message_router_1 = require("./bridge/message-router");
15
+ const file_save_handler_1 = require("./bridge/file-save-handler");
16
+ const file_input_handler_1 = require("./bridge/file-input-handler");
17
+ const index_2 = require("./capabilities/index");
18
+ const use_capability_reporting_1 = require("./capabilities/use-capability-reporting");
19
+ const use_sdk_session_1 = require("./utils/use-sdk-session");
20
+ const use_permission_request_1 = require("./webview/use-permission-request");
12
21
  const analytics_1 = require("./utils/analytics");
13
22
  const checkout_1 = require("./types/checkout");
23
+ /** Log prefix for everything this flow emits */
24
+ const LOG_PREFIX = "[Glomo-RN-SDK Standard]";
14
25
  /** The main SDK hook for the GlomoPay Standard Checkout flow */
15
- function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymentFailure, onConnectionError, onPaymentTerminate, onSdkError, onUserJourneyCompleted, onUserRefusedCameraPermissions, devMode = false, }, mockMode, internalOptions) {
16
- var _a;
26
+ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onPaymentFailure, onConnectionError, onPaymentTerminate, onSdkError, onUserJourneyCompleted, onUserRefusedDevicePermissions, devMode = false, }, mockMode, internalOptions) {
27
+ var _a, _b;
17
28
  // Resolving analytics trackers - defaults to standard, subscription flow overrides
18
29
  const trackers = (_a = internalOptions === null || internalOptions === void 0 ? void 0 : internalOptions.analyticsTrackers) !== null && _a !== void 0 ? _a : analytics_1.standardCheckoutTrackers;
30
+ const flowType = (_b = internalOptions === null || internalOptions === void 0 ? void 0 : internalOptions.flowType) !== null && _b !== void 0 ? _b : "standard";
19
31
  // Refs for the WebViews
20
32
  const mainWebViewRef = (0, react_1.useRef)(null);
21
33
  const flowWebViewRef = (0, react_1.useRef)(null);
@@ -32,7 +44,7 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
32
44
  // Building the query string manually since URLSearchParams is not fully supported in React Native
33
45
  const baseParams = [`orderId=${encodeURIComponent(orderId)}`];
34
46
  const mode = mockMode ? "mock" : "live";
35
- const params = baseParams.concat(`mode=${encodeURIComponent(mode)}`, `publicKey=${encodeURIComponent(publicKey)}`);
47
+ const params = baseParams.concat(`mode=${encodeURIComponent(mode)}`, `publicKey=${encodeURIComponent(publicKey)}`, `product=${encodeURIComponent(base_1.ANALYTICS_PRODUCT)}`, `surface=${encodeURIComponent(base_1.ANALYTICS_SURFACE)}`);
36
48
  const queryString = params.join("&");
37
49
  // Handling base URL with or without existing query parameters
38
50
  const separator = baseCheckoutUrl.includes("?") ? "&" : "?";
@@ -126,43 +138,69 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
126
138
  }
127
139
  }
128
140
  }, [devMode]);
141
+ // Correlates every event from this mount, and keeps checkoutStatus current on all of them
142
+ (0, use_sdk_session_1.useSdkSession)(() => status);
143
+ // Missing optional dependencies reach the host here; start() warns on the console separately
144
+ (0, use_capability_reporting_1.useCapabilityReporting)({ onSdkError, devMode });
145
+ /**
146
+ * Handler for `file.input` - the user tapping a file input on a bank's page.
147
+ *
148
+ * Registered beside `file.save` on the same WebViews, for the same reason: uploads are not a
149
+ * property of one flow, and until now this message had no handler anywhere.
150
+ */
151
+ const fileInputHandler = (0, react_1.useMemo)(() => (0, file_input_handler_1.createFileInputHandler)({
152
+ flowType: flowType,
153
+ orderId,
154
+ publicKey,
155
+ checkoutUrl,
156
+ mockMode,
157
+ devMode,
158
+ }), [orderId, publicKey, checkoutUrl, mockMode, devMode, flowType]);
159
+ /**
160
+ * Handler for `file.save` - the page asking the SDK to put a document on the device.
161
+ *
162
+ * One handler, registered on the main and flow WebViews both. Not on the education carousel:
163
+ * that renders our own content, which has nothing to gate, and the capability is not advertised
164
+ * there either.
165
+ */
166
+ const fileSaveHandler = (0, react_1.useMemo)(() => (0, file_save_handler_1.createFileSaveHandler)({
167
+ flowType,
168
+ orderId,
169
+ publicKey,
170
+ checkoutUrl,
171
+ mockMode,
172
+ devMode,
173
+ onSdkError,
174
+ }), [flowType, orderId, publicKey, checkoutUrl, mockMode, devMode, onSdkError]);
129
175
  /**
130
176
  * Handler for the main WebView messages.
131
177
  * The main WebView hosts the checkout page. Messages arrive as JSON strings from the injected
132
178
  * JavaScript bridge and include console logs, window.open/close interceptions, and payment
133
179
  * lifecycle events (success, failure, terminate, bank transfer, pay via bank).
134
180
  */
135
- const handleMainWebViewMessage = (0, react_1.useCallback)((event) => {
136
- var _a, _b, _c;
137
- try {
138
- // Extracting and validating the raw message data from the WebView event
139
- const data = (_a = event.nativeEvent) === null || _a === void 0 ? void 0 : _a.data;
140
- if (!data || typeof data !== "string") {
141
- if (devMode) {
142
- console.warn("[Glomo-RN-SDK Standard] Invalid message data from MainWebView");
143
- }
144
- // Tracking invalid message received
145
- trackers.trackInvalidMessageReceived("main", data, checkoutUrl, orderId, publicKey, devMode, mockMode, checkoutUrl);
146
- return;
147
- }
148
- const message = JSON.parse(data);
149
- if (devMode) {
150
- console.log("[Glomo-RN-SDK Standard] MainWebView Message received:", message.type, message);
151
- }
181
+ const handleMainWebViewMessage = (0, react_1.useMemo)(() => (0, message_router_1.createMessageRouter)({
182
+ logPrefix: LOG_PREFIX,
183
+ source: "MainWebView",
184
+ devMode,
185
+ onInvalidMessage: (data) => {
186
+ trackers.trackInvalidMessageReceived("main", data, checkoutUrl, orderId, publicKey, devMode, mockMode, checkoutUrl);
187
+ },
188
+ handlers: {
189
+ "file.save": fileSaveHandler,
190
+ "file.input": fileInputHandler,
152
191
  // Forwarding console messages from the injected script to the React Native console
153
- if (message.type === "console") {
192
+ console: (message) => {
154
193
  if (devMode) {
155
- const level = message.level || "log";
156
- console.log(`[Glomo-RN-SDK Standard] [WebView ${level.toUpperCase()}]`, message.message);
194
+ const level = (typeof message.level === "string" && message.level) || "log";
195
+ console.log(`${LOG_PREFIX} [WebView ${level.toUpperCase()}]`, message.message);
157
196
  }
158
- return;
159
- }
197
+ },
160
198
  // Intercepting window.open calls - opening the target URL in the flow WebView overlay
161
- if (message.type === "window.open") {
199
+ "window.open": (message) => {
162
200
  const url = message.url;
163
201
  if (url && typeof url === "string" && (0, validation_1.isValidUrl)(url)) {
164
202
  if (devMode) {
165
- console.log(`[Glomo-RN-SDK Standard] window.open called from MainWebView: ${url}`);
203
+ console.log(`${LOG_PREFIX} window.open called from MainWebView: ${url}`);
166
204
  }
167
205
  setFlowWebViewUrl(url);
168
206
  setShowFlowWebView(true);
@@ -171,26 +209,27 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
171
209
  }
172
210
  else {
173
211
  if (devMode) {
174
- console.log("[Glomo-RN-SDK Standard] Invalid URL in window.open from MainWebView:", url, typeof url === "undefined" ? "undefined" : "");
212
+ console.log(`${LOG_PREFIX} Invalid URL in window.open from MainWebView:`, url, typeof url === "undefined" ? "undefined" : "");
175
213
  }
176
214
  }
177
- return;
178
- }
215
+ },
179
216
  // Intercepting window.close calls - closing the flow WebView overlay
180
- if (message.type === "window.close") {
217
+ "window.close": () => {
181
218
  if (devMode) {
182
- console.log("[Glomo-RN-SDK Standard] Child window close requested from MainWebView");
219
+ console.log(`${LOG_PREFIX} Child window close requested from MainWebView`);
183
220
  }
184
221
  setShowFlowWebView(false);
185
222
  setFlowWebViewUrl("");
186
223
  // Tracking window.close interception
187
224
  trackers.trackWindowClose("main", orderId, publicKey, devMode, mockMode, checkoutUrl);
188
- return;
189
- }
225
+ },
190
226
  // Handling merchant JS callbacks on flow completion (payment events, lifecycle events)
191
- if (message.type === "message" && message.message) {
227
+ message: (message) => {
228
+ var _a;
192
229
  const messageData = message.message;
193
- const messageType = messageData === null || messageData === void 0 ? void 0 : messageData.type;
230
+ if (!messageData)
231
+ return;
232
+ const messageType = messageData.type;
194
233
  // Handling payment success - closing checkout and notifying the merchant
195
234
  if (messageType === "payment.success" || messageType === "payment.complete") {
196
235
  const paymentMessage = messageData;
@@ -205,12 +244,24 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
205
244
  setStatus("payment_successful");
206
245
  (0, validation_1.safeCallback)(onPaymentSuccess, [payload], "onPaymentSuccess", devMode);
207
246
  // Tracking payment success callback
208
- trackers.trackPaymentSuccess(payload.orderId, payload.paymentId, publicKey, devMode, mockMode, checkoutUrl);
247
+ trackers.trackPaymentSuccess((0, event_projections_1.toPaymentEventData)(payload), publicKey, devMode, mockMode, checkoutUrl);
248
+ // Fires alongside the callback, not instead of it - the outcome is real, the payload is thin
249
+ const missingFields = (0, validation_1.missingPaymentFields)(payload);
250
+ if (missingFields.length > 0) {
251
+ (0, analytics_1.trackIncompletePaymentPayload)("payment.success", missingFields, "main", payload.orderId, publicKey, devMode, mockMode, checkoutUrl, flowType);
252
+ }
209
253
  }
210
254
  else {
255
+ /**
256
+ * Reported, not dropped. Before v5 this branch was a devMode-only console.error, so in a
257
+ * release build a terminal payment outcome the SDK could not route left no trace
258
+ * anywhere - no callback, no telemetry, nothing for support to find.
259
+ */
211
260
  if (devMode) {
212
- console.error("[Glomo-RN-SDK Standard] Invalid payment success payload:", payload);
261
+ console.error(`${LOG_PREFIX} Unroutable payment.success payload from main:`, payload);
213
262
  }
263
+ (0, analytics_1.trackDroppedPaymentPayload)("payment.success", (0, payment_payload_1.payloadKeysOf)(payload), "main", orderId, publicKey, devMode, mockMode, checkoutUrl, flowType);
264
+ (0, validation_1.safeCallback)(onSdkError, [[(0, payment_payload_1.droppedPayloadSdkError)("payment.success")]], "onSdkError", devMode);
214
265
  }
215
266
  return;
216
267
  }
@@ -228,12 +279,24 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
228
279
  setStatus("payment_failed");
229
280
  (0, validation_1.safeCallback)(onPaymentFailure, [payload], "onPaymentFailure", devMode);
230
281
  // Tracking payment failure callback
231
- trackers.trackPaymentFailure(payload.orderId, payload.paymentId, publicKey, devMode, mockMode, checkoutUrl);
282
+ trackers.trackPaymentFailure((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.failure", missingFields, "main", payload.orderId, publicKey, devMode, mockMode, checkoutUrl, flowType);
287
+ }
232
288
  }
233
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
+ */
234
295
  if (devMode) {
235
- console.error("[Glomo-RN-SDK Standard] Invalid payment failure payload:", payload);
296
+ console.error(`${LOG_PREFIX} Unroutable payment.failure payload from main:`, payload);
236
297
  }
298
+ (0, analytics_1.trackDroppedPaymentPayload)("payment.failure", (0, payment_payload_1.payloadKeysOf)(payload), "main", orderId, publicKey, devMode, mockMode, checkoutUrl, flowType);
299
+ (0, validation_1.safeCallback)(onSdkError, [[(0, payment_payload_1.droppedPayloadSdkError)("payment.failure")]], "onSdkError", devMode);
237
300
  }
238
301
  return;
239
302
  }
@@ -254,7 +317,7 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
254
317
  transactionReference: payload === null || payload === void 0 ? void 0 : payload.transactionReference,
255
318
  };
256
319
  (0, validation_1.safeCallback)(onUserJourneyCompleted, [journeyPayload], "onUserJourneyCompleted", devMode);
257
- trackers.trackBankTransferSubmitted(orderId, publicKey, devMode, mockMode, checkoutUrl);
320
+ trackers.trackBankTransferSubmitted(orderId, (0, event_projections_1.toJourneyEventData)(journeyPayload), publicKey, devMode, mockMode, checkoutUrl);
258
321
  return;
259
322
  }
260
323
  /**
@@ -283,10 +346,10 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
283
346
  const journeyPayload = {
284
347
  journeyType: checkout_1.ASYNC_PAYMENT_EVENTS.PAY_VIA_BANK_COMPLETED,
285
348
  orderId: payload === null || payload === void 0 ? void 0 : payload.orderId,
286
- status: (_b = payload === null || payload === void 0 ? void 0 : payload.status) !== null && _b !== void 0 ? _b : "unknown",
349
+ status: (_a = payload === null || payload === void 0 ? void 0 : payload.status) !== null && _a !== void 0 ? _a : "unknown",
287
350
  };
288
351
  (0, validation_1.safeCallback)(onUserJourneyCompleted, [journeyPayload], "onUserJourneyCompleted", devMode);
289
- trackers.trackPayViaBankCompleted(orderId, publicKey, (_c = payload === null || payload === void 0 ? void 0 : payload.status) !== null && _c !== void 0 ? _c : "unknown", devMode, mockMode, checkoutUrl);
352
+ trackers.trackPayViaBankCompleted(orderId, (0, event_projections_1.toJourneyEventData)(journeyPayload), publicKey, devMode, mockMode, checkoutUrl);
290
353
  return;
291
354
  }
292
355
  // Handling checkout.closed events
@@ -297,19 +360,15 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
297
360
  setShowCheckout(false);
298
361
  setShowFlowWebView(false);
299
362
  setFlowWebViewUrl("");
300
- (0, validation_1.safeCallback)(onPaymentTerminate, [], "onPaymentTerminate", devMode);
301
- // Tracking payment terminate callback
302
- trackers.trackPaymentTerminate(orderId, "checkout_closed", publicKey, devMode, mockMode, checkoutUrl);
363
+ const termination = { source: checkout_1.TERMINATION_SOURCES.CHECKOUT_CLOSED };
364
+ (0, validation_1.safeCallback)(onPaymentTerminate, [termination], "onPaymentTerminate", devMode);
365
+ // One object to the host and to telemetry, so the two cannot drift
366
+ trackers.trackPaymentTerminate(orderId, (0, event_projections_1.toTerminationEventData)(termination), publicKey, devMode, mockMode, checkoutUrl);
303
367
  return;
304
368
  }
305
- }
306
- }
307
- catch (error) {
308
- if (devMode) {
309
- console.error("[Glomo-RN-SDK Standard] Error parsing MainWebView message:", error);
310
- }
311
- }
312
- }, [
369
+ },
370
+ },
371
+ }), [
313
372
  onPaymentSuccess,
314
373
  onPaymentFailure,
315
374
  onPaymentTerminate,
@@ -320,42 +379,47 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
320
379
  publicKey,
321
380
  checkoutUrl,
322
381
  trackers,
382
+ fileSaveHandler,
383
+ flowType,
384
+ onSdkError,
385
+ fileInputHandler,
323
386
  ]);
324
387
  /**
325
388
  * Handler for the flow WebView messages.
326
389
  * The flow WebView hosts child pages opened via window.open (e.g. 3DS, bank redirects).
327
390
  * Message structure mirrors the main WebView handler but operates on the overlay WebView.
328
391
  */
329
- const handleFlowWebViewMessage = (0, react_1.useCallback)((event) => {
330
- var _a, _b, _c;
331
- try {
332
- // Extracting and validating the raw message data from the flow WebView event
333
- const data = (_a = event.nativeEvent) === null || _a === void 0 ? void 0 : _a.data;
334
- if (!data || typeof data !== "string") {
335
- if (devMode) {
336
- console.warn("[Glomo-RN-SDK Standard] Invalid message data from FlowWebView");
337
- }
338
- // Tracking invalid message received
339
- trackers.trackInvalidMessageReceived("flow", data, flowWebViewUrl, orderId, publicKey, devMode, mockMode, checkoutUrl);
340
- return;
341
- }
342
- const message = JSON.parse(data);
343
- if (devMode) {
344
- console.log("[Glomo-RN-SDK Standard] FlowWebView Message received:", message.type, message);
345
- }
392
+ const handleFlowWebViewMessage = (0, react_1.useMemo)(() => (0, message_router_1.createMessageRouter)({
393
+ logPrefix: LOG_PREFIX,
394
+ source: "FlowWebView",
395
+ devMode,
396
+ onInvalidMessage: (data) => {
397
+ trackers.trackInvalidMessageReceived("flow", data, flowWebViewUrl, orderId, publicKey, devMode, mockMode, checkoutUrl);
398
+ },
399
+ handlers: {
400
+ /**
401
+ * Telemetry only, and deliberately not wired to onUserRefusedDevicePermissions.
402
+ * The flow injection sees this by watching getUserMedia, so coverage depends on
403
+ * whether a given bank uses it - partial coverage on a merchant callback is
404
+ * worse than none, because silence would stop being unambiguous.
405
+ */
406
+ "permission.denied": (message) => {
407
+ (0, analytics_1.trackDevicePermissionRefused)(typeof message.resource === "string" ? message.resource : "camera", "get_user_media", orderId, publicKey, devMode, mockMode, checkoutUrl, flowType);
408
+ },
409
+ "file.save": fileSaveHandler,
410
+ "file.input": fileInputHandler,
346
411
  // Forwarding console messages from the flow WebView's injected script
347
- if (message.type === "console") {
412
+ console: (message) => {
348
413
  if (devMode) {
349
- console.log(`[Glomo-RN-SDK Standard] [Flow] ${message.message}`);
414
+ console.log(`${LOG_PREFIX} [Flow] ${message.message}`);
350
415
  }
351
- return;
352
- }
416
+ },
353
417
  // Intercepting window.open calls from the flow WebView - navigating within the same overlay
354
- if (message.type === "window.open") {
418
+ "window.open": (message) => {
355
419
  const url = message.url;
356
420
  if (url && typeof url === "string" && (0, validation_1.isValidUrl)(url)) {
357
421
  if (devMode) {
358
- console.log(`[Glomo-RN-SDK Standard] window.open called from FlowWebView: ${url}`);
422
+ console.log(`${LOG_PREFIX} window.open called from FlowWebView: ${url}`);
359
423
  }
360
424
  setFlowWebViewUrl(url);
361
425
  // Tracking window.open interception
@@ -363,26 +427,27 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
363
427
  }
364
428
  else {
365
429
  if (devMode) {
366
- console.log("[Glomo-RN-SDK Standard] Invalid URL in window.open from FlowWebView:", url, typeof url === "undefined" ? "undefined" : "");
430
+ console.log(`${LOG_PREFIX} Invalid URL in window.open from FlowWebView:`, url, typeof url === "undefined" ? "undefined" : "");
367
431
  }
368
432
  }
369
- return;
370
- }
433
+ },
371
434
  // Intercepting window.close calls from the flow WebView - closing the overlay
372
- if (message.type === "window.close") {
435
+ "window.close": () => {
373
436
  if (devMode) {
374
- console.log("[Glomo-RN-SDK Standard] FlowWebView close requested");
437
+ console.log(`${LOG_PREFIX} FlowWebView close requested`);
375
438
  }
376
439
  setShowFlowWebView(false);
377
440
  setFlowWebViewUrl("");
378
441
  // Tracking window.close interception
379
442
  trackers.trackWindowClose("flow", orderId, publicKey, devMode, mockMode, checkoutUrl);
380
- return;
381
- }
443
+ },
382
444
  // Handling merchant JS callbacks from the flow WebView (payment events, lifecycle events)
383
- if (message.type === "message" && message.message) {
445
+ message: (message) => {
446
+ var _a;
384
447
  const messageData = message.message;
385
- const messageType = messageData === null || messageData === void 0 ? void 0 : messageData.type;
448
+ if (!messageData)
449
+ return;
450
+ const messageType = messageData.type;
386
451
  // Handling payment success from the flow WebView
387
452
  if (messageType === "payment.success" || messageType === "payment.complete") {
388
453
  const paymentMessage = messageData;
@@ -397,12 +462,24 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
397
462
  setStatus("payment_successful");
398
463
  (0, validation_1.safeCallback)(onPaymentSuccess, [payload], "onPaymentSuccess", devMode);
399
464
  // Tracking payment success callback
400
- trackers.trackPaymentSuccess(payload.orderId, payload.paymentId, publicKey, devMode, mockMode, checkoutUrl);
465
+ trackers.trackPaymentSuccess((0, event_projections_1.toPaymentEventData)(payload), publicKey, devMode, mockMode, checkoutUrl);
466
+ // Fires alongside the callback, not instead of it - the outcome is real, the payload is thin
467
+ const missingFields = (0, validation_1.missingPaymentFields)(payload);
468
+ if (missingFields.length > 0) {
469
+ (0, analytics_1.trackIncompletePaymentPayload)("payment.success", missingFields, "flow", payload.orderId, publicKey, devMode, mockMode, checkoutUrl, flowType);
470
+ }
401
471
  }
402
472
  else {
473
+ /**
474
+ * Reported, not dropped. Before v5 this branch was a devMode-only console.error, so in a
475
+ * release build a terminal payment outcome the SDK could not route left no trace
476
+ * anywhere - no callback, no telemetry, nothing for support to find.
477
+ */
403
478
  if (devMode) {
404
- console.error("[Glomo-RN-SDK Standard] Invalid payment success payload from FlowWebView:", payload);
479
+ console.error(`${LOG_PREFIX} Unroutable payment.success payload from flow:`, payload);
405
480
  }
481
+ (0, analytics_1.trackDroppedPaymentPayload)("payment.success", (0, payment_payload_1.payloadKeysOf)(payload), "flow", orderId, publicKey, devMode, mockMode, checkoutUrl, flowType);
482
+ (0, validation_1.safeCallback)(onSdkError, [[(0, payment_payload_1.droppedPayloadSdkError)("payment.success")]], "onSdkError", devMode);
406
483
  }
407
484
  return;
408
485
  }
@@ -420,12 +497,24 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
420
497
  setStatus("payment_failed");
421
498
  (0, validation_1.safeCallback)(onPaymentFailure, [payload], "onPaymentFailure", devMode);
422
499
  // Tracking payment failure callback
423
- trackers.trackPaymentFailure(payload.orderId, payload.paymentId, publicKey, devMode, mockMode, checkoutUrl);
500
+ trackers.trackPaymentFailure((0, event_projections_1.toPaymentEventData)(payload), publicKey, devMode, mockMode, checkoutUrl);
501
+ // Fires alongside the callback, not instead of it - the outcome is real, the payload is thin
502
+ const missingFields = (0, validation_1.missingPaymentFields)(payload);
503
+ if (missingFields.length > 0) {
504
+ (0, analytics_1.trackIncompletePaymentPayload)("payment.failure", missingFields, "flow", payload.orderId, publicKey, devMode, mockMode, checkoutUrl, flowType);
505
+ }
424
506
  }
425
507
  else {
508
+ /**
509
+ * Reported, not dropped. Before v5 this branch was a devMode-only console.error, so in a
510
+ * release build a terminal payment outcome the SDK could not route left no trace
511
+ * anywhere - no callback, no telemetry, nothing for support to find.
512
+ */
426
513
  if (devMode) {
427
- console.error("[Glomo-RN-SDK Standard] Invalid payment failure payload from FlowWebView:", payload);
514
+ console.error(`${LOG_PREFIX} Unroutable payment.failure payload from flow:`, payload);
428
515
  }
516
+ (0, analytics_1.trackDroppedPaymentPayload)("payment.failure", (0, payment_payload_1.payloadKeysOf)(payload), "flow", orderId, publicKey, devMode, mockMode, checkoutUrl, flowType);
517
+ (0, validation_1.safeCallback)(onSdkError, [[(0, payment_payload_1.droppedPayloadSdkError)("payment.failure")]], "onSdkError", devMode);
429
518
  }
430
519
  return;
431
520
  }
@@ -450,20 +539,15 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
450
539
  const journeyPayload = {
451
540
  journeyType: checkout_1.ASYNC_PAYMENT_EVENTS.PAY_VIA_BANK_COMPLETED,
452
541
  orderId: payload === null || payload === void 0 ? void 0 : payload.orderId,
453
- status: (_b = payload === null || payload === void 0 ? void 0 : payload.status) !== null && _b !== void 0 ? _b : "unknown",
542
+ status: (_a = payload === null || payload === void 0 ? void 0 : payload.status) !== null && _a !== void 0 ? _a : "unknown",
454
543
  };
455
544
  (0, validation_1.safeCallback)(onUserJourneyCompleted, [journeyPayload], "onUserJourneyCompleted", devMode);
456
- trackers.trackPayViaBankCompleted(orderId, publicKey, (_c = payload === null || payload === void 0 ? void 0 : payload.status) !== null && _c !== void 0 ? _c : "unknown", devMode, mockMode, checkoutUrl);
545
+ trackers.trackPayViaBankCompleted(orderId, (0, event_projections_1.toJourneyEventData)(journeyPayload), publicKey, devMode, mockMode, checkoutUrl);
457
546
  return;
458
547
  }
459
- }
460
- }
461
- catch (error) {
462
- if (devMode) {
463
- console.error("[Glomo-RN-SDK Standard] Error parsing FlowWebView message:", error);
464
- }
465
- }
466
- }, [
548
+ },
549
+ },
550
+ }), [
467
551
  onPaymentSuccess,
468
552
  onPaymentFailure,
469
553
  onUserJourneyCompleted,
@@ -474,6 +558,10 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
474
558
  flowWebViewUrl,
475
559
  checkoutUrl,
476
560
  trackers,
561
+ fileSaveHandler,
562
+ flowType,
563
+ onSdkError,
564
+ fileInputHandler,
477
565
  ]);
478
566
  // Handler for the navigation state changes
479
567
  const handleNavigationStateChange = (0, react_1.useCallback)((navState) => {
@@ -503,10 +591,18 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
503
591
  console.log("[Glomo-RN-SDK Standard] Ignoring checkout status update for: " + status);
504
592
  }
505
593
  }
506
- (0, validation_1.safeCallback)(onPaymentTerminate, [], "onPaymentTerminate", devMode);
507
- // Tracking payment terminate callback (user dismiss/back button)
508
- const terminateSource = react_native_1.Platform.OS === "ios" ? "user_dismiss" : "back_button";
509
- trackers.trackPaymentTerminate(orderId, terminateSource, publicKey, devMode, mockMode, checkoutUrl);
594
+ /**
595
+ * Inferred from the platform rather than read off an event, because `onRequestClose` is
596
+ * the only wiring here and the two platforms raise it for different gestures: Android
597
+ * for hardware back and the back gesture, iOS for swipe-down on the pageSheet. Nothing
598
+ * else routes through it, so the inference is sound - but it is an inference.
599
+ */
600
+ const termination = {
601
+ source: react_native_1.Platform.OS === "ios" ? checkout_1.TERMINATION_SOURCES.MODAL_DISMISS : checkout_1.TERMINATION_SOURCES.DEVICE_BACK_BUTTON,
602
+ };
603
+ (0, validation_1.safeCallback)(onPaymentTerminate, [termination], "onPaymentTerminate", devMode);
604
+ // One object to the host and to telemetry, so the two cannot drift
605
+ trackers.trackPaymentTerminate(orderId, (0, event_projections_1.toTerminationEventData)(termination), publicKey, devMode, mockMode, checkoutUrl);
510
606
  }
511
607
  catch (error) {
512
608
  if (devMode) {
@@ -521,88 +617,35 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
521
617
  }
522
618
  setShowFlowWebView(false);
523
619
  setFlowWebViewUrl("");
524
- trackers.trackWindowClose("flow", orderId, publicKey, devMode, mockMode, checkoutUrl);
525
- }, [devMode, orderId, publicKey, mockMode, checkoutUrl, trackers]);
620
+ (0, analytics_1.trackBackNavigationViaSdkBackButton)(orderId, publicKey, devMode, mockMode, checkoutUrl, flowType);
621
+ }, [devMode, orderId, publicKey, mockMode, checkoutUrl, flowType]);
526
622
  /**
527
- * Handler for WebView permission requests (e.g. camera access for bank authentication).
528
- * On Android, prompts the user for camera permission via PermissionsAndroid.
529
- * On iOS, grants camera access automatically (iOS handles its own permission prompt).
530
- * Non-camera permissions are denied by default.
623
+ * Closing the checkout when the user refuses camera access, since the bank page cannot
624
+ * proceed without it. Kept here rather than inside usePermissionRequest because moving the
625
+ * status and notifying the merchant are flow concerns.
626
+ *
627
+ * Resets the flow overlay as well as the modal, matching the LRS handler. The prompt is raised
628
+ * from a bank page inside that overlay, so this is the one close path where the flow state is
629
+ * guaranteed non-default. Nothing visible depends on it today - the overlay renders inside the
630
+ * Modal and unmounts with it - but "every close path resets derived state" should not hold in
631
+ * one flow and not the other.
531
632
  */
532
- const handlePermissionRequest = (0, react_1.useCallback)(async (request) => {
533
- try {
534
- const { resources } = request.nativeEvent;
535
- // Checking if the requested permission is camera-related
536
- const isCameraPermissionRequest = resources.some((resource) => resource === "camera" || resource.toLowerCase().includes("camera"));
537
- if (!isCameraPermissionRequest) {
538
- if (devMode) {
539
- console.log("[Glomo-RN-SDK Standard] Non-camera permission requested, denying:", resources);
540
- }
541
- request.deny();
542
- return;
543
- }
544
- if (devMode) {
545
- console.log("[Glomo-RN-SDK Standard] Camera permission requested by webpage");
546
- }
547
- if (react_native_1.Platform.OS === "android") {
548
- try {
549
- const granted = await react_native_1.PermissionsAndroid.request(react_native_1.PermissionsAndroid.PERMISSIONS.CAMERA, {
550
- title: "Camera Permission",
551
- message: "This app needs camera access for identity verification during checkout",
552
- buttonNeutral: "Ask Me Later",
553
- buttonNegative: "Cancel",
554
- buttonPositive: "OK",
555
- });
556
- if (granted === react_native_1.PermissionsAndroid.RESULTS.GRANTED) {
557
- if (devMode) {
558
- console.log("[Glomo-RN-SDK Standard] Camera permission granted");
559
- }
560
- request.grant(resources);
561
- }
562
- else {
563
- if (devMode) {
564
- console.log("[Glomo-RN-SDK Standard] Camera permission denied by user");
565
- }
566
- setShowCheckout(false);
567
- if (status === "payment_in_progress") {
568
- setStatus("payment_cancelled");
569
- }
570
- (0, validation_1.safeCallback)(onUserRefusedCameraPermissions, [], "onUserRefusedCameraPermissions", devMode);
571
- request.deny();
572
- }
573
- }
574
- catch (err) {
575
- if (devMode) {
576
- console.error("[Glomo-RN-SDK Standard] Error requesting camera permission:", err);
577
- }
578
- setShowCheckout(false);
579
- if (status === "payment_in_progress") {
580
- setStatus("payment_cancelled");
581
- }
582
- (0, validation_1.safeCallback)(onUserRefusedCameraPermissions, [], "onUserRefusedCameraPermissions", devMode);
583
- request.deny();
584
- }
585
- }
586
- else if (react_native_1.Platform.OS === "ios") {
587
- if (devMode) {
588
- console.log("[Glomo-RN-SDK Standard] Granting camera permission request on iOS");
589
- }
590
- request.grant(resources);
591
- }
592
- else {
593
- if (devMode) {
594
- console.warn("[Glomo-RN-SDK Standard] Unsupported platform for camera permissions");
595
- }
596
- request.deny();
597
- }
598
- }
599
- catch (error) {
600
- if (devMode) {
601
- console.error("[Glomo-RN-SDK Standard] Error in handlePermissionRequest:", error);
602
- }
603
- request.deny();
633
+ const handleCameraPermissionDenied = (0, react_1.useCallback)(() => {
634
+ setShowCheckout(false);
635
+ setShowFlowWebView(false);
636
+ setFlowWebViewUrl("");
637
+ if (status === "payment_in_progress") {
638
+ setStatus("payment_cancelled");
604
639
  }
605
- }, [devMode, status, onUserRefusedCameraPermissions]);
640
+ (0, validation_1.safeCallback)(onUserRefusedDevicePermissions, [], "onUserRefusedDevicePermissions", devMode);
641
+ // The permission hook only reaches here for camera; anything else is denied without notifying
642
+ (0, analytics_1.trackDevicePermissionRefused)("camera", "permission_request", orderId, publicKey, devMode, mockMode, checkoutUrl, flowType);
643
+ }, [status, onUserRefusedDevicePermissions, devMode, orderId, publicKey, mockMode, checkoutUrl, flowType]);
644
+ const handlePermissionRequest = (0, use_permission_request_1.usePermissionRequest)({
645
+ logPrefix: "[Glomo-RN-SDK Standard]",
646
+ onDenied: handleCameraPermissionDenied,
647
+ devMode,
648
+ });
606
649
  /**
607
650
  * Initiator method for the Standard Checkout flow.
608
651
  * Using a ref, the standard flow can be started by calling this method.
@@ -620,38 +663,62 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
620
663
  * Checking device security compliance (if peerDependencies are available)
621
664
  * A null value indicates that recommended security dependencies were not installed and compliance checks will be skipped in this case
622
665
  */
623
- const deviceComplianceCheck = (0, device_compliance_1.checkDeviceCompliance)(devMode);
624
- const jailbreakDetectionLibraryInstalled = (0, device_compliance_1.isComplianceCheckAvailable)();
625
- const jailbreakDetectionInfo = {
626
- jailbreakDetectionLibraryInstalled,
627
- jailbreakDetected: deviceComplianceCheck,
666
+ const compliance = (0, device_compliance_1.resolveDeviceCompliance)(devMode);
667
+ const complianceLibraryInstalled = (0, device_compliance_1.isComplianceCheckAvailable)();
668
+ const fileSave = (0, index_2.resolveFileSaveAvailability)();
669
+ const dependencyInfo = {
670
+ jailbreakDetectionLibraryInstalled: complianceLibraryInstalled,
671
+ jailbreakDetected: compliance.compromised,
672
+ complianceSource: compliance.source,
673
+ complianceSignals: compliance.trippedSignals,
674
+ fileSaveAvailable: fileSave.available,
675
+ missingFileSavePackages: fileSave.missing,
628
676
  };
629
677
  if (devMode) {
630
678
  console.log(`[Glomo-RN-SDK Standard] Attempting to start checkout of ${checkoutUrl} from status: ${status}`);
631
679
  }
632
680
  // Tracking start attempt with current status
633
- trackers.trackStartAttempt(status, orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
634
- if (deviceComplianceCheck === true) {
635
- // Device detected as compromised
681
+ trackers.trackStartAttempt(status, orderId, publicKey, devMode, mockMode, checkoutUrl, dependencyInfo);
682
+ /**
683
+ * Ungated on purpose, both of them. An absent optional dependency degrades silently by
684
+ * design, so a console warning is the only thing that reaches a merchant who has not
685
+ * noticed - and the compliance one now precedes a hard block on live keys.
686
+ */
687
+ if (!complianceLibraryInstalled) {
688
+ console.warn("[Glomo-RN-SDK Standard] jail-monkey is not installed or its native module is not linked, so " +
689
+ "rooted and jailbroken devices cannot be detected. As of v5 this BLOCKS live checkouts. " +
690
+ "Install jail-monkey and rebuild the app - see the README.");
691
+ }
692
+ if (!fileSave.available) {
693
+ console.warn(`[Glomo-RN-SDK Standard] Document downloads are disabled: ${fileSave.missing.join(" and ")} ` +
694
+ "could not be loaded. Install the package(s) and rebuild the app, or ignore this if your " +
695
+ "checkout does not offer downloads - the page falls back to its own behaviour.");
696
+ }
697
+ if ((0, device_compliance_1.blocksCheckout)(compliance, publicKey)) {
698
+ /**
699
+ * Two conditions, one refusal. `compromised === true` is a rooted or jailbroken device;
700
+ * `null` is a device we could not check, which on RN overwhelmingly means jail-monkey is
701
+ * absent rather than that a platform probe failed. Both fail closed on a live key, and
702
+ * neither fires on a test or mock key.
703
+ */
704
+ const unknown = compliance.compromised === null;
636
705
  if (devMode) {
637
- console.error("[Glomo-RN-SDK Standard] Cannot start checkout. Device is rooted or jailbroken.");
706
+ console.error(`[Glomo-RN-SDK Standard] Cannot start checkout. ${unknown ? "Device security could not be verified." : "Device is rooted or jailbroken."}`);
638
707
  }
639
708
  const errors = [
640
709
  {
641
710
  type: "device_forbidden",
642
- message: "Standard checkout is not allowed on rooted or jailbroken devices.",
711
+ message: unknown
712
+ ? "Standard checkout could not verify this device's security. Install jail-monkey and rebuild the app."
713
+ : "Standard checkout is not allowed on rooted or jailbroken devices.",
643
714
  },
644
715
  ];
645
716
  (0, validation_1.safeCallback)(onSdkError, [errors], "onSdkError", devMode);
646
717
  // Tracking start() failure
647
- trackers.trackStartFailure("device_forbidden", orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
718
+ trackers.trackStartFailure(unknown ? "device_compliance_unknown" : "device_forbidden", orderId, publicKey, devMode, mockMode, checkoutUrl, dependencyInfo);
648
719
  setShowCheckout(false);
649
720
  return false;
650
721
  }
651
- else if (deviceComplianceCheck === null) {
652
- // 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.
653
- 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.");
654
- }
655
722
  // Enforcing devMode requirement for development servers
656
723
  const resolvedServer = server || "prod";
657
724
  if (!devMode && resolvedServer !== "prod") {
@@ -663,7 +730,7 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
663
730
  ];
664
731
  (0, validation_1.safeCallback)(onSdkError, [errors], "onSdkError", devMode);
665
732
  trackers.trackSdkError(errors, orderId, publicKey, devMode, mockMode, checkoutUrl);
666
- trackers.trackStartFailure("validation_error", orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
733
+ trackers.trackStartFailure("validation_error", orderId, publicKey, devMode, mockMode, checkoutUrl, dependencyInfo);
667
734
  setShowCheckout(false);
668
735
  return false;
669
736
  }
@@ -706,7 +773,7 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
706
773
  (0, validation_1.safeCallback)(onSdkError, [errors], "onSdkError", devMode);
707
774
  // Tracking start() failure
708
775
  trackers.trackSdkError(errors, orderId, publicKey, devMode, mockMode, checkoutUrl);
709
- trackers.trackStartFailure("validation_error", orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
776
+ trackers.trackStartFailure("validation_error", orderId, publicKey, devMode, mockMode, checkoutUrl, dependencyInfo);
710
777
  setShowCheckout(false);
711
778
  return false;
712
779
  }
@@ -715,7 +782,7 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
715
782
  console.log(`[Glomo-RN-SDK Standard] Cannot start checkout. Current status: ${status}. Payment already successful.`);
716
783
  }
717
784
  // Tracking start() failure
718
- trackers.trackStartFailure("payment_successful", orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
785
+ trackers.trackStartFailure("payment_successful", orderId, publicKey, devMode, mockMode, checkoutUrl, dependencyInfo);
719
786
  setShowCheckout(false);
720
787
  return false;
721
788
  }
@@ -729,7 +796,7 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
729
796
  console.log(`[Glomo-RN-SDK Standard] Cannot start checkout. Unknown status: ${status}.`);
730
797
  }
731
798
  // Tracking start() failure
732
- trackers.trackStartFailure("invalid_status", orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
799
+ trackers.trackStartFailure("invalid_status", orderId, publicKey, devMode, mockMode, checkoutUrl, dependencyInfo);
733
800
  setShowCheckout(false);
734
801
  return false;
735
802
  }
@@ -738,7 +805,7 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
738
805
  console.error("[Glomo-RN-SDK Standard] Cannot start checkout. Invalid checkout URL.");
739
806
  }
740
807
  // Tracking start() failure
741
- trackers.trackStartFailure("invalid_url", orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
808
+ trackers.trackStartFailure("invalid_url", orderId, publicKey, devMode, mockMode, checkoutUrl, dependencyInfo);
742
809
  setShowCheckout(false);
743
810
  return false;
744
811
  }
@@ -751,7 +818,7 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
751
818
  payViaBankCompletedHandledRef.current = false;
752
819
  setStatus("payment_in_progress");
753
820
  // Tracking successful start
754
- trackers.trackStartSuccess(orderId, publicKey, devMode, mockMode, checkoutUrl, jailbreakDetectionInfo);
821
+ trackers.trackStartSuccess(orderId, publicKey, devMode, mockMode, checkoutUrl, dependencyInfo);
755
822
  // Opening the checkout modal
756
823
  setShowCheckout(true);
757
824
  return true;
@@ -798,6 +865,13 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
798
865
  setFlowWebViewUrl("");
799
866
  };
800
867
  }, []);
868
+ /**
869
+ * Scripts injected into each WebView, carrying what this build can advertise.
870
+ *
871
+ * Resolved once per mount rather than at module load, so a merchant who never opens a
872
+ * checkout never pays for the dependency probe.
873
+ */
874
+ const injectionScripts = (0, react_1.useMemo)(() => (0, index_1.buildInjectionScripts)((0, index_2.resolveCapabilities)()), []);
801
875
  return {
802
876
  start,
803
877
  status,
@@ -815,7 +889,9 @@ function useStandardCheckout({ server, publicKey, orderId, onPaymentSuccess, onP
815
889
  handleNavigationStateChange,
816
890
  handleModalBackButton,
817
891
  handleFlowBack,
818
- injectedMain: index_1.InjectionScripts.standard,
819
- injectedFlow: index_1.InjectionScripts.flow,
892
+ injectedMain: __DEV__ && (internalOptions === null || internalOptions === void 0 ? void 0 : internalOptions._testInjectionScript)
893
+ ? injectionScripts.standard + internalOptions._testInjectionScript
894
+ : injectionScripts.standard,
895
+ injectedFlow: injectionScripts.flow,
820
896
  };
821
897
  }