@virex-tech/paywallo-sdk 1.3.0 → 1.4.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
@@ -1,3 +1,32 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ };
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+
11
+ // src/utils/uuid.ts
12
+ var uuid_exports = {};
13
+ __export(uuid_exports, {
14
+ generateUUID: () => generateUUID
15
+ });
16
+ function generateUUID() {
17
+ const template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
18
+ return template.replace(/[xy]/g, (c) => {
19
+ const r = Math.random() * 16 | 0;
20
+ const v = c === "x" ? r : r & 3 | 8;
21
+ return v.toString(16);
22
+ });
23
+ }
24
+ var init_uuid = __esm({
25
+ "src/utils/uuid.ts"() {
26
+ "use strict";
27
+ }
28
+ });
29
+
1
30
  // src/errors/PaywalloError.ts
2
31
  var PaywalloError = class extends Error {
3
32
  constructor(domain, code, message) {
@@ -102,6 +131,9 @@ function createPurchaseError(code, message) {
102
131
  // src/domains/iap/NativeStoreKit.ts
103
132
  import { NativeModules, Platform } from "react-native";
104
133
 
134
+ // src/domains/identity/IdentityManager.ts
135
+ import DeviceInfo from "react-native-device-info";
136
+
105
137
  // src/domains/identity/IdentityError.ts
106
138
  var IdentityError = class extends PaywalloError {
107
139
  constructor(code, message) {
@@ -117,29 +149,12 @@ var IDENTITY_ERROR_CODES = {
117
149
  IDENTIFY_FAILED: "IDENTITY_IDENTIFY_FAILED"
118
150
  };
119
151
 
120
- // src/utils/uuid.ts
121
- function generateUUID() {
122
- const template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
123
- return template.replace(/[xy]/g, (c) => {
124
- const r = Math.random() * 16 | 0;
125
- const v = c === "x" ? r : r & 3 | 8;
126
- return v.toString(16);
127
- });
128
- }
152
+ // src/domains/identity/IdentityManager.ts
153
+ init_uuid();
129
154
 
130
155
  // src/core/storage/SecureStorage.ts
131
156
  import AsyncStorage from "@react-native-async-storage/async-storage";
132
- var _keychain = void 0;
133
- async function loadKeychain() {
134
- if (_keychain !== void 0) return _keychain;
135
- try {
136
- const mod = await import("react-native-keychain");
137
- _keychain = mod.default;
138
- } catch {
139
- _keychain = null;
140
- }
141
- return _keychain;
142
- }
157
+ import Keychain from "react-native-keychain";
143
158
  var KEYCHAIN_SERVICE_PREFIX = "com.paywallo.sdk.";
144
159
  var SecureStorage = class {
145
160
  // eslint-disable-next-line @typescript-eslint/no-useless-constructor
@@ -183,13 +198,11 @@ var SecureStorage = class {
183
198
  }
184
199
  }
185
200
  hasKeychainSupport() {
186
- return _keychain !== null && _keychain !== void 0;
201
+ return true;
187
202
  }
188
203
  async getFromKeychain(key) {
189
- const keychain = await loadKeychain();
190
- if (!keychain) return null;
191
204
  try {
192
- const result = await keychain.getGenericPassword({ service: `${KEYCHAIN_SERVICE_PREFIX}${key}` });
205
+ const result = await Keychain.getGenericPassword({ service: `${KEYCHAIN_SERVICE_PREFIX}${key}` });
193
206
  if (result && result.password) {
194
207
  return result.password;
195
208
  }
@@ -199,20 +212,16 @@ var SecureStorage = class {
199
212
  }
200
213
  }
201
214
  async setToKeychain(key, value) {
202
- const keychain = await loadKeychain();
203
- if (!keychain) return false;
204
215
  try {
205
- await keychain.setGenericPassword("panel", value, { service: `${KEYCHAIN_SERVICE_PREFIX}${key}` });
216
+ await Keychain.setGenericPassword("panel", value, { service: `${KEYCHAIN_SERVICE_PREFIX}${key}` });
206
217
  return true;
207
218
  } catch {
208
219
  return false;
209
220
  }
210
221
  }
211
222
  async removeFromKeychain(key) {
212
- const keychain = await loadKeychain();
213
- if (!keychain) return false;
214
223
  try {
215
- await keychain.resetGenericPassword({ service: `${KEYCHAIN_SERVICE_PREFIX}${key}` });
224
+ await Keychain.resetGenericPassword({ service: `${KEYCHAIN_SERVICE_PREFIX}${key}` });
216
225
  return true;
217
226
  } catch {
218
227
  return false;
@@ -246,19 +255,8 @@ var IdentityManager = class {
246
255
  this.secureStorage = new SecureStorage(debug);
247
256
  await this.loadPersistedState();
248
257
  if (!this.deviceId) {
249
- let DeviceInfo = null;
250
258
  try {
251
- const mod = await import("react-native-device-info");
252
- DeviceInfo = mod.default;
253
- } catch {
254
- DeviceInfo = null;
255
- }
256
- try {
257
- if (DeviceInfo) {
258
- this.deviceId = await DeviceInfo.getUniqueId();
259
- } else {
260
- this.deviceId = generateUUID();
261
- }
259
+ this.deviceId = await DeviceInfo.getUniqueId();
262
260
  } catch {
263
261
  this.deviceId = generateUUID();
264
262
  }
@@ -487,20 +485,41 @@ var nativeStoreKit = {
487
485
  );
488
486
  }
489
487
  const distinctId = identityManager.getDistinctId();
490
- const result = await PaywalloStoreKitNative.purchase(productId, distinctId ?? null);
491
- if (!result || result.pending) {
492
- return null;
488
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
489
+ if (distinctId && !uuidRegex.test(distinctId) && PaywalloClient.getConfig()?.debug) {
490
+ console.warn("[Paywallo] distinctId is not a valid UUID for appAccountToken:", distinctId);
491
+ }
492
+ try {
493
+ const result = await PaywalloStoreKitNative.purchase(productId, distinctId ?? null);
494
+ if (!result || result.pending) {
495
+ return null;
496
+ }
497
+ return mapNativePurchase(result);
498
+ } catch (err) {
499
+ const message = err instanceof Error ? err.message : String(err);
500
+ if (message.toLowerCase().includes("cancel")) {
501
+ throw new PurchaseError(PURCHASE_ERROR_CODES.USER_CANCELLED, message, true);
502
+ }
503
+ throw new PurchaseError(PURCHASE_ERROR_CODES.PURCHASE_FAILED, message);
493
504
  }
494
- return mapNativePurchase(result);
495
505
  },
496
506
  async finishTransaction(transactionId) {
497
507
  if (!PaywalloStoreKitNative) return;
498
- await PaywalloStoreKitNative.finishTransaction(transactionId);
508
+ try {
509
+ await PaywalloStoreKitNative.finishTransaction(transactionId);
510
+ } catch (err) {
511
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] finishTransaction native error", err);
512
+ }
499
513
  },
500
514
  async getActiveTransactions() {
501
515
  if (!PaywalloStoreKitNative) return [];
502
- const results = await PaywalloStoreKitNative.getActiveTransactions();
503
- return results.map(mapNativePurchase);
516
+ try {
517
+ const results = await PaywalloStoreKitNative.getActiveTransactions();
518
+ return results.map(mapNativePurchase);
519
+ } catch (err) {
520
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] getActiveTransactions failed", err);
521
+ return [];
522
+ }
504
523
  }
505
524
  };
506
525
 
@@ -543,7 +562,8 @@ var IAPService = class {
543
562
  this.productsCache.set(product.productId, product);
544
563
  }
545
564
  return products;
546
- } catch (error) {
565
+ } catch (err) {
566
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] loadProducts failed", err);
547
567
  return [];
548
568
  }
549
569
  }
@@ -588,7 +608,8 @@ var IAPService = class {
588
608
  async finishTransaction(transactionId) {
589
609
  try {
590
610
  await nativeStoreKit.finishTransaction(transactionId);
591
- } catch (finishError) {
611
+ } catch (err) {
612
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] finishTransaction failed", err);
592
613
  }
593
614
  }
594
615
  async purchase(productId, options) {
@@ -603,23 +624,10 @@ var IAPService = class {
603
624
  if (!purchase) {
604
625
  return { success: false };
605
626
  }
606
- options?.onValidating?.();
607
- let validation = null;
608
- try {
609
- validation = await this.validateWithServer(purchase, productId, options);
610
- } catch (validationError) {
611
- return {
612
- success: false,
613
- error: new PurchaseError(PURCHASE_ERROR_CODES.VALIDATION_FAILED, validationError instanceof Error ? validationError.message : "Server validation failed")
614
- };
615
- }
616
- if (!validation?.success) {
617
- return {
618
- success: false,
619
- error: new PurchaseError(PURCHASE_ERROR_CODES.VALIDATION_FAILED, validation?.error ? `Server validation: ${validation.error}` : "Server validation failed")
620
- };
621
- }
622
627
  await this.finishTransaction(purchase.transactionId);
628
+ this.validateWithServer(purchase, productId, options).catch((err) => {
629
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] background validation failed", err);
630
+ });
623
631
  return { success: true, purchase };
624
632
  } catch (error) {
625
633
  const e = error instanceof PurchaseError ? error : new PurchaseError(PURCHASE_ERROR_CODES.PURCHASE_FAILED, error instanceof Error ? error.message : String(error));
@@ -657,13 +665,15 @@ var IAPService = class {
657
665
  if (r.status === "fulfilled" && r.value.success) {
658
666
  try {
659
667
  await nativeStoreKit.finishTransaction(tx.transactionId);
660
- } catch (finishError) {
668
+ } catch (err) {
669
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] finishTransaction failed", err);
661
670
  }
662
671
  validated.push(tx);
663
672
  }
664
673
  }
665
674
  return validated;
666
- } catch (error) {
675
+ } catch (err) {
676
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] restore failed", err);
667
677
  return [];
668
678
  }
669
679
  }
