@virex-tech/paywallo-sdk 1.4.1 → 1.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.js CHANGED
@@ -52,10 +52,7 @@ var init_uuid = __esm({
52
52
  // src/index.ts
53
53
  var index_exports = {};
54
54
  __export(index_exports, {
55
- AFFILIATE_ERROR_CODES: () => AFFILIATE_ERROR_CODES,
56
55
  ANALYTICS_ERROR_CODES: () => ANALYTICS_ERROR_CODES,
57
- AffiliateError: () => AffiliateError,
58
- AffiliateManager: () => AffiliateManager,
59
56
  AnalyticsError: () => AnalyticsError,
60
57
  ApiClient: () => ApiClient,
61
58
  CAMPAIGN_ERROR_CODES: () => CAMPAIGN_ERROR_CODES,
@@ -73,7 +70,6 @@ __export(index_exports, {
73
70
  PaywallError: () => PaywallError,
74
71
  PaywallModal: () => PaywallModal,
75
72
  Paywallo: () => Paywallo,
76
- PaywalloAffiliate: () => PaywalloAffiliate,
77
73
  PaywalloClient: () => PaywalloClient,
78
74
  PaywalloContext: () => PaywalloContext,
79
75
  PaywalloError: () => PaywalloError,
@@ -83,7 +79,6 @@ __export(index_exports, {
83
79
  SessionError: () => SessionError,
84
80
  SessionManager: () => SessionManager,
85
81
  SubscriptionCache: () => SubscriptionCache,
86
- affiliateManager: () => affiliateManager,
87
82
  createPurchaseError: () => createPurchaseError,
88
83
  default: () => index_default,
89
84
  detectDeviceLanguage: () => detectDeviceLanguage,
@@ -308,6 +303,7 @@ var SecureStorage = class {
308
303
  var secureStorage = new SecureStorage();
309
304
 
310
305
  // src/domains/identity/IdentityManager.ts
306
+ var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
311
307
  var DEVICE_ID_KEY = "device_id";
312
308
  var ANON_ID_KEY = "anon_id";
313
309
  var USER_EMAIL_KEY = "user_email";
@@ -364,8 +360,15 @@ var IdentityManager = class {
364
360
  }
365
361
  }
366
362
  if (email) {
367
- this.email = email;
368
- await this.secureStorage.set(USER_EMAIL_KEY, email);
363
+ if (!EMAIL_REGEX.test(email)) {
364
+ if (this.debug) {
365
+ console.warn("[Paywallo] Invalid email format, skipping email field");
366
+ }
367
+ email = void 0;
368
+ } else {
369
+ this.email = email;
370
+ await this.secureStorage.set(USER_EMAIL_KEY, email);
371
+ }
369
372
  }
370
373
  if (properties) {
371
374
  this.properties = { ...this.properties, ...properties };
@@ -1572,227 +1575,6 @@ function usePaywallPresenter(preloadedWebView, setPreloadedWebView) {
1572
1575
  return { showingPreloadedWebView, presentPreloadedWebView, resolvePreloadedPaywall, handlePreloadedWebViewReady, handlePreloadedClose, setShowingPreloadedWebView, presentedAtRef };
1573
1576
  }
1574
1577
 
1575
- // src/domains/affiliate/AffiliateError.ts
1576
- var AffiliateError = class extends PaywalloError {
1577
- constructor(code, message) {
1578
- super("affiliate", code, message);
1579
- this.name = "AffiliateError";
1580
- }
1581
- };
1582
- var AFFILIATE_ERROR_CODES = {
1583
- NOT_INITIALIZED: "AFFILIATE_NOT_INITIALIZED",
1584
- COUPON_REGISTER_FAILED: "AFFILIATE_COUPON_REGISTER_FAILED",
1585
- COUPON_APPLY_FAILED: "AFFILIATE_COUPON_APPLY_FAILED",
1586
- BALANCE_FETCH_FAILED: "AFFILIATE_BALANCE_FETCH_FAILED",
1587
- WITHDRAWAL_FAILED: "AFFILIATE_WITHDRAWAL_FAILED",
1588
- NETWORK_ERROR: "AFFILIATE_NETWORK_ERROR"
1589
- };
1590
-
1591
- // src/domains/affiliate/affiliateHttp.ts
1592
- function buildQueryString(params) {
1593
- if (!params) return "";
1594
- const entries = Object.entries(params).filter(([, v]) => v !== void 0 && v !== null);
1595
- if (entries.length === 0) return "";
1596
- return `?${entries.map(([k, v]) => `${k}=${encodeURIComponent(String(v))}`).join("&")}`;
1597
- }
1598
- async function affiliateGet(apiClient, path, params) {
1599
- const response = await apiClient.get(`${path}${buildQueryString(params)}`);
1600
- return response.data;
1601
- }
1602
- async function affiliatePost(apiClient, path, body) {
1603
- const response = await apiClient.post(path, body);
1604
- return response.data;
1605
- }
1606
-
1607
- // src/domains/affiliate/affiliateDateMappers.ts
1608
- function mapRawCoupon(raw) {
1609
- return { code: raw.code, status: raw.status, createdAt: new Date(raw.createdAt) };
1610
- }
1611
- function mapRawCommission(raw) {
1612
- return { ...raw, createdAt: new Date(raw.createdAt) };
1613
- }
1614
- function mapRawWithdrawal(raw) {
1615
- return { ...raw, createdAt: new Date(raw.createdAt) };
1616
- }
1617
-
1618
- // src/domains/affiliate/AffiliateManager.ts
1619
- var AffiliateManager = class {
1620
- constructor() {
1621
- this.apiClient = null;
1622
- this.debug = false;
1623
- }
1624
- initialize(apiClient, debug = false) {
1625
- this.apiClient = apiClient;
1626
- this.debug = debug;
1627
- this.log("AffiliateManager initialized");
1628
- }
1629
- async registerCoupon(code) {
1630
- this.ensureInitialized();
1631
- const distinctId = identityManager.getDistinctId();
1632
- try {
1633
- const response = await this.post("/api/v1/affiliate/register-coupon", {
1634
- code,
1635
- distinctId
1636
- });
1637
- if (response.success && response.coupon) {
1638
- this.log("Coupon registered:", code);
1639
- return { success: true, coupon: mapRawCoupon(response.coupon) };
1640
- }
1641
- return {
1642
- success: false,
1643
- error: response.error
1644
- };
1645
- } catch (error) {
1646
- this.log("Failed to register coupon:", error);
1647
- return {
1648
- success: false,
1649
- error: error instanceof Error ? error.message : "Unknown error"
1650
- };
1651
- }
1652
- }
1653
- async applyCoupon(code) {
1654
- this.ensureInitialized();
1655
- const distinctId = identityManager.getDistinctId();
1656
- try {
1657
- const response = await this.post("/api/v1/affiliate/apply-coupon", {
1658
- code,
1659
- distinctId
1660
- });
1661
- if (response.success) {
1662
- this.log("Coupon applied:", code);
1663
- }
1664
- return response;
1665
- } catch (error) {
1666
- this.log("Failed to apply coupon:", error);
1667
- return {
1668
- success: false,
1669
- error: error instanceof Error ? error.message : "Unknown error"
1670
- };
1671
- }
1672
- }
1673
- async getMyCoupon() {
1674
- this.ensureInitialized();
1675
- const distinctId = identityManager.getDistinctId();
1676
- try {
1677
- const response = await this.get("/api/v1/affiliate/my-coupon", { distinctId });
1678
- if (response.coupon) {
1679
- return { coupon: mapRawCoupon(response.coupon) };
1680
- }
1681
- return { coupon: null };
1682
- } catch (error) {
1683
- this.log("Failed to get my coupon:", error);
1684
- return { coupon: null };
1685
- }
1686
- }
1687
- async getAppliedCoupon() {
1688
- this.ensureInitialized();
1689
- const distinctId = identityManager.getDistinctId();
1690
- try {
1691
- const response = await this.get("/api/v1/affiliate/applied-coupon", { distinctId });
1692
- return response;
1693
- } catch (error) {
1694
- this.log("Failed to get applied coupon:", error);
1695
- return { coupon: null };
1696
- }
1697
- }
1698
- async getBalance() {
1699
- this.ensureInitialized();
1700
- const distinctId = identityManager.getDistinctId();
1701
- try {
1702
- const response = await this.get("/api/v1/affiliate/balance", {
1703
- distinctId
1704
- });
1705
- return response;
1706
- } catch (error) {
1707
- this.log("Failed to get balance:", error);
1708
- return {
1709
- total: 0,
1710
- pending: 0,
1711
- available: 0,
1712
- withdrawn: 0
1713
- };
1714
- }
1715
- }
1716
- async getCommissions() {
1717
- this.ensureInitialized();
1718
- const distinctId = identityManager.getDistinctId();
1719
- try {
1720
- const response = await this.get("/api/v1/affiliate/commissions", { distinctId });
1721
- return response.commissions.map(mapRawCommission);
1722
- } catch (error) {
1723
- this.log("Failed to get commissions:", error);
1724
- return [];
1725
- }
1726
- }
1727
- async requestWithdrawal(amount) {
1728
- this.ensureInitialized();
1729
- const distinctId = identityManager.getDistinctId();
1730
- try {
1731
- const response = await this.post("/api/v1/affiliate/withdraw", {
1732
- amount,
1733
- distinctId
1734
- });
1735
- if (response.success && response.withdrawal) {
1736
- this.log("Withdrawal requested:", amount);
1737
- return { success: true, withdrawal: mapRawWithdrawal(response.withdrawal) };
1738
- }
1739
- return {
1740
- success: false,
1741
- error: response.error
1742
- };
1743
- } catch (error) {
1744
- this.log("Failed to request withdrawal:", error);
1745
- return {
1746
- success: false,
1747
- error: error instanceof Error ? error.message : "Unknown error"
1748
- };
1749
- }
1750
- }
1751
- async getWithdrawals() {
1752
- this.ensureInitialized();
1753
- const distinctId = identityManager.getDistinctId();
1754
- try {
1755
- const response = await this.get("/api/v1/affiliate/withdrawals", { distinctId });
1756
- return response.withdrawals.map(mapRawWithdrawal);
1757
- } catch (error) {
1758
- this.log("Failed to get withdrawals:", error);
1759
- return [];
1760
- }
1761
- }
1762
- async get(path, params) {
1763
- this.ensureInitialized();
1764
- return affiliateGet(this.apiClient, path, params);
1765
- }
1766
- async post(path, body) {
1767
- this.ensureInitialized();
1768
- return affiliatePost(this.apiClient, path, body);
1769
- }
1770
- ensureInitialized() {
1771
- if (!this.apiClient) {
1772
- throw new AffiliateError(
1773
- AFFILIATE_ERROR_CODES.NOT_INITIALIZED,
1774
- "AffiliateManager not initialized. Call initialize() first"
1775
- );
1776
- }
1777
- }
1778
- log(...args) {
1779
- if (this.debug) console.log("[Paywallo:Affiliate]", ...args);
1780
- }
1781
- };
1782
- var affiliateManager = new AffiliateManager();
1783
-
1784
- // src/domains/affiliate/PaywalloAffiliate.ts
1785
- var PaywalloAffiliate = {
1786
- registerCoupon: (code) => affiliateManager.registerCoupon(code),
1787
- applyCoupon: (code) => affiliateManager.applyCoupon(code),
1788
- getMyCoupon: () => affiliateManager.getMyCoupon(),
1789
- getAppliedCoupon: () => affiliateManager.getAppliedCoupon(),
1790
- getBalance: () => affiliateManager.getBalance(),
1791
- getCommissions: () => affiliateManager.getCommissions(),
1792
- requestWithdrawal: (amount) => affiliateManager.requestWithdrawal(amount),
1793
- getWithdrawals: () => affiliateManager.getWithdrawals()
1794
- };
1795
-
1796
1578
  // src/domains/subscription/SubscriptionCache.ts
1797
1579
  var LEGACY_CACHE_KEY = "subscription_cache";
1798
1580
  var CACHE_KEY_PREFIX = "subscription_cache:";
@@ -1902,9 +1684,6 @@ var SubscriptionCache = class {
1902
1684
  };
1903
1685
  var subscriptionCache = new SubscriptionCache();
1904
1686
 
1905
- // src/domains/subscription/SubscriptionManager.ts
1906
- var import_react_native7 = require("react-native");
1907
-
1908
1687
  // src/domains/session/SessionError.ts
1909
1688
  var SessionError = class extends PaywalloError {
1910
1689
  constructor(code, message) {
@@ -1992,11 +1771,10 @@ var SubscriptionManagerClass = class {
1992
1771
  if (!this.config) {
1993
1772
  return this.getEmptyStatus();
1994
1773
  }
1995
- const url = new URL(`${this.config.serverUrl}/subscriptions/status`);
1774
+ const url = new URL(`${this.config.serverUrl}/purchases/status/${encodeURIComponent(this.config.appKey)}`);
1996
1775
  if (this.userId) {
1997
- url.searchParams.set("userId", this.userId);
1776
+ url.searchParams.set("distinctId", this.userId);
1998
1777
  }
1999
- url.searchParams.set("platform", import_react_native7.Platform.OS);
2000
1778
  const response = await fetch(url.toString(), {
2001
1779
  method: "GET",
2002
1780
  headers: {
@@ -2051,7 +1829,7 @@ var SubscriptionManagerClass = class {
2051
1829
  var subscriptionManager = new SubscriptionManagerClass();
2052
1830
 
2053
1831
  // src/core/ApiClient.ts
2054
- var import_react_native9 = require("react-native");
1832
+ var import_react_native8 = require("react-native");
2055
1833
 
2056
1834
  // src/core/apiUrlUtils.ts
2057
1835
  var DEFAULT_WEB_URL = "https://paywallo.com.br";
@@ -2301,7 +2079,7 @@ var HttpClient = class {
2301
2079
  };
2302
2080
 
2303
2081
  // src/core/network/NetworkMonitor.ts
2304
- var import_react_native8 = require("react-native");
2082
+ var import_react_native7 = require("react-native");
2305
2083
 
2306
2084
  // src/core/network/types.ts
2307
2085
  var DEFAULT_NETWORK_CONFIG = {
@@ -2338,7 +2116,7 @@ var NetworkMonitorClass = class {
2338
2116
  }, this.config.checkInterval);
2339
2117
  }
2340
2118
  setupAppStateListener() {
2341
- this.appStateSubscription = import_react_native8.AppState.addEventListener("change", (state) => {
2119
+ this.appStateSubscription = import_react_native7.AppState.addEventListener("change", (state) => {
2342
2120
  if (state === "active") {
2343
2121
  void this.checkConnectivity();
2344
2122
  }
@@ -2734,9 +2512,78 @@ var QueueProcessorClass = class {
2734
2512
  this.log("Offline, skipping queue processing");
2735
2513
  return { processed: 0, failed: 0 };
2736
2514
  }
2737
- return offlineQueue.processQueue(async (item) => {
2515
+ const allItems = offlineQueue.getQueue();
2516
+ const eventItems = allItems.filter(
2517
+ (i) => i.url.includes("/api/v1/events") && !i.url.includes("/batch")
2518
+ );
2519
+ const eventItemIds = new Set(eventItems.map((i) => i.id));
2520
+ const { processed: batchProcessed, failed: batchFailed, retryIds } = await this.processEventBatches(eventItems);
2521
+ const batchResult = { processed: batchProcessed, failed: batchFailed };
2522
+ const individualResult = await offlineQueue.processQueue(async (item) => {
2523
+ if (eventItemIds.has(item.id)) {
2524
+ if (retryIds.has(item.id)) {
2525
+ return { success: false, permanent: false };
2526
+ }
2527
+ return { success: true, permanent: false };
2528
+ }
2738
2529
  return this.processItem(item);
2739
2530
  });
2531
+ this.log("Queue processing complete", {
2532
+ batchProcessed: batchResult.processed,
2533
+ batchFailed: batchResult.failed,
2534
+ individualProcessed: individualResult.processed,
2535
+ individualFailed: individualResult.failed
2536
+ });
2537
+ return {
2538
+ processed: batchResult.processed + individualResult.processed,
2539
+ failed: batchResult.failed + individualResult.failed
2540
+ };
2541
+ }
2542
+ async processEventBatches(eventItems) {
2543
+ if (eventItems.length === 0 || !this.httpClient) {
2544
+ return { processed: 0, failed: 0, retryIds: /* @__PURE__ */ new Set() };
2545
+ }
2546
+ const BATCH_SIZE = 50;
2547
+ let processed = 0;
2548
+ let failed = 0;
2549
+ const retryIds = /* @__PURE__ */ new Set();
2550
+ for (let i = 0; i < eventItems.length; i += BATCH_SIZE) {
2551
+ const batch = eventItems.slice(i, i + BATCH_SIZE);
2552
+ const bodies = batch.map((item) => item.body);
2553
+ const headers = batch[0].headers;
2554
+ this.log("Sending event batch", { size: batch.length, batchIndex: Math.floor(i / BATCH_SIZE) });
2555
+ const response = await this.httpClient.request("/api/v1/events/batch", {
2556
+ method: "POST",
2557
+ body: { events: bodies },
2558
+ headers,
2559
+ skipRetry: false
2560
+ });
2561
+ if (response.ok) {
2562
+ for (const item of batch) {
2563
+ await offlineQueue.removeItem(item.id);
2564
+ processed++;
2565
+ }
2566
+ this.log("Event batch processed successfully", { size: batch.length });
2567
+ } else if (response.status >= 400 && response.status < 500) {
2568
+ for (const item of batch) {
2569
+ await offlineQueue.removeItem(item.id);
2570
+ failed++;
2571
+ }
2572
+ this.log("Event batch failed permanently (4xx) - removing from queue", {
2573
+ status: response.status,
2574
+ size: batch.length
2575
+ });
2576
+ } else {
2577
+ for (const item of batch) {
2578
+ retryIds.add(item.id);
2579
+ }
2580
+ this.log("Event batch failed with server error - will retry", {
2581
+ status: response.status,
2582
+ size: batch.length
2583
+ });
2584
+ }
2585
+ }
2586
+ return { processed, failed, retryIds };
2740
2587
  }
2741
2588
  async processItem(item) {
2742
2589
  if (!this.httpClient) {
@@ -2887,10 +2734,14 @@ var ApiCache = class {
2887
2734
  };
2888
2735
 
2889
2736
  // src/core/ApiClient.ts
2890
- var SDK_VERSION = "1.4.1";
2737
+ var SDK_VERSION = "1.5.0";
2891
2738
  var _ApiClient = class _ApiClient {
2892
2739
  constructor(appKey, apiUrl, debug = false, environment = "Production", offlineQueueEnabled = true, timeout = 3e4) {
2893
2740
  this.cache = new ApiCache();
2741
+ this.eventBatch = [];
2742
+ this.batchTimer = null;
2743
+ this.BATCH_MAX_SIZE = 10;
2744
+ this.BATCH_FLUSH_MS = 5e3;
2894
2745
  this.appKey = appKey;
2895
2746
  this.baseUrl = apiUrl;
2896
2747
  this.webUrl = deriveWebUrl(this.baseUrl);
@@ -2943,30 +2794,79 @@ var _ApiClient = class _ApiClient {
2943
2794
  message,
2944
2795
  context,
2945
2796
  sdkVersion: SDK_VERSION,
2946
- platform: import_react_native9.Platform.OS
2797
+ platform: import_react_native8.Platform.OS
2947
2798
  }, true);
2948
2799
  } catch (err) {
2949
2800
  if (this.debug) console.warn("[Paywallo:ApiClient] reportError failed", err);
2950
2801
  }
2951
2802
  }
2952
2803
  async trackEvent(eventName, distinctId, properties, timestamp) {
2953
- await this.postWithQueue("/api/v1/events", {
2804
+ const EVENT_NAME_REGEX = /^(\$[a-zA-Z0-9_]+|[a-z][a-z0-9_]*)$/;
2805
+ if (!EVENT_NAME_REGEX.test(eventName)) {
2806
+ if (this.debug) {
2807
+ console.warn(`[Paywallo] Invalid event name "${eventName}". Must be snake_case or start with $`);
2808
+ }
2809
+ }
2810
+ const event = {
2954
2811
  eventName,
2955
2812
  distinctId,
2956
- properties: { ...properties, platform: import_react_native9.Platform.OS },
2813
+ properties: { ...properties, platform: import_react_native8.Platform.OS },
2957
2814
  timestamp: timestamp ?? Date.now(),
2958
2815
  environment: this.environment.toLowerCase()
2959
- }, `event:${eventName}`);
2816
+ };
2817
+ this.eventBatch.push(event);
2818
+ if (this.eventBatch.length >= this.BATCH_MAX_SIZE) {
2819
+ if (this.batchTimer !== null) {
2820
+ clearTimeout(this.batchTimer);
2821
+ this.batchTimer = null;
2822
+ }
2823
+ await this.flushEventBatch();
2824
+ return;
2825
+ }
2826
+ if (this.batchTimer !== null) {
2827
+ clearTimeout(this.batchTimer);
2828
+ }
2829
+ this.batchTimer = setTimeout(() => {
2830
+ this.batchTimer = null;
2831
+ void this.flushEventBatch();
2832
+ }, this.BATCH_FLUSH_MS);
2833
+ }
2834
+ async flushEventBatch() {
2835
+ if (this.eventBatch.length === 0) return;
2836
+ const batch = this.eventBatch.splice(0, this.eventBatch.length);
2837
+ if (this.batchTimer !== null) {
2838
+ clearTimeout(this.batchTimer);
2839
+ this.batchTimer = null;
2840
+ }
2841
+ try {
2842
+ await this.postWithQueue(
2843
+ "/api/v1/events/batch",
2844
+ { events: batch },
2845
+ `event_batch:${batch.length}`
2846
+ );
2847
+ } catch (error) {
2848
+ this.log("Batch send failed, falling back to individual sends:", error);
2849
+ for (const event of batch) {
2850
+ await this.postWithQueue(
2851
+ "/api/v1/events",
2852
+ event,
2853
+ `event:${event.eventName}`
2854
+ );
2855
+ }
2856
+ }
2960
2857
  }
2961
2858
  async identify(distinctId, properties, email, deviceId) {
2962
- await this.postWithQueue("/api/v1/identify", { distinctId, properties: properties ?? {}, email, platform: import_react_native9.Platform.OS, ...deviceId && { deviceId } }, "identify");
2859
+ await this.postWithQueue("/api/v1/identify", { distinctId, properties: properties ?? {}, email, platform: import_react_native8.Platform.OS, ...deviceId && { deviceId } }, "identify");
2963
2860
  }
2964
- async startSession(distinctId, sessionId, platform) {
2861
+ async startSession(distinctId, sessionId, platform, appVersion, deviceModel, osVersion) {
2965
2862
  await this.postWithQueue("/api/v1/sessions/start", {
2966
2863
  distinctId,
2967
2864
  sessionId,
2968
- platform: platform ?? import_react_native9.Platform.OS,
2969
- environment: this.environment.toLowerCase()
2865
+ platform: platform ?? import_react_native8.Platform.OS,
2866
+ environment: this.environment.toLowerCase(),
2867
+ appVersion: appVersion ?? "unknown",
2868
+ deviceModel: deviceModel ?? "unknown",
2869
+ osVersion: osVersion ?? String(import_react_native8.Platform.Version)
2970
2870
  }, `session.start:${sessionId}`);
2971
2871
  return { success: true };
2972
2872
  }
@@ -3274,7 +3174,7 @@ var CampaignGateService = class {
3274
3174
  const isActive = await hasActive();
3275
3175
  if (PaywalloClient.getConfig()?.debug) console.info(`[Paywallo] requireSubscription \u2014 hasActive: ${isActive}`);
3276
3176
  if (isActive) return true;
3277
- const r = await this.presentCampaign(apiClient, placement, hasActive, paywallPresenter, campaignPresenter, context);
3177
+ const r = await this.presentCampaign(apiClient, placement, hasActive, paywallPresenter, campaignPresenter, { ...context, forceShow: true });
3278
3178
  return r.purchased || r.restored;
3279
3179
  }
3280
3180
  async preloadCampaign(apiClient, placement, context) {
@@ -3312,7 +3212,7 @@ var CampaignGateService = class {
3312
3212
  var campaignGateService = new CampaignGateService();
3313
3213
 
3314
3214
  // src/domains/identity/AdvertisingIdManager.ts
3315
- var import_react_native10 = require("react-native");
3215
+ var import_react_native9 = require("react-native");
3316
3216
  var ZERO_IDFA = "00000000-0000-0000-0000-000000000000";
3317
3217
  var AdvertisingIdManager = class {
3318
3218
  constructor() {
@@ -3323,7 +3223,7 @@ var AdvertisingIdManager = class {
3323
3223
  const fallback = { idfv: null, idfa: null, gaid: null, attStatus: "unavailable" };
3324
3224
  try {
3325
3225
  const tracking = await import("expo-tracking-transparency");
3326
- if (import_react_native10.Platform.OS === "ios") {
3226
+ if (import_react_native9.Platform.OS === "ios") {
3327
3227
  if (!tracking.isAvailable()) {
3328
3228
  const idfv2 = await this.collectIdfv();
3329
3229
  this.cached = { ...fallback, idfv: idfv2 };
@@ -3342,7 +3242,7 @@ var AdvertisingIdManager = class {
3342
3242
  this.cached = { idfv, idfa, gaid: null, attStatus: status };
3343
3243
  return this.cached;
3344
3244
  }
3345
- if (import_react_native10.Platform.OS === "android") {
3245
+ if (import_react_native9.Platform.OS === "android") {
3346
3246
  const androidStatus = await tracking.getTrackingPermissionsAsync();
3347
3247
  const optedIn = androidStatus.granted;
3348
3248
  const rawGaid = optedIn ? tracking.getAdvertisingId() : null;
@@ -3378,9 +3278,9 @@ var import_async_storage3 = __toESM(require("@react-native-async-storage/async-s
3378
3278
  var import_react_native_device_info2 = __toESM(require("react-native-device-info"));
3379
3279
 
3380
3280
  // src/domains/identity/MetaBridge.ts
3381
- var import_react_native11 = require("react-native");
3281
+ var import_react_native10 = require("react-native");
3382
3282
  var TIMEOUT_MS = 2e3;
3383
- var NativeBridge = import_react_native11.NativeModules.PaywalloFBBridge ?? null;
3283
+ var NativeBridge = import_react_native10.NativeModules.PaywalloFBBridge ?? null;
3384
3284
  var MetaBridge = class {
3385
3285
  async getAnonymousID() {
3386
3286
  if (!NativeBridge) return null;
@@ -3428,11 +3328,11 @@ var MetaBridge = class {
3428
3328
  var metaBridge = new MetaBridge();
3429
3329
 
3430
3330
  // src/domains/identity/InstallReferrerManager.ts
3431
- var import_react_native12 = require("react-native");
3331
+ var import_react_native11 = require("react-native");
3432
3332
  var REFERRER_TIMEOUT_MS = 5e3;
3433
3333
  var InstallReferrerManager = class {
3434
3334
  async getReferrer() {
3435
- if (import_react_native12.Platform.OS !== "android") {
3335
+ if (import_react_native11.Platform.OS !== "android") {
3436
3336
  return this.emptyResult();
3437
3337
  }
3438
3338
  let timerId;
@@ -3495,14 +3395,77 @@ var installReferrerManager = new InstallReferrerManager();
3495
3395
  var import_react_native13 = require("react-native");
3496
3396
  init_uuid();
3497
3397
 
3398
+ // src/utils/deviceInfo.ts
3399
+ var import_react_native12 = require("react-native");
3400
+ async function getAppVersion() {
3401
+ try {
3402
+ const app = await import("expo-application");
3403
+ if (app.nativeApplicationVersion) {
3404
+ return app.nativeApplicationVersion;
3405
+ }
3406
+ } catch {
3407
+ }
3408
+ try {
3409
+ const DeviceInfo2 = (await import("react-native-device-info")).default;
3410
+ const v = DeviceInfo2.getVersion();
3411
+ if (v) return v;
3412
+ } catch {
3413
+ }
3414
+ return "unknown";
3415
+ }
3416
+ async function getDeviceModel() {
3417
+ try {
3418
+ const DeviceInfo2 = (await import("react-native-device-info")).default;
3419
+ const model = DeviceInfo2.getModel();
3420
+ if (model) return model;
3421
+ } catch {
3422
+ }
3423
+ return "unknown";
3424
+ }
3425
+ function getOsVersion() {
3426
+ const v = import_react_native12.Platform.Version;
3427
+ if (typeof v === "number") return String(v);
3428
+ if (typeof v === "string" && v.length > 0) return v;
3429
+ return "unknown";
3430
+ }
3431
+ async function collectDeviceInfo() {
3432
+ const [appVersion, deviceModel] = await Promise.all([
3433
+ getAppVersion(),
3434
+ getDeviceModel()
3435
+ ]);
3436
+ return {
3437
+ appVersion,
3438
+ deviceModel,
3439
+ osVersion: getOsVersion()
3440
+ };
3441
+ }
3442
+
3498
3443
  // src/domains/session/sessionTracking.ts
3499
3444
  async function trackSessionStart(apiClient, sessionId, sessionStartedAt) {
3500
3445
  const distinctId = identityManager.getDistinctId();
3501
- await apiClient.trackEvent("$session_start", distinctId, {
3502
- sessionId,
3503
- timestamp: sessionStartedAt?.toISOString() ?? null
3504
- }).catch(() => {
3505
- });
3446
+ const deviceInfo = await collectDeviceInfo().catch(() => ({
3447
+ appVersion: "unknown",
3448
+ deviceModel: "unknown",
3449
+ osVersion: "unknown"
3450
+ }));
3451
+ await Promise.all([
3452
+ apiClient.startSession(
3453
+ distinctId,
3454
+ sessionId,
3455
+ void 0,
3456
+ deviceInfo.appVersion,
3457
+ deviceInfo.deviceModel,
3458
+ deviceInfo.osVersion
3459
+ ),
3460
+ apiClient.trackEvent("$session_start", distinctId, {
3461
+ sessionId,
3462
+ timestamp: sessionStartedAt?.toISOString() ?? null,
3463
+ appVersion: deviceInfo.appVersion,
3464
+ deviceModel: deviceInfo.deviceModel,
3465
+ osVersion: deviceInfo.osVersion
3466
+ }).catch(() => {
3467
+ })
3468
+ ]);
3506
3469
  }
3507
3470
  async function trackSessionEnd(apiClient, sessionId, durationSeconds, sessionStartedAt) {
3508
3471
  const distinctId = identityManager.getDistinctId();
@@ -3599,6 +3562,9 @@ var SessionManager = class {
3599
3562
  if (!this.sessionId || !this.sessionStartedAt) {
3600
3563
  return;
3601
3564
  }
3565
+ if (this.apiClient) {
3566
+ await this.apiClient.flushEventBatch();
3567
+ }
3602
3568
  await this.trackSessionEnd();
3603
3569
  this.sessionId = null;
3604
3570
  this.sessionStartedAt = null;
@@ -3683,6 +3649,9 @@ var SessionManager = class {
3683
3649
  async handleBackground() {
3684
3650
  this.backgroundTimestamp = Date.now();
3685
3651
  await this.trackAppBackground();
3652
+ if (this.apiClient) {
3653
+ await this.apiClient.flushEventBatch();
3654
+ }
3686
3655
  }
3687
3656
  async checkEmergencyPaywall() {
3688
3657
  if (!this.apiClient || !this.emergencyPaywallHandler) {
@@ -4038,7 +4007,6 @@ var _PaywalloClientClass = class _PaywalloClientClass {
4038
4007
  ]);
4039
4008
  this.apiClient = new ApiClient(config.appKey, config.apiUrl ?? "https://paywallo.com.br", config.debug, environment, offlineQueueEnabled, config.timeout);
4040
4009
  queueProcessor.initialize(this.apiClient.getHttpClient(), { debug: config.debug });
4041
- affiliateManager.initialize(this.apiClient, config.debug);
4042
4010
  subscriptionManager.init({
4043
4011
  serverUrl: this.apiClient.getBaseUrl(),
4044
4012
  appKey: config.appKey,
@@ -4065,6 +4033,12 @@ var _PaywalloClientClass = class _PaywalloClientClass {
4065
4033
  this.resolveReady = null;
4066
4034
  }
4067
4035
  if (config.debug) console.info("[Paywallo INIT] complete");
4036
+ if (distinctId) {
4037
+ this.apiClient.getSubscriptionStatus(distinctId).then(() => {
4038
+ if (config.debug) console.info("[Paywallo SUB] preloaded on boot");
4039
+ }).catch(() => {
4040
+ });
4041
+ }
4068
4042
  if (this.apiClient) {
4069
4043
  const apiClientRef = this.apiClient;
4070
4044
  if (config.autoPreloadCampaign) {
@@ -4349,6 +4323,10 @@ var _PaywalloClientClass = class _PaywalloClientClass {
4349
4323
  registerEmergencyPaywallHandler(h) {
4350
4324
  sessionManager.registerEmergencyPaywallHandler(h);
4351
4325
  }
4326
+ async getEmergencyPaywall() {
4327
+ if (!this.apiClient) throw new ClientError(CLIENT_ERROR_CODES.NOT_INITIALIZED, "SDK not initialized");
4328
+ return this.apiClient.getEmergencyPaywall();
4329
+ }
4352
4330
  async requireSubscription(paywallPlacement) {
4353
4331
  this.ensureInitialized();
4354
4332
  if (await this.hasActiveSubscription()) return true;
@@ -4893,7 +4871,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
4893
4871
  // src/hooks/useSubscriptionSync.ts
4894
4872
  var import_react17 = require("react");
4895
4873
  var CACHE_TTL_MS2 = 5 * 60 * 1e3;
4896
- var ACTIVE_STATUSES = ["active", "grace_period"];
4874
+ var ACTIVE_STATUSES = ["active", "in_grace_period"];
4897
4875
  function toDomainSubscriptionInfo(sub) {
4898
4876
  return {
4899
4877
  id: "",
@@ -4912,7 +4890,7 @@ function toDomainResponse(status) {
4912
4890
  return {
4913
4891
  hasActiveSubscription: status.hasActiveSubscription,
4914
4892
  subscription: status.subscription ? toDomainSubscriptionInfo(status.subscription) : null,
4915
- entitlements: []
4893
+ entitlements: status.entitlements ?? []
4916
4894
  };
4917
4895
  }
4918
4896
  function isSubscriptionStillActive(sub) {
@@ -4968,13 +4946,14 @@ function useSubscriptionSync() {
4968
4946
  const persisted = await subscriptionCache.get(distinctId);
4969
4947
  if (persisted && !persisted.isStale) {
4970
4948
  const persistedSub = persisted.data.subscription;
4949
+ const persistedStatus = persistedSub?.status;
4971
4950
  const sub = persistedSub ? {
4972
4951
  productId: persistedSub.productId,
4973
- status: persistedSub.status,
4952
+ status: persistedStatus,
4974
4953
  expiresAt: persistedSub.expiresAt ?? null,
4975
4954
  platform: persistedSub.platform,
4976
4955
  autoRenewEnabled: persistedSub.willRenew,
4977
- inGracePeriod: persistedSub.status === "grace_period"
4956
+ inGracePeriod: persistedStatus === "in_grace_period"
4978
4957
  } : null;
4979
4958
  const stillActive = isSubscriptionStillActive(sub);
4980
4959
  if (stillActive) {
@@ -5012,14 +4991,14 @@ function useSubscriptionSync() {
5012
4991
  const persisted = await subscriptionCache.get(distinctId);
5013
4992
  if (!persisted?.data.subscription) return null;
5014
4993
  const s = persisted.data.subscription;
5015
- const normalisedStatus = s.status === "grace_period" ? "in_grace_period" : s.status;
4994
+ const status = s.status;
5016
4995
  return {
5017
4996
  productId: s.productId,
5018
- status: normalisedStatus,
4997
+ status,
5019
4998
  expiresAt: s.expiresAt ?? null,
5020
4999
  platform: s.platform,
5021
5000
  autoRenewEnabled: s.willRenew,
5022
- inGracePeriod: normalisedStatus === "in_grace_period"
5001
+ inGracePeriod: status === "in_grace_period"
5023
5002
  };
5024
5003
  } catch {
5025
5004
  return null;
@@ -5445,10 +5424,7 @@ var Paywallo = PaywalloClient;
5445
5424
  var index_default = Paywallo;
5446
5425
  // Annotate the CommonJS export names for ESM import in node:
5447
5426
  0 && (module.exports = {
5448
- AFFILIATE_ERROR_CODES,
5449
5427
  ANALYTICS_ERROR_CODES,
5450
- AffiliateError,
5451
- AffiliateManager,
5452
5428
  AnalyticsError,
5453
5429
  ApiClient,
5454
5430
  CAMPAIGN_ERROR_CODES,
@@ -5466,7 +5442,6 @@ var index_default = Paywallo;
5466
5442
  PaywallError,
5467
5443
  PaywallModal,
5468
5444
  Paywallo,
5469
- PaywalloAffiliate,
5470
5445
  PaywalloClient,
5471
5446
  PaywalloContext,
5472
5447
  PaywalloError,
@@ -5476,7 +5451,6 @@ var index_default = Paywallo;
5476
5451
  SessionError,
5477
5452
  SessionManager,
5478
5453
  SubscriptionCache,
5479
- affiliateManager,
5480
5454
  createPurchaseError,
5481
5455
  detectDeviceLanguage,
5482
5456
  getCurrentLanguage,