@virex-tech/paywallo-sdk 2.4.1 → 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/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 { NativeModules as NativeModules2 } from "react-native";
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 isDeviceInfoResult(value) {
255
- if (typeof value !== "object" || value === null) return false;
256
- const v = value;
257
- return typeof v["deviceId"] === "string" && typeof v["model"] === "string" && typeof v["systemVersion"] === "string" && typeof v["appVersion"] === "string" && typeof v["bundleId"] === "string";
258
- }
259
- function getNativeModule() {
260
- const mod = NativeModules2.PaywalloDevice;
261
- if (!mod) {
262
- if (!warnedOnce) {
263
- warnedOnce = true;
264
- if (_debug2) console.log("[Paywallo] NativeModules.PaywalloDevice not found \u2014 falling back to defaults");
265
- }
266
- return null;
267
- }
268
- return mod;
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
- const mod = getNativeModule();
273
- if (!mod) return FALLBACK;
274
- const raw = await mod.getDeviceInfo();
275
- if (!isDeviceInfoResult(raw)) return FALLBACK;
276
- cachedDeviceInfo = raw;
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 mod.getInstallReferrer();
319
+ return await Application.getInstallReferrerAsync();
284
320
  } catch {
285
321
  return null;
286
322
  }
287
323
  }
288
324
  function isAvailable2() {
289
- return NativeModules2.PaywalloDevice != null;
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 FALLBACK, _debug2, warnedOnce, cachedDeviceInfo, nativeDeviceInfo;
339
+ var _debug2, cachedDeviceInfo, nativeDeviceInfo;
298
340
  var init_NativeDeviceInfo = __esm({
299
341
  "src/utils/NativeDeviceInfo.ts"() {
300
342
  "use strict";
301
- FALLBACK = {
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 (Platform.OS === "ios") {
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 (Platform.OS === "android") {
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.model,
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 Platform2 } from "react-native";
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 (Platform2.OS !== "android") {
1468
- return this.emptyResult();
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 Platform3 } from "react-native";
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 osVersion = info.systemVersion || "unknown";
1850
+ const osVersion2 = info.systemVersion || "unknown";
1682
1851
  const appVersion = info.appVersion || "unknown";
1683
1852
  const buildNumber = info.buildNumber || "0";
1684
- let carrier = "unknown";
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
- if (Platform3.OS === "android") {
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
- ...carrier !== "unknown" && { carrier },
1727
- ...totalDisk > 0 && { totalDisk },
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, installEventId] = await Promise.all([
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: Platform3.OS,
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: Platform3.OS,
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 NativeModules4, Platform as Platform4 } from "react-native";
2083
+ import { NativeEventEmitter, NativeModules as NativeModules3, Platform as Platform5 } from "react-native";
1942
2084
  function getPaywalloNotifications() {
1943
- return NativeModules4.PaywalloNotifications ?? null;
2085
+ return NativeModules3.PaywalloNotifications ?? null;
1944
2086
  }
1945
2087
  function isNativePushAvailable() {
1946
- return !!NativeModules4.PaywalloNotifications;
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 = NativeModules4.PaywalloNotifications;
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 (Platform4.OS !== "ios") return null;
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://panel.lucasqueiroga.shop";
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 Platform5 } from "react-native";
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 Platform5.OS === "ios" ? "ios" : "android";
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.4.1") {
2409
- return "2.4.1";
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 Platform6 } from "react-native";
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 = getLocales()[0]?.languageCode ?? "en";
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 Platform6.OS === "ios" ? "ios" : "android";
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 Platform7 } from "react-native";
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 Platform7;
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 NativeModules5, Platform as Platform8 } from "react-native";
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 (Platform8.OS === "ios") {
3293
- const settings = NativeModules5.SettingsManager?.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 NativeModules5.I18nManager?.localeIdentifier ?? "en-US";
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 NativeModules6,
3468
- Platform as Platform9,
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 = NativeModules6.PaywalloWebViewModule;
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 (Platform9.OS === "android") {
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 (Platform9.OS !== "android") return;
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 `transaction {type: "completed"}` after a server-validated purchase.
4026
- * Fire-and-forgeta tracking failure must never poison the purchase flow.
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 emitTransactionCompleted(purchase, productId, options) {
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
- type: "completed",
4037
- tx_id: purchase.transactionId,
4038
- product_id: purchase.productId
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,93 +3944,20 @@ var init_IAPTransactionEmitter = __esm({
4077
3944
  }
4078
3945
  }
4079
3946
  /**
4080
- * Forwards a native transaction update to the EventBatcher.
4081
- * Public so tests can exercise the wiring without a fake NativeEventEmitter.
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
- * Removes the native listener. Safe to call multiple times.
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
3956
  }
4140
3957
  };
4141
3958
  }
4142
3959
  });
4143
3960
 
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
- }
4155
- };
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
- }
4165
- });
4166
-
4167
3961
  // src/core/queue/deduplication.ts
4168
3962
  function normalizeForComparison(obj) {
4169
3963
  if (obj === null || obj === void 0) {
@@ -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,8 +4989,202 @@ var init_IAPValidator = __esm({
5145
4989
  }
5146
4990
  });
5147
4991
 
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;
5022
+ }
5023
+ return product;
5024
+ }
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"() {
5094
+ "use strict";
5095
+ init_PaywalloClient();
5096
+ init_uuid();
5097
+ init_IdentityManager();
5098
+ init_PurchaseError();
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
+
5148
5186
  // src/domains/iap/IAPService.ts
5149
- import { Platform as Platform12 } from "react-native";
5187
+ import { Platform as Platform13 } from "react-native";
5150
5188
  function getIAPService() {
5151
5189
  if (!_instance) {
5152
5190
  _instance = new IAPService();
@@ -5245,8 +5283,8 @@ var init_IAPService = __esm({
5245
5283
  stopTransactionListener() {
5246
5284
  this.emitter.stopListener();
5247
5285
  }
5248
- async emitTransactionUpdate(update) {
5249
- return this.emitter.emitTransactionUpdate(update);
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 = Platform12.OS;
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);
@@ -6374,8 +6408,145 @@ var init_plan = __esm({
6374
6408
  var init_session = __esm({
6375
6409
  "src/domains/session/index.ts"() {
6376
6410
  "use strict";
6377
- init_SessionError();
6378
- init_SessionManager();
6411
+ init_SessionError();
6412
+ init_SessionManager();
6413
+ }
6414
+ });
6415
+
6416
+ // src/domains/onboarding/OnboardingError.ts
6417
+ var OnboardingError, ONBOARDING_ERROR_CODES;
6418
+ var init_OnboardingError = __esm({
6419
+ "src/domains/onboarding/OnboardingError.ts"() {
6420
+ "use strict";
6421
+ init_PaywalloError();
6422
+ OnboardingError = class extends PaywalloError {
6423
+ constructor(code, message) {
6424
+ super("onboarding", code, message);
6425
+ this.name = "OnboardingError";
6426
+ }
6427
+ };
6428
+ ONBOARDING_ERROR_CODES = {
6429
+ NOT_INITIALIZED: "ONBOARDING_NOT_INITIALIZED",
6430
+ INVALID_STEP_NAME: "ONBOARDING_INVALID_STEP_NAME"
6431
+ };
6432
+ }
6433
+ });
6434
+
6435
+ // src/domains/onboarding/OnboardingManager.ts
6436
+ var OnboardingManager, onboardingManager;
6437
+ var init_OnboardingManager = __esm({
6438
+ "src/domains/onboarding/OnboardingManager.ts"() {
6439
+ "use strict";
6440
+ init_OnboardingError();
6441
+ OnboardingManager = class {
6442
+ constructor() {
6443
+ this.pipeline = null;
6444
+ this.debug = false;
6445
+ this.lastStep = null;
6446
+ this.finished = false;
6447
+ }
6448
+ /**
6449
+ * Injects runtime dependencies. Called by PaywalloClient during init.
6450
+ */
6451
+ injectDeps(config) {
6452
+ this.pipeline = config.pipeline;
6453
+ this.debug = config.debug ?? false;
6454
+ }
6455
+ /**
6456
+ * Tracks an onboarding step view.
6457
+ * Saves the step name internally for implicit drop detection on the backend.
6458
+ *
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).
6461
+ */
6462
+ async step(stepName, order) {
6463
+ this.assertConfig();
6464
+ this.validateStepName(stepName, "step");
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
+ }
6470
+ const payload = {
6471
+ family: "onboarding",
6472
+ type: "step",
6473
+ ...stepPayload
6474
+ };
6475
+ return this.emit("onboarding", payload, "step");
6476
+ }
6477
+ /**
6478
+ * Tracks successful completion of the onboarding flow.
6479
+ * Marks the flow as finished.
6480
+ */
6481
+ async complete() {
6482
+ this.assertConfig();
6483
+ this.finished = true;
6484
+ return this.emit("onboarding", { family: "onboarding", type: "complete" }, "complete");
6485
+ }
6486
+ /**
6487
+ * Tracks an explicit drop (abandonment) at the given step.
6488
+ * Marks the flow as finished.
6489
+ *
6490
+ * @param stepName - The step name where the user dropped off.
6491
+ */
6492
+ async drop(stepName) {
6493
+ this.assertConfig();
6494
+ this.validateStepName(stepName, "drop");
6495
+ this.finished = true;
6496
+ const payload = {
6497
+ family: "onboarding",
6498
+ type: "drop",
6499
+ ...{ step_name: stepName }
6500
+ };
6501
+ return this.emit("onboarding", payload, "drop");
6502
+ }
6503
+ /** Returns the last step name seen, or null if no step was tracked yet. */
6504
+ getLastStep() {
6505
+ return this.lastStep;
6506
+ }
6507
+ /** Returns whether the flow has been marked as finished (complete or drop). */
6508
+ isFinished() {
6509
+ return this.finished;
6510
+ }
6511
+ async emit(eventName, properties, logLabel) {
6512
+ const pipeline = this.pipeline;
6513
+ const distinctId = pipeline.getDistinctId();
6514
+ if (!distinctId) {
6515
+ if (this.debug) console.log(`[Paywallo:Onboarding] skipped (no distinctId): ${logLabel}`);
6516
+ return;
6517
+ }
6518
+ await pipeline.trackEvent(eventName, distinctId, properties);
6519
+ if (this.debug) {
6520
+ console.log(`[Paywallo:Onboarding] event emitted: ${eventName}.${logLabel}`, properties);
6521
+ }
6522
+ }
6523
+ assertConfig() {
6524
+ if (!this.pipeline) {
6525
+ throw new OnboardingError(
6526
+ ONBOARDING_ERROR_CODES.NOT_INITIALIZED,
6527
+ "OnboardingManager not initialized. Call Paywallo.init() first."
6528
+ );
6529
+ }
6530
+ }
6531
+ validateStepName(stepName, method) {
6532
+ if (typeof stepName !== "string" || stepName.trim().length === 0) {
6533
+ throw new OnboardingError(
6534
+ ONBOARDING_ERROR_CODES.INVALID_STEP_NAME,
6535
+ `OnboardingManager.${method}(): stepName must be a non-empty string.`
6536
+ );
6537
+ }
6538
+ }
6539
+ };
6540
+ onboardingManager = new OnboardingManager();
6541
+ }
6542
+ });
6543
+
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();
6379
6550
  }
6380
6551
  });