@@ -693,7 +703,8 @@ function usePaywallTracking() {
693
703
  ...opts.campaignId && { campaignId: opts.campaignId },
694
704
  ...sessionId && { sessionId }
695
705
  });
696
- } catch {
706
+ } catch (err) {
707
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] trackDismissed failed", err);
697
708
  }
698
709
  }, []);
699
710
  const trackProductSelected = useCallback((opts) => {
@@ -707,7 +718,8 @@ function usePaywallTracking() {
707
718
  ...opts.variantKey && { variantKey: opts.variantKey },
708
719
  ...opts.campaignId && { campaignId: opts.campaignId },
709
720
  ...sessionId && { sessionId }
710
- }).catch(() => {
721
+ }).catch((err) => {
722
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] trackProductSelected failed", err);
711
723
  });
712
724
  }, []);
713
725
  const trackPurchased = useCallback(async (opts) => {
@@ -723,7 +735,8 @@ function usePaywallTracking() {
723
735
  ...opts.campaignId && { campaignId: opts.campaignId },
724
736
  ...sessionId && { sessionId }
725
737
  });
726
- } catch {
738
+ } catch (err) {
739
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] trackPurchased failed", err);
727
740
  }
728
741
  }, []);
729
742
  return { trackDismissed, trackProductSelected, trackPurchased };
