@virex-tech/paywallo-sdk 1.3.0 → 1.4.1

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
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
8
11
  var __export = (target, all) => {
9
12
  for (var name in all)
10
13
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -27,6 +30,25 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
30
  ));
28
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
32
 
33
+ // src/utils/uuid.ts
34
+ var uuid_exports = {};
35
+ __export(uuid_exports, {
36
+ generateUUID: () => generateUUID
37
+ });
38
+ function generateUUID() {
39
+ const template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
40
+ return template.replace(/[xy]/g, (c) => {
41
+ const r = Math.random() * 16 | 0;
42
+ const v = c === "x" ? r : r & 3 | 8;
43
+ return v.toString(16);
44
+ });
45
+ }
46
+ var init_uuid = __esm({
47
+ "src/utils/uuid.ts"() {
48
+ "use strict";
49
+ }
50
+ });
51
+
30
52
  // src/index.ts
31
53
  var index_exports = {};
32
54
  __export(index_exports, {
@@ -44,6 +66,7 @@ __export(index_exports, {
44
66
  IDENTITY_ERROR_CODES: () => IDENTITY_ERROR_CODES,
45
67
  IdentityError: () => IdentityError,
46
68
  IdentityManager: () => IdentityManager,
69
+ MetaBridge: () => MetaBridge,
47
70
  PAYWALL_ERROR_CODES: () => PAYWALL_ERROR_CODES,
48
71
  PURCHASE_ERROR_CODES: () => PURCHASE_ERROR_CODES,
49
72
  PaywallContext: () => PaywallContext,
@@ -71,6 +94,7 @@ __export(index_exports, {
71
94
  hasVariables: () => hasVariables,
72
95
  identityManager: () => identityManager,
73
96
  initLocalization: () => initLocalization,
97
+ metaBridge: () => metaBridge,
74
98
  nativeStoreKit: () => nativeStoreKit,
75
99
  networkMonitor: () => networkMonitor,
76
100
  offlineQueue: () => offlineQueue,
@@ -184,6 +208,9 @@ function createPurchaseError(code, message) {
184
208
  // src/domains/iap/NativeStoreKit.ts
185
209
  var import_react_native = require("react-native");
186
210
 
211
+ // src/domains/identity/IdentityManager.ts
212
+ var import_react_native_device_info = __toESM(require("react-native-device-info"));
213
+
187
214
  // src/domains/identity/IdentityError.ts
188
215
  var IdentityError = class extends PaywalloError {
189
216
  constructor(code, message) {
@@ -199,29 +226,12 @@ var IDENTITY_ERROR_CODES = {
199
226
  IDENTIFY_FAILED: "IDENTITY_IDENTIFY_FAILED"
200
227
  };
201
228
 
202
- // src/utils/uuid.ts
203
- function generateUUID() {
204
- const template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
205
- return template.replace(/[xy]/g, (c) => {
206
- const r = Math.random() * 16 | 0;
207
- const v = c === "x" ? r : r & 3 | 8;
208
- return v.toString(16);
209
- });
210
- }
229
+ // src/domains/identity/IdentityManager.ts
230
+ init_uuid();
211
231
 
212
232
  // src/core/storage/SecureStorage.ts
213
233
  var import_async_storage = __toESM(require("@react-native-async-storage/async-storage"));
214
- var _keychain = void 0;
215
- async function loadKeychain() {
216
- if (_keychain !== void 0) return _keychain;
217
- try {
218
- const mod = await import("react-native-keychain");
219
- _keychain = mod.default;
220
- } catch {
221
- _keychain = null;
222
- }
223
- return _keychain;
224
- }
234
+ var import_react_native_keychain = __toESM(require("react-native-keychain"));
225
235
  var KEYCHAIN_SERVICE_PREFIX = "com.paywallo.sdk.";
226
236
  var SecureStorage = class {
227
237
  // eslint-disable-next-line @typescript-eslint/no-useless-constructor
@@ -265,13 +275,11 @@ var SecureStorage = class {
265
275
  }
266
276
  }
267
277
  hasKeychainSupport() {
268
- return _keychain !== null && _keychain !== void 0;
278
+ return true;
269
279
  }
270
280
  async getFromKeychain(key) {
271
- const keychain = await loadKeychain();
272
- if (!keychain) return null;
273
281
  try {
274
- const result = await keychain.getGenericPassword({ service: `${KEYCHAIN_SERVICE_PREFIX}${key}` });
282
+ const result = await import_react_native_keychain.default.getGenericPassword({ service: `${KEYCHAIN_SERVICE_PREFIX}${key}` });
275
283
  if (result && result.password) {
276
284
  return result.password;
277
285
  }
@@ -281,20 +289,16 @@ var SecureStorage = class {
281
289
  }
282
290
  }
283
291
  async setToKeychain(key, value) {
284
- const keychain = await loadKeychain();
285
- if (!keychain) return false;
286
292
  try {
287
- await keychain.setGenericPassword("panel", value, { service: `${KEYCHAIN_SERVICE_PREFIX}${key}` });
293
+ await import_react_native_keychain.default.setGenericPassword("panel", value, { service: `${KEYCHAIN_SERVICE_PREFIX}${key}` });
288
294
  return true;
289
295
  } catch {
290
296
  return false;
291
297
  }
292
298
  }
293
299
  async removeFromKeychain(key) {
294
- const keychain = await loadKeychain();
295
- if (!keychain) return false;
296
300
  try {
297
- await keychain.resetGenericPassword({ service: `${KEYCHAIN_SERVICE_PREFIX}${key}` });
301
+ await import_react_native_keychain.default.resetGenericPassword({ service: `${KEYCHAIN_SERVICE_PREFIX}${key}` });
298
302
  return true;
299
303
  } catch {
300
304
  return false;
@@ -328,19 +332,8 @@ var IdentityManager = class {
328
332
  this.secureStorage = new SecureStorage(debug);
329
333
  await this.loadPersistedState();
330
334
  if (!this.deviceId) {
331
- let DeviceInfo = null;
332
335
  try {
333
- const mod = await import("react-native-device-info");
334
- DeviceInfo = mod.default;
335
- } catch {
336
- DeviceInfo = null;
337
- }
338
- try {
339
- if (DeviceInfo) {
340
- this.deviceId = await DeviceInfo.getUniqueId();
341
- } else {
342
- this.deviceId = generateUUID();
343
- }
336
+ this.deviceId = await import_react_native_device_info.default.getUniqueId();
344
337
  } catch {
345
338
  this.deviceId = generateUUID();
346
339
  }
@@ -569,20 +562,41 @@ var nativeStoreKit = {
569
562
  );
570
563
  }
571
564
  const distinctId = identityManager.getDistinctId();
572
- const result = await PaywalloStoreKitNative.purchase(productId, distinctId ?? null);
573
- if (!result || result.pending) {
574
- return null;
565
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
566
+ if (distinctId && !uuidRegex.test(distinctId) && PaywalloClient.getConfig()?.debug) {
567
+ console.warn("[Paywallo] distinctId is not a valid UUID for appAccountToken:", distinctId);
568
+ }
569
+ try {
570
+ const result = await PaywalloStoreKitNative.purchase(productId, distinctId ?? null);
571
+ if (!result || result.pending) {
572
+ return null;
573
+ }
574
+ return mapNativePurchase(result);
575
+ } catch (err) {
576
+ const message = err instanceof Error ? err.message : String(err);
577
+ if (message.toLowerCase().includes("cancel")) {
578
+ throw new PurchaseError(PURCHASE_ERROR_CODES.USER_CANCELLED, message, true);
579
+ }
580
+ throw new PurchaseError(PURCHASE_ERROR_CODES.PURCHASE_FAILED, message);
575
581
  }
576
- return mapNativePurchase(result);
577
582
  },
578
583
  async finishTransaction(transactionId) {
579
584
  if (!PaywalloStoreKitNative) return;
580
- await PaywalloStoreKitNative.finishTransaction(transactionId);
585
+ try {
586
+ await PaywalloStoreKitNative.finishTransaction(transactionId);
587
+ } catch (err) {
588
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] finishTransaction native error", err);
589
+ }
581
590
  },
582
591
  async getActiveTransactions() {
583
592
  if (!PaywalloStoreKitNative) return [];
584
- const results = await PaywalloStoreKitNative.getActiveTransactions();
585
- return results.map(mapNativePurchase);
593
+ try {
594
+ const results = await PaywalloStoreKitNative.getActiveTransactions();
595
+ return results.map(mapNativePurchase);
596
+ } catch (err) {
597
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] getActiveTransactions failed", err);
598
+ return [];
599
+ }
586
600
  }
587
601
  };
588
602
 
@@ -625,7 +639,8 @@ var IAPService = class {
625
639
  this.productsCache.set(product.productId, product);
626
640
  }
627
641
  return products;
628
- } catch (error) {
642
+ } catch (err) {
643
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] loadProducts failed", err);
629
644
  return [];
630
645
  }
631
646
  }
@@ -670,7 +685,8 @@ var IAPService = class {
670
685
  async finishTransaction(transactionId) {
671
686
  try {
672
687
  await nativeStoreKit.finishTransaction(transactionId);
673
- } catch (finishError) {
688
+ } catch (err) {
689
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] finishTransaction failed", err);
674
690
  }
675
691
  }
676
692
  async purchase(productId, options) {
@@ -680,30 +696,25 @@ var IAPService = class {
680
696
  error: new PurchaseError(PURCHASE_ERROR_CODES.STORE_NOT_AVAILABLE, "In-app purchases are not available on this device")
681
697
  };
682
698
  }
699
+ const debug = PaywalloClient.getConfig()?.debug;
700
+ if (debug) console.info(`[Paywallo PURCHASE] starting ${productId}`);
683
701
  try {
684
702
  const purchase = await nativeStoreKit.purchase(productId);
685
703
  if (!purchase) {
704
+ if (debug) console.info(`[Paywallo PURCHASE] ${productId} cancelled`);
686
705
  return { success: false };
687
706
  }
688
- options?.onValidating?.();
689
- let validation = null;
690
- try {
691
- validation = await this.validateWithServer(purchase, productId, options);
692
- } catch (validationError) {
693
- return {
694
- success: false,
695
- error: new PurchaseError(PURCHASE_ERROR_CODES.VALIDATION_FAILED, validationError instanceof Error ? validationError.message : "Server validation failed")
696
- };
697
- }
698
- if (!validation?.success) {
699
- return {
700
- success: false,
701
- error: new PurchaseError(PURCHASE_ERROR_CODES.VALIDATION_FAILED, validation?.error ? `Server validation: ${validation.error}` : "Server validation failed")
702
- };
703
- }
707
+ if (debug) console.info(`[Paywallo PURCHASE] ${productId} approved`, { transactionId: purchase.transactionId });
704
708
  await this.finishTransaction(purchase.transactionId);
709
+ if (debug) console.info(`[Paywallo PURCHASE] ${productId} finished`);
710
+ this.validateWithServer(purchase, productId, options).then(() => {
711
+ if (debug) console.info(`[Paywallo PURCHASE] ${productId} server validated`);
712
+ }).catch((err) => {
713
+ if (debug) console.warn("[Paywallo PURCHASE] background validation failed", err);
714
+ });
705
715
  return { success: true, purchase };
706
716
  } catch (error) {
717
+ if (debug) console.warn(`[Paywallo PURCHASE] ${productId} failed`, error);
707
718
  const e = error instanceof PurchaseError ? error : new PurchaseError(PURCHASE_ERROR_CODES.PURCHASE_FAILED, error instanceof Error ? error.message : String(error));
708
719
  return { success: false, error: e };
709
720
  }
@@ -739,13 +750,15 @@ var IAPService = class {
739
750
  if (r.status === "fulfilled" && r.value.success) {
740
751
  try {
741
752
  await nativeStoreKit.finishTransaction(tx.transactionId);
742
- } catch (finishError) {
753
+ } catch (err) {
754
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] finishTransaction failed", err);
743
755
  }
744
756
  validated.push(tx);
745
757
  }
746
758
  }
747
759
  return validated;
748
- } catch (error) {
760
+ } catch (err) {
761
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] restore failed", err);
749
762
  return [];
750
763
  }
751
764
  }
@@ -775,7 +788,8 @@ function usePaywallTracking() {
775
788
  ...opts.campaignId && { campaignId: opts.campaignId },
776
789
  ...sessionId && { sessionId }
777
790
  });
778
- } catch {
791
+ } catch (err) {
792
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] trackDismissed failed", err);
779
793
  }