6381
6552
 
@@ -6594,18 +6765,6 @@ async function trackEvent(apiClient, state, eventName, options) {
6594
6765
  options?.priority ? { priority: options.priority } : void 0
6595
6766
  );
6596
6767
  }
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
6768
  function validateCoreAction(actionName) {
6610
6769
  if (!actionName || typeof actionName !== "string") {
6611
6770
  const msg = `Invalid actionName: "${actionName}". Must be a non-empty string.`;
@@ -6669,138 +6828,6 @@ var init_PaywalloFlags = __esm({
6669
6828
  }
6670
6829
  });
6671
6830
 
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"() {
6695
- "use strict";
6696
- init_OnboardingError();
6697
- OnboardingManager = class {
6698
- constructor() {
6699
- this.pipeline = null;
6700
- this.debug = false;
6701
- this.lastStep = null;
6702
- this.finished = false;
6703
- }
6704
- /**
6705
- * Injects runtime dependencies. Called by PaywalloClient during init.
6706
- */
6707
- injectDeps(config) {
6708
- this.pipeline = config.pipeline;
6709
- this.debug = config.debug ?? false;
6710
- }
6711
- /**
6712
- * Tracks an onboarding step view.
6713
- * Saves the step name internally for implicit drop detection on the backend.
6714
- *
6715
- * @param stepName - Unique name for this onboarding step.
6716
- */
6717
- async step(stepName) {
6718
- this.assertConfig();
6719
- this.validateStepName(stepName, "step");
6720
- this.lastStep = stepName;
6721
- const payload = {
6722
- family: "onboarding",
6723
- type: "step",
6724
- ...{ step_name: stepName }
6725
- };
6726
- return this.emit("onboarding", payload, "step");
6727
- }
6728
- /**
6729
- * Tracks successful completion of the onboarding flow.
6730
- * Marks the flow as finished.
6731
- */
6732
- async complete() {
6733
- this.assertConfig();
6734
- this.finished = true;
6735
- return this.emit("onboarding", { family: "onboarding", type: "complete" }, "complete");
6736
- }
6737
- /**
6738
- * Tracks an explicit drop (abandonment) at the given step.
6739
- * Marks the flow as finished.
6740
- *
6741
- * @param stepName - The step name where the user dropped off.
6742
- */
6743
- async drop(stepName) {
6744
- this.assertConfig();
6745
- this.validateStepName(stepName, "drop");
6746
- this.finished = true;
6747
- const payload = {
6748
- family: "onboarding",
6749
- type: "drop",
6750
- ...{ step_name: stepName }
6751
- };
6752
- return this.emit("onboarding", payload, "drop");
6753
- }
6754
- /** Returns the last step name seen, or null if no step was tracked yet. */
6755
- getLastStep() {
6756
- return this.lastStep;
6757
- }
6758
- /** Returns whether the flow has been marked as finished (complete or drop). */
6759
- isFinished() {
6760
- return this.finished;
6761
- }
6762
- async emit(eventName, properties, logLabel) {
6763
- const pipeline = this.pipeline;
6764
- const distinctId = pipeline.getDistinctId();
6765
- if (!distinctId) {
6766
- if (this.debug) console.log(`[Paywallo:Onboarding] skipped (no distinctId): ${logLabel}`);
6767
- return;
6768
- }
6769
- await pipeline.trackEvent(eventName, distinctId, properties);
6770
- if (this.debug) {
6771
- console.log(`[Paywallo:Onboarding] event emitted: ${eventName}.${logLabel}`, properties);
6772
- }
6773
- }
6774
- assertConfig() {
6775
- if (!this.pipeline) {
6776
- throw new OnboardingError(
6777
- ONBOARDING_ERROR_CODES.NOT_INITIALIZED,
6778
- "OnboardingManager not initialized. Call Paywallo.init() first."
6779
- );
6780
- }
6781
- }
6782
- validateStepName(stepName, method) {
6783
- if (typeof stepName !== "string" || stepName.trim().length === 0) {
6784
- throw new OnboardingError(
6785
- ONBOARDING_ERROR_CODES.INVALID_STEP_NAME,
6786
- `OnboardingManager.${method}(): stepName must be a non-empty string.`
6787
- );
6788
- }
6789
- }
6790
- };
6791
- onboardingManager = new OnboardingManager();
6792
- }
6793
- });
6794
-
6795
- // src/domains/onboarding/index.ts
6796
- var init_onboarding = __esm({
6797
- "src/domains/onboarding/index.ts"() {
6798
- "use strict";
6799
- init_OnboardingError();
6800
- init_OnboardingManager();
6801
- }
6802
- });
6803
-
6804
6831
  // src/domains/paywall/paywallHeartbeatRecovery.ts
