@virex-tech/paywallo-sdk 2.0.2 → 2.2.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.mjs CHANGED
@@ -197,6 +197,9 @@ function isAvailable() {
197
197
  function clearCache() {
198
198
  cachedDeviceInfo = null;
199
199
  }
200
+ function getCachedDeviceInfo() {
201
+ return cachedDeviceInfo;
202
+ }
200
203
  var FALLBACK, _debug, warnedOnce, cachedDeviceInfo, nativeDeviceInfo;
201
204
  var init_NativeDeviceInfo = __esm({
202
205
  "src/utils/NativeDeviceInfo.ts"() {
@@ -223,6 +226,7 @@ var init_NativeDeviceInfo = __esm({
223
226
  cachedDeviceInfo = null;
224
227
  nativeDeviceInfo = {
225
228
  getDeviceInfo,
229
+ getCachedDeviceInfo,
226
230
  getInstallReferrer,
227
231
  isAvailable,
228
232
  clearCache
@@ -483,7 +487,7 @@ var init_IdentityStorage = __esm({
483
487
  });
484
488
 
485
489
  // src/domains/identity/IdentityManager.ts
486
- var EMAIL_REGEX, IdentityManager, identityManager;
490
+ var USER_PHONE_KEY, USER_FIRST_NAME_KEY, USER_LAST_NAME_KEY, USER_DOB_KEY, USER_GENDER_KEY, EMAIL_REGEX, IdentityManager, identityManager;
487
491
  var init_IdentityManager = __esm({
488
492
  "src/domains/identity/IdentityManager.ts"() {
489
493
  "use strict";
@@ -493,6 +497,11 @@ var init_IdentityManager = __esm({
493
497
  init_SecureStorage();
494
498
  init_StorageMigration();
495
499
  init_IdentityStorage();
500
+ USER_PHONE_KEY = "@panel:user_phone";
501
+ USER_FIRST_NAME_KEY = "@panel:user_first_name";
502
+ USER_LAST_NAME_KEY = "@panel:user_last_name";
503
+ USER_DOB_KEY = "@panel:user_dob";
504
+ USER_GENDER_KEY = "@panel:user_gender";
496
505
  EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
497
506
  IdentityManager = class {
498
507
  constructor() {
@@ -500,6 +509,11 @@ var init_IdentityManager = __esm({
500
509
  this.anonId = null;
501
510
  this.email = null;
502
511
  this.properties = {};
512
+ this.phone = null;
513
+ this.firstName = null;
514
+ this.lastName = null;
515
+ this.dateOfBirth = null;
516
+ this.gender = null;
503
517
  this.apiClient = null;
504
518
  this.secureStorage = null;
505
519
  this.debug = false;
@@ -555,7 +569,7 @@ var init_IdentityManager = __esm({
555
569
  let email;
556
570
  let properties;
557
571
  if (options) {
558
- if ("email" in options || "properties" in options) {
572
+ if ("email" in options || "properties" in options || "phone" in options || "firstName" in options || "lastName" in options || "dateOfBirth" in options || "gender" in options) {
559
573
  email = options.email;
560
574
  properties = options.properties;
561
575
  } else {
@@ -575,9 +589,36 @@ var init_IdentityManager = __esm({
575
589
  this.properties = { ...this.properties, ...properties };
576
590
  await this.secureStorage.set(USER_PROPERTIES_KEY, JSON.stringify(this.properties));
577
591
  }
592
+ const opts = options;
593
+ if (opts?.phone) {
594
+ this.phone = opts.phone;
595
+ await this.secureStorage.set(USER_PHONE_KEY, this.phone);
596
+ }
597
+ if (opts?.firstName) {
598
+ this.firstName = opts.firstName;
599
+ await this.secureStorage.set(USER_FIRST_NAME_KEY, this.firstName);
600
+ }
601
+ if (opts?.lastName) {
602
+ this.lastName = opts.lastName;
603
+ await this.secureStorage.set(USER_LAST_NAME_KEY, this.lastName);
604
+ }
605
+ if (opts?.dateOfBirth) {
606
+ this.dateOfBirth = opts.dateOfBirth;
607
+ await this.secureStorage.set(USER_DOB_KEY, this.dateOfBirth);
608
+ }
609
+ if (opts?.gender) {
610
+ this.gender = opts.gender;
611
+ await this.secureStorage.set(USER_GENDER_KEY, this.gender);
612
+ }
578
613
  if (this.apiClient && this.anonId) {
579
614
  try {
580
- await this.apiClient.identify(this.anonId, properties, email, this.deviceId ?? void 0);
615
+ await this.apiClient.identify(this.anonId, properties, email, this.deviceId ?? void 0, {
616
+ phone: this.phone ?? void 0,
617
+ firstName: this.firstName ?? void 0,
618
+ lastName: this.lastName ?? void 0,
619
+ dateOfBirth: this.dateOfBirth ?? void 0,
620
+ gender: this.gender ?? void 0
621
+ });
581
622
  } catch {
582
623
  }
583
624
  }
@@ -612,17 +653,32 @@ var init_IdentityManager = __esm({
612
653
  this.distinctId = null;
613
654
  this.email = null;
614
655
  this.properties = {};
656
+ this.phone = null;
657
+ this.firstName = null;
658
+ this.lastName = null;
659
+ this.dateOfBirth = null;
660
+ this.gender = null;
615
661
  await Promise.all([
616
662
  this.secureStorage?.set(ANON_ID_KEY, newAnonId) ?? Promise.resolve(false),
617
663
  this.secureStorage?.remove(USER_EMAIL_KEY) ?? Promise.resolve(false),
618
- this.secureStorage?.remove(USER_PROPERTIES_KEY) ?? Promise.resolve(false)
664
+ this.secureStorage?.remove(USER_PROPERTIES_KEY) ?? Promise.resolve(false),
665
+ this.secureStorage?.remove(USER_PHONE_KEY) ?? Promise.resolve(false),
666
+ this.secureStorage?.remove(USER_FIRST_NAME_KEY) ?? Promise.resolve(false),
667
+ this.secureStorage?.remove(USER_LAST_NAME_KEY) ?? Promise.resolve(false),
668
+ this.secureStorage?.remove(USER_DOB_KEY) ?? Promise.resolve(false),
669
+ this.secureStorage?.remove(USER_GENDER_KEY) ?? Promise.resolve(false)
619
670
  ]);
620
671
  }
621
672
  getState() {
622
673
  return {
623
674
  deviceId: this.deviceId ?? "",
624
675
  email: this.email,
625
- properties: this.getProperties()
676
+ properties: this.getProperties(),
677
+ phone: this.phone,
678
+ firstName: this.firstName,
679
+ lastName: this.lastName,
680
+ dateOfBirth: this.dateOfBirth,
681
+ gender: this.gender
626
682
  };
627
683
  }
628
684
  isSecureStorageAvailable() {
@@ -647,6 +703,18 @@ var init_IdentityManager = __esm({
647
703
  if (state.email) this.email = state.email;
648
704
  if (state.propertiesJson) this.properties = parseProperties(state.propertiesJson);
649
705
  if (state.distinctId) this.distinctId = state.distinctId;
706
+ const [phone, firstName, lastName, dateOfBirth, gender] = await Promise.all([
707
+ this.secureStorage.get(USER_PHONE_KEY),
708
+ this.secureStorage.get(USER_FIRST_NAME_KEY),
709
+ this.secureStorage.get(USER_LAST_NAME_KEY),
710
+ this.secureStorage.get(USER_DOB_KEY),
711
+ this.secureStorage.get(USER_GENDER_KEY)
712
+ ]);
713
+ if (phone) this.phone = phone;
714
+ if (firstName) this.firstName = firstName;
715
+ if (lastName) this.lastName = lastName;
716
+ if (dateOfBirth) this.dateOfBirth = dateOfBirth;
717
+ if (gender) this.gender = gender;
650
718
  }
651
719
  ensureInitialized() {
652
720
  if (!this.initialized) {
@@ -787,7 +855,8 @@ var init_NativeStoreKit = __esm({
787
855
  );
788
856
  }
789
857
  const distinctId = identityManager.getDistinctId();
790
- const appAccountToken = isValidUUIDv4(distinctId) ? distinctId : null;
858
+ const rawUuid = distinctId?.startsWith("$paywallo_anon:") ? distinctId.slice("$paywallo_anon:".length) : distinctId;
859
+ const appAccountToken = isValidUUIDv4(rawUuid) ? rawUuid : null;
791
860
  if (distinctId && appAccountToken === null) {
792
861
  if (PaywalloClient.getConfig()?.debug) console.log("[Paywallo] distinctId is not a valid UUID v4 \u2014 passing null to appAccountToken:", distinctId);
793
862
  }
@@ -2268,6 +2337,7 @@ import { useCallback as useCallback2, useEffect, useRef, useState } from "react"
2268
2337
  import { Animated, AppState as AppState2, Dimensions } from "react-native";
2269
2338
  function usePaywallActions(placement, paywallConfig, onResult, variantKey, campaignId, openAnim, variantId) {
2270
2339
  const [isPurchasing, setIsPurchasing] = useState(false);
2340
+ const isPurchasingRef = useRef(false);
2271
2341
  const [isClosing, setIsClosing] = useState(false);
2272
2342
  const slideAnim = useRef(new Animated.Value(0)).current;
2273
2343
  const presentedAtRef = useRef(Date.now());
@@ -2299,6 +2369,7 @@ function usePaywallActions(placement, paywallConfig, onResult, variantKey, campa
2299
2369
  }, [isClosing, closeWithReason]);
2300
2370
  useEffect(() => {
2301
2371
  const sub = AppState2.addEventListener("change", (nextState) => {
2372
+ if (isPurchasingRef.current) return;
2302
2373
  if (nextState === "background" || nextState === "inactive") {
2303
2374
  closeWithReason("backgrounded");
2304
2375
  }
@@ -2314,6 +2385,7 @@ function usePaywallActions(placement, paywallConfig, onResult, variantKey, campa
2314
2385
  const handlePurchase = useCallback2(async (productId) => {
2315
2386
  if (isPurchasing) return;
2316
2387
  setIsPurchasing(true);
2388
+ isPurchasingRef.current = true;
2317
2389
  try {
2318
2390
  if (paywallConfig) trackProductSelected({ placement, paywallId: paywallConfig.id, productId, variantKey, campaignId, variantId });
2319
2391
  const result = await getIAPService().purchase(productId, {
@@ -2336,12 +2408,14 @@ function usePaywallActions(placement, paywallConfig, onResult, variantKey, campa
2336
2408
  } catch (error) {
2337
2409
  onResult({ presented: true, purchased: false, cancelled: false, restored: false });
2338
2410
  } finally {
2411
+ isPurchasingRef.current = false;
2339
2412
  setIsPurchasing(false);
2340
2413
  }
2341
2414
  }, [isPurchasing, onResult, paywallConfig, placement, trackDismissed, trackProductSelected, trackPurchased, variantKey, campaignId, variantId]);
2342
2415
  const handleRestore = useCallback2(async () => {
2343
2416
  if (isPurchasing) return;
2344
2417
  setIsPurchasing(true);
2418
+ isPurchasingRef.current = true;
2345
2419
  try {
2346
2420
  const transactions = await getIAPService().restore();
2347
2421
  if (transactions.length > 0) {
@@ -2356,6 +2430,7 @@ function usePaywallActions(placement, paywallConfig, onResult, variantKey, campa
2356
2430
  } catch (error) {
2357
2431
  onResult({ presented: true, purchased: false, cancelled: false, restored: false });
2358
2432
  } finally {
2433
+ isPurchasingRef.current = false;
2359
2434
  setIsPurchasing(false);
2360
2435
  }
2361
2436
  }, [isPurchasing, onResult, paywallConfig, placement, trackDismissed, variantKey, campaignId, variantId]);
@@ -2457,7 +2532,8 @@ function usePaywallLoader(placement, visible, preloadedConfig, preloadedProducts
2457
2532
  if (!trackingDoneRef.current && !cancelled) {
2458
2533
  trackingDoneRef.current = true;
2459
2534
  const sessionId = PaywalloClient.getSessionId();
2460
- await apiClient.trackEvent("$paywall_viewed", PaywalloClient.getDistinctId(), {
2535
+ const distinctId = PaywalloClient.getDistinctId();
2536
+ await apiClient.trackEvent("$paywall_viewed", distinctId, {
2461
2537
  placement,
2462
2538
  paywallId: config.id,
2463
2539
  ...sessionId && { sessionId },
@@ -2465,6 +2541,16 @@ function usePaywallLoader(placement, visible, preloadedConfig, preloadedProducts
2465
2541
  ...campaignId && { campaignId },
2466
2542
  ...variantId && { variant_id: variantId }
2467
2543
  });
2544
+ await apiClient.trackEvent("paywall", distinctId, {
2545
+ type: "open",
2546
+ paywall_id: config.id,
2547
+ placement,
2548
+ opened_at: (/* @__PURE__ */ new Date()).toISOString(),
2549
+ ...sessionId && { sessionId },
2550
+ ...variantKey && { variant_key: variantKey },
2551
+ ...campaignId && { campaign_id: campaignId },
2552
+ ...variantId && { variant_id: variantId }
2553
+ });
2468
2554
  }
2469
2555
  } catch (err) {
2470
2556
  if (cancelled) return;
@@ -2649,15 +2735,21 @@ import {
2649
2735
  } from "react-native";
2650
2736
  import { jsx } from "react/jsx-runtime";
2651
2737
  function getNativeWebView() {
2652
- if (!NativeWebViewComponent) {
2738
+ if (NativeWebViewComponent) return NativeWebViewComponent;
2739
+ if (nativeWebViewLookupFailed) return null;
2740
+ try {
2653
2741
  NativeWebViewComponent = requireNativeComponent("PaywalloWebView");
2742
+ return NativeWebViewComponent;
2743
+ } catch {
2744
+ nativeWebViewLookupFailed = true;
2745
+ return null;
2654
2746
  }
2655
- return NativeWebViewComponent;
2656
2747
  }
2657
2748
  function getWebViewEmitter() {
2658
- if (!webViewEmitter) {
2659
- webViewEmitter = new NativeEventEmitter2(NativeModules5.PaywalloWebViewModule);
2660
- }
2749
+ if (webViewEmitter) return webViewEmitter;
2750
+ const mod = NativeModules5.PaywalloWebViewModule;
2751
+ if (!mod) return null;
2752
+ webViewEmitter = new NativeEventEmitter2(mod);
2661
2753
  return webViewEmitter;
2662
2754
  }
2663
2755
  function parseErrorEvent(raw) {
@@ -2689,6 +2781,14 @@ function PaywallWebView({
2689
2781
  const onReadyRef = useRef3(onReady);
2690
2782
  onReadyRef.current = onReady;
2691
2783
  const NativeWebView = useMemo(() => getNativeWebView(), []);
2784
+ useEffect3(() => {
2785
+ if (!NativeWebView) {
2786
+ onErrorRef.current?.({
2787
+ code: -1,
2788
+ description: "Paywallo native module not linked. Run `pod install` (iOS) or rebuild the Android app."
2789
+ });
2790
+ }
2791
+ }, [NativeWebView]);
2692
2792
  const [resolvedWebUrl, setResolvedWebUrl] = useState3(
2693
2793
  () => webUrlProp ?? PaywalloClient.getWebUrl()
2694
2794
  );
@@ -2780,6 +2880,7 @@ function PaywallWebView({
2780
2880
  );
2781
2881
  useEffect3(() => {
2782
2882
  const emitter = getWebViewEmitter();
2883
+ if (!emitter) return;
2783
2884
  const loadEndSub = emitter.addListener("PaywalloWebViewLoadEnd", () => {
2784
2885
  if (!loadEndReceived.current) {
2785
2886
  loadEndReceived.current = true;
@@ -2846,7 +2947,7 @@ function PaywallWebView({
2846
2947
  if (!paywallDataSent.current) return;
2847
2948
  setScriptToInject(buildPurchaseStateScript(isPurchasing));
2848
2949
  }, [isPurchasing]);
2849
- if (!resolvedWebUrl) return null;
2950
+ if (!resolvedWebUrl || !NativeWebView) return null;
2850
2951
  const paywallUrl = `${resolvedWebUrl}/paywall/${paywallId}`;
2851
2952
  return /* @__PURE__ */ jsx(View, { style: styles.container, children: /* @__PURE__ */ jsx(
2852
2953
  NativeWebView,
@@ -2860,7 +2961,7 @@ function PaywallWebView({
2860
2961
  }
2861
2962
  ) });
2862
2963
  }
2863
- var NativeWebViewComponent, webViewEmitter, styles;
2964
+ var NativeWebViewComponent, nativeWebViewLookupFailed, webViewEmitter, styles;
2864
2965
  var init_PaywallWebView = __esm({
2865
2966
  "src/domains/paywall/PaywallWebView.tsx"() {
2866
2967
  "use strict";
@@ -2868,6 +2969,7 @@ var init_PaywallWebView = __esm({
2868
2969
  init_parseWebViewMessage();
2869
2970
  init_paywallScripts();
2870
2971
  NativeWebViewComponent = null;
2972
+ nativeWebViewLookupFailed = false;
2871
2973
  webViewEmitter = null;
2872
2974
  styles = StyleSheet.create({
2873
2975
  container: { flex: 1 },
@@ -3194,7 +3296,8 @@ function usePaywallPresenter(preloadedWebView, setPreloadedWebView) {
3194
3296
  const apiClient = PaywalloClient.getApiClient();
3195
3297
  if (apiClient && preloadedWebView) {
3196
3298
  const sessionId = PaywalloClient.getSessionId();
3197
- void apiClient.trackEvent("$paywall_viewed", PaywalloClient.getDistinctId(), {
3299
+ const distinctId = PaywalloClient.getDistinctId();
3300
+ void apiClient.trackEvent("$paywall_viewed", distinctId, {
3198
3301
  placement: preloadedWebView.placement,
3199
3302
  paywallId: preloadedWebView.paywallId,
3200
3303
  preloaded: true,
@@ -3204,6 +3307,18 @@ function usePaywallPresenter(preloadedWebView, setPreloadedWebView) {
3204
3307
  ...sessionId && { sessionId }
3205
3308
  }).catch(() => {
3206
3309
  });
3310
+ void apiClient.trackEvent("paywall", distinctId, {
3311
+ type: "open",
3312
+ paywall_id: preloadedWebView.paywallId,
3313
+ placement: preloadedWebView.placement,
3314
+ opened_at: (/* @__PURE__ */ new Date()).toISOString(),
3315
+ preloaded: true,
3316
+ ...preloadedWebView.variantKey && { variant_key: preloadedWebView.variantKey },
3317
+ ...preloadedWebView.campaignId && { campaign_id: preloadedWebView.campaignId },
3318
+ ...preloadedWebView.variantId && { variant_id: preloadedWebView.variantId },
3319
+ ...sessionId && { sessionId }
3320
+ }).catch(() => {
3321
+ });
3207
3322
  }
3208
3323
  });
3209
3324
  }, [preloadedWebView]);
@@ -3296,6 +3411,8 @@ var init_CampaignGateService = __esm({
3296
3411
  constructor() {
3297
3412
  this.preloadedCampaigns = /* @__PURE__ */ new Map();
3298
3413
  this.pendingPreloads = /* @__PURE__ */ new Set();
3414
+ this.apiClientRef = null;
3415
+ this.STALE_THRESHOLD = 0.8;
3299
3416
  this.activePreloadPromises = /* @__PURE__ */ new Map();
3300
3417
  }
3301
3418
  async getCampaign(apiClient, placement, context) {
@@ -3378,12 +3495,29 @@ var init_CampaignGateService = __esm({
3378
3495
  getPreloadedCampaign(placement) {
3379
3496
  const cached = this.preloadedCampaigns.get(placement);
3380
3497
  if (!cached) return null;
3381
- if (Date.now() - cached.preloadedAt >= PRELOAD_CACHE_TTL_MS) {
3498
+ const age = Date.now() - cached.preloadedAt;
3499
+ if (age >= PRELOAD_CACHE_TTL_MS) {
3382
3500
  this.preloadedCampaigns.delete(placement);
3383
3501
  return null;
3384
3502
  }
3503
+ if (age >= PRELOAD_CACHE_TTL_MS * this.STALE_THRESHOLD && this.apiClientRef) {
3504
+ void this.preloadCampaign(this.apiClientRef, placement).catch(() => {
3505
+ });
3506
+ }
3385
3507
  return cached.campaign;
3386
3508
  }
3509
+ async preloadAllActive(apiClient, debug) {
3510
+ this.apiClientRef = apiClient;
3511
+ const placements = await apiClient.getActivePlacements();
3512
+ if (placements.length === 0) return;
3513
+ if (debug) console.log(`[Paywallo PRELOAD] auto-preloading ${placements.length} placements`);
3514
+ for (let i = 0; i < placements.length; i++) {
3515
+ if (i > 0) await new Promise((r) => setTimeout(r, 500));
3516
+ void this.preloadCampaign(apiClient, placements[i]).catch((err) => {
3517
+ if (debug) console.log(`[Paywallo PRELOAD] failed ${placements[i]}:`, err);
3518
+ });
3519
+ }
3520
+ }
3387
3521
  clearPreloaded() {
3388
3522
  this.preloadedCampaigns.clear();
3389
3523
  this.pendingPreloads.clear();
@@ -3404,7 +3538,7 @@ var init_campaign = __esm({
3404
3538
 
3405
3539
  // src/domains/identity/AdvertisingIdManager.ts
3406
3540
  import { Platform as Platform5 } from "react-native";
3407
- var ZERO_IDFA, AdvertisingIdManager;
3541
+ var ZERO_IDFA, AdvertisingIdManager, advertisingIdManager;
3408
3542
  var init_AdvertisingIdManager = __esm({
3409
3543
  "src/domains/identity/AdvertisingIdManager.ts"() {
3410
3544
  "use strict";
@@ -3414,6 +3548,14 @@ var init_AdvertisingIdManager = __esm({
3414
3548
  constructor() {
3415
3549
  this.cached = null;
3416
3550
  }
3551
+ /**
3552
+ * Synchronous accessor for the cached collect() result. Returns null until
3553
+ * `collect()` resolves at least once. Used by the V2 envelope context
3554
+ * provider — which is sync — to populate `context.ids` without awaiting.
3555
+ */
3556
+ getCached() {
3557
+ return this.cached;
3558
+ }
3417
3559
  async collect(requestATT = false) {
3418
3560
  if (this.cached) return this.cached;
3419
3561
  const fallback = { idfv: null, idfa: null, gaid: null, attStatus: "unavailable" };
@@ -3464,136 +3606,26 @@ var init_AdvertisingIdManager = __esm({
3464
3606
  return id;
3465
3607
  }
3466
3608
  };
3609
+ advertisingIdManager = new AdvertisingIdManager();
3467
3610
  }
3468
3611
  });
3469
3612
 
3470
- // src/domains/identity/MetaBridge.ts
3471
- import { NativeModules as NativeModules6 } from "react-native";
3472
- var TIMEOUT_MS, NativeBridge, MetaBridge, metaBridge;
3473
- var init_MetaBridge = __esm({
3474
- "src/domains/identity/MetaBridge.ts"() {
3475
- "use strict";
3476
- TIMEOUT_MS = 2e3;
3477
- NativeBridge = NativeModules6.PaywalloFBBridge ?? null;
3478
- MetaBridge = class {
3479
- async getAnonymousID() {
3480
- if (!NativeBridge) return null;
3481
- try {
3482
- let timerId;
3483
- const timeout = new Promise((resolve) => {
3484
- timerId = setTimeout(() => resolve(null), TIMEOUT_MS);
3485
- });
3486
- const result = await Promise.race([NativeBridge.getAnonymousID(), timeout]);
3487
- clearTimeout(timerId);
3488
- return result;
3489
- } catch {
3490
- return null;
3491
- }
3492
- }
3493
- async logEvent(name, params = {}) {
3494
- if (!NativeBridge) return;
3495
- try {
3496
- await NativeBridge.logEvent(name, params);
3497
- } catch (error) {
3498
- console.warn("[Paywallo:MetaBridge] logEvent failed", { name, error });
3499
- }
3500
- }
3501
- async logPurchase(amount, currency, params = {}) {
3502
- if (!NativeBridge) return;
3503
- try {
3504
- await NativeBridge.logPurchase(amount, currency, params);
3505
- } catch (error) {
3506
- console.warn("[Paywallo:MetaBridge] logPurchase failed", { amount, currency, error });
3507
- }
3508
- }
3509
- setUserID(userId) {
3510
- if (!NativeBridge) return;
3511
- try {
3512
- NativeBridge.setUserID(userId);
3513
- } catch (error) {
3514
- console.warn("[Paywallo:MetaBridge] setUserID failed", { error });
3515
- }
3516
- }
3517
- flush() {
3518
- if (!NativeBridge) return;
3519
- try {
3520
- NativeBridge.flush();
3521
- } catch (error) {
3522
- console.warn("[Paywallo:MetaBridge] flush failed", { error });
3523
- }
3524
- }
3525
- };
3526
- metaBridge = new MetaBridge();
3613
+ // src/domains/session/distinctIdGuard.ts
3614
+ async function waitForDistinctId(provider, retries = DISTINCT_ID_WAIT_RETRIES, intervalMs = DISTINCT_ID_WAIT_INTERVAL_MS) {
3615
+ if (!provider) return true;
3616
+ if (provider()) return true;
3617
+ for (let attempt = 0; attempt < retries; attempt++) {
3618
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
3619
+ if (provider()) return true;
3527
3620
  }
3528
- });
3529
-
3530
- // src/domains/identity/InstallReferrerManager.ts
3531
- import { Platform as Platform6 } from "react-native";
3532
- var REFERRER_TIMEOUT_MS, InstallReferrerManager, installReferrerManager;
3533
- var init_InstallReferrerManager = __esm({
3534
- "src/domains/identity/InstallReferrerManager.ts"() {
3621
+ return false;
3622
+ }
3623
+ var DISTINCT_ID_WAIT_RETRIES, DISTINCT_ID_WAIT_INTERVAL_MS;
3624
+ var init_distinctIdGuard = __esm({
3625
+ "src/domains/session/distinctIdGuard.ts"() {
3535
3626
  "use strict";
3536
- init_NativeDeviceInfo();
3537
- REFERRER_TIMEOUT_MS = 5e3;
3538
- InstallReferrerManager = class {
3539
- async getReferrer() {
3540
- if (Platform6.OS !== "android") {
3541
- return this.emptyResult();
3542
- }
3543
- let timerId;
3544
- const timeoutPromise = new Promise((resolve) => {
3545
- timerId = setTimeout(() => resolve(this.emptyResult()), REFERRER_TIMEOUT_MS);
3546
- });
3547
- const fetchPromise = this.fetchReferrer();
3548
- const result = await Promise.race([fetchPromise, timeoutPromise]);
3549
- clearTimeout(timerId);
3550
- return result;
3551
- }
3552
- async fetchReferrer() {
3553
- try {
3554
- const referrer = await nativeDeviceInfo.getInstallReferrer();
3555
- if (!referrer) return this.emptyResult();
3556
- const capped = referrer.slice(0, 2048);
3557
- const params = this.parseReferrer(capped);
3558
- return {
3559
- rawReferrer: capped,
3560
- trackingId: params["tracking_id"] || null,
3561
- fbclid: params["fbclid"] || null,
3562
- utmSource: params["utm_source"] || null,
3563
- utmMedium: params["utm_medium"] || null,
3564
- utmCampaign: params["utm_campaign"] || null
3565
- };
3566
- } catch {
3567
- return this.emptyResult();
3568
- }
3569
- }
3570
- parseReferrer(referrer) {
3571
- const result = {};
3572
- for (const pair of referrer.split("&")) {
3573
- const eqIndex = pair.indexOf("=");
3574
- if (eqIndex === -1) continue;
3575
- try {
3576
- const key = decodeURIComponent(pair.slice(0, eqIndex).replace(/\+/g, " "));
3577
- const raw = pair.slice(eqIndex + 1).replace(/\+/g, " ");
3578
- const value = decodeURIComponent(raw);
3579
- if (key) result[key] = value;
3580
- } catch {
3581
- }
3582
- }
3583
- return result;
3584
- }
3585
- emptyResult() {
3586
- return {
3587
- rawReferrer: null,
3588
- trackingId: null,
3589
- fbclid: null,
3590
- utmSource: null,
3591
- utmMedium: null,
3592
- utmCampaign: null
3593
- };
3594
- }
3595
- };
3596
- installReferrerManager = new InstallReferrerManager();
3627
+ DISTINCT_ID_WAIT_RETRIES = 10;
3628
+ DISTINCT_ID_WAIT_INTERVAL_MS = 100;
3597
3629
  }
3598
3630
  });
3599
3631
 
@@ -3618,25 +3650,6 @@ var init_SessionError = __esm({
3618
3650
  }
3619
3651
  });
3620
3652
 
3621
- // src/domains/session/distinctIdGuard.ts
3622
- async function waitForDistinctId(provider, retries = DISTINCT_ID_WAIT_RETRIES, intervalMs = DISTINCT_ID_WAIT_INTERVAL_MS) {
3623
- if (!provider) return true;
3624
- if (provider()) return true;
3625
- for (let attempt = 0; attempt < retries; attempt++) {
3626
- await new Promise((resolve) => setTimeout(resolve, intervalMs));
3627
- if (provider()) return true;
3628
- }
3629
- return false;
3630
- }
3631
- var DISTINCT_ID_WAIT_RETRIES, DISTINCT_ID_WAIT_INTERVAL_MS;
3632
- var init_distinctIdGuard = __esm({
3633
- "src/domains/session/distinctIdGuard.ts"() {
3634
- "use strict";
3635
- DISTINCT_ID_WAIT_RETRIES = 10;
3636
- DISTINCT_ID_WAIT_INTERVAL_MS = 100;
3637
- }
3638
- });
3639
-
3640
3653
  // src/utils/deviceInfo.ts
3641
3654
  var deviceInfo_exports = {};
3642
3655
  __export(deviceInfo_exports, {
@@ -3647,7 +3660,9 @@ async function collectDeviceInfo() {
3647
3660
  return {
3648
3661
  appVersion: result.appVersion,
3649
3662
  deviceModel: result.model,
3650
- osVersion: result.systemVersion
3663
+ osVersion: result.systemVersion,
3664
+ darwinVersion: result.darwinVersion,
3665
+ webkitVersion: result.webkitVersion
3651
3666
  };
3652
3667
  }
3653
3668
  var init_deviceInfo = __esm({
@@ -3947,57 +3962,6 @@ var init_SessionManager = __esm({
3947
3962
  }
3948
3963
  });
3949
3964
 
3950
- // src/domains/identity/InstallIdempotency.ts
3951
- async function checkAndArmInstallGuard(storage) {
3952
- const alreadyTracked = await storage.get(INSTALL_KEYS.INSTALL_TRACKED);
3953
- if (alreadyTracked) return true;
3954
- let fallbackTracked = null;
3955
- try {
3956
- fallbackTracked = await nativeStorage.get(INSTALL_KEYS.LEGACY_INSTALL_TRACKED);
3957
- } catch {
3958
- }
3959
- if (fallbackTracked) return true;
3960
- await nativeStorage.set(INSTALL_KEYS.INSTALL_SENT, "1");
3961
- return false;
3962
- }
3963
- async function markInstallTracked(storage, installedAt) {
3964
- await storage.set(INSTALL_KEYS.INSTALL_TRACKED, String(installedAt));
3965
- try {
3966
- await nativeStorage.set(INSTALL_KEYS.LEGACY_INSTALL_TRACKED, String(installedAt));
3967
- } catch {
3968
- }
3969
- }
3970
- async function getOrCreateInstallEventId() {
3971
- const existing = await nativeStorage.get(INSTALL_KEYS.INSTALL_EVENT_ID);
3972
- if (existing) return existing;
3973
- const { generateUUID: generateUUID2 } = await Promise.resolve().then(() => (init_uuid(), uuid_exports));
3974
- const id = generateUUID2();
3975
- await nativeStorage.set(INSTALL_KEYS.INSTALL_EVENT_ID, id).catch(() => {
3976
- });
3977
- return id;
3978
- }
3979
- var INSTALL_KEYS;
3980
- var init_InstallIdempotency = __esm({
3981
- "src/domains/identity/InstallIdempotency.ts"() {
3982
- "use strict";
3983
- init_NativeStorage();
3984
- INSTALL_KEYS = {
3985
- /** Primary flag — set AFTER a successful $app_installed dispatch. */
3986
- INSTALL_TRACKED: "@panel:install_tracked",
3987
- /** Legacy fallback flag written by older SDK versions. */
3988
- LEGACY_INSTALL_TRACKED: "@paywallo:install_tracked",
3989
- /** Pre-send guard — set BEFORE dispatching to survive concurrent cold-starts. */
3990
- INSTALL_SENT: "@paywallo:app_installed_sent",
3991
- /** Stable event ID for backend dedup — generated once, reused on retries. */
3992
- INSTALL_EVENT_ID: "@panel:install_event_id",
3993
- /** Deferred match flag — set once iOS fingerprint match succeeds. */
3994
- DEFERRED_MATCH_DONE: "@panel:deferred_match_done",
3995
- /** Anonymous ID fallback when FB SDK not available. */
3996
- ANON_ID: "@panel:anon_id"
3997
- };
3998
- }
3999
- });
4000
-
4001
3965
  // src/domains/identity/AttributionTracker.ts
4002
3966
  function hasAnyField(data) {
4003
3967
  return Object.values(data).some((v) => v !== void 0 && v !== null && v !== "");
@@ -4069,21 +4033,212 @@ var init_AttributionTracker = __esm({
4069
4033
  }
4070
4034
  });
4071
4035
 
4072
- // src/domains/identity/InstallTracker.ts
4073
- import { Dimensions as Dimensions3, Platform as Platform7 } from "react-native";
4074
- var InstallTracker;
4075
- var init_InstallTracker = __esm({
4076
- "src/domains/identity/InstallTracker.ts"() {
4077
- "use strict";
4078
- init_NativeDeviceInfo();
4079
- init_SecureStorage();
4080
- init_AdvertisingIdManager();
4081
- init_MetaBridge();
4082
- init_IdentityManager();
4083
- init_InstallReferrerManager();
4084
- init_SessionManager();
4036
+ // src/domains/identity/InstallIdempotency.ts
4037
+ async function checkAndArmInstallGuard(storage) {
4038
+ const alreadyTracked = await storage.get(INSTALL_KEYS.INSTALL_TRACKED);
4039
+ if (alreadyTracked) return true;
4040
+ let fallbackTracked = null;
4041
+ try {
4042
+ fallbackTracked = await nativeStorage.get(INSTALL_KEYS.LEGACY_INSTALL_TRACKED);
4043
+ } catch {
4044
+ }
4045
+ if (fallbackTracked) return true;
4046
+ await nativeStorage.set(INSTALL_KEYS.INSTALL_SENT, "1");
4047
+ return false;
4048
+ }
4049
+ async function markInstallTracked(storage, installedAt) {
4050
+ await storage.set(INSTALL_KEYS.INSTALL_TRACKED, String(installedAt));
4051
+ try {
4052
+ await nativeStorage.set(INSTALL_KEYS.LEGACY_INSTALL_TRACKED, String(installedAt));
4053
+ } catch {
4054
+ }
4055
+ }
4056
+ async function getOrCreateInstallEventId() {
4057
+ const existing = await nativeStorage.get(INSTALL_KEYS.INSTALL_EVENT_ID);
4058
+ if (existing) return existing;
4059
+ const { generateUUID: generateUUID2 } = await Promise.resolve().then(() => (init_uuid(), uuid_exports));
4060
+ const id = generateUUID2();
4061
+ await nativeStorage.set(INSTALL_KEYS.INSTALL_EVENT_ID, id).catch(() => {
4062
+ });
4063
+ return id;
4064
+ }
4065
+ var INSTALL_KEYS;
4066
+ var init_InstallIdempotency = __esm({
4067
+ "src/domains/identity/InstallIdempotency.ts"() {
4068
+ "use strict";
4069
+ init_NativeStorage();
4070
+ INSTALL_KEYS = {
4071
+ /** Primary flag — set AFTER a successful $app_installed dispatch. */
4072
+ INSTALL_TRACKED: "@panel:install_tracked",
4073
+ /** Legacy fallback flag written by older SDK versions. */
4074
+ LEGACY_INSTALL_TRACKED: "@paywallo:install_tracked",
4075
+ /** Pre-send guard — set BEFORE dispatching to survive concurrent cold-starts. */
4076
+ INSTALL_SENT: "@paywallo:app_installed_sent",
4077
+ /** Stable event ID for backend dedup — generated once, reused on retries. */
4078
+ INSTALL_EVENT_ID: "@panel:install_event_id",
4079
+ /** Deferred match flag — set once iOS fingerprint match succeeds. */
4080
+ DEFERRED_MATCH_DONE: "@panel:deferred_match_done",
4081
+ /** Anonymous ID fallback when FB SDK not available. */
4082
+ ANON_ID: "@panel:anon_id"
4083
+ };
4084
+ }
4085
+ });
4086
+
4087
+ // src/domains/identity/InstallReferrerManager.ts
4088
+ import { Platform as Platform6 } from "react-native";
4089
+ var REFERRER_TIMEOUT_MS, InstallReferrerManager, installReferrerManager;
4090
+ var init_InstallReferrerManager = __esm({
4091
+ "src/domains/identity/InstallReferrerManager.ts"() {
4092
+ "use strict";
4093
+ init_NativeDeviceInfo();
4094
+ REFERRER_TIMEOUT_MS = 5e3;
4095
+ InstallReferrerManager = class {
4096
+ async getReferrer() {
4097
+ if (Platform6.OS !== "android") {
4098
+ return this.emptyResult();
4099
+ }
4100
+ let timerId;
4101
+ const timeoutPromise = new Promise((resolve) => {
4102
+ timerId = setTimeout(() => resolve(this.emptyResult()), REFERRER_TIMEOUT_MS);
4103
+ });
4104
+ const fetchPromise = this.fetchReferrer();
4105
+ const result = await Promise.race([fetchPromise, timeoutPromise]);
4106
+ clearTimeout(timerId);
4107
+ return result;
4108
+ }
4109
+ async fetchReferrer() {
4110
+ try {
4111
+ const referrer = await nativeDeviceInfo.getInstallReferrer();
4112
+ if (!referrer) return this.emptyResult();
4113
+ const capped = referrer.slice(0, 2048);
4114
+ const params = this.parseReferrer(capped);
4115
+ return {
4116
+ rawReferrer: capped,
4117
+ trackingId: params["tracking_id"] || null,
4118
+ fbclid: params["fbclid"] || null,
4119
+ utmSource: params["utm_source"] || null,
4120
+ utmMedium: params["utm_medium"] || null,
4121
+ utmCampaign: params["utm_campaign"] || null
4122
+ };
4123
+ } catch {
4124
+ return this.emptyResult();
4125
+ }
4126
+ }
4127
+ parseReferrer(referrer) {
4128
+ const result = {};
4129
+ for (const pair of referrer.split("&")) {
4130
+ const eqIndex = pair.indexOf("=");
4131
+ if (eqIndex === -1) continue;
4132
+ try {
4133
+ const key = decodeURIComponent(pair.slice(0, eqIndex).replace(/\+/g, " "));
4134
+ const raw = pair.slice(eqIndex + 1).replace(/\+/g, " ");
4135
+ const value = decodeURIComponent(raw);
4136
+ if (key) result[key] = value;
4137
+ } catch {
4138
+ }
4139
+ }
4140
+ return result;
4141
+ }
4142
+ emptyResult() {
4143
+ return {
4144
+ rawReferrer: null,
4145
+ trackingId: null,
4146
+ fbclid: null,
4147
+ utmSource: null,
4148
+ utmMedium: null,
4149
+ utmCampaign: null
4150
+ };
4151
+ }
4152
+ };
4153
+ installReferrerManager = new InstallReferrerManager();
4154
+ }
4155
+ });
4156
+
4157
+ // src/domains/identity/MetaBridge.ts
4158
+ import { NativeModules as NativeModules6 } from "react-native";
4159
+ var TIMEOUT_MS, NativeBridge, MetaBridge, metaBridge;
4160
+ var init_MetaBridge = __esm({
4161
+ "src/domains/identity/MetaBridge.ts"() {
4162
+ "use strict";
4163
+ TIMEOUT_MS = 2e3;
4164
+ NativeBridge = NativeModules6.PaywalloFBBridge ?? null;
4165
+ MetaBridge = class {
4166
+ constructor() {
4167
+ this.cachedAnonId = null;
4168
+ }
4169
+ /** Sync read of the cached anonymous ID. Returns null until `getAnonymousID()` resolves once. */
4170
+ getCachedAnonymousID() {
4171
+ return this.cachedAnonId;
4172
+ }
4173
+ async getAnonymousID() {
4174
+ if (this.cachedAnonId) return this.cachedAnonId;
4175
+ if (!NativeBridge) return null;
4176
+ try {
4177
+ let timerId;
4178
+ const timeout = new Promise((resolve) => {
4179
+ timerId = setTimeout(() => resolve(null), TIMEOUT_MS);
4180
+ });
4181
+ const result = await Promise.race([NativeBridge.getAnonymousID(), timeout]);
4182
+ clearTimeout(timerId);
4183
+ if (result) this.cachedAnonId = result;
4184
+ return result;
4185
+ } catch {
4186
+ return null;
4187
+ }
4188
+ }
4189
+ async logEvent(name, params = {}) {
4190
+ if (!NativeBridge) return;
4191
+ try {
4192
+ await NativeBridge.logEvent(name, params);
4193
+ } catch (error) {
4194
+ console.warn("[Paywallo:MetaBridge] logEvent failed", { name, error });
4195
+ }
4196
+ }
4197
+ async logPurchase(amount, currency, params = {}) {
4198
+ if (!NativeBridge) return;
4199
+ try {
4200
+ await NativeBridge.logPurchase(amount, currency, params);
4201
+ } catch (error) {
4202
+ console.warn("[Paywallo:MetaBridge] logPurchase failed", { amount, currency, error });
4203
+ }
4204
+ }
4205
+ setUserID(userId) {
4206
+ if (!NativeBridge) return;
4207
+ try {
4208
+ NativeBridge.setUserID(userId);
4209
+ } catch (error) {
4210
+ console.warn("[Paywallo:MetaBridge] setUserID failed", { error });
4211
+ }
4212
+ }
4213
+ flush() {
4214
+ if (!NativeBridge) return;
4215
+ try {
4216
+ NativeBridge.flush();
4217
+ } catch (error) {
4218
+ console.warn("[Paywallo:MetaBridge] flush failed", { error });
4219
+ }
4220
+ }
4221
+ };
4222
+ metaBridge = new MetaBridge();
4223
+ }
4224
+ });
4225
+
4226
+ // src/domains/identity/InstallTracker.ts
4227
+ import { Dimensions as Dimensions3, Platform as Platform7, PixelRatio } from "react-native";
4228
+ var InstallTracker;
4229
+ var init_InstallTracker = __esm({
4230
+ "src/domains/identity/InstallTracker.ts"() {
4231
+ "use strict";
4232
+ init_SecureStorage();
4233
+ init_NativeDeviceInfo();
4085
4234
  init_distinctIdGuard();
4235
+ init_SessionManager();
4236
+ init_AdvertisingIdManager();
4237
+ init_AttributionTracker();
4238
+ init_IdentityManager();
4086
4239
  init_InstallIdempotency();
4240
+ init_InstallReferrerManager();
4241
+ init_MetaBridge();
4087
4242
  init_AttributionTracker();
4088
4243
  InstallTracker = class {
4089
4244
  constructor(debug = false, optionsOrManager) {
@@ -4094,7 +4249,7 @@ var init_InstallTracker = __esm({
4094
4249
  this.distinctIdProvider = () => identityManager.getDistinctId();
4095
4250
  } else {
4096
4251
  const opts = optionsOrManager ?? {};
4097
- this.advertisingIdManager = opts.advertisingIdManager ?? new AdvertisingIdManager();
4252
+ this.advertisingIdManager = opts.advertisingIdManager ?? advertisingIdManager;
4098
4253
  this.distinctIdProvider = opts.distinctIdProvider ?? (() => identityManager.getDistinctId());
4099
4254
  }
4100
4255
  }
@@ -4132,6 +4287,7 @@ var init_InstallTracker = __esm({
4132
4287
  const screen = Dimensions3.get("screen");
4133
4288
  const screenWidth = Math.round(screen.width ?? 0);
4134
4289
  const screenHeight = Math.round(screen.height ?? 0);
4290
+ const screenDensity = PixelRatio.get();
4135
4291
  const locale = Intl.DateTimeFormat().resolvedOptions().locale ?? "unknown";
4136
4292
  const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone ?? "unknown";
4137
4293
  const info = await nativeDeviceInfo.getDeviceInfo();
@@ -4178,6 +4334,7 @@ var init_InstallTracker = __esm({
4178
4334
  buildNumber,
4179
4335
  ...screenWidth > 0 && { screenWidth },
4180
4336
  ...screenHeight > 0 && { screenHeight },
4337
+ ...screenDensity > 0 && { screenDensity },
4181
4338
  timezone,
4182
4339
  locale,
4183
4340
  ...carrier !== "unknown" && { carrier },
@@ -4195,6 +4352,16 @@ var init_InstallTracker = __esm({
4195
4352
  this.advertisingIdManager.collect(requestATT),
4196
4353
  getOrCreateInstallEventId()
4197
4354
  ]);
4355
+ if (referrer.fbclid || referrer.utmSource || referrer.utmMedium || referrer.utmCampaign) {
4356
+ attributionTracker.capture({
4357
+ ...referrer.fbclid && { fbclid: referrer.fbclid },
4358
+ ...referrer.utmSource && { utmSource: referrer.utmSource },
4359
+ ...referrer.utmMedium && { utmMedium: referrer.utmMedium },
4360
+ ...referrer.utmCampaign && { utmCampaign: referrer.utmCampaign },
4361
+ ...referrer.rawReferrer && { referrer: referrer.rawReferrer }
4362
+ }).catch(() => {
4363
+ });
4364
+ }
4198
4365
  let effectiveAnonId = fbAnonId;
4199
4366
  if (!effectiveAnonId) {
4200
4367
  const stored = await storage.get(INSTALL_KEYS.ANON_ID);
@@ -4239,7 +4406,7 @@ var init_InstallTracker = __esm({
4239
4406
  const baseUrl = apiClient.getBaseUrl();
4240
4407
  const appKey = apiClient.getAppKey();
4241
4408
  const referrer = await installReferrerManager.getReferrer();
4242
- await fetch(`${baseUrl}/attribution/deferred-match/${appKey}`, {
4409
+ await fetch(`${baseUrl}/sdk/attribution/deferred-match/${appKey}`, {
4243
4410
  method: "POST",
4244
4411
  headers: { "Content-Type": "application/json", "X-App-Key": appKey },
4245
4412
  body: JSON.stringify({
@@ -4371,6 +4538,9 @@ var init_identity = __esm({
4371
4538
 
4372
4539
  // src/domains/notifications/bridges/NativePushBridge.ts
4373
4540
  import { NativeModules as NativeModules7, NativeEventEmitter as NativeEventEmitter3, Platform as Platform8 } from "react-native";
4541
+ function isNativePushAvailable() {
4542
+ return !!NativeModules7.PaywalloNotifications;
4543
+ }
4374
4544
  function mapStatusToNumber(status) {
4375
4545
  switch (status) {
4376
4546
  case "granted":
@@ -4887,6 +5057,16 @@ var init_PermissionManager = __esm({
4887
5057
  }
4888
5058
  });
4889
5059
 
5060
+ // src/core/constants.ts
5061
+ var DEFAULT_API_URL, DEFAULT_WEB_URL;
5062
+ var init_constants = __esm({
5063
+ "src/core/constants.ts"() {
5064
+ "use strict";
5065
+ DEFAULT_API_URL = "https://panel.lucasqueiroga.shop";
5066
+ DEFAULT_WEB_URL = "https://paywallo.com.br";
5067
+ }
5068
+ });
5069
+
4890
5070
  // src/domains/notifications/config.ts
4891
5071
  function resolveApiBaseUrl(config) {
4892
5072
  if (config.apiBaseUrl) return config.apiBaseUrl;
@@ -4897,8 +5077,9 @@ var DEFAULT_API_BASE_URL, SANDBOX_API_BASE_URL;
4897
5077
  var init_config = __esm({
4898
5078
  "src/domains/notifications/config.ts"() {
4899
5079
  "use strict";
4900
- DEFAULT_API_BASE_URL = "https://api.paywallo.com.br";
4901
- SANDBOX_API_BASE_URL = "https://sandbox.paywallo.com.br";
5080
+ init_constants();
5081
+ DEFAULT_API_BASE_URL = DEFAULT_API_URL;
5082
+ SANDBOX_API_BASE_URL = DEFAULT_API_URL;
4902
5083
  }
4903
5084
  });
4904
5085
 
@@ -4993,6 +5174,9 @@ function mapPermissionStatus2(status) {
4993
5174
  function detectPlatform2() {
4994
5175
  return Platform11.OS === "ios" ? "ios" : "android";
4995
5176
  }
5177
+ function detectEnvironment() {
5178
+ return typeof __DEV__ !== "undefined" && __DEV__ ? "sandbox" : "production";
5179
+ }
4996
5180
  async function getAppVersion() {
4997
5181
  try {
4998
5182
  const { collectDeviceInfo: collectDeviceInfo2 } = await Promise.resolve().then(() => (init_deviceInfo(), deviceInfo_exports));
@@ -5020,8 +5204,8 @@ var init_utils = __esm({
5020
5204
  // src/core/version.ts
5021
5205
  function resolveVersion() {
5022
5206
  try {
5023
- if ("2.0.2") {
5024
- return "2.0.2";
5207
+ if ("2.2.1") {
5208
+ return "2.2.1";
5025
5209
  }
5026
5210
  } catch {
5027
5211
  }
@@ -5060,7 +5244,8 @@ async function buildRegistration(token, getDeviceId, getDistinctId) {
5060
5244
  sdk_version: SDK_VERSION,
5061
5245
  deviceId: getDeviceId?.() ?? void 0,
5062
5246
  locale: getLocale(),
5063
- timezone: getTimezone2()
5247
+ timezone: getTimezone2(),
5248
+ environment: detectEnvironment()
5064
5249
  };
5065
5250
  }
5066
5251
  async function registerToken(deps, token) {
@@ -5102,7 +5287,7 @@ var init_TokenRegistration = __esm({
5102
5287
  init_utils();
5103
5288
  init_version();
5104
5289
  FCM_TOKEN_KEY = "@panel:push_token";
5105
- TOKEN_ENDPOINT = "/push-tokens";
5290
+ TOKEN_ENDPOINT = "/sdk/push-tokens";
5106
5291
  }
5107
5292
  });
5108
5293
 
@@ -5394,58 +5579,400 @@ var init_NotificationsManager = __esm({
5394
5579
  this.currentToken = null;
5395
5580
  }
5396
5581
  };
5397
- notificationsManager = new NotificationsManager();
5582
+ notificationsManager = new NotificationsManager();
5583
+ }
5584
+ });
5585
+
5586
+ // src/domains/notifications/index.ts
5587
+ var init_notifications = __esm({
5588
+ "src/domains/notifications/index.ts"() {
5589
+ "use strict";
5590
+ init_NotificationsManager();
5591
+ init_NativePushBridge();
5592
+ init_NotificationsError();
5593
+ }
5594
+ });
5595
+
5596
+ // src/domains/session/index.ts
5597
+ var init_session = __esm({
5598
+ "src/domains/session/index.ts"() {
5599
+ "use strict";
5600
+ init_SessionManager();
5601
+ init_SessionError();
5602
+ }
5603
+ });
5604
+
5605
+ // src/core/PaywalloState.ts
5606
+ function createPaywalloState() {
5607
+ return {
5608
+ config: null,
5609
+ apiClient: null,
5610
+ initPromise: null,
5611
+ readyPromise: null,
5612
+ resolveReady: null,
5613
+ paywallPresenter: null,
5614
+ campaignPresenter: null,
5615
+ subscriptionGetter: null,
5616
+ activeChecker: null,
5617
+ restoreHandler: null,
5618
+ lastOnboardingStepTimestamp: null,
5619
+ pendingConfig: null,
5620
+ networkCleanup: null,
5621
+ initAttempts: 0,
5622
+ autoPreloadedPlacement: null,
5623
+ autoPreloadPlacementPromise: null,
5624
+ sessionFlagsMap: /* @__PURE__ */ new Map(),
5625
+ pendingIdentifies: []
5626
+ };
5627
+ }
5628
+ var MAX_INIT_RETRIES, RETRY_DELAYS;
5629
+ var init_PaywalloState = __esm({
5630
+ "src/core/PaywalloState.ts"() {
5631
+ "use strict";
5632
+ MAX_INIT_RETRIES = 3;
5633
+ RETRY_DELAYS = [2e3, 5e3, 1e4];
5634
+ }
5635
+ });
5636
+
5637
+ // src/domains/plan/PlanCache.ts
5638
+ var DEFAULT_TTL, PlanCache, planCache;
5639
+ var init_PlanCache = __esm({
5640
+ "src/domains/plan/PlanCache.ts"() {
5641
+ "use strict";
5642
+ DEFAULT_TTL = 5 * 60 * 1e3;
5643
+ PlanCache = class {
5644
+ constructor() {
5645
+ this.cache = null;
5646
+ this.ttl = DEFAULT_TTL;
5647
+ }
5648
+ get() {
5649
+ if (!this.cache) return null;
5650
+ const isStale = Date.now() - this.cache.cachedAt > this.ttl;
5651
+ return { ...this.cache, isStale };
5652
+ }
5653
+ set(data) {
5654
+ this.cache = { data, cachedAt: Date.now(), isStale: false };
5655
+ }
5656
+ invalidate() {
5657
+ this.cache = null;
5658
+ }
5659
+ setTTL(ttl) {
5660
+ this.ttl = ttl;
5661
+ }
5662
+ };
5663
+ planCache = new PlanCache();
5664
+ }
5665
+ });
5666
+
5667
+ // src/domains/plan/PlanError.ts
5668
+ var PlanError, PLAN_ERROR_CODES;
5669
+ var init_PlanError = __esm({
5670
+ "src/domains/plan/PlanError.ts"() {
5671
+ "use strict";
5672
+ init_PaywalloError();
5673
+ PlanError = class extends PaywalloError {
5674
+ constructor(code, message) {
5675
+ super("plan", code, message);
5676
+ this.name = "PlanError";
5677
+ }
5678
+ };
5679
+ PLAN_ERROR_CODES = {
5680
+ NOT_INITIALIZED: "PLAN_NOT_INITIALIZED",
5681
+ FETCH_FAILED: "PLAN_FETCH_FAILED"
5682
+ };
5683
+ }
5684
+ });
5685
+
5686
+ // src/domains/plan/PlanService.ts
5687
+ var PlanService_exports = {};
5688
+ __export(PlanService_exports, {
5689
+ PlanService: () => PlanService,
5690
+ planService: () => planService
5691
+ });
5692
+ function mapProduct(raw) {
5693
+ return {
5694
+ id: raw.id,
5695
+ name: raw.name,
5696
+ description: raw.description,
5697
+ type: raw.type,
5698
+ appleProductId: raw.apple_product_id,
5699
+ googleProductId: raw.google_product_id,
5700
+ billingPeriod: raw.billing_period,
5701
+ trialDays: raw.trial_days,
5702
+ priceUsd: raw.price_usd,
5703
+ regionalPrices: raw.regional_prices,
5704
+ displayOrder: raw.display_order,
5705
+ isActive: raw.is_active
5706
+ };
5707
+ }
5708
+ function mapPlan(raw) {
5709
+ return {
5710
+ id: raw.id,
5711
+ identifier: raw.identifier,
5712
+ name: raw.name,
5713
+ description: raw.description,
5714
+ metadata: raw.metadata,
5715
+ products: raw.products.map(mapProduct)
5716
+ };
5717
+ }
5718
+ var PlanService, planService;
5719
+ var init_PlanService = __esm({
5720
+ "src/domains/plan/PlanService.ts"() {
5721
+ "use strict";
5722
+ init_PlanCache();
5723
+ init_PlanError();
5724
+ PlanService = class {
5725
+ constructor() {
5726
+ this.config = null;
5727
+ this.cache = planCache;
5728
+ }
5729
+ init(config) {
5730
+ this.config = config;
5731
+ }
5732
+ async getCurrentPlan(forceRefresh = false) {
5733
+ if (!forceRefresh) {
5734
+ const cached = this.cache.get();
5735
+ if (cached && !cached.isStale) {
5736
+ const current = cached.data.plans.find(
5737
+ (p) => p.identifier === cached.data.currentIdentifier
5738
+ );
5739
+ return current ?? null;
5740
+ }
5741
+ }
5742
+ return this.fetchCurrentPlan();
5743
+ }
5744
+ async getAllPlans(forceRefresh = false) {
5745
+ if (!forceRefresh) {
5746
+ const cached = this.cache.get();
5747
+ if (cached && !cached.isStale) return cached.data;
5748
+ }
5749
+ return this.fetchAllPlans();
5750
+ }
5751
+ invalidateCache() {
5752
+ this.cache.invalidate();
5753
+ }
5754
+ getApiClient() {
5755
+ if (!this.config) {
5756
+ throw new PlanError(PLAN_ERROR_CODES.NOT_INITIALIZED, "PlanService not initialized. Call Paywallo.init() first");
5757
+ }
5758
+ return this.config.apiClient;
5759
+ }
5760
+ async fetchCurrentPlan() {
5761
+ try {
5762
+ const res = await this.getApiClient().get("/sdk/plans");
5763
+ if (!res.ok) {
5764
+ this.log("fetchCurrentPlan failed", res.status);
5765
+ return null;
5766
+ }
5767
+ const raw = res.data.data.plan;
5768
+ return raw ? mapPlan(raw) : null;
5769
+ } catch (err) {
5770
+ this.log("fetchCurrentPlan error", err);
5771
+ return null;
5772
+ }
5773
+ }
5774
+ async fetchAllPlans() {
5775
+ try {
5776
+ const res = await this.getApiClient().get("/sdk/plans/all");
5777
+ if (!res.ok) {
5778
+ this.log("fetchAllPlans failed", res.status);
5779
+ return { plans: [], currentIdentifier: null };
5780
+ }
5781
+ const result = {
5782
+ plans: res.data.data.plans.map(mapPlan),
5783
+ currentIdentifier: res.data.data.current_identifier
5784
+ };
5785
+ this.cache.set(result);
5786
+ return result;
5787
+ } catch (err) {
5788
+ this.log("fetchAllPlans error", err);
5789
+ return { plans: [], currentIdentifier: null };
5790
+ }
5791
+ }
5792
+ log(...args) {
5793
+ if (this.config?.debug) console.log("[Paywallo Plans]", ...args);
5794
+ }
5795
+ };
5796
+ planService = new PlanService();
5797
+ }
5798
+ });
5799
+
5800
+ // src/domains/plan/index.ts
5801
+ var init_plan = __esm({
5802
+ "src/domains/plan/index.ts"() {
5803
+ "use strict";
5804
+ init_PlanCache();
5805
+ init_PlanService();
5806
+ }
5807
+ });
5808
+
5809
+ // src/domains/onboarding/OnboardingError.ts
5810
+ var OnboardingError, ONBOARDING_ERROR_CODES;
5811
+ var init_OnboardingError = __esm({
5812
+ "src/domains/onboarding/OnboardingError.ts"() {
5813
+ "use strict";
5814
+ init_PaywalloError();
5815
+ OnboardingError = class extends PaywalloError {
5816
+ constructor(code, message) {
5817
+ super("onboarding", code, message);
5818
+ this.name = "OnboardingError";
5819
+ }
5820
+ };
5821
+ ONBOARDING_ERROR_CODES = {
5822
+ NOT_INITIALIZED: "ONBOARDING_NOT_INITIALIZED",
5823
+ INVALID_STEP_NAME: "ONBOARDING_INVALID_STEP_NAME"
5824
+ };
5825
+ }
5826
+ });
5827
+
5828
+ // src/domains/onboarding/OnboardingManager.ts
5829
+ var OnboardingManager, onboardingManager;
5830
+ var init_OnboardingManager = __esm({
5831
+ "src/domains/onboarding/OnboardingManager.ts"() {
5832
+ "use strict";
5833
+ init_OnboardingError();
5834
+ OnboardingManager = class {
5835
+ constructor() {
5836
+ this.pipeline = null;
5837
+ this.debug = false;
5838
+ this.lastStep = null;
5839
+ this.finished = false;
5840
+ }
5841
+ /**
5842
+ * Injects runtime dependencies. Called by PaywalloClient during init.
5843
+ */
5844
+ injectDeps(config) {
5845
+ this.pipeline = config.pipeline;
5846
+ this.debug = config.debug ?? false;
5847
+ }
5848
+ /**
5849
+ * Tracks an onboarding step view.
5850
+ * Saves the step name internally for implicit drop detection on the backend.
5851
+ *
5852
+ * @param stepName - Unique name for this onboarding step.
5853
+ */
5854
+ async step(stepName) {
5855
+ this.assertConfig();
5856
+ this.validateStepName(stepName, "step");
5857
+ this.lastStep = stepName;
5858
+ const payload = {
5859
+ family: "onboarding",
5860
+ type: "step",
5861
+ ...{ step_name: stepName }
5862
+ };
5863
+ return this.emit("onboarding", payload, "step");
5864
+ }
5865
+ /**
5866
+ * Tracks successful completion of the onboarding flow.
5867
+ * Marks the flow as finished.
5868
+ */
5869
+ async complete() {
5870
+ this.assertConfig();
5871
+ this.finished = true;
5872
+ return this.emit("onboarding", { family: "onboarding", type: "complete" }, "complete");
5873
+ }
5874
+ /**
5875
+ * Tracks an explicit drop (abandonment) at the given step.
5876
+ * Marks the flow as finished.
5877
+ *
5878
+ * @param stepName - The step name where the user dropped off.
5879
+ */
5880
+ async drop(stepName) {
5881
+ this.assertConfig();
5882
+ this.validateStepName(stepName, "drop");
5883
+ this.finished = true;
5884
+ const payload = {
5885
+ family: "onboarding",
5886
+ type: "drop",
5887
+ ...{ step_name: stepName }
5888
+ };
5889
+ return this.emit("onboarding", payload, "drop");
5890
+ }
5891
+ /** Returns the last step name seen, or null if no step was tracked yet. */
5892
+ getLastStep() {
5893
+ return this.lastStep;
5894
+ }
5895
+ /** Returns whether the flow has been marked as finished (complete or drop). */
5896
+ isFinished() {
5897
+ return this.finished;
5898
+ }
5899
+ async emit(eventName, properties, logLabel) {
5900
+ const pipeline = this.pipeline;
5901
+ const distinctId = pipeline.getDistinctId();
5902
+ if (!distinctId) {
5903
+ if (this.debug) console.log(`[Paywallo:Onboarding] skipped (no distinctId): ${logLabel}`);
5904
+ return;
5905
+ }
5906
+ await pipeline.trackEvent(eventName, distinctId, properties);
5907
+ if (this.debug) {
5908
+ console.log(`[Paywallo:Onboarding] event emitted: ${eventName}.${logLabel}`, properties);
5909
+ }
5910
+ }
5911
+ assertConfig() {
5912
+ if (!this.pipeline) {
5913
+ throw new OnboardingError(
5914
+ ONBOARDING_ERROR_CODES.NOT_INITIALIZED,
5915
+ "OnboardingManager not initialized. Call Paywallo.init() first."
5916
+ );
5917
+ }
5918
+ }
5919
+ validateStepName(stepName, method) {
5920
+ if (typeof stepName !== "string" || stepName.trim().length === 0) {
5921
+ throw new OnboardingError(
5922
+ ONBOARDING_ERROR_CODES.INVALID_STEP_NAME,
5923
+ `OnboardingManager.${method}(): stepName must be a non-empty string.`
5924
+ );
5925
+ }
5926
+ }
5927
+ };
5928
+ onboardingManager = new OnboardingManager();
5398
5929
  }
5399
5930
  });
5400
5931
 
5401
- // src/domains/notifications/index.ts
5402
- var init_notifications = __esm({
5403
- "src/domains/notifications/index.ts"() {
5932
+ // src/domains/onboarding/index.ts
5933
+ var init_onboarding = __esm({
5934
+ "src/domains/onboarding/index.ts"() {
5404
5935
  "use strict";
5405
- init_NotificationsManager();
5406
- init_NativePushBridge();
5407
- init_NotificationsError();
5936
+ init_OnboardingManager();
5937
+ init_OnboardingError();
5408
5938
  }
5409
5939
  });
5410
5940
 
5411
- // src/domains/session/index.ts
5412
- var init_session = __esm({
5413
- "src/domains/session/index.ts"() {
5414
- "use strict";
5415
- init_SessionManager();
5416
- init_SessionError();
5941
+ // src/domains/paywall/paywallHeartbeatRecovery.ts
5942
+ async function recoverPaywallHeartbeat(apiClient, distinctId, sessionId, debug) {
5943
+ const snapshot = await readPersistedHeartbeat();
5944
+ if (!snapshot) return false;
5945
+ try {
5946
+ const closedAt = new Date(snapshot.lastSeen);
5947
+ const durationS = Math.max(0, Math.round((snapshot.lastSeen - snapshot.presentedAt) / 1e3));
5948
+ await apiClient.trackEvent("paywall", distinctId, {
5949
+ type: "closed",
5950
+ paywall_id: snapshot.paywallId,
5951
+ placement: snapshot.placement,
5952
+ closed_at: closedAt.toISOString(),
5953
+ duration_s: durationS,
5954
+ close_reason: "error",
5955
+ ...snapshot.variantId && { variant_id: snapshot.variantId },
5956
+ ...snapshot.variantKey && { variant_key: snapshot.variantKey },
5957
+ ...snapshot.campaignId && { campaign_id: snapshot.campaignId },
5958
+ ...sessionId && { sessionId }
5959
+ });
5960
+ if (debug) console.log("[Paywallo HEARTBEAT] recovered orphan paywall close", {
5961
+ paywallId: snapshot.paywallId,
5962
+ durationS
5963
+ });
5964
+ return true;
5965
+ } catch (err) {
5966
+ if (debug) console.log("[Paywallo HEARTBEAT] recovery failed", err);
5967
+ return false;
5968
+ } finally {
5969
+ await clearPersistedHeartbeat();
5417
5970
  }
5418
- });
5419
-
5420
- // src/core/PaywalloState.ts
5421
- function createPaywalloState() {
5422
- return {
5423
- config: null,
5424
- apiClient: null,
5425
- initPromise: null,
5426
- readyPromise: null,
5427
- resolveReady: null,
5428
- paywallPresenter: null,
5429
- campaignPresenter: null,
5430
- subscriptionGetter: null,
5431
- activeChecker: null,
5432
- restoreHandler: null,
5433
- lastOnboardingStepTimestamp: null,
5434
- pendingConfig: null,
5435
- networkCleanup: null,
5436
- initAttempts: 0,
5437
- autoPreloadedPlacement: null,
5438
- autoPreloadPlacementPromise: null,
5439
- sessionFlagsMap: /* @__PURE__ */ new Map(),
5440
- pendingIdentifies: []
5441
- };
5442
5971
  }
5443
- var MAX_INIT_RETRIES, RETRY_DELAYS;
5444
- var init_PaywalloState = __esm({
5445
- "src/core/PaywalloState.ts"() {
5972
+ var init_paywallHeartbeatRecovery = __esm({
5973
+ "src/domains/paywall/paywallHeartbeatRecovery.ts"() {
5446
5974
  "use strict";
5447
- MAX_INIT_RETRIES = 3;
5448
- RETRY_DELAYS = [2e3, 5e3, 1e4];
5975
+ init_paywallHeartbeat();
5449
5976
  }
5450
5977
  });
5451
5978
 
@@ -5453,7 +5980,7 @@ var init_PaywalloState = __esm({
5453
5980
  function keyFor(distinctId) {
5454
5981
  return `${CACHE_KEY_PREFIX}${distinctId}`;
5455
5982
  }
5456
- var LEGACY_CACHE_KEY, CACHE_KEY_PREFIX, CACHE_INDEX_KEY, DEFAULT_TTL, SubscriptionCache, subscriptionCache;
5983
+ var LEGACY_CACHE_KEY, CACHE_KEY_PREFIX, CACHE_INDEX_KEY, DEFAULT_TTL2, SubscriptionCache, subscriptionCache;
5457
5984
  var init_SubscriptionCache = __esm({
5458
5985
  "src/domains/subscription/SubscriptionCache.ts"() {
5459
5986
  "use strict";
@@ -5461,9 +5988,9 @@ var init_SubscriptionCache = __esm({
5461
5988
  LEGACY_CACHE_KEY = "@panel:subscription_cache";
5462
5989
  CACHE_KEY_PREFIX = "subscription_cache:";
5463
5990
  CACHE_INDEX_KEY = "subscription_cache:__index__";
5464
- DEFAULT_TTL = 24 * 60 * 60 * 1e3;
5991
+ DEFAULT_TTL2 = 24 * 60 * 60 * 1e3;
5465
5992
  SubscriptionCache = class {
5466
- constructor(ttl = DEFAULT_TTL) {
5993
+ constructor(ttl = DEFAULT_TTL2) {
5467
5994
  this.memoryCache = /* @__PURE__ */ new Map();
5468
5995
  this.legacyCleanupDone = false;
5469
5996
  this.ttl = ttl;
@@ -5670,7 +6197,7 @@ var init_SubscriptionManager = __esm({
5670
6197
  if (!this.config) {
5671
6198
  return this.getEmptyStatus();
5672
6199
  }
5673
- const url = new URL(`${this.config.serverUrl}/purchases/status/${this.config.appKey}`);
6200
+ const url = new URL(`${this.config.serverUrl}/sdk/purchases/status/${this.config.appKey}`);
5674
6201
  if (this.userId) {
5675
6202
  url.searchParams.set("distinctId", this.userId);
5676
6203
  }
@@ -5754,11 +6281,10 @@ function buildConditionalFlagUrl(baseUrl, flagKey, context) {
5754
6281
  if (context.distinctId) url.searchParams.set("distinctId", context.distinctId);
5755
6282
  return url.toString();
5756
6283
  }
5757
- var DEFAULT_WEB_URL;
5758
6284
  var init_apiUrlUtils = __esm({
5759
6285
  "src/core/apiUrlUtils.ts"() {
5760
6286
  "use strict";
5761
- DEFAULT_WEB_URL = "https://paywallo.com.br";
6287
+ init_constants();
5762
6288
  }
5763
6289
  });
5764
6290
 
@@ -5911,7 +6437,7 @@ async function getSubscriptionStatus(client, baseUrl, appKey, distinctId) {
5911
6437
  }
5912
6438
  async function getEmergencyPaywall(client) {
5913
6439
  try {
5914
- const response = await client.get("/emergency-paywall");
6440
+ const response = await client.get("/sdk/emergency-paywall");
5915
6441
  if (response.status === 404) return { enabled: false };
5916
6442
  return response.data;
5917
6443
  } catch {
@@ -6528,7 +7054,7 @@ var init_EventBatcher = __esm({
6528
7054
  BATCH_MAX_SIZE = 25;
6529
7055
  BATCH_FLUSH_MS = 1e4;
6530
7056
  EVENT_NAME_REGEX = /^(\$[a-zA-Z0-9_]+|[a-z][a-z0-9_]*)$/;
6531
- V2_BATCH_ENDPOINT = "/ingest/batch";
7057
+ V2_BATCH_ENDPOINT = "/sdk/ingest/batch";
6532
7058
  V1_BATCH_ENDPOINT = "/sdk/events/batch";
6533
7059
  V1_SINGLE_ENDPOINT = "/sdk/events";
6534
7060
  EventBatcher = class {
@@ -6892,7 +7418,7 @@ var init_ApiClient = __esm({
6892
7418
  dispose() {
6893
7419
  this.batcher.dispose();
6894
7420
  }
6895
- async identify(distinctId, properties, email, deviceId) {
7421
+ async identify(distinctId, properties, email, deviceId, pii) {
6896
7422
  await postWithQueue(
6897
7423
  {
6898
7424
  isOfflineQueueEnabled: () => this.offlineQueueEnabled,
@@ -6900,8 +7426,19 @@ var init_ApiClient = __esm({
6900
7426
  isDebug: () => this.debug,
6901
7427
  post: (path, body, skipRetry) => this.post(path, body, skipRetry)
6902
7428
  },
6903
- "/identify",
6904
- { distinctId, properties: properties ?? {}, email, platform: Platform14.OS, ...deviceId && { deviceId } },
7429
+ "/sdk/identify",
7430
+ {
7431
+ distinctId,
7432
+ properties: properties ?? {},
7433
+ email,
7434
+ platform: Platform14.OS,
7435
+ ...deviceId && { deviceId },
7436
+ ...pii?.phone && { phone: pii.phone },
7437
+ ...pii?.firstName && { firstName: pii.firstName },
7438
+ ...pii?.lastName && { lastName: pii.lastName },
7439
+ ...pii?.dateOfBirth && { dateOfBirth: pii.dateOfBirth },
7440
+ ...pii?.gender && { gender: pii.gender }
7441
+ },
6905
7442
  "identify"
6906
7443
  );
6907
7444
  }
@@ -6929,245 +7466,84 @@ var init_ApiClient = __esm({
6929
7466
  return res.data;
6930
7467
  } catch {
6931
7468
  const stale = this.cache.getStalePaywall(placement);
6932
- return stale !== void 0 ? stale : null;
6933
- }
6934
- }
6935
- async getEmergencyPaywall() {
6936
- return getEmergencyPaywall(this);
6937
- }
6938
- async getCampaign(placement, distinctId, context) {
6939
- const key = `${placement}:${distinctId}`;
6940
- const hit = this.cache.getCampaign(key);
6941
- if (hit !== void 0) return hit;
6942
- try {
6943
- const result = await getCampaignData(this, this.baseUrl, placement, distinctId, context);
6944
- if (!result) {
6945
- this.cache.setCampaign(key, null, this.cache.nullTTL);
6946
- return null;
6947
- }
6948
- this.cache.setCampaign(key, result);
6949
- return result;
6950
- } catch {
6951
- const stale = this.cache.getStaleCampaign(key);
6952
- return stale !== void 0 ? stale : null;
6953
- }
6954
- }
6955
- async getPrimaryCampaign(distinctId) {
6956
- const key = `primary:${distinctId}`;
6957
- const hit = this.cache.getCampaign(key);
6958
- if (hit !== void 0) return hit;
6959
- try {
6960
- const res = await this.get(`/campaigns/public/primary?distinctId=${encodeURIComponent(distinctId)}`);
6961
- if (res.status === 404) {
6962
- this.cache.setCampaign(key, null, this.cache.nullTTL);
6963
- return null;
6964
- }
6965
- if (!res.ok) {
6966
- const stale = this.cache.getStaleCampaign(key);
6967
- return stale !== void 0 ? stale : null;
6968
- }
6969
- this.cache.setCampaign(key, res.data);
6970
- return res.data;
6971
- } catch {
6972
- const stale = this.cache.getStaleCampaign(key);
6973
- return stale !== void 0 ? stale : null;
6974
- }
6975
- }
6976
- async validatePurchase(platform, receipt, productId, transactionId, priceLocal, currency, country, distinctId, paywallPlacement, variantKey) {
6977
- return validatePurchase(this, this.appKey, platform, receipt, productId, transactionId, priceLocal, currency, country, distinctId, paywallPlacement, variantKey);
6978
- }
6979
- async getSubscriptionStatus(distinctId) {
6980
- return getSubscriptionStatus(this, this.baseUrl, this.appKey, distinctId);
6981
- }
6982
- async evaluateFlags(keys, distinctId) {
6983
- return evaluateFlags(this.flagDeps(), keys, distinctId);
6984
- }
6985
- async getConditionalFlag(flagKey, context) {
6986
- return getConditionalFlag(this.flagDeps(), flagKey, context);
6987
- }
6988
- async getVariantFromCache(flagKey, distinctId) {
6989
- return getVariantFromCache(this.flagDeps(), flagKey, distinctId);
6990
- }
6991
- flagDeps() {
6992
- return {
6993
- get: (path) => this.get(path),
6994
- httpClient: this.httpClient,
6995
- cache: this.cache,
6996
- appKey: this.appKey,
6997
- baseUrl: this.baseUrl
6998
- };
6999
- }
7000
- };
7001
- }
7002
- });
7003
-
7004
- // src/domains/onboarding/OnboardingError.ts
7005
- var OnboardingError, ONBOARDING_ERROR_CODES;
7006
- var init_OnboardingError = __esm({
7007
- "src/domains/onboarding/OnboardingError.ts"() {
7008
- "use strict";
7009
- init_PaywalloError();
7010
- OnboardingError = class extends PaywalloError {
7011
- constructor(code, message) {
7012
- super("onboarding", code, message);
7013
- this.name = "OnboardingError";
7014
- }
7015
- };
7016
- ONBOARDING_ERROR_CODES = {
7017
- NOT_INITIALIZED: "ONBOARDING_NOT_INITIALIZED",
7018
- INVALID_STEP_NAME: "ONBOARDING_INVALID_STEP_NAME"
7019
- };
7020
- }
7021
- });
7022
-
7023
- // src/domains/onboarding/OnboardingManager.ts
7024
- var OnboardingManager, onboardingManager;
7025
- var init_OnboardingManager = __esm({
7026
- "src/domains/onboarding/OnboardingManager.ts"() {
7027
- "use strict";
7028
- init_OnboardingError();
7029
- OnboardingManager = class {
7030
- constructor() {
7031
- this.pipeline = null;
7032
- this.debug = false;
7033
- this.lastStep = null;
7034
- this.finished = false;
7035
- }
7036
- /**
7037
- * Injects runtime dependencies. Called by PaywalloClient during init.
7038
- */
7039
- injectDeps(config) {
7040
- this.pipeline = config.pipeline;
7041
- this.debug = config.debug ?? false;
7042
- }
7043
- /**
7044
- * Tracks an onboarding step view.
7045
- * Saves the step name internally for implicit drop detection on the backend.
7046
- *
7047
- * @param stepName - Unique name for this onboarding step.
7048
- */
7049
- async step(stepName) {
7050
- this.assertConfig();
7051
- this.validateStepName(stepName, "step");
7052
- this.lastStep = stepName;
7053
- const payload = {
7054
- family: "onboarding",
7055
- type: "step",
7056
- ...{ step_name: stepName }
7057
- };
7058
- return this.emit("onboarding", payload, "step");
7059
- }
7060
- /**
7061
- * Tracks successful completion of the onboarding flow.
7062
- * Marks the flow as finished.
7063
- */
7064
- async complete() {
7065
- this.assertConfig();
7066
- this.finished = true;
7067
- return this.emit("onboarding", { family: "onboarding", type: "complete" }, "complete");
7068
- }
7069
- /**
7070
- * Tracks an explicit drop (abandonment) at the given step.
7071
- * Marks the flow as finished.
7072
- *
7073
- * @param stepName - The step name where the user dropped off.
7074
- */
7075
- async drop(stepName) {
7076
- this.assertConfig();
7077
- this.validateStepName(stepName, "drop");
7078
- this.finished = true;
7079
- const payload = {
7080
- family: "onboarding",
7081
- type: "drop",
7082
- ...{ step_name: stepName }
7083
- };
7084
- return this.emit("onboarding", payload, "drop");
7085
- }
7086
- /** Returns the last step name seen, or null if no step was tracked yet. */
7087
- getLastStep() {
7088
- return this.lastStep;
7469
+ return stale !== void 0 ? stale : null;
7470
+ }
7089
7471
  }
7090
- /** Returns whether the flow has been marked as finished (complete or drop). */
7091
- isFinished() {
7092
- return this.finished;
7472
+ async getEmergencyPaywall() {
7473
+ return getEmergencyPaywall(this);
7093
7474
  }
7094
- async emit(eventName, properties, logLabel) {
7095
- const pipeline = this.pipeline;
7096
- const distinctId = pipeline.getDistinctId();
7097
- if (!distinctId) {
7098
- if (this.debug) console.log(`[Paywallo:Onboarding] skipped (no distinctId): ${logLabel}`);
7099
- return;
7100
- }
7101
- await pipeline.trackEvent(eventName, distinctId, properties);
7102
- if (this.debug) {
7103
- console.log(`[Paywallo:Onboarding] event emitted: ${eventName}.${logLabel}`, properties);
7475
+ async getCampaign(placement, distinctId, context) {
7476
+ const key = `${placement}:${distinctId}`;
7477
+ const hit = this.cache.getCampaign(key);
7478
+ if (hit !== void 0) return hit;
7479
+ try {
7480
+ const result = await getCampaignData(this, this.baseUrl, placement, distinctId, context);
7481
+ if (!result) {
7482
+ this.cache.setCampaign(key, null, this.cache.nullTTL);
7483
+ return null;
7484
+ }
7485
+ this.cache.setCampaign(key, result);
7486
+ return result;
7487
+ } catch {
7488
+ const stale = this.cache.getStaleCampaign(key);
7489
+ return stale !== void 0 ? stale : null;
7104
7490
  }
7105
7491
  }
7106
- assertConfig() {
7107
- if (!this.pipeline) {
7108
- throw new OnboardingError(
7109
- ONBOARDING_ERROR_CODES.NOT_INITIALIZED,
7110
- "OnboardingManager not initialized. Call Paywallo.init() first."
7111
- );
7492
+ async getActivePlacements() {
7493
+ try {
7494
+ const res = await this.get("/sdk/campaigns/placements");
7495
+ if (!res.ok || !Array.isArray(res.data?.data)) return [];
7496
+ return res.data.data;
7497
+ } catch {
7498
+ return [];
7112
7499
  }
7113
7500
  }
7114
- validateStepName(stepName, method) {
7115
- if (typeof stepName !== "string" || stepName.trim().length === 0) {
7116
- throw new OnboardingError(
7117
- ONBOARDING_ERROR_CODES.INVALID_STEP_NAME,
7118
- `OnboardingManager.${method}(): stepName must be a non-empty string.`
7119
- );
7501
+ async getPrimaryCampaign(distinctId) {
7502
+ const key = `primary:${distinctId}`;
7503
+ const hit = this.cache.getCampaign(key);
7504
+ if (hit !== void 0) return hit;
7505
+ try {
7506
+ const res = await this.get(`/sdk/campaigns/public/primary?distinctId=${encodeURIComponent(distinctId)}`);
7507
+ if (res.status === 404) {
7508
+ this.cache.setCampaign(key, null, this.cache.nullTTL);
7509
+ return null;
7510
+ }
7511
+ if (!res.ok) {
7512
+ const stale = this.cache.getStaleCampaign(key);
7513
+ return stale !== void 0 ? stale : null;
7514
+ }
7515
+ this.cache.setCampaign(key, res.data);
7516
+ return res.data;
7517
+ } catch {
7518
+ const stale = this.cache.getStaleCampaign(key);
7519
+ return stale !== void 0 ? stale : null;
7120
7520
  }
7121
7521
  }
7522
+ async validatePurchase(platform, receipt, productId, transactionId, priceLocal, currency, country, distinctId, paywallPlacement, variantKey) {
7523
+ return validatePurchase(this, this.appKey, platform, receipt, productId, transactionId, priceLocal, currency, country, distinctId, paywallPlacement, variantKey);
7524
+ }
7525
+ async getSubscriptionStatus(distinctId) {
7526
+ return getSubscriptionStatus(this, this.baseUrl, this.appKey, distinctId);
7527
+ }
7528
+ async evaluateFlags(keys, distinctId) {
7529
+ return evaluateFlags(this.flagDeps(), keys, distinctId);
7530
+ }
7531
+ async getConditionalFlag(flagKey, context) {
7532
+ return getConditionalFlag(this.flagDeps(), flagKey, context);
7533
+ }
7534
+ async getVariantFromCache(flagKey, distinctId) {
7535
+ return getVariantFromCache(this.flagDeps(), flagKey, distinctId);
7536
+ }
7537
+ flagDeps() {
7538
+ return {
7539
+ get: (path) => this.get(path),
7540
+ httpClient: this.httpClient,
7541
+ cache: this.cache,
7542
+ appKey: this.appKey,
7543
+ baseUrl: this.baseUrl
7544
+ };
7545
+ }
7122
7546
  };
7123
- onboardingManager = new OnboardingManager();
7124
- }
7125
- });
7126
-
7127
- // src/domains/onboarding/index.ts
7128
- var init_onboarding = __esm({
7129
- "src/domains/onboarding/index.ts"() {
7130
- "use strict";
7131
- init_OnboardingManager();
7132
- init_OnboardingError();
7133
- }
7134
- });
7135
-
7136
- // src/domains/paywall/paywallHeartbeatRecovery.ts
7137
- async function recoverPaywallHeartbeat(apiClient, distinctId, sessionId, debug) {
7138
- const snapshot = await readPersistedHeartbeat();
7139
- if (!snapshot) return false;
7140
- try {
7141
- const closedAt = new Date(snapshot.lastSeen);
7142
- const durationS = Math.max(0, Math.round((snapshot.lastSeen - snapshot.presentedAt) / 1e3));
7143
- await apiClient.trackEvent("paywall", distinctId, {
7144
- type: "closed",
7145
- paywall_id: snapshot.paywallId,
7146
- placement: snapshot.placement,
7147
- closed_at: closedAt.toISOString(),
7148
- duration_s: durationS,
7149
- close_reason: "error",
7150
- ...snapshot.variantId && { variant_id: snapshot.variantId },
7151
- ...snapshot.variantKey && { variant_key: snapshot.variantKey },
7152
- ...snapshot.campaignId && { campaign_id: snapshot.campaignId },
7153
- ...sessionId && { sessionId }
7154
- });
7155
- if (debug) console.log("[Paywallo HEARTBEAT] recovered orphan paywall close", {
7156
- paywallId: snapshot.paywallId,
7157
- durationS
7158
- });
7159
- return true;
7160
- } catch (err) {
7161
- if (debug) console.log("[Paywallo HEARTBEAT] recovery failed", err);
7162
- return false;
7163
- } finally {
7164
- await clearPersistedHeartbeat();
7165
- }
7166
- }
7167
- var init_paywallHeartbeatRecovery = __esm({
7168
- "src/domains/paywall/paywallHeartbeatRecovery.ts"() {
7169
- "use strict";
7170
- init_paywallHeartbeat();
7171
7547
  }
7172
7548
  });
7173
7549
 
@@ -7211,6 +7587,8 @@ var init_eventPipelineBridge = __esm({
7211
7587
 
7212
7588
  // src/core/PaywalloInitHelpers.ts
7213
7589
  function setupNotifications(apiClient, config, environment, eventPipeline) {
7590
+ if (config.notifications === false) return;
7591
+ if (config.notifications === void 0 && !isNativePushAvailable()) return;
7214
7592
  try {
7215
7593
  notificationsManager.injectDeps({
7216
7594
  httpClient: apiClient.getHttpClient(),
@@ -7252,7 +7630,10 @@ function setupAutoPreload(state, apiClient, config, distinctId) {
7252
7630
  return null;
7253
7631
  }).catch(() => null);
7254
7632
  }
7255
- var DEFAULT_API_URL;
7633
+ function startBatchPreload(apiClient, debug) {
7634
+ void campaignGateService.preloadAllActive(apiClient, debug).catch(() => {
7635
+ });
7636
+ }
7256
7637
  var init_PaywalloInitHelpers = __esm({
7257
7638
  "src/core/PaywalloInitHelpers.ts"() {
7258
7639
  "use strict";
@@ -7260,7 +7641,8 @@ var init_PaywalloInitHelpers = __esm({
7260
7641
  init_identity();
7261
7642
  init_notifications();
7262
7643
  init_SecureStorage();
7263
- DEFAULT_API_URL = "https://paywallo.com.br";
7644
+ init_NativePushBridge();
7645
+ init_constants();
7264
7646
  }
7265
7647
  });
7266
7648
 
@@ -7309,7 +7691,7 @@ function reportInitFailure(state, config, error, reason) {
7309
7691
  try {
7310
7692
  const tempClient = new ApiClient(
7311
7693
  config.appKey,
7312
- config.apiUrl ?? DEFAULT_API_URL2,
7694
+ config.apiUrl ?? DEFAULT_API_URL,
7313
7695
  config.debug
7314
7696
  );
7315
7697
  void tempClient.reportError("INIT_FAILED", reason, {
@@ -7353,7 +7735,7 @@ async function doInit(state, config) {
7353
7735
  ]);
7354
7736
  const apiClient = new ApiClient(
7355
7737
  config.appKey,
7356
- config.apiUrl ?? DEFAULT_API_URL2,
7738
+ config.apiUrl ?? DEFAULT_API_URL,
7357
7739
  config.debug,
7358
7740
  environment,
7359
7741
  offlineQueueEnabled,
@@ -7373,6 +7755,7 @@ async function doInit(state, config) {
7373
7755
  cacheTTL: config.subscriptionCacheTTL,
7374
7756
  debug: config.debug
7375
7757
  });
7758
+ planService.init({ apiClient, debug: config.debug ?? false });
7376
7759
  await identityManager.initialize(apiClient, config.debug);
7377
7760
  if (state.pendingIdentifies.length > 0) {
7378
7761
  for (const pending of state.pendingIdentifies) {
@@ -7395,6 +7778,9 @@ async function doInit(state, config) {
7395
7778
  } catch (err) {
7396
7779
  if (config.debug) console.log("[Paywallo ATTR] start error", err);
7397
7780
  }
7781
+ void nativeDeviceInfo.getDeviceInfo().catch(() => null);
7782
+ void advertisingIdManager.collect(false).catch(() => null);
7783
+ void metaBridge.getAnonymousID().catch(() => null);
7398
7784
  apiClient.setEventContextProvider(() => {
7399
7785
  const attr = attributionTracker.get();
7400
7786
  const attrEntries = attr ? [
@@ -7409,11 +7795,35 @@ async function doInit(state, config) {
7409
7795
  ["referrer", attr.referrer]
7410
7796
  ].filter((pair) => pair[1] !== void 0 && pair[1] !== "") : [];
7411
7797
  const attribution = attrEntries.length > 0 ? Object.fromEntries(attrEntries) : void 0;
7798
+ const adIds = advertisingIdManager.getCached();
7799
+ const fbAnonId = metaBridge.getCachedAnonymousID();
7800
+ const idsEntries = [
7801
+ ["idfa", adIds?.idfa ?? null],
7802
+ ["idfv", adIds?.idfv ?? null],
7803
+ ["gaid", adIds?.gaid ?? null],
7804
+ ["fb_anon_id", fbAnonId]
7805
+ ].filter((pair) => pair[1] !== null && pair[1] !== "");
7806
+ const ids = idsEntries.length > 0 ? Object.fromEntries(idsEntries) : void 0;
7807
+ const device = nativeDeviceInfo.getCachedDeviceInfo();
7808
+ let locale;
7809
+ let timezone;
7810
+ try {
7811
+ const opts = Intl.DateTimeFormat().resolvedOptions();
7812
+ locale = opts.locale || void 0;
7813
+ timezone = opts.timeZone || void 0;
7814
+ } catch {
7815
+ }
7412
7816
  return {
7413
7817
  distinct_id: identityManager.getDistinctId() || void 0,
7414
7818
  device_id: identityManager.getDeviceId() ?? void 0,
7415
7819
  session_id: sessionManager.getSessionId() ?? void 0,
7416
- ...attribution && Object.keys(attribution).length > 0 && { attribution }
7820
+ app_version: device?.appVersion && device.appVersion !== "0.0.0" ? device.appVersion : void 0,
7821
+ os_version: device?.systemVersion && device.systemVersion !== "0.0.0" ? device.systemVersion : void 0,
7822
+ device_model: device?.modelId || device?.model || void 0,
7823
+ timezone,
7824
+ locale,
7825
+ ...attribution && Object.keys(attribution).length > 0 && { attribution },
7826
+ ...ids && Object.keys(ids).length > 0 && { ids }
7417
7827
  };
7418
7828
  });
7419
7829
  await sessionManager.initialize(
@@ -7447,7 +7857,7 @@ async function doInit(state, config) {
7447
7857
  state.resolveReady();
7448
7858
  state.resolveReady = null;
7449
7859
  }
7450
- if (config.debug) console.warn("[Paywallo INIT] complete");
7860
+ console.warn("[Paywallo] initialized successfully");
7451
7861
  if (distinctId) {
7452
7862
  apiClient.getSubscriptionStatus(distinctId).then(() => {
7453
7863
  if (config.debug) console.log("[Paywallo SUB] preloaded on boot");
@@ -7458,27 +7868,30 @@ async function doInit(state, config) {
7458
7868
  });
7459
7869
  }
7460
7870
  setupAutoPreload(state, apiClient, config, distinctId);
7871
+ startBatchPreload(apiClient, config.debug);
7461
7872
  }
7462
- var DEFAULT_API_URL2;
7463
7873
  var init_PaywalloInitializer = __esm({
7464
7874
  "src/core/PaywalloInitializer.ts"() {
7465
7875
  "use strict";
7466
- init_SubscriptionManager();
7467
- init_ApiClient();
7468
7876
  init_identity();
7877
+ init_plan();
7878
+ init_AdvertisingIdManager();
7469
7879
  init_DeepLinkAttributionCapture();
7470
7880
  init_InstallTracker();
7471
- init_network();
7472
- init_queue();
7473
- init_session();
7881
+ init_MetaBridge();
7474
7882
  init_onboarding();
7475
- init_NativeStorage();
7476
- init_NativeDeviceInfo();
7477
7883
  init_paywallHeartbeatRecovery();
7884
+ init_session();
7885
+ init_SubscriptionManager();
7886
+ init_NativeDeviceInfo();
7887
+ init_ApiClient();
7888
+ init_constants();
7478
7889
  init_eventPipelineBridge();
7479
- init_PaywalloState();
7890
+ init_network();
7480
7891
  init_PaywalloInitHelpers();
7481
- DEFAULT_API_URL2 = "https://paywallo.com.br";
7892
+ init_PaywalloState();
7893
+ init_queue();
7894
+ init_NativeStorage();
7482
7895
  }
7483
7896
  });
7484
7897
 
@@ -7732,6 +8145,7 @@ var init_PaywalloClient = __esm({
7732
8145
  init_PaywalloAnalytics();
7733
8146
  init_PaywalloSubscription();
7734
8147
  init_PaywalloFlags();
8148
+ init_plan();
7735
8149
  PaywalloClientClass = class {
7736
8150
  constructor() {
7737
8151
  this.state = createPaywalloState();
@@ -7983,6 +8397,14 @@ var init_PaywalloClient = __esm({
7983
8397
  async processOfflineQueue() {
7984
8398
  return queueProcessor.processQueue();
7985
8399
  }
8400
+ async getPlans() {
8401
+ this.ensureInitialized();
8402
+ return planService.getAllPlans();
8403
+ }
8404
+ async getCurrentPlan() {
8405
+ this.ensureInitialized();
8406
+ return planService.getCurrentPlan();
8407
+ }
7986
8408
  getApiClientOrThrow() {
7987
8409
  if (!this.state.config || !this.state.apiClient) throw new ClientError(CLIENT_ERROR_CODES.NOT_INITIALIZED, "SDK not initialized. Call Paywallo.init() first");
7988
8410
  return this.state.apiClient;
@@ -8065,8 +8487,124 @@ function usePaywallo() {
8065
8487
  return context;
8066
8488
  }
8067
8489
 
8068
- // src/hooks/useProducts.ts
8490
+ // src/hooks/usePlans.ts
8069
8491
  import { useCallback as useCallback8, useEffect as useEffect7, useState as useState6 } from "react";
8492
+ async function getPlanService() {
8493
+ try {
8494
+ const mod = await Promise.resolve().then(() => (init_PlanService(), PlanService_exports));
8495
+ return mod.planService ?? null;
8496
+ } catch {
8497
+ return null;
8498
+ }
8499
+ }
8500
+ function usePlans() {
8501
+ const [allPlans, setAllPlans] = useState6([]);
8502
+ const [currentPlan, setCurrentPlan] = useState6(null);
8503
+ const [isLoading, setIsLoading] = useState6(true);
8504
+ const [error, setError] = useState6(null);
8505
+ const fetchPlans = useCallback8(async (forceRefresh = false) => {
8506
+ setIsLoading(true);
8507
+ setError(null);
8508
+ try {
8509
+ const service = await getPlanService();
8510
+ if (!service) {
8511
+ throw new PurchaseError(PURCHASE_ERROR_CODES.PURCHASE_FAILED, "PlanService not available");
8512
+ }
8513
+ const response = await service.getAllPlans(forceRefresh);
8514
+ const { plans, currentIdentifier } = response;
8515
+ setAllPlans(plans);
8516
+ setCurrentPlan(
8517
+ currentIdentifier != null ? plans.find((p) => p.identifier === currentIdentifier) ?? null : null
8518
+ );
8519
+ } catch (err) {
8520
+ const e = err instanceof PurchaseError ? err : new PurchaseError(PURCHASE_ERROR_CODES.PURCHASE_FAILED, String(err));
8521
+ setError(e);
8522
+ } finally {
8523
+ setIsLoading(false);
8524
+ }
8525
+ }, []);
8526
+ const refresh = useCallback8(() => {
8527
+ return fetchPlans(true);
8528
+ }, [fetchPlans]);
8529
+ useEffect7(() => {
8530
+ void fetchPlans(false);
8531
+ }, [fetchPlans]);
8532
+ return { currentPlan, allPlans, isLoading, error, refresh };
8533
+ }
8534
+
8535
+ // src/hooks/usePlanPurchase.ts
8536
+ import { useCallback as useCallback9, useState as useState7 } from "react";
8537
+ async function invalidatePlanCache() {
8538
+ try {
8539
+ const mod = await Promise.resolve().then(() => (init_PlanService(), PlanService_exports));
8540
+ mod.planService?.invalidateCache();
8541
+ } catch {
8542
+ }
8543
+ }
8544
+ function usePlanPurchase() {
8545
+ const [state, setState] = useState7("idle");
8546
+ const [error, setError] = useState7(null);
8547
+ const purchase = useCallback9(async (productId) => {
8548
+ setState("purchasing");
8549
+ setError(null);
8550
+ try {
8551
+ const iapService = getIAPService();
8552
+ const result = await iapService.purchase(productId);
8553
+ setState("idle");
8554
+ if (result.success) {
8555
+ await invalidatePlanCache();
8556
+ }
8557
+ return result.success ? {
8558
+ success: true,
8559
+ purchase: result.purchase ? {
8560
+ productId: result.purchase.productId,
8561
+ transactionId: result.purchase.transactionId ?? "",
8562
+ transactionDate: result.purchase.transactionDate,
8563
+ receipt: result.purchase.receipt,
8564
+ platform: "ios"
8565
+ } : void 0
8566
+ } : { success: false, error: result.error };
8567
+ } catch (err) {
8568
+ const purchaseError = err instanceof PurchaseError ? err : createPurchaseError("PURCHASE_FAILED", err instanceof Error ? err.message : String(err));
8569
+ if (purchaseError.userCancelled) {
8570
+ setState("idle");
8571
+ return null;
8572
+ }
8573
+ setError(purchaseError);
8574
+ setState("error");
8575
+ return { success: false, error: purchaseError };
8576
+ }
8577
+ }, []);
8578
+ const restore = useCallback9(async () => {
8579
+ setState("restoring");
8580
+ setError(null);
8581
+ try {
8582
+ const iapService = getIAPService();
8583
+ const transactions = await iapService.restore();
8584
+ setState("idle");
8585
+ await invalidatePlanCache();
8586
+ return {
8587
+ restoredCount: transactions.length,
8588
+ productIds: transactions.map((tx) => tx.productId)
8589
+ };
8590
+ } catch (err) {
8591
+ const e = err instanceof PurchaseError ? err : createPurchaseError("RESTORE_FAILED", String(err));
8592
+ setError(e);
8593
+ setState("error");
8594
+ return null;
8595
+ }
8596
+ }, []);
8597
+ return {
8598
+ purchase,
8599
+ restore,
8600
+ isPurchasing: state === "purchasing",
8601
+ isRestoring: state === "restoring",
8602
+ error
8603
+ };
8604
+ }
8605
+
8606
+ // src/hooks/useProducts.ts
8607
+ import { useCallback as useCallback10, useEffect as useEffect8, useState as useState8 } from "react";
8070
8608
 
8071
8609
  // src/utils/productMappers.ts
8072
8610
  function mapToIAPProduct(product) {
@@ -8105,11 +8643,11 @@ function mapToFormattedProduct(product) {
8105
8643
 
8106
8644
  // src/hooks/useProducts.ts
8107
8645
  function useProducts(initialProductIds) {
8108
- const [products, setProducts] = useState6([]);
8109
- const [formattedProducts, setFormattedProducts] = useState6([]);
8110
- const [isLoading, setIsLoading] = useState6(false);
8111
- const [error, setError] = useState6(null);
8112
- const loadProducts = useCallback8(async (productIds) => {
8646
+ const [products, setProducts] = useState8([]);
8647
+ const [formattedProducts, setFormattedProducts] = useState8([]);
8648
+ const [isLoading, setIsLoading] = useState8(false);
8649
+ const [error, setError] = useState8(null);
8650
+ const loadProducts = useCallback10(async (productIds) => {
8113
8651
  setIsLoading(true);
8114
8652
  setError(null);
8115
8653
  try {
@@ -8123,24 +8661,24 @@ function useProducts(initialProductIds) {
8123
8661
  setIsLoading(false);
8124
8662
  }
8125
8663
  }, []);
8126
- useEffect7(() => {
8664
+ useEffect8(() => {
8127
8665
  if (initialProductIds && initialProductIds.length > 0) {
8128
8666
  void loadProducts(initialProductIds);
8129
8667
  }
8130
8668
  }, []);
8131
- const getProduct = useCallback8(
8669
+ const getProduct = useCallback10(
8132
8670
  (productId) => {
8133
8671
  return products.find((p) => p.productId === productId);
8134
8672
  },
8135
8673
  [products]
8136
8674
  );
8137
- const getFormattedProduct = useCallback8(
8675
+ const getFormattedProduct = useCallback10(
8138
8676
  (productId) => {
8139
8677
  return formattedProducts.find((p) => p.productId === productId);
8140
8678
  },
8141
8679
  [formattedProducts]
8142
8680
  );
8143
- const getPriceInfo = useCallback8(
8681
+ const getPriceInfo = useCallback10(
8144
8682
  (productId) => {
8145
8683
  const p = formattedProducts.find((fp) => fp.productId === productId);
8146
8684
  if (!p) return void 0;
@@ -8169,11 +8707,11 @@ function useProducts(initialProductIds) {
8169
8707
  }
8170
8708
 
8171
8709
  // src/hooks/usePurchase.ts
8172
- import { useCallback as useCallback9, useState as useState7 } from "react";
8710
+ import { useCallback as useCallback11, useState as useState9 } from "react";
8173
8711
  function usePurchase() {
8174
- const [state, setState] = useState7("idle");
8175
- const [error, setError] = useState7(null);
8176
- const purchase = useCallback9(async (productId) => {
8712
+ const [state, setState] = useState9("idle");
8713
+ const [error, setError] = useState9(null);
8714
+ const purchase = useCallback11(async (productId) => {
8177
8715
  setState("purchasing");
8178
8716
  setError(null);
8179
8717
  try {
@@ -8209,7 +8747,7 @@ function usePurchase() {
8209
8747
  return { status: "failed", error: purchaseError };
8210
8748
  }
8211
8749
  }, []);
8212
- const restore = useCallback9(async () => {
8750
+ const restore = useCallback11(async () => {
8213
8751
  setState("restoring");
8214
8752
  setError(null);
8215
8753
  try {
@@ -8240,14 +8778,14 @@ function usePurchase() {
8240
8778
  }
8241
8779
 
8242
8780
  // src/hooks/useSubscription.ts
8243
- import { useCallback as useCallback10, useEffect as useEffect8, useState as useState8 } from "react";
8781
+ import { useCallback as useCallback12, useEffect as useEffect9, useState as useState10 } from "react";
8244
8782
  init_SubscriptionManager();
8245
8783
  var EMPTY_ENTITLEMENTS = [];
8246
8784
  function useSubscription() {
8247
- const [status, setStatus] = useState8(null);
8248
- const [isLoading, setIsLoading] = useState8(true);
8249
- const [error, setError] = useState8(null);
8250
- const refresh = useCallback10(async () => {
8785
+ const [status, setStatus] = useState10(null);
8786
+ const [isLoading, setIsLoading] = useState10(true);
8787
+ const [error, setError] = useState10(null);
8788
+ const refresh = useCallback12(async () => {
8251
8789
  setIsLoading(true);
8252
8790
  setError(null);
8253
8791
  try {
@@ -8260,7 +8798,7 @@ function useSubscription() {
8260
8798
  setIsLoading(false);
8261
8799
  }
8262
8800
  }, []);
8263
- const restore = useCallback10(async () => {
8801
+ const restore = useCallback12(async () => {
8264
8802
  setIsLoading(true);
8265
8803
  setError(null);
8266
8804
  try {
@@ -8273,7 +8811,7 @@ function useSubscription() {
8273
8811
  setIsLoading(false);
8274
8812
  }
8275
8813
  }, []);
8276
- useEffect8(() => {
8814
+ useEffect9(() => {
8277
8815
  void subscriptionManager.getSubscriptionStatus().then((s) => {
8278
8816
  setStatus(s);
8279
8817
  setIsLoading(false);
@@ -8300,16 +8838,19 @@ function useSubscription() {
8300
8838
  };
8301
8839
  }
8302
8840
 
8841
+ // src/index.ts
8842
+ init_plan();
8843
+
8303
8844
  // src/PaywalloProvider.tsx
8304
8845
  init_paywall();
8305
8846
  init_paywall();
8306
8847
  init_paywall();
8307
- import { useCallback as useCallback15, useEffect as useEffect10, useLayoutEffect, useMemo as useMemo2, useRef as useRef10, useState as useState12 } from "react";
8848
+ import { useCallback as useCallback17, useEffect as useEffect11, useLayoutEffect, useMemo as useMemo2, useRef as useRef10, useState as useState14 } from "react";
8308
8849
  import { Animated as Animated3, Dimensions as Dimensions4, Easing as Easing2, StyleSheet as StyleSheet3 } from "react-native";
8309
8850
  init_PaywalloClient();
8310
8851
 
8311
8852
  // src/hooks/useAppAutoEvents.ts
8312
- import { useEffect as useEffect9, useRef as useRef7 } from "react";
8853
+ import { useEffect as useEffect10, useRef as useRef7 } from "react";
8313
8854
  var FIRST_SEEN_KEY = "@panel:firstSeen";
8314
8855
  var DEBOUNCE_MS = 1e3;
8315
8856
  async function detectPlatform3() {
@@ -8340,7 +8881,7 @@ function useAppAutoEvents(config) {
8340
8881
  debug
8341
8882
  } = config;
8342
8883
  const didRunRef = useRef7(false);
8343
- useEffect9(() => {
8884
+ useEffect10(() => {
8344
8885
  if (!isInitialized || didRunRef.current) return;
8345
8886
  const timer = setTimeout(() => {
8346
8887
  if (didRunRef.current) return;
@@ -8387,7 +8928,7 @@ function useAppAutoEvents(config) {
8387
8928
  // src/hooks/useCampaignPreload.ts
8388
8929
  init_PaywalloClient();
8389
8930
  init_CampaignError();
8390
- import { useCallback as useCallback11, useRef as useRef8, useState as useState9 } from "react";
8931
+ import { useCallback as useCallback13, useRef as useRef8, useState as useState11 } from "react";
8391
8932
 
8392
8933
  // src/utils/loadCampaignProducts.ts
8393
8934
  init_IAPService();
@@ -8456,17 +8997,17 @@ function getCacheEntry(cache, placement) {
8456
8997
 
8457
8998
  // src/hooks/useCampaignPreload.ts
8458
8999
  function useCampaignPreload() {
8459
- const [preloadedWebView, setPreloadedWebView] = useState9(null);
9000
+ const [preloadedWebView, setPreloadedWebView] = useState11(null);
8460
9001
  const preloadedCampaignsRef = useRef8(/* @__PURE__ */ new Map());
8461
- const isPreloadValid = useCallback11(
9002
+ const isPreloadValid = useCallback13(
8462
9003
  (placement) => isCacheEntryValid(preloadedCampaignsRef.current.get(placement)),
8463
9004
  []
8464
9005
  );
8465
- const consumePreloadedCampaign = useCallback11(
9006
+ const consumePreloadedCampaign = useCallback13(
8466
9007
  (placement) => getCacheEntry(preloadedCampaignsRef.current, placement),
8467
9008
  []
8468
9009
  );
8469
- const preloadCampaign = useCallback11(
9010
+ const preloadCampaign = useCallback13(
8470
9011
  async (placement, context) => {
8471
9012
  try {
8472
9013
  const campaign = await PaywalloClient.getCampaign(placement, context);
@@ -8540,12 +9081,12 @@ init_SecureStorage();
8540
9081
  init_paywall();
8541
9082
 
8542
9083
  // src/hooks/useProductLoader.ts
8543
- import { useCallback as useCallback12, useState as useState10 } from "react";
9084
+ import { useCallback as useCallback14, useState as useState12 } from "react";
8544
9085
  init_PaywalloClient();
8545
9086
  function useProductLoader() {
8546
- const [products, setProducts] = useState10(/* @__PURE__ */ new Map());
8547
- const [isLoadingProducts, setIsLoadingProducts] = useState10(false);
8548
- const refreshProducts = useCallback12(async (productIds) => {
9087
+ const [products, setProducts] = useState12(/* @__PURE__ */ new Map());
9088
+ const [isLoadingProducts, setIsLoadingProducts] = useState12(false);
9089
+ const refreshProducts = useCallback14(async (productIds) => {
8549
9090
  setIsLoadingProducts(true);
8550
9091
  try {
8551
9092
  const iapService = getIAPService();
@@ -8563,12 +9104,12 @@ function useProductLoader() {
8563
9104
  }
8564
9105
 
8565
9106
  // src/hooks/usePurchaseOrchestrator.ts
8566
- import { useCallback as useCallback13 } from "react";
9107
+ import { useCallback as useCallback15 } from "react";
8567
9108
  init_PaywalloClient();
8568
9109
  init_paywall();
8569
9110
  function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, invalidateSubscriptionCache) {
8570
9111
  const { trackDismissed, trackProductSelected, trackPurchased } = usePaywallTracking();
8571
- const handlePreloadedPurchase = useCallback13(
9112
+ const handlePreloadedPurchase = useCallback15(
8572
9113
  async (productId) => {
8573
9114
  try {
8574
9115
  if (preloadedWebView) {
@@ -8606,7 +9147,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
8606
9147
  },
8607
9148
  [preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, trackDismissed, trackProductSelected, trackPurchased, invalidateSubscriptionCache]
8608
9149
  );
8609
- const handlePreloadedRestore = useCallback13(async () => {
9150
+ const handlePreloadedRestore = useCallback15(async () => {
8610
9151
  try {
8611
9152
  const transactions = await getIAPService().restore();
8612
9153
  if (transactions.length === 0) return;
@@ -8631,7 +9172,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
8631
9172
  // src/hooks/useSubscriptionSync.ts
8632
9173
  init_PaywalloClient();
8633
9174
  init_SubscriptionCache();
8634
- import { useCallback as useCallback14, useRef as useRef9, useState as useState11 } from "react";
9175
+ import { useCallback as useCallback16, useRef as useRef9, useState as useState13 } from "react";
8635
9176
  var CACHE_TTL_MS2 = 5 * 60 * 1e3;
8636
9177
  var ACTIVE_STATUSES = ["active", "in_grace_period"];
8637
9178
  var PUBLIC_TO_DOMAIN_STATUS = {
@@ -8696,14 +9237,14 @@ function isActiveCacheEntry(entry) {
8696
9237
  return isSubscriptionStillActive(entry.data);
8697
9238
  }
8698
9239
  function useSubscriptionSync() {
8699
- const [subscription, setSubscription] = useState11(null);
9240
+ const [subscription, setSubscription] = useState13(null);
8700
9241
  const cacheRef = useRef9(null);
8701
- const invalidateCache = useCallback14(() => {
9242
+ const invalidateCache = useCallback16(() => {
8702
9243
  cacheRef.current = null;
8703
9244
  const distinctId = PaywalloClient.getDistinctId();
8704
9245
  if (distinctId) void subscriptionCache.invalidate(distinctId);
8705
9246
  }, []);
8706
- const fetchAndCache = useCallback14(async () => {
9247
+ const fetchAndCache = useCallback16(async () => {
8707
9248
  const apiClient = PaywalloClient.getApiClient();
8708
9249
  const distinctId = PaywalloClient.getDistinctId();
8709
9250
  if (!apiClient || !distinctId) return null;
@@ -8717,7 +9258,7 @@ function useSubscriptionSync() {
8717
9258
  void subscriptionCache.set(distinctId, toDomainResponse(status));
8718
9259
  return sub;
8719
9260
  }, []);
8720
- const hasActiveSubscription = useCallback14(async () => {
9261
+ const hasActiveSubscription = useCallback16(async () => {
8721
9262
  const apiClient = PaywalloClient.getApiClient();
8722
9263
  const distinctId = PaywalloClient.getDistinctId();
8723
9264
  if (!apiClient || !distinctId) return false;
@@ -8760,7 +9301,7 @@ function useSubscriptionSync() {
8760
9301
  return resolveOfflineFromCache(distinctId);
8761
9302
  }
8762
9303
  }, []);
8763
- const getSubscription = useCallback14(async () => {
9304
+ const getSubscription = useCallback16(async () => {
8764
9305
  const apiClient = PaywalloClient.getApiClient();
8765
9306
  const distinctId = PaywalloClient.getDistinctId();
8766
9307
  if (!apiClient || !distinctId) return null;
@@ -8787,20 +9328,20 @@ init_ClientError();
8787
9328
  init_localization();
8788
9329
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
8789
9330
  function PaywalloProvider({ children, config }) {
8790
- const [isInitialized, setIsInitialized] = useState12(false);
8791
- const [initError, setInitError] = useState12(null);
8792
- const [distinctId, setDistinctId] = useState12(null);
8793
- const [activePaywall, setActivePaywall] = useState12(null);
9331
+ const [isInitialized, setIsInitialized] = useState14(false);
9332
+ const [initError, setInitError] = useState14(null);
9333
+ const [distinctId, setDistinctId] = useState14(null);
9334
+ const [activePaywall, setActivePaywall] = useState14(null);
8794
9335
  const { products, isLoadingProducts, refreshProducts } = useProductLoader();
8795
9336
  const { subscription, hasActiveSubscription, getSubscription, invalidateCache } = useSubscriptionSync();
8796
9337
  const { preloadedWebView, setPreloadedWebView, preloadCampaign, consumePreloadedCampaign, isPreloadValid } = useCampaignPreload();
8797
- const clearPreloadedWebView = useCallback15(() => setPreloadedWebView(null), [setPreloadedWebView]);
9338
+ const clearPreloadedWebView = useCallback17(() => setPreloadedWebView(null), [setPreloadedWebView]);
8798
9339
  const autoPreloadTriggeredRef = useRef10(false);
8799
9340
  const preloadedWebViewRef = useRef10(preloadedWebView);
8800
9341
  const pollTimerRef = useRef10(null);
8801
9342
  const pollCancelledRef = useRef10(false);
8802
9343
  const secureStorageRef = useRef10(new SecureStorage(config.debug));
8803
- useEffect10(() => {
9344
+ useEffect11(() => {
8804
9345
  preloadedWebViewRef.current = preloadedWebView;
8805
9346
  }, [preloadedWebView]);
8806
9347
  useAppAutoEvents({
@@ -8816,20 +9357,20 @@ function PaywalloProvider({ children, config }) {
8816
9357
  storageSet: (key, value) => secureStorageRef.current.set(key, value),
8817
9358
  debug: config.debug
8818
9359
  });
8819
- const triggerRepreload = useCallback15((placement) => {
9360
+ const triggerRepreload = useCallback17((placement) => {
8820
9361
  void PaywalloClient.preloadCampaign(placement).catch(() => {
8821
9362
  });
8822
9363
  }, []);
8823
9364
  const { showingPreloadedWebView, presentPreloadedWebView, resolvePreloadedPaywall, handlePreloadedWebViewReady: baseHandlePreloadedWebViewReady, handlePreloadedClose: baseHandlePreloadedClose, setShowingPreloadedWebView, presentedAtRef } = usePaywallPresenter(preloadedWebView, setPreloadedWebView);
8824
9365
  const SCREEN_HEIGHT = useMemo2(() => Dimensions4.get("window").height, []);
8825
9366
  const slideAnim = useRef10(new Animated3.Value(SCREEN_HEIGHT)).current;
8826
- const handlePreloadedWebViewReady = useCallback15(() => {
9367
+ const handlePreloadedWebViewReady = useCallback17(() => {
8827
9368
  if (PaywalloClient.getConfig()?.debug) {
8828
9369
  console.log(`[Paywallo WEBVIEW] loaded \u2014 placement: ${preloadedWebView?.placement}`);
8829
9370
  }
8830
9371
  baseHandlePreloadedWebViewReady();
8831
9372
  }, [preloadedWebView?.placement, baseHandlePreloadedWebViewReady]);
8832
- const handlePreloadedClose = useCallback15(() => {
9373
+ const handlePreloadedClose = useCallback17(() => {
8833
9374
  const placement = preloadedWebView?.placement;
8834
9375
  Animated3.timing(slideAnim, { toValue: SCREEN_HEIGHT, duration: 200, easing: Easing2.in(Easing2.cubic), useNativeDriver: true }).start(() => {
8835
9376
  baseHandlePreloadedClose();
@@ -8844,8 +9385,8 @@ function PaywalloProvider({ children, config }) {
8844
9385
  presentedAtRef,
8845
9386
  invalidateCache
8846
9387
  );
8847
- const [isPreloadPurchasing, setIsPreloadPurchasing] = useState12(false);
8848
- const handlePreloadedPurchase = useCallback15(async (productId) => {
9388
+ const [isPreloadPurchasing, setIsPreloadPurchasing] = useState14(false);
9389
+ const handlePreloadedPurchase = useCallback17(async (productId) => {
8849
9390
  setIsPreloadPurchasing(true);
8850
9391
  try {
8851
9392
  await rawPreloadedPurchase(productId);
@@ -8854,7 +9395,7 @@ function PaywalloProvider({ children, config }) {
8854
9395
  }
8855
9396
  }, [rawPreloadedPurchase]);
8856
9397
  const configRef = useRef10(config);
8857
- useEffect10(() => {
9398
+ useEffect11(() => {
8858
9399
  let mounted = true;
8859
9400
  initLocalization({ detectDevice: true });
8860
9401
  PaywalloClient.init(configRef.current).then(async () => {
@@ -8875,14 +9416,14 @@ function PaywalloProvider({ children, config }) {
8875
9416
  mounted = false;
8876
9417
  };
8877
9418
  }, [preloadCampaign]);
8878
- const getPaywallConfig = useCallback15(async (p) => {
9419
+ const getPaywallConfig = useCallback17(async (p) => {
8879
9420
  const api = PaywalloClient.getApiClient();
8880
9421
  return api ? api.getPaywall(p) : null;
8881
9422
  }, []);
8882
- const presentPaywall = useCallback15((placement) => {
9423
+ const presentPaywall = useCallback17((placement) => {
8883
9424
  return new Promise((resolve) => setActivePaywall({ placement, resolver: resolve }));
8884
9425
  }, []);
8885
- const trackCampaignImpression = useCallback15((placement, variantKey, campaignId) => {
9426
+ const trackCampaignImpression = useCallback17((placement, variantKey, campaignId) => {
8886
9427
  const api = PaywalloClient.getApiClient();
8887
9428
  if (!api) return;
8888
9429
  const sessionId = PaywalloClient.getSessionId();
@@ -8896,7 +9437,7 @@ function PaywalloProvider({ children, config }) {
8896
9437
  }).catch(() => {
8897
9438
  });
8898
9439
  }, []);
8899
- const presentCampaign = useCallback15(
9440
+ const presentCampaign = useCallback17(
8900
9441
  async (placement, context) => {
8901
9442
  try {
8902
9443
  if (preloadedWebView?.placement === placement && preloadedWebView.loaded && isPreloadValid(placement)) {
@@ -8970,7 +9511,7 @@ function PaywalloProvider({ children, config }) {
8970
9511
  },
8971
9512
  [preloadedWebView, isPreloadValid, consumePreloadedCampaign, presentPreloadedWebView, trackCampaignImpression]
8972
9513
  );
8973
- const restorePurchases = useCallback15(async () => {
9514
+ const restorePurchases = useCallback17(async () => {
8974
9515
  invalidateCache();
8975
9516
  try {
8976
9517
  const txs = await getIAPService().restore();
@@ -8979,13 +9520,13 @@ function PaywalloProvider({ children, config }) {
8979
9520
  return { success: false, restoredProducts: [], error: error instanceof Error ? error : new PurchaseError(PURCHASE_ERROR_CODES.RESTORE_FAILED, String(error)) };
8980
9521
  }
8981
9522
  }, [invalidateCache]);
8982
- const handlePaywallResult = useCallback15((result) => {
9523
+ const handlePaywallResult = useCallback17((result) => {
8983
9524
  if (activePaywall) {
8984
9525
  activePaywall.resolver(result);
8985
9526
  setActivePaywall(null);
8986
9527
  }
8987
9528
  }, [activePaywall]);
8988
- const bridgePaywallPresenter = useCallback15((placement) => {
9529
+ const bridgePaywallPresenter = useCallback17((placement) => {
8989
9530
  if (PaywalloClient.getConfig()?.debug) {
8990
9531
  console.log(`[Paywallo PRESENT] called \u2014 preloaded: ${preloadedWebViewRef.current?.loaded === true} placement: ${placement}`);
8991
9532
  }
@@ -9014,7 +9555,7 @@ function PaywalloProvider({ children, config }) {
9014
9555
  }
9015
9556
  return presentCampaign(placement);
9016
9557
  }, [presentPreloadedWebView, presentCampaign]);
9017
- useEffect10(() => {
9558
+ useEffect11(() => {
9018
9559
  if (!isInitialized) return;
9019
9560
  const onEmergency = async (id) => {
9020
9561
  try {
@@ -9041,7 +9582,7 @@ function PaywalloProvider({ children, config }) {
9041
9582
  };
9042
9583
  }, [isInitialized, bridgePaywallPresenter, getSubscription, hasActiveSubscription, restorePurchases]);
9043
9584
  const isReady = isInitialized && !isLoadingProducts;
9044
- const noOp = useCallback15(() => {
9585
+ const noOp = useCallback17(() => {
9045
9586
  }, []);
9046
9587
  const contextValue = useMemo2(() => ({
9047
9588
  config: PaywalloClient.getConfig(),
@@ -9283,6 +9824,8 @@ export {
9283
9824
  PaywalloContext,
9284
9825
  PaywalloError,
9285
9826
  PaywalloProvider,
9827
+ PlanCache,
9828
+ PlanService,
9286
9829
  PurchaseError,
9287
9830
  SESSION_ERROR_CODES,
9288
9831
  SessionError,
@@ -9305,6 +9848,8 @@ export {
9305
9848
  offlineQueue,
9306
9849
  onboardingManager,
9307
9850
  parseVariables,
9851
+ planCache,
9852
+ planService,
9308
9853
  queueProcessor,
9309
9854
  resolveText,
9310
9855
  sessionManager,
@@ -9314,6 +9859,8 @@ export {
9314
9859
  useOnboarding,
9315
9860
  usePaywallContext,
9316
9861
  usePaywallo,
9862
+ usePlanPurchase,
9863
+ usePlans,
9317
9864
  useProducts,
9318
9865
  usePurchase,
9319
9866
  useSubscription