780
794
  }, []);
781
795
  const trackProductSelected = (0, import_react.useCallback)((opts) => {
@@ -789,7 +803,8 @@ function usePaywallTracking() {
789
803
  ...opts.variantKey && { variantKey: opts.variantKey },
790
804
  ...opts.campaignId && { campaignId: opts.campaignId },
791
805
  ...sessionId && { sessionId }
792
- }).catch(() => {
806
+ }).catch((err) => {
807
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] trackProductSelected failed", err);
793
808
  });
794
809
  }, []);
795
810
  const trackPurchased = (0, import_react.useCallback)(async (opts) => {
@@ -805,7 +820,8 @@ function usePaywallTracking() {
805
820
  ...opts.campaignId && { campaignId: opts.campaignId },
806
821
  ...sessionId && { sessionId }
807
822
  });
808
- } catch {
823
+ } catch (err) {
824
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] trackPurchased failed", err);
809
825
  }
810
826
  }, []);
811
827
  return { trackDismissed, trackProductSelected, trackPurchased };
@@ -945,7 +961,8 @@ function usePaywallLoader(placement, visible, preloadedConfig, preloadedProducts
945
961
  const map = /* @__PURE__ */ new Map();
946
962
  for (const p of storeProducts) map.set(p.productId, p);
947
963
  setProducts(map);
948
- } catch {
964
+ } catch (err) {
965
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] loadProducts in paywall failed", err);
949
966
  setProducts(/* @__PURE__ */ new Map());
950
967
  }
