@virex-tech/paywallo-sdk 2.2.7 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -412,11 +412,28 @@ function parseProperties(json) {
412
412
  return {};
413
413
  }
414
414
  }
415
- var DEVICE_ID_KEY, ANON_ID_KEY, USER_EMAIL_KEY, USER_PROPERTIES_KEY, USER_DISTINCT_ID_KEY, USER_NAME_KEY, USER_COUNTRY_KEY, USER_LOCALE_KEY, USER_PHONE_KEY, USER_FIRST_NAME_KEY, USER_LAST_NAME_KEY, USER_DOB_KEY, USER_GENDER_KEY, LEGACY_USER_PHONE_KEY, LEGACY_USER_FIRST_NAME_KEY, LEGACY_USER_LAST_NAME_KEY, LEGACY_USER_DOB_KEY, LEGACY_USER_GENDER_KEY, LEGACY_DEVICE_ID_KEY, LEGACY_ANON_ID_KEY, LEGACY_EMAIL_KEY, LEGACY_PROPERTIES_KEY, LEGACY_DISTINCT_ID_KEY, LEGACY_NAME_KEY, LEGACY_COUNTRY_KEY, LEGACY_LOCALE_KEY;
415
+ async function persistAnonIdDurably(storage, anonId) {
416
+ let stored = await storage.set(ANON_ID_KEY, anonId);
417
+ for (let i = 0; i < ANON_ID_SET_RETRIES && !stored; i++) {
418
+ await new Promise((r) => setTimeout(r, ANON_ID_RETRY_DELAY_MS));
419
+ stored = await storage.set(ANON_ID_KEY, anonId);
420
+ }
421
+ if (!stored) {
422
+ await nativeStorage.set(ANON_ID_FALLBACK_KEY, anonId).catch(() => void 0);
423
+ }
424
+ }
425
+ async function readAnonIdDurably(storage) {
426
+ const secure = await readWithMigration(storage, ANON_ID_KEY, LEGACY_ANON_ID_KEY);
427
+ if (secure) return secure;
428
+ return nativeStorage.get(ANON_ID_FALLBACK_KEY);
429
+ }
430
+ var DEVICE_ID_KEY, ANON_ID_FALLBACK_KEY, ANON_ID_KEY, USER_EMAIL_KEY, USER_PROPERTIES_KEY, USER_DISTINCT_ID_KEY, USER_NAME_KEY, USER_COUNTRY_KEY, USER_LOCALE_KEY, USER_PHONE_KEY, USER_FIRST_NAME_KEY, USER_LAST_NAME_KEY, USER_DOB_KEY, USER_GENDER_KEY, LEGACY_USER_PHONE_KEY, LEGACY_USER_FIRST_NAME_KEY, LEGACY_USER_LAST_NAME_KEY, LEGACY_USER_DOB_KEY, LEGACY_USER_GENDER_KEY, LEGACY_DEVICE_ID_KEY, LEGACY_ANON_ID_KEY, LEGACY_EMAIL_KEY, LEGACY_PROPERTIES_KEY, LEGACY_DISTINCT_ID_KEY, LEGACY_NAME_KEY, LEGACY_COUNTRY_KEY, LEGACY_LOCALE_KEY, ANON_ID_SET_RETRIES, ANON_ID_RETRY_DELAY_MS;
416
431
  var init_IdentityStorage = __esm({
417
432
  "src/domains/identity/IdentityStorage.ts"() {
418
433
  "use strict";
434
+ init_NativeStorage();
419
435
  DEVICE_ID_KEY = "@paywallo:device_id";
436
+ ANON_ID_FALLBACK_KEY = "@paywallo:anon_id_fallback";
420
437
  ANON_ID_KEY = "@paywallo:anon_id";
421
438
  USER_EMAIL_KEY = "@paywallo:user_email";
422
439
  USER_PROPERTIES_KEY = "@paywallo:user_properties";
@@ -442,6 +459,8 @@ var init_IdentityStorage = __esm({
442
459
  LEGACY_NAME_KEY = "user_name";
443
460
  LEGACY_COUNTRY_KEY = "user_country";
444
461
  LEGACY_LOCALE_KEY = "user_locale";
462
+ ANON_ID_SET_RETRIES = 2;
463
+ ANON_ID_RETRY_DELAY_MS = 50;
445
464
  }
446
465
  });
447
466
 
@@ -521,8 +540,7 @@ var init_IdentityManager = __esm({
521
540
  }
522
541
  if (!this.anonId) {
523
542
  this.anonId = generateUUID();
524
- await this.secureStorage.set(ANON_ID_KEY, this.anonId).catch(() => {
525
- });
543
+ await persistAnonIdDurably(this.secureStorage, this.anonId);
526
544
  }
527
545
  this.initialized = true;
528
546
  return this.deviceId ?? "";
@@ -582,7 +600,8 @@ var init_IdentityManager = __esm({
582
600
  dateOfBirth: this.dateOfBirth ?? void 0,
583
601
  gender: this.gender ?? void 0
584
602
  });
585
- } catch {
603
+ } catch (err) {
604
+ if (this.debug) console.log("[Paywallo] identify dispatch error", err);
586
605
  }
587
606
  }
588
607
  }