@@ -863,7 +876,8 @@ function usePaywallLoader(placement, visible, preloadedConfig, preloadedProducts
863
876
  const map = /* @__PURE__ */ new Map();
864
877
  for (const p of storeProducts) map.set(p.productId, p);
865
878
  setProducts(map);
866
- } catch {
879
+ } catch (err) {
880
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] loadProducts in paywall failed", err);
867
881
  setProducts(/* @__PURE__ */ new Map());
868
882
  }
869
883
  } else {
@@ -1398,7 +1412,12 @@ var PaywallErrorBoundary = class extends Component {
1398
1412
  static getDerivedStateFromError() {
1399
1413
  return { hasError: true };
1400
1414
  }
1401
- componentDidCatch(_error) {
1415
+ componentDidCatch(error) {
1416
+ if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] Paywall render error:", error);
1417
+ try {
1418
+ PaywalloClient.getApiClient()?.reportError("PAYWALL_RENDER_ERROR", error.message);
1419
+ } catch {
1420
+ }
1402
1421
  }
1403
1422
  render() {
1404
1423
  return this.state.hasError ? null : this.props.children;
@@ -1678,7 +1697,8 @@ var AffiliateManager = class {
1678
1697
  );
1679
1698
  }
1680
1699
  }
1681
- log(..._args) {
1700
+ log(...args) {
1701
+ if (this.debug) console.log("[Paywallo:Affiliate]", ...args);
1682
1702
  }
1683
1703
  };
1684
1704
  var affiliateManager = new AffiliateManager();
@@ -1946,7 +1966,8 @@ var SubscriptionManagerClass = class {
1946
1966
  await this.cache.invalidate(this.cacheKey());
1947
1967
  await this.getSubscriptionStatus(true);
1948
1968
  }
1949
- log(..._args) {
1969
+ log(...args) {
1970
+ if (this.config?.debug) console.log("[Paywallo:Subscription]", ...args);
1950
1971
  }
1951
1972
  };
1952
1973
  var subscriptionManager = new SubscriptionManagerClass();
@@ -1999,12 +2020,18 @@ async function validatePurchase(client, appKey, platform, receipt, productId, tr
1999
2020
  },
2000
2021
  false
2001
2022
  );
2023
+ if (!response.ok) {
2024
+ throw new Error(`Server validation failed: HTTP ${response.status}`);
2025
+ }
2002
2026
  return response.data;
2003
2027
  }
2004
2028
  async function getSubscriptionStatus(client, baseUrl, appKey, distinctId) {
2005
2029
  const url = new URL(`${baseUrl}/purchases/status/${appKey}`);
2006
2030
  url.searchParams.set("distinctId", distinctId);
2007
2031
  const response = await client.get(url.toString());
2032
+ if (!response.ok) {
2033
+ throw new Error(`Subscription status check failed: HTTP ${response.status}`);
2034
+ }
2008
2035
  return response.data;
2009
2036
  }