951
968
  } else {
@@ -1473,7 +1490,12 @@ var PaywallErrorBoundary = class extends import_react6.Component {
1473
1490
  static getDerivedStateFromError() {
1474
1491
  return { hasError: true };
1475
1492
  }
1476
- componentDidCatch(_error) {
1493
+ componentDidCatch(error) {
1494
+ if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] Paywall render error:", error);
1495
+ try {
1496
+ PaywalloClient.getApiClient()?.reportError("PAYWALL_RENDER_ERROR", error.message);
1497
+ } catch {
1498
+ }
1477
1499
  }
1478
1500
  render() {
1479
1501
  return this.state.hasError ? null : this.props.children;
@@ -1753,7 +1775,8 @@ var AffiliateManager = class {
1753
1775
  );
1754
1776
  }
1755
1777
  }
1756
- log(..._args) {
1778
+ log(...args) {
1779
+ if (this.debug) console.log("[Paywallo:Affiliate]", ...args);
1757
1780
  }
1758
1781
  };
1759
1782
  var affiliateManager = new AffiliateManager();
@@ -2021,7 +2044,8 @@ var SubscriptionManagerClass = class {
2021
2044
  await this.cache.invalidate(this.cacheKey());
2022
2045
  await this.getSubscriptionStatus(true);
2023
2046
  }
2024
- log(..._args) {
2047
+ log(...args) {
2048
+ if (this.config?.debug) console.log("[Paywallo:Subscription]", ...args);
2025
2049
  }
2026
2050
  };
2027
2051
  var subscriptionManager = new SubscriptionManagerClass();
@@ -2074,12 +2098,18 @@ async function validatePurchase(client, appKey, platform, receipt, productId, tr
2074
2098
  },
2075
2099
  false
2076
2100
  );
2101
+ if (!response.ok) {
2102
+ throw new Error(`Server validation failed: HTTP ${response.status}`);
2103
+ }
2077
2104
  return response.data;
2078
2105
  }
2079
2106
  async function getSubscriptionStatus(client, baseUrl, appKey, distinctId) {
2080
2107
  const url = new URL(`${baseUrl}/purchases/status/${appKey}`);
2081
2108
  url.searchParams.set("distinctId", distinctId);
2082
2109
  const response = await client.get(url.toString());
2110
+ if (!response.ok) {
2111
+ throw new Error(`Subscription status check failed: HTTP ${response.status}`);
2112
+ }
2083
2113
  return response.data;
2084
2114
  }
