@virex-tech/paywallo-sdk 2.5.0 → 2.5.2

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
@@ -271,8 +271,8 @@ var init_ClientError = __esm({
271
271
 
272
272
  // src/utils/NativeDeviceInfo.ts
273
273
  import { Platform } from "react-native";
274
- import * as Device from "expo-device";
275
274
  import * as Application from "expo-application";
275
+ import * as Device from "expo-device";
276
276
  import * as Localization from "expo-localization";
277
277
  function assertExpoModules() {
278
278
  if (!Device || Device.osName === void 0) {
@@ -952,7 +952,9 @@ var init_AdvertisingIdManager = __esm({
952
952
  return this.cached;
953
953
  }
954
954
  async collect(requestATT = false) {
955
- if (this.cached) return this.cached;
955
+ if (this.cached && !(requestATT && this.cached.attStatus === "undetermined")) {
956
+ return this.cached;
957
+ }
956
958
  const fallback = { idfv: null, idfa: null, gaid: null, attStatus: "unavailable" };
957
959
  try {
958
960
  const tracking = await import("expo-tracking-transparency");
@@ -1375,6 +1377,7 @@ var init_AttributionTracker = __esm({
1375
1377
  constructor(debug = false) {
1376
1378
  this.cache = null;
1377
1379
  this.captureInProgress = false;
1380
+ this.listeners = /* @__PURE__ */ new Set();
1378
1381
  this.storage = new SecureStorage(debug);
1379
1382
  }
1380
1383
  /**
@@ -1414,10 +1417,31 @@ var init_AttributionTracker = __esm({
1414
1417
  };
1415
1418
  await this.storage.set(ATTRIBUTION_STORAGE_KEY, JSON.stringify(capture));
1416
1419
  this.cache = capture;
1420
+ this.notifyListeners(capture);
1417
1421
  } finally {
1418
1422
  this.captureInProgress = false;
1419
1423
  }
1420
1424
  }
1425
+ /**
1426
+ * Subscribes to the first real attribution write (first-write-wins — no-op
1427
+ * captures never fire). Useful for late enrichment consumers (e.g. re-push
1428
+ * of Superwall attributes when a warm deep link / deferred match lands after
1429
+ * init). Returns an unsubscribe function.
1430
+ */
1431
+ onCapture(listener) {
1432
+ this.listeners.add(listener);
1433
+ return () => {
1434
+ this.listeners.delete(listener);
1435
+ };
1436
+ }
1437
+ notifyListeners(capture) {
1438
+ for (const listener of this.listeners) {
1439
+ try {
1440
+ listener(capture);
1441
+ } catch {
1442
+ }
1443
+ }
1444
+ }
1421
1445
  /**
1422
1446
  * Returns the persisted attribution capture, or `null` if none exists.
1423
1447
  * Synchronous — reads from in-memory cache (populated via `loadFromStorage()`).
@@ -1511,6 +1535,68 @@ var init_InstallIdempotency = __esm({
1511
1535
  }
1512
1536
  });
1513
1537
 
1538
+ // src/domains/identity/deferredAppLink.ts
1539
+ function parseQuery(s) {
1540
+ const result = {};
1541
+ for (const pair of s.split("&")) {
1542
+ const eqIndex = pair.indexOf("=");
1543
+ if (eqIndex === -1) continue;
1544
+ try {
1545
+ const key = decodeURIComponent(pair.slice(0, eqIndex).replace(/\+/g, " "));
1546
+ const value = decodeURIComponent(pair.slice(eqIndex + 1).replace(/\+/g, " "));
1547
+ if (key) result[key] = value;
1548
+ } catch {
1549
+ }
1550
+ }
1551
+ return result;
1552
+ }
1553
+ function parseDeferredAppLink(url) {
1554
+ if (!url || url.trim() === "") return null;
1555
+ const raw = url.slice(0, 2048);
1556
+ const cleanRaw = raw.split("#")[0];
1557
+ const qIndex = cleanRaw.indexOf("?");
1558
+ if (qIndex === -1) return null;
1559
+ const topParams = parseQuery(cleanRaw.slice(qIndex + 1));
1560
+ let fbclid = nz(topParams["fbclid"]);
1561
+ let utmSource = nz(topParams["utm_source"]);
1562
+ let utmMedium = nz(topParams["utm_medium"]);
1563
+ let utmCampaign = nz(topParams["utm_campaign"]);
1564
+ let ttclid = nz(topParams["ttclid"]);
1565
+ let trackingId = nz(topParams["tracking_id"]);
1566
+ let targetUrl = null;
1567
+ if (topParams["target_url"]) {
1568
+ try {
1569
+ targetUrl = decodeURIComponent(topParams["target_url"]);
1570
+ } catch {
1571
+ targetUrl = topParams["target_url"];
1572
+ }
1573
+ }
1574
+ if (!fbclid && targetUrl) {
1575
+ const cleanTargetUrl = targetUrl.split("#")[0];
1576
+ const innerQ = cleanTargetUrl.indexOf("?");
1577
+ if (innerQ !== -1) {
1578
+ const inner = parseQuery(cleanTargetUrl.slice(innerQ + 1));
1579
+ fbclid = fbclid ?? nz(inner["fbclid"]);
1580
+ utmSource = utmSource ?? nz(inner["utm_source"]);
1581
+ utmMedium = utmMedium ?? nz(inner["utm_medium"]);
1582
+ utmCampaign = utmCampaign ?? nz(inner["utm_campaign"]);
1583
+ ttclid = ttclid ?? nz(inner["ttclid"]);
1584
+ trackingId = trackingId ?? nz(inner["tracking_id"]);
1585
+ }
1586
+ }
1587
+ if (!fbclid && !ttclid && !trackingId && !utmSource && !utmMedium && !utmCampaign) {
1588
+ return null;
1589
+ }
1590
+ return { fbclid, utmSource, utmMedium, utmCampaign, ttclid, trackingId, targetUrl, raw };
1591
+ }
1592
+ var nz;
1593
+ var init_deferredAppLink = __esm({
1594
+ "src/domains/identity/deferredAppLink.ts"() {
1595
+ "use strict";
1596
+ nz = (v) => typeof v === "string" && v.trim().length > 0 ? v : null;
1597
+ }
1598
+ });
1599
+
1514
1600
  // src/domains/identity/MetaBridge.ts
1515
1601
  import { NativeModules as NativeModules2 } from "react-native";
1516
1602
  var TIMEOUT_MS, NativeBridge, MetaBridge, metaBridge;
@@ -1609,68 +1695,6 @@ var init_MetaBridge = __esm({
1609
1695
  }
1610
1696
  });
1611
1697
 
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
-
1674
1698
  // src/domains/identity/InstallReferrerManager.ts
1675
1699
  import { Platform as Platform3 } from "react-native";
1676
1700
  var REFERRER_TIMEOUT_MS, InstallReferrerManager, installReferrerManager;
@@ -1678,8 +1702,8 @@ var init_InstallReferrerManager = __esm({
1678
1702
  "src/domains/identity/InstallReferrerManager.ts"() {
1679
1703
  "use strict";
1680
1704
  init_NativeDeviceInfo();
1681
- init_MetaBridge();
1682
1705
  init_deferredAppLink();
1706
+ init_MetaBridge();
1683
1707
  REFERRER_TIMEOUT_MS = 5e3;
1684
1708
  InstallReferrerManager = class {
1685
1709
  async getReferrer() {
@@ -1780,7 +1804,17 @@ var init_InstallReferrerManager = __esm({
1780
1804
 
1781
1805
  // src/domains/identity/InstallTracker.ts
1782
1806
  import { Dimensions, PixelRatio, Platform as Platform4 } from "react-native";
1783
- var InstallTracker;
1807
+ function extractDeferredAttribution(raw) {
1808
+ if (raw === null || typeof raw !== "object") return null;
1809
+ const source = raw;
1810
+ const out = {};
1811
+ for (const field of DEFERRED_ATTRIBUTION_FIELDS) {
1812
+ const value = source[field];
1813
+ if (typeof value === "string" && value !== "") out[field] = value;
1814
+ }
1815
+ return Object.keys(out).length > 0 ? out : null;
1816
+ }
1817
+ var InstallTracker, DEFERRED_ATTRIBUTION_FIELDS;
1784
1818
  var init_InstallTracker = __esm({
1785
1819
  "src/domains/identity/InstallTracker.ts"() {
1786
1820
  "use strict";
@@ -1945,7 +1979,7 @@ var init_InstallTracker = __esm({
1945
1979
  const baseUrl = apiClient.getBaseUrl();
1946
1980
  const appKey = apiClient.getAppKey();
1947
1981
  const referrer = await installReferrerManager.getReferrer();
1948
- await fetch(`${baseUrl}/sdk/attribution/deferred-match/${appKey}`, {
1982
+ const response = await fetch(`${baseUrl}/sdk/attribution/deferred-match/${appKey}`, {
1949
1983
  method: "POST",
1950
1984
  headers: { "Content-Type": "application/json", "X-App-Key": appKey },
1951
1985
  body: JSON.stringify({
@@ -1967,12 +2001,42 @@ var init_InstallTracker = __esm({
1967
2001
  });
1968
2002
  await storage.set(INSTALL_KEYS.DEFERRED_MATCH_DONE, "1").catch(() => {
1969
2003
  });
2004
+ await this.applyDeferredMatchResponse(response);
1970
2005
  } catch {
1971
2006
  } finally {
1972
2007
  clearTimeout(timer);
1973
2008
  }
1974
2009
  }
2010
+ async applyDeferredMatchResponse(response) {
2011
+ try {
2012
+ const body = await response.json();
2013
+ if (body === null || typeof body !== "object") return;
2014
+ const outer = body;
2015
+ const source = typeof outer.data === "object" && outer.data !== null ? outer.data : body;
2016
+ if (source.matched !== true) return;
2017
+ const attribution = extractDeferredAttribution(source.attribution);
2018
+ if (!attribution) return;
2019
+ await attributionTracker.capture({
2020
+ ...attribution,
2021
+ installReferrerSource: "deferred_match"
2022
+ });
2023
+ } catch {
2024
+ }
2025
+ }
1975
2026
  };
2027
+ DEFERRED_ATTRIBUTION_FIELDS = [
2028
+ "fbclid",
2029
+ "gclid",
2030
+ "ttclid",
2031
+ "utmSource",
2032
+ "utmMedium",
2033
+ "utmCampaign",
2034
+ "utmContent",
2035
+ "utmTerm",
2036
+ "tiktokCampaignId",
2037
+ "tiktokAdgroupId",
2038
+ "tiktokAdId"
2039
+ ];
1976
2040
  }
1977
2041
  });
1978
2042
 
@@ -2215,7 +2279,7 @@ var DEFAULT_API_URL, DEFAULT_WEB_URL;
2215
2279
  var init_constants = __esm({
2216
2280
  "src/core/constants.ts"() {
2217
2281
  "use strict";
2218
- DEFAULT_API_URL = "https://incommensurably-unprotrudent-brooklynn.ngrok-free.dev";
2282
+ DEFAULT_API_URL = "https://panel.lucasqueiroga.shop";
2219
2283
  DEFAULT_WEB_URL = "https://paywallo.com.br";
2220
2284
  }
2221
2285
  });
@@ -2547,8 +2611,8 @@ var init_NotificationHandlerSetup = __esm({
2547
2611
  // src/core/version.ts
2548
2612
  function resolveVersion() {
2549
2613
  try {
2550
- if ("2.5.0") {
2551
- return "2.5.0";
2614
+ if ("2.5.2") {
2615
+ return "2.5.2";
2552
2616
  }
2553
2617
  } catch {
2554
2618
  }
@@ -3153,17 +3217,154 @@ var init_NotificationsManager = __esm({
3153
3217
  this.currentToken = null;
3154
3218
  }
3155
3219
  };
3156
- notificationsManager = new NotificationsManager();
3220
+ notificationsManager = new NotificationsManager();
3221
+ }
3222
+ });
3223
+
3224
+ // src/domains/notifications/index.ts
3225
+ var init_notifications = __esm({
3226
+ "src/domains/notifications/index.ts"() {
3227
+ "use strict";
3228
+ init_NativePushBridge();
3229
+ init_NotificationsError();
3230
+ init_NotificationsManager();
3231
+ }
3232
+ });
3233
+
3234
+ // src/domains/onboarding/OnboardingError.ts
3235
+ var OnboardingError, ONBOARDING_ERROR_CODES;
3236
+ var init_OnboardingError = __esm({
3237
+ "src/domains/onboarding/OnboardingError.ts"() {
3238
+ "use strict";
3239
+ init_PaywalloError();
3240
+ OnboardingError = class extends PaywalloError {
3241
+ constructor(code, message) {
3242
+ super("onboarding", code, message);
3243
+ this.name = "OnboardingError";
3244
+ }
3245
+ };
3246
+ ONBOARDING_ERROR_CODES = {
3247
+ NOT_INITIALIZED: "ONBOARDING_NOT_INITIALIZED",
3248
+ INVALID_STEP_NAME: "ONBOARDING_INVALID_STEP_NAME"
3249
+ };
3250
+ }
3251
+ });
3252
+
3253
+ // src/domains/onboarding/OnboardingManager.ts
3254
+ var OnboardingManager, onboardingManager;
3255
+ var init_OnboardingManager = __esm({
3256
+ "src/domains/onboarding/OnboardingManager.ts"() {
3257
+ "use strict";
3258
+ init_OnboardingError();
3259
+ OnboardingManager = class {
3260
+ constructor() {
3261
+ this.pipeline = null;
3262
+ this.debug = false;
3263
+ this.lastStep = null;
3264
+ this.finished = false;
3265
+ }
3266
+ /**
3267
+ * Injects runtime dependencies. Called by PaywalloClient during init.
3268
+ */
3269
+ injectDeps(config) {
3270
+ this.pipeline = config.pipeline;
3271
+ this.debug = config.debug ?? false;
3272
+ }
3273
+ /**
3274
+ * Tracks an onboarding step view.
3275
+ * Saves the step name internally for implicit drop detection on the backend.
3276
+ *
3277
+ * @param stepName - Unique name for this onboarding step.
3278
+ * @param order - Optional explicit display order (accepts decimals, e.g. 2.1 for A/B variants).
3279
+ */
3280
+ async step(stepName, order) {
3281
+ this.assertConfig();
3282
+ this.validateStepName(stepName, "step");
3283
+ this.lastStep = stepName;
3284
+ const stepPayload = { step_name: stepName };
3285
+ if (order !== void 0 && Number.isFinite(order) && order >= 0) {
3286
+ stepPayload.order = order;
3287
+ }
3288
+ const payload = {
3289
+ family: "onboarding",
3290
+ type: "step",
3291
+ ...stepPayload
3292
+ };
3293
+ return this.emit("onboarding", payload, "step");
3294
+ }
3295
+ /**
3296
+ * Tracks successful completion of the onboarding flow.
3297
+ * Marks the flow as finished.
3298
+ */
3299
+ async complete() {
3300
+ this.assertConfig();
3301
+ this.finished = true;
3302
+ return this.emit("onboarding", { family: "onboarding", type: "complete" }, "complete");
3303
+ }
3304
+ /**
3305
+ * Tracks an explicit drop (abandonment) at the given step.
3306
+ * Marks the flow as finished.
3307
+ *
3308
+ * @param stepName - The step name where the user dropped off.
3309
+ */
3310
+ async drop(stepName) {
3311
+ this.assertConfig();
3312
+ this.validateStepName(stepName, "drop");
3313
+ this.finished = true;
3314
+ const payload = {
3315
+ family: "onboarding",
3316
+ type: "drop",
3317
+ ...{ step_name: stepName }
3318
+ };
3319
+ return this.emit("onboarding", payload, "drop");
3320
+ }
3321
+ /** Returns the last step name seen, or null if no step was tracked yet. */
3322
+ getLastStep() {
3323
+ return this.lastStep;
3324
+ }
3325
+ /** Returns whether the flow has been marked as finished (complete or drop). */
3326
+ isFinished() {
3327
+ return this.finished;
3328
+ }
3329
+ async emit(eventName, properties, logLabel) {
3330
+ const pipeline = this.pipeline;
3331
+ const distinctId = pipeline.getDistinctId();
3332
+ if (!distinctId) {
3333
+ if (this.debug) console.log(`[Paywallo:Onboarding] skipped (no distinctId): ${logLabel}`);
3334
+ return;
3335
+ }
3336
+ await pipeline.trackEvent(eventName, distinctId, properties);
3337
+ if (this.debug) {
3338
+ console.log(`[Paywallo:Onboarding] event emitted: ${eventName}.${logLabel}`, properties);
3339
+ }
3340
+ }
3341
+ assertConfig() {
3342
+ if (!this.pipeline) {
3343
+ throw new OnboardingError(
3344
+ ONBOARDING_ERROR_CODES.NOT_INITIALIZED,
3345
+ "OnboardingManager not initialized. Call Paywallo.init() first."
3346
+ );
3347
+ }
3348
+ }
3349
+ validateStepName(stepName, method) {
3350
+ if (typeof stepName !== "string" || stepName.trim().length === 0) {
3351
+ throw new OnboardingError(
3352
+ ONBOARDING_ERROR_CODES.INVALID_STEP_NAME,
3353
+ `OnboardingManager.${method}(): stepName must be a non-empty string.`
3354
+ );
3355
+ }
3356
+ }
3357
+ };
3358
+ onboardingManager = new OnboardingManager();
3157
3359
  }
3158
3360
  });
3159
3361
 
3160
- // src/domains/notifications/index.ts
3161
- var init_notifications = __esm({
3162
- "src/domains/notifications/index.ts"() {
3362
+ // src/domains/onboarding/index.ts
3363
+ var init_onboarding = __esm({
3364
+ "src/domains/onboarding/index.ts"() {
3163
3365
  "use strict";
3164
- init_NativePushBridge();
3165
- init_NotificationsError();
3166
- init_NotificationsManager();
3366
+ init_OnboardingError();
3367
+ init_OnboardingManager();
3167
3368
  }
3168
3369
  });
3169
3370
 
@@ -6413,143 +6614,6 @@ var init_session = __esm({
6413
6614
  }
6414
6615
  });
6415
6616
 
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();
6550
- }
6551
- });
6552
-
6553
6617
  // src/domains/analytics/AnalyticsError.ts
6554
6618
  var AnalyticsError, ANALYTICS_ERROR_CODES;
6555
6619
  var init_AnalyticsError = __esm({
@@ -6622,7 +6686,21 @@ var init_schemas = __esm({
6622
6686
  // `closed`-specific
6623
6687
  closed_at: z.string().optional(),
6624
6688
  duration_s: z.number().nonnegative().optional(),
6625
- close_reason: z.enum(["dismiss", "cta", "purchase", "error", "timeout"]).optional(),
6689
+ // Two emitter dialects coexist: the canonical emitters
6690
+ // (`emitPaywallClosed`/heartbeat) send "dismiss"/"cta"/"purchase"/"error"/
6691
+ // "timeout"; the SuperwallAutoBridge forwards the server-enum values
6692
+ // "purchased"/"dismissed"/"restored" straight through. The server accepts
6693
+ // both — the schema must too, or every bridge close fails debug validation.
6694
+ close_reason: z.enum([
6695
+ "dismiss",
6696
+ "cta",
6697
+ "purchase",
6698
+ "error",
6699
+ "timeout",
6700
+ "purchased",
6701
+ "dismissed",
6702
+ "restored"
6703
+ ]).optional(),
6626
6704
  scroll_depth: z.number().min(0).max(1).optional(),
6627
6705
  // `purchased`-specific (mínimo — transaction canônica leva detalhes)
6628
6706
  product_id: z.string().optional()
@@ -6630,21 +6708,33 @@ var init_schemas = __esm({
6630
6708
  transactionEventSchema = z.object({
6631
6709
  type: z.enum([
6632
6710
  "completed",
6711
+ "trial_started",
6633
6712
  "failed",
6634
6713
  "refunded",
6635
6714
  "renewed",
6636
6715
  "canceled",
6637
6716
  "expired"
6638
6717
  ]),
6639
- tx_id: z.string().min(1),
6718
+ // Server contract key is `transaction_id` (what IngestTranslator reads and
6719
+ // the SuperwallAutoBridge emits); `tx_id` is the legacy key still emitted
6720
+ // by IAPTransactionEmitter — the server accepts both. At least one is
6721
+ // required (enforced by the refine below).
6722
+ transaction_id: z.string().min(1).optional(),
6723
+ tx_id: z.string().min(1).optional(),
6640
6724
  amount: z.number().optional(),
6725
+ // Full (post-trial) price carried on trial_started for analytics, since
6726
+ // `amount` is 0 for a free trial.
6727
+ full_price: z.number().nonnegative().optional(),
6641
6728
  currency: z.string().length(3).optional(),
6642
6729
  product_id: z.string().optional(),
6643
6730
  subscription_id: z.string().optional(),
6644
6731
  paywall_id: z.string().optional(),
6645
6732
  variant_id: z.string().optional(),
6646
6733
  reason: z.string().optional()
6647
- }).strict();
6734
+ }).strict().refine((v) => v.transaction_id !== void 0 || v.tx_id !== void 0, {
6735
+ message: "transaction requires transaction_id (or legacy tx_id)",
6736
+ path: ["transaction_id"]
6737
+ });
6648
6738
  onboardingEventSchema = z.object({
6649
6739
  type: z.enum(["step", "complete", "drop"]),
6650
6740
  step_index: z.number().int().nonnegative().optional(),
@@ -7474,8 +7564,13 @@ var init_ApiClientFlags = __esm({
7474
7564
  });
7475
7565
 
7476
7566
  // src/core/ApiClientQueue.ts
7567
+ function isServerError(res) {
7568
+ if (typeof res !== "object" || res === null) return false;
7569
+ const r = res;
7570
+ return r.ok === false && typeof r.status === "number" && r.status >= 500;
7571
+ }
7477
7572
  async function postWithQueue(deps, url, payload, label, priority) {
7478
- if (deps.isOfflineQueueEnabled() && !networkMonitor.isOnline()) {
7573
+ const enqueue = async () => {
7479
7574
  await offlineQueue.enqueue(
7480
7575
  "POST",
7481
7576
  url,
@@ -7484,22 +7579,22 @@ async function postWithQueue(deps, url, payload, label, priority) {
7484
7579
  void 0,
7485
7580
  priority
7486
7581
  );
7582
+ };
7583
+ if (deps.isOfflineQueueEnabled() && !networkMonitor.isOnline()) {
7584
+ await enqueue();
7487
7585
  if (deps.isDebug()) console.log("[Paywallo:ApiClient] Queued for offline:", label, priority ?? "normal");
7488
7586
  return;
7489
7587
  }
7490
7588
  try {
7491
- await deps.post(url, payload);
7589
+ const res = await deps.post(url, payload);
7590
+ if (isServerError(res) && deps.isOfflineQueueEnabled()) {
7591
+ if (deps.isDebug()) console.log("[Paywallo:ApiClient] Server error, queued for retry:", label, priority ?? "normal");
7592
+ await enqueue();
7593
+ }
7492
7594
  } catch (error) {
7493
7595
  if (deps.isDebug()) console.log("[Paywallo:ApiClient] Request failed:", label, error);
7494
7596
  if (deps.isOfflineQueueEnabled()) {
7495
- await offlineQueue.enqueue(
7496
- "POST",
7497
- url,
7498
- payload,
7499
- { "X-App-Key": deps.getAppKey() },
7500
- void 0,
7501
- priority
7502
- );
7597
+ await enqueue();
7503
7598
  return;
7504
7599
  }
7505
7600
  throw error;
@@ -8904,11 +8999,11 @@ var init_PaywalloClient = __esm({
8904
8999
  init_campaign();
8905
9000
  init_identity();
8906
9001
  init_notifications();
9002
+ init_onboarding();
8907
9003
  init_paywall();
8908
9004
  init_plan();
8909
9005
  init_session();
8910
9006
  init_ClientError();
8911
- init_onboarding();
8912
9007
  init_network();
8913
9008
  init_PaywalloAnalytics();
8914
9009
  init_PaywalloFlags();
@@ -9536,8 +9631,41 @@ function usePlanPurchase() {
9536
9631
  };
9537
9632
  }
9538
9633
 
9634
+ // src/hooks/usePaywalloScreenTracking.ts
9635
+ init_PaywalloClient();
9636
+ import { useCallback as useCallback11, useRef as useRef8 } from "react";
9637
+ function isNavState(value) {
9638
+ return typeof value === "object" && value !== null && "routes" in value && "index" in value && Array.isArray(value.routes) && typeof value.index === "number";
9639
+ }
9640
+ function getActiveRouteName(state) {
9641
+ try {
9642
+ if (!isNavState(state)) return null;
9643
+ const route = state.routes[state.index];
9644
+ if (!route) return null;
9645
+ if (isNavState(route.state)) {
9646
+ return getActiveRouteName(route.state);
9647
+ }
9648
+ return typeof route.name === "string" ? route.name : null;
9649
+ } catch {
9650
+ return null;
9651
+ }
9652
+ }
9653
+ function usePaywalloScreenTracking() {
9654
+ const lastScreenRef = useRef8(null);
9655
+ return useCallback11((state) => {
9656
+ if (!PaywalloClient.isReady()) return;
9657
+ const screenName = getActiveRouteName(state);
9658
+ if (!screenName || screenName === lastScreenRef.current) return;
9659
+ lastScreenRef.current = screenName;
9660
+ void PaywalloClient.track("screen_view", {
9661
+ properties: { screen_name: screenName }
9662
+ }).catch(() => {
9663
+ });
9664
+ }, []);
9665
+ }
9666
+
9539
9667
  // src/hooks/usePlans.ts
9540
- import { useCallback as useCallback11, useEffect as useEffect10, useState as useState10 } from "react";
9668
+ import { useCallback as useCallback12, useEffect as useEffect10, useState as useState10 } from "react";
9541
9669
  async function getPlanService() {
9542
9670
  try {
9543
9671
  const mod = await Promise.resolve().then(() => (init_PlanService(), PlanService_exports));
@@ -9551,7 +9679,7 @@ function usePlans() {
9551
9679
  const [currentPlan, setCurrentPlan] = useState10(null);
9552
9680
  const [isLoading, setIsLoading] = useState10(true);
9553
9681
  const [error, setError] = useState10(null);
9554
- const fetchPlans = useCallback11(async (forceRefresh = false) => {
9682
+ const fetchPlans = useCallback12(async (forceRefresh = false) => {
9555
9683
  setIsLoading(true);
9556
9684
  setError(null);
9557
9685
  try {
@@ -9572,7 +9700,7 @@ function usePlans() {
9572
9700
  setIsLoading(false);
9573
9701
  }
9574
9702
  }, []);
9575
- const refresh = useCallback11(() => {
9703
+ const refresh = useCallback12(() => {
9576
9704
  return fetchPlans(true);
9577
9705
  }, [fetchPlans]);
9578
9706
  useEffect10(() => {
@@ -9582,7 +9710,7 @@ function usePlans() {
9582
9710
  }
9583
9711
 
9584
9712
  // src/hooks/useProducts.ts
9585
- import { useCallback as useCallback12, useEffect as useEffect11, useState as useState11 } from "react";
9713
+ import { useCallback as useCallback13, useEffect as useEffect11, useState as useState11 } from "react";
9586
9714
 
9587
9715
  // src/utils/productMappers.ts
9588
9716
  function mapToIAPProduct(product) {
@@ -9625,7 +9753,7 @@ function useProducts(initialProductIds) {
9625
9753
  const [formattedProducts, setFormattedProducts] = useState11([]);
9626
9754
  const [isLoading, setIsLoading] = useState11(false);
9627
9755
  const [error, setError] = useState11(null);
9628
- const loadProducts = useCallback12(async (productIds) => {
9756
+ const loadProducts = useCallback13(async (productIds) => {
9629
9757
  setIsLoading(true);
9630
9758
  setError(null);
9631
9759
  try {
@@ -9644,19 +9772,19 @@ function useProducts(initialProductIds) {
9644
9772
  void loadProducts(initialProductIds);
9645
9773
  }
9646
9774
  }, []);
9647
- const getProduct = useCallback12(
9775
+ const getProduct = useCallback13(
9648
9776
  (productId) => {
9649
9777
  return products.find((p) => p.productId === productId);
9650
9778
  },
9651
9779
  [products]
9652
9780
  );
9653
- const getFormattedProduct = useCallback12(
9781
+ const getFormattedProduct = useCallback13(
9654
9782
  (productId) => {
9655
9783
  return formattedProducts.find((p) => p.productId === productId);
9656
9784
  },
9657
9785
  [formattedProducts]
9658
9786
  );
9659
- const getPriceInfo = useCallback12(
9787
+ const getPriceInfo = useCallback13(
9660
9788
  (productId) => {
9661
9789
  const p = formattedProducts.find((fp) => fp.productId === productId);
9662
9790
  if (!p) return void 0;
@@ -9685,14 +9813,14 @@ function useProducts(initialProductIds) {
9685
9813
  }
9686
9814
 
9687
9815
  // src/hooks/usePurchase.ts
9688
- import { useCallback as useCallback13, useState as useState12 } from "react";
9816
+ import { useCallback as useCallback14, useState as useState12 } from "react";
9689
9817
  function toIAPPurchase(p) {
9690
9818
  return { productId: p.productId, transactionId: p.transactionId, transactionDate: p.transactionDate, transactionReceipt: p.receipt };
9691
9819
  }
9692
9820
  function usePurchase() {
9693
9821
  const [state, setState] = useState12("idle");
9694
9822
  const [error, setError] = useState12(null);
9695
- const purchase = useCallback13(async (productId) => {
9823
+ const purchase = useCallback14(async (productId) => {
9696
9824
  setState("purchasing");
9697
9825
  setError(null);
9698
9826
  try {
@@ -9720,7 +9848,7 @@ function usePurchase() {
9720
9848
  return { status: "failed", error: purchaseError };
9721
9849
  }
9722
9850
  }, []);
9723
- const restore = useCallback13(async () => {
9851
+ const restore = useCallback14(async () => {
9724
9852
  setState("restoring");
9725
9853
  setError(null);
9726
9854
  try {
@@ -9739,14 +9867,14 @@ function usePurchase() {
9739
9867
  }
9740
9868
 
9741
9869
  // src/hooks/useSubscription.ts
9742
- import { useCallback as useCallback14, useEffect as useEffect12, useState as useState13 } from "react";
9870
+ import { useCallback as useCallback15, useEffect as useEffect12, useState as useState13 } from "react";
9743
9871
  init_SubscriptionManager();
9744
9872
  var EMPTY_ENTITLEMENTS = [];
9745
9873
  function useSubscription() {
9746
9874
  const [status, setStatus] = useState13(null);
9747
9875
  const [isLoading, setIsLoading] = useState13(true);
9748
9876
  const [error, setError] = useState13(null);
9749
- const refresh = useCallback14(async () => {
9877
+ const refresh = useCallback15(async () => {
9750
9878
  setIsLoading(true);
9751
9879
  setError(null);
9752
9880
  try {
@@ -9759,7 +9887,7 @@ function useSubscription() {
9759
9887
  setIsLoading(false);
9760
9888
  }
9761
9889
  }, []);
9762
- const restore = useCallback14(async () => {
9890
+ const restore = useCallback15(async () => {
9763
9891
  setIsLoading(true);
9764
9892
  setError(null);
9765
9893
  try {
@@ -9799,39 +9927,6 @@ function useSubscription() {
9799
9927
  };
9800
9928
  }
9801
9929
 
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
-
9835
9930
  // src/PaywalloProvider.tsx
9836
9931
  import { useCallback as useCallback20, useEffect as useEffect14, useLayoutEffect, useMemo as useMemo3, useRef as useRef12, useState as useState17 } from "react";
9837
9932
  import { Animated as Animated4, Dimensions as Dimensions5, Easing as Easing2, StyleSheet as StyleSheet5 } from "react-native";
@@ -9839,6 +9934,7 @@ init_PaywalloClient();
9839
9934
  init_NativeStorage();
9840
9935
  init_SecureStorage();
9841
9936
  init_campaign();
9937
+ init_identity();
9842
9938
  init_paywall();
9843
9939
  init_paywall();
9844
9940
  init_paywall();
@@ -9850,7 +9946,9 @@ init_paywall();
9850
9946
  init_identity();
9851
9947
  var CONFIG_POLL_INTERVAL_MS = 250;
9852
9948
  var CONFIG_POLL_MAX_ATTEMPTS = 40;
9949
+ var CONFIG_RETRY_DELAYS_MS = [3e4, 6e4, 12e4];
9853
9950
  var lastSignature = null;
9951
+ var retryTimer = null;
9854
9952
  function log(debug, msg, data) {
9855
9953
  if (!debug) return;
9856
9954
  if (data !== void 0) console.log(`[Paywallo:SuperwallAttr] ${msg}`, data);
@@ -9917,28 +10015,37 @@ async function waitForConfigured(mod, debug) {
9917
10015
  status = String(await mod.getConfigurationStatus());
9918
10016
  } catch (err) {
9919
10017
  log(debug, "getConfigurationStatus error", { err: String(err) });
9920
- return false;
10018
+ return "failed";
9921
10019
  }
9922
- if (status === "CONFIGURED") return true;
10020
+ if (status === "CONFIGURED") return "configured";
9923
10021
  if (status === "FAILED") {
9924
10022
  log(debug, "Superwall configuration FAILED \u2014 skipping attribute push");
9925
- return false;
10023
+ return "failed";
9926
10024
  }
9927
10025
  await delay(CONFIG_POLL_INTERVAL_MS);
9928
10026
  }
9929
10027
  log(debug, "Superwall not configured within timeout \u2014 skipping attribute push");
9930
- return false;
10028
+ return "timeout";
9931
10029
  }
9932
10030
  async function pushAttributionToSuperwall(debug = false) {
10031
+ clearRetryTimer();
10032
+ await attemptPush(debug, 0);
10033
+ }
10034
+ async function attemptPush(debug, retryIndex) {
10035
+ const native = await resolveSuperwallNativeModule(debug);
10036
+ if (!native) return;
10037
+ const waited = await waitForConfigured(native, debug);
10038
+ if (waited === "timeout") {
10039
+ scheduleRetry(debug, retryIndex);
10040
+ return;
10041
+ }
10042
+ if (waited !== "configured") return;
9933
10043
  const attrs = buildSuperwallAttributes(attributionTracker.get());
9934
10044
  const signature = JSON.stringify(attrs);
9935
10045
  if (signature === lastSignature) {
9936
10046
  log(debug, "attributes unchanged \u2014 skipping push");
9937
10047
  return;
9938
10048
  }
9939
- const native = await resolveSuperwallNativeModule(debug);
9940
- if (!native) return;
9941
- if (!await waitForConfigured(native, debug)) return;
9942
10049
  try {
9943
10050
  await native.setUserAttributes(attrs);
9944
10051
  lastSignature = signature;
@@ -9947,6 +10054,28 @@ async function pushAttributionToSuperwall(debug = false) {
9947
10054
  log(debug, "setUserAttributes error", { err: String(err) });
9948
10055
  }
9949
10056
  }
10057
+ function scheduleRetry(debug, retryIndex) {
10058
+ const delayMs = CONFIG_RETRY_DELAYS_MS[retryIndex];
10059
+ if (delayMs === void 0) {
10060
+ log(debug, "Superwall never configured \u2014 giving up attribute push");
10061
+ return;
10062
+ }
10063
+ clearRetryTimer();
10064
+ log(debug, "scheduling attribute push retry", { retryIndex, delayMs });
10065
+ retryTimer = setTimeout(() => {
10066
+ retryTimer = null;
10067
+ void attemptPush(debug, retryIndex + 1);
10068
+ }, delayMs);
10069
+ }
10070
+ function clearRetryTimer() {
10071
+ if (retryTimer !== null) {
10072
+ clearTimeout(retryTimer);
10073
+ retryTimer = null;
10074
+ }
10075
+ }
10076
+ function cancelSuperwallAttributeRetry() {
10077
+ clearRetryTimer();
10078
+ }
9950
10079
 
9951
10080
  // src/domains/paywall/SuperwallAutoBridge.ts
9952
10081
  init_PaywalloClient();
@@ -9979,6 +10108,7 @@ function buildDeterministicEventId(parts) {
9979
10108
 
9980
10109
  // src/domains/paywall/SuperwallAutoBridge.ts
9981
10110
  var bridgeStarted = false;
10111
+ var startInProgress = false;
9982
10112
  var unsubscribe = null;
9983
10113
  var debugEnabled = false;
9984
10114
  function log2(msg, data) {
@@ -10019,8 +10149,7 @@ function mapDismissToCloseReason(resultType) {
10019
10149
  case "purchased":
10020
10150
  return "purchased";
10021
10151
  case "restored":
10022
- return "purchased";
10023
- // restore counts as a purchase outcome for funnel
10152
+ return "restored";
10024
10153
  case "declined":
10025
10154
  return "dismissed";
10026
10155
  default:
@@ -10057,16 +10186,18 @@ function buildCallbacks() {
10057
10186
  log2("onPurchase", { productId, identifier });
10058
10187
  void trackPaywallEvent("purchased", identifier, ts, { product_id: productId });
10059
10188
  }
10060
- // onSuperwallEvent removed transaction sale events are sourced exclusively
10061
- // from server-side webhooks (Apple/Google/Superwall/RevenueCat).
10189
+ // onSuperwallEvent (transactionComplete transaction track) removed
10190
+ // sale/transaction events are sourced exclusively from server-side webhooks
10191
+ // (Apple/Google/Superwall/RevenueCat), the single source of truth for revenue.
10062
10192
  };
10063
10193
  }
10064
10194
  function startSuperwallAutoBridge(debug = false) {
10065
10195
  debugEnabled = debug;
10066
- if (bridgeStarted) {
10067
- log2("already started, skipping");
10196
+ if (bridgeStarted || startInProgress) {
10197
+ log2("already started or starting, skipping");
10068
10198
  return;
10069
10199
  }
10200
+ startInProgress = true;
10070
10201
  void detectAndStart();
10071
10202
  }
10072
10203
  async function detectAndStart() {
@@ -10077,6 +10208,7 @@ async function detectAndStart() {
10077
10208
  const internal = await import("expo-superwall/build/src/internal/superwallEventBridge");
10078
10209
  if (typeof internal.subscribeToSuperwallEvents !== "function") {
10079
10210
  log2("expo-superwall subscribeToSuperwallEvents not found \u2014 bridge inactive");
10211
+ startInProgress = false;
10080
10212
  return;
10081
10213
  }
10082
10214
  subscribeFn = internal.subscribeToSuperwallEvents;
@@ -10085,6 +10217,11 @@ async function detectAndStart() {
10085
10217
  }
10086
10218
  } catch {
10087
10219
  log2("expo-superwall not installed \u2014 bridge inactive");
10220
+ startInProgress = false;
10221
+ return;
10222
+ }
10223
+ if (!startInProgress) {
10224
+ log2("start cancelled by stop \u2014 bridge inactive");
10088
10225
  return;
10089
10226
  }
10090
10227
  try {
@@ -10094,7 +10231,18 @@ async function detectAndStart() {
10094
10231
  log2("bridge started");
10095
10232
  } catch (err) {
10096
10233
  log2("subscribe failed", { err: String(err) });
10234
+ } finally {
10235
+ startInProgress = false;
10236
+ }
10237
+ }
10238
+ function stopSuperwallAutoBridge() {
10239
+ if (unsubscribe) {
10240
+ unsubscribe();
10241
+ unsubscribe = null;
10097
10242
  }
10243
+ bridgeStarted = false;
10244
+ startInProgress = false;
10245
+ log2("bridge stopped");
10098
10246
  }
10099
10247
 
10100
10248
  // src/PaywalloProvider.tsx
@@ -10661,6 +10809,9 @@ function PaywalloProvider({ children, config }) {
10661
10809
  const configRef = useRef12(config);
10662
10810
  useEffect14(() => {
10663
10811
  let mounted = true;
10812
+ const unsubscribeAttribution = attributionTracker.onCapture(() => {
10813
+ void pushAttributionToSuperwall(configRef.current.debug);
10814
+ });
10664
10815
  const bootstrap = async () => {
10665
10816
  if (!PaywalloClient.isReady()) {
10666
10817
  initLocalization({ detectDevice: true });
@@ -10671,11 +10822,10 @@ function PaywalloProvider({ children, config }) {
10671
10822
  setInitError(err instanceof Error ? err : new ClientError(CLIENT_ERROR_CODES.NOT_INITIALIZED, String(err)));
10672
10823
  return;
10673
10824
  }
10674
- if (!mounted) return;
10675
- startSuperwallAutoBridge(configRef.current.debug);
10676
- void pushAttributionToSuperwall(configRef.current.debug);
10677
10825
  }
10678
10826
  if (!mounted) return;
10827
+ startSuperwallAutoBridge(configRef.current.debug);
10828
+ void pushAttributionToSuperwall(configRef.current.debug);
10679
10829
  setDistinctId(PaywalloClient.getDistinctId());
10680
10830
  setIsInitialized(true);
10681
10831
  if (!autoPreloadTriggeredRef.current) {
@@ -10693,6 +10843,9 @@ function PaywalloProvider({ children, config }) {
10693
10843
  void bootstrap();
10694
10844
  return () => {
10695
10845
  mounted = false;
10846
+ unsubscribeAttribution();
10847
+ stopSuperwallAutoBridge();
10848
+ cancelSuperwallAttributeRetry();
10696
10849
  };
10697
10850
  }, [preloadCampaign]);
10698
10851
  const getPaywallConfig = useCallback20(async (p) => {