@glomopay/react-native-sdk 4.1.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.
- package/CHANGELOG.md +29 -0
- package/MIGRATION.md +112 -0
- package/README.md +173 -36
- package/lib/bridge/file-input-handler.d.ts +25 -0
- package/lib/bridge/file-input-handler.d.ts.map +1 -0
- package/lib/bridge/file-input-handler.js +38 -0
- package/lib/bridge/file-save-handler.d.ts +32 -0
- package/lib/bridge/file-save-handler.d.ts.map +1 -0
- package/lib/bridge/file-save-handler.js +92 -0
- package/lib/bridge/message-router.d.ts +44 -0
- package/lib/bridge/message-router.d.ts.map +1 -0
- package/lib/bridge/message-router.js +48 -0
- package/lib/bridge/payment-payload.d.ts +18 -0
- package/lib/bridge/payment-payload.d.ts.map +1 -0
- package/lib/bridge/payment-payload.js +31 -0
- package/lib/capabilities/file-save.d.ts +40 -0
- package/lib/capabilities/file-save.d.ts.map +1 -0
- package/lib/capabilities/file-save.js +101 -0
- package/lib/capabilities/index.d.ts +22 -0
- package/lib/capabilities/index.d.ts.map +1 -0
- package/lib/capabilities/index.js +34 -0
- package/lib/capabilities/use-capability-reporting.d.ts +24 -0
- package/lib/capabilities/use-capability-reporting.d.ts.map +1 -0
- package/lib/capabilities/use-capability-reporting.js +44 -0
- package/lib/config/base.d.ts +12 -0
- package/lib/config/base.d.ts.map +1 -1
- package/lib/config/base.js +13 -1
- package/lib/glomo-checkout.d.ts +6 -1
- package/lib/glomo-checkout.d.ts.map +1 -1
- package/lib/glomo-checkout.js +11 -5
- package/lib/glomo-lrs-checkout.d.ts.map +1 -1
- package/lib/glomo-lrs-checkout.js +33 -10
- package/lib/glomo-standard-checkout.d.ts +3 -1
- package/lib/glomo-standard-checkout.d.ts.map +1 -1
- package/lib/glomo-standard-checkout.js +21 -13
- package/lib/glomo-subscriptions-checkout.d.ts.map +1 -1
- package/lib/glomo-subscriptions-checkout.js +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +4 -3
- package/lib/injections/index.d.ts +24 -0
- package/lib/injections/index.d.ts.map +1 -1
- package/lib/injections/index.js +41 -0
- package/lib/injections/webview-flow.injection.d.ts.map +1 -1
- package/lib/injections/webview-flow.injection.js +76 -0
- package/lib/injections/webview-main.injection.d.ts.map +1 -1
- package/lib/injections/webview-main.injection.js +31 -0
- package/lib/injections/webview-standard.injection.d.ts.map +1 -1
- package/lib/injections/webview-standard.injection.js +31 -0
- package/lib/services/file-saver.d.ts +109 -0
- package/lib/services/file-saver.d.ts.map +1 -0
- package/lib/services/file-saver.js +416 -0
- package/lib/types/checkout.d.ts +58 -6
- package/lib/types/checkout.d.ts.map +1 -1
- package/lib/types/checkout.js +24 -2
- package/lib/types/standard-checkout.d.ts +6 -6
- package/lib/types/standard-checkout.d.ts.map +1 -1
- package/lib/types/subscriptions-checkout.d.ts +3 -3
- package/lib/types/subscriptions-checkout.d.ts.map +1 -1
- package/lib/use-glomo-checkout.js +2 -2
- package/lib/use-lrs-checkout.d.ts +9 -5
- package/lib/use-lrs-checkout.d.ts.map +1 -1
- package/lib/use-lrs-checkout.js +317 -171
- package/lib/use-standard-checkout.d.ts +17 -13
- package/lib/use-standard-checkout.d.ts.map +1 -1
- package/lib/use-standard-checkout.js +281 -207
- package/lib/utils/analytics.d.ts +177 -51
- package/lib/utils/analytics.d.ts.map +1 -1
- package/lib/utils/analytics.js +299 -81
- package/lib/utils/device-compliance.d.ts +57 -12
- package/lib/utils/device-compliance.d.ts.map +1 -1
- package/lib/utils/device-compliance.js +101 -37
- package/lib/utils/event-projections.d.ts +56 -0
- package/lib/utils/event-projections.d.ts.map +1 -0
- package/lib/utils/event-projections.js +42 -0
- package/lib/utils/file-metadata.d.ts +52 -0
- package/lib/utils/file-metadata.d.ts.map +1 -0
- package/lib/utils/file-metadata.js +134 -0
- package/lib/utils/use-sdk-session.d.ts +12 -0
- package/lib/utils/use-sdk-session.d.ts.map +1 -0
- package/lib/utils/use-sdk-session.js +37 -0
- package/lib/utils/validation.d.ts +37 -3
- package/lib/utils/validation.d.ts.map +1 -1
- package/lib/utils/validation.js +64 -8
- package/lib/webview/capability-props.d.ts +27 -0
- package/lib/webview/capability-props.d.ts.map +1 -0
- package/lib/webview/capability-props.js +61 -0
- package/lib/webview/use-permission-request.d.ts +33 -0
- package/lib/webview/use-permission-request.d.ts.map +1 -0
- package/lib/webview/use-permission-request.js +89 -0
- package/package.json +10 -2
- package/src/bridge/file-input-handler.ts +66 -0
- package/src/bridge/file-save-handler.ts +141 -0
- package/src/bridge/message-router.ts +92 -0
- package/src/bridge/payment-payload.ts +31 -0
- package/src/capabilities/file-save.ts +124 -0
- package/src/capabilities/index.ts +34 -0
- package/src/capabilities/use-capability-reporting.ts +55 -0
- package/src/config/base.ts +13 -0
- package/src/glomo-checkout.tsx +11 -7
- package/src/glomo-lrs-checkout.tsx +41 -26
- package/src/glomo-standard-checkout.tsx +31 -34
- package/src/glomo-subscriptions-checkout.tsx +1 -0
- package/src/index.ts +4 -2
- package/src/injections/index.ts +43 -0
- package/src/injections/webview-flow.injection.ts +76 -0
- package/src/injections/webview-main.injection.ts +31 -0
- package/src/injections/webview-standard.injection.ts +31 -0
- package/src/services/file-saver.ts +497 -0
- package/src/types/checkout.ts +62 -6
- package/src/types/standard-checkout.ts +6 -6
- package/src/types/subscriptions-checkout.ts +7 -3
- package/src/use-glomo-checkout.tsx +2 -2
- package/src/use-lrs-checkout.tsx +656 -365
- package/src/use-standard-checkout.tsx +728 -466
- package/src/utils/analytics.ts +422 -117
- package/src/utils/device-compliance.ts +148 -38
- package/src/utils/event-projections.ts +74 -0
- package/src/utils/file-metadata.ts +140 -0
- package/src/utils/use-sdk-session.ts +41 -0
- package/src/utils/validation.ts +70 -10
- package/src/webview/capability-props.ts +86 -0
- package/src/webview/use-permission-request.ts +114 -0
- package/src/jail-monkey.d.ts +0 -19
|
@@ -1,24 +1,69 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Device compliance
|
|
2
|
+
* Device compliance for the GlomoPay RN SDK
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* If jail-monkey is not installed, compliance checks are skipped.
|
|
4
|
+
* Whether a checkout may run on this device, from `jail-monkey`'s root/jailbreak detection.
|
|
6
5
|
*
|
|
7
|
-
*
|
|
6
|
+
* The gate exists to protect a **compromised customer device**: malware on a real user's rooted
|
|
7
|
+
* phone reading cached KYC documents. That framing is what decides the two judgement calls below -
|
|
8
|
+
* which root signals to trust, and when not to gate at all.
|
|
8
9
|
*/
|
|
10
|
+
/** The package name, so a missing dependency can be named in a warning and in telemetry */
|
|
11
|
+
export declare const COMPLIANCE_PACKAGE = "jail-monkey";
|
|
9
12
|
/**
|
|
10
|
-
*
|
|
11
|
-
* Returns null if jail-monkey is not available (compliance check skipped)
|
|
12
|
-
* Returns true if device is compromised, false if device is safe
|
|
13
|
+
* rootBeer checks deliberately excluded from our verdict.
|
|
13
14
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
15
|
+
* Both fire on a stock AOSP emulator image - `ro.build.tags=test-keys`, and `ro.debuggable=1` /
|
|
16
|
+
* `ro.secure=0` - so an Android emulator reports as rooted through `isJailBroken()`, which is a
|
|
17
|
+
* hardcoded OR over every check and is not configurable. Merchants integration-test with live keys
|
|
18
|
+
* on emulators; blocking that turns integration into a support ticket, and an emulator is not the
|
|
19
|
+
* threat this gate exists for - there is no unwitting victim whose documents are being read.
|
|
20
|
+
*
|
|
21
|
+
* What this costs is custom-ROM detection on physical devices, the weakest signal in the set. Every
|
|
22
|
+
* strong indicator is kept: a genuinely rooted handset trips su, Magisk or a root-manager app
|
|
23
|
+
* essentially always.
|
|
24
|
+
*/
|
|
25
|
+
export declare const IGNORED_ROOT_SIGNALS: string[];
|
|
26
|
+
/** How the verdict below was reached. Rides telemetry, because the three are not equally strong. */
|
|
27
|
+
export type ComplianceSource =
|
|
28
|
+
/** Per-check rootBeer breakdown, with IGNORED_ROOT_SIGNALS excluded. Android only. */
|
|
29
|
+
"decomposed"
|
|
30
|
+
/** jail-monkey's own `isJailBroken()`. iOS, and Android builds that expose no breakdown. */
|
|
31
|
+
| "aggregate"
|
|
32
|
+
/** jail-monkey could not be loaded at all. */
|
|
33
|
+
| "unavailable";
|
|
34
|
+
/** What the probe concluded */
|
|
35
|
+
export interface DeviceComplianceResult {
|
|
36
|
+
/** True if compromised, false if clean, **null if it could not be determined** - never "clean" */
|
|
37
|
+
compromised: boolean | null;
|
|
38
|
+
source: ComplianceSource;
|
|
39
|
+
/** Names of the checks that tripped. Never the device identifiers behind them. */
|
|
40
|
+
trippedSignals: string[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Whether this device is compromised.
|
|
44
|
+
*
|
|
45
|
+
* Prefers the per-check breakdown over `isJailBroken()`, because the aggregate is a hardcoded OR
|
|
46
|
+
* that includes the two emulator-tripping checks. Falls back to the aggregate whenever the
|
|
47
|
+
* breakdown is absent - iOS never reports one, and a future jail-monkey that stops exporting it
|
|
48
|
+
* degrades to today's behaviour rather than to "clean".
|
|
49
|
+
*/
|
|
50
|
+
export declare function resolveDeviceCompliance(devMode?: boolean): DeviceComplianceResult;
|
|
51
|
+
/**
|
|
52
|
+
* Whether a checkout must be refused on this device.
|
|
53
|
+
*
|
|
54
|
+
* **Fails closed on a live key**: unknown blocks, because on RN unknown overwhelmingly means the
|
|
55
|
+
* merchant did not install `jail-monkey` rather than that a platform probe failed. That is why the
|
|
56
|
+
* package stopped being an optional peer dependency in v5 - a dependency cannot be optional and
|
|
57
|
+
* load-bearing at once, and an install-time warning beats a silent runtime outage.
|
|
58
|
+
*
|
|
59
|
+
* **Never gates mock or test traffic.** Measured, never blocked - matching the Flutter SDK's
|
|
60
|
+
* `enforced` model, and so QA on a rooted handset or an emulator image stays possible.
|
|
16
61
|
*/
|
|
17
|
-
export declare function
|
|
62
|
+
export declare function blocksCheckout(result: DeviceComplianceResult, publicKey?: string): boolean;
|
|
18
63
|
/**
|
|
19
|
-
*
|
|
64
|
+
* Whether the device compliance check could run at all
|
|
20
65
|
*
|
|
21
|
-
* @returns true if
|
|
66
|
+
* @returns true if jail-monkey is installed and its native module is linked
|
|
22
67
|
*/
|
|
23
68
|
export declare function isComplianceCheckAvailable(): boolean;
|
|
24
69
|
//# sourceMappingURL=device-compliance.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"device-compliance.d.ts","sourceRoot":"","sources":["../../src/utils/device-compliance.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"device-compliance.d.ts","sourceRoot":"","sources":["../../src/utils/device-compliance.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,2FAA2F;AAC3F,eAAO,MAAM,kBAAkB,gBAAgB,CAAC;AAEhD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,UAA+C,CAAC;AAEjF,oGAAoG;AACpG,MAAM,MAAM,gBAAgB;AACxB,sFAAsF;AACpF,YAAY;AAEd,4FAA4F;GAC1F,WAAW;AAEb,8CAA8C;GAC5C,aAAa,CAAC;AAEpB,+BAA+B;AAC/B,MAAM,WAAW,sBAAsB;IACnC,kGAAkG;IAClG,WAAW,EAAE,OAAO,GAAG,IAAI,CAAC;IAE5B,MAAM,EAAE,gBAAgB,CAAC;IAEzB,kFAAkF;IAClF,cAAc,EAAE,MAAM,EAAE,CAAC;CAC5B;AA4CD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,OAAe,GAAG,sBAAsB,CAuDxF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,sBAAsB,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAK1F;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD"}
|
|
@@ -1,71 +1,135 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Device compliance
|
|
3
|
+
* Device compliance for the GlomoPay RN SDK
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* If jail-monkey is not installed, compliance checks are skipped.
|
|
5
|
+
* Whether a checkout may run on this device, from `jail-monkey`'s root/jailbreak detection.
|
|
7
6
|
*
|
|
8
|
-
*
|
|
7
|
+
* The gate exists to protect a **compromised customer device**: malware on a real user's rooted
|
|
8
|
+
* phone reading cached KYC documents. That framing is what decides the two judgement calls below -
|
|
9
|
+
* which root signals to trust, and when not to gate at all.
|
|
9
10
|
*/
|
|
10
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
-
exports.
|
|
12
|
+
exports.IGNORED_ROOT_SIGNALS = exports.COMPLIANCE_PACKAGE = void 0;
|
|
13
|
+
exports.resolveDeviceCompliance = resolveDeviceCompliance;
|
|
14
|
+
exports.blocksCheckout = blocksCheckout;
|
|
12
15
|
exports.isComplianceCheckAvailable = isComplianceCheckAvailable;
|
|
16
|
+
const validation_1 = require("./validation");
|
|
17
|
+
/** The package name, so a missing dependency can be named in a warning and in telemetry */
|
|
18
|
+
exports.COMPLIANCE_PACKAGE = "jail-monkey";
|
|
13
19
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
20
|
+
* rootBeer checks deliberately excluded from our verdict.
|
|
21
|
+
*
|
|
22
|
+
* Both fire on a stock AOSP emulator image - `ro.build.tags=test-keys`, and `ro.debuggable=1` /
|
|
23
|
+
* `ro.secure=0` - so an Android emulator reports as rooted through `isJailBroken()`, which is a
|
|
24
|
+
* hardcoded OR over every check and is not configurable. Merchants integration-test with live keys
|
|
25
|
+
* on emulators; blocking that turns integration into a support ticket, and an emulator is not the
|
|
26
|
+
* threat this gate exists for - there is no unwitting victim whose documents are being read.
|
|
27
|
+
*
|
|
28
|
+
* What this costs is custom-ROM detection on physical devices, the weakest signal in the set. Every
|
|
29
|
+
* strong indicator is kept: a genuinely rooted handset trips su, Magisk or a root-manager app
|
|
30
|
+
* essentially always.
|
|
16
31
|
*/
|
|
17
|
-
|
|
32
|
+
exports.IGNORED_ROOT_SIGNALS = ["detectTestKeys", "checkForDangerousProps"];
|
|
33
|
+
/**
|
|
34
|
+
* Loads jail-monkey, or null.
|
|
35
|
+
*
|
|
36
|
+
* Literal `require` directly inside the `try`, resolved once at module init. Both are what Metro
|
|
37
|
+
* requires of an optional dependency, and the second is the one that is easy to get wrong: a lazy
|
|
38
|
+
* first `require` runs outside Metro's `inGuard`, where a failed load is reported through
|
|
39
|
+
* `ErrorUtils.reportFatalError` and raises a full-screen LogBox in dev builds even though the
|
|
40
|
+
* `catch` here degrades correctly. See the full note in capabilities/file-save.ts.
|
|
41
|
+
*
|
|
42
|
+
* Also catches the installed-but-not-rebuilt case: jail-monkey reads
|
|
43
|
+
* `NativeModules.JailMonkey.rootedDetectionMethods` at module scope, so an unlinked native module
|
|
44
|
+
* throws on import rather than degrading.
|
|
45
|
+
*/
|
|
46
|
+
const jailMonkeyModule = (() => {
|
|
47
|
+
var _a;
|
|
18
48
|
try {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
return null;
|
|
49
|
+
const loaded = require("jail-monkey");
|
|
50
|
+
const module = ((_a = loaded === null || loaded === void 0 ? void 0 : loaded.default) !== null && _a !== void 0 ? _a : loaded);
|
|
51
|
+
return module && typeof module.isJailBroken === "function" ? module : null;
|
|
24
52
|
}
|
|
25
|
-
catch (
|
|
26
|
-
// jail-monkey is not installed
|
|
53
|
+
catch (_b) {
|
|
27
54
|
return null;
|
|
28
55
|
}
|
|
56
|
+
})();
|
|
57
|
+
function loadJailMonkey() {
|
|
58
|
+
return jailMonkeyModule;
|
|
29
59
|
}
|
|
30
60
|
/**
|
|
31
|
-
*
|
|
32
|
-
* Returns null if jail-monkey is not available (compliance check skipped)
|
|
33
|
-
* Returns true if device is compromised, false if device is safe
|
|
61
|
+
* Whether this device is compromised.
|
|
34
62
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
63
|
+
* Prefers the per-check breakdown over `isJailBroken()`, because the aggregate is a hardcoded OR
|
|
64
|
+
* that includes the two emulator-tripping checks. Falls back to the aggregate whenever the
|
|
65
|
+
* breakdown is absent - iOS never reports one, and a future jail-monkey that stops exporting it
|
|
66
|
+
* degrades to today's behaviour rather than to "clean".
|
|
37
67
|
*/
|
|
38
|
-
function
|
|
39
|
-
const
|
|
40
|
-
if (!
|
|
68
|
+
function resolveDeviceCompliance(devMode = false) {
|
|
69
|
+
const jailMonkey = loadJailMonkey();
|
|
70
|
+
if (!jailMonkey) {
|
|
41
71
|
if (devMode) {
|
|
42
|
-
console.log(
|
|
43
|
-
"Install jail-monkey for enhanced security compliance.");
|
|
72
|
+
console.log(`[Glomo-RN-SDK] ${exports.COMPLIANCE_PACKAGE} not available. Device compliance could not be determined.`);
|
|
44
73
|
}
|
|
45
|
-
|
|
46
|
-
return null;
|
|
74
|
+
return { compromised: null, source: "unavailable", trippedSignals: [] };
|
|
47
75
|
}
|
|
48
76
|
try {
|
|
49
|
-
const
|
|
77
|
+
const breakdown = jailMonkey.androidRootedDetectionMethods;
|
|
78
|
+
// Read as an open map: the checks are iterated, and a jail-monkey that adds one should have
|
|
79
|
+
// it counted rather than ignored for being absent from a hardcoded list
|
|
80
|
+
const rootBeer = breakdown === null || breakdown === void 0 ? void 0 : breakdown.rootBeer;
|
|
81
|
+
if (rootBeer && typeof rootBeer === "object") {
|
|
82
|
+
const trippedSignals = Object.keys(rootBeer).filter((signal) => rootBeer[signal] === true && !exports.IGNORED_ROOT_SIGNALS.includes(signal));
|
|
83
|
+
// jail-monkey's own non-rootBeer signal sits beside the map rather than inside it
|
|
84
|
+
if ((breakdown === null || breakdown === void 0 ? void 0 : breakdown.jailMonkey) === true) {
|
|
85
|
+
trippedSignals.push("jailMonkey");
|
|
86
|
+
}
|
|
87
|
+
if (devMode) {
|
|
88
|
+
console.log(`[Glomo-RN-SDK] Device compliance (decomposed): ${trippedSignals.length > 0 ? `FAILED via ${trippedSignals.join(", ")}` : "PASSED"}`);
|
|
89
|
+
}
|
|
90
|
+
return { compromised: trippedSignals.length > 0, source: "decomposed", trippedSignals };
|
|
91
|
+
}
|
|
92
|
+
const compromised = jailMonkey.isJailBroken();
|
|
50
93
|
if (devMode) {
|
|
51
|
-
console.log(`[Glomo-RN-SDK] Device compliance
|
|
94
|
+
console.log(`[Glomo-RN-SDK] Device compliance (aggregate): ${compromised ? "FAILED" : "PASSED"}`);
|
|
52
95
|
}
|
|
53
|
-
return
|
|
96
|
+
return {
|
|
97
|
+
compromised,
|
|
98
|
+
source: "aggregate",
|
|
99
|
+
trippedSignals: compromised ? ["isJailBroken"] : [],
|
|
100
|
+
};
|
|
54
101
|
}
|
|
55
102
|
catch (error) {
|
|
56
103
|
if (devMode) {
|
|
57
|
-
const
|
|
58
|
-
console.error("[Glomo-RN-SDK] Error checking device compliance:",
|
|
104
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
105
|
+
console.error("[Glomo-RN-SDK] Error checking device compliance:", message);
|
|
59
106
|
}
|
|
60
|
-
//
|
|
61
|
-
return null;
|
|
107
|
+
// Unknown, not clean. The caller decides what to do with that, and on a live key it blocks.
|
|
108
|
+
return { compromised: null, source: "unavailable", trippedSignals: [] };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Whether a checkout must be refused on this device.
|
|
113
|
+
*
|
|
114
|
+
* **Fails closed on a live key**: unknown blocks, because on RN unknown overwhelmingly means the
|
|
115
|
+
* merchant did not install `jail-monkey` rather than that a platform probe failed. That is why the
|
|
116
|
+
* package stopped being an optional peer dependency in v5 - a dependency cannot be optional and
|
|
117
|
+
* load-bearing at once, and an install-time warning beats a silent runtime outage.
|
|
118
|
+
*
|
|
119
|
+
* **Never gates mock or test traffic.** Measured, never blocked - matching the Flutter SDK's
|
|
120
|
+
* `enforced` model, and so QA on a rooted handset or an emulator image stays possible.
|
|
121
|
+
*/
|
|
122
|
+
function blocksCheckout(result, publicKey) {
|
|
123
|
+
if ((0, validation_1.isMockModeKey)(publicKey)) {
|
|
124
|
+
return false;
|
|
62
125
|
}
|
|
126
|
+
return result.compromised !== false;
|
|
63
127
|
}
|
|
64
128
|
/**
|
|
65
|
-
*
|
|
129
|
+
* Whether the device compliance check could run at all
|
|
66
130
|
*
|
|
67
|
-
* @returns true if
|
|
131
|
+
* @returns true if jail-monkey is installed and its native module is linked
|
|
68
132
|
*/
|
|
69
133
|
function isComplianceCheckAvailable() {
|
|
70
|
-
return
|
|
134
|
+
return loadJailMonkey() !== null;
|
|
71
135
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime allowlists for what a callback payload may contribute to telemetry
|
|
3
|
+
*
|
|
4
|
+
* **Every tracker that reports a payload takes one of these, never the payload itself.**
|
|
5
|
+
*
|
|
6
|
+
* The rule exists because TypeScript types are erased. A `Pick<>`, a cast or a projection
|
|
7
|
+
* *interface* removes nothing from the object at runtime - the value still carries every field, and
|
|
8
|
+
* `client.post("/track", payload)` serialises all of them. So "pass the payload, constrain it with a
|
|
9
|
+
* type" compiles cleanly, reviews cleanly, and ships `signature` to Segment on every payment. A
|
|
10
|
+
* signature is a bearer credential.
|
|
11
|
+
*
|
|
12
|
+
* Constructing the outbound object field by field is what actually enforces the allowlist, and
|
|
13
|
+
* keeping it here rather than at each call site is what keeps the callback and the event assembled
|
|
14
|
+
* from one source, so a field added to a payload cannot reach an event unless someone adds it here
|
|
15
|
+
* deliberately.
|
|
16
|
+
*/
|
|
17
|
+
import { type GlomoCheckoutPayload, type GlomoTerminationPayload } from "../types/checkout";
|
|
18
|
+
/** What a payment payload may say about itself */
|
|
19
|
+
export interface PaymentEventData {
|
|
20
|
+
orderId: string;
|
|
21
|
+
paymentId?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Projection of a success or failure payload.
|
|
25
|
+
*
|
|
26
|
+
* `signature` is excluded and must stay excluded - it is the field this whole module exists for.
|
|
27
|
+
*/
|
|
28
|
+
export declare function toPaymentEventData(payload: GlomoCheckoutPayload): PaymentEventData;
|
|
29
|
+
/** What a termination payload may say about itself */
|
|
30
|
+
export interface TerminationEventData {
|
|
31
|
+
source: string;
|
|
32
|
+
}
|
|
33
|
+
/** Projection of a termination payload. Everything it carries today is safe; that is not a licence. */
|
|
34
|
+
export declare function toTerminationEventData(payload: GlomoTerminationPayload): TerminationEventData;
|
|
35
|
+
/** What an asynchronous journey payload may say about itself */
|
|
36
|
+
export interface JourneyEventData {
|
|
37
|
+
journeyType: string;
|
|
38
|
+
journeyStatus?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Whether the page sent bank details, not what they were.
|
|
41
|
+
*
|
|
42
|
+
* `senderAccountNumber` is a customer bank account number and `transactionReference` identifies
|
|
43
|
+
* a specific transfer; neither belongs in analytics. Presence is what triage actually needs - it
|
|
44
|
+
* separates "the page reported a transfer without details" from "the SDK dropped them".
|
|
45
|
+
*/
|
|
46
|
+
hasSenderAccountNumber: boolean;
|
|
47
|
+
hasTransactionReference: boolean;
|
|
48
|
+
}
|
|
49
|
+
/** Projection of an asynchronous journey payload */
|
|
50
|
+
export declare function toJourneyEventData(payload: {
|
|
51
|
+
journeyType: string;
|
|
52
|
+
status?: string;
|
|
53
|
+
senderAccountNumber?: string;
|
|
54
|
+
transactionReference?: string;
|
|
55
|
+
}): JourneyEventData;
|
|
56
|
+
//# sourceMappingURL=event-projections.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-projections.d.ts","sourceRoot":"","sources":["../../src/utils/event-projections.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,KAAK,oBAAoB,EAAE,KAAK,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAE5F,kDAAkD;AAClD,MAAM,WAAW,gBAAgB;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,oBAAoB,GAAG,gBAAgB,CAElF;AAED,sDAAsD;AACtD,MAAM,WAAW,oBAAoB;IACjC,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,uGAAuG;AACvG,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,GAAG,oBAAoB,CAE7F;AAED,gEAAgE;AAChE,MAAM,WAAW,gBAAgB;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;;;OAMG;IACH,sBAAsB,EAAE,OAAO,CAAC;IAChC,uBAAuB,EAAE,OAAO,CAAC;CACpC;AAED,oDAAoD;AACpD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;CACjC,GAAG,gBAAgB,CAOnB"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Runtime allowlists for what a callback payload may contribute to telemetry
|
|
4
|
+
*
|
|
5
|
+
* **Every tracker that reports a payload takes one of these, never the payload itself.**
|
|
6
|
+
*
|
|
7
|
+
* The rule exists because TypeScript types are erased. A `Pick<>`, a cast or a projection
|
|
8
|
+
* *interface* removes nothing from the object at runtime - the value still carries every field, and
|
|
9
|
+
* `client.post("/track", payload)` serialises all of them. So "pass the payload, constrain it with a
|
|
10
|
+
* type" compiles cleanly, reviews cleanly, and ships `signature` to Segment on every payment. A
|
|
11
|
+
* signature is a bearer credential.
|
|
12
|
+
*
|
|
13
|
+
* Constructing the outbound object field by field is what actually enforces the allowlist, and
|
|
14
|
+
* keeping it here rather than at each call site is what keeps the callback and the event assembled
|
|
15
|
+
* from one source, so a field added to a payload cannot reach an event unless someone adds it here
|
|
16
|
+
* deliberately.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.toPaymentEventData = toPaymentEventData;
|
|
20
|
+
exports.toTerminationEventData = toTerminationEventData;
|
|
21
|
+
exports.toJourneyEventData = toJourneyEventData;
|
|
22
|
+
/**
|
|
23
|
+
* Projection of a success or failure payload.
|
|
24
|
+
*
|
|
25
|
+
* `signature` is excluded and must stay excluded - it is the field this whole module exists for.
|
|
26
|
+
*/
|
|
27
|
+
function toPaymentEventData(payload) {
|
|
28
|
+
return { orderId: payload.orderId, paymentId: payload.paymentId };
|
|
29
|
+
}
|
|
30
|
+
/** Projection of a termination payload. Everything it carries today is safe; that is not a licence. */
|
|
31
|
+
function toTerminationEventData(payload) {
|
|
32
|
+
return { source: payload.source };
|
|
33
|
+
}
|
|
34
|
+
/** Projection of an asynchronous journey payload */
|
|
35
|
+
function toJourneyEventData(payload) {
|
|
36
|
+
return {
|
|
37
|
+
journeyType: payload.journeyType,
|
|
38
|
+
journeyStatus: payload.status,
|
|
39
|
+
hasSenderAccountNumber: Boolean(payload.senderAccountNumber),
|
|
40
|
+
hasTransactionReference: Boolean(payload.transactionReference),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shapes what a saved or picked file may say about itself in telemetry
|
|
3
|
+
*
|
|
4
|
+
* Extension and byte count only - never the file name, which is the user's data
|
|
5
|
+
* (`passport_A1234567.pdf`) where these two are not.
|
|
6
|
+
*
|
|
7
|
+
* Values are sentinels rather than nulls so an event always carries the same shape and a missing
|
|
8
|
+
* value is something to filter on. Ported from the Flutter SDK's `FileMetadata` so the two report
|
|
9
|
+
* the same vocabulary on the same events.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Length could not be read. Negative because `0` is legitimate: a zero-byte file is a real failure
|
|
13
|
+
* that would otherwise look like an unexplained bank rejection.
|
|
14
|
+
*/
|
|
15
|
+
export declare const SIZE_UNAVAILABLE = -1;
|
|
16
|
+
/** The file had no extension. User behaviour, not an error. */
|
|
17
|
+
export declare const EXTENSION_NONE = "FILE_WITH_NO_EXTENSION";
|
|
18
|
+
/**
|
|
19
|
+
* The tail was rejected: too long, or not purely alphanumeric. In the field this is ordinary user
|
|
20
|
+
* naming (`scan.jpeg (1)`), not an SDK fault. Do not alert on it.
|
|
21
|
+
*/
|
|
22
|
+
export declare const EXTENSION_EXTRACTION_FAILED = "EXTRACTION_FAILED";
|
|
23
|
+
/**
|
|
24
|
+
* A well-formed extension that is not one we know. Reports that something novel was handled without
|
|
25
|
+
* carrying the value, so a new format is still visible as an event.
|
|
26
|
+
*/
|
|
27
|
+
export declare const EXTENSION_UNKNOWN = "UNKNOWN_EXTENSION";
|
|
28
|
+
/**
|
|
29
|
+
* Lowercased extension of a path, or a sentinel.
|
|
30
|
+
*
|
|
31
|
+
* Not `split(".").pop()`, which returns the whole filename when there is no dot - shipping exactly
|
|
32
|
+
* what this exists to keep out.
|
|
33
|
+
*/
|
|
34
|
+
export declare function extensionOf(path?: string | null): string;
|
|
35
|
+
/**
|
|
36
|
+
* The MIME type to declare for a file, from the extension extensionOf resolved.
|
|
37
|
+
*
|
|
38
|
+
* Falls back to the generic type for every sentinel: an unrecognised extension is exactly the case
|
|
39
|
+
* where a guess would be wrong, and Android will still let the user save with a generic type.
|
|
40
|
+
*/
|
|
41
|
+
export declare function mimeTypeFor(fileExtension: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Normalises a raw HTML `accept` attribute for telemetry.
|
|
44
|
+
*
|
|
45
|
+
* The injected JS reads the attribute verbatim (`".pdf, .jpeg"`) where a native layer may hand over
|
|
46
|
+
* an already-parsed list (`".pdf,.jpeg"`), so without this the request event and anything reported
|
|
47
|
+
* later for the same click will not join on it.
|
|
48
|
+
*
|
|
49
|
+
* Never used to decide what may be picked - the bank owns that.
|
|
50
|
+
*/
|
|
51
|
+
export declare function normalizeAcceptTypes(acceptTypes?: string | null): string | undefined;
|
|
52
|
+
//# sourceMappingURL=file-metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-metadata.d.ts","sourceRoot":"","sources":["../../src/utils/file-metadata.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;GAGG;AACH,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC,+DAA+D;AAC/D,eAAO,MAAM,cAAc,2BAA2B,CAAC;AAEvD;;;GAGG;AACH,eAAO,MAAM,2BAA2B,sBAAsB,CAAC;AAE/D;;;GAGG;AACH,eAAO,MAAM,iBAAiB,sBAAsB,CAAC;AA2CrD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CA4BxD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAWpF"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shapes what a saved or picked file may say about itself in telemetry
|
|
4
|
+
*
|
|
5
|
+
* Extension and byte count only - never the file name, which is the user's data
|
|
6
|
+
* (`passport_A1234567.pdf`) where these two are not.
|
|
7
|
+
*
|
|
8
|
+
* Values are sentinels rather than nulls so an event always carries the same shape and a missing
|
|
9
|
+
* value is something to filter on. Ported from the Flutter SDK's `FileMetadata` so the two report
|
|
10
|
+
* the same vocabulary on the same events.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.EXTENSION_UNKNOWN = exports.EXTENSION_EXTRACTION_FAILED = exports.EXTENSION_NONE = exports.SIZE_UNAVAILABLE = void 0;
|
|
14
|
+
exports.extensionOf = extensionOf;
|
|
15
|
+
exports.mimeTypeFor = mimeTypeFor;
|
|
16
|
+
exports.normalizeAcceptTypes = normalizeAcceptTypes;
|
|
17
|
+
/**
|
|
18
|
+
* Length could not be read. Negative because `0` is legitimate: a zero-byte file is a real failure
|
|
19
|
+
* that would otherwise look like an unexplained bank rejection.
|
|
20
|
+
*/
|
|
21
|
+
exports.SIZE_UNAVAILABLE = -1;
|
|
22
|
+
/** The file had no extension. User behaviour, not an error. */
|
|
23
|
+
exports.EXTENSION_NONE = "FILE_WITH_NO_EXTENSION";
|
|
24
|
+
/**
|
|
25
|
+
* The tail was rejected: too long, or not purely alphanumeric. In the field this is ordinary user
|
|
26
|
+
* naming (`scan.jpeg (1)`), not an SDK fault. Do not alert on it.
|
|
27
|
+
*/
|
|
28
|
+
exports.EXTENSION_EXTRACTION_FAILED = "EXTRACTION_FAILED";
|
|
29
|
+
/**
|
|
30
|
+
* A well-formed extension that is not one we know. Reports that something novel was handled without
|
|
31
|
+
* carrying the value, so a new format is still visible as an event.
|
|
32
|
+
*/
|
|
33
|
+
exports.EXTENSION_UNKNOWN = "UNKNOWN_EXTENSION";
|
|
34
|
+
/** First of two gates. Rejects a long tail early; KNOWN_EXTENSIONS is what actually bounds this. */
|
|
35
|
+
const MAX_EXTENSION_LENGTH = 5;
|
|
36
|
+
/**
|
|
37
|
+
* Extensions reported verbatim, and the MIME type each one maps to.
|
|
38
|
+
*
|
|
39
|
+
* Anything outside this map becomes a sentinel, so no part of a filename can reach telemetry by
|
|
40
|
+
* being short enough to look plausible. The MIME side exists because Android's save dialog is an
|
|
41
|
+
* `ACTION_CREATE_DOCUMENT` intent that wants a type: deriving it here rather than forwarding the
|
|
42
|
+
* `mimeType` the page sends keeps untrusted page input out of an Intent, and is the same call the
|
|
43
|
+
* Flutter SDK makes when it declines to forward that field to `file_picker`.
|
|
44
|
+
*/
|
|
45
|
+
const KNOWN_EXTENSIONS = {
|
|
46
|
+
pdf: "application/pdf",
|
|
47
|
+
epdf: "application/pdf",
|
|
48
|
+
jpg: "image/jpeg",
|
|
49
|
+
jpeg: "image/jpeg",
|
|
50
|
+
png: "image/png",
|
|
51
|
+
gif: "image/gif",
|
|
52
|
+
webp: "image/webp",
|
|
53
|
+
bmp: "image/bmp",
|
|
54
|
+
tiff: "image/tiff",
|
|
55
|
+
tif: "image/tiff",
|
|
56
|
+
heic: "image/heic",
|
|
57
|
+
heif: "image/heif",
|
|
58
|
+
jfif: "image/jpeg",
|
|
59
|
+
pjpeg: "image/jpeg",
|
|
60
|
+
doc: "application/msword",
|
|
61
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
62
|
+
xls: "application/vnd.ms-excel",
|
|
63
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
64
|
+
csv: "text/csv",
|
|
65
|
+
txt: "text/plain",
|
|
66
|
+
rtf: "application/rtf",
|
|
67
|
+
odt: "application/vnd.oasis.opendocument.text",
|
|
68
|
+
zip: "application/zip",
|
|
69
|
+
};
|
|
70
|
+
/** Hoisted - extensionOf runs on every save. */
|
|
71
|
+
const ALPHANUMERIC = /^[a-z0-9]+$/;
|
|
72
|
+
/**
|
|
73
|
+
* Lowercased extension of a path, or a sentinel.
|
|
74
|
+
*
|
|
75
|
+
* Not `split(".").pop()`, which returns the whole filename when there is no dot - shipping exactly
|
|
76
|
+
* what this exists to keep out.
|
|
77
|
+
*/
|
|
78
|
+
function extensionOf(path) {
|
|
79
|
+
var _a;
|
|
80
|
+
if (!path) {
|
|
81
|
+
return exports.EXTENSION_EXTRACTION_FAILED;
|
|
82
|
+
}
|
|
83
|
+
// A staging directory can carry dots, so the basename is isolated before looking for one
|
|
84
|
+
const name = (_a = path.split("/").pop()) !== null && _a !== void 0 ? _a : "";
|
|
85
|
+
if (name.length === 0) {
|
|
86
|
+
return exports.EXTENSION_EXTRACTION_FAILED;
|
|
87
|
+
}
|
|
88
|
+
const dot = name.lastIndexOf(".");
|
|
89
|
+
// `dot === 0` is a dotfile (`.bank_statement`), not an extension
|
|
90
|
+
if (dot <= 0 || dot === name.length - 1) {
|
|
91
|
+
return exports.EXTENSION_NONE;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Rejected, not stripped. Stripping separators first let a 15-character filename tail collapse
|
|
95
|
+
* into a plausible 8-character extension (`aadhaar.9876-5432` -> `98765432`), which is filename
|
|
96
|
+
* content on a KYC upload. A real extension has no separators to remove.
|
|
97
|
+
*/
|
|
98
|
+
const raw = name.slice(dot + 1).toLowerCase();
|
|
99
|
+
if (raw.length === 0 || raw.length > MAX_EXTENSION_LENGTH || !ALPHANUMERIC.test(raw)) {
|
|
100
|
+
return exports.EXTENSION_EXTRACTION_FAILED;
|
|
101
|
+
}
|
|
102
|
+
return raw in KNOWN_EXTENSIONS ? raw : exports.EXTENSION_UNKNOWN;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The MIME type to declare for a file, from the extension extensionOf resolved.
|
|
106
|
+
*
|
|
107
|
+
* Falls back to the generic type for every sentinel: an unrecognised extension is exactly the case
|
|
108
|
+
* where a guess would be wrong, and Android will still let the user save with a generic type.
|
|
109
|
+
*/
|
|
110
|
+
function mimeTypeFor(fileExtension) {
|
|
111
|
+
var _a;
|
|
112
|
+
return (_a = KNOWN_EXTENSIONS[fileExtension]) !== null && _a !== void 0 ? _a : "application/octet-stream";
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Normalises a raw HTML `accept` attribute for telemetry.
|
|
116
|
+
*
|
|
117
|
+
* The injected JS reads the attribute verbatim (`".pdf, .jpeg"`) where a native layer may hand over
|
|
118
|
+
* an already-parsed list (`".pdf,.jpeg"`), so without this the request event and anything reported
|
|
119
|
+
* later for the same click will not join on it.
|
|
120
|
+
*
|
|
121
|
+
* Never used to decide what may be picked - the bank owns that.
|
|
122
|
+
*/
|
|
123
|
+
function normalizeAcceptTypes(acceptTypes) {
|
|
124
|
+
if (!acceptTypes || acceptTypes.trim().length === 0) {
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
const parts = acceptTypes
|
|
128
|
+
.toLowerCase()
|
|
129
|
+
.split(",")
|
|
130
|
+
.map((part) => part.trim())
|
|
131
|
+
.filter((part) => part.length > 0)
|
|
132
|
+
.sort();
|
|
133
|
+
return parts.length === 0 ? undefined : parts.join(",");
|
|
134
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Binds one checkout attempt's telemetry context for as long as the component is mounted */
|
|
2
|
+
/**
|
|
3
|
+
* Mints a session id and binds a checkout-status getter, both for the mounted lifetime.
|
|
4
|
+
*
|
|
5
|
+
* Deliberately a hook rather than something `start()` calls, so a merchant who retries produces two
|
|
6
|
+
* `start_attempted` events inside one session instead of two unrelated sessions.
|
|
7
|
+
*
|
|
8
|
+
* The status getter is held in a ref and re-read on every event, so a status that changes mid-session
|
|
9
|
+
* reaches later events without anything re-binding.
|
|
10
|
+
*/
|
|
11
|
+
export declare function useSdkSession(readStatus: () => string): void;
|
|
12
|
+
//# sourceMappingURL=use-sdk-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-sdk-session.d.ts","sourceRoot":"","sources":["../../src/utils/use-sdk-session.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAS7F;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,MAAM,GAAG,IAAI,CAsB5D"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** Binds one checkout attempt's telemetry context for as long as the component is mounted */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.useSdkSession = useSdkSession;
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const analytics_1 = require("./analytics");
|
|
7
|
+
/** Distinguishes two mounts in the same process without reaching for a uuid dependency */
|
|
8
|
+
let mountCounter = 0;
|
|
9
|
+
/**
|
|
10
|
+
* Mints a session id and binds a checkout-status getter, both for the mounted lifetime.
|
|
11
|
+
*
|
|
12
|
+
* Deliberately a hook rather than something `start()` calls, so a merchant who retries produces two
|
|
13
|
+
* `start_attempted` events inside one session instead of two unrelated sessions.
|
|
14
|
+
*
|
|
15
|
+
* The status getter is held in a ref and re-read on every event, so a status that changes mid-session
|
|
16
|
+
* reaches later events without anything re-binding.
|
|
17
|
+
*/
|
|
18
|
+
function useSdkSession(readStatus) {
|
|
19
|
+
const readStatusRef = (0, react_1.useRef)(readStatus);
|
|
20
|
+
readStatusRef.current = readStatus;
|
|
21
|
+
(0, react_1.useEffect)(() => {
|
|
22
|
+
const sessionId = `sess-${Date.now().toString(36)}-${(++mountCounter).toString(36)}-${Math.random()
|
|
23
|
+
.toString(36)
|
|
24
|
+
.slice(2, 8)}`;
|
|
25
|
+
(0, analytics_1.setSdkSession)(sessionId);
|
|
26
|
+
(0, analytics_1.bindCheckoutStatus)(() => readStatusRef.current());
|
|
27
|
+
return () => {
|
|
28
|
+
/**
|
|
29
|
+
* Cleared rather than left dangling, so an event from an unmounted checkout carries
|
|
30
|
+
* neither - but only if this mount's session is still the active one. A merchant with
|
|
31
|
+
* two checkouts alive would otherwise have the one unmounting silence the one still
|
|
32
|
+
* taking a payment.
|
|
33
|
+
*/
|
|
34
|
+
(0, analytics_1.releaseSdkSession)(sessionId);
|
|
35
|
+
};
|
|
36
|
+
}, []);
|
|
37
|
+
}
|