6805
6832
  async function recoverPaywallHeartbeat(apiClient, distinctId, sessionId, debug) {
6806
6833
  const snapshot = await readPersistedHeartbeat();
@@ -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 Platform13 } from "react-native";
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
- return {
7477
- id: generateUUID(),
7478
- ts: event.timestamp,
7479
- name: family,
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 = Platform13.OS;
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 Platform14 } from "react-native";
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: Platform14.OS },
7735
+ properties: { ...properties, platform: Platform15.OS },
7650
7736
  timestamp: timestamp ?? Date.now(),
7651
7737
  environment: environment.toLowerCase()
7652
7738
  };
@@ -7972,7 +8058,7 @@ var init_http = __esm({
7972
8058
  });
7973
8059
 
7974
8060
  // src/core/ApiClient.ts
7975
- import { Platform as Platform15 } from "react-native";
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": Platform15.OS,
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: Platform15.OS
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: Platform15.OS,
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: config.apiUrl ?? DEFAULT_API_URL,
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) {
@@ -8507,7 +8563,7 @@ function reportInitFailure(state, config, error, reason) {
8507
8563
  try {
8508
8564
  const tempClient = new ApiClient(
8509
8565
  config.appKey,
8510
- config.apiUrl ?? DEFAULT_API_URL,
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
- config.apiUrl ?? DEFAULT_API_URL,
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
- carrier: device?.carrier || void 0,
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(index, name) {
8925
- return trackOnboardingStep(this.getApiClientOrThrow(), this.state, index, name);
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 Platform16 } from "react-native";
9210
+ import { Platform as Platform17 } from "react-native";
9150
9211
  function resolveProductId(raw) {
9151
- const isIos = Platform16.OS === "ios";
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 = Platform16.OS === "ios";
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 useCallback19, useEffect as useEffect14, useLayoutEffect, useMemo as useMemo3, useRef as useRef11, useState as useState17 } from "react";
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();
@@ -9962,50 +10056,9 @@ function buildCallbacks() {
9962
10056
  const identifier = "paywallIdentifier" in safe && typeof safe.paywallIdentifier === "string" ? safe.paywallIdentifier : productId;
9963
10057
  log2("onPurchase", { productId, identifier });
9964
10058
  void trackPaywallEvent("purchased", identifier, ts, { product_id: productId });
9965
- },
9966
- // Global Superwall event listener — used specifically for `transactionComplete`
9967
- // which is the only event that exposes the full product (currency/price) +
9968
- // transaction (storeTransactionId) shape needed to create a server-side
9969
- // `transactions` row. Other events are skipped (paywallOpen/Dismiss already
9970
- // handled above with their typed callbacks).
9971
- onSuperwallEvent: (info) => {
9972
- const ev = info?.event;
9973
- if (!ev || ev.event !== "transactionComplete") return;
9974
- const ts = Date.now();
9975
- const product = ev.product;
9976
- const transaction = ev.transaction;
9977
- if (!product) {
9978
- log2("transactionComplete missing product \u2014 skipping transaction track");
9979
- return;
9980
- }
9981
- const productId = product.productIdentifier ?? product.id ?? "unknown";
9982
- const currency = product.currencyCode ?? "USD";
9983
- const price = product.price ?? 0;
9984
- const transactionId = transaction?.storeTransactionId ?? transaction?.originalTransactionIdentifier ?? `${productId}_${String(ts)}`;
9985
- const isTrial = product.hasFreeTrial === true || (product.trialPeriodPrice ?? 0) > 0;
9986
- const paywallIdentifier = ev.paywallInfo?.identifier;
9987
- log2("onSuperwallEvent: transactionComplete", { productId, currency, price, transactionId, isTrial });
9988
- void (async () => {
9989
- try {
9990
- await PaywalloClient.track("transaction", {
9991
- priority: "critical",
9992
- properties: {
9993
- type: isTrial ? "trial_started" : "completed",
9994
- // Server's IngestTranslator looks for `transaction_id`/`product_id`
9995
- // /`amount`/`currency`/`paywall_id` keys (not `tx_id`). PurchaseHandler
9996
- // then resolves type=trial/new/renewal based on amount being 0 vs >0.
9997
- transaction_id: transactionId,
9998
- product_id: productId,
9999
- amount: price,
10000
- currency,
10001
- ...paywallIdentifier ? { paywall_id: paywallIdentifier } : {}
10002
- }
10003
- });
10004
- } catch (err) {
10005
- log2("transactionComplete track error", { err: String(err) });
10006
- }
10007
- })();
10008
10059
  }
10060
+ // onSuperwallEvent removed — transaction sale events are sourced exclusively
10061
+ // from server-side webhooks (Apple/Google/Superwall/RevenueCat).
10009
10062
  };
10010
10063
  }
10011
10064
  function startSuperwallAutoBridge(debug = false) {
@@ -10049,7 +10102,7 @@ init_session();
10049
10102
  init_ClientError();
10050
10103
 
10051
10104
  // src/hooks/useAppAutoEvents.ts
10052
- import { useEffect as useEffect13, useRef as useRef8 } from "react";
10105
+ import { useEffect as useEffect13, useRef as useRef9 } from "react";
10053
10106
 
10054
10107
  // src/utils/platformDetection.ts
10055
10108
  async function detectPlatform3() {
@@ -10075,7 +10128,7 @@ var FIRST_SEEN_KEY = "@paywallo:firstSeen_v226";
10075
10128
  var DEBOUNCE_MS = 1e3;
10076
10129
  function useAppAutoEvents(config) {
10077
10130
  const { isInitialized, getAppVersion: getAppVersion2, getSessionId, emitLifecycle, storageGet, storageSet, debug } = config;
10078
- const didRunRef = useRef8(false);
10131
+ const didRunRef = useRef9(false);
10079
10132
  useEffect13(() => {
10080
10133
  if (debug) console.log("[Paywallo EVENTS] useEffect fired", { isInitialized, didRunRefCurrent: didRunRef.current });
10081
10134
  if (!isInitialized || didRunRef.current) return;
@@ -10086,13 +10139,13 @@ function useAppAutoEvents(config) {
10086
10139
  const sessionId = getSessionId();
10087
10140
  const appVersion = getAppVersion2();
10088
10141
  const deviceType = await detectPlatform3();
10089
- const osVersion = await detectOsVersion();
10090
- 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 });
10091
10144
  try {
10092
10145
  if (debug) console.log("[Paywallo EVENTS] checking install \u2014 about to read FIRST_SEEN_KEY");
10093
10146
  const firstSeen = await storageGet(FIRST_SEEN_KEY);
10094
10147
  if (!firstSeen) {
10095
- await emitLifecycle({ type: "install", device_type: deviceType, os_version: osVersion, app_version: appVersion, ...sessionId ? { session_id: sessionId } : {} }, "critical");
10148
+ await emitLifecycle({ type: "install", device_type: deviceType, os_version: osVersion2, app_version: appVersion, ...sessionId ? { session_id: sessionId } : {} }, "critical");
10096
10149
  await storageSet(FIRST_SEEN_KEY, (/* @__PURE__ */ new Date()).toISOString());
10097
10150
  if (debug) console.log("[Paywallo EVENTS] install event emitted (first time)");
10098
10151
  if (debug) console.log("[Paywallo EVENTS] lifecycle install emitted");
@@ -10104,7 +10157,7 @@ function useAppAutoEvents(config) {
10104
10157
  }
10105
10158
  try {
10106
10159
  if (debug) console.log("[Paywallo EVENTS] emitting cold_start");
10107
- await emitLifecycle({ type: "cold_start", app_version: appVersion, device_type: deviceType, os_version: osVersion, ...sessionId ? { session_id: sessionId } : {} }, "normal");
10160
+ await emitLifecycle({ type: "cold_start", app_version: appVersion, device_type: deviceType, os_version: osVersion2, ...sessionId ? { session_id: sessionId } : {} }, "normal");
10108
10161
  if (debug) console.log("[Paywallo EVENTS] lifecycle cold_start emitted");
10109
10162
  } catch (err) {
10110
10163
  if (debug) console.log("[Paywallo EVENTS] lifecycle cold_start failed", err);
@@ -10118,7 +10171,7 @@ function useAppAutoEvents(config) {
10118
10171
  // src/hooks/useCampaignPreload.ts
10119
10172
  init_PaywalloClient();
10120
10173
  init_CampaignError();
10121
- import { useCallback as useCallback15, useRef as useRef9, useState as useState14 } from "react";
10174
+ import { useCallback as useCallback16, useRef as useRef10, useState as useState14 } from "react";
10122
10175
 
10123
10176
  // src/utils/loadCampaignProducts.ts
10124
10177
  init_IAPService();
@@ -10188,16 +10241,16 @@ function getCacheEntry(cache, placement) {
10188
10241
  // src/hooks/useCampaignPreload.ts
10189
10242
  function useCampaignPreload() {
10190
10243
  const [preloadedWebView, setPreloadedWebView] = useState14(null);
10191
- const preloadedCampaignsRef = useRef9(/* @__PURE__ */ new Map());
10192
- const isPreloadValid = useCallback15(
10244
+ const preloadedCampaignsRef = useRef10(/* @__PURE__ */ new Map());
10245
+ const isPreloadValid = useCallback16(
10193
10246
  (placement) => isCacheEntryValid(preloadedCampaignsRef.current.get(placement)),
10194
10247
  []
10195
10248
  );
10196
- const consumePreloadedCampaign = useCallback15(
10249
+ const consumePreloadedCampaign = useCallback16(
10197
10250
  (placement) => getCacheEntry(preloadedCampaignsRef.current, placement),
10198
10251
  []
10199
10252
  );
10200
- const preloadCampaign = useCallback15(
10253
+ const preloadCampaign = useCallback16(
10201
10254
  async (placement, context) => {
10202
10255
  try {
10203
10256
  const campaign = await PaywalloClient.getCampaign(placement, context);
@@ -10266,11 +10319,11 @@ function useCampaignPreload() {
10266
10319
  }
10267
10320
 
10268
10321
  // src/hooks/useProductLoader.ts
10269
- import { useCallback as useCallback16, useState as useState15 } from "react";
10322
+ import { useCallback as useCallback17, useState as useState15 } from "react";
10270
10323
  function useProductLoader() {
10271
10324
  const [products, setProducts] = useState15(/* @__PURE__ */ new Map());
10272
10325
  const [isLoadingProducts, setIsLoadingProducts] = useState15(false);
10273
- const refreshProducts = useCallback16(async (productIds) => {
10326
+ const refreshProducts = useCallback17(async (productIds) => {
10274
10327
  setIsLoadingProducts(true);
10275
10328
  try {
10276
10329
  const iapService = getIAPService();
@@ -10288,11 +10341,11 @@ function useProductLoader() {
10288
10341
  }
10289
10342
 
10290
10343
  // src/hooks/usePurchaseOrchestrator.ts
10291
- import { useCallback as useCallback17 } from "react";
10344
+ import { useCallback as useCallback18 } from "react";
10292
10345
  init_paywall();
10293
10346
  function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, invalidateSubscriptionCache) {
10294
10347
  const { trackDismissed, trackProductSelected, trackPurchased } = usePaywallTracking();
10295
- const handlePreloadedPurchase = useCallback17(
10348
+ const handlePreloadedPurchase = useCallback18(
10296
10349
  async (productId) => {
10297
10350
  try {
10298
10351
  if (preloadedWebView) {
@@ -10330,7 +10383,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
10330
10383
  },
10331
10384
  [preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, trackDismissed, trackProductSelected, trackPurchased, invalidateSubscriptionCache]
10332
10385
  );
10333
- const handlePreloadedRestore = useCallback17(async () => {
10386
+ const handlePreloadedRestore = useCallback18(async () => {
10334
10387
  try {
10335
10388
  const transactions = await getIAPService().restore();
10336
10389
  if (transactions.length === 0) return;
@@ -10355,7 +10408,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
10355
10408
  // src/hooks/useSubscriptionSync.ts
10356
10409
  init_PaywalloClient();
10357
10410
  init_SubscriptionCache();
10358
- import { useCallback as useCallback18, useRef as useRef10, useState as useState16 } from "react";
10411
+ import { useCallback as useCallback19, useRef as useRef11, useState as useState16 } from "react";
10359
10412
 
10360
10413
  // src/hooks/subscriptionSyncHelpers.ts
10361
10414
  init_SubscriptionCache();
@@ -10464,17 +10517,17 @@ async function checkPersistedCache(distinctId, onHit) {
10464
10517
  // src/hooks/useSubscriptionSync.ts
10465
10518
  function useSubscriptionSync() {
10466
10519
  const [subscription, setSubscription] = useState16(null);
10467
- const cacheRef = useRef10(null);
10468
- const setCacheAndState = useCallback18((sub) => {
10520
+ const cacheRef = useRef11(null);
10521
+ const setCacheAndState = useCallback19((sub) => {
10469
10522
  cacheRef.current = { data: sub, expiresAt: Date.now() + CACHE_TTL_MS2 };
10470
10523
  setSubscription(sub);
10471
10524
  }, []);
10472
- const invalidateCache = useCallback18(() => {
10525
+ const invalidateCache = useCallback19(() => {
10473
10526
  cacheRef.current = null;
10474
10527
  const distinctId = PaywalloClient.getDistinctId();
10475
10528
  if (distinctId) void subscriptionCache.invalidate(distinctId);
10476
10529
  }, []);
10477
- const fetchAndCache = useCallback18(async () => {
10530
+ const fetchAndCache = useCallback19(async () => {
10478
10531
  const apiClient = PaywalloClient.getApiClient();
10479
10532
  const distinctId = PaywalloClient.getDistinctId();
10480
10533
  if (!apiClient || !distinctId) return null;
@@ -10484,7 +10537,7 @@ function useSubscriptionSync() {
10484
10537
  void subscriptionCache.set(distinctId, toDomainResponse(status));
10485
10538
  return sub;
10486
10539
  }, [setCacheAndState]);
10487
- const hasActiveSubscription = useCallback18(async () => {
10540
+ const hasActiveSubscription = useCallback19(async () => {
10488
10541
  const apiClient = PaywalloClient.getApiClient();
10489
10542
  const distinctId = PaywalloClient.getDistinctId();
10490
10543
  if (!apiClient || !distinctId) return false;
@@ -10504,7 +10557,7 @@ function useSubscriptionSync() {
10504
10557
  return resolveOfflineFromCache(distinctId);
10505
10558
  }
10506
10559
  }, [setCacheAndState]);
10507
- const getSubscription = useCallback18(async () => {
10560
+ const getSubscription = useCallback19(async () => {
10508
10561
  const apiClient = PaywalloClient.getApiClient();
10509
10562
  const distinctId = PaywalloClient.getDistinctId();
10510
10563
  if (!apiClient || !distinctId) return null;
@@ -10536,12 +10589,12 @@ function PaywalloProvider({ children, config }) {
10536
10589
  const { products, isLoadingProducts, refreshProducts } = useProductLoader();
10537
10590
  const { subscription, hasActiveSubscription, getSubscription, invalidateCache } = useSubscriptionSync();
10538
10591
  const { preloadedWebView, setPreloadedWebView, preloadCampaign, consumePreloadedCampaign, isPreloadValid } = useCampaignPreload();
10539
- const clearPreloadedWebView = useCallback19(() => setPreloadedWebView(null), [setPreloadedWebView]);
10540
- const autoPreloadTriggeredRef = useRef11(false);
10541
- const preloadedWebViewRef = useRef11(preloadedWebView);
10542
- const pollTimerRef = useRef11(null);
10543
- const pollCancelledRef = useRef11(false);
10544
- const secureStorageRef = useRef11(new SecureStorage(config.debug));
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));
10545
10598
  useEffect14(() => {
10546
10599
  preloadedWebViewRef.current = preloadedWebView;
10547
10600
  }, [preloadedWebView]);
@@ -10568,20 +10621,20 @@ function PaywalloProvider({ children, config }) {
10568
10621
  },
10569
10622
  debug: config.debug
10570
10623
  });
10571
- const triggerRepreload = useCallback19((placement) => {
10624
+ const triggerRepreload = useCallback20((placement) => {
10572
10625
  void PaywalloClient.preloadCampaign(placement).catch(() => {
10573
10626
  });
10574
10627
  }, []);
10575
10628
  const { showingPreloadedWebView, presentPreloadedWebView, resolvePreloadedPaywall, handlePreloadedWebViewReady: baseHandlePreloadedWebViewReady, handlePreloadedClose: baseHandlePreloadedClose, setShowingPreloadedWebView, presentedAtRef } = usePaywallPresenter(preloadedWebView, setPreloadedWebView);
10576
10629
  const SCREEN_HEIGHT = useMemo3(() => Dimensions5.get("window").height, []);
10577
- const slideAnim = useRef11(new Animated4.Value(SCREEN_HEIGHT)).current;
10578
- const handlePreloadedWebViewReady = useCallback19(() => {
10630
+ const slideAnim = useRef12(new Animated4.Value(SCREEN_HEIGHT)).current;
10631
+ const handlePreloadedWebViewReady = useCallback20(() => {
10579
10632
  if (PaywalloClient.getConfig()?.debug) {
10580
10633
  console.log(`[Paywallo WEBVIEW] loaded \u2014 placement: ${preloadedWebView?.placement}`);
10581
10634
  }
10582
10635
  baseHandlePreloadedWebViewReady();
10583
10636
  }, [preloadedWebView?.placement, baseHandlePreloadedWebViewReady]);
10584
- const handlePreloadedClose = useCallback19(() => {
10637
+ const handlePreloadedClose = useCallback20(() => {
10585
10638
  const placement = preloadedWebView?.placement;
10586
10639
  Animated4.timing(slideAnim, { toValue: SCREEN_HEIGHT, duration: 200, easing: Easing2.in(Easing2.cubic), useNativeDriver: true }).start(() => {
10587
10640
  baseHandlePreloadedClose();
@@ -10597,7 +10650,7 @@ function PaywalloProvider({ children, config }) {
10597
10650
  invalidateCache
10598
10651
  );
10599
10652
  const [isPreloadPurchasing, setIsPreloadPurchasing] = useState17(false);
10600
- const handlePreloadedPurchase = useCallback19(async (productId) => {
10653
+ const handlePreloadedPurchase = useCallback20(async (productId) => {
10601
10654
  setIsPreloadPurchasing(true);
10602
10655
  try {
10603
10656
  await rawPreloadedPurchase(productId);
@@ -10605,15 +10658,25 @@ function PaywalloProvider({ children, config }) {
10605
10658
  setIsPreloadPurchasing(false);
10606
10659
  }
10607
10660
  }, [rawPreloadedPurchase]);
10608
- const configRef = useRef11(config);
10661
+ const configRef = useRef12(config);
10609
10662
  useEffect14(() => {
10610
10663
  let mounted = true;
10611
- initLocalization({ detectDevice: true });
10612
- PaywalloClient.init(configRef.current).then(async () => {
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
+ }
10613
10678
  if (!mounted) return;
10614
10679
  setDistinctId(PaywalloClient.getDistinctId());
10615
- startSuperwallAutoBridge(configRef.current.debug);
10616
- void pushAttributionToSuperwall(configRef.current.debug);
10617
10680
  setIsInitialized(true);
10618
10681
  if (!autoPreloadTriggeredRef.current) {
10619
10682
  autoPreloadTriggeredRef.current = true;
@@ -10626,19 +10689,17 @@ function PaywalloProvider({ children, config }) {
10626
10689
  });
10627
10690
  }
10628
10691
  }
10629
- }).catch((err) => {
10630
- if (!mounted) return;
10631
- setInitError(err instanceof Error ? err : new ClientError(CLIENT_ERROR_CODES.NOT_INITIALIZED, String(err)));
10632
- });
10692
+ };
10693
+ void bootstrap();
10633
10694
  return () => {
10634
10695
  mounted = false;
10635
10696
  };
10636
10697
  }, [preloadCampaign]);
10637
- const getPaywallConfig = useCallback19(async (p) => {
10698
+ const getPaywallConfig = useCallback20(async (p) => {
10638
10699
  const api = PaywalloClient.getApiClient();
10639
10700
  return api ? api.getPaywall(p) : null;
10640
10701
  }, []);
10641
- const presentPaywall = useCallback19((placement) => {
10702
+ const presentPaywall = useCallback20((placement) => {
10642
10703
  const preloaded = paywallPreloadService.getPreloaded(placement);
10643
10704
  return new Promise((resolve) => setActivePaywall({
10644
10705
  placement,
@@ -10656,7 +10717,7 @@ function PaywalloProvider({ children, config }) {
10656
10717
  }
10657
10718
  }));
10658
10719
  }, []);
10659
- const trackCampaignImpression = useCallback19((placement, variantKey, campaignId) => {
10720
+ const trackCampaignImpression = useCallback20((placement, variantKey, campaignId) => {
10660
10721
  const api = PaywalloClient.getApiClient();
10661
10722
  if (!api) return;
10662
10723
  const sessionId = PaywalloClient.getSessionId();
@@ -10670,7 +10731,7 @@ function PaywalloProvider({ children, config }) {
10670
10731
  }).catch(() => {
10671
10732
  });
10672
10733
  }, []);
10673
- const presentCampaign = useCallback19(
10734
+ const presentCampaign = useCallback20(
10674
10735
  async (placement, context) => {
10675
10736
  try {
10676
10737
  if (preloadedWebView?.placement === placement && preloadedWebView.loaded && isPreloadValid(placement)) {
@@ -10744,7 +10805,7 @@ function PaywalloProvider({ children, config }) {
10744
10805
  },
10745
10806
  [preloadedWebView, isPreloadValid, consumePreloadedCampaign, presentPreloadedWebView, trackCampaignImpression]
10746
10807
  );
10747
- const restorePurchases = useCallback19(async () => {
10808
+ const restorePurchases = useCallback20(async () => {
10748
10809
  invalidateCache();
10749
10810
  try {
10750
10811
  const txs = await getIAPService().restore();
@@ -10753,13 +10814,13 @@ function PaywalloProvider({ children, config }) {
10753
10814
  return { success: false, restoredProducts: [], error: error instanceof Error ? error : new PurchaseError(PURCHASE_ERROR_CODES.RESTORE_FAILED, String(error)) };
10754
10815
  }
10755
10816
  }, [invalidateCache]);
10756
- const handlePaywallResult = useCallback19((result) => {
10817
+ const handlePaywallResult = useCallback20((result) => {
10757
10818
  if (activePaywall) {
10758
10819
  activePaywall.resolver(result);
10759
10820
  setActivePaywall(null);
10760
10821
  }
10761
10822
  }, [activePaywall]);
10762
- const bridgePaywallPresenter = useCallback19((placement) => {
10823
+ const bridgePaywallPresenter = useCallback20((placement) => {
10763
10824
  if (PaywalloClient.getConfig()?.debug) {
10764
10825
  console.log(`[Paywallo PRESENT] called \u2014 preloaded: ${preloadedWebViewRef.current?.loaded === true} placement: ${placement}`);
10765
10826
  }
@@ -10815,7 +10876,7 @@ function PaywalloProvider({ children, config }) {
10815
10876
  };
10816
10877
  }, [isInitialized, bridgePaywallPresenter, getSubscription, hasActiveSubscription, restorePurchases]);
10817
10878
  const isReady = isInitialized && !isLoadingProducts;
10818
- const noOp = useCallback19(() => {
10879
+ const noOp = useCallback20(() => {
10819
10880
  }, []);
10820
10881
  const contextValue = useMemo3(() => ({
10821
10882
  config: PaywalloClient.getConfig(),
@@ -11070,6 +11131,7 @@ export {
11070
11131
  createPurchaseError,
11071
11132
  index_default as default,
11072
11133
  detectDeviceLanguage,
11134
+ getActiveRouteName,
11073
11135
  getCurrentLanguage,
11074
11136
  getDefaultLanguage,
11075
11137
  getIAPService,
@@ -11098,6 +11160,7 @@ export {
11098
11160
  useOnboarding,
11099
11161
  usePaywallContext,
11100
11162
  usePaywallo,
11163
+ usePaywalloScreenTracking,
11101
11164
  usePlanPurchase,
11102
11165
  usePlans,
11103
11166
  useProducts,