2010
2037
  async function getEmergencyPaywall(client) {
@@ -2184,7 +2211,8 @@ var HttpClient = class {
2184
2211
  sleep(ms) {
2185
2212
  return new Promise((resolve) => setTimeout(resolve, ms));
2186
2213
  }
2187
- log(..._args) {
2214
+ log(...args) {
2215
+ if (this.config.debug) console.log("[Paywallo:Http]", ...args);
2188
2216
  }
2189
2217
  setDebug(debug) {
2190
2218
  this.config.debug = debug;
@@ -2210,8 +2238,6 @@ var NetworkMonitorClass = class {
2210
2238
  this.config = DEFAULT_NETWORK_CONFIG;
2211
2239
  this.listeners = /* @__PURE__ */ new Set();
2212
2240
  this.currentState = "unknown";
2213
- this.netInfoModule = null;
2214
- this.netInfoSubscription = null;
2215
2241
  this.appStateSubscription = null;
2216
2242
  this.initialized = false;
2217
2243
  this.checkIntervalId = null;
@@ -2227,13 +2253,6 @@ var NetworkMonitorClass = class {
2227
2253
  void this.setupFallbackDetection();
2228
2254
  this.setupAppStateListener();
2229
2255
  }
2230
- setupNetInfoListener() {
2231
- if (!this.netInfoModule) return;
2232
- this.netInfoSubscription = this.netInfoModule.addEventListener((state) => {
2233
- const newState = this.netInfoStateToNetworkState(state);
2234
- this.updateState(newState);
2235
- });
2236
- }
2237
2256
  async setupFallbackDetection() {
2238
2257
  await this.checkConnectivity();
2239
2258
  this.checkIntervalId = setInterval(() => {
@@ -2261,15 +2280,6 @@ var NetworkMonitorClass = class {
2261
2280
  this.updateState("offline");
2262
2281
  }
2263
2282
  }
2264
- netInfoStateToNetworkState(state) {
2265
- if (state.isConnected === null) {
2266
- return "unknown";
2267
- }
2268
- if (!state.isConnected) {
2269
- return "offline";
2270
- }
2271
- return "online";
2272
- }
2273
2283
  updateState(newState) {
2274
2284
  const previousState = this.currentState;
2275
2285
  if (previousState === newState) {
@@ -2312,10 +2322,6 @@ var NetworkMonitorClass = class {
2312
2322
  return this.currentState;
2313
2323
  }
2314
2324
  dispose() {
2315
- if (this.netInfoSubscription) {
2316
- this.netInfoSubscription();
2317
- this.netInfoSubscription = null;
2318
- }
2319
2325
  if (this.appStateSubscription) {
2320
2326
  this.appStateSubscription.remove();
2321
2327
  this.appStateSubscription = null;
@@ -2328,7 +2334,8 @@ var NetworkMonitorClass = class {
2328
2334
  this.initialized = false;
2329
2335
  this.currentState = "unknown";
2330
2336
  }
2331
- log(..._args) {
2337
+ log(...args) {
2338
+ if (this.config.debug) console.log("[Paywallo:Network]", ...args);
2332
2339
  }
2333
2340
  setDebug(debug) {
2334
2341
  this.config.debug = debug;
@@ -2338,6 +2345,7 @@ var networkMonitor = new NetworkMonitorClass();
2338
2345
 
2339
2346
  // src/core/queue/OfflineQueue.ts
2340
2347
  import AsyncStorage2 from "@react-native-async-storage/async-storage";
2348
+ init_uuid();
2341
2349
 
2342
2350
  // src/core/queue/deduplication.ts
2343
2351
  function normalizeForComparison(obj) {
@@ -2575,7 +2583,8 @@ var OfflineQueueClass = class {
2575
2583
  }
2576
2584
  }
2577
2585
  }
2578
- log(..._args) {
2586
+ log(...args) {
2587
+ if (this.config.debug) console.log("[Paywallo:Queue]", ...args);
2579
2588
  }
2580
2589
  setDebug(debug) {
2581
2590
  this.config.debug = debug;
@@ -2717,7 +2726,8 @@ var QueueProcessorClass = class {
2717
2726
  this.httpClient = null;
2718
2727
  this.initialized = false;
2719
2728
  }
2720
- log(..._args) {
2729
+ log(...args) {
2730
+ if (this.config.debug) console.log("[Paywallo:QueueProcessor]", ...args);
2721
2731
  }
2722
2732
  setDebug(debug) {
2723
2733
  this.config.debug = debug;
@@ -2799,13 +2809,12 @@ var ApiCache = class {
2799
2809
  };
2800
2810
 
2801
2811
  // src/core/ApiClient.ts
2802
- var SDK_VERSION = "1.0.0";
2803
- var DEFAULT_BASE_URL = "https://panel.lucasqueiroga.shop";
2812
+ var SDK_VERSION = "1.4.0";
2804
2813
  var _ApiClient = class _ApiClient {
2805
- constructor(appKey, debug = false, environment = "Production", offlineQueueEnabled = true, timeout = 3e4) {
2814
+ constructor(appKey, apiUrl, debug = false, environment = "Production", offlineQueueEnabled = true, timeout = 3e4) {
2806
2815
  this.cache = new ApiCache();
2807
2816
  this.appKey = appKey;
2808
- this.baseUrl = DEFAULT_BASE_URL;
2817
+ this.baseUrl = apiUrl;
2809
2818
  this.webUrl = deriveWebUrl(this.baseUrl);
2810
2819
  this.debug = debug;
2811
2820
  this.environment = environment;
@@ -2858,7 +2867,8 @@ var _ApiClient = class _ApiClient {
2858
2867
  sdkVersion: SDK_VERSION,
2859
2868
  platform: Platform5.OS
2860
2869
  }, true);
2861
- } catch {
2870
+ } catch (err) {
2871
+ if (this.debug) console.warn("[Paywallo:ApiClient] reportError failed", err);
2862
2872
  }
2863
2873
  }
2864
2874
  async trackEvent(eventName, distinctId, properties, timestamp) {
@@ -3090,7 +3100,8 @@ var _ApiClient = class _ApiClient {
3090
3100
  try {
3091
3101
  const value = JSON.stringify({ variant: flag.variant, payload: flag.payload, cachedAt: Date.now() });
3092
3102
  await secureStorage.set(`flag:${flagKey}:${distinctId}`, value);
3093
- } catch {
3103
+ } catch (err) {
3104
+ this.log("writeFlagToStorage failed", err);
3094
3105
  }
3095
3106
  }
3096
3107
  async postWithQueue(url, payload, label) {
@@ -3111,7 +3122,8 @@ var _ApiClient = class _ApiClient {
3111
3122
  throw error;
3112
3123
  }
3113
3124
  }
3114
- log(..._args) {
3125
+ log(...args) {
3126
+ if (this.debug) console.log("[Paywallo:ApiClient]", ...args);
3115
3127
  }
3116
3128
  };
3117
3129
  _ApiClient.FLAG_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
@@ -3279,36 +3291,60 @@ var AdvertisingIdManager = class {
3279
3291
  }
3280
3292
  };
3281
3293
 
3282
- // src/domains/identity/FacebookIdManager.ts
3294
+ // src/domains/identity/InstallTracker.ts
3295
+ import { Dimensions as Dimensions3, Platform as Platform8 } from "react-native";
3296
+ import AsyncStorage3 from "@react-native-async-storage/async-storage";
3297
+ import RNDeviceInfo from "react-native-device-info";
3298
+
3299
+ // src/domains/identity/MetaBridge.ts
3300
+ import { NativeModules as NativeModules4 } from "react-native";
3283
3301
  var TIMEOUT_MS = 2e3;
3284
- var FacebookIdManager = class {
3285
- async getAnonId() {
3302
+ var NativeBridge = NativeModules4.PaywalloFBBridge ?? null;
3303
+ var MetaBridge = class {
3304
+ async getAnonymousID() {
3305
+ if (!NativeBridge) return null;
3286
3306
  try {
3287
3307
  let timerId;
3288
- const timeoutPromise = new Promise((resolve) => {
3308
+ const timeout = new Promise((resolve) => {
3289
3309
  timerId = setTimeout(() => resolve(null), TIMEOUT_MS);
3290
3310
  });
3291
- const result = await Promise.race([this.fetchAnonId(), timeoutPromise]);
3311
+ const result = await Promise.race([NativeBridge.getAnonymousID(), timeout]);
3292
3312
  clearTimeout(timerId);
3293
3313
  return result;
3294
3314
  } catch {
3295
3315
  return null;
3296
3316
  }
3297
3317
  }
3298
- async fetchAnonId() {
3318
+ async logEvent(name, params = {}) {
3319
+ if (!NativeBridge) return;
3299
3320
  try {
3300
- const fbsdk = await import("react-native-fbsdk-next");
3301
- const anonId = await fbsdk.AppEventsLogger.getAnonymousID();
3302
- return anonId || null;
3321
+ await NativeBridge.logEvent(name, params);
3322
+ } catch {
3323
+ }
3324
+ }
3325
+ async logPurchase(amount, currency, params = {}) {
3326
+ if (!NativeBridge) return;
3327
+ try {
3328
+ await NativeBridge.logPurchase(amount, currency, params);
3329
+ } catch {
3330
+ }
3331
+ }
3332
+ setUserID(userId) {
3333
+ if (!NativeBridge) return;
3334
+ try {
3335
+ NativeBridge.setUserID(userId);
3336
+ } catch {
3337
+ }
3338
+ }
3339
+ flush() {
3340
+ if (!NativeBridge) return;
3341
+ try {
3342
+ NativeBridge.flush();
3303
3343
  } catch {
3304
- return null;
3305
3344
  }
3306
3345
  }
3307
3346
  };
3308
- var facebookIdManager = new FacebookIdManager();
3309
-
3310
- // src/domains/identity/InstallTracker.ts
3311
- import { Dimensions as Dimensions3, Platform as Platform8 } from "react-native";
3347
+ var metaBridge = new MetaBridge();
3312
3348
 
3313
3349
  // src/domains/identity/InstallReferrerManager.ts
3314
3350
  import { Platform as Platform7 } from "react-native";
@@ -3376,6 +3412,7 @@ var installReferrerManager = new InstallReferrerManager();
3376
3412
 
3377
3413
  // src/domains/session/SessionManager.ts
3378
3414
  import { AppState as AppState2 } from "react-native";
3415
+ init_uuid();
3379
3416
 
3380
3417
  // src/domains/session/sessionTracking.ts
3381
3418
  async function trackSessionStart(apiClient, sessionId, sessionStartedAt) {
@@ -3604,7 +3641,8 @@ var SessionManager = class {
3604
3641
  );
3605
3642
  }
3606
3643
  }
3607
- log(..._args) {
3644
+ log(...args) {
3645
+ if (this.debug) console.log("[Paywallo:Session]", ...args);
3608
3646
  }
3609
3647
  };
3610
3648
  var sessionManager = new SessionManager();
@@ -3617,11 +3655,10 @@ var InstallTracker = class {
3617
3655
  }
3618
3656
  async trackIfNeeded(apiClient, requestATT = false) {
3619
3657
  const storage = new SecureStorage(this.debug);
3620
- const alreadyTracked = await storage.get("install_tracked");
3658
+ const alreadyTracked = await storage.get("@panel:install_tracked");
3621
3659
  if (alreadyTracked) return;
3622
3660
  let fallbackTracked = null;
3623
3661
  try {
3624
- const AsyncStorage3 = (await import("@react-native-async-storage/async-storage")).default;
3625
3662
  fallbackTracked = await AsyncStorage3.getItem("@paywallo:install_tracked");
3626
3663
  } catch {
3627
3664
  }
@@ -3630,13 +3667,7 @@ var InstallTracker = class {
3630
3667
  const sessionId = sessionManager.getSessionId();
3631
3668
  const installedAt = Date.now();
3632
3669
  if (!distinctId) return;
3633
- let deviceInfo = null;
3634
- try {
3635
- const mod = await import("react-native-device-info");
3636
- deviceInfo = mod.default;
3637
- } catch {
3638
- deviceInfo = null;
3639
- }
3670
+ const deviceInfo = RNDeviceInfo;
3640
3671
  let deviceData = {};
3641
3672
  try {
3642
3673
  const screen = Dimensions3.get("screen");
@@ -3648,6 +3679,38 @@ var InstallTracker = class {
3648
3679
  const osVersion = deviceInfo?.getSystemVersion() || "unknown";
3649
3680
  const appVersion = deviceInfo?.getVersion() || "unknown";
3650
3681
  const buildNumber = deviceInfo?.getBuildNumber() || "0";
3682
+ let carrier = "unknown";
3683
+ try {
3684
+ carrier = await deviceInfo?.getCarrier?.() ?? "unknown";
3685
+ } catch {
3686
+ }
3687
+ let totalDisk = 0;
3688
+ try {
3689
+ totalDisk = Number(await deviceInfo?.getTotalDiskCapacity?.()) || 0;
3690
+ } catch {
3691
+ }
3692
+ let freeDisk = 0;
3693
+ try {
3694
+ freeDisk = Number(await deviceInfo?.getFreeDiskStorage?.()) || 0;
3695
+ } catch {
3696
+ }
3697
+ let totalRam = 0;
3698
+ try {
3699
+ totalRam = Number(await deviceInfo?.getTotalMemory?.()) || 0;
3700
+ } catch {
3701
+ }
3702
+ let brand = "unknown";
3703
+ try {
3704
+ brand = deviceInfo?.getBrand?.() ?? "unknown";
3705
+ } catch {
3706
+ }
3707
+ let installerPackage = null;
3708
+ try {
3709
+ if (Platform8.OS === "android") {
3710
+ installerPackage = await deviceInfo?.getInstallerPackageName?.() ?? null;
3711
+ }
3712
+ } catch {
3713
+ }
3651
3714
  deviceData = {
3652
3715
  deviceModel,
3653
3716
  osVersion,
@@ -3656,22 +3719,40 @@ var InstallTracker = class {
3656
3719
  ...screenWidth > 0 && { screenWidth },
3657
3720
  ...screenHeight > 0 && { screenHeight },
3658
3721
  timezone,
3659
- locale
3722
+ locale,
3723
+ ...carrier !== "unknown" && { carrier },
3724
+ ...totalDisk > 0 && { totalDisk },
3725
+ ...freeDisk > 0 && { freeDisk },
3726
+ ...totalRam > 0 && { totalRam },
3727
+ ...brand !== "unknown" && { brand },
3728
+ ...installerPackage && { installerPackage }
3660
3729
  };
3661
3730
  } catch {
3662
3731
  }
3663
3732
  const [fbAnonId, referrer, adIds] = await Promise.all([
3664
- facebookIdManager.getAnonId(),
3733
+ metaBridge.getAnonymousID(),
3665
3734
  installReferrerManager.getReferrer(),
3666
3735
  this.advertisingIdManager.collect(requestATT)
3667
3736
  ]);
3737
+ let effectiveAnonId = fbAnonId;
3738
+ if (!effectiveAnonId) {
3739
+ const stored = await storage.get("@panel:anon_id");
3740
+ if (stored) {
3741
+ effectiveAnonId = stored;
3742
+ } else {
3743
+ const { generateUUID: generateUUID2 } = await Promise.resolve().then(() => (init_uuid(), uuid_exports));
3744
+ effectiveAnonId = `PW_${generateUUID2()}`;
3745
+ await storage.set("@panel:anon_id", effectiveAnonId).catch(() => {
3746
+ });
3747
+ }
3748
+ }
3668
3749
  try {
3669
3750
  await apiClient.trackEvent("$app_installed", distinctId, {
3670
3751
  installedAt,
3671
3752
  platform: Platform8.OS,
3672
3753
  ...sessionId && { sessionId },
3673
3754
  ...deviceData,
3674
- ...fbAnonId && { fbAnonId },
3755
+ ...effectiveAnonId && { fbAnonId: effectiveAnonId },
3675
3756
  ...referrer.rawReferrer && { installReferrer: referrer.rawReferrer },
3676
3757
  ...referrer.trackingId && { referrerTrackingId: referrer.trackingId },
3677
3758
  ...referrer.fbclid && { referrerFbclid: referrer.fbclid },
@@ -3683,15 +3764,49 @@ var InstallTracker = class {
3683
3764
  ...adIds.gaid && { gaid: adIds.gaid },
3684
3765
  attStatus: adIds.attStatus
3685
3766
  });
3686
- const stored = await storage.set("install_tracked", String(installedAt));
3767
+ if (Platform8.OS === "ios") {
3768
+ void this.callDeferredMatch(apiClient, deviceData, adIds, effectiveAnonId).catch(() => {
3769
+ });
3770
+ }
3771
+ await storage.set("@panel:install_tracked", String(installedAt));
3687
3772
  try {
3688
- const AsyncStorage3 = (await import("@react-native-async-storage/async-storage")).default;
3689
3773
  await AsyncStorage3.setItem("@paywallo:install_tracked", String(installedAt));
3690
3774
  } catch {
3691
3775
  }
3692
3776
  } catch {
3693
3777
  }
3694
3778
  }
3779
+ async callDeferredMatch(apiClient, deviceData, adIds, anonId) {
3780
+ const storage = new SecureStorage(this.debug);
3781
+ const alreadyMatched = await storage.get("@panel:deferred_match_done");
3782
+ if (alreadyMatched) return;
3783
+ const controller = new AbortController();
3784
+ const timer = setTimeout(() => controller.abort(), 5e3);
3785
+ try {
3786
+ const baseUrl = apiClient.getBaseUrl();
3787
+ const appKey = apiClient.appKey;
3788
+ await fetch(`${baseUrl}/api/v1/attribution/deferred-match`, {
3789
+ method: "POST",
3790
+ headers: { "Content-Type": "application/json", "X-App-Key": appKey },
3791
+ body: JSON.stringify({
3792
+ idfv: adIds.idfv,
3793
+ deviceModel: deviceData.deviceModel,
3794
+ osVersion: deviceData.osVersion,
3795
+ screenWidth: deviceData.screenWidth,
3796
+ screenHeight: deviceData.screenHeight,
3797
+ timezone: deviceData.timezone,
3798
+ locale: deviceData.locale,
3799
+ fbAnonId: anonId
3800
+ }),
3801
+ signal: controller.signal
3802
+ });
3803
+ await storage.set("@panel:deferred_match_done", "1").catch(() => {
3804
+ });
3805
+ } catch {
3806
+ } finally {
3807
+ clearTimeout(timer);
3808
+ }
3809
+ }
3695
3810
  };
3696
3811
 
3697
3812
  // src/core/PaywalloClient.ts
@@ -3783,9 +3898,12 @@ var _PaywalloClientClass = class _PaywalloClientClass {
3783
3898
  this.networkCleanup = networkMonitor.addListener((state) => {
3784
3899
  if (state !== "online") return;
3785
3900
  if (this.config && this.apiClient) return;
3901
+ if (this.initPromise) return;
3786
3902
  this.initPromise = this.initWithRetry(config);
3787
3903
  this.initPromise.catch(() => {
3788
3904
  this.reportInitFailure(config, null, "NETWORK_RECOVERY_FAILED");
3905
+ }).finally(() => {
3906
+ if (!this.isReady()) this.initPromise = null;
3789
3907
  });
3790
3908
  });
3791
3909
  }
@@ -3797,12 +3915,13 @@ var _PaywalloClientClass = class _PaywalloClientClass {
3797
3915
  }
3798
3916
  reportInitFailure(config, error, reason) {
3799
3917
  try {
3800
- const tempClient = new ApiClient(config.appKey, config.debug);
3918
+ const tempClient = new ApiClient(config.appKey, config.apiUrl ?? "https://paywallo.com.br", config.debug);
3801
3919
  void tempClient.reportError("INIT_FAILED", reason, {
3802
3920
  attempts: this.initAttempts,
3803
3921
  error: error instanceof Error ? error.message : String(error)
3804
3922
  });
3805
- } catch {
3923
+ } catch (err) {
3924
+ if (config.debug) console.warn("[Paywallo] reportInitFailure error", err);
3806
3925
  }
3807
3926
  }
3808
3927
  reportInitSuccess(config, attempts) {
@@ -3810,7 +3929,8 @@ var _PaywalloClientClass = class _PaywalloClientClass {
3810
3929
  if (this.apiClient) {
3811
3930
  void this.apiClient.reportError("INIT_RECOVERED", `Succeeded after ${attempts} retries`, { attempts });
3812
3931
  }
3813
- } catch {
3932
+ } catch (err) {
3933
+ if (config.debug) console.warn("[Paywallo] reportInitSuccess error", err);
3814
3934
  }
3815
3935
  }
3816
3936
  async resolveSessionFlags(keys, apiClient, distinctId) {
@@ -3835,7 +3955,7 @@ var _PaywalloClientClass = class _PaywalloClientClass {
3835
3955
  () => offlineQueue.clearItemsWithInvalidAppKey(config.appKey)
3836
3956
  )
3837
3957
  ]);
3838
- this.apiClient = new ApiClient(config.appKey, config.debug, environment, offlineQueueEnabled, config.timeout);
3958
+ this.apiClient = new ApiClient(config.appKey, config.apiUrl ?? "https://paywallo.com.br", config.debug, environment, offlineQueueEnabled, config.timeout);
3839
3959
  queueProcessor.initialize(this.apiClient.getHttpClient(), { debug: config.debug });
3840
3960
  affiliateManager.initialize(this.apiClient, config.debug);
3841
3961
  subscriptionManager.init({
@@ -4320,9 +4440,7 @@ function usePurchase() {
4320
4440
  setError(null);
4321
4441
  try {
4322
4442
  const iapService = getIAPService();
4323
- const result = await iapService.purchase(productId, {
4324
- onValidating: () => setState("validating")
4325
- });
4443
+ const result = await iapService.purchase(productId);
4326
4444
  if (result.success && result.purchase) {
4327
4445
  setState("idle");
4328
4446
  return {
@@ -4378,9 +4496,7 @@ function usePurchase() {
4378
4496
  purchase,
4379
4497
  restore,
4380
4498
  error,
4381
- isLoading: state === "loading",
4382
- isPurchasing: state === "purchasing" || state === "validating",
4383
- isValidating: state === "validating",
4499
+ isPurchasing: state === "purchasing",
4384
4500
  isRestoring: state === "restoring"
4385
4501
  };
4386
4502
  }
@@ -4421,6 +4537,9 @@ function useSubscription() {
4421
4537
  void subscriptionManager.getSubscriptionStatus().then((s) => {
4422
4538
  setStatus(s);
4423
4539
  setIsLoading(false);
4540
+ }).catch((err) => {
4541
+ setError(err instanceof Error ? err : new Error(String(err)));
4542
+ setIsLoading(false);
4424
4543
  });
4425
4544
  const removeListener = subscriptionManager.addListener((newStatus) => {
4426
4545
  setStatus(newStatus);
@@ -4600,7 +4719,8 @@ function useProductLoader() {
4600
4719
  const map = /* @__PURE__ */ new Map();
4601
4720
  for (const p of loaded) map.set(p.productId, p);
4602
4721
  setProducts(map);
4603
- } catch (error) {
4722
+ } catch (err) {
4723
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] refreshProducts failed", err);
4604
4724
  } finally {
4605
4725
  setIsLoadingProducts(false);
4606
4726
  }
@@ -4640,6 +4760,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
4640
4760
  clearPreloadedWebView();
4641
4761
  resolvePreloadedPaywall({ presented: true, purchased: true, cancelled: false, restored: false });
4642
4762
  } catch (error) {
4763
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] preloaded purchase failed", error);
4643
4764
  setShowingPreloadedWebView(false);
4644
4765
  clearPreloadedWebView();
4645
4766
  resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: false });
@@ -4660,6 +4781,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
4660
4781
  clearPreloadedWebView();
4661
4782
  resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: true });
4662
4783
  } catch (error) {
4784
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] preloaded restore failed", error);
4663
4785
  setShowingPreloadedWebView(false);
4664
4786
  clearPreloadedWebView();
4665
4787
  resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: false });
@@ -5236,6 +5358,7 @@ export {
5236
5358
  IDENTITY_ERROR_CODES,
5237
5359
  IdentityError,
5238
5360
  IdentityManager,
5361
+ MetaBridge,
5239
5362
  PAYWALL_ERROR_CODES,
5240
5363
  PURCHASE_ERROR_CODES,
5241
5364
  PaywallContext,
@@ -5263,6 +5386,7 @@ export {
5263
5386
  hasVariables,
5264
5387
  identityManager,
5265
5388
  initLocalization,
5389
+ metaBridge,
5266
5390
  nativeStoreKit,
5267
5391
  networkMonitor,
5268
5392
  offlineQueue,