@@ -606,7 +625,8 @@ var init_IdentityManager = __esm({
606
625
  if (this.apiClient && this.anonId) {
607
626
  try {
608
627
  await this.apiClient.identify(this.anonId, this.properties, this.email ?? void 0, this.deviceId ?? void 0);
609
- } catch {
628
+ } catch (err) {
629
+ if (this.debug) console.log("[Paywallo] identify dispatch error", err);
610
630
  }
611
631
  }
612
632
  }
@@ -649,18 +669,20 @@ var init_IdentityManager = __esm({
649
669
  }
650
670
  async loadPersistedState() {
651
671
  if (!this.secureStorage) return;
652
- const state = await loadPersistedIdentity(this.secureStorage);
672
+ const [state, rawAnonId] = await Promise.all([
673
+ loadPersistedIdentity(this.secureStorage),
674
+ readAnonIdDurably(this.secureStorage)
675
+ ]);
653
676
  const deviceId = state.deviceId ?? await readDeviceIdWithFallback(this.secureStorage);
654
677
  if (deviceId) this.deviceId = deviceId;
655
- if (state.anonId) {
678
+ if (rawAnonId) {
656
679
  const legacyPrefix = "$paywallo_anon:";
657
- if (state.anonId.startsWith(legacyPrefix)) {
658
- const stripped = state.anonId.slice(legacyPrefix.length);
680
+ if (rawAnonId.startsWith(legacyPrefix)) {
681
+ const stripped = rawAnonId.slice(legacyPrefix.length);
659
682
  this.anonId = stripped;
660
- await this.secureStorage.set(ANON_ID_KEY, stripped).catch(() => {
661
- });
683
+ await this.secureStorage.set(ANON_ID_KEY, stripped).catch(() => void 0);
662
684
  } else {
663
- this.anonId = state.anonId;
685
+ this.anonId = rawAnonId;
664
686
  }
665
687
  }
666
688
  if (state.email) this.email = state.email;
@@ -885,21 +907,23 @@ var init_AdvertisingIdManager = __esm({
885
907
  if (Platform.OS === "ios") {
886
908
  if (!tracking.isAvailable()) {
887
909
  const idfv2 = await this.collectIdfv();
888
- this.cached = { ...fallback, idfv: idfv2 };
889
- return this.cached;
910
+ const result2 = { ...fallback, idfv: idfv2 };
911
+ if (idfv2 !== null) this.cached = result2;
912
+ return result2;
890
913
  }
891
914
  const current = await tracking.getTrackingPermissionsAsync();
892
915
  let status = current.status;
893
916
  let granted = current.granted;
894
917
  if (status === "undetermined" && requestATT) {
895
- const result = await tracking.requestTrackingPermissionsAsync();
896
- status = result.status;
897
- granted = result.granted;
918
+ const result2 = await tracking.requestTrackingPermissionsAsync();
919
+ status = result2.status;
920
+ granted = result2.granted;
898
921
  }
899
922
  const idfv = await this.collectIdfv();
900
923
  const idfa = granted ? this.sanitizeId(tracking.getAdvertisingId()) : null;
901
- this.cached = { idfv, idfa, gaid: null, attStatus: status };
902
- return this.cached;
924
+ const result = { idfv, idfa, gaid: null, attStatus: status };
925
+ if (idfv !== null) this.cached = result;
926
+ return result;
903
927
  }
904
928
  if (Platform.OS === "android") {
905
929
  const rawGaid = tracking.getAdvertisingId();
@@ -915,12 +939,20 @@ var init_AdvertisingIdManager = __esm({
915
939
  }
916
940
  }
917
941
  async collectIdfv() {
918
- try {
919
- const info = await nativeDeviceInfo.getDeviceInfo();
920
- return this.sanitizeId(info.idfv);
921
- } catch {
922
- return null;
942
+ const RETRY_DELAY_MS = 80;
943
+ const MAX_ATTEMPTS = 3;
944
+ for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
945
+ try {
946
+ const info = await nativeDeviceInfo.getDeviceInfo();
947
+ const idfv = this.sanitizeId(info.idfv);
948
+ if (idfv !== null) return idfv;
949
+ } catch {
950
+ }
951
+ if (attempt < MAX_ATTEMPTS - 1) {
952
+ await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS));
953
+ }
923
954
  }
955
+ return null;
924
956
  }
925
957
  sanitizeId(id) {
926
958
  if (!id || id === ZERO_IDFA) return null;
@@ -1358,6 +1390,8 @@ var init_AttributionTracker = __esm({
1358
1390
 
1359
1391
  // src/domains/identity/InstallIdempotency.ts
1360
1392
  async function checkAndArmInstallGuard(storage) {
1393
+ if (armedThisLaunch) return true;
1394
+ armedThisLaunch = true;
1361
1395
  const alreadyTracked = await storage.get(INSTALL_KEYS.INSTALL_TRACKED);
1362
1396
  if (alreadyTracked) return true;
1363
1397
  let fallbackTracked = null;
@@ -1391,7 +1425,7 @@ async function getOrCreateInstallEventId() {
1391
1425
  });
1392
1426
  return id;
1393
1427
  }
1394
- var INSTALL_KEYS;
1428
+ var INSTALL_KEYS, armedThisLaunch;
1395
1429
  var init_InstallIdempotency = __esm({
1396
1430
  "src/domains/identity/InstallIdempotency.ts"() {
1397
1431
  "use strict";
@@ -1416,6 +1450,7 @@ var init_InstallIdempotency = __esm({
1416
1450
  /** Legacy anon ID key written by SDK versions that used @panel: prefix. */
1417
1451
  LEGACY_ANON_ID: "@panel:anon_id"
1418
1452
  };
1453
+ armedThisLaunch = false;
1419
1454
  }
1420
1455
  });
1421
1456
 
@@ -2370,8 +2405,8 @@ var init_NotificationHandlerSetup = __esm({
2370
2405
  // src/core/version.ts
2371
2406
  function resolveVersion() {
2372
2407
  try {
2373
- if ("2.2.7") {
2374
- return "2.2.7";
2408
+ if ("2.4.0") {
2409
+ return "2.4.0";
2375
2410
  }
2376
2411
  } catch {
2377
2412
  }
@@ -3387,6 +3422,7 @@ function buildPaywallInjectionScript(data) {
3387
3422
  products: data.products,
3388
3423
  primaryProductId: data.primaryProductId,
3389
3424
  secondaryProductId: data.secondaryProductId,
3425
+ tertiaryProductId: data.tertiaryProductId,
3390
3426
  currentLanguage: getCurrentLanguage(),
3391
3427
  defaultLanguage: getDefaultLanguage()
3392
3428
  }
@@ -3485,6 +3521,7 @@ function PaywallWebView({
3485
3521
  products,
3486
3522
  primaryProductId,
3487
3523
  secondaryProductId,
3524
+ tertiaryProductId,
3488
3525
  isPurchasing,
3489
3526
  webUrl: webUrlProp,
3490
3527
  onPurchase,
@@ -3556,10 +3593,11 @@ function PaywallWebView({
3556
3593
  craftData,
3557
3594
  products: Array.from(products.values()),
3558
3595
  primaryProductId,
3559
- secondaryProductId
3596
+ secondaryProductId,
3597
+ tertiaryProductId
3560
3598
  });
3561
3599
  injectScript(script);
3562
- }, [craftData, products, primaryProductId, secondaryProductId, injectScript]);
3600
+ }, [craftData, products, primaryProductId, secondaryProductId, tertiaryProductId, injectScript]);
3563
3601
  const processedIdsRef = useRef2(/* @__PURE__ */ new Set());
3564
3602
  const dedupeTimersRef = useRef2(/* @__PURE__ */ new Map());
3565
3603
  useEffect2(() => {
@@ -4241,10 +4279,10 @@ var init_OfflineQueueStorage = __esm({
4241
4279
  init_NativeStorage();
4242
4280
  init_QueueJournal();
4243
4281
  OfflineQueueStorage = class {
4244
- constructor(config, emit, log2) {
4282
+ constructor(config, emit, log3) {
4245
4283
  this.config = config;
4246
4284
  this.emit = emit;
4247
- this.log = log2;
4285
+ this.log = log3;
4248
4286
  }
4249
4287
  async loadFromStorage(queue) {
4250
4288
  const { merged, recovered, hadJournal } = await mergeJournalIntoMain(
@@ -5502,7 +5540,8 @@ function usePaywallLoader(placement, visible, preloadedConfig, preloadedProducts
5502
5540
  placement: snap.placement,
5503
5541
  config: snap.config,
5504
5542
  primaryProductId: snap.primaryProductId,
5505
- secondaryProductId: snap.secondaryProductId
5543
+ secondaryProductId: snap.secondaryProductId,
5544
+ tertiaryProductId: snap.tertiaryProductId
5506
5545
  };
5507
5546
  } else {
5508
5547
  const fetched = await apiClient.getPaywall(placement);
@@ -5514,6 +5553,7 @@ function usePaywallLoader(placement, visible, preloadedConfig, preloadedProducts
5514
5553
  const productIds = [];
5515
5554
  if (config.primaryProductId) productIds.push(config.primaryProductId);
5516
5555
  if (config.secondaryProductId) productIds.push(config.secondaryProductId);
5556
+ if (config.tertiaryProductId) productIds.push(config.tertiaryProductId);
5517
5557
  const snapProducts = preloadedProductsRef.current;
5518
5558
  if (snapProducts && snapProducts.size > 0) {
5519
5559
  if (!cancelled) setProducts(snapProducts);
@@ -5707,6 +5747,7 @@ function PaywallModal({
5707
5747
  products,
5708
5748
  primaryProductId: paywallConfig.primaryProductId,
5709
5749
  secondaryProductId: paywallConfig.secondaryProductId,
5750
+ tertiaryProductId: paywallConfig.tertiaryProductId,
5710
5751
  isPurchasing,
5711
5752
  onReady: handleWebViewReady,
5712
5753
  onClose: handleClose,
@@ -5885,6 +5926,7 @@ var init_PaywallPreloadService = __esm({
5885
5926
  const productIds = [];
5886
5927
  if (config.primaryProductId) productIds.push(config.primaryProductId);
5887
5928
  if (config.secondaryProductId) productIds.push(config.secondaryProductId);
5929
+ if (config.tertiaryProductId) productIds.push(config.tertiaryProductId);
5888
5930
  if (productIds.length === 0) return /* @__PURE__ */ new Map();
5889
5931
  try {
5890
5932
  const loaded = await getIAPService().loadProducts(productIds);
@@ -7931,7 +7973,36 @@ var init_http = __esm({
7931
7973
 
7932
7974
  // src/core/ApiClient.ts
7933
7975
  import { Platform as Platform15 } from "react-native";
7934
- var ApiClient;
7976
+ function normalizeDateOfBirth(raw) {
7977
+ if (!raw) return void 0;
7978
+ if (DATE_OF_BIRTH_RE.test(raw)) return raw;
7979
+ const d = new Date(raw);
7980
+ if (isNaN(d.getTime())) return void 0;
7981
+ const hasTimeComponent = raw.includes("T") || raw.includes("Z");
7982
+ const yyyy = hasTimeComponent ? d.getUTCFullYear() : d.getFullYear();
7983
+ const mm = String((hasTimeComponent ? d.getUTCMonth() : d.getMonth()) + 1).padStart(2, "0");
7984
+ const dd = String(hasTimeComponent ? d.getUTCDate() : d.getDate()).padStart(2, "0");
7985
+ return `${yyyy}-${mm}-${dd}`;
7986
+ }
7987
+ function buildPiiPayload(pii) {
7988
+ const result = {};
7989
+ if (pii.phone) result.phone = pii.phone;
7990
+ if (pii.firstName) result.firstName = pii.firstName;
7991
+ if (pii.lastName) result.lastName = pii.lastName;
7992
+ const dob = normalizeDateOfBirth(pii.dateOfBirth);
7993
+ if (dob) result.dateOfBirth = dob;
7994
+ const g = normalizeGender(pii.gender);
7995
+ if (g) result.gender = g;
7996
+ return result;
7997
+ }
7998
+ function normalizeGender(raw) {
7999
+ if (!raw) return void 0;
8000
+ const lower = raw.toLowerCase();
8001
+ if (lower === "m" || lower === "male") return "m";
8002
+ if (lower === "f" || lower === "female") return "f";
8003
+ return void 0;
8004
+ }
8005
+ var DATE_OF_BIRTH_RE, ApiClient;
7935
8006
  var init_ApiClient = __esm({
7936
8007
  "src/core/ApiClient.ts"() {
7937
8008
  "use strict";
@@ -7944,6 +8015,7 @@ var init_ApiClient = __esm({
7944
8015
  init_http();
7945
8016
  init_queue();
7946
8017
  init_version();
8018
+ DATE_OF_BIRTH_RE = /^\d{4}-\d{2}-\d{2}$/;
7947
8019
  ApiClient = class {
7948
8020
  constructor(appKey, apiUrl, debug = false, environment = "Production", offlineQueueEnabled = true, timeout = 3e4) {
7949
8021
  this.cache = new ApiCache();
@@ -8102,6 +8174,8 @@ var init_ApiClient = __esm({
8102
8174
  }
8103
8175
  }
8104
8176
  const attribution = Object.keys(rawAttribution).length > 0 ? rawAttribution : void 0;
8177
+ const rawUserId = properties?.["userId"];
8178
+ const externalUserId = typeof rawUserId === "string" && rawUserId.length > 0 ? rawUserId : void 0;
8105
8179
  await postWithQueue(
8106
8180
  {
8107
8181
  isOfflineQueueEnabled: () => this.offlineQueueEnabled,
@@ -8112,14 +8186,11 @@ var init_ApiClient = __esm({
8112
8186
  "/sdk/identity/identify",
8113
8187
  {
8114
8188
  distinct_id: distinctId,
8189
+ ...externalUserId && { external_user_id: externalUserId },
8115
8190
  traits,
8116
8191
  ...attribution && { attribution },
8117
8192
  ...deviceId && { deviceId },
8118
- ...pii?.phone && { phone: pii.phone },
8119
- ...pii?.firstName && { firstName: pii.firstName },
8120
- ...pii?.lastName && { lastName: pii.lastName },
8121
- ...pii?.dateOfBirth && { dateOfBirth: pii.dateOfBirth },
8122
- ...pii?.gender && { gender: pii.gender }
8193
+ ...pii ? buildPiiPayload(pii) : {}
8123
8194
  },
8124
8195
  "identify"
8125
8196
  );
@@ -9681,6 +9752,88 @@ init_paywall();
9681
9752
  init_paywall();
9682
9753
  init_paywall();
9683
9754
 
9755
+ // src/domains/paywall/SuperwallAttributeBridge.ts
9756
+ init_identity();
9757
+ var lastSignature = null;
9758
+ function log(debug, msg, data) {
9759
+ if (!debug) return;
9760
+ if (data !== void 0) console.log(`[Paywallo:SuperwallAttr] ${msg}`, data);
9761
+ else console.log(`[Paywallo:SuperwallAttr] ${msg}`);
9762
+ }
9763
+ var PAID_MEDIUM = /cpc|ppc|cpm|cpa|paid|display/i;
9764
+ function deriveIsPaid(a) {
9765
+ if (a.fbclid || a.gclid || a.ttclid || a.tiktokCampaignId) return true;
9766
+ return Boolean(a.utmMedium && PAID_MEDIUM.test(a.utmMedium));
9767
+ }
9768
+ function deriveAdNetwork(a) {
9769
+ if (a.fbclid) return "meta";
9770
+ if (a.ttclid || a.tiktokCampaignId) return "tiktok";
9771
+ if (a.gclid) return "google";
9772
+ return a.utmSource;
9773
+ }
9774
+ function buildSuperwallAttributes(a) {
9775
+ const out = {};
9776
+ if (!a) {
9777
+ out.pw_is_paid = false;
9778
+ return out;
9779
+ }
9780
+ const set2 = (key, value) => {
9781
+ if (value) out[key] = value;
9782
+ };
9783
+ set2("pw_utm_source", a.utmSource);
9784
+ set2("pw_utm_medium", a.utmMedium);
9785
+ set2("pw_utm_campaign", a.utmCampaign);
9786
+ set2("pw_utm_content", a.utmContent);
9787
+ set2("pw_utm_term", a.utmTerm);
9788
+ set2("pw_fbclid", a.fbclid);
9789
+ set2("pw_gclid", a.gclid);
9790
+ set2("pw_ttclid", a.ttclid);
9791
+ set2("pw_tiktok_campaign_id", a.tiktokCampaignId);
9792
+ set2("pw_tiktok_adgroup_id", a.tiktokAdgroupId);
9793
+ set2("pw_tiktok_ad_id", a.tiktokAdId);
9794
+ set2("pw_referrer", a.referrer);
9795
+ set2("pw_install_referrer_source", a.installReferrerSource);
9796
+ set2("pw_ad_network", deriveAdNetwork(a));
9797
+ out.pw_is_paid = deriveIsPaid(a);
9798
+ out.pw_attributed_at = a.capturedAt;
9799
+ return out;
9800
+ }
9801
+ function pickShared(mod) {
9802
+ const shared = mod?.default?.shared ?? mod?.shared;
9803
+ return shared && typeof shared.setUserAttributes === "function" ? shared : null;
9804
+ }
9805
+ async function resolveSuperwallShared(debug) {
9806
+ try {
9807
+ const shared = pickShared(await import("expo-superwall/compat"));
9808
+ if (shared) return shared;
9809
+ } catch {
9810
+ }
9811
+ try {
9812
+ const shared = pickShared(await import("expo-superwall"));
9813
+ if (shared) return shared;
9814
+ } catch {
9815
+ }
9816
+ log(debug, "expo-superwall not available \u2014 attribute push skipped");
9817
+ return null;
9818
+ }
9819
+ async function pushAttributionToSuperwall(debug = false) {
9820
+ const attrs = buildSuperwallAttributes(attributionTracker.get());
9821
+ const signature = JSON.stringify(attrs);
9822
+ if (signature === lastSignature) {
9823
+ log(debug, "attributes unchanged \u2014 skipping push");
9824
+ return;
9825
+ }
9826
+ const shared = await resolveSuperwallShared(debug);
9827
+ if (!shared) return;
9828
+ try {
9829
+ await shared.setUserAttributes(attrs);
9830
+ lastSignature = signature;
9831
+ log(debug, "attributes pushed to Superwall", attrs);
9832
+ } catch (err) {
9833
+ log(debug, "setUserAttributes error", { err: String(err) });
9834
+ }
9835
+ }
9836
+
9684
9837
  // src/domains/paywall/SuperwallAutoBridge.ts
9685
9838
  init_PaywalloClient();
9686
9839
 
@@ -9714,7 +9867,7 @@ function buildDeterministicEventId(parts) {
9714
9867
  var bridgeStarted = false;
9715
9868
  var unsubscribe = null;
9716
9869
  var debugEnabled = false;
9717
- function log(msg, data) {
9870
+ function log2(msg, data) {
9718
9871
  if (!debugEnabled) return;
9719
9872
  if (data !== void 0) {
9720
9873
  console.log(`[Paywallo:SuperwallBridge] ${msg}`, data);
@@ -9744,7 +9897,7 @@ async function trackPaywallEvent(type, identifier, ts, extra) {
9744
9897
  }
9745
9898
  });
9746
9899
  } catch (err) {
9747
- log("track error", { type, identifier, eventId: _eventId, err: String(err) });
9900
+ log2("track error", { type, identifier, eventId: _eventId, err: String(err) });
9748
9901
  }
9749
9902
  }
9750
9903
  function mapDismissToCloseReason(resultType) {
@@ -9764,7 +9917,7 @@ function buildCallbacks() {
9764
9917
  return {
9765
9918
  onPaywallPresent: (info) => {
9766
9919
  const ts = Date.now();
9767
- log("onPaywallPresent", { identifier: info.identifier });
9920
+ log2("onPaywallPresent", { identifier: info.identifier });
9768
9921
  void trackPaywallEvent("viewed", info.identifier, ts, {
9769
9922
  placement: info.presentedByEventWithName,
9770
9923
  variant_id: info.experiment?.variant?.id,
@@ -9773,7 +9926,7 @@ function buildCallbacks() {
9773
9926
  },
9774
9927
  onPaywallDismiss: (info, result) => {
9775
9928
  const ts = Date.now();
9776
- log("onPaywallDismiss", { identifier: info.identifier, result: result.type });
9929
+ log2("onPaywallDismiss", { identifier: info.identifier, result: result.type });
9777
9930
  void trackPaywallEvent("closed", info.identifier, ts, {
9778
9931
  placement: info.presentedByEventWithName,
9779
9932
  variant_id: info.experiment?.variant?.id,
@@ -9787,7 +9940,7 @@ function buildCallbacks() {
9787
9940
  const safe = params !== null && typeof params === "object" ? params : {};
9788
9941
  const productId = "productId" in safe && typeof safe.productId === "string" ? safe.productId : "unknown";
9789
9942
  const identifier = "paywallIdentifier" in safe && typeof safe.paywallIdentifier === "string" ? safe.paywallIdentifier : productId;
9790
- log("onPurchase", { productId, identifier });
9943
+ log2("onPurchase", { productId, identifier });
9791
9944
  void trackPaywallEvent("purchased", identifier, ts, { product_id: productId });
9792
9945
  },
9793
9946
  // Global Superwall event listener — used specifically for `transactionComplete`
@@ -9802,7 +9955,7 @@ function buildCallbacks() {
9802
9955
  const product = ev.product;
9803
9956
  const transaction = ev.transaction;
9804
9957
  if (!product) {
9805
- log("transactionComplete missing product \u2014 skipping transaction track");
9958
+ log2("transactionComplete missing product \u2014 skipping transaction track");
9806
9959
  return;
9807
9960
  }
9808
9961
  const productId = product.productIdentifier ?? product.id ?? "unknown";
@@ -9811,7 +9964,7 @@ function buildCallbacks() {
9811
9964
  const transactionId = transaction?.storeTransactionId ?? transaction?.originalTransactionIdentifier ?? `${productId}_${String(ts)}`;
9812
9965
  const isTrial = product.hasFreeTrial === true || (product.trialPeriodPrice ?? 0) > 0;
9813
9966
  const paywallIdentifier = ev.paywallInfo?.identifier;
9814
- log("onSuperwallEvent: transactionComplete", { productId, currency, price, transactionId, isTrial });
9967
+ log2("onSuperwallEvent: transactionComplete", { productId, currency, price, transactionId, isTrial });
9815
9968
  void (async () => {
9816
9969
  try {
9817
9970
  await PaywalloClient.track("transaction", {
@@ -9829,7 +9982,7 @@ function buildCallbacks() {
9829
9982
  }
9830
9983
  });
9831
9984
  } catch (err) {
9832
- log("transactionComplete track error", { err: String(err) });
9985
+ log2("transactionComplete track error", { err: String(err) });
9833
9986
  }
9834
9987
  })();
9835
9988
  }
@@ -9838,7 +9991,7 @@ function buildCallbacks() {
9838
9991
  function startSuperwallAutoBridge(debug = false) {
9839
9992
  debugEnabled = debug;
9840
9993
  if (bridgeStarted) {
9841
- log("already started, skipping");
9994
+ log2("already started, skipping");
9842
9995
  return;
9843
9996
  }
9844
9997
  void detectAndStart();
@@ -9850,7 +10003,7 @@ async function detectAndStart() {
9850
10003
  if (!mod || typeof mod.subscribeToSuperwallEvents !== "function") {
9851
10004
  const internal = await import("expo-superwall/build/src/internal/superwallEventBridge");
9852
10005
  if (typeof internal.subscribeToSuperwallEvents !== "function") {
9853
- log("expo-superwall subscribeToSuperwallEvents not found \u2014 bridge inactive");
10006
+ log2("expo-superwall subscribeToSuperwallEvents not found \u2014 bridge inactive");
9854
10007
  return;
9855
10008
  }
9856
10009
  subscribeFn = internal.subscribeToSuperwallEvents;
@@ -9858,16 +10011,16 @@ async function detectAndStart() {
9858
10011
  subscribeFn = mod.subscribeToSuperwallEvents;
9859
10012
  }
9860
10013
  } catch {
9861
- log("expo-superwall not installed \u2014 bridge inactive");
10014
+ log2("expo-superwall not installed \u2014 bridge inactive");
9862
10015
  return;
9863
10016
  }
9864
10017
  try {
9865
10018
  const callbacks = buildCallbacks();
9866
10019
  unsubscribe = subscribeFn(void 0, () => callbacks);
9867
10020
  bridgeStarted = true;
9868
- log("bridge started");
10021
+ log2("bridge started");
9869
10022
  } catch (err) {
9870
- log("subscribe failed", { err: String(err) });
10023
+ log2("subscribe failed", { err: String(err) });
9871
10024
  }
9872
10025
  }
9873
10026
 
@@ -10440,6 +10593,7 @@ function PaywalloProvider({ children, config }) {
10440
10593
  if (!mounted) return;
10441
10594
  setDistinctId(PaywalloClient.getDistinctId());
10442
10595
  startSuperwallAutoBridge(configRef.current.debug);
10596
+ void pushAttributionToSuperwall(configRef.current.debug);
10443
10597
  setIsInitialized(true);
10444
10598
  if (!autoPreloadTriggeredRef.current) {
10445
10599
  autoPreloadTriggeredRef.current = true;
@@ -10475,7 +10629,8 @@ function PaywalloProvider({ children, config }) {
10475
10629
  placement: preloaded.config.placement,
10476
10630
  config: preloaded.config.config,
10477
10631
  primaryProductId: preloaded.config.primaryProductId,
10478
- secondaryProductId: preloaded.config.secondaryProductId
10632
+ secondaryProductId: preloaded.config.secondaryProductId,
10633
+ tertiaryProductId: preloaded.config.tertiaryProductId
10479
10634
  },
10480
10635
  preloadedProducts: preloaded.products
10481
10636
  }
@@ -10777,6 +10932,9 @@ function resolveProductsVariable(parts, context) {
10777
10932
  if (parts[1] === "secondary" && context.secondaryProductId) {
10778
10933
  return getProductVariable(context.products.get(context.secondaryProductId), parts[2]);
10779
10934
  }
10935
+ if (parts[1] === "tertiary" && context.tertiaryProductId) {
10936
+ return getProductVariable(context.products.get(context.tertiaryProductId), parts[2]);
10937
+ }
10780
10938
  if (parts[1] === "hasIntroductoryOffer") {
10781
10939
  return Array.from(context.products.values()).some((p) => p.introductoryPrice) ? "true" : "false";
10782
10940
  }