2085
2115
  async function getEmergencyPaywall(client) {
@@ -2259,7 +2289,8 @@ var HttpClient = class {
2259
2289
  sleep(ms) {
2260
2290
  return new Promise((resolve) => setTimeout(resolve, ms));
2261
2291
  }
2262
- log(..._args) {
2292
+ log(...args) {
2293
+ if (this.config.debug) console.log("[Paywallo:Http]", ...args);
2263
2294
  }
2264
2295
  setDebug(debug) {
2265
2296
  this.config.debug = debug;
@@ -2285,8 +2316,6 @@ var NetworkMonitorClass = class {
2285
2316
  this.config = DEFAULT_NETWORK_CONFIG;
2286
2317
  this.listeners = /* @__PURE__ */ new Set();
2287
2318
  this.currentState = "unknown";
2288
- this.netInfoModule = null;
2289
- this.netInfoSubscription = null;
2290
2319
  this.appStateSubscription = null;
2291
2320
  this.initialized = false;
2292
2321
  this.checkIntervalId = null;
@@ -2302,13 +2331,6 @@ var NetworkMonitorClass = class {
2302
2331
  void this.setupFallbackDetection();
2303
2332
  this.setupAppStateListener();
2304
2333
  }
2305
- setupNetInfoListener() {
2306
- if (!this.netInfoModule) return;
2307
- this.netInfoSubscription = this.netInfoModule.addEventListener((state) => {
2308
- const newState = this.netInfoStateToNetworkState(state);
2309
- this.updateState(newState);
2310
- });
2311
- }
2312
2334
  async setupFallbackDetection() {
2313
2335
  await this.checkConnectivity();
2314
2336
  this.checkIntervalId = setInterval(() => {
@@ -2336,15 +2358,6 @@ var NetworkMonitorClass = class {
2336
2358
  this.updateState("offline");
2337
2359
  }
2338
2360
  }
2339
- netInfoStateToNetworkState(state) {
2340
- if (state.isConnected === null) {
2341
- return "unknown";
2342
- }
2343
- if (!state.isConnected) {
2344
- return "offline";
2345
- }
2346
- return "online";
2347
- }
2348
2361
  updateState(newState) {
2349
2362
  const previousState = this.currentState;
2350
2363
  if (previousState === newState) {
@@ -2387,10 +2400,6 @@ var NetworkMonitorClass = class {
2387
2400
  return this.currentState;
2388
2401
  }
2389
2402
  dispose() {
2390
- if (this.netInfoSubscription) {
2391
- this.netInfoSubscription();
2392
- this.netInfoSubscription = null;
2393
- }
2394
2403
  if (this.appStateSubscription) {
2395
2404
  this.appStateSubscription.remove();
2396
2405
  this.appStateSubscription = null;
@@ -2403,7 +2412,8 @@ var NetworkMonitorClass = class {
2403
2412
  this.initialized = false;
2404
2413
  this.currentState = "unknown";
2405
2414
  }
2406
- log(..._args) {
2415
+ log(...args) {
2416
+ if (this.config.debug) console.log("[Paywallo:Network]", ...args);
2407
2417
  }
2408
2418
  setDebug(debug) {
2409
2419
  this.config.debug = debug;
@@ -2413,6 +2423,7 @@ var networkMonitor = new NetworkMonitorClass();
2413
2423
 
2414
2424
  // src/core/queue/OfflineQueue.ts
2415
2425
  var import_async_storage2 = __toESM(require("@react-native-async-storage/async-storage"));
2426
+ init_uuid();
2416
2427
 
2417
2428
  // src/core/queue/deduplication.ts
2418
2429
  function normalizeForComparison(obj) {
@@ -2650,7 +2661,8 @@ var OfflineQueueClass = class {
2650
2661
  }
2651
2662
  }
2652
2663
  }
2653
- log(..._args) {
2664
+ log(...args) {
2665
+ if (this.config.debug) console.log("[Paywallo:Queue]", ...args);
2654
2666
  }
2655
2667
  setDebug(debug) {
2656
2668
  this.config.debug = debug;
@@ -2792,7 +2804,8 @@ var QueueProcessorClass = class {
2792
2804
  this.httpClient = null;
2793
2805
  this.initialized = false;
2794
2806
  }
2795
- log(..._args) {
2807
+ log(...args) {
2808
+ if (this.config.debug) console.log("[Paywallo:QueueProcessor]", ...args);
2796
2809
  }
2797
2810
  setDebug(debug) {
2798
2811
  this.config.debug = debug;
@@ -2874,13 +2887,12 @@ var ApiCache = class {
2874
2887
  };
2875
2888
 
2876
2889
  // src/core/ApiClient.ts
2877
- var SDK_VERSION = "1.0.0";
2878
- var DEFAULT_BASE_URL = "https://panel.lucasqueiroga.shop";
2890
+ var SDK_VERSION = "1.4.1";
2879
2891
  var _ApiClient = class _ApiClient {
2880
- constructor(appKey, debug = false, environment = "Production", offlineQueueEnabled = true, timeout = 3e4) {
2892
+ constructor(appKey, apiUrl, debug = false, environment = "Production", offlineQueueEnabled = true, timeout = 3e4) {
2881
2893
  this.cache = new ApiCache();
2882
2894
  this.appKey = appKey;
2883
- this.baseUrl = DEFAULT_BASE_URL;
2895
+ this.baseUrl = apiUrl;
2884
2896
  this.webUrl = deriveWebUrl(this.baseUrl);
2885
2897
  this.debug = debug;
2886
2898
  this.environment = environment;
@@ -2933,7 +2945,8 @@ var _ApiClient = class _ApiClient {
2933
2945
  sdkVersion: SDK_VERSION,
2934
2946
  platform: import_react_native9.Platform.OS
2935
2947
  }, true);
2936
- } catch {
2948
+ } catch (err) {
2949
+ if (this.debug) console.warn("[Paywallo:ApiClient] reportError failed", err);
2937
2950
  }
2938
2951
  }
2939
2952
  async trackEvent(eventName, distinctId, properties, timestamp) {
@@ -2973,6 +2986,7 @@ var _ApiClient = class _ApiClient {
2973
2986
  try {
2974
2987
  const res = await this.get(`/api/v1/flags/${flagKey}?distinctId=${encodeURIComponent(distinctId)}`);
2975
2988
  if (res.status === 404) {
2989
+ this.log("Flag not found (404):", flagKey);
2976
2990
  const v = { variant: null };
2977
2991
  this.cache.setVariant(key, v, this.cache.nullTTL);
2978
2992
  return v;
@@ -2999,6 +3013,7 @@ var _ApiClient = class _ApiClient {
2999
3013
  try {
3000
3014
  const res = await this.get(`/api/v1/paywalls/${placement}`);
3001
3015
  if (res.status === 404) {
3016
+ this.log("Paywall not found (404):", placement);
3002
3017
  this.cache.setPaywall(placement, null, this.cache.nullTTL);
3003
3018
  return null;
3004
3019
  }
@@ -3050,6 +3065,7 @@ var _ApiClient = class _ApiClient {
3050
3065
  try {
3051
3066
  const res = await this.get(`/campaigns/public/primary?distinctId=${encodeURIComponent(distinctId)}`);
3052
3067
  if (res.status === 404) {
3068
+ this.log("Primary campaign not found (404)");
3053
3069
  this.cache.setCampaign(key, null, this.cache.nullTTL);
3054
3070
  return null;
3055
3071
  }
@@ -3100,7 +3116,7 @@ var _ApiClient = class _ApiClient {
3100
3116
  headers: { "X-App-Key": this.appKey, "x-distinct-id": distinctId }
3101
3117
  });
3102
3118
  if (!res.ok) {
3103
- this.log("Non-OK response for evaluateFlags:", res.status);
3119
+ this.log("evaluateFlags failed:", res.status, keys);
3104
3120
  return {};
3105
3121
  }
3106
3122
  const raw = res.data;
@@ -3165,7 +3181,8 @@ var _ApiClient = class _ApiClient {
3165
3181
  try {
3166
3182
  const value = JSON.stringify({ variant: flag.variant, payload: flag.payload, cachedAt: Date.now() });
3167
3183
  await secureStorage.set(`flag:${flagKey}:${distinctId}`, value);
3168
- } catch {
3184
+ } catch (err) {
3185
+ this.log("writeFlagToStorage failed", err);
3169
3186
  }
3170
3187
  }
3171
3188
  async postWithQueue(url, payload, label) {
@@ -3186,7 +3203,8 @@ var _ApiClient = class _ApiClient {
3186
3203
  throw error;
3187
3204
  }
3188
3205
  }
3189
- log(..._args) {
3206
+ log(...args) {
3207
+ if (this.debug) console.log("[Paywallo:ApiClient]", ...args);
3190
3208
  }
3191
3209
  };
3192
3210
  _ApiClient.FLAG_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
@@ -3354,43 +3372,67 @@ var AdvertisingIdManager = class {
3354
3372
  }
3355
3373
  };
3356
3374
 
3357
- // src/domains/identity/FacebookIdManager.ts
3375
+ // src/domains/identity/InstallTracker.ts
3376
+ var import_react_native14 = require("react-native");
3377
+ var import_async_storage3 = __toESM(require("@react-native-async-storage/async-storage"));
3378
+ var import_react_native_device_info2 = __toESM(require("react-native-device-info"));
3379
+
3380
+ // src/domains/identity/MetaBridge.ts
3381
+ var import_react_native11 = require("react-native");
3358
3382
  var TIMEOUT_MS = 2e3;
3359
- var FacebookIdManager = class {
3360
- async getAnonId() {
3383
+ var NativeBridge = import_react_native11.NativeModules.PaywalloFBBridge ?? null;
3384
+ var MetaBridge = class {
3385
+ async getAnonymousID() {
3386
+ if (!NativeBridge) return null;
3361
3387
  try {
3362
3388
  let timerId;
3363
- const timeoutPromise = new Promise((resolve) => {
3389
+ const timeout = new Promise((resolve) => {
3364
3390
  timerId = setTimeout(() => resolve(null), TIMEOUT_MS);
3365
3391
  });
3366
- const result = await Promise.race([this.fetchAnonId(), timeoutPromise]);
3392
+ const result = await Promise.race([NativeBridge.getAnonymousID(), timeout]);
3367
3393
  clearTimeout(timerId);
3368
3394
  return result;
3369
3395
  } catch {
3370
3396
  return null;
3371
3397
  }
3372
3398
  }
3373
- async fetchAnonId() {
3399
+ async logEvent(name, params = {}) {
3400
+ if (!NativeBridge) return;
3374
3401
  try {
3375
- const fbsdk = await import("react-native-fbsdk-next");
3376
- const anonId = await fbsdk.AppEventsLogger.getAnonymousID();
3377
- return anonId || null;
3402
+ await NativeBridge.logEvent(name, params);
3403
+ } catch {
3404
+ }
3405
+ }
3406
+ async logPurchase(amount, currency, params = {}) {
3407
+ if (!NativeBridge) return;
3408
+ try {
3409
+ await NativeBridge.logPurchase(amount, currency, params);
3410
+ } catch {
3411
+ }
3412
+ }
3413
+ setUserID(userId) {
3414
+ if (!NativeBridge) return;
3415
+ try {
3416
+ NativeBridge.setUserID(userId);
3417
+ } catch {
3418
+ }
3419
+ }
3420
+ flush() {
3421
+ if (!NativeBridge) return;
3422
+ try {
3423
+ NativeBridge.flush();
3378
3424
  } catch {
3379
- return null;
3380
3425
  }
3381
3426
  }
3382
3427
  };
3383
- var facebookIdManager = new FacebookIdManager();
3384
-
3385
- // src/domains/identity/InstallTracker.ts
3386
- var import_react_native13 = require("react-native");
3428
+ var metaBridge = new MetaBridge();
3387
3429
 
3388
3430
  // src/domains/identity/InstallReferrerManager.ts
3389
- var import_react_native11 = require("react-native");
3431
+ var import_react_native12 = require("react-native");
3390
3432
  var REFERRER_TIMEOUT_MS = 5e3;
3391
3433
  var InstallReferrerManager = class {
3392
3434
  async getReferrer() {
3393
- if (import_react_native11.Platform.OS !== "android") {
3435
+ if (import_react_native12.Platform.OS !== "android") {
3394
3436
  return this.emptyResult();
3395
3437
  }
3396
3438
  let timerId;
@@ -3450,7 +3492,8 @@ var InstallReferrerManager = class {
3450
3492
  var installReferrerManager = new InstallReferrerManager();
3451
3493
 
3452
3494
  // src/domains/session/SessionManager.ts
3453
- var import_react_native12 = require("react-native");
3495
+ var import_react_native13 = require("react-native");
3496
+ init_uuid();
3454
3497
 
3455
3498
  // src/domains/session/sessionTracking.ts
3456
3499
  async function trackSessionStart(apiClient, sessionId, sessionStartedAt) {
@@ -3618,7 +3661,7 @@ var SessionManager = class {
3618
3661
  if (this.appStateSubscription) {
3619
3662
  this.appStateSubscription.remove();
3620
3663
  }
3621
- this.appStateSubscription = import_react_native12.AppState.addEventListener("change", this.handleAppStateChange);
3664
+ this.appStateSubscription = import_react_native13.AppState.addEventListener("change", this.handleAppStateChange);
3622
3665
  }
3623
3666
  async handleForeground() {
3624
3667
  if (this.backgroundTimestamp) {
@@ -3679,7 +3722,8 @@ var SessionManager = class {
3679
3722
  );
3680
3723
  }
3681
3724
  }
3682
- log(..._args) {
3725
+ log(...args) {
3726
+ if (this.debug) console.log("[Paywallo:Session]", ...args);
3683
3727
  }
3684
3728
  };
3685
3729
  var sessionManager = new SessionManager();
@@ -3692,12 +3736,11 @@ var InstallTracker = class {
3692
3736
  }
3693
3737
  async trackIfNeeded(apiClient, requestATT = false) {
3694
3738
  const storage = new SecureStorage(this.debug);
3695
- const alreadyTracked = await storage.get("install_tracked");
3739
+ const alreadyTracked = await storage.get("@panel:install_tracked");
3696
3740
  if (alreadyTracked) return;
3697
3741
  let fallbackTracked = null;
3698
3742
  try {
3699
- const AsyncStorage3 = (await import("@react-native-async-storage/async-storage")).default;
3700
- fallbackTracked = await AsyncStorage3.getItem("@paywallo:install_tracked");
3743
+ fallbackTracked = await import_async_storage3.default.getItem("@paywallo:install_tracked");
3701
3744
  } catch {
3702
3745
  }
3703
3746
  if (fallbackTracked) return;
@@ -3705,16 +3748,10 @@ var InstallTracker = class {
3705
3748
  const sessionId = sessionManager.getSessionId();
3706
3749
  const installedAt = Date.now();
3707
3750
  if (!distinctId) return;
3708
- let deviceInfo = null;
3709
- try {
3710
- const mod = await import("react-native-device-info");
3711
- deviceInfo = mod.default;
3712
- } catch {
3713
- deviceInfo = null;
3714
- }
3751
+ const deviceInfo = import_react_native_device_info2.default;
3715
3752
  let deviceData = {};
3716
3753
  try {
3717
- const screen = import_react_native13.Dimensions.get("screen");
3754
+ const screen = import_react_native14.Dimensions.get("screen");
3718
3755
  const screenWidth = Math.round(screen.width ?? 0);
3719
3756
  const screenHeight = Math.round(screen.height ?? 0);
3720
3757
  const locale = Intl.DateTimeFormat().resolvedOptions().locale ?? "unknown";
@@ -3723,6 +3760,38 @@ var InstallTracker = class {
3723
3760
  const osVersion = deviceInfo?.getSystemVersion() || "unknown";
3724
3761
  const appVersion = deviceInfo?.getVersion() || "unknown";
3725
3762
  const buildNumber = deviceInfo?.getBuildNumber() || "0";
3763
+ let carrier = "unknown";
3764
+ try {
3765
+ carrier = await deviceInfo?.getCarrier?.() ?? "unknown";
3766
+ } catch {
3767
+ }
3768
+ let totalDisk = 0;
3769
+ try {
3770
+ totalDisk = Number(await deviceInfo?.getTotalDiskCapacity?.()) || 0;
3771
+ } catch {
3772
+ }
3773
+ let freeDisk = 0;
3774
+ try {
3775
+ freeDisk = Number(await deviceInfo?.getFreeDiskStorage?.()) || 0;
3776
+ } catch {
3777
+ }
3778
+ let totalRam = 0;
3779
+ try {
3780
+ totalRam = Number(await deviceInfo?.getTotalMemory?.()) || 0;
3781
+ } catch {
3782
+ }
3783
+ let brand = "unknown";
3784
+ try {
3785
+ brand = deviceInfo?.getBrand?.() ?? "unknown";
3786
+ } catch {
3787
+ }
3788
+ let installerPackage = null;
3789
+ try {
3790
+ if (import_react_native14.Platform.OS === "android") {
3791
+ installerPackage = await deviceInfo?.getInstallerPackageName?.() ?? null;
3792
+ }
3793
+ } catch {
3794
+ }
3726
3795
  deviceData = {
3727
3796
  deviceModel,
3728
3797
  osVersion,
@@ -3731,22 +3800,40 @@ var InstallTracker = class {
3731
3800
  ...screenWidth > 0 && { screenWidth },
3732
3801
  ...screenHeight > 0 && { screenHeight },
3733
3802
  timezone,
3734
- locale
3803
+ locale,
3804
+ ...carrier !== "unknown" && { carrier },
3805
+ ...totalDisk > 0 && { totalDisk },
3806
+ ...freeDisk > 0 && { freeDisk },
3807
+ ...totalRam > 0 && { totalRam },
3808
+ ...brand !== "unknown" && { brand },
3809
+ ...installerPackage && { installerPackage }
3735
3810
  };
3736
3811
  } catch {
3737
3812
  }
3738
3813
  const [fbAnonId, referrer, adIds] = await Promise.all([
3739
- facebookIdManager.getAnonId(),
3814
+ metaBridge.getAnonymousID(),
3740
3815
  installReferrerManager.getReferrer(),
3741
3816
  this.advertisingIdManager.collect(requestATT)
3742
3817
  ]);
3818
+ let effectiveAnonId = fbAnonId;
3819
+ if (!effectiveAnonId) {
3820
+ const stored = await storage.get("@panel:anon_id");
3821
+ if (stored) {
3822
+ effectiveAnonId = stored;
3823
+ } else {
3824
+ const { generateUUID: generateUUID2 } = await Promise.resolve().then(() => (init_uuid(), uuid_exports));
3825
+ effectiveAnonId = `PW_${generateUUID2()}`;
3826
+ await storage.set("@panel:anon_id", effectiveAnonId).catch(() => {
3827
+ });
3828
+ }
3829
+ }
3743
3830
  try {
3744
3831
  await apiClient.trackEvent("$app_installed", distinctId, {
3745
3832
  installedAt,
3746
- platform: import_react_native13.Platform.OS,
3833
+ platform: import_react_native14.Platform.OS,
3747
3834
  ...sessionId && { sessionId },
3748
3835
  ...deviceData,
3749
- ...fbAnonId && { fbAnonId },
3836
+ ...effectiveAnonId && { fbAnonId: effectiveAnonId },
3750
3837
  ...referrer.rawReferrer && { installReferrer: referrer.rawReferrer },
3751
3838
  ...referrer.trackingId && { referrerTrackingId: referrer.trackingId },
3752
3839
  ...referrer.fbclid && { referrerFbclid: referrer.fbclid },
@@ -3758,15 +3845,49 @@ var InstallTracker = class {
3758
3845
  ...adIds.gaid && { gaid: adIds.gaid },
3759
3846
  attStatus: adIds.attStatus
3760
3847
  });
3761
- const stored = await storage.set("install_tracked", String(installedAt));
3848
+ if (import_react_native14.Platform.OS === "ios") {
3849
+ void this.callDeferredMatch(apiClient, deviceData, adIds, effectiveAnonId).catch(() => {
3850
+ });
3851
+ }
3852
+ await storage.set("@panel:install_tracked", String(installedAt));
3762
3853
  try {
3763
- const AsyncStorage3 = (await import("@react-native-async-storage/async-storage")).default;
3764
- await AsyncStorage3.setItem("@paywallo:install_tracked", String(installedAt));
3854
+ await import_async_storage3.default.setItem("@paywallo:install_tracked", String(installedAt));
3765
3855
  } catch {
3766
3856
  }
3767
3857
  } catch {
3768
3858
  }
3769
3859
  }
3860
+ async callDeferredMatch(apiClient, deviceData, adIds, anonId) {
3861
+ const storage = new SecureStorage(this.debug);
3862
+ const alreadyMatched = await storage.get("@panel:deferred_match_done");
3863
+ if (alreadyMatched) return;
3864
+ const controller = new AbortController();
3865
+ const timer = setTimeout(() => controller.abort(), 5e3);
3866
+ try {
3867
+ const baseUrl = apiClient.getBaseUrl();
3868
+ const appKey = apiClient.appKey;
3869
+ await fetch(`${baseUrl}/api/v1/attribution/deferred-match`, {
3870
+ method: "POST",
3871
+ headers: { "Content-Type": "application/json", "X-App-Key": appKey },
3872
+ body: JSON.stringify({
3873
+ idfv: adIds.idfv,
3874
+ deviceModel: deviceData.deviceModel,
3875
+ osVersion: deviceData.osVersion,
3876
+ screenWidth: deviceData.screenWidth,
3877
+ screenHeight: deviceData.screenHeight,
3878
+ timezone: deviceData.timezone,
3879
+ locale: deviceData.locale,
3880
+ fbAnonId: anonId
3881
+ }),
3882
+ signal: controller.signal
3883
+ });
3884
+ await storage.set("@panel:deferred_match_done", "1").catch(() => {
3885
+ });
3886
+ } catch {
3887
+ } finally {
3888
+ clearTimeout(timer);
3889
+ }
3890
+ }
3770
3891
  };
3771
3892
 
3772
3893
  // src/core/PaywalloClient.ts
@@ -3858,9 +3979,12 @@ var _PaywalloClientClass = class _PaywalloClientClass {
3858
3979
  this.networkCleanup = networkMonitor.addListener((state) => {
3859
3980
  if (state !== "online") return;
3860
3981
  if (this.config && this.apiClient) return;
3982
+ if (this.initPromise) return;
3861
3983
  this.initPromise = this.initWithRetry(config);
3862
3984
  this.initPromise.catch(() => {
3863
3985
  this.reportInitFailure(config, null, "NETWORK_RECOVERY_FAILED");
3986
+ }).finally(() => {
3987
+ if (!this.isReady()) this.initPromise = null;
3864
3988
  });
3865
3989
  });
3866
3990
  }
@@ -3872,12 +3996,13 @@ var _PaywalloClientClass = class _PaywalloClientClass {
3872
3996
  }
3873
3997
  reportInitFailure(config, error, reason) {
3874
3998
  try {
3875
- const tempClient = new ApiClient(config.appKey, config.debug);
3999
+ const tempClient = new ApiClient(config.appKey, config.apiUrl ?? "https://paywallo.com.br", config.debug);
3876
4000
  void tempClient.reportError("INIT_FAILED", reason, {
3877
4001
  attempts: this.initAttempts,
3878
4002
  error: error instanceof Error ? error.message : String(error)
3879
4003
  });
3880
- } catch {
4004
+ } catch (err) {
4005
+ if (config.debug) console.warn("[Paywallo] reportInitFailure error", err);
3881
4006
  }
3882
4007
  }
3883
4008
  reportInitSuccess(config, attempts) {
@@ -3885,7 +4010,8 @@ var _PaywalloClientClass = class _PaywalloClientClass {
3885
4010
  if (this.apiClient) {
3886
4011
  void this.apiClient.reportError("INIT_RECOVERED", `Succeeded after ${attempts} retries`, { attempts });
3887
4012
  }
3888
- } catch {
4013
+ } catch (err) {
4014
+ if (config.debug) console.warn("[Paywallo] reportInitSuccess error", err);
3889
4015
  }
3890
4016
  }
3891
4017
  async resolveSessionFlags(keys, apiClient, distinctId) {
@@ -3910,7 +4036,7 @@ var _PaywalloClientClass = class _PaywalloClientClass {
3910
4036
  () => offlineQueue.clearItemsWithInvalidAppKey(config.appKey)
3911
4037
  )
3912
4038
  ]);
3913
- this.apiClient = new ApiClient(config.appKey, config.debug, environment, offlineQueueEnabled, config.timeout);
4039
+ this.apiClient = new ApiClient(config.appKey, config.apiUrl ?? "https://paywallo.com.br", config.debug, environment, offlineQueueEnabled, config.timeout);
3914
4040
  queueProcessor.initialize(this.apiClient.getHttpClient(), { debug: config.debug });
3915
4041
  affiliateManager.initialize(this.apiClient, config.debug);
3916
4042
  subscriptionManager.init({
@@ -3964,18 +4090,21 @@ var _PaywalloClientClass = class _PaywalloClientClass {
3964
4090
  }
3965
4091
  async identify(options) {
3966
4092
  this.ensureInitialized();
4093
+ if (this.config?.debug) console.info("[Paywallo IDENTIFY]", options);
3967
4094
  await identityManager.identify(options);
3968
4095
  }
3969
4096
  async track(eventName, options) {
3970
4097
  const apiClient = this.getApiClientOrThrow();
3971
4098
  const distinctId = identityManager.getDistinctId();
3972
4099
  if (!distinctId) {
4100
+ if (this.config?.debug) console.info(`[Paywallo TRACK] ${eventName} skipped (no distinctId)`);
3973
4101
  return;
3974
4102
  }
3975
4103
  if (!isValidEventName(eventName)) {
3976
4104
  const msg = `Invalid event name: "${eventName}". Use snake_case or $reserved_name.`;
3977
4105
  throw new ClientError(CLIENT_ERROR_CODES.INVALID_EVENT_NAME, msg);
3978
4106
  }
4107
+ if (this.config?.debug && eventName.startsWith("$purchase")) console.info(`[Paywallo TRACK] ${eventName}`, options?.properties ?? {});
3979
4108
  const sessionId = sessionManager.getSessionId();
3980
4109
  await apiClient.trackEvent(eventName, distinctId, {
3981
4110
  ...identityManager.getProperties(),
@@ -3996,14 +4125,10 @@ var _PaywalloClientClass = class _PaywalloClientClass {
3996
4125
  const now = Date.now();
3997
4126
  const timeOnPreviousStep = this.lastOnboardingStepTimestamp !== null ? Math.round((now - this.lastOnboardingStepTimestamp) / 1e3) : 0;
3998
4127
  this.lastOnboardingStepTimestamp = now;
3999
- const sessionId = sessionManager.getSessionId();
4000
4128
  await apiClient.trackEvent("$onboarding_step", distinctId, {
4001
- ...identityManager.getProperties(),
4002
4129
  stepIndex: index,
4003
4130
  stepName: name,
4004
- timestamp: now,
4005
- timeOnPreviousStep,
4006
- ...sessionId && { sessionId }
4131
+ timeOnPreviousStep
4007
4132
  });
4008
4133
  }
4009
4134
  async coreAction(actionName) {
@@ -4038,10 +4163,14 @@ var _PaywalloClientClass = class _PaywalloClientClass {
4038
4163
  try {
4039
4164
  const distinctId = identityManager.getDistinctId();
4040
4165
  if (!distinctId) {
4166
+ if (this.config?.debug) console.info(`[Paywallo FLAG] ${flagKey} \u2192 null (no distinctId)`);
4041
4167
  return { variant: null };
4042
4168
  }
4043
- return await this.getApiClientOrThrow().getVariant(flagKey, distinctId);
4169
+ const result = await this.getApiClientOrThrow().getVariant(flagKey, distinctId);
4170
+ if (this.config?.debug) console.info(`[Paywallo FLAG] ${flagKey} \u2192`, result.variant);
4171
+ return result;
4044
4172
  } catch (error) {
4173
+ if (this.config?.debug) console.warn(`[Paywallo FLAG] ${flagKey} \u2192 error`, error);
4045
4174
  if (this.apiClient) void this.apiClient.reportError("GET_VARIANT_FAILED", error instanceof Error ? error.message : String(error));
4046
4175
  return { variant: null };
4047
4176
  }
@@ -4074,21 +4203,34 @@ var _PaywalloClientClass = class _PaywalloClientClass {
4074
4203
  }
4075
4204
  async presentCampaign(placement, context) {
4076
4205
  this.ensureInitialized();
4077
- return campaignGateService.presentCampaign(this.getApiClientOrThrow(), placement, () => this.hasActiveSubscription(), this.paywallPresenter, this.campaignPresenter, context);
4206
+ if (this.config?.debug) console.info(`[Paywallo CAMPAIGN] presenting ${placement}`);
4207
+ const result = await campaignGateService.presentCampaign(this.getApiClientOrThrow(), placement, () => this.hasActiveSubscription(), this.paywallPresenter, this.campaignPresenter, context);
4208
+ if (this.config?.debug) {
4209
+ if (result.error) console.warn(`[Paywallo CAMPAIGN] ${placement} error:`, result.error.message);
4210
+ else console.info(`[Paywallo CAMPAIGN] ${placement} result:`, { purchased: result.purchased, cancelled: result.cancelled, restored: result.restored, skippedReason: result.skippedReason });
4211
+ }
4212
+ return result;
4078
4213
  }
4079
4214
  async hasActiveSubscription() {
4080
4215
  this.ensureInitialized();
4081
4216
  try {
4082
4217
  if (this.activeChecker) {
4083
- return await this.activeChecker();
4218
+ const result = await this.activeChecker();
4219
+ const isActive2 = result === true;
4220
+ if (this.config?.debug) console.info("[Paywallo SUB] hasActiveSubscription (checker):", isActive2);
4221
+ return isActive2;
4084
4222
  }
4085
4223
  const distinctId = identityManager.getDistinctId();
4086
4224
  if (!distinctId || !this.apiClient) {
4225
+ if (this.config?.debug) console.info("[Paywallo SUB] hasActiveSubscription: false (no distinctId/apiClient)");
4087
4226
  return false;
4088
4227
  }
4089
4228
  const status = await this.apiClient.getSubscriptionStatus(distinctId);
4090
- return status.hasActiveSubscription;
4229
+ const isActive = status.hasActiveSubscription === true;
4230
+ if (this.config?.debug) console.info("[Paywallo SUB] hasActiveSubscription (server):", isActive);
4231
+ return isActive;
4091
4232
  } catch (error) {
4233
+ if (this.config?.debug) console.warn("[Paywallo SUB] hasActiveSubscription error", error);
4092
4234
  if (this.apiClient) void this.apiClient.reportError("SUBSCRIPTION_CHECK_FAILED", error instanceof Error ? error.message : "hasActiveSubscription failed");
4093
4235
  return false;
4094
4236
  }
@@ -4109,7 +4251,10 @@ var _PaywalloClientClass = class _PaywalloClientClass {
4109
4251
  async restorePurchases() {
4110
4252
  this.ensureInitialized();
4111
4253
  if (!this.restoreHandler) throw new PaywallError(PAYWALL_ERROR_CODES.NOT_INITIALIZED, "PaywalloProvider not mounted. Wrap your app with <PaywalloProvider>");
4112
- return this.restoreHandler();
4254
+ if (this.config?.debug) console.info("[Paywallo RESTORE] starting");
4255
+ const result = await this.restoreHandler();
4256
+ if (this.config?.debug) console.info("[Paywallo RESTORE] result:", { success: result.success, restoredProducts: result.restoredProducts?.length ?? 0 });
4257
+ return result;
4113
4258
  }
4114
4259
  async reset() {
4115
4260
  await identityManager.reset();
@@ -4395,9 +4540,7 @@ function usePurchase() {
4395
4540
  setError(null);
4396
4541
  try {
4397
4542
  const iapService = getIAPService();
4398
- const result = await iapService.purchase(productId, {
4399
- onValidating: () => setState("validating")
4400
- });
4543
+ const result = await iapService.purchase(productId);
4401
4544
  if (result.success && result.purchase) {
4402
4545
  setState("idle");
4403
4546
  return {
@@ -4453,9 +4596,7 @@ function usePurchase() {
4453
4596
  purchase,
4454
4597
  restore,
4455
4598
  error,
4456
- isLoading: state === "loading",
4457
- isPurchasing: state === "purchasing" || state === "validating",
4458
- isValidating: state === "validating",
4599
+ isPurchasing: state === "purchasing",
4459
4600
  isRestoring: state === "restoring"
4460
4601
  };
4461
4602
  }
@@ -4496,6 +4637,9 @@ function useSubscription() {
4496
4637
  void subscriptionManager.getSubscriptionStatus().then((s) => {
4497
4638
  setStatus(s);
4498
4639
  setIsLoading(false);
4640
+ }).catch((err) => {
4641
+ setError(err instanceof Error ? err : new Error(String(err)));
4642
+ setIsLoading(false);
4499
4643
  });
4500
4644
  const removeListener = subscriptionManager.addListener((newStatus) => {
4501
4645
  setStatus(newStatus);
@@ -4517,7 +4661,7 @@ function useSubscription() {
4517
4661
 
4518
4662
  // src/PaywalloProvider.tsx
4519
4663
  var import_react18 = require("react");
4520
- var import_react_native14 = require("react-native");
4664
+ var import_react_native15 = require("react-native");
4521
4665
 
4522
4666
  // src/hooks/useCampaignPreload.ts
4523
4667
  var import_react14 = require("react");
@@ -4675,7 +4819,8 @@ function useProductLoader() {
4675
4819
  const map = /* @__PURE__ */ new Map();
4676
4820
  for (const p of loaded) map.set(p.productId, p);
4677
4821
  setProducts(map);
4678
- } catch (error) {
4822
+ } catch (err) {
4823
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] refreshProducts failed", err);
4679
4824
  } finally {
4680
4825
  setIsLoadingProducts(false);
4681
4826
  }
@@ -4715,6 +4860,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
4715
4860
  clearPreloadedWebView();
4716
4861
  resolvePreloadedPaywall({ presented: true, purchased: true, cancelled: false, restored: false });
4717
4862
  } catch (error) {
4863
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] preloaded purchase failed", error);
4718
4864
  setShowingPreloadedWebView(false);
4719
4865
  clearPreloadedWebView();
4720
4866
  resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: false });
@@ -4735,6 +4881,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
4735
4881
  clearPreloadedWebView();
4736
4882
  resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: true });
4737
4883
  } catch (error) {
4884
+ if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] preloaded restore failed", error);
4738
4885
  setShowingPreloadedWebView(false);
4739
4886
  clearPreloadedWebView();
4740
4887
  resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: false });
@@ -4847,7 +4994,7 @@ function useSubscriptionSync() {
4847
4994
  cacheRef.current = { data: sub, expiresAt: Date.now() + CACHE_TTL_MS2 };
4848
4995
  setSubscription(sub);
4849
4996
  void subscriptionCache.set(distinctId, toDomainResponse(status));
4850
- return status.hasActiveSubscription;
4997
+ return status.hasActiveSubscription === true;
4851
4998
  } catch {
4852
4999
  return await resolveOfflineFromCache(distinctId);
4853
5000
  }
@@ -4903,8 +5050,8 @@ function PaywalloProvider({ children, config }) {
4903
5050
  });
4904
5051
  }, []);
4905
5052
  const { showingPreloadedWebView, presentPreloadedWebView, resolvePreloadedPaywall, handlePreloadedWebViewReady: baseHandlePreloadedWebViewReady, handlePreloadedClose: baseHandlePreloadedClose, setShowingPreloadedWebView, presentedAtRef } = usePaywallPresenter(preloadedWebView, setPreloadedWebView);
4906
- const SCREEN_HEIGHT3 = (0, import_react18.useMemo)(() => import_react_native14.Dimensions.get("window").height, []);
4907
- const slideAnim = (0, import_react18.useRef)(new import_react_native14.Animated.Value(SCREEN_HEIGHT3)).current;
5053
+ const SCREEN_HEIGHT3 = (0, import_react18.useMemo)(() => import_react_native15.Dimensions.get("window").height, []);
5054
+ const slideAnim = (0, import_react18.useRef)(new import_react_native15.Animated.Value(SCREEN_HEIGHT3)).current;
4908
5055
  const handlePreloadedWebViewReady = (0, import_react18.useCallback)(() => {
4909
5056
  if (PaywalloClient.getConfig()?.debug) {
4910
5057
  console.info(`[Paywallo WEBVIEW] loaded \u2014 placement: ${preloadedWebView?.placement}`);
@@ -4913,7 +5060,7 @@ function PaywalloProvider({ children, config }) {
4913
5060
  }, [config?.debug, preloadedWebView?.placement, baseHandlePreloadedWebViewReady]);
4914
5061
  const handlePreloadedClose = (0, import_react18.useCallback)(() => {
4915
5062
  const placement = preloadedWebView?.placement;
4916
- import_react_native14.Animated.timing(slideAnim, { toValue: SCREEN_HEIGHT3, duration: 200, easing: import_react_native14.Easing.in(import_react_native14.Easing.cubic), useNativeDriver: true }).start(() => {
5063
+ import_react_native15.Animated.timing(slideAnim, { toValue: SCREEN_HEIGHT3, duration: 200, easing: import_react_native15.Easing.in(import_react_native15.Easing.cubic), useNativeDriver: true }).start(() => {
4917
5064
  baseHandlePreloadedClose();
4918
5065
  if (placement) triggerRepreload(placement);
4919
5066
  });
@@ -5129,7 +5276,7 @@ function PaywalloProvider({ children, config }) {
5129
5276
  (0, import_react18.useLayoutEffect)(() => {
5130
5277
  if (showingPreloadedWebView) {
5131
5278
  slideAnim.setValue(SCREEN_HEIGHT3);
5132
- import_react_native14.Animated.timing(slideAnim, { toValue: 0, duration: 200, easing: import_react_native14.Easing.out(import_react_native14.Easing.cubic), useNativeDriver: true }).start();
5279
+ import_react_native15.Animated.timing(slideAnim, { toValue: 0, duration: 200, easing: import_react_native15.Easing.out(import_react_native15.Easing.cubic), useNativeDriver: true }).start();
5133
5280
  }
5134
5281
  }, [showingPreloadedWebView, slideAnim, SCREEN_HEIGHT3]);
5135
5282
  const preloadStyle = [
@@ -5139,7 +5286,7 @@ function PaywalloProvider({ children, config }) {
5139
5286
  ];
5140
5287
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(PaywalloContext.Provider, { value: contextValue, children: [
5141
5288
  children,
5142
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PaywallErrorBoundary, { children: !!preloadedWebView?.craftData && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native14.Animated.View, { style: preloadStyle, pointerEvents: showingPreloadedWebView ? "auto" : "none", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5289
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PaywallErrorBoundary, { children: !!preloadedWebView?.craftData && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native15.Animated.View, { style: preloadStyle, pointerEvents: showingPreloadedWebView ? "auto" : "none", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5143
5290
  PaywallWebView,
5144
5291
  {
5145
5292
  paywallId: preloadedWebView.paywallId,
@@ -5169,7 +5316,7 @@ function PaywalloProvider({ children, config }) {
5169
5316
  ) })
5170
5317
  ] });
5171
5318
  }
5172
- var styles3 = import_react_native14.StyleSheet.create({
5319
+ var styles3 = import_react_native15.StyleSheet.create({
5173
5320
  preloadOverlay: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, backgroundColor: "#ffffff" },
5174
5321
  hidden: { zIndex: -1, opacity: 0, width: 1, height: 1, overflow: "hidden" },
5175
5322
  visible: { zIndex: 9999, opacity: 1 }
@@ -5312,6 +5459,7 @@ var index_default = Paywallo;
5312
5459
  IDENTITY_ERROR_CODES,
5313
5460
  IdentityError,
5314
5461
  IdentityManager,
5462
+ MetaBridge,
5315
5463
  PAYWALL_ERROR_CODES,
5316
5464
  PURCHASE_ERROR_CODES,
5317
5465
  PaywallContext,
@@ -5338,6 +5486,7 @@ var index_default = Paywallo;
5338
5486
  hasVariables,
5339
5487
  identityManager,
5340
5488
  initLocalization,
5489
+ metaBridge,
5341
5490
  nativeStoreKit,
5342
5491
  networkMonitor,
5343
5492
  offlineQueue,