@virex-tech/paywallo-sdk 2.4.0 → 2.5.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/PaywalloSdk.podspec +1 -1
- package/README.md +45 -4
- package/android/build.gradle +0 -1
- package/android/src/main/java/com/paywallo/sdk/PaywalloFBBridgeModule.kt +7 -0
- package/android/src/main/java/com/paywallo/sdk/PaywalloSdkPackage.kt +1 -2
- package/dist/index.d.mts +298 -176
- package/dist/index.d.ts +298 -176
- package/dist/index.js +1146 -1061
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1175 -1092
- package/dist/index.mjs.map +1 -1
- package/ios/PaywalloFBBridge.m +3 -0
- package/ios/PaywalloFBBridge.swift +20 -0
- package/package.json +4 -1
- package/android/src/main/java/com/paywallo/sdk/PaywalloDeviceModule.kt +0 -148
- package/ios/PaywalloDevice.m +0 -8
- package/ios/PaywalloDevice.swift +0 -119
package/dist/index.mjs
CHANGED
|
@@ -246,77 +246,102 @@ var init_StorageMigration = __esm({
|
|
|
246
246
|
}
|
|
247
247
|
});
|
|
248
248
|
|
|
249
|
+
// src/errors/ClientError.ts
|
|
250
|
+
var ClientError, CLIENT_ERROR_CODES;
|
|
251
|
+
var init_ClientError = __esm({
|
|
252
|
+
"src/errors/ClientError.ts"() {
|
|
253
|
+
"use strict";
|
|
254
|
+
init_PaywalloError();
|
|
255
|
+
ClientError = class extends PaywalloError {
|
|
256
|
+
constructor(code, message) {
|
|
257
|
+
super("client", code, message);
|
|
258
|
+
this.name = "ClientError";
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
CLIENT_ERROR_CODES = {
|
|
262
|
+
NOT_INITIALIZED: "CLIENT_NOT_INITIALIZED",
|
|
263
|
+
MISSING_APP_KEY: "CLIENT_MISSING_APP_KEY",
|
|
264
|
+
INVALID_EVENT_NAME: "CLIENT_INVALID_EVENT_NAME",
|
|
265
|
+
PROVIDER_MISSING: "CLIENT_PROVIDER_MISSING",
|
|
266
|
+
INSECURE_REQUEST: "CLIENT_INSECURE_REQUEST",
|
|
267
|
+
UNKNOWN: "CLIENT_UNKNOWN"
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
|
|
249
272
|
// src/utils/NativeDeviceInfo.ts
|
|
250
|
-
import {
|
|
273
|
+
import { Platform } from "react-native";
|
|
274
|
+
import * as Device from "expo-device";
|
|
275
|
+
import * as Application from "expo-application";
|
|
276
|
+
import * as Localization from "expo-localization";
|
|
277
|
+
function assertExpoModules() {
|
|
278
|
+
if (!Device || Device.osName === void 0) {
|
|
279
|
+
throw new ClientError(
|
|
280
|
+
"MISSING_EXPO_DEPS",
|
|
281
|
+
"Paywallo SDK 3.x requires expo-device, expo-localization and expo-application. Run: npx expo install expo-device expo-localization expo-application"
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
251
285
|
function setNativeDeviceInfoDebug(debug) {
|
|
252
286
|
_debug2 = debug;
|
|
253
287
|
}
|
|
254
|
-
function
|
|
255
|
-
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
288
|
+
function buildSyncFields(idfv) {
|
|
289
|
+
const locale = Localization.getLocales()[0] ?? null;
|
|
290
|
+
const calendar = Localization.getCalendars()[0] ?? null;
|
|
291
|
+
const androidId = Platform.OS === "android" ? Application.getAndroidId() ?? "unknown" : "unknown";
|
|
292
|
+
return {
|
|
293
|
+
deviceId: Platform.OS === "ios" ? idfv ?? "unknown" : androidId,
|
|
294
|
+
model: Device.modelName ?? "unknown",
|
|
295
|
+
modelId: Device.modelId ?? Device.modelName ?? "unknown",
|
|
296
|
+
osName: Device.osName ?? Platform.OS,
|
|
297
|
+
systemVersion: Device.osVersion ?? "unknown",
|
|
298
|
+
appVersion: Application.nativeApplicationVersion ?? "unknown",
|
|
299
|
+
buildNumber: Application.nativeBuildVersion ?? "0",
|
|
300
|
+
bundleId: Application.applicationId ?? "unknown",
|
|
301
|
+
brand: Device.brand ?? "unknown",
|
|
302
|
+
idfv,
|
|
303
|
+
regionCode: locale?.regionCode ?? null,
|
|
304
|
+
languageTag: locale?.languageTag ?? null,
|
|
305
|
+
languageCode: locale?.languageCode ?? null,
|
|
306
|
+
timeZone: calendar?.timeZone ?? null
|
|
307
|
+
};
|
|
269
308
|
}
|
|
270
309
|
async function getDeviceInfo() {
|
|
271
310
|
if (cachedDeviceInfo !== null) return cachedDeviceInfo;
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
cachedDeviceInfo
|
|
277
|
-
return raw;
|
|
311
|
+
assertExpoModules();
|
|
312
|
+
const idfv = Platform.OS === "ios" ? await Application.getIosIdForVendorAsync() : null;
|
|
313
|
+
if (_debug2) console.log("[Paywallo] device info collected, idfv present:", idfv != null);
|
|
314
|
+
cachedDeviceInfo = buildSyncFields(idfv);
|
|
315
|
+
return cachedDeviceInfo;
|
|
278
316
|
}
|
|
279
317
|
async function getInstallReferrer() {
|
|
280
|
-
const mod = getNativeModule();
|
|
281
|
-
if (!mod) return null;
|
|
282
318
|
try {
|
|
283
|
-
return await
|
|
319
|
+
return await Application.getInstallReferrerAsync();
|
|
284
320
|
} catch {
|
|
285
321
|
return null;
|
|
286
322
|
}
|
|
287
323
|
}
|
|
288
324
|
function isAvailable2() {
|
|
289
|
-
return
|
|
325
|
+
return typeof Device !== "undefined" && Device.osName !== void 0;
|
|
290
326
|
}
|
|
291
327
|
function clearCache() {
|
|
292
328
|
cachedDeviceInfo = null;
|
|
293
329
|
}
|
|
294
330
|
function getCachedDeviceInfo() {
|
|
295
|
-
return cachedDeviceInfo;
|
|
331
|
+
if (cachedDeviceInfo !== null) return cachedDeviceInfo;
|
|
332
|
+
try {
|
|
333
|
+
assertExpoModules();
|
|
334
|
+
return buildSyncFields(null);
|
|
335
|
+
} catch {
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
296
338
|
}
|
|
297
|
-
var
|
|
339
|
+
var _debug2, cachedDeviceInfo, nativeDeviceInfo;
|
|
298
340
|
var init_NativeDeviceInfo = __esm({
|
|
299
341
|
"src/utils/NativeDeviceInfo.ts"() {
|
|
300
342
|
"use strict";
|
|
301
|
-
|
|
302
|
-
deviceId: "unknown",
|
|
303
|
-
model: "unknown",
|
|
304
|
-
modelId: "unknown",
|
|
305
|
-
systemVersion: "0.0.0",
|
|
306
|
-
appVersion: "0.0.0",
|
|
307
|
-
buildNumber: "0",
|
|
308
|
-
bundleId: "unknown",
|
|
309
|
-
brand: "unknown",
|
|
310
|
-
systemName: "unknown",
|
|
311
|
-
totalDisk: 0,
|
|
312
|
-
freeDisk: 0,
|
|
313
|
-
totalRam: 0,
|
|
314
|
-
carrier: "unknown",
|
|
315
|
-
installerPackage: null,
|
|
316
|
-
idfv: null
|
|
317
|
-
};
|
|
343
|
+
init_ClientError();
|
|
318
344
|
_debug2 = false;
|
|
319
|
-
warnedOnce = false;
|
|
320
345
|
cachedDeviceInfo = null;
|
|
321
346
|
nativeDeviceInfo = {
|
|
322
347
|
getDeviceInfo,
|
|
@@ -331,6 +356,7 @@ var init_NativeDeviceInfo = __esm({
|
|
|
331
356
|
// src/utils/uuid.ts
|
|
332
357
|
var uuid_exports = {};
|
|
333
358
|
__export(uuid_exports, {
|
|
359
|
+
deterministicUUID: () => deterministicUUID,
|
|
334
360
|
generateUUID: () => generateUUID,
|
|
335
361
|
isValidUUIDv4: () => isValidUUIDv4
|
|
336
362
|
});
|
|
@@ -342,6 +368,28 @@ function generateUUID() {
|
|
|
342
368
|
return v.toString(16);
|
|
343
369
|
});
|
|
344
370
|
}
|
|
371
|
+
function deterministicUUID(seed) {
|
|
372
|
+
const fnv1a32 = (s, basis) => {
|
|
373
|
+
let h = basis >>> 0;
|
|
374
|
+
for (let i = 0; i < s.length; i++) {
|
|
375
|
+
h = Math.imul(h ^ s.charCodeAt(i), 16777619) >>> 0;
|
|
376
|
+
}
|
|
377
|
+
return h;
|
|
378
|
+
};
|
|
379
|
+
const B = 2166136261;
|
|
380
|
+
const h0 = fnv1a32(seed, B);
|
|
381
|
+
const h1 = fnv1a32(seed, B ^ 252645135);
|
|
382
|
+
const h2 = fnv1a32(seed, B ^ 4042322160);
|
|
383
|
+
const h3 = fnv1a32(seed, B ^ 2863311530);
|
|
384
|
+
const x = (n, len) => (n >>> 0).toString(16).padStart(len, "0").slice(-len);
|
|
385
|
+
const s1 = x(h0, 8);
|
|
386
|
+
const s2 = x(h1 >>> 16, 4);
|
|
387
|
+
const s3 = "4" + x(h1 & 4095, 3);
|
|
388
|
+
const vb = (8 | h2 >>> 30 & 3).toString(16);
|
|
389
|
+
const s4 = vb + x(h2 >>> 18 & 4095, 3);
|
|
390
|
+
const s5 = x(h2 & 65535, 4) + x(h3, 8);
|
|
391
|
+
return `${s1}-${s2}-${s3}-${s4}-${s5}`;
|
|
392
|
+
}
|
|
345
393
|
function isValidUUIDv4(value) {
|
|
346
394
|
if (!value) return false;
|
|
347
395
|
return UUID_V4_REGEX.test(value);
|
|
@@ -609,6 +657,10 @@ var init_IdentityManager = __esm({
|
|
|
609
657
|
if (!this.initialized) return "";
|
|
610
658
|
return this.distinctId ?? this.anonId ?? "";
|
|
611
659
|
}
|
|
660
|
+
/** Public stable identifier — returns the same value as `getDistinctId()`. */
|
|
661
|
+
getId() {
|
|
662
|
+
return this.getDistinctId();
|
|
663
|
+
}
|
|
612
664
|
getDeviceId() {
|
|
613
665
|
return this.deviceId;
|
|
614
666
|
}
|
|
@@ -880,7 +932,7 @@ var init_campaign = __esm({
|
|
|
880
932
|
});
|
|
881
933
|
|
|
882
934
|
// src/domains/identity/AdvertisingIdManager.ts
|
|
883
|
-
import { Platform } from "react-native";
|
|
935
|
+
import { Platform as Platform2 } from "react-native";
|
|
884
936
|
var ZERO_IDFA, AdvertisingIdManager, advertisingIdManager;
|
|
885
937
|
var init_AdvertisingIdManager = __esm({
|
|
886
938
|
"src/domains/identity/AdvertisingIdManager.ts"() {
|
|
@@ -904,7 +956,7 @@ var init_AdvertisingIdManager = __esm({
|
|
|
904
956
|
const fallback = { idfv: null, idfa: null, gaid: null, attStatus: "unavailable" };
|
|
905
957
|
try {
|
|
906
958
|
const tracking = await import("expo-tracking-transparency");
|
|
907
|
-
if (
|
|
959
|
+
if (Platform2.OS === "ios") {
|
|
908
960
|
if (!tracking.isAvailable()) {
|
|
909
961
|
const idfv2 = await this.collectIdfv();
|
|
910
962
|
const result2 = { ...fallback, idfv: idfv2 };
|
|
@@ -925,7 +977,7 @@ var init_AdvertisingIdManager = __esm({
|
|
|
925
977
|
if (idfv !== null) this.cached = result;
|
|
926
978
|
return result;
|
|
927
979
|
}
|
|
928
|
-
if (
|
|
980
|
+
if (Platform2.OS === "android") {
|
|
929
981
|
const rawGaid = tracking.getAdvertisingId();
|
|
930
982
|
const gaid = this.sanitizeId(rawGaid);
|
|
931
983
|
this.cached = { idfv: null, idfa: null, gaid, attStatus: "unavailable" };
|
|
@@ -1012,10 +1064,8 @@ async function collectDeviceInfo() {
|
|
|
1012
1064
|
const result = await nativeDeviceInfo.getDeviceInfo();
|
|
1013
1065
|
return {
|
|
1014
1066
|
appVersion: result.appVersion,
|
|
1015
|
-
deviceModel: result.
|
|
1016
|
-
osVersion: result.systemVersion
|
|
1017
|
-
darwinVersion: result.darwinVersion,
|
|
1018
|
-
webkitVersion: result.webkitVersion
|
|
1067
|
+
deviceModel: result.modelId,
|
|
1068
|
+
osVersion: result.systemVersion
|
|
1019
1069
|
};
|
|
1020
1070
|
}
|
|
1021
1071
|
var init_deviceInfo = __esm({
|
|
@@ -1410,6 +1460,12 @@ async function markInstallTracked(storage, installedAt) {
|
|
|
1410
1460
|
} catch {
|
|
1411
1461
|
}
|
|
1412
1462
|
}
|
|
1463
|
+
async function resolveInstallEventId(deviceKey, appKey) {
|
|
1464
|
+
if (deviceKey && deviceKey.trim().length > 0) {
|
|
1465
|
+
return deterministicUUID(`${appKey}:${deviceKey}`);
|
|
1466
|
+
}
|
|
1467
|
+
return getOrCreateInstallEventId();
|
|
1468
|
+
}
|
|
1413
1469
|
async function getOrCreateInstallEventId() {
|
|
1414
1470
|
const existing = await nativeStorage.get(INSTALL_KEYS.INSTALL_EVENT_ID);
|
|
1415
1471
|
if (existing) return existing;
|
|
@@ -1430,6 +1486,7 @@ var init_InstallIdempotency = __esm({
|
|
|
1430
1486
|
"src/domains/identity/InstallIdempotency.ts"() {
|
|
1431
1487
|
"use strict";
|
|
1432
1488
|
init_NativeStorage();
|
|
1489
|
+
init_uuid();
|
|
1433
1490
|
INSTALL_KEYS = {
|
|
1434
1491
|
/** Primary flag — set AFTER a successful $app_installed dispatch. */
|
|
1435
1492
|
INSTALL_TRACKED: "@paywallo:install_tracked",
|
|
@@ -1454,18 +1511,180 @@ var init_InstallIdempotency = __esm({
|
|
|
1454
1511
|
}
|
|
1455
1512
|
});
|
|
1456
1513
|
|
|
1514
|
+
// src/domains/identity/MetaBridge.ts
|
|
1515
|
+
import { NativeModules as NativeModules2 } from "react-native";
|
|
1516
|
+
var TIMEOUT_MS, NativeBridge, MetaBridge, metaBridge;
|
|
1517
|
+
var init_MetaBridge = __esm({
|
|
1518
|
+
"src/domains/identity/MetaBridge.ts"() {
|
|
1519
|
+
"use strict";
|
|
1520
|
+
TIMEOUT_MS = 2e3;
|
|
1521
|
+
NativeBridge = NativeModules2.PaywalloFBBridge ?? null;
|
|
1522
|
+
MetaBridge = class {
|
|
1523
|
+
constructor() {
|
|
1524
|
+
this.cachedAnonId = null;
|
|
1525
|
+
this.cachedDeferredLink = null;
|
|
1526
|
+
this.deferredLinkFetched = false;
|
|
1527
|
+
this.debug = false;
|
|
1528
|
+
}
|
|
1529
|
+
setDebug(debug) {
|
|
1530
|
+
this.debug = debug;
|
|
1531
|
+
}
|
|
1532
|
+
/** Sync read of the cached anonymous ID. Returns null until `getAnonymousID()` resolves once. */
|
|
1533
|
+
getCachedAnonymousID() {
|
|
1534
|
+
return this.cachedAnonId;
|
|
1535
|
+
}
|
|
1536
|
+
async getAnonymousID() {
|
|
1537
|
+
if (this.cachedAnonId) return this.cachedAnonId;
|
|
1538
|
+
if (!NativeBridge) return null;
|
|
1539
|
+
try {
|
|
1540
|
+
let timerId;
|
|
1541
|
+
const timeout = new Promise((resolve) => {
|
|
1542
|
+
timerId = setTimeout(() => resolve(null), TIMEOUT_MS);
|
|
1543
|
+
});
|
|
1544
|
+
const result = await Promise.race([NativeBridge.getAnonymousID(), timeout]);
|
|
1545
|
+
clearTimeout(timerId);
|
|
1546
|
+
if (result) this.cachedAnonId = result;
|
|
1547
|
+
return result;
|
|
1548
|
+
} catch {
|
|
1549
|
+
return null;
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
async fetchDeferredAppLink() {
|
|
1553
|
+
if (this.deferredLinkFetched) return this.cachedDeferredLink;
|
|
1554
|
+
if (!NativeBridge) return null;
|
|
1555
|
+
if (typeof NativeBridge.fetchDeferredAppLink !== "function") return null;
|
|
1556
|
+
try {
|
|
1557
|
+
let timerId;
|
|
1558
|
+
const timeout = new Promise((resolve) => {
|
|
1559
|
+
timerId = setTimeout(() => resolve(null), TIMEOUT_MS);
|
|
1560
|
+
});
|
|
1561
|
+
const result = await Promise.race([
|
|
1562
|
+
NativeBridge.fetchDeferredAppLink().catch(() => null),
|
|
1563
|
+
timeout
|
|
1564
|
+
]);
|
|
1565
|
+
clearTimeout(timerId);
|
|
1566
|
+
this.deferredLinkFetched = true;
|
|
1567
|
+
this.cachedDeferredLink = result;
|
|
1568
|
+
return result;
|
|
1569
|
+
} catch {
|
|
1570
|
+
this.deferredLinkFetched = true;
|
|
1571
|
+
this.cachedDeferredLink = null;
|
|
1572
|
+
return null;
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
async logEvent(name, params = {}) {
|
|
1576
|
+
if (!NativeBridge) return;
|
|
1577
|
+
try {
|
|
1578
|
+
await NativeBridge.logEvent(name, params);
|
|
1579
|
+
} catch (error) {
|
|
1580
|
+
console.error("[Paywallo:MetaBridge] logEvent failed", { name, error });
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
async logPurchase(amount, currency, params = {}) {
|
|
1584
|
+
if (!NativeBridge) return;
|
|
1585
|
+
try {
|
|
1586
|
+
await NativeBridge.logPurchase(amount, currency, params);
|
|
1587
|
+
} catch (error) {
|
|
1588
|
+
console.error("[Paywallo:MetaBridge] logPurchase failed", { amount, currency, error });
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
setUserID(userId) {
|
|
1592
|
+
if (!NativeBridge) return;
|
|
1593
|
+
try {
|
|
1594
|
+
NativeBridge.setUserID(userId);
|
|
1595
|
+
} catch (error) {
|
|
1596
|
+
console.error("[Paywallo:MetaBridge] setUserID failed", { error });
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
flush() {
|
|
1600
|
+
if (!NativeBridge) return;
|
|
1601
|
+
try {
|
|
1602
|
+
NativeBridge.flush();
|
|
1603
|
+
} catch (error) {
|
|
1604
|
+
console.error("[Paywallo:MetaBridge] flush failed", { error });
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
};
|
|
1608
|
+
metaBridge = new MetaBridge();
|
|
1609
|
+
}
|
|
1610
|
+
});
|
|
1611
|
+
|
|
1612
|
+
// src/domains/identity/deferredAppLink.ts
|
|
1613
|
+
function parseQuery(s) {
|
|
1614
|
+
const result = {};
|
|
1615
|
+
for (const pair of s.split("&")) {
|
|
1616
|
+
const eqIndex = pair.indexOf("=");
|
|
1617
|
+
if (eqIndex === -1) continue;
|
|
1618
|
+
try {
|
|
1619
|
+
const key = decodeURIComponent(pair.slice(0, eqIndex).replace(/\+/g, " "));
|
|
1620
|
+
const value = decodeURIComponent(pair.slice(eqIndex + 1).replace(/\+/g, " "));
|
|
1621
|
+
if (key) result[key] = value;
|
|
1622
|
+
} catch {
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
return result;
|
|
1626
|
+
}
|
|
1627
|
+
function parseDeferredAppLink(url) {
|
|
1628
|
+
if (!url || url.trim() === "") return null;
|
|
1629
|
+
const raw = url.slice(0, 2048);
|
|
1630
|
+
const cleanRaw = raw.split("#")[0];
|
|
1631
|
+
const qIndex = cleanRaw.indexOf("?");
|
|
1632
|
+
if (qIndex === -1) return null;
|
|
1633
|
+
const topParams = parseQuery(cleanRaw.slice(qIndex + 1));
|
|
1634
|
+
let fbclid = nz(topParams["fbclid"]);
|
|
1635
|
+
let utmSource = nz(topParams["utm_source"]);
|
|
1636
|
+
let utmMedium = nz(topParams["utm_medium"]);
|
|
1637
|
+
let utmCampaign = nz(topParams["utm_campaign"]);
|
|
1638
|
+
let ttclid = nz(topParams["ttclid"]);
|
|
1639
|
+
let trackingId = nz(topParams["tracking_id"]);
|
|
1640
|
+
let targetUrl = null;
|
|
1641
|
+
if (topParams["target_url"]) {
|
|
1642
|
+
try {
|
|
1643
|
+
targetUrl = decodeURIComponent(topParams["target_url"]);
|
|
1644
|
+
} catch {
|
|
1645
|
+
targetUrl = topParams["target_url"];
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
if (!fbclid && targetUrl) {
|
|
1649
|
+
const cleanTargetUrl = targetUrl.split("#")[0];
|
|
1650
|
+
const innerQ = cleanTargetUrl.indexOf("?");
|
|
1651
|
+
if (innerQ !== -1) {
|
|
1652
|
+
const inner = parseQuery(cleanTargetUrl.slice(innerQ + 1));
|
|
1653
|
+
fbclid = fbclid ?? nz(inner["fbclid"]);
|
|
1654
|
+
utmSource = utmSource ?? nz(inner["utm_source"]);
|
|
1655
|
+
utmMedium = utmMedium ?? nz(inner["utm_medium"]);
|
|
1656
|
+
utmCampaign = utmCampaign ?? nz(inner["utm_campaign"]);
|
|
1657
|
+
ttclid = ttclid ?? nz(inner["ttclid"]);
|
|
1658
|
+
trackingId = trackingId ?? nz(inner["tracking_id"]);
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
if (!fbclid && !ttclid && !trackingId && !utmSource && !utmMedium && !utmCampaign) {
|
|
1662
|
+
return null;
|
|
1663
|
+
}
|
|
1664
|
+
return { fbclid, utmSource, utmMedium, utmCampaign, ttclid, trackingId, targetUrl, raw };
|
|
1665
|
+
}
|
|
1666
|
+
var nz;
|
|
1667
|
+
var init_deferredAppLink = __esm({
|
|
1668
|
+
"src/domains/identity/deferredAppLink.ts"() {
|
|
1669
|
+
"use strict";
|
|
1670
|
+
nz = (v) => typeof v === "string" && v.trim().length > 0 ? v : null;
|
|
1671
|
+
}
|
|
1672
|
+
});
|
|
1673
|
+
|
|
1457
1674
|
// src/domains/identity/InstallReferrerManager.ts
|
|
1458
|
-
import { Platform as
|
|
1675
|
+
import { Platform as Platform3 } from "react-native";
|
|
1459
1676
|
var REFERRER_TIMEOUT_MS, InstallReferrerManager, installReferrerManager;
|
|
1460
1677
|
var init_InstallReferrerManager = __esm({
|
|
1461
1678
|
"src/domains/identity/InstallReferrerManager.ts"() {
|
|
1462
1679
|
"use strict";
|
|
1463
1680
|
init_NativeDeviceInfo();
|
|
1681
|
+
init_MetaBridge();
|
|
1682
|
+
init_deferredAppLink();
|
|
1464
1683
|
REFERRER_TIMEOUT_MS = 5e3;
|
|
1465
1684
|
InstallReferrerManager = class {
|
|
1466
1685
|
async getReferrer() {
|
|
1467
|
-
if (
|
|
1468
|
-
return this.
|
|
1686
|
+
if (Platform3.OS !== "android") {
|
|
1687
|
+
return this.fetchDeferredReferrer();
|
|
1469
1688
|
}
|
|
1470
1689
|
let timerId;
|
|
1471
1690
|
const timeoutPromise = new Promise((resolve) => {
|
|
@@ -1476,6 +1695,29 @@ var init_InstallReferrerManager = __esm({
|
|
|
1476
1695
|
clearTimeout(timerId);
|
|
1477
1696
|
return result;
|
|
1478
1697
|
}
|
|
1698
|
+
async fetchDeferredReferrer() {
|
|
1699
|
+
try {
|
|
1700
|
+
const url = await metaBridge.fetchDeferredAppLink();
|
|
1701
|
+
const parsed = parseDeferredAppLink(url);
|
|
1702
|
+
if (!parsed) return this.emptyResult();
|
|
1703
|
+
return {
|
|
1704
|
+
rawReferrer: parsed.targetUrl ?? parsed.raw,
|
|
1705
|
+
trackingId: parsed.trackingId,
|
|
1706
|
+
fbclid: parsed.fbclid,
|
|
1707
|
+
utmSource: parsed.utmSource,
|
|
1708
|
+
utmMedium: parsed.utmMedium,
|
|
1709
|
+
utmCampaign: parsed.utmCampaign,
|
|
1710
|
+
ttclid: parsed.ttclid,
|
|
1711
|
+
tiktokCampaignId: null,
|
|
1712
|
+
tiktokAdgroupId: null,
|
|
1713
|
+
tiktokAdId: null,
|
|
1714
|
+
installReferrerRaw: parsed.raw,
|
|
1715
|
+
installReferrerSource: "meta_deferred"
|
|
1716
|
+
};
|
|
1717
|
+
} catch {
|
|
1718
|
+
return this.emptyResult();
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1479
1721
|
async fetchReferrer() {
|
|
1480
1722
|
try {
|
|
1481
1723
|
const referrer = await nativeDeviceInfo.getInstallReferrer();
|
|
@@ -1536,81 +1778,8 @@ var init_InstallReferrerManager = __esm({
|
|
|
1536
1778
|
}
|
|
1537
1779
|
});
|
|
1538
1780
|
|
|
1539
|
-
// src/domains/identity/MetaBridge.ts
|
|
1540
|
-
import { NativeModules as NativeModules3 } from "react-native";
|
|
1541
|
-
var TIMEOUT_MS, NativeBridge, MetaBridge, metaBridge;
|
|
1542
|
-
var init_MetaBridge = __esm({
|
|
1543
|
-
"src/domains/identity/MetaBridge.ts"() {
|
|
1544
|
-
"use strict";
|
|
1545
|
-
TIMEOUT_MS = 2e3;
|
|
1546
|
-
NativeBridge = NativeModules3.PaywalloFBBridge ?? null;
|
|
1547
|
-
MetaBridge = class {
|
|
1548
|
-
constructor() {
|
|
1549
|
-
this.cachedAnonId = null;
|
|
1550
|
-
this.debug = false;
|
|
1551
|
-
}
|
|
1552
|
-
setDebug(debug) {
|
|
1553
|
-
this.debug = debug;
|
|
1554
|
-
}
|
|
1555
|
-
/** Sync read of the cached anonymous ID. Returns null until `getAnonymousID()` resolves once. */
|
|
1556
|
-
getCachedAnonymousID() {
|
|
1557
|
-
return this.cachedAnonId;
|
|
1558
|
-
}
|
|
1559
|
-
async getAnonymousID() {
|
|
1560
|
-
if (this.cachedAnonId) return this.cachedAnonId;
|
|
1561
|
-
if (!NativeBridge) return null;
|
|
1562
|
-
try {
|
|
1563
|
-
let timerId;
|
|
1564
|
-
const timeout = new Promise((resolve) => {
|
|
1565
|
-
timerId = setTimeout(() => resolve(null), TIMEOUT_MS);
|
|
1566
|
-
});
|
|
1567
|
-
const result = await Promise.race([NativeBridge.getAnonymousID(), timeout]);
|
|
1568
|
-
clearTimeout(timerId);
|
|
1569
|
-
if (result) this.cachedAnonId = result;
|
|
1570
|
-
return result;
|
|
1571
|
-
} catch {
|
|
1572
|
-
return null;
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
async logEvent(name, params = {}) {
|
|
1576
|
-
if (!NativeBridge) return;
|
|
1577
|
-
try {
|
|
1578
|
-
await NativeBridge.logEvent(name, params);
|
|
1579
|
-
} catch (error) {
|
|
1580
|
-
console.error("[Paywallo:MetaBridge] logEvent failed", { name, error });
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1583
|
-
async logPurchase(amount, currency, params = {}) {
|
|
1584
|
-
if (!NativeBridge) return;
|
|
1585
|
-
try {
|
|
1586
|
-
await NativeBridge.logPurchase(amount, currency, params);
|
|
1587
|
-
} catch (error) {
|
|
1588
|
-
console.error("[Paywallo:MetaBridge] logPurchase failed", { amount, currency, error });
|
|
1589
|
-
}
|
|
1590
|
-
}
|
|
1591
|
-
setUserID(userId) {
|
|
1592
|
-
if (!NativeBridge) return;
|
|
1593
|
-
try {
|
|
1594
|
-
NativeBridge.setUserID(userId);
|
|
1595
|
-
} catch (error) {
|
|
1596
|
-
console.error("[Paywallo:MetaBridge] setUserID failed", { error });
|
|
1597
|
-
}
|
|
1598
|
-
}
|
|
1599
|
-
flush() {
|
|
1600
|
-
if (!NativeBridge) return;
|
|
1601
|
-
try {
|
|
1602
|
-
NativeBridge.flush();
|
|
1603
|
-
} catch (error) {
|
|
1604
|
-
console.error("[Paywallo:MetaBridge] flush failed", { error });
|
|
1605
|
-
}
|
|
1606
|
-
}
|
|
1607
|
-
};
|
|
1608
|
-
metaBridge = new MetaBridge();
|
|
1609
|
-
}
|
|
1610
|
-
});
|
|
1611
|
-
|
|
1612
1781
|
// src/domains/identity/InstallTracker.ts
|
|
1613
|
-
import { Dimensions, PixelRatio, Platform as
|
|
1782
|
+
import { Dimensions, PixelRatio, Platform as Platform4 } from "react-native";
|
|
1614
1783
|
var InstallTracker;
|
|
1615
1784
|
var init_InstallTracker = __esm({
|
|
1616
1785
|
"src/domains/identity/InstallTracker.ts"() {
|
|
@@ -1678,44 +1847,17 @@ var init_InstallTracker = __esm({
|
|
|
1678
1847
|
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone ?? "unknown";
|
|
1679
1848
|
const info = await nativeDeviceInfo.getDeviceInfo();
|
|
1680
1849
|
const deviceModel = info.modelId || info.model || "unknown";
|
|
1681
|
-
const
|
|
1850
|
+
const osVersion2 = info.systemVersion || "unknown";
|
|
1682
1851
|
const appVersion = info.appVersion || "unknown";
|
|
1683
1852
|
const buildNumber = info.buildNumber || "0";
|
|
1684
|
-
let
|
|
1685
|
-
try {
|
|
1686
|
-
carrier = info.carrier ?? "unknown";
|
|
1687
|
-
} catch {
|
|
1688
|
-
}
|
|
1689
|
-
let totalDisk = 0;
|
|
1690
|
-
try {
|
|
1691
|
-
totalDisk = Number(info.totalDisk) || 0;
|
|
1692
|
-
} catch {
|
|
1693
|
-
}
|
|
1694
|
-
let freeDisk = 0;
|
|
1695
|
-
try {
|
|
1696
|
-
freeDisk = Number(info.freeDisk) || 0;
|
|
1697
|
-
} catch {
|
|
1698
|
-
}
|
|
1699
|
-
let totalRam = 0;
|
|
1700
|
-
try {
|
|
1701
|
-
totalRam = Number(info.totalRam) || 0;
|
|
1702
|
-
} catch {
|
|
1703
|
-
}
|
|
1704
|
-
let brand = "unknown";
|
|
1705
|
-
try {
|
|
1706
|
-
brand = info.brand ?? "unknown";
|
|
1707
|
-
} catch {
|
|
1708
|
-
}
|
|
1709
|
-
let installerPackage = null;
|
|
1853
|
+
let brand2 = "unknown";
|
|
1710
1854
|
try {
|
|
1711
|
-
|
|
1712
|
-
installerPackage = info.installerPackage ?? null;
|
|
1713
|
-
}
|
|
1855
|
+
brand2 = info.brand ?? "unknown";
|
|
1714
1856
|
} catch {
|
|
1715
1857
|
}
|
|
1716
1858
|
deviceData = {
|
|
1717
1859
|
deviceModel,
|
|
1718
|
-
osVersion,
|
|
1860
|
+
osVersion: osVersion2,
|
|
1719
1861
|
appVersion,
|
|
1720
1862
|
buildNumber,
|
|
1721
1863
|
...screenWidth > 0 && { screenWidth },
|
|
@@ -1723,21 +1865,20 @@ var init_InstallTracker = __esm({
|
|
|
1723
1865
|
...screenDensity > 0 && { screenDensity },
|
|
1724
1866
|
timezone,
|
|
1725
1867
|
locale,
|
|
1726
|
-
...
|
|
1727
|
-
...
|
|
1728
|
-
...freeDisk > 0 && { freeDisk },
|
|
1729
|
-
...totalRam > 0 && { totalRam },
|
|
1730
|
-
...brand !== "unknown" && { brand },
|
|
1731
|
-
...installerPackage && { installerPackage }
|
|
1868
|
+
...brand2 !== "unknown" && { brand: brand2 },
|
|
1869
|
+
...info.regionCode && { country: info.regionCode }
|
|
1732
1870
|
};
|
|
1733
1871
|
} catch {
|
|
1734
1872
|
}
|
|
1735
|
-
const [fbAnonId, referrer, adIds
|
|
1873
|
+
const [fbAnonId, referrer, adIds] = await Promise.all([
|
|
1736
1874
|
metaBridge.getAnonymousID(),
|
|
1737
1875
|
installReferrerManager.getReferrer(),
|
|
1738
|
-
this.advertisingIdManager.collect(requestATT)
|
|
1739
|
-
getOrCreateInstallEventId()
|
|
1876
|
+
this.advertisingIdManager.collect(requestATT)
|
|
1740
1877
|
]);
|
|
1878
|
+
const installEventId = await resolveInstallEventId(
|
|
1879
|
+
adIds.idfv ?? adIds.gaid ?? null,
|
|
1880
|
+
apiClient.getAppKey()
|
|
1881
|
+
);
|
|
1741
1882
|
if (referrer.fbclid || referrer.utmSource || referrer.utmMedium || referrer.utmCampaign || referrer.ttclid || referrer.tiktokCampaignId || referrer.tiktokAdgroupId || referrer.tiktokAdId) {
|
|
1742
1883
|
attributionTracker.capture({
|
|
1743
1884
|
...referrer.fbclid && { fbclid: referrer.fbclid },
|
|
@@ -1768,7 +1909,7 @@ var init_InstallTracker = __esm({
|
|
|
1768
1909
|
}
|
|
1769
1910
|
await apiClient.trackEvent("$app_installed", distinctId, {
|
|
1770
1911
|
installedAt,
|
|
1771
|
-
platform:
|
|
1912
|
+
platform: Platform4.OS,
|
|
1772
1913
|
installEventId,
|
|
1773
1914
|
...sessionId && { sessionId },
|
|
1774
1915
|
...deviceData,
|
|
@@ -1808,7 +1949,7 @@ var init_InstallTracker = __esm({
|
|
|
1808
1949
|
method: "POST",
|
|
1809
1950
|
headers: { "Content-Type": "application/json", "X-App-Key": appKey },
|
|
1810
1951
|
body: JSON.stringify({
|
|
1811
|
-
platform:
|
|
1952
|
+
platform: Platform4.OS,
|
|
1812
1953
|
...adIds.idfv && { idfv: adIds.idfv },
|
|
1813
1954
|
...adIds.gaid && { gaid: adIds.gaid },
|
|
1814
1955
|
...referrer.rawReferrer && { installReferrer: referrer.rawReferrer },
|
|
@@ -1818,6 +1959,7 @@ var init_InstallTracker = __esm({
|
|
|
1818
1959
|
screenHeight: deviceData.screenHeight,
|
|
1819
1960
|
timezone: deviceData.timezone,
|
|
1820
1961
|
language: deviceData.locale,
|
|
1962
|
+
...deviceData.country && { country: deviceData.country },
|
|
1821
1963
|
fbAnonId: anonId,
|
|
1822
1964
|
installTimestamp: new Date(installedAt).toISOString()
|
|
1823
1965
|
}),
|
|
@@ -1938,12 +2080,12 @@ var init_identity = __esm({
|
|
|
1938
2080
|
});
|
|
1939
2081
|
|
|
1940
2082
|
// src/domains/notifications/bridges/NativePushBridge.ts
|
|
1941
|
-
import { NativeEventEmitter, NativeModules as
|
|
2083
|
+
import { NativeEventEmitter, NativeModules as NativeModules3, Platform as Platform5 } from "react-native";
|
|
1942
2084
|
function getPaywalloNotifications() {
|
|
1943
|
-
return
|
|
2085
|
+
return NativeModules3.PaywalloNotifications ?? null;
|
|
1944
2086
|
}
|
|
1945
2087
|
function isNativePushAvailable() {
|
|
1946
|
-
return !!
|
|
2088
|
+
return !!NativeModules3.PaywalloNotifications;
|
|
1947
2089
|
}
|
|
1948
2090
|
function mapStatusToNumber(status) {
|
|
1949
2091
|
switch (status) {
|
|
@@ -1965,7 +2107,7 @@ function mapStatusToNumber(status) {
|
|
|
1965
2107
|
}
|
|
1966
2108
|
function getEmitter() {
|
|
1967
2109
|
if (_emitter) return _emitter;
|
|
1968
|
-
const nativeMod =
|
|
2110
|
+
const nativeMod = NativeModules3.PaywalloNotifications;
|
|
1969
2111
|
if (!nativeMod) return null;
|
|
1970
2112
|
_emitter = new NativeEventEmitter(nativeMod);
|
|
1971
2113
|
return _emitter;
|
|
@@ -1986,7 +2128,7 @@ var init_NativePushBridge = __esm({
|
|
|
1986
2128
|
}
|
|
1987
2129
|
}
|
|
1988
2130
|
async getAPNSToken() {
|
|
1989
|
-
if (
|
|
2131
|
+
if (Platform5.OS !== "ios") return null;
|
|
1990
2132
|
return this.getToken();
|
|
1991
2133
|
}
|
|
1992
2134
|
async requestPermission(options) {
|
|
@@ -2073,7 +2215,7 @@ var DEFAULT_API_URL, DEFAULT_WEB_URL;
|
|
|
2073
2215
|
var init_constants = __esm({
|
|
2074
2216
|
"src/core/constants.ts"() {
|
|
2075
2217
|
"use strict";
|
|
2076
|
-
DEFAULT_API_URL = "https://
|
|
2218
|
+
DEFAULT_API_URL = "https://incommensurably-unprotrudent-brooklynn.ngrok-free.dev";
|
|
2077
2219
|
DEFAULT_WEB_URL = "https://paywallo.com.br";
|
|
2078
2220
|
}
|
|
2079
2221
|
});
|
|
@@ -2220,7 +2362,7 @@ var init_ForegroundHandler = __esm({
|
|
|
2220
2362
|
});
|
|
2221
2363
|
|
|
2222
2364
|
// src/domains/notifications/NotificationEventTracker.ts
|
|
2223
|
-
import { Platform as
|
|
2365
|
+
import { Platform as Platform6 } from "react-native";
|
|
2224
2366
|
function getTimezone() {
|
|
2225
2367
|
try {
|
|
2226
2368
|
return Intl.DateTimeFormat().resolvedOptions().timeZone ?? "UTC";
|
|
@@ -2229,7 +2371,7 @@ function getTimezone() {
|
|
|
2229
2371
|
}
|
|
2230
2372
|
}
|
|
2231
2373
|
function detectPlatform() {
|
|
2232
|
-
return
|
|
2374
|
+
return Platform6.OS === "ios" ? "ios" : "android";
|
|
2233
2375
|
}
|
|
2234
2376
|
var SEEN_MESSAGES_KEY, LEGACY_SEEN_MESSAGES_KEY, SEEN_TTL_MS, MAX_SEEN_SIZE, NOTIFICATION_TYPE_TO_FAMILY, NotificationEventTracker;
|
|
2235
2377
|
var init_NotificationEventTracker = __esm({
|
|
@@ -2405,8 +2547,8 @@ var init_NotificationHandlerSetup = __esm({
|
|
|
2405
2547
|
// src/core/version.ts
|
|
2406
2548
|
function resolveVersion() {
|
|
2407
2549
|
try {
|
|
2408
|
-
if ("2.
|
|
2409
|
-
return "2.
|
|
2550
|
+
if ("2.5.0") {
|
|
2551
|
+
return "2.5.0";
|
|
2410
2552
|
}
|
|
2411
2553
|
} catch {
|
|
2412
2554
|
}
|
|
@@ -2421,7 +2563,7 @@ var init_version = __esm({
|
|
|
2421
2563
|
});
|
|
2422
2564
|
|
|
2423
2565
|
// src/domains/notifications/utils.ts
|
|
2424
|
-
import { Platform as
|
|
2566
|
+
import { Platform as Platform7 } from "react-native";
|
|
2425
2567
|
function sleep(ms, signal) {
|
|
2426
2568
|
return new Promise((resolve, reject) => {
|
|
2427
2569
|
if (signal?.aborted) {
|
|
@@ -2449,8 +2591,8 @@ async function getLocale() {
|
|
|
2449
2591
|
return clampLocale(raw);
|
|
2450
2592
|
} catch {
|
|
2451
2593
|
try {
|
|
2452
|
-
const { getLocales } = __require("expo-localization");
|
|
2453
|
-
const code =
|
|
2594
|
+
const { getLocales: getLocales2 } = __require("expo-localization");
|
|
2595
|
+
const code = getLocales2()[0]?.languageCode ?? "en";
|
|
2454
2596
|
return clampLocale(code);
|
|
2455
2597
|
} catch {
|
|
2456
2598
|
return "en";
|
|
@@ -2477,7 +2619,7 @@ function mapPermissionStatus(status) {
|
|
|
2477
2619
|
}
|
|
2478
2620
|
}
|
|
2479
2621
|
function detectPlatform2() {
|
|
2480
|
-
return
|
|
2622
|
+
return Platform7.OS === "ios" ? "ios" : "android";
|
|
2481
2623
|
}
|
|
2482
2624
|
async function getAppVersion() {
|
|
2483
2625
|
try {
|
|
@@ -2654,7 +2796,7 @@ var init_NotificationTokenOps = __esm({
|
|
|
2654
2796
|
});
|
|
2655
2797
|
|
|
2656
2798
|
// src/domains/notifications/PermissionManager.ts
|
|
2657
|
-
import { Platform as
|
|
2799
|
+
import { Platform as Platform8 } from "react-native";
|
|
2658
2800
|
function mapPermissionStatus2(status) {
|
|
2659
2801
|
switch (status) {
|
|
2660
2802
|
case AUTH_STATUS2.AUTHORIZED:
|
|
@@ -2668,7 +2810,7 @@ function mapPermissionStatus2(status) {
|
|
|
2668
2810
|
}
|
|
2669
2811
|
}
|
|
2670
2812
|
function getPlatform() {
|
|
2671
|
-
return
|
|
2813
|
+
return Platform8;
|
|
2672
2814
|
}
|
|
2673
2815
|
function needsAndroidRuntimePermission(platform) {
|
|
2674
2816
|
if (platform.OS !== "android") return false;
|
|
@@ -3269,7 +3411,7 @@ var init_paywallHeartbeat = __esm({
|
|
|
3269
3411
|
});
|
|
3270
3412
|
|
|
3271
3413
|
// src/utils/localization.ts
|
|
3272
|
-
import { NativeModules as
|
|
3414
|
+
import { NativeModules as NativeModules4, Platform as Platform9 } from "react-native";
|
|
3273
3415
|
function setCurrentLanguage(language) {
|
|
3274
3416
|
currentLanguage = language;
|
|
3275
3417
|
}
|
|
@@ -3289,11 +3431,11 @@ function getLocalizedString(text) {
|
|
|
3289
3431
|
}
|
|
3290
3432
|
function detectDeviceLanguage() {
|
|
3291
3433
|
try {
|
|
3292
|
-
if (
|
|
3293
|
-
const settings =
|
|
3434
|
+
if (Platform9.OS === "ios") {
|
|
3435
|
+
const settings = NativeModules4.SettingsManager?.settings;
|
|
3294
3436
|
return settings?.AppleLocale ?? settings?.AppleLanguages?.[0] ?? "en-US";
|
|
3295
3437
|
}
|
|
3296
|
-
return
|
|
3438
|
+
return NativeModules4.I18nManager?.localeIdentifier ?? "en-US";
|
|
3297
3439
|
} catch {
|
|
3298
3440
|
return "en-US";
|
|
3299
3441
|
}
|
|
@@ -3464,8 +3606,8 @@ import {
|
|
|
3464
3606
|
findNodeHandle,
|
|
3465
3607
|
Linking as Linking2,
|
|
3466
3608
|
NativeEventEmitter as NativeEventEmitter2,
|
|
3467
|
-
NativeModules as
|
|
3468
|
-
Platform as
|
|
3609
|
+
NativeModules as NativeModules5,
|
|
3610
|
+
Platform as Platform10,
|
|
3469
3611
|
requireNativeComponent,
|
|
3470
3612
|
StyleSheet as StyleSheet2,
|
|
3471
3613
|
UIManager,
|
|
@@ -3485,7 +3627,7 @@ function getNativeWebView() {
|
|
|
3485
3627
|
}
|
|
3486
3628
|
function getWebViewEmitter() {
|
|
3487
3629
|
if (webViewEmitter) return webViewEmitter;
|
|
3488
|
-
const mod =
|
|
3630
|
+
const mod = NativeModules5.PaywalloWebViewModule;
|
|
3489
3631
|
if (!mod) return null;
|
|
3490
3632
|
webViewEmitter = new NativeEventEmitter2(mod);
|
|
3491
3633
|
return webViewEmitter;
|
|
@@ -3578,7 +3720,7 @@ function PaywallWebView({
|
|
|
3578
3720
|
}
|
|
3579
3721
|
}, []);
|
|
3580
3722
|
const injectScript = useCallback((script) => {
|
|
3581
|
-
if (
|
|
3723
|
+
if (Platform10.OS === "android") {
|
|
3582
3724
|
const handle = findNodeHandle(webViewRef.current);
|
|
3583
3725
|
if (handle == null) return;
|
|
3584
3726
|
UIManager.dispatchViewManagerCommand(handle, "injectJavaScript", [script]);
|
|
@@ -3722,7 +3864,7 @@ function PaywallWebView({
|
|
|
3722
3864
|
injectScript(buildPurchaseStateScript(isPurchasing));
|
|
3723
3865
|
}, [isPurchasing, injectScript]);
|
|
3724
3866
|
useEffect2(() => {
|
|
3725
|
-
if (
|
|
3867
|
+
if (Platform10.OS !== "android") return;
|
|
3726
3868
|
const onBackPress = () => {
|
|
3727
3869
|
if (!paywallDataSent.current) return false;
|
|
3728
3870
|
injectScript(buildBackButtonScript());
|
|
@@ -3763,269 +3905,25 @@ var init_PaywallWebView = __esm({
|
|
|
3763
3905
|
}
|
|
3764
3906
|
});
|
|
3765
3907
|
|
|
3766
|
-
// src/domains/iap/PurchaseError.ts
|
|
3767
|
-
function createPurchaseError(code, message) {
|
|
3768
|
-
return new PurchaseError(
|
|
3769
|
-
PURCHASE_ERROR_CODES[code],
|
|
3770
|
-
message ?? DEFAULT_MESSAGES[code] ?? "Unknown purchase error",
|
|
3771
|
-
code === "USER_CANCELLED"
|
|
3772
|
-
);
|
|
3773
|
-
}
|
|
3774
|
-
var PurchaseError, PURCHASE_ERROR_CODES, DEFAULT_MESSAGES;
|
|
3775
|
-
var init_PurchaseError = __esm({
|
|
3776
|
-
"src/domains/iap/PurchaseError.ts"() {
|
|
3777
|
-
"use strict";
|
|
3778
|
-
init_PaywalloError();
|
|
3779
|
-
PurchaseError = class extends PaywalloError {
|
|
3780
|
-
constructor(code, message, userCancelled = false, httpStatus) {
|
|
3781
|
-
super("purchase", code, message);
|
|
3782
|
-
this.name = "PurchaseError";
|
|
3783
|
-
this.userCancelled = userCancelled;
|
|
3784
|
-
this.httpStatus = httpStatus;
|
|
3785
|
-
}
|
|
3786
|
-
};
|
|
3787
|
-
PURCHASE_ERROR_CODES = {
|
|
3788
|
-
NOT_INITIALIZED: "PURCHASE_NOT_INITIALIZED",
|
|
3789
|
-
PRODUCT_NOT_FOUND: "PRODUCT_NOT_FOUND",
|
|
3790
|
-
PURCHASE_FAILED: "PURCHASE_FAILED",
|
|
3791
|
-
RESTORE_FAILED: "RESTORE_FAILED",
|
|
3792
|
-
VALIDATION_FAILED: "VALIDATION_FAILED",
|
|
3793
|
-
USER_CANCELLED: "USER_CANCELLED",
|
|
3794
|
-
NETWORK_ERROR: "PURCHASE_NETWORK_ERROR",
|
|
3795
|
-
STORE_ERROR: "PURCHASE_STORE_ERROR",
|
|
3796
|
-
PENDING_PURCHASE: "PURCHASE_PENDING",
|
|
3797
|
-
DEFERRED_PURCHASE: "PURCHASE_DEFERRED",
|
|
3798
|
-
STORE_NOT_AVAILABLE: "PURCHASE_STORE_NOT_AVAILABLE"
|
|
3799
|
-
};
|
|
3800
|
-
DEFAULT_MESSAGES = {
|
|
3801
|
-
NOT_INITIALIZED: "Purchase controller not initialized. Call init() first.",
|
|
3802
|
-
PRODUCT_NOT_FOUND: "Product not found in store.",
|
|
3803
|
-
PURCHASE_FAILED: "Purchase failed. Please try again.",
|
|
3804
|
-
RESTORE_FAILED: "Failed to restore purchases. Please try again.",
|
|
3805
|
-
VALIDATION_FAILED: "Failed to validate purchase with server.",
|
|
3806
|
-
USER_CANCELLED: "Purchase was cancelled.",
|
|
3807
|
-
NETWORK_ERROR: "Network error. Please check your connection.",
|
|
3808
|
-
STORE_ERROR: "Store error. Please try again later.",
|
|
3809
|
-
PENDING_PURCHASE: "Purchase is pending approval.",
|
|
3810
|
-
DEFERRED_PURCHASE: "Purchase is deferred (e.g., parental approval required).",
|
|
3811
|
-
STORE_NOT_AVAILABLE: "In-app purchases are not available on this device."
|
|
3812
|
-
};
|
|
3813
|
-
}
|
|
3814
|
-
});
|
|
3815
|
-
|
|
3816
|
-
// src/domains/iap/NativeStoreKit.ts
|
|
3817
|
-
var NativeStoreKit_exports = {};
|
|
3818
|
-
__export(NativeStoreKit_exports, {
|
|
3819
|
-
NativeStoreKit: () => NativeStoreKit,
|
|
3820
|
-
nativeStoreKit: () => nativeStoreKit
|
|
3821
|
-
});
|
|
3822
|
-
import { NativeEventEmitter as NativeEventEmitter3, NativeModules as NativeModules7, Platform as Platform10 } from "react-native";
|
|
3823
|
-
function isAvailable3() {
|
|
3824
|
-
return PaywalloStoreKitNative != null;
|
|
3825
|
-
}
|
|
3826
|
-
function mapNativeProduct(native) {
|
|
3827
|
-
const product = {
|
|
3828
|
-
productId: native.productId,
|
|
3829
|
-
title: native.title,
|
|
3830
|
-
description: native.description,
|
|
3831
|
-
price: native.price,
|
|
3832
|
-
priceValue: native.priceValue,
|
|
3833
|
-
currency: native.currency ?? "USD",
|
|
3834
|
-
localizedPrice: native.localizedPrice,
|
|
3835
|
-
type: mapProductType(native.type),
|
|
3836
|
-
subscriptionPeriod: native.subscriptionPeriod,
|
|
3837
|
-
introductoryPrice: native.introductoryPrice,
|
|
3838
|
-
introductoryPriceValue: native.introductoryPriceValue,
|
|
3839
|
-
freeTrialPeriod: native.freeTrialPeriod
|
|
3840
|
-
};
|
|
3841
|
-
if (native.subscriptionPeriod) {
|
|
3842
|
-
const prices = calculatePerPeriodPrices(native.priceValue, native.subscriptionPeriod);
|
|
3843
|
-
product.pricePerMonth = prices.perMonth;
|
|
3844
|
-
product.pricePerWeek = prices.perWeek;
|
|
3845
|
-
product.pricePerDay = prices.perDay;
|
|
3846
|
-
}
|
|
3847
|
-
return product;
|
|
3848
|
-
}
|
|
3849
|
-
function mapProductType(type) {
|
|
3850
|
-
switch (type) {
|
|
3851
|
-
case "subscription":
|
|
3852
|
-
case "autoRenewable":
|
|
3853
|
-
return "subscription";
|
|
3854
|
-
case "consumable":
|
|
3855
|
-
return "consumable";
|
|
3856
|
-
default:
|
|
3857
|
-
return "non_consumable";
|
|
3858
|
-
}
|
|
3859
|
-
}
|
|
3860
|
-
function calculatePerPeriodPrices(priceValue, period) {
|
|
3861
|
-
const totalDays = periodToDays(period);
|
|
3862
|
-
if (totalDays <= 0) return {};
|
|
3863
|
-
const perDay = priceValue / totalDays;
|
|
3864
|
-
const perWeek = perDay * 7;
|
|
3865
|
-
const perMonth = perDay * 30;
|
|
3866
|
-
return {
|
|
3867
|
-
perDay: perDay.toFixed(2),
|
|
3868
|
-
perWeek: perWeek.toFixed(2),
|
|
3869
|
-
perMonth: perMonth.toFixed(2)
|
|
3870
|
-
};
|
|
3871
|
-
}
|
|
3872
|
-
function periodToDays(period) {
|
|
3873
|
-
const match = period.match(/^P(\d+)([DWMY])$/);
|
|
3874
|
-
if (!match) return 0;
|
|
3875
|
-
const value = parseInt(match[1], 10);
|
|
3876
|
-
switch (match[2]) {
|
|
3877
|
-
case "D":
|
|
3878
|
-
return value;
|
|
3879
|
-
case "W":
|
|
3880
|
-
return value * 7;
|
|
3881
|
-
case "M":
|
|
3882
|
-
return value * 30;
|
|
3883
|
-
case "Y":
|
|
3884
|
-
return value * 365;
|
|
3885
|
-
default:
|
|
3886
|
-
return 0;
|
|
3887
|
-
}
|
|
3888
|
-
}
|
|
3889
|
-
function mapNativePurchase(native) {
|
|
3890
|
-
return {
|
|
3891
|
-
productId: native.productId,
|
|
3892
|
-
transactionId: native.transactionId,
|
|
3893
|
-
transactionDate: native.transactionDate,
|
|
3894
|
-
receipt: native.receipt,
|
|
3895
|
-
platform: Platform10.OS
|
|
3896
|
-
};
|
|
3897
|
-
}
|
|
3898
|
-
function normalizeTransactionUpdate(raw) {
|
|
3899
|
-
if (!raw || typeof raw !== "object") return null;
|
|
3900
|
-
const r = raw;
|
|
3901
|
-
const type = r.type;
|
|
3902
|
-
if (type !== "renewed" && type !== "refunded" && type !== "canceled") return null;
|
|
3903
|
-
const transactionId = typeof r.transactionId === "string" ? r.transactionId : null;
|
|
3904
|
-
const productId = typeof r.productId === "string" ? r.productId : null;
|
|
3905
|
-
if (!transactionId) return null;
|
|
3906
|
-
const update = {
|
|
3907
|
-
type,
|
|
3908
|
-
transactionId,
|
|
3909
|
-
productId: productId ?? ""
|
|
3910
|
-
};
|
|
3911
|
-
if (typeof r.amount === "number") update.amount = r.amount;
|
|
3912
|
-
if (typeof r.currency === "string") update.currency = r.currency;
|
|
3913
|
-
return update;
|
|
3914
|
-
}
|
|
3915
|
-
var TRANSACTION_UPDATE_EVENT, PaywalloStoreKitNative, nativeStoreKit, NativeStoreKit;
|
|
3916
|
-
var init_NativeStoreKit = __esm({
|
|
3917
|
-
"src/domains/iap/NativeStoreKit.ts"() {
|
|
3918
|
-
"use strict";
|
|
3919
|
-
init_PaywalloClient();
|
|
3920
|
-
init_uuid();
|
|
3921
|
-
init_IdentityManager();
|
|
3922
|
-
init_PurchaseError();
|
|
3923
|
-
TRANSACTION_UPDATE_EVENT = "PaywalloTransactionUpdate";
|
|
3924
|
-
PaywalloStoreKitNative = NativeModules7.PaywalloStoreKit ?? null;
|
|
3925
|
-
nativeStoreKit = {
|
|
3926
|
-
isAvailable: isAvailable3,
|
|
3927
|
-
async getProducts(productIds) {
|
|
3928
|
-
if (!PaywalloStoreKitNative) {
|
|
3929
|
-
return [];
|
|
3930
|
-
}
|
|
3931
|
-
const nativeProducts = await PaywalloStoreKitNative.getProducts(productIds);
|
|
3932
|
-
return nativeProducts.map(mapNativeProduct);
|
|
3933
|
-
},
|
|
3934
|
-
async purchase(productId) {
|
|
3935
|
-
if (!PaywalloStoreKitNative) {
|
|
3936
|
-
throw new PurchaseError(
|
|
3937
|
-
PURCHASE_ERROR_CODES.STORE_NOT_AVAILABLE,
|
|
3938
|
-
"StoreKit native module not available"
|
|
3939
|
-
);
|
|
3940
|
-
}
|
|
3941
|
-
const distinctId = identityManager.getDistinctId();
|
|
3942
|
-
const rawUuid = distinctId?.startsWith("$paywallo_anon:") ? distinctId.slice("$paywallo_anon:".length) : distinctId;
|
|
3943
|
-
const appAccountToken = isValidUUIDv4(rawUuid) ? rawUuid : null;
|
|
3944
|
-
if (distinctId && appAccountToken === null) {
|
|
3945
|
-
if (PaywalloClient.getConfig()?.debug) console.log("[Paywallo] distinctId is not a valid UUID v4 \u2014 passing null to appAccountToken:", distinctId);
|
|
3946
|
-
}
|
|
3947
|
-
try {
|
|
3948
|
-
const result = await PaywalloStoreKitNative.purchase(productId, appAccountToken);
|
|
3949
|
-
if (!result || result.pending) {
|
|
3950
|
-
return null;
|
|
3951
|
-
}
|
|
3952
|
-
return mapNativePurchase(result);
|
|
3953
|
-
} catch (err) {
|
|
3954
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
3955
|
-
if (message.toLowerCase().includes("cancel")) {
|
|
3956
|
-
throw new PurchaseError(PURCHASE_ERROR_CODES.USER_CANCELLED, message, true);
|
|
3957
|
-
}
|
|
3958
|
-
throw new PurchaseError(PURCHASE_ERROR_CODES.PURCHASE_FAILED, message);
|
|
3959
|
-
}
|
|
3960
|
-
},
|
|
3961
|
-
async finishTransaction(transactionId) {
|
|
3962
|
-
if (!PaywalloStoreKitNative) return;
|
|
3963
|
-
try {
|
|
3964
|
-
await PaywalloStoreKitNative.finishTransaction(transactionId);
|
|
3965
|
-
} catch (err) {
|
|
3966
|
-
console.error("[Paywallo] finishTransaction native error", err);
|
|
3967
|
-
}
|
|
3968
|
-
},
|
|
3969
|
-
async getActiveTransactions() {
|
|
3970
|
-
if (!PaywalloStoreKitNative) return [];
|
|
3971
|
-
try {
|
|
3972
|
-
const results = await PaywalloStoreKitNative.getActiveTransactions();
|
|
3973
|
-
return results.map(mapNativePurchase);
|
|
3974
|
-
} catch (err) {
|
|
3975
|
-
console.error("[Paywallo] getActiveTransactions failed", err);
|
|
3976
|
-
return [];
|
|
3977
|
-
}
|
|
3978
|
-
},
|
|
3979
|
-
/**
|
|
3980
|
-
* Subscribes to native transaction update events (renewals, refunds,
|
|
3981
|
-
* cancellations) from StoreKit 2 (`Transaction.updates`) on iOS or
|
|
3982
|
-
* `PurchasesUpdatedListener` + refund diff on Android.
|
|
3983
|
-
*
|
|
3984
|
-
* Returns a subscription with a `remove()` method. The caller is
|
|
3985
|
-
* responsible for cleanup (typically on `Paywallo.reset()`).
|
|
3986
|
-
*
|
|
3987
|
-
* Returns `null` when the native module isn't available (non-mobile env,
|
|
3988
|
-
* native module not linked) — callers should treat null as a no-op.
|
|
3989
|
-
*/
|
|
3990
|
-
subscribeToTransactionUpdates(listener) {
|
|
3991
|
-
if (!PaywalloStoreKitNative) return null;
|
|
3992
|
-
try {
|
|
3993
|
-
const EmitterCtor = NativeEventEmitter3;
|
|
3994
|
-
const emitter = new EmitterCtor(PaywalloStoreKitNative);
|
|
3995
|
-
const sub = emitter.addListener(TRANSACTION_UPDATE_EVENT, (payload) => {
|
|
3996
|
-
const update = normalizeTransactionUpdate(payload);
|
|
3997
|
-
if (update) listener(update);
|
|
3998
|
-
});
|
|
3999
|
-
return { remove: () => sub.remove() };
|
|
4000
|
-
} catch (err) {
|
|
4001
|
-
if (PaywalloClient.getConfig()?.debug) console.log("[Paywallo] subscribeToTransactionUpdates failed", err);
|
|
4002
|
-
return null;
|
|
4003
|
-
}
|
|
4004
|
-
}
|
|
4005
|
-
};
|
|
4006
|
-
NativeStoreKit = nativeStoreKit;
|
|
4007
|
-
}
|
|
4008
|
-
});
|
|
4009
|
-
|
|
4010
3908
|
// src/domains/iap/IAPTransactionEmitter.ts
|
|
4011
3909
|
var IAPTransactionEmitter;
|
|
4012
3910
|
var init_IAPTransactionEmitter = __esm({
|
|
4013
3911
|
"src/domains/iap/IAPTransactionEmitter.ts"() {
|
|
4014
3912
|
"use strict";
|
|
4015
3913
|
init_PaywalloClient();
|
|
4016
|
-
init_NativeStoreKit();
|
|
4017
3914
|
IAPTransactionEmitter = class {
|
|
4018
3915
|
constructor(getProductFromCache, getApiClientFn, getDebug) {
|
|
4019
3916
|
this.getProductFromCache = getProductFromCache;
|
|
4020
3917
|
this.getApiClientFn = getApiClientFn;
|
|
4021
3918
|
this.getDebug = getDebug;
|
|
4022
|
-
this.transactionUpdateSubscription = null;
|
|
4023
3919
|
}
|
|
4024
3920
|
/**
|
|
4025
|
-
* Emits `
|
|
4026
|
-
*
|
|
3921
|
+
* Emits `checkout_started` quando o usuário inicia a compra (toca em comprar),
|
|
3922
|
+
* antes do StoreKit/Billing abrir — conta mesmo que a sheet nativa seja cancelada.
|
|
3923
|
+
* Vira InitiateCheckout no Meta/TikTok. Fire-and-forget — uma falha de tracking
|
|
3924
|
+
* nunca pode travar a compra.
|
|
4027
3925
|
*/
|
|
4028
|
-
async
|
|
3926
|
+
async emitCheckoutStarted(productId) {
|
|
4029
3927
|
try {
|
|
4030
3928
|
const apiClient = this.getApiClientFn();
|
|
4031
3929
|
if (!apiClient) return;
|
|
@@ -4033,40 +3931,9 @@ var init_IAPTransactionEmitter = __esm({
|
|
|
4033
3931
|
if (!distinctId) return;
|
|
4034
3932
|
const product = this.getProductFromCache(productId);
|
|
4035
3933
|
const properties = {
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
};
|
|
4040
|
-
if (product?.priceValue !== void 0 && product.priceValue > 0) {
|
|
4041
|
-
properties.amount = product.priceValue;
|
|
4042
|
-
}
|
|
4043
|
-
if (product?.currency) {
|
|
4044
|
-
properties.currency = product.currency;
|
|
4045
|
-
}
|
|
4046
|
-
if (options?.paywallId) properties.paywall_id = options.paywallId;
|
|
4047
|
-
if (options?.variantId) properties.variant_id = options.variantId;
|
|
4048
|
-
await apiClient.trackEvent("transaction", distinctId, properties, void 0, { priority: "critical" });
|
|
4049
|
-
} catch (err) {
|
|
4050
|
-
if (this.getDebug()) console.log("[Paywallo PURCHASE] transaction event emission failed", err);
|
|
4051
|
-
}
|
|
4052
|
-
}
|
|
4053
|
-
/**
|
|
4054
|
-
* Emits `checkout_started` quando o usuário inicia a compra (toca em comprar),
|
|
4055
|
-
* antes do StoreKit/Billing abrir — conta mesmo que a sheet nativa seja cancelada.
|
|
4056
|
-
* Vira InitiateCheckout no Meta/TikTok. Fire-and-forget — uma falha de tracking
|
|
4057
|
-
* nunca pode travar a compra.
|
|
4058
|
-
*/
|
|
4059
|
-
async emitCheckoutStarted(productId) {
|
|
4060
|
-
try {
|
|
4061
|
-
const apiClient = this.getApiClientFn();
|
|
4062
|
-
if (!apiClient) return;
|
|
4063
|
-
const distinctId = PaywalloClient.getDistinctId();
|
|
4064
|
-
if (!distinctId) return;
|
|
4065
|
-
const product = this.getProductFromCache(productId);
|
|
4066
|
-
const properties = {
|
|
4067
|
-
product_id: productId,
|
|
4068
|
-
amount: product?.priceValue ?? 0,
|
|
4069
|
-
currency: product?.currency ?? "USD"
|
|
3934
|
+
product_id: productId,
|
|
3935
|
+
amount: product?.priceValue ?? 0,
|
|
3936
|
+
currency: product?.currency ?? "USD"
|
|
4070
3937
|
};
|
|
4071
3938
|
await apiClient.trackEvent("checkout_started", distinctId, properties, void 0, {
|
|
4072
3939
|
priority: "critical"
|
|
@@ -4077,90 +3944,17 @@ var init_IAPTransactionEmitter = __esm({
|
|
|
4077
3944
|
}
|
|
4078
3945
|
}
|
|
4079
3946
|
/**
|
|
4080
|
-
*
|
|
4081
|
-
*
|
|
4082
|
-
*/
|
|
4083
|
-
async emitTransactionUpdate(update) {
|
|
4084
|
-
try {
|
|
4085
|
-
const apiClient = this.getApiClientFn() ?? PaywalloClient.getApiClient();
|
|
4086
|
-
if (!apiClient) {
|
|
4087
|
-
if (this.getDebug()) console.log("[Paywallo IAP] transaction update dropped (no apiClient)", update.type);
|
|
4088
|
-
return;
|
|
4089
|
-
}
|
|
4090
|
-
const distinctId = PaywalloClient.getDistinctId();
|
|
4091
|
-
if (!distinctId) {
|
|
4092
|
-
if (this.getDebug()) console.log("[Paywallo IAP] transaction update dropped (no distinctId)", update.type);
|
|
4093
|
-
return;
|
|
4094
|
-
}
|
|
4095
|
-
const properties = {
|
|
4096
|
-
type: update.type,
|
|
4097
|
-
tx_id: update.transactionId,
|
|
4098
|
-
product_id: update.productId
|
|
4099
|
-
};
|
|
4100
|
-
if (update.type !== "canceled") {
|
|
4101
|
-
const cached2 = this.getProductFromCache(update.productId);
|
|
4102
|
-
const amount = update.amount ?? cached2?.priceValue;
|
|
4103
|
-
const currency = update.currency ?? cached2?.currency;
|
|
4104
|
-
if (typeof amount === "number" && amount > 0) properties.amount = amount;
|
|
4105
|
-
if (currency) properties.currency = currency;
|
|
4106
|
-
}
|
|
4107
|
-
await apiClient.trackEvent("transaction", distinctId, properties, void 0, { priority: "critical" });
|
|
4108
|
-
if (this.getDebug()) console.log(`[Paywallo IAP] emitted transaction {${update.type}}`, update.transactionId);
|
|
4109
|
-
} catch (err) {
|
|
4110
|
-
if (this.getDebug()) console.log("[Paywallo IAP] transaction update emission failed", err);
|
|
4111
|
-
}
|
|
4112
|
-
}
|
|
4113
|
-
/**
|
|
4114
|
-
* Starts listening to native Transaction.updates. Idempotent.
|
|
3947
|
+
* No-op. Native transaction update listener removed — sale events are sourced
|
|
3948
|
+
* exclusively from server-side webhooks (Apple/Google/Superwall/RevenueCat).
|
|
4115
3949
|
*/
|
|
4116
3950
|
startListener() {
|
|
4117
|
-
if (this.transactionUpdateSubscription) return;
|
|
4118
|
-
this.transactionUpdateSubscription = nativeStoreKit.subscribeToTransactionUpdates(
|
|
4119
|
-
(update) => {
|
|
4120
|
-
void this.emitTransactionUpdate(update);
|
|
4121
|
-
}
|
|
4122
|
-
);
|
|
4123
|
-
if (this.getDebug() && this.transactionUpdateSubscription) {
|
|
4124
|
-
console.log("[Paywallo IAP] transaction update listener started");
|
|
4125
|
-
}
|
|
4126
3951
|
}
|
|
4127
3952
|
/**
|
|
4128
|
-
*
|
|
3953
|
+
* No-op. See startListener.
|
|
4129
3954
|
*/
|
|
4130
3955
|
stopListener() {
|
|
4131
|
-
if (!this.transactionUpdateSubscription) return;
|
|
4132
|
-
try {
|
|
4133
|
-
this.transactionUpdateSubscription.remove();
|
|
4134
|
-
} catch (err) {
|
|
4135
|
-
if (this.getDebug()) console.log("[Paywallo IAP] transaction listener removal failed", err);
|
|
4136
|
-
}
|
|
4137
|
-
this.transactionUpdateSubscription = null;
|
|
4138
|
-
if (this.getDebug()) console.log("[Paywallo IAP] transaction update listener stopped");
|
|
4139
|
-
}
|
|
4140
|
-
};
|
|
4141
|
-
}
|
|
4142
|
-
});
|
|
4143
|
-
|
|
4144
|
-
// src/errors/ClientError.ts
|
|
4145
|
-
var ClientError, CLIENT_ERROR_CODES;
|
|
4146
|
-
var init_ClientError = __esm({
|
|
4147
|
-
"src/errors/ClientError.ts"() {
|
|
4148
|
-
"use strict";
|
|
4149
|
-
init_PaywalloError();
|
|
4150
|
-
ClientError = class extends PaywalloError {
|
|
4151
|
-
constructor(code, message) {
|
|
4152
|
-
super("client", code, message);
|
|
4153
|
-
this.name = "ClientError";
|
|
4154
3956
|
}
|
|
4155
3957
|
};
|
|
4156
|
-
CLIENT_ERROR_CODES = {
|
|
4157
|
-
NOT_INITIALIZED: "CLIENT_NOT_INITIALIZED",
|
|
4158
|
-
MISSING_APP_KEY: "CLIENT_MISSING_APP_KEY",
|
|
4159
|
-
INVALID_EVENT_NAME: "CLIENT_INVALID_EVENT_NAME",
|
|
4160
|
-
PROVIDER_MISSING: "CLIENT_PROVIDER_MISSING",
|
|
4161
|
-
INSECURE_REQUEST: "CLIENT_INSECURE_REQUEST",
|
|
4162
|
-
UNKNOWN: "CLIENT_UNKNOWN"
|
|
4163
|
-
};
|
|
4164
3958
|
}
|
|
4165
3959
|
});
|
|
4166
3960
|
|
|
@@ -4531,11 +4325,11 @@ var init_OfflineQueue = __esm({
|
|
|
4531
4325
|
});
|
|
4532
4326
|
return true;
|
|
4533
4327
|
}
|
|
4534
|
-
const
|
|
4328
|
+
const delay2 = Math.min(
|
|
4535
4329
|
this.config.baseRetryDelayMs * Math.pow(2, item.attempts - 1),
|
|
4536
4330
|
this.config.maxRetryDelayMs
|
|
4537
4331
|
);
|
|
4538
|
-
item.nextRetryAt = Date.now() +
|
|
4332
|
+
item.nextRetryAt = Date.now() + delay2;
|
|
4539
4333
|
await this.storage.saveToStorage(this.queue);
|
|
4540
4334
|
return false;
|
|
4541
4335
|
}
|
|
@@ -5047,6 +4841,56 @@ var init_queue = __esm({
|
|
|
5047
4841
|
}
|
|
5048
4842
|
});
|
|
5049
4843
|
|
|
4844
|
+
// src/domains/iap/PurchaseError.ts
|
|
4845
|
+
function createPurchaseError(code, message) {
|
|
4846
|
+
return new PurchaseError(
|
|
4847
|
+
PURCHASE_ERROR_CODES[code],
|
|
4848
|
+
message ?? DEFAULT_MESSAGES[code] ?? "Unknown purchase error",
|
|
4849
|
+
code === "USER_CANCELLED"
|
|
4850
|
+
);
|
|
4851
|
+
}
|
|
4852
|
+
var PurchaseError, PURCHASE_ERROR_CODES, DEFAULT_MESSAGES;
|
|
4853
|
+
var init_PurchaseError = __esm({
|
|
4854
|
+
"src/domains/iap/PurchaseError.ts"() {
|
|
4855
|
+
"use strict";
|
|
4856
|
+
init_PaywalloError();
|
|
4857
|
+
PurchaseError = class extends PaywalloError {
|
|
4858
|
+
constructor(code, message, userCancelled = false, httpStatus) {
|
|
4859
|
+
super("purchase", code, message);
|
|
4860
|
+
this.name = "PurchaseError";
|
|
4861
|
+
this.userCancelled = userCancelled;
|
|
4862
|
+
this.httpStatus = httpStatus;
|
|
4863
|
+
}
|
|
4864
|
+
};
|
|
4865
|
+
PURCHASE_ERROR_CODES = {
|
|
4866
|
+
NOT_INITIALIZED: "PURCHASE_NOT_INITIALIZED",
|
|
4867
|
+
PRODUCT_NOT_FOUND: "PRODUCT_NOT_FOUND",
|
|
4868
|
+
PURCHASE_FAILED: "PURCHASE_FAILED",
|
|
4869
|
+
RESTORE_FAILED: "RESTORE_FAILED",
|
|
4870
|
+
VALIDATION_FAILED: "VALIDATION_FAILED",
|
|
4871
|
+
USER_CANCELLED: "USER_CANCELLED",
|
|
4872
|
+
NETWORK_ERROR: "PURCHASE_NETWORK_ERROR",
|
|
4873
|
+
STORE_ERROR: "PURCHASE_STORE_ERROR",
|
|
4874
|
+
PENDING_PURCHASE: "PURCHASE_PENDING",
|
|
4875
|
+
DEFERRED_PURCHASE: "PURCHASE_DEFERRED",
|
|
4876
|
+
STORE_NOT_AVAILABLE: "PURCHASE_STORE_NOT_AVAILABLE"
|
|
4877
|
+
};
|
|
4878
|
+
DEFAULT_MESSAGES = {
|
|
4879
|
+
NOT_INITIALIZED: "Purchase controller not initialized. Call init() first.",
|
|
4880
|
+
PRODUCT_NOT_FOUND: "Product not found in store.",
|
|
4881
|
+
PURCHASE_FAILED: "Purchase failed. Please try again.",
|
|
4882
|
+
RESTORE_FAILED: "Failed to restore purchases. Please try again.",
|
|
4883
|
+
VALIDATION_FAILED: "Failed to validate purchase with server.",
|
|
4884
|
+
USER_CANCELLED: "Purchase was cancelled.",
|
|
4885
|
+
NETWORK_ERROR: "Network error. Please check your connection.",
|
|
4886
|
+
STORE_ERROR: "Store error. Please try again later.",
|
|
4887
|
+
PENDING_PURCHASE: "Purchase is pending approval.",
|
|
4888
|
+
DEFERRED_PURCHASE: "Purchase is deferred (e.g., parental approval required).",
|
|
4889
|
+
STORE_NOT_AVAILABLE: "In-app purchases are not available on this device."
|
|
4890
|
+
};
|
|
4891
|
+
}
|
|
4892
|
+
});
|
|
4893
|
+
|
|
5050
4894
|
// src/domains/iap/IAPValidator.ts
|
|
5051
4895
|
import { Platform as Platform11 } from "react-native";
|
|
5052
4896
|
var IAPValidator;
|
|
@@ -5145,53 +4989,247 @@ var init_IAPValidator = __esm({
|
|
|
5145
4989
|
}
|
|
5146
4990
|
});
|
|
5147
4991
|
|
|
5148
|
-
// src/domains/iap/
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
4992
|
+
// src/domains/iap/NativeStoreKit.ts
|
|
4993
|
+
var NativeStoreKit_exports = {};
|
|
4994
|
+
__export(NativeStoreKit_exports, {
|
|
4995
|
+
NativeStoreKit: () => NativeStoreKit,
|
|
4996
|
+
nativeStoreKit: () => nativeStoreKit
|
|
4997
|
+
});
|
|
4998
|
+
import { NativeEventEmitter as NativeEventEmitter3, NativeModules as NativeModules6, Platform as Platform12 } from "react-native";
|
|
4999
|
+
function isAvailable3() {
|
|
5000
|
+
return PaywalloStoreKitNative != null;
|
|
5001
|
+
}
|
|
5002
|
+
function mapNativeProduct(native) {
|
|
5003
|
+
const product = {
|
|
5004
|
+
productId: native.productId,
|
|
5005
|
+
title: native.title,
|
|
5006
|
+
description: native.description,
|
|
5007
|
+
price: native.price,
|
|
5008
|
+
priceValue: native.priceValue,
|
|
5009
|
+
currency: native.currency ?? "USD",
|
|
5010
|
+
localizedPrice: native.localizedPrice,
|
|
5011
|
+
type: mapProductType(native.type),
|
|
5012
|
+
subscriptionPeriod: native.subscriptionPeriod,
|
|
5013
|
+
introductoryPrice: native.introductoryPrice,
|
|
5014
|
+
introductoryPriceValue: native.introductoryPriceValue,
|
|
5015
|
+
freeTrialPeriod: native.freeTrialPeriod
|
|
5016
|
+
};
|
|
5017
|
+
if (native.subscriptionPeriod) {
|
|
5018
|
+
const prices = calculatePerPeriodPrices(native.priceValue, native.subscriptionPeriod);
|
|
5019
|
+
product.pricePerMonth = prices.perMonth;
|
|
5020
|
+
product.pricePerWeek = prices.perWeek;
|
|
5021
|
+
product.pricePerDay = prices.perDay;
|
|
5154
5022
|
}
|
|
5155
|
-
return
|
|
5023
|
+
return product;
|
|
5156
5024
|
}
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5025
|
+
function mapProductType(type) {
|
|
5026
|
+
switch (type) {
|
|
5027
|
+
case "subscription":
|
|
5028
|
+
case "autoRenewable":
|
|
5029
|
+
return "subscription";
|
|
5030
|
+
case "consumable":
|
|
5031
|
+
return "consumable";
|
|
5032
|
+
default:
|
|
5033
|
+
return "non_consumable";
|
|
5034
|
+
}
|
|
5035
|
+
}
|
|
5036
|
+
function calculatePerPeriodPrices(priceValue, period) {
|
|
5037
|
+
const totalDays = periodToDays(period);
|
|
5038
|
+
if (totalDays <= 0) return {};
|
|
5039
|
+
const perDay = priceValue / totalDays;
|
|
5040
|
+
const perWeek = perDay * 7;
|
|
5041
|
+
const perMonth = perDay * 30;
|
|
5042
|
+
return {
|
|
5043
|
+
perDay: perDay.toFixed(2),
|
|
5044
|
+
perWeek: perWeek.toFixed(2),
|
|
5045
|
+
perMonth: perMonth.toFixed(2)
|
|
5046
|
+
};
|
|
5047
|
+
}
|
|
5048
|
+
function periodToDays(period) {
|
|
5049
|
+
const match = period.match(/^P(\d+)([DWMY])$/);
|
|
5050
|
+
if (!match) return 0;
|
|
5051
|
+
const value = parseInt(match[1], 10);
|
|
5052
|
+
switch (match[2]) {
|
|
5053
|
+
case "D":
|
|
5054
|
+
return value;
|
|
5055
|
+
case "W":
|
|
5056
|
+
return value * 7;
|
|
5057
|
+
case "M":
|
|
5058
|
+
return value * 30;
|
|
5059
|
+
case "Y":
|
|
5060
|
+
return value * 365;
|
|
5061
|
+
default:
|
|
5062
|
+
return 0;
|
|
5063
|
+
}
|
|
5064
|
+
}
|
|
5065
|
+
function mapNativePurchase(native) {
|
|
5066
|
+
return {
|
|
5067
|
+
productId: native.productId,
|
|
5068
|
+
transactionId: native.transactionId,
|
|
5069
|
+
transactionDate: native.transactionDate,
|
|
5070
|
+
receipt: native.receipt,
|
|
5071
|
+
platform: Platform12.OS
|
|
5072
|
+
};
|
|
5073
|
+
}
|
|
5074
|
+
function normalizeTransactionUpdate(raw) {
|
|
5075
|
+
if (!raw || typeof raw !== "object") return null;
|
|
5076
|
+
const r = raw;
|
|
5077
|
+
const type = r.type;
|
|
5078
|
+
if (type !== "renewed" && type !== "refunded" && type !== "canceled") return null;
|
|
5079
|
+
const transactionId = typeof r.transactionId === "string" ? r.transactionId : null;
|
|
5080
|
+
const productId = typeof r.productId === "string" ? r.productId : null;
|
|
5081
|
+
if (!transactionId) return null;
|
|
5082
|
+
const update = {
|
|
5083
|
+
type,
|
|
5084
|
+
transactionId,
|
|
5085
|
+
productId: productId ?? ""
|
|
5086
|
+
};
|
|
5087
|
+
if (typeof r.amount === "number") update.amount = r.amount;
|
|
5088
|
+
if (typeof r.currency === "string") update.currency = r.currency;
|
|
5089
|
+
return update;
|
|
5090
|
+
}
|
|
5091
|
+
var TRANSACTION_UPDATE_EVENT, PaywalloStoreKitNative, nativeStoreKit, NativeStoreKit;
|
|
5092
|
+
var init_NativeStoreKit = __esm({
|
|
5093
|
+
"src/domains/iap/NativeStoreKit.ts"() {
|
|
5160
5094
|
"use strict";
|
|
5161
5095
|
init_PaywalloClient();
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
init_NativeStoreKit();
|
|
5096
|
+
init_uuid();
|
|
5097
|
+
init_IdentityManager();
|
|
5165
5098
|
init_PurchaseError();
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
)
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5099
|
+
TRANSACTION_UPDATE_EVENT = "PaywalloTransactionUpdate";
|
|
5100
|
+
PaywalloStoreKitNative = NativeModules6.PaywalloStoreKit ?? null;
|
|
5101
|
+
nativeStoreKit = {
|
|
5102
|
+
isAvailable: isAvailable3,
|
|
5103
|
+
async getProducts(productIds) {
|
|
5104
|
+
if (!PaywalloStoreKitNative) {
|
|
5105
|
+
return [];
|
|
5106
|
+
}
|
|
5107
|
+
const nativeProducts = await PaywalloStoreKitNative.getProducts(productIds);
|
|
5108
|
+
return nativeProducts.map(mapNativeProduct);
|
|
5109
|
+
},
|
|
5110
|
+
async purchase(productId) {
|
|
5111
|
+
if (!PaywalloStoreKitNative) {
|
|
5112
|
+
throw new PurchaseError(
|
|
5113
|
+
PURCHASE_ERROR_CODES.STORE_NOT_AVAILABLE,
|
|
5114
|
+
"StoreKit native module not available"
|
|
5115
|
+
);
|
|
5116
|
+
}
|
|
5117
|
+
const distinctId = identityManager.getDistinctId();
|
|
5118
|
+
const rawUuid = distinctId?.startsWith("$paywallo_anon:") ? distinctId.slice("$paywallo_anon:".length) : distinctId;
|
|
5119
|
+
const appAccountToken = isValidUUIDv4(rawUuid) ? rawUuid : null;
|
|
5120
|
+
if (distinctId && appAccountToken === null) {
|
|
5121
|
+
if (PaywalloClient.getConfig()?.debug) console.log("[Paywallo] distinctId is not a valid UUID v4 \u2014 passing null to appAccountToken:", distinctId);
|
|
5122
|
+
}
|
|
5123
|
+
try {
|
|
5124
|
+
const result = await PaywalloStoreKitNative.purchase(productId, appAccountToken);
|
|
5125
|
+
if (!result || result.pending) {
|
|
5126
|
+
return null;
|
|
5127
|
+
}
|
|
5128
|
+
return mapNativePurchase(result);
|
|
5129
|
+
} catch (err) {
|
|
5130
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5131
|
+
if (message.toLowerCase().includes("cancel")) {
|
|
5132
|
+
throw new PurchaseError(PURCHASE_ERROR_CODES.USER_CANCELLED, message, true);
|
|
5133
|
+
}
|
|
5134
|
+
throw new PurchaseError(PURCHASE_ERROR_CODES.PURCHASE_FAILED, message);
|
|
5135
|
+
}
|
|
5136
|
+
},
|
|
5137
|
+
async finishTransaction(transactionId) {
|
|
5138
|
+
if (!PaywalloStoreKitNative) return;
|
|
5139
|
+
try {
|
|
5140
|
+
await PaywalloStoreKitNative.finishTransaction(transactionId);
|
|
5141
|
+
} catch (err) {
|
|
5142
|
+
console.error("[Paywallo] finishTransaction native error", err);
|
|
5143
|
+
}
|
|
5144
|
+
},
|
|
5145
|
+
async getActiveTransactions() {
|
|
5146
|
+
if (!PaywalloStoreKitNative) return [];
|
|
5147
|
+
try {
|
|
5148
|
+
const results = await PaywalloStoreKitNative.getActiveTransactions();
|
|
5149
|
+
return results.map(mapNativePurchase);
|
|
5150
|
+
} catch (err) {
|
|
5151
|
+
console.error("[Paywallo] getActiveTransactions failed", err);
|
|
5152
|
+
return [];
|
|
5153
|
+
}
|
|
5154
|
+
},
|
|
5155
|
+
/**
|
|
5156
|
+
* Subscribes to native transaction update events (renewals, refunds,
|
|
5157
|
+
* cancellations) from StoreKit 2 (`Transaction.updates`) on iOS or
|
|
5158
|
+
* `PurchasesUpdatedListener` + refund diff on Android.
|
|
5159
|
+
*
|
|
5160
|
+
* Returns a subscription with a `remove()` method. The caller is
|
|
5161
|
+
* responsible for cleanup (typically on `Paywallo.reset()`).
|
|
5162
|
+
*
|
|
5163
|
+
* Returns `null` when the native module isn't available (non-mobile env,
|
|
5164
|
+
* native module not linked) — callers should treat null as a no-op.
|
|
5165
|
+
*/
|
|
5166
|
+
subscribeToTransactionUpdates(listener) {
|
|
5167
|
+
if (!PaywalloStoreKitNative) return null;
|
|
5168
|
+
try {
|
|
5169
|
+
const EmitterCtor = NativeEventEmitter3;
|
|
5170
|
+
const emitter = new EmitterCtor(PaywalloStoreKitNative);
|
|
5171
|
+
const sub = emitter.addListener(TRANSACTION_UPDATE_EVENT, (payload) => {
|
|
5172
|
+
const update = normalizeTransactionUpdate(payload);
|
|
5173
|
+
if (update) listener(update);
|
|
5174
|
+
});
|
|
5175
|
+
return { remove: () => sub.remove() };
|
|
5176
|
+
} catch (err) {
|
|
5177
|
+
if (PaywalloClient.getConfig()?.debug) console.log("[Paywallo] subscribeToTransactionUpdates failed", err);
|
|
5178
|
+
return null;
|
|
5179
|
+
}
|
|
5180
|
+
}
|
|
5181
|
+
};
|
|
5182
|
+
NativeStoreKit = nativeStoreKit;
|
|
5183
|
+
}
|
|
5184
|
+
});
|
|
5185
|
+
|
|
5186
|
+
// src/domains/iap/IAPService.ts
|
|
5187
|
+
import { Platform as Platform13 } from "react-native";
|
|
5188
|
+
function getIAPService() {
|
|
5189
|
+
if (!_instance) {
|
|
5190
|
+
_instance = new IAPService();
|
|
5191
|
+
_instance.startTransactionListener();
|
|
5192
|
+
}
|
|
5193
|
+
return _instance;
|
|
5194
|
+
}
|
|
5195
|
+
var IAPService, _instance;
|
|
5196
|
+
var init_IAPService = __esm({
|
|
5197
|
+
"src/domains/iap/IAPService.ts"() {
|
|
5198
|
+
"use strict";
|
|
5199
|
+
init_PaywalloClient();
|
|
5200
|
+
init_IAPTransactionEmitter();
|
|
5201
|
+
init_IAPValidator();
|
|
5202
|
+
init_NativeStoreKit();
|
|
5203
|
+
init_PurchaseError();
|
|
5204
|
+
IAPService = class {
|
|
5205
|
+
constructor(apiClient) {
|
|
5206
|
+
this.productsCache = /* @__PURE__ */ new Map();
|
|
5207
|
+
this.apiClient = null;
|
|
5208
|
+
this.purchaseInFlight = null;
|
|
5209
|
+
/**
|
|
5210
|
+
* Idempotency guard against double-finish. Populated with transaction IDs
|
|
5211
|
+
* that have already been forwarded to `NativeStoreKit.finishTransaction`.
|
|
5212
|
+
*/
|
|
5213
|
+
this.finishedTransactions = /* @__PURE__ */ new Set();
|
|
5214
|
+
this.apiClient = apiClient ?? null;
|
|
5215
|
+
this.validator = new IAPValidator(
|
|
5216
|
+
(id) => this.productsCache.get(id),
|
|
5217
|
+
() => this.getApiClient(),
|
|
5218
|
+
() => this.debug,
|
|
5219
|
+
() => this.getDeviceCountry()
|
|
5220
|
+
);
|
|
5221
|
+
this.emitter = new IAPTransactionEmitter(
|
|
5222
|
+
(id) => this.productsCache.get(id),
|
|
5223
|
+
() => this.apiClient ?? PaywalloClient.getApiClient(),
|
|
5224
|
+
() => this.debug
|
|
5225
|
+
);
|
|
5226
|
+
}
|
|
5227
|
+
get debug() {
|
|
5228
|
+
return PaywalloClient.getConfig()?.debug ?? false;
|
|
5229
|
+
}
|
|
5230
|
+
getDeviceCountry() {
|
|
5231
|
+
try {
|
|
5232
|
+
const locale = Intl.DateTimeFormat().resolvedOptions().locale;
|
|
5195
5233
|
const parts = locale.split("-");
|
|
5196
5234
|
return parts[parts.length - 1]?.toUpperCase() || "US";
|
|
5197
5235
|
} catch {
|
|
@@ -5245,8 +5283,8 @@ var init_IAPService = __esm({
|
|
|
5245
5283
|
stopTransactionListener() {
|
|
5246
5284
|
this.emitter.stopListener();
|
|
5247
5285
|
}
|
|
5248
|
-
|
|
5249
|
-
|
|
5286
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
5287
|
+
async emitTransactionUpdate(_update) {
|
|
5250
5288
|
}
|
|
5251
5289
|
async purchase(productId, options) {
|
|
5252
5290
|
if (!nativeStoreKit.isAvailable()) {
|
|
@@ -5275,10 +5313,6 @@ var init_IAPService = __esm({
|
|
|
5275
5313
|
if (this.debug) console.log(`[Paywallo PURCHASE] ${productId} server validated`);
|
|
5276
5314
|
await this.finishTransaction(purchase.transactionId);
|
|
5277
5315
|
if (this.debug) console.log(`[Paywallo PURCHASE] ${productId} finished`);
|
|
5278
|
-
await this.emitter.emitTransactionCompleted(purchase, productId, {
|
|
5279
|
-
paywallId: options?.paywallId,
|
|
5280
|
-
variantId: options?.variantId
|
|
5281
|
-
});
|
|
5282
5316
|
}).catch(async (err) => {
|
|
5283
5317
|
const kind = this.validator.classifyValidationError(err);
|
|
5284
5318
|
if (kind === "server4xx") {
|
|
@@ -5307,7 +5341,7 @@ var init_IAPService = __esm({
|
|
|
5307
5341
|
const transactions = await nativeStoreKit.getActiveTransactions();
|
|
5308
5342
|
const apiClient = this.getApiClient();
|
|
5309
5343
|
const distinctId = PaywalloClient.getDistinctId();
|
|
5310
|
-
const platform =
|
|
5344
|
+
const platform = Platform13.OS;
|
|
5311
5345
|
const results = await Promise.allSettled(
|
|
5312
5346
|
transactions.map((tx) => {
|
|
5313
5347
|
const product = this.productsCache.get(tx.productId);
|
|
@@ -6379,319 +6413,29 @@ var init_session = __esm({
|
|
|
6379
6413
|
}
|
|
6380
6414
|
});
|
|
6381
6415
|
|
|
6382
|
-
// src/domains/
|
|
6383
|
-
var
|
|
6384
|
-
var
|
|
6385
|
-
"src/domains/
|
|
6416
|
+
// src/domains/onboarding/OnboardingError.ts
|
|
6417
|
+
var OnboardingError, ONBOARDING_ERROR_CODES;
|
|
6418
|
+
var init_OnboardingError = __esm({
|
|
6419
|
+
"src/domains/onboarding/OnboardingError.ts"() {
|
|
6386
6420
|
"use strict";
|
|
6387
6421
|
init_PaywalloError();
|
|
6388
|
-
|
|
6422
|
+
OnboardingError = class extends PaywalloError {
|
|
6389
6423
|
constructor(code, message) {
|
|
6390
|
-
super("
|
|
6391
|
-
this.name = "
|
|
6424
|
+
super("onboarding", code, message);
|
|
6425
|
+
this.name = "OnboardingError";
|
|
6392
6426
|
}
|
|
6393
6427
|
};
|
|
6394
|
-
|
|
6395
|
-
NOT_INITIALIZED: "
|
|
6396
|
-
|
|
6397
|
-
INVALID_ACTION_NAME: "ANALYTICS_INVALID_ACTION_NAME",
|
|
6398
|
-
VALIDATION_ERROR: "ANALYTICS_VALIDATION_ERROR"
|
|
6428
|
+
ONBOARDING_ERROR_CODES = {
|
|
6429
|
+
NOT_INITIALIZED: "ONBOARDING_NOT_INITIALIZED",
|
|
6430
|
+
INVALID_STEP_NAME: "ONBOARDING_INVALID_STEP_NAME"
|
|
6399
6431
|
};
|
|
6400
6432
|
}
|
|
6401
6433
|
});
|
|
6402
6434
|
|
|
6403
|
-
// src/domains/
|
|
6404
|
-
var
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
init_AnalyticsError();
|
|
6408
|
-
}
|
|
6409
|
-
});
|
|
6410
|
-
|
|
6411
|
-
// src/core/events/schemas.ts
|
|
6412
|
-
import { z } from "zod";
|
|
6413
|
-
var lifecycleEventSchema, identifyEventSchema, paywallEventSchema, transactionEventSchema, onboardingEventSchema, notificationEventSchema, customEventSchema, EVENT_SCHEMAS;
|
|
6414
|
-
var init_schemas = __esm({
|
|
6415
|
-
"src/core/events/schemas.ts"() {
|
|
6416
|
-
"use strict";
|
|
6417
|
-
lifecycleEventSchema = z.object({
|
|
6418
|
-
type: z.enum([
|
|
6419
|
-
"install",
|
|
6420
|
-
"cold_start",
|
|
6421
|
-
"foreground",
|
|
6422
|
-
"background",
|
|
6423
|
-
"session_start",
|
|
6424
|
-
"session_end"
|
|
6425
|
-
]),
|
|
6426
|
-
session_id: z.string().optional(),
|
|
6427
|
-
duration_s: z.number().nonnegative().optional(),
|
|
6428
|
-
first_ever: z.boolean().optional(),
|
|
6429
|
-
// Contexto leve transportado em lifecycle (útil pra cold_start/install)
|
|
6430
|
-
app_version: z.string().optional(),
|
|
6431
|
-
device_type: z.enum(["ios", "android"]).optional(),
|
|
6432
|
-
os_version: z.string().optional(),
|
|
6433
|
-
// Timestamps opcionais — o EventBatcher injeta `timestamp` no envelope
|
|
6434
|
-
started_at: z.string().optional(),
|
|
6435
|
-
ended_at: z.string().optional()
|
|
6436
|
-
}).strict();
|
|
6437
|
-
identifyEventSchema = z.object({
|
|
6438
|
-
distinct_id: z.string().min(1),
|
|
6439
|
-
email: z.email().optional(),
|
|
6440
|
-
traits: z.record(z.string(), z.unknown()).optional(),
|
|
6441
|
-
attribution: z.record(z.string(), z.unknown()).optional(),
|
|
6442
|
-
device: z.record(z.string(), z.unknown()).optional()
|
|
6443
|
-
}).strict();
|
|
6444
|
-
paywallEventSchema = z.object({
|
|
6445
|
-
type: z.enum(["viewed", "closed", "purchased"]),
|
|
6446
|
-
paywall_id: z.string().min(1),
|
|
6447
|
-
placement: z.string().optional(),
|
|
6448
|
-
variant_id: z.string().optional(),
|
|
6449
|
-
variant_key: z.string().optional(),
|
|
6450
|
-
campaign_id: z.string().optional(),
|
|
6451
|
-
// `closed`-specific
|
|
6452
|
-
closed_at: z.string().optional(),
|
|
6453
|
-
duration_s: z.number().nonnegative().optional(),
|
|
6454
|
-
close_reason: z.enum(["dismiss", "cta", "purchase", "error", "timeout"]).optional(),
|
|
6455
|
-
scroll_depth: z.number().min(0).max(1).optional(),
|
|
6456
|
-
// `purchased`-specific (mínimo — transaction canônica leva detalhes)
|
|
6457
|
-
product_id: z.string().optional()
|
|
6458
|
-
}).strict();
|
|
6459
|
-
transactionEventSchema = z.object({
|
|
6460
|
-
type: z.enum([
|
|
6461
|
-
"completed",
|
|
6462
|
-
"failed",
|
|
6463
|
-
"refunded",
|
|
6464
|
-
"renewed",
|
|
6465
|
-
"canceled",
|
|
6466
|
-
"expired"
|
|
6467
|
-
]),
|
|
6468
|
-
tx_id: z.string().min(1),
|
|
6469
|
-
amount: z.number().optional(),
|
|
6470
|
-
currency: z.string().length(3).optional(),
|
|
6471
|
-
product_id: z.string().optional(),
|
|
6472
|
-
subscription_id: z.string().optional(),
|
|
6473
|
-
paywall_id: z.string().optional(),
|
|
6474
|
-
variant_id: z.string().optional(),
|
|
6475
|
-
reason: z.string().optional()
|
|
6476
|
-
}).strict();
|
|
6477
|
-
onboardingEventSchema = z.object({
|
|
6478
|
-
type: z.enum(["step", "complete", "drop"]),
|
|
6479
|
-
step_index: z.number().int().nonnegative().optional(),
|
|
6480
|
-
step_name: z.string().optional(),
|
|
6481
|
-
variant_key: z.string().optional(),
|
|
6482
|
-
time_on_prev_s: z.number().nonnegative().optional()
|
|
6483
|
-
}).strict();
|
|
6484
|
-
notificationEventSchema = z.object({
|
|
6485
|
-
type: z.enum(["delivered", "displayed", "clicked", "dismissed"]),
|
|
6486
|
-
notification_id: z.string().optional(),
|
|
6487
|
-
campaign_id: z.string().optional(),
|
|
6488
|
-
variant_key: z.string().nullable().optional(),
|
|
6489
|
-
message_id: z.string().optional(),
|
|
6490
|
-
device_id: z.string().optional(),
|
|
6491
|
-
push_platform: z.enum(["ios", "android"]).optional(),
|
|
6492
|
-
timezone: z.string().optional(),
|
|
6493
|
-
failure_reason: z.string().optional(),
|
|
6494
|
-
app_user_id: z.string().optional()
|
|
6495
|
-
}).strict();
|
|
6496
|
-
customEventSchema = z.object({
|
|
6497
|
-
name: z.string().min(1).regex(
|
|
6498
|
-
/^(\$[a-zA-Z0-9_]+|[a-z][a-z0-9_]*)$/,
|
|
6499
|
-
"custom event name must be snake_case or $reserved"
|
|
6500
|
-
),
|
|
6501
|
-
props: z.record(z.string(), z.unknown()).optional()
|
|
6502
|
-
}).strict();
|
|
6503
|
-
EVENT_SCHEMAS = {
|
|
6504
|
-
lifecycle: lifecycleEventSchema,
|
|
6505
|
-
identify: identifyEventSchema,
|
|
6506
|
-
paywall: paywallEventSchema,
|
|
6507
|
-
transaction: transactionEventSchema,
|
|
6508
|
-
onboarding: onboardingEventSchema,
|
|
6509
|
-
notification: notificationEventSchema,
|
|
6510
|
-
custom: customEventSchema
|
|
6511
|
-
};
|
|
6512
|
-
}
|
|
6513
|
-
});
|
|
6514
|
-
|
|
6515
|
-
// src/core/events/eventFamilies.ts
|
|
6516
|
-
function isDeprecatedEvent(eventName) {
|
|
6517
|
-
return DEPRECATED_EVENT_NAMES.includes(eventName);
|
|
6518
|
-
}
|
|
6519
|
-
function isCanonicalFamily(name) {
|
|
6520
|
-
return CANONICAL_FAMILIES.includes(name);
|
|
6521
|
-
}
|
|
6522
|
-
function detectFamily(eventName) {
|
|
6523
|
-
if (isCanonicalFamily(eventName) && eventName !== "custom") {
|
|
6524
|
-
return eventName;
|
|
6525
|
-
}
|
|
6526
|
-
return "custom";
|
|
6527
|
-
}
|
|
6528
|
-
function validateEvent(eventName, properties) {
|
|
6529
|
-
const family = detectFamily(eventName);
|
|
6530
|
-
const schema = EVENT_SCHEMAS[family];
|
|
6531
|
-
const input = family === "custom" ? { name: eventName, ...properties ? { props: properties } : {} } : properties ?? {};
|
|
6532
|
-
const parsed = schema.safeParse(input);
|
|
6533
|
-
if (parsed.success) {
|
|
6534
|
-
return { ok: true, family, data: parsed.data };
|
|
6535
|
-
}
|
|
6536
|
-
return { ok: false, family, error: parsed.error };
|
|
6537
|
-
}
|
|
6538
|
-
function formatValidationError(error) {
|
|
6539
|
-
return error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
|
|
6540
|
-
}
|
|
6541
|
-
var CANONICAL_FAMILIES, DEPRECATED_EVENT_NAMES;
|
|
6542
|
-
var init_eventFamilies = __esm({
|
|
6543
|
-
"src/core/events/eventFamilies.ts"() {
|
|
6544
|
-
"use strict";
|
|
6545
|
-
init_schemas();
|
|
6546
|
-
CANONICAL_FAMILIES = [
|
|
6547
|
-
"lifecycle",
|
|
6548
|
-
"identify",
|
|
6549
|
-
"paywall",
|
|
6550
|
-
"transaction",
|
|
6551
|
-
"onboarding",
|
|
6552
|
-
"notification",
|
|
6553
|
-
"custom"
|
|
6554
|
-
];
|
|
6555
|
-
DEPRECATED_EVENT_NAMES = [
|
|
6556
|
-
"$paywall_purchased",
|
|
6557
|
-
"$paywall_product_selected",
|
|
6558
|
-
"$core_action",
|
|
6559
|
-
"$campaign_impression",
|
|
6560
|
-
"$app_open",
|
|
6561
|
-
"$app_background",
|
|
6562
|
-
"$app_foreground",
|
|
6563
|
-
"session.end"
|
|
6564
|
-
];
|
|
6565
|
-
}
|
|
6566
|
-
});
|
|
6567
|
-
|
|
6568
|
-
// src/core/PaywalloAnalytics.ts
|
|
6569
|
-
function isValidEventName(name) {
|
|
6570
|
-
if (name.startsWith("$")) return /^\$[a-z][a-z0-9]*(_[a-z0-9]+)*$/.test(name);
|
|
6571
|
-
return /^[a-z][a-z0-9]*(_[a-z0-9]+)*$/.test(name);
|
|
6572
|
-
}
|
|
6573
|
-
async function trackEvent(apiClient, state, eventName, options) {
|
|
6574
|
-
const distinctId = identityManager.getDistinctId();
|
|
6575
|
-
if (!distinctId) {
|
|
6576
|
-
console.error("[Paywallo:DROP] event dropped \u2014 no distinctId available yet", { eventName });
|
|
6577
|
-
return;
|
|
6578
|
-
}
|
|
6579
|
-
if (!isValidEventName(eventName)) {
|
|
6580
|
-
const msg = `Invalid event name: "${eventName}". Use snake_case or $reserved_name.`;
|
|
6581
|
-
throw new ClientError(CLIENT_ERROR_CODES.INVALID_EVENT_NAME, msg);
|
|
6582
|
-
}
|
|
6583
|
-
if (state.config?.debug && eventName.startsWith("$purchase")) console.log(`[Paywallo TRACK] ${eventName}`, options?.properties ?? {});
|
|
6584
|
-
const sessionId = sessionManager.getSessionId();
|
|
6585
|
-
const family = detectFamily(eventName);
|
|
6586
|
-
const isCustomFamily = family === "custom";
|
|
6587
|
-
const identityProps = isCustomFamily ? identityManager.getProperties() : {};
|
|
6588
|
-
const sessionProp = isCustomFamily && sessionId ? { sessionId } : {};
|
|
6589
|
-
await apiClient.trackEvent(
|
|
6590
|
-
eventName,
|
|
6591
|
-
distinctId,
|
|
6592
|
-
{ ...identityProps, ...options?.properties, ...sessionProp },
|
|
6593
|
-
options?.timestamp,
|
|
6594
|
-
options?.priority ? { priority: options.priority } : void 0
|
|
6595
|
-
);
|
|
6596
|
-
}
|
|
6597
|
-
async function trackOnboardingStep(apiClient, state, index, name) {
|
|
6598
|
-
const distinctId = identityManager.getDistinctId();
|
|
6599
|
-
if (!distinctId) return;
|
|
6600
|
-
if (!Number.isInteger(index) || index < 0) {
|
|
6601
|
-
const msg = `Invalid stepIndex: "${index}". Must be a non-negative integer.`;
|
|
6602
|
-
throw new AnalyticsError(ANALYTICS_ERROR_CODES.INVALID_STEP_INDEX, msg);
|
|
6603
|
-
}
|
|
6604
|
-
const now = Date.now();
|
|
6605
|
-
const timeOnPreviousStep = state.lastOnboardingStepTimestamp !== null ? Math.round((now - state.lastOnboardingStepTimestamp) / 1e3) : 0;
|
|
6606
|
-
state.lastOnboardingStepTimestamp = now;
|
|
6607
|
-
await apiClient.trackEvent("$onboarding_step", distinctId, { stepIndex: index, stepName: name, timeOnPreviousStep });
|
|
6608
|
-
}
|
|
6609
|
-
function validateCoreAction(actionName) {
|
|
6610
|
-
if (!actionName || typeof actionName !== "string") {
|
|
6611
|
-
const msg = `Invalid actionName: "${actionName}". Must be a non-empty string.`;
|
|
6612
|
-
throw new AnalyticsError(ANALYTICS_ERROR_CODES.INVALID_ACTION_NAME, msg);
|
|
6613
|
-
}
|
|
6614
|
-
}
|
|
6615
|
-
var init_PaywalloAnalytics = __esm({
|
|
6616
|
-
"src/core/PaywalloAnalytics.ts"() {
|
|
6617
|
-
"use strict";
|
|
6618
|
-
init_analytics();
|
|
6619
|
-
init_identity();
|
|
6620
|
-
init_session();
|
|
6621
|
-
init_ClientError();
|
|
6622
|
-
init_eventFamilies();
|
|
6623
|
-
}
|
|
6624
|
-
});
|
|
6625
|
-
|
|
6626
|
-
// src/core/PaywalloFlags.ts
|
|
6627
|
-
async function getVariantCached(apiClient, flagKey, defaultValue) {
|
|
6628
|
-
const distinctId = identityManager.getDistinctId();
|
|
6629
|
-
const result = await apiClient.getVariantFromCache(flagKey, distinctId);
|
|
6630
|
-
if (result) return result;
|
|
6631
|
-
void apiClient.getVariant(flagKey, distinctId).catch(() => {
|
|
6632
|
-
});
|
|
6633
|
-
return { variant: defaultValue ?? null, payload: void 0 };
|
|
6634
|
-
}
|
|
6635
|
-
async function getVariant(apiClient, state, flagKey) {
|
|
6636
|
-
try {
|
|
6637
|
-
const distinctId = identityManager.getDistinctId();
|
|
6638
|
-
if (!distinctId) {
|
|
6639
|
-
if (state.config?.debug) console.log(`[Paywallo FLAG] ${flagKey} \u2192 null (no distinctId)`);
|
|
6640
|
-
return { variant: null };
|
|
6641
|
-
}
|
|
6642
|
-
const result = await apiClient.getVariant(flagKey, distinctId);
|
|
6643
|
-
if (state.config?.debug) console.log(`[Paywallo FLAG] ${flagKey} \u2192`, result.variant);
|
|
6644
|
-
return result;
|
|
6645
|
-
} catch (error) {
|
|
6646
|
-
if (state.config?.debug) console.log(`[Paywallo FLAG] ${flagKey} \u2192 error`, error);
|
|
6647
|
-
if (state.apiClient) void state.apiClient.reportError("GET_VARIANT_FAILED", error instanceof Error ? error.message : String(error));
|
|
6648
|
-
return { variant: null };
|
|
6649
|
-
}
|
|
6650
|
-
}
|
|
6651
|
-
function evaluateFlags(apiClient, keys) {
|
|
6652
|
-
return apiClient.evaluateFlags(keys, identityManager.getDistinctId());
|
|
6653
|
-
}
|
|
6654
|
-
async function getConditionalFlag(apiClient, state, flagKey, context) {
|
|
6655
|
-
try {
|
|
6656
|
-
const distinctId = identityManager.getDistinctId();
|
|
6657
|
-
if (!distinctId) return false;
|
|
6658
|
-
const result = await apiClient.getConditionalFlag(flagKey, { distinctId, ...context });
|
|
6659
|
-
return result.value;
|
|
6660
|
-
} catch (error) {
|
|
6661
|
-
if (state.apiClient) void state.apiClient.reportError("GET_CONDITIONAL_FLAG_FAILED", error instanceof Error ? error.message : String(error));
|
|
6662
|
-
return false;
|
|
6663
|
-
}
|
|
6664
|
-
}
|
|
6665
|
-
var init_PaywalloFlags = __esm({
|
|
6666
|
-
"src/core/PaywalloFlags.ts"() {
|
|
6667
|
-
"use strict";
|
|
6668
|
-
init_identity();
|
|
6669
|
-
}
|
|
6670
|
-
});
|
|
6671
|
-
|
|
6672
|
-
// src/domains/onboarding/OnboardingError.ts
|
|
6673
|
-
var OnboardingError, ONBOARDING_ERROR_CODES;
|
|
6674
|
-
var init_OnboardingError = __esm({
|
|
6675
|
-
"src/domains/onboarding/OnboardingError.ts"() {
|
|
6676
|
-
"use strict";
|
|
6677
|
-
init_PaywalloError();
|
|
6678
|
-
OnboardingError = class extends PaywalloError {
|
|
6679
|
-
constructor(code, message) {
|
|
6680
|
-
super("onboarding", code, message);
|
|
6681
|
-
this.name = "OnboardingError";
|
|
6682
|
-
}
|
|
6683
|
-
};
|
|
6684
|
-
ONBOARDING_ERROR_CODES = {
|
|
6685
|
-
NOT_INITIALIZED: "ONBOARDING_NOT_INITIALIZED",
|
|
6686
|
-
INVALID_STEP_NAME: "ONBOARDING_INVALID_STEP_NAME"
|
|
6687
|
-
};
|
|
6688
|
-
}
|
|
6689
|
-
});
|
|
6690
|
-
|
|
6691
|
-
// src/domains/onboarding/OnboardingManager.ts
|
|
6692
|
-
var OnboardingManager, onboardingManager;
|
|
6693
|
-
var init_OnboardingManager = __esm({
|
|
6694
|
-
"src/domains/onboarding/OnboardingManager.ts"() {
|
|
6435
|
+
// src/domains/onboarding/OnboardingManager.ts
|
|
6436
|
+
var OnboardingManager, onboardingManager;
|
|
6437
|
+
var init_OnboardingManager = __esm({
|
|
6438
|
+
"src/domains/onboarding/OnboardingManager.ts"() {
|
|
6695
6439
|
"use strict";
|
|
6696
6440
|
init_OnboardingError();
|
|
6697
6441
|
OnboardingManager = class {
|
|
@@ -6713,15 +6457,20 @@ var init_OnboardingManager = __esm({
|
|
|
6713
6457
|
* Saves the step name internally for implicit drop detection on the backend.
|
|
6714
6458
|
*
|
|
6715
6459
|
* @param stepName - Unique name for this onboarding step.
|
|
6460
|
+
* @param order - Optional explicit display order (accepts decimals, e.g. 2.1 for A/B variants).
|
|
6716
6461
|
*/
|
|
6717
|
-
async step(stepName) {
|
|
6462
|
+
async step(stepName, order) {
|
|
6718
6463
|
this.assertConfig();
|
|
6719
6464
|
this.validateStepName(stepName, "step");
|
|
6720
6465
|
this.lastStep = stepName;
|
|
6466
|
+
const stepPayload = { step_name: stepName };
|
|
6467
|
+
if (order !== void 0 && Number.isFinite(order) && order >= 0) {
|
|
6468
|
+
stepPayload.order = order;
|
|
6469
|
+
}
|
|
6721
6470
|
const payload = {
|
|
6722
6471
|
family: "onboarding",
|
|
6723
6472
|
type: "step",
|
|
6724
|
-
...
|
|
6473
|
+
...stepPayload
|
|
6725
6474
|
};
|
|
6726
6475
|
return this.emit("onboarding", payload, "step");
|
|
6727
6476
|
}
|
|
@@ -6792,12 +6541,290 @@ var init_OnboardingManager = __esm({
|
|
|
6792
6541
|
}
|
|
6793
6542
|
});
|
|
6794
6543
|
|
|
6795
|
-
// src/domains/onboarding/index.ts
|
|
6796
|
-
var init_onboarding = __esm({
|
|
6797
|
-
"src/domains/onboarding/index.ts"() {
|
|
6544
|
+
// src/domains/onboarding/index.ts
|
|
6545
|
+
var init_onboarding = __esm({
|
|
6546
|
+
"src/domains/onboarding/index.ts"() {
|
|
6547
|
+
"use strict";
|
|
6548
|
+
init_OnboardingError();
|
|
6549
|
+
init_OnboardingManager();
|
|
6550
|
+
}
|
|
6551
|
+
});
|
|
6552
|
+
|
|
6553
|
+
// src/domains/analytics/AnalyticsError.ts
|
|
6554
|
+
var AnalyticsError, ANALYTICS_ERROR_CODES;
|
|
6555
|
+
var init_AnalyticsError = __esm({
|
|
6556
|
+
"src/domains/analytics/AnalyticsError.ts"() {
|
|
6557
|
+
"use strict";
|
|
6558
|
+
init_PaywalloError();
|
|
6559
|
+
AnalyticsError = class extends PaywalloError {
|
|
6560
|
+
constructor(code, message) {
|
|
6561
|
+
super("analytics", code, message);
|
|
6562
|
+
this.name = "AnalyticsError";
|
|
6563
|
+
}
|
|
6564
|
+
};
|
|
6565
|
+
ANALYTICS_ERROR_CODES = {
|
|
6566
|
+
NOT_INITIALIZED: "ANALYTICS_NOT_INITIALIZED",
|
|
6567
|
+
INVALID_STEP_INDEX: "ANALYTICS_INVALID_STEP_INDEX",
|
|
6568
|
+
INVALID_ACTION_NAME: "ANALYTICS_INVALID_ACTION_NAME",
|
|
6569
|
+
VALIDATION_ERROR: "ANALYTICS_VALIDATION_ERROR"
|
|
6570
|
+
};
|
|
6571
|
+
}
|
|
6572
|
+
});
|
|
6573
|
+
|
|
6574
|
+
// src/domains/analytics/index.ts
|
|
6575
|
+
var init_analytics = __esm({
|
|
6576
|
+
"src/domains/analytics/index.ts"() {
|
|
6577
|
+
"use strict";
|
|
6578
|
+
init_AnalyticsError();
|
|
6579
|
+
}
|
|
6580
|
+
});
|
|
6581
|
+
|
|
6582
|
+
// src/core/events/schemas.ts
|
|
6583
|
+
import { z } from "zod";
|
|
6584
|
+
var lifecycleEventSchema, identifyEventSchema, paywallEventSchema, transactionEventSchema, onboardingEventSchema, notificationEventSchema, customEventSchema, EVENT_SCHEMAS;
|
|
6585
|
+
var init_schemas = __esm({
|
|
6586
|
+
"src/core/events/schemas.ts"() {
|
|
6587
|
+
"use strict";
|
|
6588
|
+
lifecycleEventSchema = z.object({
|
|
6589
|
+
type: z.enum([
|
|
6590
|
+
"install",
|
|
6591
|
+
"cold_start",
|
|
6592
|
+
"foreground",
|
|
6593
|
+
"background",
|
|
6594
|
+
"session_start",
|
|
6595
|
+
"session_end"
|
|
6596
|
+
]),
|
|
6597
|
+
session_id: z.string().optional(),
|
|
6598
|
+
duration_s: z.number().nonnegative().optional(),
|
|
6599
|
+
first_ever: z.boolean().optional(),
|
|
6600
|
+
// Contexto leve transportado em lifecycle (útil pra cold_start/install)
|
|
6601
|
+
app_version: z.string().optional(),
|
|
6602
|
+
device_type: z.enum(["ios", "android"]).optional(),
|
|
6603
|
+
os_version: z.string().optional(),
|
|
6604
|
+
// Timestamps opcionais — o EventBatcher injeta `timestamp` no envelope
|
|
6605
|
+
started_at: z.string().optional(),
|
|
6606
|
+
ended_at: z.string().optional()
|
|
6607
|
+
}).strict();
|
|
6608
|
+
identifyEventSchema = z.object({
|
|
6609
|
+
distinct_id: z.string().min(1),
|
|
6610
|
+
email: z.email().optional(),
|
|
6611
|
+
traits: z.record(z.string(), z.unknown()).optional(),
|
|
6612
|
+
attribution: z.record(z.string(), z.unknown()).optional(),
|
|
6613
|
+
device: z.record(z.string(), z.unknown()).optional()
|
|
6614
|
+
}).strict();
|
|
6615
|
+
paywallEventSchema = z.object({
|
|
6616
|
+
type: z.enum(["viewed", "closed", "purchased"]),
|
|
6617
|
+
paywall_id: z.string().min(1),
|
|
6618
|
+
placement: z.string().optional(),
|
|
6619
|
+
variant_id: z.string().optional(),
|
|
6620
|
+
variant_key: z.string().optional(),
|
|
6621
|
+
campaign_id: z.string().optional(),
|
|
6622
|
+
// `closed`-specific
|
|
6623
|
+
closed_at: z.string().optional(),
|
|
6624
|
+
duration_s: z.number().nonnegative().optional(),
|
|
6625
|
+
close_reason: z.enum(["dismiss", "cta", "purchase", "error", "timeout"]).optional(),
|
|
6626
|
+
scroll_depth: z.number().min(0).max(1).optional(),
|
|
6627
|
+
// `purchased`-specific (mínimo — transaction canônica leva detalhes)
|
|
6628
|
+
product_id: z.string().optional()
|
|
6629
|
+
}).strict();
|
|
6630
|
+
transactionEventSchema = z.object({
|
|
6631
|
+
type: z.enum([
|
|
6632
|
+
"completed",
|
|
6633
|
+
"failed",
|
|
6634
|
+
"refunded",
|
|
6635
|
+
"renewed",
|
|
6636
|
+
"canceled",
|
|
6637
|
+
"expired"
|
|
6638
|
+
]),
|
|
6639
|
+
tx_id: z.string().min(1),
|
|
6640
|
+
amount: z.number().optional(),
|
|
6641
|
+
currency: z.string().length(3).optional(),
|
|
6642
|
+
product_id: z.string().optional(),
|
|
6643
|
+
subscription_id: z.string().optional(),
|
|
6644
|
+
paywall_id: z.string().optional(),
|
|
6645
|
+
variant_id: z.string().optional(),
|
|
6646
|
+
reason: z.string().optional()
|
|
6647
|
+
}).strict();
|
|
6648
|
+
onboardingEventSchema = z.object({
|
|
6649
|
+
type: z.enum(["step", "complete", "drop"]),
|
|
6650
|
+
step_index: z.number().int().nonnegative().optional(),
|
|
6651
|
+
step_name: z.string().optional(),
|
|
6652
|
+
variant_key: z.string().optional(),
|
|
6653
|
+
time_on_prev_s: z.number().nonnegative().optional()
|
|
6654
|
+
}).strict();
|
|
6655
|
+
notificationEventSchema = z.object({
|
|
6656
|
+
type: z.enum(["delivered", "displayed", "clicked", "dismissed"]),
|
|
6657
|
+
notification_id: z.string().optional(),
|
|
6658
|
+
campaign_id: z.string().optional(),
|
|
6659
|
+
variant_key: z.string().nullable().optional(),
|
|
6660
|
+
message_id: z.string().optional(),
|
|
6661
|
+
device_id: z.string().optional(),
|
|
6662
|
+
push_platform: z.enum(["ios", "android"]).optional(),
|
|
6663
|
+
timezone: z.string().optional(),
|
|
6664
|
+
failure_reason: z.string().optional(),
|
|
6665
|
+
app_user_id: z.string().optional()
|
|
6666
|
+
}).strict();
|
|
6667
|
+
customEventSchema = z.object({
|
|
6668
|
+
name: z.string().min(1).regex(
|
|
6669
|
+
/^(\$[a-zA-Z0-9_]+|[a-z][a-z0-9_]*)$/,
|
|
6670
|
+
"custom event name must be snake_case or $reserved"
|
|
6671
|
+
),
|
|
6672
|
+
props: z.record(z.string(), z.unknown()).optional()
|
|
6673
|
+
}).strict();
|
|
6674
|
+
EVENT_SCHEMAS = {
|
|
6675
|
+
lifecycle: lifecycleEventSchema,
|
|
6676
|
+
identify: identifyEventSchema,
|
|
6677
|
+
paywall: paywallEventSchema,
|
|
6678
|
+
transaction: transactionEventSchema,
|
|
6679
|
+
onboarding: onboardingEventSchema,
|
|
6680
|
+
notification: notificationEventSchema,
|
|
6681
|
+
custom: customEventSchema
|
|
6682
|
+
};
|
|
6683
|
+
}
|
|
6684
|
+
});
|
|
6685
|
+
|
|
6686
|
+
// src/core/events/eventFamilies.ts
|
|
6687
|
+
function isDeprecatedEvent(eventName) {
|
|
6688
|
+
return DEPRECATED_EVENT_NAMES.includes(eventName);
|
|
6689
|
+
}
|
|
6690
|
+
function isCanonicalFamily(name) {
|
|
6691
|
+
return CANONICAL_FAMILIES.includes(name);
|
|
6692
|
+
}
|
|
6693
|
+
function detectFamily(eventName) {
|
|
6694
|
+
if (isCanonicalFamily(eventName) && eventName !== "custom") {
|
|
6695
|
+
return eventName;
|
|
6696
|
+
}
|
|
6697
|
+
return "custom";
|
|
6698
|
+
}
|
|
6699
|
+
function validateEvent(eventName, properties) {
|
|
6700
|
+
const family = detectFamily(eventName);
|
|
6701
|
+
const schema = EVENT_SCHEMAS[family];
|
|
6702
|
+
const input = family === "custom" ? { name: eventName, ...properties ? { props: properties } : {} } : properties ?? {};
|
|
6703
|
+
const parsed = schema.safeParse(input);
|
|
6704
|
+
if (parsed.success) {
|
|
6705
|
+
return { ok: true, family, data: parsed.data };
|
|
6706
|
+
}
|
|
6707
|
+
return { ok: false, family, error: parsed.error };
|
|
6708
|
+
}
|
|
6709
|
+
function formatValidationError(error) {
|
|
6710
|
+
return error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
|
|
6711
|
+
}
|
|
6712
|
+
var CANONICAL_FAMILIES, DEPRECATED_EVENT_NAMES;
|
|
6713
|
+
var init_eventFamilies = __esm({
|
|
6714
|
+
"src/core/events/eventFamilies.ts"() {
|
|
6715
|
+
"use strict";
|
|
6716
|
+
init_schemas();
|
|
6717
|
+
CANONICAL_FAMILIES = [
|
|
6718
|
+
"lifecycle",
|
|
6719
|
+
"identify",
|
|
6720
|
+
"paywall",
|
|
6721
|
+
"transaction",
|
|
6722
|
+
"onboarding",
|
|
6723
|
+
"notification",
|
|
6724
|
+
"custom"
|
|
6725
|
+
];
|
|
6726
|
+
DEPRECATED_EVENT_NAMES = [
|
|
6727
|
+
"$paywall_purchased",
|
|
6728
|
+
"$paywall_product_selected",
|
|
6729
|
+
"$core_action",
|
|
6730
|
+
"$campaign_impression",
|
|
6731
|
+
"$app_open",
|
|
6732
|
+
"$app_background",
|
|
6733
|
+
"$app_foreground",
|
|
6734
|
+
"session.end"
|
|
6735
|
+
];
|
|
6736
|
+
}
|
|
6737
|
+
});
|
|
6738
|
+
|
|
6739
|
+
// src/core/PaywalloAnalytics.ts
|
|
6740
|
+
function isValidEventName(name) {
|
|
6741
|
+
if (name.startsWith("$")) return /^\$[a-z][a-z0-9]*(_[a-z0-9]+)*$/.test(name);
|
|
6742
|
+
return /^[a-z][a-z0-9]*(_[a-z0-9]+)*$/.test(name);
|
|
6743
|
+
}
|
|
6744
|
+
async function trackEvent(apiClient, state, eventName, options) {
|
|
6745
|
+
const distinctId = identityManager.getDistinctId();
|
|
6746
|
+
if (!distinctId) {
|
|
6747
|
+
console.error("[Paywallo:DROP] event dropped \u2014 no distinctId available yet", { eventName });
|
|
6748
|
+
return;
|
|
6749
|
+
}
|
|
6750
|
+
if (!isValidEventName(eventName)) {
|
|
6751
|
+
const msg = `Invalid event name: "${eventName}". Use snake_case or $reserved_name.`;
|
|
6752
|
+
throw new ClientError(CLIENT_ERROR_CODES.INVALID_EVENT_NAME, msg);
|
|
6753
|
+
}
|
|
6754
|
+
if (state.config?.debug && eventName.startsWith("$purchase")) console.log(`[Paywallo TRACK] ${eventName}`, options?.properties ?? {});
|
|
6755
|
+
const sessionId = sessionManager.getSessionId();
|
|
6756
|
+
const family = detectFamily(eventName);
|
|
6757
|
+
const isCustomFamily = family === "custom";
|
|
6758
|
+
const identityProps = isCustomFamily ? identityManager.getProperties() : {};
|
|
6759
|
+
const sessionProp = isCustomFamily && sessionId ? { sessionId } : {};
|
|
6760
|
+
await apiClient.trackEvent(
|
|
6761
|
+
eventName,
|
|
6762
|
+
distinctId,
|
|
6763
|
+
{ ...identityProps, ...options?.properties, ...sessionProp },
|
|
6764
|
+
options?.timestamp,
|
|
6765
|
+
options?.priority ? { priority: options.priority } : void 0
|
|
6766
|
+
);
|
|
6767
|
+
}
|
|
6768
|
+
function validateCoreAction(actionName) {
|
|
6769
|
+
if (!actionName || typeof actionName !== "string") {
|
|
6770
|
+
const msg = `Invalid actionName: "${actionName}". Must be a non-empty string.`;
|
|
6771
|
+
throw new AnalyticsError(ANALYTICS_ERROR_CODES.INVALID_ACTION_NAME, msg);
|
|
6772
|
+
}
|
|
6773
|
+
}
|
|
6774
|
+
var init_PaywalloAnalytics = __esm({
|
|
6775
|
+
"src/core/PaywalloAnalytics.ts"() {
|
|
6798
6776
|
"use strict";
|
|
6799
|
-
|
|
6800
|
-
|
|
6777
|
+
init_analytics();
|
|
6778
|
+
init_identity();
|
|
6779
|
+
init_session();
|
|
6780
|
+
init_ClientError();
|
|
6781
|
+
init_eventFamilies();
|
|
6782
|
+
}
|
|
6783
|
+
});
|
|
6784
|
+
|
|
6785
|
+
// src/core/PaywalloFlags.ts
|
|
6786
|
+
async function getVariantCached(apiClient, flagKey, defaultValue) {
|
|
6787
|
+
const distinctId = identityManager.getDistinctId();
|
|
6788
|
+
const result = await apiClient.getVariantFromCache(flagKey, distinctId);
|
|
6789
|
+
if (result) return result;
|
|
6790
|
+
void apiClient.getVariant(flagKey, distinctId).catch(() => {
|
|
6791
|
+
});
|
|
6792
|
+
return { variant: defaultValue ?? null, payload: void 0 };
|
|
6793
|
+
}
|
|
6794
|
+
async function getVariant(apiClient, state, flagKey) {
|
|
6795
|
+
try {
|
|
6796
|
+
const distinctId = identityManager.getDistinctId();
|
|
6797
|
+
if (!distinctId) {
|
|
6798
|
+
if (state.config?.debug) console.log(`[Paywallo FLAG] ${flagKey} \u2192 null (no distinctId)`);
|
|
6799
|
+
return { variant: null };
|
|
6800
|
+
}
|
|
6801
|
+
const result = await apiClient.getVariant(flagKey, distinctId);
|
|
6802
|
+
if (state.config?.debug) console.log(`[Paywallo FLAG] ${flagKey} \u2192`, result.variant);
|
|
6803
|
+
return result;
|
|
6804
|
+
} catch (error) {
|
|
6805
|
+
if (state.config?.debug) console.log(`[Paywallo FLAG] ${flagKey} \u2192 error`, error);
|
|
6806
|
+
if (state.apiClient) void state.apiClient.reportError("GET_VARIANT_FAILED", error instanceof Error ? error.message : String(error));
|
|
6807
|
+
return { variant: null };
|
|
6808
|
+
}
|
|
6809
|
+
}
|
|
6810
|
+
function evaluateFlags(apiClient, keys) {
|
|
6811
|
+
return apiClient.evaluateFlags(keys, identityManager.getDistinctId());
|
|
6812
|
+
}
|
|
6813
|
+
async function getConditionalFlag(apiClient, state, flagKey, context) {
|
|
6814
|
+
try {
|
|
6815
|
+
const distinctId = identityManager.getDistinctId();
|
|
6816
|
+
if (!distinctId) return false;
|
|
6817
|
+
const result = await apiClient.getConditionalFlag(flagKey, { distinctId, ...context });
|
|
6818
|
+
return result.value;
|
|
6819
|
+
} catch (error) {
|
|
6820
|
+
if (state.apiClient) void state.apiClient.reportError("GET_CONDITIONAL_FLAG_FAILED", error instanceof Error ? error.message : String(error));
|
|
6821
|
+
return false;
|
|
6822
|
+
}
|
|
6823
|
+
}
|
|
6824
|
+
var init_PaywalloFlags = __esm({
|
|
6825
|
+
"src/core/PaywalloFlags.ts"() {
|
|
6826
|
+
"use strict";
|
|
6827
|
+
init_identity();
|
|
6801
6828
|
}
|
|
6802
6829
|
});
|
|
6803
6830
|
|
|
@@ -7132,6 +7159,36 @@ var init_SubscriptionManager = __esm({
|
|
|
7132
7159
|
}
|
|
7133
7160
|
});
|
|
7134
7161
|
|
|
7162
|
+
// src/utils/detectEnvironment.ts
|
|
7163
|
+
import { NativeModules as NativeModules7 } from "react-native";
|
|
7164
|
+
async function detectEnvironment() {
|
|
7165
|
+
if (cached !== null) return cached;
|
|
7166
|
+
if (typeof globalThis.__DEV__ !== "undefined" && globalThis.__DEV__) {
|
|
7167
|
+
cached = "sandbox";
|
|
7168
|
+
return cached;
|
|
7169
|
+
}
|
|
7170
|
+
if (!nativeModule) {
|
|
7171
|
+
cached = "production";
|
|
7172
|
+
return cached;
|
|
7173
|
+
}
|
|
7174
|
+
try {
|
|
7175
|
+
const result = await nativeModule.detect();
|
|
7176
|
+
cached = result === "sandbox" ? "sandbox" : "production";
|
|
7177
|
+
return cached;
|
|
7178
|
+
} catch {
|
|
7179
|
+
cached = "production";
|
|
7180
|
+
return cached;
|
|
7181
|
+
}
|
|
7182
|
+
}
|
|
7183
|
+
var nativeModule, cached;
|
|
7184
|
+
var init_detectEnvironment = __esm({
|
|
7185
|
+
"src/utils/detectEnvironment.ts"() {
|
|
7186
|
+
"use strict";
|
|
7187
|
+
nativeModule = NativeModules7.PaywalloEnv ?? null;
|
|
7188
|
+
cached = null;
|
|
7189
|
+
}
|
|
7190
|
+
});
|
|
7191
|
+
|
|
7135
7192
|
// src/domains/iap/apiPurchaseMethods.ts
|
|
7136
7193
|
async function validatePurchase(client, appKey, platform, receipt, productId, transactionId, priceLocal, currency, country, distinctId, paywallPlacement, variantKey) {
|
|
7137
7194
|
const response = await client.post(
|
|
@@ -7457,7 +7514,7 @@ var init_ApiClientQueue = __esm({
|
|
|
7457
7514
|
});
|
|
7458
7515
|
|
|
7459
7516
|
// src/core/eventEnvelopeV2.ts
|
|
7460
|
-
import { Platform as
|
|
7517
|
+
import { Platform as Platform14 } from "react-native";
|
|
7461
7518
|
import { z as z2 } from "zod";
|
|
7462
7519
|
function buildV2Envelope(events, providerContext) {
|
|
7463
7520
|
const context = { ...providerContext };
|
|
@@ -7473,21 +7530,42 @@ function buildV2Envelope(events, providerContext) {
|
|
|
7473
7530
|
if (family === "custom") {
|
|
7474
7531
|
payload.event_name = event.eventName;
|
|
7475
7532
|
}
|
|
7476
|
-
|
|
7477
|
-
|
|
7478
|
-
|
|
7479
|
-
|
|
7480
|
-
payload
|
|
7481
|
-
}
|
|
7533
|
+
const candidateId = payload.installEventId;
|
|
7534
|
+
let id;
|
|
7535
|
+
if (typeof candidateId === "string" && isValidUUIDv4(candidateId)) {
|
|
7536
|
+
id = candidateId;
|
|
7537
|
+
delete payload.installEventId;
|
|
7538
|
+
} else {
|
|
7539
|
+
id = generateUUID();
|
|
7540
|
+
}
|
|
7541
|
+
return { id, ts: event.timestamp, name: family, payload };
|
|
7482
7542
|
});
|
|
7483
7543
|
if (context.sdk_version === void 0) context.sdk_version = SDK_VERSION;
|
|
7484
|
-
if (context.platform === void 0) context.platform =
|
|
7544
|
+
if (context.platform === void 0) context.platform = Platform14.OS;
|
|
7485
7545
|
if (context.distinct_id === void 0 && events.length > 0) {
|
|
7486
7546
|
context.distinct_id = events[0].distinctId;
|
|
7487
7547
|
}
|
|
7488
7548
|
if (!context.distinct_id || context.distinct_id.length === 0) {
|
|
7489
7549
|
console.error("[Paywallo:ENVELOPE] distinct_id is empty after resolution \u2014 envelope will fail schema", { providerContext, eventCount: events.length, firstEventName: events[0]?.eventName });
|
|
7490
7550
|
}
|
|
7551
|
+
const idsFromProps = {};
|
|
7552
|
+
const existingIds = context.ids;
|
|
7553
|
+
for (const event of events) {
|
|
7554
|
+
for (const { key, aliases } of IDS_PROMO_ALIASES) {
|
|
7555
|
+
if (existingIds?.[key] !== void 0) continue;
|
|
7556
|
+
if (idsFromProps[key] !== void 0) continue;
|
|
7557
|
+
for (const alias of aliases) {
|
|
7558
|
+
const val = event.properties[alias];
|
|
7559
|
+
if (typeof val === "string" && val.length > 0) {
|
|
7560
|
+
idsFromProps[key] = val;
|
|
7561
|
+
break;
|
|
7562
|
+
}
|
|
7563
|
+
}
|
|
7564
|
+
}
|
|
7565
|
+
}
|
|
7566
|
+
if (Object.keys(idsFromProps).length > 0) {
|
|
7567
|
+
context.ids = { ...idsFromProps, ...context.ids };
|
|
7568
|
+
}
|
|
7491
7569
|
return { context, events: v2Events };
|
|
7492
7570
|
}
|
|
7493
7571
|
function resolveProviderContext(provider) {
|
|
@@ -7521,7 +7599,7 @@ function splitPayloadAndContext(properties) {
|
|
|
7521
7599
|
}
|
|
7522
7600
|
return { payload, promoted };
|
|
7523
7601
|
}
|
|
7524
|
-
var v2EventSchema, v2ContextSchema, v2EnvelopeSchema, CONTEXT_KEY_ALIASES;
|
|
7602
|
+
var v2EventSchema, v2ContextSchema, v2EnvelopeSchema, IDS_PROMO_ALIASES, CONTEXT_KEY_ALIASES;
|
|
7525
7603
|
var init_eventEnvelopeV2 = __esm({
|
|
7526
7604
|
"src/core/eventEnvelopeV2.ts"() {
|
|
7527
7605
|
"use strict";
|
|
@@ -7547,6 +7625,7 @@ var init_eventEnvelopeV2 = __esm({
|
|
|
7547
7625
|
device_model: z2.string().optional(),
|
|
7548
7626
|
timezone: z2.string().optional(),
|
|
7549
7627
|
locale: z2.string().optional(),
|
|
7628
|
+
country: z2.string().optional(),
|
|
7550
7629
|
carrier: z2.string().optional(),
|
|
7551
7630
|
screen_width: z2.number().optional(),
|
|
7552
7631
|
screen_height: z2.number().optional(),
|
|
@@ -7558,6 +7637,12 @@ var init_eventEnvelopeV2 = __esm({
|
|
|
7558
7637
|
context: v2ContextSchema,
|
|
7559
7638
|
events: z2.array(v2EventSchema).min(1)
|
|
7560
7639
|
});
|
|
7640
|
+
IDS_PROMO_ALIASES = [
|
|
7641
|
+
{ key: "idfv", aliases: ["idfv"] },
|
|
7642
|
+
{ key: "gaid", aliases: ["gaid"] },
|
|
7643
|
+
{ key: "fb_anon_id", aliases: ["fbAnonId", "fb_anon_id"] },
|
|
7644
|
+
{ key: "idfa", aliases: ["idfa"] }
|
|
7645
|
+
];
|
|
7561
7646
|
CONTEXT_KEY_ALIASES = {
|
|
7562
7647
|
distinct_id: ["distinct_id", "distinctId"],
|
|
7563
7648
|
session_id: ["session_id", "sessionId"],
|
|
@@ -7574,6 +7659,7 @@ var init_eventEnvelopeV2 = __esm({
|
|
|
7574
7659
|
device_model: ["device_model", "deviceModel", "model"],
|
|
7575
7660
|
timezone: ["timezone", "timeZone"],
|
|
7576
7661
|
locale: ["locale"],
|
|
7662
|
+
country: ["country", "regionCode"],
|
|
7577
7663
|
carrier: ["carrier_ctx"],
|
|
7578
7664
|
screen_width: ["screen_width"],
|
|
7579
7665
|
screen_height: ["screen_height"],
|
|
@@ -7593,7 +7679,7 @@ var init_events = __esm({
|
|
|
7593
7679
|
});
|
|
7594
7680
|
|
|
7595
7681
|
// src/core/EventBatcher.ts
|
|
7596
|
-
import { Platform as
|
|
7682
|
+
import { Platform as Platform15 } from "react-native";
|
|
7597
7683
|
var BATCH_MAX_SIZE, BATCH_FLUSH_MS, EVENT_NAME_REGEX, V2_BATCH_ENDPOINT, EventBatcher;
|
|
7598
7684
|
var init_EventBatcher = __esm({
|
|
7599
7685
|
"src/core/EventBatcher.ts"() {
|
|
@@ -7646,7 +7732,7 @@ var init_EventBatcher = __esm({
|
|
|
7646
7732
|
const event = {
|
|
7647
7733
|
eventName,
|
|
7648
7734
|
distinctId,
|
|
7649
|
-
properties: { ...properties, platform:
|
|
7735
|
+
properties: { ...properties, platform: Platform15.OS },
|
|
7650
7736
|
timestamp: timestamp ?? Date.now(),
|
|
7651
7737
|
environment: environment.toLowerCase()
|
|
7652
7738
|
};
|
|
@@ -7813,8 +7899,8 @@ var init_HttpClient = __esm({
|
|
|
7813
7899
|
if (this.shouldRetry(response.status, retryConfig)) {
|
|
7814
7900
|
if (state.attempt < retryConfig.maxRetries) {
|
|
7815
7901
|
state.attempt++;
|
|
7816
|
-
const
|
|
7817
|
-
await this.sleep(
|
|
7902
|
+
const delay2 = this.calculateDelayForResponse(state.attempt, response.headers, retryConfig);
|
|
7903
|
+
await this.sleep(delay2);
|
|
7818
7904
|
continue;
|
|
7819
7905
|
}
|
|
7820
7906
|
}
|
|
@@ -7823,8 +7909,8 @@ var init_HttpClient = __esm({
|
|
|
7823
7909
|
state.lastError = error instanceof Error ? error : new ClientError(CLIENT_ERROR_CODES.UNKNOWN, String(error));
|
|
7824
7910
|
if (this.isNetworkError(state.lastError) && state.attempt < retryConfig.maxRetries) {
|
|
7825
7911
|
state.attempt++;
|
|
7826
|
-
const
|
|
7827
|
-
await this.sleep(
|
|
7912
|
+
const delay2 = this.calculateDelay(state.attempt, retryConfig);
|
|
7913
|
+
await this.sleep(delay2);
|
|
7828
7914
|
continue;
|
|
7829
7915
|
}
|
|
7830
7916
|
throw state.lastError;
|
|
@@ -7972,7 +8058,7 @@ var init_http = __esm({
|
|
|
7972
8058
|
});
|
|
7973
8059
|
|
|
7974
8060
|
// src/core/ApiClient.ts
|
|
7975
|
-
import { Platform as
|
|
8061
|
+
import { Platform as Platform16 } from "react-native";
|
|
7976
8062
|
function normalizeDateOfBirth(raw) {
|
|
7977
8063
|
if (!raw) return void 0;
|
|
7978
8064
|
if (DATE_OF_BIRTH_RE.test(raw)) return raw;
|
|
@@ -8108,7 +8194,7 @@ var init_ApiClient = __esm({
|
|
|
8108
8194
|
buildSdkHeaders() {
|
|
8109
8195
|
return {
|
|
8110
8196
|
"x-sdk-version": SDK_VERSION,
|
|
8111
|
-
"x-sdk-platform":
|
|
8197
|
+
"x-sdk-platform": Platform16.OS,
|
|
8112
8198
|
"x-sdk-environment": this.environment
|
|
8113
8199
|
};
|
|
8114
8200
|
}
|
|
@@ -8138,7 +8224,7 @@ var init_ApiClient = __esm({
|
|
|
8138
8224
|
message,
|
|
8139
8225
|
context,
|
|
8140
8226
|
sdkVersion: SDK_VERSION,
|
|
8141
|
-
platform:
|
|
8227
|
+
platform: Platform16.OS
|
|
8142
8228
|
}, true);
|
|
8143
8229
|
} catch (err) {
|
|
8144
8230
|
if (this.debug) console.log("[Paywallo:ApiClient] reportError failed", err);
|
|
@@ -8156,7 +8242,7 @@ var init_ApiClient = __esm({
|
|
|
8156
8242
|
async identify(distinctId, properties, email, deviceId, pii) {
|
|
8157
8243
|
const rawTraits = {
|
|
8158
8244
|
email: email || void 0,
|
|
8159
|
-
platform:
|
|
8245
|
+
platform: Platform16.OS,
|
|
8160
8246
|
name: properties?.name,
|
|
8161
8247
|
country: properties?.country,
|
|
8162
8248
|
locale: properties?.locale,
|
|
@@ -8352,7 +8438,7 @@ function setupNotifications(apiClient, config, environment, eventPipeline) {
|
|
|
8352
8438
|
notificationsManager.setupHandlers({ eventBatcher: eventPipeline });
|
|
8353
8439
|
notificationsManager.initialize({
|
|
8354
8440
|
appKey: config.appKey,
|
|
8355
|
-
apiBaseUrl:
|
|
8441
|
+
apiBaseUrl: DEFAULT_API_URL,
|
|
8356
8442
|
debug: config.debug,
|
|
8357
8443
|
environment: environment === "Sandbox" ? "sandbox" : "production"
|
|
8358
8444
|
}).catch((err) => {
|
|
@@ -8431,36 +8517,6 @@ var init_PaywalloState = __esm({
|
|
|
8431
8517
|
}
|
|
8432
8518
|
});
|
|
8433
8519
|
|
|
8434
|
-
// src/utils/detectEnvironment.ts
|
|
8435
|
-
import { NativeModules as NativeModules8 } from "react-native";
|
|
8436
|
-
async function detectEnvironment() {
|
|
8437
|
-
if (cached !== null) return cached;
|
|
8438
|
-
if (typeof globalThis.__DEV__ !== "undefined" && globalThis.__DEV__) {
|
|
8439
|
-
cached = "sandbox";
|
|
8440
|
-
return cached;
|
|
8441
|
-
}
|
|
8442
|
-
if (!nativeModule) {
|
|
8443
|
-
cached = "production";
|
|
8444
|
-
return cached;
|
|
8445
|
-
}
|
|
8446
|
-
try {
|
|
8447
|
-
const result = await nativeModule.detect();
|
|
8448
|
-
cached = result === "sandbox" ? "sandbox" : "production";
|
|
8449
|
-
return cached;
|
|
8450
|
-
} catch {
|
|
8451
|
-
cached = "production";
|
|
8452
|
-
return cached;
|
|
8453
|
-
}
|
|
8454
|
-
}
|
|
8455
|
-
var nativeModule, cached;
|
|
8456
|
-
var init_detectEnvironment = __esm({
|
|
8457
|
-
"src/utils/detectEnvironment.ts"() {
|
|
8458
|
-
"use strict";
|
|
8459
|
-
nativeModule = NativeModules8.PaywalloEnv ?? null;
|
|
8460
|
-
cached = null;
|
|
8461
|
-
}
|
|
8462
|
-
});
|
|
8463
|
-
|
|
8464
8520
|
// src/core/PaywalloInitializer.ts
|
|
8465
8521
|
import { Dimensions as Dimensions4, PixelRatio as PixelRatio2 } from "react-native";
|
|
8466
8522
|
async function initWithRetry(state, config) {
|
|
@@ -8476,8 +8532,8 @@ async function initWithRetry(state, config) {
|
|
|
8476
8532
|
} catch (error) {
|
|
8477
8533
|
state.initAttempts++;
|
|
8478
8534
|
if (state.initAttempts > MAX_INIT_RETRIES) throw error;
|
|
8479
|
-
const
|
|
8480
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
8535
|
+
const delay2 = RETRY_DELAYS[state.initAttempts - 1] ?? 1e4;
|
|
8536
|
+
await new Promise((resolve) => setTimeout(resolve, delay2));
|
|
8481
8537
|
}
|
|
8482
8538
|
}
|
|
8483
8539
|
}
|
|
@@ -8507,7 +8563,7 @@ function reportInitFailure(state, config, error, reason) {
|
|
|
8507
8563
|
try {
|
|
8508
8564
|
const tempClient = new ApiClient(
|
|
8509
8565
|
config.appKey,
|
|
8510
|
-
|
|
8566
|
+
DEFAULT_API_URL,
|
|
8511
8567
|
config.debug
|
|
8512
8568
|
);
|
|
8513
8569
|
void tempClient.reportError("INIT_FAILED", reason, {
|
|
@@ -8557,7 +8613,7 @@ async function doInit(state, config) {
|
|
|
8557
8613
|
]);
|
|
8558
8614
|
const apiClient = new ApiClient(
|
|
8559
8615
|
config.appKey,
|
|
8560
|
-
|
|
8616
|
+
DEFAULT_API_URL,
|
|
8561
8617
|
config.debug,
|
|
8562
8618
|
environment,
|
|
8563
8619
|
offlineQueueEnabled,
|
|
@@ -8631,8 +8687,8 @@ async function doInit(state, config) {
|
|
|
8631
8687
|
let timezone;
|
|
8632
8688
|
try {
|
|
8633
8689
|
const opts = Intl.DateTimeFormat().resolvedOptions();
|
|
8634
|
-
locale = opts.locale || void 0;
|
|
8635
|
-
timezone = opts.timeZone || void 0;
|
|
8690
|
+
locale = device?.languageTag || opts.locale || void 0;
|
|
8691
|
+
timezone = device?.timeZone || opts.timeZone || void 0;
|
|
8636
8692
|
} catch {
|
|
8637
8693
|
}
|
|
8638
8694
|
let screenWidth;
|
|
@@ -8656,7 +8712,7 @@ async function doInit(state, config) {
|
|
|
8656
8712
|
device_model: device?.modelId || device?.model || void 0,
|
|
8657
8713
|
timezone,
|
|
8658
8714
|
locale,
|
|
8659
|
-
|
|
8715
|
+
...device?.regionCode && { country: device.regionCode },
|
|
8660
8716
|
screen_width: screenWidth,
|
|
8661
8717
|
screen_height: screenHeight,
|
|
8662
8718
|
screen_density: screenDensity,
|
|
@@ -8722,6 +8778,7 @@ var init_PaywalloInitializer = __esm({
|
|
|
8722
8778
|
init_plan();
|
|
8723
8779
|
init_session();
|
|
8724
8780
|
init_SubscriptionManager();
|
|
8781
|
+
init_detectEnvironment();
|
|
8725
8782
|
init_NativeDeviceInfo();
|
|
8726
8783
|
init_ApiClient();
|
|
8727
8784
|
init_constants();
|
|
@@ -8731,7 +8788,6 @@ var init_PaywalloInitializer = __esm({
|
|
|
8731
8788
|
init_PaywalloState();
|
|
8732
8789
|
init_queue();
|
|
8733
8790
|
init_NativeStorage();
|
|
8734
|
-
init_detectEnvironment();
|
|
8735
8791
|
}
|
|
8736
8792
|
});
|
|
8737
8793
|
|
|
@@ -8852,6 +8908,7 @@ var init_PaywalloClient = __esm({
|
|
|
8852
8908
|
init_plan();
|
|
8853
8909
|
init_session();
|
|
8854
8910
|
init_ClientError();
|
|
8911
|
+
init_onboarding();
|
|
8855
8912
|
init_network();
|
|
8856
8913
|
init_PaywalloAnalytics();
|
|
8857
8914
|
init_PaywalloFlags();
|
|
@@ -8921,8 +8978,9 @@ var init_PaywalloClient = __esm({
|
|
|
8921
8978
|
async track(eventName, options) {
|
|
8922
8979
|
return trackEvent(this.getApiClientOrThrow(), this.state, eventName, options);
|
|
8923
8980
|
}
|
|
8924
|
-
async onboardingStep(
|
|
8925
|
-
|
|
8981
|
+
async onboardingStep(order, name) {
|
|
8982
|
+
this.ensureInitialized();
|
|
8983
|
+
return onboardingManager.step(name, order);
|
|
8926
8984
|
}
|
|
8927
8985
|
async coreAction(actionName) {
|
|
8928
8986
|
this.ensureInitialized();
|
|
@@ -9009,6 +9067,9 @@ var init_PaywalloClient = __esm({
|
|
|
9009
9067
|
getConfig() {
|
|
9010
9068
|
return this.state.config;
|
|
9011
9069
|
}
|
|
9070
|
+
getId() {
|
|
9071
|
+
return identityManager.getDistinctId();
|
|
9072
|
+
}
|
|
9012
9073
|
getDistinctId() {
|
|
9013
9074
|
return identityManager.getDistinctId();
|
|
9014
9075
|
}
|
|
@@ -9146,9 +9207,9 @@ __export(OfferingService_exports, {
|
|
|
9146
9207
|
OfferingService: () => OfferingService,
|
|
9147
9208
|
offeringService: () => offeringService
|
|
9148
9209
|
});
|
|
9149
|
-
import { Platform as
|
|
9210
|
+
import { Platform as Platform17 } from "react-native";
|
|
9150
9211
|
function resolveProductId(raw) {
|
|
9151
|
-
const isIos =
|
|
9212
|
+
const isIos = Platform17.OS === "ios";
|
|
9152
9213
|
const productId = isIos ? raw.apple_product_id : raw.google_product_id;
|
|
9153
9214
|
return productId ?? "";
|
|
9154
9215
|
}
|
|
@@ -9167,7 +9228,7 @@ function mapProduct2(raw) {
|
|
|
9167
9228
|
};
|
|
9168
9229
|
}
|
|
9169
9230
|
function pickPlatformProduct(products) {
|
|
9170
|
-
const isIos =
|
|
9231
|
+
const isIos = Platform17.OS === "ios";
|
|
9171
9232
|
const match = products.find((p) => isIos ? p.apple_product_id : p.google_product_id);
|
|
9172
9233
|
return match ?? products[0] ?? null;
|
|
9173
9234
|
}
|
|
@@ -9394,7 +9455,7 @@ init_onboarding();
|
|
|
9394
9455
|
import { useCallback as useCallback9 } from "react";
|
|
9395
9456
|
function useOnboarding() {
|
|
9396
9457
|
const step = useCallback9(
|
|
9397
|
-
(stepName) => onboardingManager.step(stepName),
|
|
9458
|
+
(stepName, order) => onboardingManager.step(stepName, order),
|
|
9398
9459
|
[]
|
|
9399
9460
|
);
|
|
9400
9461
|
const complete = useCallback9(
|
|
@@ -9738,8 +9799,41 @@ function useSubscription() {
|
|
|
9738
9799
|
};
|
|
9739
9800
|
}
|
|
9740
9801
|
|
|
9802
|
+
// src/hooks/usePaywalloScreenTracking.ts
|
|
9803
|
+
init_PaywalloClient();
|
|
9804
|
+
import { useCallback as useCallback15, useRef as useRef8 } from "react";
|
|
9805
|
+
function isNavState(value) {
|
|
9806
|
+
return typeof value === "object" && value !== null && "routes" in value && "index" in value && Array.isArray(value.routes) && typeof value.index === "number";
|
|
9807
|
+
}
|
|
9808
|
+
function getActiveRouteName(state) {
|
|
9809
|
+
try {
|
|
9810
|
+
if (!isNavState(state)) return null;
|
|
9811
|
+
const route = state.routes[state.index];
|
|
9812
|
+
if (!route) return null;
|
|
9813
|
+
if (isNavState(route.state)) {
|
|
9814
|
+
return getActiveRouteName(route.state);
|
|
9815
|
+
}
|
|
9816
|
+
return typeof route.name === "string" ? route.name : null;
|
|
9817
|
+
} catch {
|
|
9818
|
+
return null;
|
|
9819
|
+
}
|
|
9820
|
+
}
|
|
9821
|
+
function usePaywalloScreenTracking() {
|
|
9822
|
+
const lastScreenRef = useRef8(null);
|
|
9823
|
+
return useCallback15((state) => {
|
|
9824
|
+
if (!PaywalloClient.isReady()) return;
|
|
9825
|
+
const screenName = getActiveRouteName(state);
|
|
9826
|
+
if (!screenName || screenName === lastScreenRef.current) return;
|
|
9827
|
+
lastScreenRef.current = screenName;
|
|
9828
|
+
void PaywalloClient.track("screen_view", {
|
|
9829
|
+
properties: { screen_name: screenName }
|
|
9830
|
+
}).catch(() => {
|
|
9831
|
+
});
|
|
9832
|
+
}, []);
|
|
9833
|
+
}
|
|
9834
|
+
|
|
9741
9835
|
// src/PaywalloProvider.tsx
|
|
9742
|
-
import { useCallback as
|
|
9836
|
+
import { useCallback as useCallback20, useEffect as useEffect14, useLayoutEffect, useMemo as useMemo3, useRef as useRef12, useState as useState17 } from "react";
|
|
9743
9837
|
import { Animated as Animated4, Dimensions as Dimensions5, Easing as Easing2, StyleSheet as StyleSheet5 } from "react-native";
|
|
9744
9838
|
init_PaywalloClient();
|
|
9745
9839
|
init_NativeStorage();
|
|
@@ -9754,12 +9848,17 @@ init_paywall();
|
|
|
9754
9848
|
|
|
9755
9849
|
// src/domains/paywall/SuperwallAttributeBridge.ts
|
|
9756
9850
|
init_identity();
|
|
9851
|
+
var CONFIG_POLL_INTERVAL_MS = 250;
|
|
9852
|
+
var CONFIG_POLL_MAX_ATTEMPTS = 40;
|
|
9757
9853
|
var lastSignature = null;
|
|
9758
9854
|
function log(debug, msg, data) {
|
|
9759
9855
|
if (!debug) return;
|
|
9760
9856
|
if (data !== void 0) console.log(`[Paywallo:SuperwallAttr] ${msg}`, data);
|
|
9761
9857
|
else console.log(`[Paywallo:SuperwallAttr] ${msg}`);
|
|
9762
9858
|
}
|
|
9859
|
+
function delay(ms) {
|
|
9860
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
9861
|
+
}
|
|
9763
9862
|
var PAID_MEDIUM = /cpc|ppc|cpm|cpa|paid|display/i;
|
|
9764
9863
|
function deriveIsPaid(a) {
|
|
9765
9864
|
if (a.fbclid || a.gclid || a.ttclid || a.tiktokCampaignId) return true;
|
|
@@ -9798,24 +9897,38 @@ function buildSuperwallAttributes(a) {
|
|
|
9798
9897
|
out.pw_attributed_at = a.capturedAt;
|
|
9799
9898
|
return out;
|
|
9800
9899
|
}
|
|
9801
|
-
function
|
|
9802
|
-
const
|
|
9803
|
-
return
|
|
9900
|
+
function pickNative(mod) {
|
|
9901
|
+
const native = mod?.SuperwallExpoModule ?? mod?.default?.SuperwallExpoModule;
|
|
9902
|
+
return native && typeof native.setUserAttributes === "function" && typeof native.getConfigurationStatus === "function" ? native : null;
|
|
9804
9903
|
}
|
|
9805
|
-
async function
|
|
9904
|
+
async function resolveSuperwallNativeModule(debug) {
|
|
9806
9905
|
try {
|
|
9807
|
-
const
|
|
9808
|
-
if (
|
|
9906
|
+
const native = pickNative(await import("expo-superwall"));
|
|
9907
|
+
if (native) return native;
|
|
9809
9908
|
} catch {
|
|
9810
9909
|
}
|
|
9811
|
-
|
|
9812
|
-
const shared = pickShared(await import("expo-superwall"));
|
|
9813
|
-
if (shared) return shared;
|
|
9814
|
-
} catch {
|
|
9815
|
-
}
|
|
9816
|
-
log(debug, "expo-superwall not available \u2014 attribute push skipped");
|
|
9910
|
+
log(debug, "expo-superwall native module not available \u2014 attribute push skipped");
|
|
9817
9911
|
return null;
|
|
9818
9912
|
}
|
|
9913
|
+
async function waitForConfigured(mod, debug) {
|
|
9914
|
+
for (let attempt = 0; attempt < CONFIG_POLL_MAX_ATTEMPTS; attempt++) {
|
|
9915
|
+
let status;
|
|
9916
|
+
try {
|
|
9917
|
+
status = String(await mod.getConfigurationStatus());
|
|
9918
|
+
} catch (err) {
|
|
9919
|
+
log(debug, "getConfigurationStatus error", { err: String(err) });
|
|
9920
|
+
return false;
|
|
9921
|
+
}
|
|
9922
|
+
if (status === "CONFIGURED") return true;
|
|
9923
|
+
if (status === "FAILED") {
|
|
9924
|
+
log(debug, "Superwall configuration FAILED \u2014 skipping attribute push");
|
|
9925
|
+
return false;
|
|
9926
|
+
}
|
|
9927
|
+
await delay(CONFIG_POLL_INTERVAL_MS);
|
|
9928
|
+
}
|
|
9929
|
+
log(debug, "Superwall not configured within timeout \u2014 skipping attribute push");
|
|
9930
|
+
return false;
|
|
9931
|
+
}
|
|
9819
9932
|
async function pushAttributionToSuperwall(debug = false) {
|
|
9820
9933
|
const attrs = buildSuperwallAttributes(attributionTracker.get());
|
|
9821
9934
|
const signature = JSON.stringify(attrs);
|
|
@@ -9823,10 +9936,11 @@ async function pushAttributionToSuperwall(debug = false) {
|
|
|
9823
9936
|
log(debug, "attributes unchanged \u2014 skipping push");
|
|
9824
9937
|
return;
|
|
9825
9938
|
}
|
|
9826
|
-
const
|
|
9827
|
-
if (!
|
|
9939
|
+
const native = await resolveSuperwallNativeModule(debug);
|
|
9940
|
+
if (!native) return;
|
|
9941
|
+
if (!await waitForConfigured(native, debug)) return;
|
|
9828
9942
|
try {
|
|
9829
|
-
await
|
|
9943
|
+
await native.setUserAttributes(attrs);
|
|
9830
9944
|
lastSignature = signature;
|
|
9831
9945
|
log(debug, "attributes pushed to Superwall", attrs);
|
|
9832
9946
|
} catch (err) {
|
|
@@ -9942,50 +10056,9 @@ function buildCallbacks() {
|
|
|
9942
10056
|
const identifier = "paywallIdentifier" in safe && typeof safe.paywallIdentifier === "string" ? safe.paywallIdentifier : productId;
|
|
9943
10057
|
log2("onPurchase", { productId, identifier });
|
|
9944
10058
|
void trackPaywallEvent("purchased", identifier, ts, { product_id: productId });
|
|
9945
|
-
},
|
|
9946
|
-
// Global Superwall event listener — used specifically for `transactionComplete`
|
|
9947
|
-
// which is the only event that exposes the full product (currency/price) +
|
|
9948
|
-
// transaction (storeTransactionId) shape needed to create a server-side
|
|
9949
|
-
// `transactions` row. Other events are skipped (paywallOpen/Dismiss already
|
|
9950
|
-
// handled above with their typed callbacks).
|
|
9951
|
-
onSuperwallEvent: (info) => {
|
|
9952
|
-
const ev = info?.event;
|
|
9953
|
-
if (!ev || ev.event !== "transactionComplete") return;
|
|
9954
|
-
const ts = Date.now();
|
|
9955
|
-
const product = ev.product;
|
|
9956
|
-
const transaction = ev.transaction;
|
|
9957
|
-
if (!product) {
|
|
9958
|
-
log2("transactionComplete missing product \u2014 skipping transaction track");
|
|
9959
|
-
return;
|
|
9960
|
-
}
|
|
9961
|
-
const productId = product.productIdentifier ?? product.id ?? "unknown";
|
|
9962
|
-
const currency = product.currencyCode ?? "USD";
|
|
9963
|
-
const price = product.price ?? 0;
|
|
9964
|
-
const transactionId = transaction?.storeTransactionId ?? transaction?.originalTransactionIdentifier ?? `${productId}_${String(ts)}`;
|
|
9965
|
-
const isTrial = product.hasFreeTrial === true || (product.trialPeriodPrice ?? 0) > 0;
|
|
9966
|
-
const paywallIdentifier = ev.paywallInfo?.identifier;
|
|
9967
|
-
log2("onSuperwallEvent: transactionComplete", { productId, currency, price, transactionId, isTrial });
|
|
9968
|
-
void (async () => {
|
|
9969
|
-
try {
|
|
9970
|
-
await PaywalloClient.track("transaction", {
|
|
9971
|
-
priority: "critical",
|
|
9972
|
-
properties: {
|
|
9973
|
-
type: isTrial ? "trial_started" : "completed",
|
|
9974
|
-
// Server's IngestTranslator looks for `transaction_id`/`product_id`
|
|
9975
|
-
// /`amount`/`currency`/`paywall_id` keys (not `tx_id`). PurchaseHandler
|
|
9976
|
-
// then resolves type=trial/new/renewal based on amount being 0 vs >0.
|
|
9977
|
-
transaction_id: transactionId,
|
|
9978
|
-
product_id: productId,
|
|
9979
|
-
amount: price,
|
|
9980
|
-
currency,
|
|
9981
|
-
...paywallIdentifier ? { paywall_id: paywallIdentifier } : {}
|
|
9982
|
-
}
|
|
9983
|
-
});
|
|
9984
|
-
} catch (err) {
|
|
9985
|
-
log2("transactionComplete track error", { err: String(err) });
|
|
9986
|
-
}
|
|
9987
|
-
})();
|
|
9988
10059
|
}
|
|
10060
|
+
// onSuperwallEvent removed — transaction sale events are sourced exclusively
|
|
10061
|
+
// from server-side webhooks (Apple/Google/Superwall/RevenueCat).
|
|
9989
10062
|
};
|
|
9990
10063
|
}
|
|
9991
10064
|
function startSuperwallAutoBridge(debug = false) {
|
|
@@ -10029,7 +10102,7 @@ init_session();
|
|
|
10029
10102
|
init_ClientError();
|
|
10030
10103
|
|
|
10031
10104
|
// src/hooks/useAppAutoEvents.ts
|
|
10032
|
-
import { useEffect as useEffect13, useRef as
|
|
10105
|
+
import { useEffect as useEffect13, useRef as useRef9 } from "react";
|
|
10033
10106
|
|
|
10034
10107
|
// src/utils/platformDetection.ts
|
|
10035
10108
|
async function detectPlatform3() {
|
|
@@ -10055,7 +10128,7 @@ var FIRST_SEEN_KEY = "@paywallo:firstSeen_v226";
|
|
|
10055
10128
|
var DEBOUNCE_MS = 1e3;
|
|
10056
10129
|
function useAppAutoEvents(config) {
|
|
10057
10130
|
const { isInitialized, getAppVersion: getAppVersion2, getSessionId, emitLifecycle, storageGet, storageSet, debug } = config;
|
|
10058
|
-
const didRunRef =
|
|
10131
|
+
const didRunRef = useRef9(false);
|
|
10059
10132
|
useEffect13(() => {
|
|
10060
10133
|
if (debug) console.log("[Paywallo EVENTS] useEffect fired", { isInitialized, didRunRefCurrent: didRunRef.current });
|
|
10061
10134
|
if (!isInitialized || didRunRef.current) return;
|
|
@@ -10066,13 +10139,13 @@ function useAppAutoEvents(config) {
|
|
|
10066
10139
|
const sessionId = getSessionId();
|
|
10067
10140
|
const appVersion = getAppVersion2();
|
|
10068
10141
|
const deviceType = await detectPlatform3();
|
|
10069
|
-
const
|
|
10070
|
-
if (debug) console.log("[Paywallo EVENTS] computed device context", { deviceType, osVersion, appVersion, sessionId, hasSessionId: !!sessionId });
|
|
10142
|
+
const osVersion2 = await detectOsVersion();
|
|
10143
|
+
if (debug) console.log("[Paywallo EVENTS] computed device context", { deviceType, osVersion: osVersion2, appVersion, sessionId, hasSessionId: !!sessionId });
|
|
10071
10144
|
try {
|
|
10072
10145
|
if (debug) console.log("[Paywallo EVENTS] checking install \u2014 about to read FIRST_SEEN_KEY");
|
|
10073
10146
|
const firstSeen = await storageGet(FIRST_SEEN_KEY);
|
|
10074
10147
|
if (!firstSeen) {
|
|
10075
|
-
await emitLifecycle({ type: "install", device_type: deviceType, os_version:
|
|
10148
|
+
await emitLifecycle({ type: "install", device_type: deviceType, os_version: osVersion2, app_version: appVersion, ...sessionId ? { session_id: sessionId } : {} }, "critical");
|
|
10076
10149
|
await storageSet(FIRST_SEEN_KEY, (/* @__PURE__ */ new Date()).toISOString());
|
|
10077
10150
|
if (debug) console.log("[Paywallo EVENTS] install event emitted (first time)");
|
|
10078
10151
|
if (debug) console.log("[Paywallo EVENTS] lifecycle install emitted");
|
|
@@ -10084,7 +10157,7 @@ function useAppAutoEvents(config) {
|
|
|
10084
10157
|
}
|
|
10085
10158
|
try {
|
|
10086
10159
|
if (debug) console.log("[Paywallo EVENTS] emitting cold_start");
|
|
10087
|
-
await emitLifecycle({ type: "cold_start", app_version: appVersion, device_type: deviceType, os_version:
|
|
10160
|
+
await emitLifecycle({ type: "cold_start", app_version: appVersion, device_type: deviceType, os_version: osVersion2, ...sessionId ? { session_id: sessionId } : {} }, "normal");
|
|
10088
10161
|
if (debug) console.log("[Paywallo EVENTS] lifecycle cold_start emitted");
|
|
10089
10162
|
} catch (err) {
|
|
10090
10163
|
if (debug) console.log("[Paywallo EVENTS] lifecycle cold_start failed", err);
|
|
@@ -10098,7 +10171,7 @@ function useAppAutoEvents(config) {
|
|
|
10098
10171
|
// src/hooks/useCampaignPreload.ts
|
|
10099
10172
|
init_PaywalloClient();
|
|
10100
10173
|
init_CampaignError();
|
|
10101
|
-
import { useCallback as
|
|
10174
|
+
import { useCallback as useCallback16, useRef as useRef10, useState as useState14 } from "react";
|
|
10102
10175
|
|
|
10103
10176
|
// src/utils/loadCampaignProducts.ts
|
|
10104
10177
|
init_IAPService();
|
|
@@ -10168,16 +10241,16 @@ function getCacheEntry(cache, placement) {
|
|
|
10168
10241
|
// src/hooks/useCampaignPreload.ts
|
|
10169
10242
|
function useCampaignPreload() {
|
|
10170
10243
|
const [preloadedWebView, setPreloadedWebView] = useState14(null);
|
|
10171
|
-
const preloadedCampaignsRef =
|
|
10172
|
-
const isPreloadValid =
|
|
10244
|
+
const preloadedCampaignsRef = useRef10(/* @__PURE__ */ new Map());
|
|
10245
|
+
const isPreloadValid = useCallback16(
|
|
10173
10246
|
(placement) => isCacheEntryValid(preloadedCampaignsRef.current.get(placement)),
|
|
10174
10247
|
[]
|
|
10175
10248
|
);
|
|
10176
|
-
const consumePreloadedCampaign =
|
|
10249
|
+
const consumePreloadedCampaign = useCallback16(
|
|
10177
10250
|
(placement) => getCacheEntry(preloadedCampaignsRef.current, placement),
|
|
10178
10251
|
[]
|
|
10179
10252
|
);
|
|
10180
|
-
const preloadCampaign =
|
|
10253
|
+
const preloadCampaign = useCallback16(
|
|
10181
10254
|
async (placement, context) => {
|
|
10182
10255
|
try {
|
|
10183
10256
|
const campaign = await PaywalloClient.getCampaign(placement, context);
|
|
@@ -10246,11 +10319,11 @@ function useCampaignPreload() {
|
|
|
10246
10319
|
}
|
|
10247
10320
|
|
|
10248
10321
|
// src/hooks/useProductLoader.ts
|
|
10249
|
-
import { useCallback as
|
|
10322
|
+
import { useCallback as useCallback17, useState as useState15 } from "react";
|
|
10250
10323
|
function useProductLoader() {
|
|
10251
10324
|
const [products, setProducts] = useState15(/* @__PURE__ */ new Map());
|
|
10252
10325
|
const [isLoadingProducts, setIsLoadingProducts] = useState15(false);
|
|
10253
|
-
const refreshProducts =
|
|
10326
|
+
const refreshProducts = useCallback17(async (productIds) => {
|
|
10254
10327
|
setIsLoadingProducts(true);
|
|
10255
10328
|
try {
|
|
10256
10329
|
const iapService = getIAPService();
|
|
@@ -10268,11 +10341,11 @@ function useProductLoader() {
|
|
|
10268
10341
|
}
|
|
10269
10342
|
|
|
10270
10343
|
// src/hooks/usePurchaseOrchestrator.ts
|
|
10271
|
-
import { useCallback as
|
|
10344
|
+
import { useCallback as useCallback18 } from "react";
|
|
10272
10345
|
init_paywall();
|
|
10273
10346
|
function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, invalidateSubscriptionCache) {
|
|
10274
10347
|
const { trackDismissed, trackProductSelected, trackPurchased } = usePaywallTracking();
|
|
10275
|
-
const handlePreloadedPurchase =
|
|
10348
|
+
const handlePreloadedPurchase = useCallback18(
|
|
10276
10349
|
async (productId) => {
|
|
10277
10350
|
try {
|
|
10278
10351
|
if (preloadedWebView) {
|
|
@@ -10310,7 +10383,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
|
|
|
10310
10383
|
},
|
|
10311
10384
|
[preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, trackDismissed, trackProductSelected, trackPurchased, invalidateSubscriptionCache]
|
|
10312
10385
|
);
|
|
10313
|
-
const handlePreloadedRestore =
|
|
10386
|
+
const handlePreloadedRestore = useCallback18(async () => {
|
|
10314
10387
|
try {
|
|
10315
10388
|
const transactions = await getIAPService().restore();
|
|
10316
10389
|
if (transactions.length === 0) return;
|
|
@@ -10335,7 +10408,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
|
|
|
10335
10408
|
// src/hooks/useSubscriptionSync.ts
|
|
10336
10409
|
init_PaywalloClient();
|
|
10337
10410
|
init_SubscriptionCache();
|
|
10338
|
-
import { useCallback as
|
|
10411
|
+
import { useCallback as useCallback19, useRef as useRef11, useState as useState16 } from "react";
|
|
10339
10412
|
|
|
10340
10413
|
// src/hooks/subscriptionSyncHelpers.ts
|
|
10341
10414
|
init_SubscriptionCache();
|
|
@@ -10444,17 +10517,17 @@ async function checkPersistedCache(distinctId, onHit) {
|
|
|
10444
10517
|
// src/hooks/useSubscriptionSync.ts
|
|
10445
10518
|
function useSubscriptionSync() {
|
|
10446
10519
|
const [subscription, setSubscription] = useState16(null);
|
|
10447
|
-
const cacheRef =
|
|
10448
|
-
const setCacheAndState =
|
|
10520
|
+
const cacheRef = useRef11(null);
|
|
10521
|
+
const setCacheAndState = useCallback19((sub) => {
|
|
10449
10522
|
cacheRef.current = { data: sub, expiresAt: Date.now() + CACHE_TTL_MS2 };
|
|
10450
10523
|
setSubscription(sub);
|
|
10451
10524
|
}, []);
|
|
10452
|
-
const invalidateCache =
|
|
10525
|
+
const invalidateCache = useCallback19(() => {
|
|
10453
10526
|
cacheRef.current = null;
|
|
10454
10527
|
const distinctId = PaywalloClient.getDistinctId();
|
|
10455
10528
|
if (distinctId) void subscriptionCache.invalidate(distinctId);
|
|
10456
10529
|
}, []);
|
|
10457
|
-
const fetchAndCache =
|
|
10530
|
+
const fetchAndCache = useCallback19(async () => {
|
|
10458
10531
|
const apiClient = PaywalloClient.getApiClient();
|
|
10459
10532
|
const distinctId = PaywalloClient.getDistinctId();
|
|
10460
10533
|
if (!apiClient || !distinctId) return null;
|
|
@@ -10464,7 +10537,7 @@ function useSubscriptionSync() {
|
|
|
10464
10537
|
void subscriptionCache.set(distinctId, toDomainResponse(status));
|
|
10465
10538
|
return sub;
|
|
10466
10539
|
}, [setCacheAndState]);
|
|
10467
|
-
const hasActiveSubscription =
|
|
10540
|
+
const hasActiveSubscription = useCallback19(async () => {
|
|
10468
10541
|
const apiClient = PaywalloClient.getApiClient();
|
|
10469
10542
|
const distinctId = PaywalloClient.getDistinctId();
|
|
10470
10543
|
if (!apiClient || !distinctId) return false;
|
|
@@ -10484,7 +10557,7 @@ function useSubscriptionSync() {
|
|
|
10484
10557
|
return resolveOfflineFromCache(distinctId);
|
|
10485
10558
|
}
|
|
10486
10559
|
}, [setCacheAndState]);
|
|
10487
|
-
const getSubscription =
|
|
10560
|
+
const getSubscription = useCallback19(async () => {
|
|
10488
10561
|
const apiClient = PaywalloClient.getApiClient();
|
|
10489
10562
|
const distinctId = PaywalloClient.getDistinctId();
|
|
10490
10563
|
if (!apiClient || !distinctId) return null;
|
|
@@ -10516,12 +10589,12 @@ function PaywalloProvider({ children, config }) {
|
|
|
10516
10589
|
const { products, isLoadingProducts, refreshProducts } = useProductLoader();
|
|
10517
10590
|
const { subscription, hasActiveSubscription, getSubscription, invalidateCache } = useSubscriptionSync();
|
|
10518
10591
|
const { preloadedWebView, setPreloadedWebView, preloadCampaign, consumePreloadedCampaign, isPreloadValid } = useCampaignPreload();
|
|
10519
|
-
const clearPreloadedWebView =
|
|
10520
|
-
const autoPreloadTriggeredRef =
|
|
10521
|
-
const preloadedWebViewRef =
|
|
10522
|
-
const pollTimerRef =
|
|
10523
|
-
const pollCancelledRef =
|
|
10524
|
-
const secureStorageRef =
|
|
10592
|
+
const clearPreloadedWebView = useCallback20(() => setPreloadedWebView(null), [setPreloadedWebView]);
|
|
10593
|
+
const autoPreloadTriggeredRef = useRef12(false);
|
|
10594
|
+
const preloadedWebViewRef = useRef12(preloadedWebView);
|
|
10595
|
+
const pollTimerRef = useRef12(null);
|
|
10596
|
+
const pollCancelledRef = useRef12(false);
|
|
10597
|
+
const secureStorageRef = useRef12(new SecureStorage(config.debug));
|
|
10525
10598
|
useEffect14(() => {
|
|
10526
10599
|
preloadedWebViewRef.current = preloadedWebView;
|
|
10527
10600
|
}, [preloadedWebView]);
|
|
@@ -10548,20 +10621,20 @@ function PaywalloProvider({ children, config }) {
|
|
|
10548
10621
|
},
|
|
10549
10622
|
debug: config.debug
|
|
10550
10623
|
});
|
|
10551
|
-
const triggerRepreload =
|
|
10624
|
+
const triggerRepreload = useCallback20((placement) => {
|
|
10552
10625
|
void PaywalloClient.preloadCampaign(placement).catch(() => {
|
|
10553
10626
|
});
|
|
10554
10627
|
}, []);
|
|
10555
10628
|
const { showingPreloadedWebView, presentPreloadedWebView, resolvePreloadedPaywall, handlePreloadedWebViewReady: baseHandlePreloadedWebViewReady, handlePreloadedClose: baseHandlePreloadedClose, setShowingPreloadedWebView, presentedAtRef } = usePaywallPresenter(preloadedWebView, setPreloadedWebView);
|
|
10556
10629
|
const SCREEN_HEIGHT = useMemo3(() => Dimensions5.get("window").height, []);
|
|
10557
|
-
const slideAnim =
|
|
10558
|
-
const handlePreloadedWebViewReady =
|
|
10630
|
+
const slideAnim = useRef12(new Animated4.Value(SCREEN_HEIGHT)).current;
|
|
10631
|
+
const handlePreloadedWebViewReady = useCallback20(() => {
|
|
10559
10632
|
if (PaywalloClient.getConfig()?.debug) {
|
|
10560
10633
|
console.log(`[Paywallo WEBVIEW] loaded \u2014 placement: ${preloadedWebView?.placement}`);
|
|
10561
10634
|
}
|
|
10562
10635
|
baseHandlePreloadedWebViewReady();
|
|
10563
10636
|
}, [preloadedWebView?.placement, baseHandlePreloadedWebViewReady]);
|
|
10564
|
-
const handlePreloadedClose =
|
|
10637
|
+
const handlePreloadedClose = useCallback20(() => {
|
|
10565
10638
|
const placement = preloadedWebView?.placement;
|
|
10566
10639
|
Animated4.timing(slideAnim, { toValue: SCREEN_HEIGHT, duration: 200, easing: Easing2.in(Easing2.cubic), useNativeDriver: true }).start(() => {
|
|
10567
10640
|
baseHandlePreloadedClose();
|
|
@@ -10577,7 +10650,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
10577
10650
|
invalidateCache
|
|
10578
10651
|
);
|
|
10579
10652
|
const [isPreloadPurchasing, setIsPreloadPurchasing] = useState17(false);
|
|
10580
|
-
const handlePreloadedPurchase =
|
|
10653
|
+
const handlePreloadedPurchase = useCallback20(async (productId) => {
|
|
10581
10654
|
setIsPreloadPurchasing(true);
|
|
10582
10655
|
try {
|
|
10583
10656
|
await rawPreloadedPurchase(productId);
|
|
@@ -10585,15 +10658,25 @@ function PaywalloProvider({ children, config }) {
|
|
|
10585
10658
|
setIsPreloadPurchasing(false);
|
|
10586
10659
|
}
|
|
10587
10660
|
}, [rawPreloadedPurchase]);
|
|
10588
|
-
const configRef =
|
|
10661
|
+
const configRef = useRef12(config);
|
|
10589
10662
|
useEffect14(() => {
|
|
10590
10663
|
let mounted = true;
|
|
10591
|
-
|
|
10592
|
-
|
|
10664
|
+
const bootstrap = async () => {
|
|
10665
|
+
if (!PaywalloClient.isReady()) {
|
|
10666
|
+
initLocalization({ detectDevice: true });
|
|
10667
|
+
try {
|
|
10668
|
+
await PaywalloClient.init(configRef.current);
|
|
10669
|
+
} catch (err) {
|
|
10670
|
+
if (!mounted) return;
|
|
10671
|
+
setInitError(err instanceof Error ? err : new ClientError(CLIENT_ERROR_CODES.NOT_INITIALIZED, String(err)));
|
|
10672
|
+
return;
|
|
10673
|
+
}
|
|
10674
|
+
if (!mounted) return;
|
|
10675
|
+
startSuperwallAutoBridge(configRef.current.debug);
|
|
10676
|
+
void pushAttributionToSuperwall(configRef.current.debug);
|
|
10677
|
+
}
|
|
10593
10678
|
if (!mounted) return;
|
|
10594
10679
|
setDistinctId(PaywalloClient.getDistinctId());
|
|
10595
|
-
startSuperwallAutoBridge(configRef.current.debug);
|
|
10596
|
-
void pushAttributionToSuperwall(configRef.current.debug);
|
|
10597
10680
|
setIsInitialized(true);
|
|
10598
10681
|
if (!autoPreloadTriggeredRef.current) {
|
|
10599
10682
|
autoPreloadTriggeredRef.current = true;
|
|
@@ -10606,19 +10689,17 @@ function PaywalloProvider({ children, config }) {
|
|
|
10606
10689
|
});
|
|
10607
10690
|
}
|
|
10608
10691
|
}
|
|
10609
|
-
}
|
|
10610
|
-
|
|
10611
|
-
setInitError(err instanceof Error ? err : new ClientError(CLIENT_ERROR_CODES.NOT_INITIALIZED, String(err)));
|
|
10612
|
-
});
|
|
10692
|
+
};
|
|
10693
|
+
void bootstrap();
|
|
10613
10694
|
return () => {
|
|
10614
10695
|
mounted = false;
|
|
10615
10696
|
};
|
|
10616
10697
|
}, [preloadCampaign]);
|
|
10617
|
-
const getPaywallConfig =
|
|
10698
|
+
const getPaywallConfig = useCallback20(async (p) => {
|
|
10618
10699
|
const api = PaywalloClient.getApiClient();
|
|
10619
10700
|
return api ? api.getPaywall(p) : null;
|
|
10620
10701
|
}, []);
|
|
10621
|
-
const presentPaywall =
|
|
10702
|
+
const presentPaywall = useCallback20((placement) => {
|
|
10622
10703
|
const preloaded = paywallPreloadService.getPreloaded(placement);
|
|
10623
10704
|
return new Promise((resolve) => setActivePaywall({
|
|
10624
10705
|
placement,
|
|
@@ -10636,7 +10717,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
10636
10717
|
}
|
|
10637
10718
|
}));
|
|
10638
10719
|
}, []);
|
|
10639
|
-
const trackCampaignImpression =
|
|
10720
|
+
const trackCampaignImpression = useCallback20((placement, variantKey, campaignId) => {
|
|
10640
10721
|
const api = PaywalloClient.getApiClient();
|
|
10641
10722
|
if (!api) return;
|
|
10642
10723
|
const sessionId = PaywalloClient.getSessionId();
|
|
@@ -10650,7 +10731,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
10650
10731
|
}).catch(() => {
|
|
10651
10732
|
});
|
|
10652
10733
|
}, []);
|
|
10653
|
-
const presentCampaign =
|
|
10734
|
+
const presentCampaign = useCallback20(
|
|
10654
10735
|
async (placement, context) => {
|
|
10655
10736
|
try {
|
|
10656
10737
|
if (preloadedWebView?.placement === placement && preloadedWebView.loaded && isPreloadValid(placement)) {
|
|
@@ -10724,7 +10805,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
10724
10805
|
},
|
|
10725
10806
|
[preloadedWebView, isPreloadValid, consumePreloadedCampaign, presentPreloadedWebView, trackCampaignImpression]
|
|
10726
10807
|
);
|
|
10727
|
-
const restorePurchases =
|
|
10808
|
+
const restorePurchases = useCallback20(async () => {
|
|
10728
10809
|
invalidateCache();
|
|
10729
10810
|
try {
|
|
10730
10811
|
const txs = await getIAPService().restore();
|
|
@@ -10733,13 +10814,13 @@ function PaywalloProvider({ children, config }) {
|
|
|
10733
10814
|
return { success: false, restoredProducts: [], error: error instanceof Error ? error : new PurchaseError(PURCHASE_ERROR_CODES.RESTORE_FAILED, String(error)) };
|
|
10734
10815
|
}
|
|
10735
10816
|
}, [invalidateCache]);
|
|
10736
|
-
const handlePaywallResult =
|
|
10817
|
+
const handlePaywallResult = useCallback20((result) => {
|
|
10737
10818
|
if (activePaywall) {
|
|
10738
10819
|
activePaywall.resolver(result);
|
|
10739
10820
|
setActivePaywall(null);
|
|
10740
10821
|
}
|
|
10741
10822
|
}, [activePaywall]);
|
|
10742
|
-
const bridgePaywallPresenter =
|
|
10823
|
+
const bridgePaywallPresenter = useCallback20((placement) => {
|
|
10743
10824
|
if (PaywalloClient.getConfig()?.debug) {
|
|
10744
10825
|
console.log(`[Paywallo PRESENT] called \u2014 preloaded: ${preloadedWebViewRef.current?.loaded === true} placement: ${placement}`);
|
|
10745
10826
|
}
|
|
@@ -10795,7 +10876,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
10795
10876
|
};
|
|
10796
10877
|
}, [isInitialized, bridgePaywallPresenter, getSubscription, hasActiveSubscription, restorePurchases]);
|
|
10797
10878
|
const isReady = isInitialized && !isLoadingProducts;
|
|
10798
|
-
const noOp =
|
|
10879
|
+
const noOp = useCallback20(() => {
|
|
10799
10880
|
}, []);
|
|
10800
10881
|
const contextValue = useMemo3(() => ({
|
|
10801
10882
|
config: PaywalloClient.getConfig(),
|
|
@@ -11050,6 +11131,7 @@ export {
|
|
|
11050
11131
|
createPurchaseError,
|
|
11051
11132
|
index_default as default,
|
|
11052
11133
|
detectDeviceLanguage,
|
|
11134
|
+
getActiveRouteName,
|
|
11053
11135
|
getCurrentLanguage,
|
|
11054
11136
|
getDefaultLanguage,
|
|
11055
11137
|
getIAPService,
|
|
@@ -11078,6 +11160,7 @@ export {
|
|
|
11078
11160
|
useOnboarding,
|
|
11079
11161
|
usePaywallContext,
|
|
11080
11162
|
usePaywallo,
|
|
11163
|
+
usePaywalloScreenTracking,
|
|
11081
11164
|
usePlanPurchase,
|
|
11082
11165
|
usePlans,
|
|
11083
11166
|
useProducts,
|