@virex-tech/paywallo-sdk 2.5.3 → 2.6.2

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
@@ -475,7 +475,7 @@ async function readAnonIdDurably(storage) {
475
475
  if (secure) return secure;
476
476
  return nativeStorage.get(ANON_ID_FALLBACK_KEY);
477
477
  }
478
- 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;
478
+ 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, USER_ZIP_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;
479
479
  var init_IdentityStorage = __esm({
480
480
  "src/domains/identity/IdentityStorage.ts"() {
481
481
  "use strict";
@@ -494,6 +494,7 @@ var init_IdentityStorage = __esm({
494
494
  USER_LAST_NAME_KEY = "@paywallo:user_last_name";
495
495
  USER_DOB_KEY = "@paywallo:user_dob";
496
496
  USER_GENDER_KEY = "@paywallo:user_gender";
497
+ USER_ZIP_KEY = "@paywallo:user_zip";
497
498
  LEGACY_USER_PHONE_KEY = "@panel:user_phone";
498
499
  LEGACY_USER_FIRST_NAME_KEY = "@panel:user_first_name";
499
500
  LEGACY_USER_LAST_NAME_KEY = "@panel:user_last_name";
@@ -544,6 +545,7 @@ var init_IdentityManager = __esm({
544
545
  this.lastName = null;
545
546
  this.dateOfBirth = null;
546
547
  this.gender = null;
548
+ this.zipCode = null;
547
549
  this.apiClient = null;
548
550
  this.secureStorage = null;
549
551
  this.debug = false;
@@ -598,7 +600,7 @@ var init_IdentityManager = __esm({
598
600
  let email;
599
601
  let properties;
600
602
  if (options) {
601
- if ("email" in options || "properties" in options || "phone" in options || "firstName" in options || "lastName" in options || "dateOfBirth" in options || "gender" in options) {
603
+ if ("email" in options || "properties" in options || "phone" in options || "firstName" in options || "lastName" in options || "dateOfBirth" in options || "gender" in options || "zipCode" in options) {
602
604
  email = options.email;
603
605
  properties = options.properties;
604
606
  } else {
@@ -639,6 +641,10 @@ var init_IdentityManager = __esm({
639
641
  this.gender = opts.gender;
640
642
  await this.secureStorage.set(USER_GENDER_KEY, this.gender);
641
643
  }
644
+ if (opts?.zipCode) {
645
+ this.zipCode = opts.zipCode;
646
+ await this.secureStorage.set(USER_ZIP_KEY, this.zipCode);
647
+ }
642
648
  if (this.apiClient && this.anonId) {
643
649
  try {
644
650
  await this.apiClient.identify(this.anonId, properties, email, this.deviceId ?? void 0, {
@@ -646,11 +652,15 @@ var init_IdentityManager = __esm({
646
652
  firstName: this.firstName ?? void 0,
647
653
  lastName: this.lastName ?? void 0,
648
654
  dateOfBirth: this.dateOfBirth ?? void 0,
649
- gender: this.gender ?? void 0
655
+ gender: this.gender ?? void 0,
656
+ zipCode: this.zipCode ?? void 0
650
657
  });
658
+ if (this.debug) console.log("[Paywallo] identify confirmed", this.anonId.slice(0, 8));
651
659
  } catch (err) {
652
- if (this.debug) console.log("[Paywallo] identify dispatch error", err);
660
+ if (this.debug) console.log("[Paywallo] identify failed:", err);
653
661
  }
662
+ } else {
663
+ if (this.debug) console.log("[Paywallo] identify skipped \u2014 anonId not ready");
654
664
  }
655
665
  }
656
666
  getDistinctId() {
@@ -693,6 +703,7 @@ var init_IdentityManager = __esm({
693
703
  this.lastName = null;
694
704
  this.dateOfBirth = null;
695
705
  this.gender = null;
706
+ this.zipCode = null;
696
707
  await Promise.all([
697
708
  this.secureStorage?.set(ANON_ID_KEY, newAnonId) ?? Promise.resolve(false),
698
709
  this.secureStorage?.remove(USER_EMAIL_KEY) ?? Promise.resolve(false),
@@ -701,7 +712,8 @@ var init_IdentityManager = __esm({
701
712
  this.secureStorage?.remove(USER_FIRST_NAME_KEY) ?? Promise.resolve(false),
702
713
  this.secureStorage?.remove(USER_LAST_NAME_KEY) ?? Promise.resolve(false),
703
714
  this.secureStorage?.remove(USER_DOB_KEY) ?? Promise.resolve(false),
704
- this.secureStorage?.remove(USER_GENDER_KEY) ?? Promise.resolve(false)
715
+ this.secureStorage?.remove(USER_GENDER_KEY) ?? Promise.resolve(false),
716
+ this.secureStorage?.remove(USER_ZIP_KEY) ?? Promise.resolve(false)
705
717
  ]);
706
718
  }
707
719
  getState() {
@@ -713,7 +725,8 @@ var init_IdentityManager = __esm({
713
725
  firstName: this.firstName,
714
726
  lastName: this.lastName,
715
727
  dateOfBirth: this.dateOfBirth,
716
- gender: this.gender
728
+ gender: this.gender,
729
+ zipCode: this.zipCode
717
730
  };
718
731
  }
719
732
  isSecureStorageAvailable() {
@@ -931,17 +944,39 @@ var init_campaign = __esm({
931
944
  }
932
945
  });
933
946
 
947
+ // src/domains/identity/NativeAdvertisingId.ts
948
+ import { NativeModules as NativeModules2 } from "react-native";
949
+ var nativeModule, nativeAdvertisingId;
950
+ var init_NativeAdvertisingId = __esm({
951
+ "src/domains/identity/NativeAdvertisingId.ts"() {
952
+ "use strict";
953
+ nativeModule = NativeModules2.PaywalloAdvertisingId ?? null;
954
+ nativeAdvertisingId = {
955
+ async getAdvertisingId() {
956
+ try {
957
+ return await nativeModule?.getAdvertisingId?.() ?? null;
958
+ } catch {
959
+ return null;
960
+ }
961
+ }
962
+ };
963
+ }
964
+ });
965
+
934
966
  // src/domains/identity/AdvertisingIdManager.ts
935
967
  import { Platform as Platform2 } from "react-native";
936
- var ZERO_IDFA, AdvertisingIdManager, advertisingIdManager;
968
+ var ZERO_IDFA, ATT_PROMPT_TIMEOUT_MS, AdvertisingIdManager, advertisingIdManager;
937
969
  var init_AdvertisingIdManager = __esm({
938
970
  "src/domains/identity/AdvertisingIdManager.ts"() {
939
971
  "use strict";
940
972
  init_NativeDeviceInfo();
973
+ init_NativeAdvertisingId();
941
974
  ZERO_IDFA = "00000000-0000-0000-0000-000000000000";
975
+ ATT_PROMPT_TIMEOUT_MS = 4e3;
942
976
  AdvertisingIdManager = class {
943
- constructor() {
977
+ constructor(debug = false) {
944
978
  this.cached = null;
979
+ this.debug = debug;
945
980
  }
946
981
  /**
947
982
  * Synchronous accessor for the cached collect() result. Returns null until
@@ -956,6 +991,9 @@ var init_AdvertisingIdManager = __esm({
956
991
  return this.cached;
957
992
  }
958
993
  const fallback = { idfv: null, idfa: null, gaid: null, attStatus: "unavailable" };
994
+ if (Platform2.OS === "android") {
995
+ return this.collectAndroid();
996
+ }
959
997
  try {
960
998
  const tracking = await import("expo-tracking-transparency");
961
999
  if (Platform2.OS === "ios") {
@@ -969,9 +1007,20 @@ var init_AdvertisingIdManager = __esm({
969
1007
  let status = current.status;
970
1008
  let granted = current.granted;
971
1009
  if (status === "undetermined" && requestATT) {
972
- const result2 = await tracking.requestTrackingPermissionsAsync();
973
- status = result2.status;
974
- granted = result2.granted;
1010
+ const result2 = await Promise.race([
1011
+ tracking.requestTrackingPermissionsAsync(),
1012
+ new Promise((r) => setTimeout(() => r(null), ATT_PROMPT_TIMEOUT_MS))
1013
+ ]);
1014
+ if (result2 === null) {
1015
+ if (this.debug) {
1016
+ console.log(`[Paywallo] ATT prompt timed out after ${ATT_PROMPT_TIMEOUT_MS}ms \u2014 treating as undetermined`);
1017
+ }
1018
+ status = "undetermined";
1019
+ granted = false;
1020
+ } else {
1021
+ status = result2.status;
1022
+ granted = result2.granted;
1023
+ }
975
1024
  }
976
1025
  const idfv = await this.collectIdfv();
977
1026
  const idfa = granted ? this.sanitizeId(tracking.getAdvertisingId()) : null;
@@ -979,12 +1028,6 @@ var init_AdvertisingIdManager = __esm({
979
1028
  if (idfv !== null) this.cached = result;
980
1029
  return result;
981
1030
  }
982
- if (Platform2.OS === "android") {
983
- const rawGaid = tracking.getAdvertisingId();
984
- const gaid = this.sanitizeId(rawGaid);
985
- this.cached = { idfv: null, idfa: null, gaid, attStatus: "unavailable" };
986
- return this.cached;
987
- }
988
1031
  this.cached = fallback;
989
1032
  return this.cached;
990
1033
  } catch {
@@ -992,6 +1035,27 @@ var init_AdvertisingIdManager = __esm({
992
1035
  return this.cached;
993
1036
  }
994
1037
  }
1038
+ async collectAndroid() {
1039
+ const nativeGaid = this.sanitizeId(
1040
+ await Promise.race([
1041
+ nativeAdvertisingId.getAdvertisingId(),
1042
+ new Promise((r) => setTimeout(() => r(null), 1500))
1043
+ ])
1044
+ );
1045
+ if (nativeGaid !== null) {
1046
+ this.cached = { idfv: null, idfa: null, gaid: nativeGaid, attStatus: "unavailable" };
1047
+ return this.cached;
1048
+ }
1049
+ try {
1050
+ const tracking = await import("expo-tracking-transparency");
1051
+ const gaid = this.sanitizeId(tracking.getAdvertisingId());
1052
+ this.cached = { idfv: null, idfa: null, gaid, attStatus: "unavailable" };
1053
+ return this.cached;
1054
+ } catch {
1055
+ this.cached = { idfv: null, idfa: null, gaid: null, attStatus: "unavailable" };
1056
+ return this.cached;
1057
+ }
1058
+ }
995
1059
  async collectIdfv() {
996
1060
  const RETRY_DELAY_MS = 80;
997
1061
  const MAX_ATTEMPTS = 3;
@@ -1598,13 +1662,13 @@ var init_deferredAppLink = __esm({
1598
1662
  });
1599
1663
 
1600
1664
  // src/domains/identity/MetaBridge.ts
1601
- import { NativeModules as NativeModules2 } from "react-native";
1665
+ import { NativeModules as NativeModules3 } from "react-native";
1602
1666
  var TIMEOUT_MS, NativeBridge, MetaBridge, metaBridge;
1603
1667
  var init_MetaBridge = __esm({
1604
1668
  "src/domains/identity/MetaBridge.ts"() {
1605
1669
  "use strict";
1606
1670
  TIMEOUT_MS = 2e3;
1607
- NativeBridge = NativeModules2.PaywalloFBBridge ?? null;
1671
+ NativeBridge = NativeModules3.PaywalloFBBridge ?? null;
1608
1672
  MetaBridge = class {
1609
1673
  constructor() {
1610
1674
  this.cachedAnonId = null;
@@ -1937,6 +2001,7 @@ var init_InstallTracker = __esm({
1937
2001
  } else {
1938
2002
  const { generateUUID: generateUUID2 } = await Promise.resolve().then(() => (init_uuid(), uuid_exports));
1939
2003
  effectiveAnonId = `PW_${generateUUID2()}`;
2004
+ if (this.debug) console.log("[Paywallo] fb_anon_id: gerado PW_ local (Facebook Android SDK ausente no host? _fbp real indispon\xEDvel \u2014 Meta n\xE3o reconhece PW_)");
1940
2005
  await storage.set(INSTALL_KEYS.ANON_ID, effectiveAnonId).catch(() => {
1941
2006
  });
1942
2007
  }
@@ -1965,11 +2030,11 @@ var init_InstallTracker = __esm({
1965
2030
  ...adIds.gaid && { gaid: adIds.gaid },
1966
2031
  attStatus: adIds.attStatus
1967
2032
  }, void 0, { priority: "critical" });
1968
- void this.callDeferredMatch(apiClient, deviceData, adIds, effectiveAnonId, installedAt).catch(() => {
2033
+ void this.callDeferredMatch(apiClient, distinctId, deviceData, adIds, effectiveAnonId, installedAt).catch(() => {
1969
2034
  });
1970
2035
  await markInstallTracked(storage, installedAt);
1971
2036
  }
1972
- async callDeferredMatch(apiClient, deviceData, adIds, anonId, installedAt) {
2037
+ async callDeferredMatch(apiClient, distinctId, deviceData, adIds, anonId, installedAt) {
1973
2038
  const storage = new SecureStorage(this.debug);
1974
2039
  const alreadyMatched = await storage.get(INSTALL_KEYS.DEFERRED_MATCH_DONE);
1975
2040
  if (alreadyMatched) return;
@@ -1984,6 +2049,10 @@ var init_InstallTracker = __esm({
1984
2049
  headers: { "Content-Type": "application/json", "X-App-Key": appKey },
1985
2050
  body: JSON.stringify({
1986
2051
  platform: Platform4.OS,
2052
+ // Sem isto o servidor não tem identificador no Android (não há IDFV) e grava a
2053
+ // attribution com distinct_id vazio — o reader junta por
2054
+ // install_attributions.distinct_id = app_users.external_id e nunca casa.
2055
+ distinctId,
1987
2056
  ...adIds.idfv && { idfv: adIds.idfv },
1988
2057
  ...adIds.gaid && { gaid: adIds.gaid },
1989
2058
  ...referrer.rawReferrer && { installReferrer: referrer.rawReferrer },
@@ -2144,12 +2213,12 @@ var init_identity = __esm({
2144
2213
  });
2145
2214
 
2146
2215
  // src/domains/notifications/bridges/NativePushBridge.ts
2147
- import { NativeEventEmitter, NativeModules as NativeModules3, Platform as Platform5 } from "react-native";
2216
+ import { NativeEventEmitter, NativeModules as NativeModules4, Platform as Platform5 } from "react-native";
2148
2217
  function getPaywalloNotifications() {
2149
- return NativeModules3.PaywalloNotifications ?? null;
2218
+ return NativeModules4.PaywalloNotifications ?? null;
2150
2219
  }
2151
2220
  function isNativePushAvailable() {
2152
- return !!NativeModules3.PaywalloNotifications;
2221
+ return !!NativeModules4.PaywalloNotifications;
2153
2222
  }
2154
2223
  function mapStatusToNumber(status) {
2155
2224
  switch (status) {
@@ -2171,7 +2240,7 @@ function mapStatusToNumber(status) {
2171
2240
  }
2172
2241
  function getEmitter() {
2173
2242
  if (_emitter) return _emitter;
2174
- const nativeMod = NativeModules3.PaywalloNotifications;
2243
+ const nativeMod = NativeModules4.PaywalloNotifications;
2175
2244
  if (!nativeMod) return null;
2176
2245
  _emitter = new NativeEventEmitter(nativeMod);
2177
2246
  return _emitter;
@@ -2285,18 +2354,15 @@ var init_constants = __esm({
2285
2354
  });
2286
2355
 
2287
2356
  // src/domains/notifications/config.ts
2288
- function resolveApiBaseUrl(config) {
2289
- if (config.apiBaseUrl) return config.apiBaseUrl;
2290
- if (config.environment === "sandbox") return SANDBOX_API_BASE_URL;
2357
+ function resolveApiBaseUrl() {
2291
2358
  return DEFAULT_API_BASE_URL;
2292
2359
  }
2293
- var DEFAULT_API_BASE_URL, SANDBOX_API_BASE_URL;
2360
+ var DEFAULT_API_BASE_URL;
2294
2361
  var init_config = __esm({
2295
2362
  "src/domains/notifications/config.ts"() {
2296
2363
  "use strict";
2297
2364
  init_constants();
2298
2365
  DEFAULT_API_BASE_URL = DEFAULT_API_URL;
2299
- SANDBOX_API_BASE_URL = DEFAULT_API_URL;
2300
2366
  }
2301
2367
  });
2302
2368
 
@@ -2611,8 +2677,8 @@ var init_NotificationHandlerSetup = __esm({
2611
2677
  // src/core/version.ts
2612
2678
  function resolveVersion() {
2613
2679
  try {
2614
- if ("2.5.3") {
2615
- return "2.5.3";
2680
+ if ("2.6.2") {
2681
+ return "2.6.2";
2616
2682
  }
2617
2683
  } catch {
2618
2684
  }
@@ -3066,10 +3132,7 @@ var init_NotificationsManager = __esm({
3066
3132
  }
3067
3133
  applyConfig(config) {
3068
3134
  this.appKey = config.appKey;
3069
- this.apiBaseUrl = resolveApiBaseUrl({
3070
- apiBaseUrl: config.apiBaseUrl,
3071
- environment: config.environment
3072
- });
3135
+ this.apiBaseUrl = resolveApiBaseUrl();
3073
3136
  this.debug = config.debug ?? false;
3074
3137
  this.permissionManager.setDebug(this.debug);
3075
3138
  }
@@ -3245,7 +3308,8 @@ var init_OnboardingError = __esm({
3245
3308
  };
3246
3309
  ONBOARDING_ERROR_CODES = {
3247
3310
  NOT_INITIALIZED: "ONBOARDING_NOT_INITIALIZED",
3248
- INVALID_STEP_NAME: "ONBOARDING_INVALID_STEP_NAME"
3311
+ INVALID_STEP_NAME: "ONBOARDING_INVALID_STEP_NAME",
3312
+ INVALID_ORDER: "ONBOARDING_INVALID_ORDER"
3249
3313
  };
3250
3314
  }
3251
3315
  });
@@ -3275,16 +3339,20 @@ var init_OnboardingManager = __esm({
3275
3339
  * Saves the step name internally for implicit drop detection on the backend.
3276
3340
  *
3277
3341
  * @param stepName - Unique name for this onboarding step.
3278
- * @param order - Optional explicit display order (accepts decimals, e.g. 2.1 for A/B variants).
3342
+ * @param order - Ordem de exibição do step. Obrigatório. Aceita decimais (ex: 2.1 para variantes A/B).
3343
+ * Deve ser um número finito e não-negativo. O servidor faz o floor — não arredondar aqui.
3279
3344
  */
3280
- async step(stepName, order) {
3345
+ async step(stepName, order, options) {
3281
3346
  this.assertConfig();
3282
3347
  this.validateStepName(stepName, "step");
3348
+ this.validateOrder(order);
3283
3349
  this.lastStep = stepName;
3284
- const stepPayload = { step_name: stepName };
3285
- if (order !== void 0 && Number.isFinite(order) && order >= 0) {
3286
- stepPayload.order = order;
3287
- }
3350
+ const stepPayload = {
3351
+ step_name: stepName,
3352
+ order,
3353
+ ...options?.variantKey !== void 0 ? { variant_key: options.variantKey } : {},
3354
+ ...options?.timeOnPrevS !== void 0 ? { time_on_prev_s: options.timeOnPrevS } : {}
3355
+ };
3288
3356
  const payload = {
3289
3357
  family: "onboarding",
3290
3358
  type: "step",
@@ -3296,33 +3364,21 @@ var init_OnboardingManager = __esm({
3296
3364
  * Tracks successful completion of the onboarding flow.
3297
3365
  * Marks the flow as finished.
3298
3366
  */
3299
- async complete() {
3300
- this.assertConfig();
3301
- this.finished = true;
3302
- return this.emit("onboarding", { family: "onboarding", type: "complete" }, "complete");
3303
- }
3304
- /**
3305
- * Tracks an explicit drop (abandonment) at the given step.
3306
- * Marks the flow as finished.
3307
- *
3308
- * @param stepName - The step name where the user dropped off.
3309
- */
3310
- async drop(stepName) {
3367
+ async complete(options) {
3311
3368
  this.assertConfig();
3312
- this.validateStepName(stepName, "drop");
3313
3369
  this.finished = true;
3314
3370
  const payload = {
3315
3371
  family: "onboarding",
3316
- type: "drop",
3317
- ...{ step_name: stepName }
3372
+ type: "complete",
3373
+ ...options?.variantKey !== void 0 ? { variant_key: options.variantKey } : {}
3318
3374
  };
3319
- return this.emit("onboarding", payload, "drop");
3375
+ return this.emit("onboarding", payload, "complete");
3320
3376
  }
3321
3377
  /** Returns the last step name seen, or null if no step was tracked yet. */
3322
3378
  getLastStep() {
3323
3379
  return this.lastStep;
3324
3380
  }
3325
- /** Returns whether the flow has been marked as finished (complete or drop). */
3381
+ /** Returns whether the flow has been marked as finished (complete). */
3326
3382
  isFinished() {
3327
3383
  return this.finished;
3328
3384
  }
@@ -3354,6 +3410,14 @@ var init_OnboardingManager = __esm({
3354
3410
  );
3355
3411
  }
3356
3412
  }
3413
+ validateOrder(order) {
3414
+ if (order === void 0 || order === null || !Number.isFinite(order) || order < 0) {
3415
+ throw new OnboardingError(
3416
+ ONBOARDING_ERROR_CODES.INVALID_ORDER,
3417
+ "OnboardingManager.step(): order deve ser um n\xFAmero finito e n\xE3o-negativo."
3418
+ );
3419
+ }
3420
+ }
3357
3421
  };
3358
3422
  onboardingManager = new OnboardingManager();
3359
3423
  }
@@ -3612,7 +3676,7 @@ var init_paywallHeartbeat = __esm({
3612
3676
  });
3613
3677
 
3614
3678
  // src/utils/localization.ts
3615
- import { NativeModules as NativeModules4, Platform as Platform9 } from "react-native";
3679
+ import { NativeModules as NativeModules5, Platform as Platform9 } from "react-native";
3616
3680
  function setCurrentLanguage(language) {
3617
3681
  currentLanguage = language;
3618
3682
  }
@@ -3633,10 +3697,10 @@ function getLocalizedString(text) {
3633
3697
  function detectDeviceLanguage() {
3634
3698
  try {
3635
3699
  if (Platform9.OS === "ios") {
3636
- const settings = NativeModules4.SettingsManager?.settings;
3700
+ const settings = NativeModules5.SettingsManager?.settings;
3637
3701
  return settings?.AppleLocale ?? settings?.AppleLanguages?.[0] ?? "en-US";
3638
3702
  }
3639
- return NativeModules4.I18nManager?.localeIdentifier ?? "en-US";
3703
+ return NativeModules5.I18nManager?.localeIdentifier ?? "en-US";
3640
3704
  } catch {
3641
3705
  return "en-US";
3642
3706
  }
@@ -3807,7 +3871,7 @@ import {
3807
3871
  findNodeHandle,
3808
3872
  Linking as Linking2,
3809
3873
  NativeEventEmitter as NativeEventEmitter2,
3810
- NativeModules as NativeModules5,
3874
+ NativeModules as NativeModules6,
3811
3875
  Platform as Platform10,
3812
3876
  requireNativeComponent,
3813
3877
  StyleSheet as StyleSheet2,
@@ -3828,7 +3892,7 @@ function getNativeWebView() {
3828
3892
  }
3829
3893
  function getWebViewEmitter() {
3830
3894
  if (webViewEmitter) return webViewEmitter;
3831
- const mod = NativeModules5.PaywalloWebViewModule;
3895
+ const mod = NativeModules6.PaywalloWebViewModule;
3832
3896
  if (!mod) return null;
3833
3897
  webViewEmitter = new NativeEventEmitter2(mod);
3834
3898
  return webViewEmitter;
@@ -4162,7 +4226,7 @@ __export(NativeStoreKit_exports, {
4162
4226
  NativeStoreKit: () => NativeStoreKit,
4163
4227
  nativeStoreKit: () => nativeStoreKit
4164
4228
  });
4165
- import { NativeEventEmitter as NativeEventEmitter3, NativeModules as NativeModules6, Platform as Platform11 } from "react-native";
4229
+ import { NativeEventEmitter as NativeEventEmitter3, NativeModules as NativeModules7, Platform as Platform11 } from "react-native";
4166
4230
  function isAvailable3() {
4167
4231
  return PaywalloStoreKitNative != null;
4168
4232
  }
@@ -4264,7 +4328,7 @@ var init_NativeStoreKit = __esm({
4264
4328
  init_IdentityManager();
4265
4329
  init_PurchaseError();
4266
4330
  TRANSACTION_UPDATE_EVENT = "PaywalloTransactionUpdate";
4267
- PaywalloStoreKitNative = NativeModules6.PaywalloStoreKit ?? null;
4331
+ PaywalloStoreKitNative = NativeModules7.PaywalloStoreKit ?? null;
4268
4332
  nativeStoreKit = {
4269
4333
  isAvailable: isAvailable3,
4270
4334
  async getProducts(productIds) {
@@ -5221,7 +5285,7 @@ var init_QueueProcessor = __esm({
5221
5285
  for (const item of batch) {
5222
5286
  retryIds.add(item.id);
5223
5287
  }
5224
- console.error("[Paywallo:QUEUE] event batch network error \u2014 items will retry", { size: batch.length, error: error instanceof Error ? error.message : String(error) });
5288
+ if (this.config.debug) console.log("[Paywallo:QUEUE] event batch network error \u2014 items will retry", { size: batch.length, error: error instanceof Error ? error.message : String(error) });
5225
5289
  this.log("Event batch threw network error - will retry", {
5226
5290
  size: batch.length,
5227
5291
  error: error instanceof Error ? error.message : String(error)
@@ -5234,7 +5298,7 @@ var init_QueueProcessor = __esm({
5234
5298
  processed++;
5235
5299
  }
5236
5300
  this.log("Event batch processed successfully", { size: batch.length });
5237
- } else if (response.status >= 400 && response.status < 500) {
5301
+ } else if (response.status >= 400 && response.status < 500 && response.status !== 429) {
5238
5302
  for (const item of batch) {
5239
5303
  await offlineQueue.removeItem(item.id);
5240
5304
  failed++;
@@ -5246,7 +5310,7 @@ var init_QueueProcessor = __esm({
5246
5310
  console.log("[Paywallo:QUEUE] event batch deduplicated by server \u2014 items dropped (benign)", { status: response.status, size: batch.length, error: responseError });
5247
5311
  }
5248
5312
  } else {
5249
- console.error("[Paywallo:QUEUE] event batch failed PERMANENTLY (4xx) \u2014 items dropped", { status: response.status, size: batch.length, responseBody: typeof response.data === "string" ? response.data.slice(0, 1e3) : JSON.stringify(response.data).slice(0, 1e3), firstItemBody: typeof batch[0].body === "string" ? batch[0].body.slice(0, 1e3) : JSON.stringify(batch[0].body).slice(0, 1e3) });
5313
+ if (this.config.debug) console.log("[Paywallo:QUEUE] event batch failed PERMANENTLY (4xx) \u2014 items dropped", { status: response.status, size: batch.length, responseBody: typeof response.data === "string" ? response.data.slice(0, 1e3) : JSON.stringify(response.data).slice(0, 1e3), firstItemBody: typeof batch[0].body === "string" ? batch[0].body.slice(0, 1e3) : JSON.stringify(batch[0].body).slice(0, 1e3) });
5250
5314
  }
5251
5315
  this.log("Event batch failed permanently (4xx) - removing from queue", {
5252
5316
  status: response.status,
@@ -5257,7 +5321,7 @@ var init_QueueProcessor = __esm({
5257
5321
  for (const item of batch) {
5258
5322
  retryIds.add(item.id);
5259
5323
  }
5260
- console.error("[Paywallo:QUEUE] event batch server error (5xx) \u2014 will retry", { status: response.status, size: batch.length });
5324
+ if (this.config.debug) console.log("[Paywallo:QUEUE] event batch server error (5xx) \u2014 will retry", { status: response.status, size: batch.length });
5261
5325
  this.log("Event batch failed with server error - will retry", {
5262
5326
  status: response.status,
5263
5327
  size: batch.length
@@ -5287,7 +5351,7 @@ var init_QueueProcessor = __esm({
5287
5351
  skipRetry: false
5288
5352
  });
5289
5353
  } catch (error) {
5290
- console.error("[Paywallo:QUEUE] individual item network error \u2014 will retry", { id: item.id, url: item.url, error: error instanceof Error ? error.message : String(error) });
5354
+ if (this.config.debug) console.log("[Paywallo:QUEUE] individual item network error \u2014 will retry", { id: item.id, url: item.url, error: error instanceof Error ? error.message : String(error) });
5291
5355
  this.log("Request threw network error - will retry", {
5292
5356
  id: item.id,
5293
5357
  error: error instanceof Error ? error.message : String(error)
@@ -5298,7 +5362,7 @@ var init_QueueProcessor = __esm({
5298
5362
  this.log("Queue item processed successfully", { id: item.id });
5299
5363
  return { success: true, permanent: false };
5300
5364
  }
5301
- if (response.status >= 400 && response.status < 500) {
5365
+ if (response.status >= 400 && response.status < 500 && response.status !== 429) {
5302
5366
  this.log("Request failed with permanent client error - removing from queue", {
5303
5367
  id: item.id,
5304
5368
  url: item.url,
@@ -6821,8 +6885,9 @@ var init_schemas = __esm({
6821
6885
  path: ["transaction_id"]
6822
6886
  });
6823
6887
  onboardingEventSchema = z.object({
6824
- type: z.enum(["step", "complete", "drop"]),
6825
- step_index: z.number().int().nonnegative().optional(),
6888
+ type: z.enum(["step", "complete"]),
6889
+ family: z.literal("onboarding").optional(),
6890
+ order: z.number().nonnegative().optional(),
6826
6891
  step_name: z.string().optional(),
6827
6892
  variant_key: z.string().optional(),
6828
6893
  time_on_prev_s: z.number().nonnegative().optional()
@@ -6919,7 +6984,7 @@ function isValidEventName(name) {
6919
6984
  async function trackEvent(apiClient, state, eventName, options) {
6920
6985
  const distinctId = identityManager.getDistinctId();
6921
6986
  if (!distinctId) {
6922
- console.error("[Paywallo:DROP] event dropped \u2014 no distinctId available yet", { eventName });
6987
+ if (state.config?.debug) console.log("[Paywallo:DROP] event dropped \u2014 no distinctId available yet", { eventName });
6923
6988
  return;
6924
6989
  }
6925
6990
  if (!isValidEventName(eventName)) {
@@ -7335,19 +7400,19 @@ var init_SubscriptionManager = __esm({
7335
7400
  });
7336
7401
 
7337
7402
  // src/utils/detectEnvironment.ts
7338
- import { NativeModules as NativeModules7 } from "react-native";
7403
+ import { NativeModules as NativeModules8 } from "react-native";
7339
7404
  async function detectEnvironment() {
7340
7405
  if (cached !== null) return cached;
7341
7406
  if (typeof globalThis.__DEV__ !== "undefined" && globalThis.__DEV__) {
7342
7407
  cached = "sandbox";
7343
7408
  return cached;
7344
7409
  }
7345
- if (!nativeModule) {
7410
+ if (!nativeModule2) {
7346
7411
  cached = "production";
7347
7412
  return cached;
7348
7413
  }
7349
7414
  try {
7350
- const result = await nativeModule.detect();
7415
+ const result = await nativeModule2.detect();
7351
7416
  cached = result === "sandbox" ? "sandbox" : "production";
7352
7417
  return cached;
7353
7418
  } catch {
@@ -7355,11 +7420,11 @@ async function detectEnvironment() {
7355
7420
  return cached;
7356
7421
  }
7357
7422
  }
7358
- var nativeModule, cached;
7423
+ var nativeModule2, cached;
7359
7424
  var init_detectEnvironment = __esm({
7360
7425
  "src/utils/detectEnvironment.ts"() {
7361
7426
  "use strict";
7362
- nativeModule = NativeModules7.PaywalloEnv ?? null;
7427
+ nativeModule2 = NativeModules8.PaywalloEnv ?? null;
7363
7428
  cached = null;
7364
7429
  }
7365
7430
  });
@@ -7427,6 +7492,31 @@ var init_apiPurchaseMethods = __esm({
7427
7492
  }
7428
7493
  });
7429
7494
 
7495
+ // src/utils/userAgent.ts
7496
+ import { Platform as Platform14 } from "react-native";
7497
+ function buildUserAgent(sdkVersion) {
7498
+ const clean = (v) => v.replace(/[^\x20-\x7E]/g, "").replace(/[();]/g, "").trim();
7499
+ const info = typeof nativeDeviceInfo.getCachedDeviceInfo === "function" ? nativeDeviceInfo.getCachedDeviceInfo() : null;
7500
+ if (!info) {
7501
+ return `PaywalloSDK/${sdkVersion} (${clean(Platform14.OS)})`;
7502
+ }
7503
+ const isValid = (v) => {
7504
+ if (typeof v !== "string") return false;
7505
+ const c = clean(v);
7506
+ return c.length > 0 && c !== "unknown";
7507
+ };
7508
+ const parts = [clean(info.osName)];
7509
+ if (isValid(info.systemVersion)) parts.push(clean(info.systemVersion));
7510
+ const detail = isValid(info.model) ? `; ${clean(info.model)}` : "";
7511
+ return `PaywalloSDK/${sdkVersion} (${parts.join(" ")}${detail})`;
7512
+ }
7513
+ var init_userAgent = __esm({
7514
+ "src/utils/userAgent.ts"() {
7515
+ "use strict";
7516
+ init_NativeDeviceInfo();
7517
+ }
7518
+ });
7519
+
7430
7520
  // src/core/ApiCache.ts
7431
7521
  var ApiCache;
7432
7522
  var init_ApiCache = __esm({
@@ -7654,6 +7744,11 @@ function isServerError(res) {
7654
7744
  const r = res;
7655
7745
  return r.ok === false && typeof r.status === "number" && r.status >= 500;
7656
7746
  }
7747
+ function isRateLimitError(res) {
7748
+ if (typeof res !== "object" || res === null) return false;
7749
+ const r = res;
7750
+ return r.ok === false && r.status === 429;
7751
+ }
7657
7752
  async function postWithQueue(deps, url, payload, label, priority) {
7658
7753
  const enqueue = async () => {
7659
7754
  await offlineQueue.enqueue(
@@ -7665,6 +7760,11 @@ async function postWithQueue(deps, url, payload, label, priority) {
7665
7760
  priority
7666
7761
  );
7667
7762
  };
7763
+ if (priority === "critical" && deps.isOfflineQueueEnabled()) {
7764
+ await enqueue();
7765
+ if (deps.isDebug()) console.log("[Paywallo:ApiClient] Critical event queued (write-ahead):", label);
7766
+ return;
7767
+ }
7668
7768
  if (deps.isOfflineQueueEnabled() && !networkMonitor.isOnline()) {
7669
7769
  await enqueue();
7670
7770
  if (deps.isDebug()) console.log("[Paywallo:ApiClient] Queued for offline:", label, priority ?? "normal");
@@ -7675,6 +7775,9 @@ async function postWithQueue(deps, url, payload, label, priority) {
7675
7775
  if (isServerError(res) && deps.isOfflineQueueEnabled()) {
7676
7776
  if (deps.isDebug()) console.log("[Paywallo:ApiClient] Server error, queued for retry:", label, priority ?? "normal");
7677
7777
  await enqueue();
7778
+ } else if (isRateLimitError(res) && deps.isOfflineQueueEnabled()) {
7779
+ if (deps.isDebug()) console.log("[Paywallo:ApiClient] Rate limited (429), queued for retry:", label, priority ?? "normal");
7780
+ await enqueue();
7678
7781
  }
7679
7782
  } catch (error) {
7680
7783
  if (deps.isDebug()) console.log("[Paywallo:ApiClient] Request failed:", label, error);
@@ -7694,7 +7797,7 @@ var init_ApiClientQueue = __esm({
7694
7797
  });
7695
7798
 
7696
7799
  // src/core/eventEnvelopeV2.ts
7697
- import { Platform as Platform14 } from "react-native";
7800
+ import { Platform as Platform15 } from "react-native";
7698
7801
  import { z as z2 } from "zod";
7699
7802
  function buildV2Envelope(events, providerContext) {
7700
7803
  const context = { ...providerContext };
@@ -7721,7 +7824,7 @@ function buildV2Envelope(events, providerContext) {
7721
7824
  return { id, ts: event.timestamp, name: family, payload };
7722
7825
  });
7723
7826
  if (context.sdk_version === void 0) context.sdk_version = SDK_VERSION;
7724
- if (context.platform === void 0) context.platform = Platform14.OS;
7827
+ if (context.platform === void 0) context.platform = Platform15.OS;
7725
7828
  if (context.distinct_id === void 0 && events.length > 0) {
7726
7829
  context.distinct_id = events[0].distinctId;
7727
7830
  }
@@ -7859,7 +7962,7 @@ var init_events = __esm({
7859
7962
  });
7860
7963
 
7861
7964
  // src/core/EventBatcher.ts
7862
- import { Platform as Platform15 } from "react-native";
7965
+ import { Platform as Platform16 } from "react-native";
7863
7966
  var BATCH_MAX_SIZE, BATCH_FLUSH_MS, EVENT_NAME_REGEX, V2_BATCH_ENDPOINT, EventBatcher;
7864
7967
  var init_EventBatcher = __esm({
7865
7968
  "src/core/EventBatcher.ts"() {
@@ -7872,10 +7975,8 @@ var init_EventBatcher = __esm({
7872
7975
  V2_BATCH_ENDPOINT = "/sdk/ingest/batch";
7873
7976
  EventBatcher = class {
7874
7977
  constructor(post, debug = false, contextProvider = null) {
7875
- this.criticalQueue = [];
7876
7978
  this.normalQueue = [];
7877
7979
  this.normalTimer = null;
7878
- this.criticalScheduled = false;
7879
7980
  this.post = post;
7880
7981
  this.debug = debug;
7881
7982
  this.contextProvider = contextProvider;
@@ -7912,14 +8013,17 @@ var init_EventBatcher = __esm({
7912
8013
  const event = {
7913
8014
  eventName,
7914
8015
  distinctId,
7915
- properties: { ...properties, platform: Platform15.OS },
8016
+ properties: { ...properties, platform: Platform16.OS },
7916
8017
  timestamp: timestamp ?? Date.now(),
7917
8018
  environment: environment.toLowerCase()
7918
8019
  };
7919
8020
  const priority = options?.priority ?? "normal";
7920
8021
  if (priority === "critical") {
7921
- this.criticalQueue.push(event);
7922
- this.scheduleCriticalFlush();
8022
+ try {
8023
+ await this.postBatch([event], `event_critical:${event.eventName}`, "critical");
8024
+ } catch (err) {
8025
+ if (this.debug) console.log("[Paywallo:CRITICAL] critical event post failed", { eventName: event.eventName, error: err instanceof Error ? err.message : String(err) });
8026
+ }
7923
8027
  return;
7924
8028
  }
7925
8029
  this.normalQueue.push(event);
@@ -7940,9 +8044,10 @@ var init_EventBatcher = __esm({
7940
8044
  }
7941
8045
  /**
7942
8046
  * Force flush of the normal queue. Critical events are never held in the
7943
- * normal queue, so this does not affect them (they flush on their own
7944
- * microtask). Exposed so callers (e.g. `AppState=background` listener in
7945
- * phase 2.4) can drain pending events before the app is suspended.
8047
+ * normal queue, so this does not affect them they are posted (and
8048
+ * awaited) synchronously inside `track()`. Exposed so callers (e.g.
8049
+ * `AppState=background` listener in phase 2.4) can drain pending events
8050
+ * before the app is suspended.
7946
8051
  */
7947
8052
  async flush() {
7948
8053
  await this.flushNormal();
@@ -7952,28 +8057,7 @@ var init_EventBatcher = __esm({
7952
8057
  clearTimeout(this.normalTimer);
7953
8058
  this.normalTimer = null;
7954
8059
  }
7955
- this.criticalQueue = [];
7956
8060
  this.normalQueue = [];
7957
- this.criticalScheduled = false;
7958
- }
7959
- scheduleCriticalFlush() {
7960
- if (this.criticalScheduled) return;
7961
- this.criticalScheduled = true;
7962
- queueMicrotask(() => {
7963
- this.criticalScheduled = false;
7964
- void this.flushCritical();
7965
- });
7966
- }
7967
- async flushCritical() {
7968
- if (this.criticalQueue.length === 0) return;
7969
- const drained = this.criticalQueue.splice(0, this.criticalQueue.length);
7970
- for (const event of drained) {
7971
- try {
7972
- await this.postBatch([event], `event_critical:${event.eventName}`, "critical");
7973
- } catch (err) {
7974
- console.error("[Paywallo:CRITICAL] critical event post failed", { eventName: event.eventName, error: err instanceof Error ? err.message : String(err) });
7975
- }
7976
- }
7977
8061
  }
7978
8062
  async flushNormal() {
7979
8063
  if (this.normalQueue.length === 0) return;
@@ -7985,7 +8069,7 @@ var init_EventBatcher = __esm({
7985
8069
  try {
7986
8070
  await this.postBatch(batch, `event_batch:${batch.length}`);
7987
8071
  } catch (err) {
7988
- console.error("[Paywallo:NORMAL] batch post failed \u2014 relying on OfflineQueue", { batchSize: batch.length, error: err instanceof Error ? err.message : String(err) });
8072
+ if (this.debug) console.log("[Paywallo:NORMAL] batch post failed \u2014 relying on OfflineQueue", { batchSize: batch.length, error: err instanceof Error ? err.message : String(err) });
7989
8073
  }
7990
8074
  }
7991
8075
  async postBatch(events, label, priority = "normal") {
@@ -8000,7 +8084,7 @@ var init_EventBatcher = __esm({
8000
8084
  const parsed = v2EnvelopeSchema.safeParse(envelope);
8001
8085
  if (!parsed.success) {
8002
8086
  if (this.debug) console.log("[Paywallo EVENTS] V2 envelope schema validation failed:", parsed.error.issues);
8003
- console.error("[Paywallo:CRITICAL] V2 envelope schema validation failed", { issues: parsed.error.issues, eventCount: events.length, firstEventName: events[0]?.eventName, context: providerContext });
8087
+ if (this.debug) console.log("[Paywallo:CRITICAL] V2 envelope schema validation failed", { issues: parsed.error.issues, eventCount: events.length, firstEventName: events[0]?.eventName, context: providerContext });
8004
8088
  throw parsed.error;
8005
8089
  }
8006
8090
  await this.post(V2_BATCH_ENDPOINT, parsed.data, label, priority);
@@ -8238,7 +8322,7 @@ var init_http = __esm({
8238
8322
  });
8239
8323
 
8240
8324
  // src/core/ApiClient.ts
8241
- import { Platform as Platform16 } from "react-native";
8325
+ import { Platform as Platform17 } from "react-native";
8242
8326
  function normalizeDateOfBirth(raw) {
8243
8327
  if (!raw) return void 0;
8244
8328
  if (DATE_OF_BIRTH_RE.test(raw)) return raw;
@@ -8259,6 +8343,7 @@ function buildPiiPayload(pii) {
8259
8343
  if (dob) result.dateOfBirth = dob;
8260
8344
  const g = normalizeGender(pii.gender);
8261
8345
  if (g) result.gender = g;
8346
+ if (pii.zipCode) result.zipCode = pii.zipCode.trim();
8262
8347
  return result;
8263
8348
  }
8264
8349
  function normalizeGender(raw) {
@@ -8273,6 +8358,7 @@ var init_ApiClient = __esm({
8273
8358
  "src/core/ApiClient.ts"() {
8274
8359
  "use strict";
8275
8360
  init_apiPurchaseMethods();
8361
+ init_userAgent();
8276
8362
  init_ApiCache();
8277
8363
  init_ApiClientFlags();
8278
8364
  init_ApiClientQueue();
@@ -8372,10 +8458,13 @@ var init_ApiClient = __esm({
8372
8458
  * should carry. Keep this tiny — it runs once per ApiClient instance.
8373
8459
  */
8374
8460
  buildSdkHeaders() {
8461
+ const ua = buildUserAgent(SDK_VERSION);
8375
8462
  return {
8376
8463
  "x-sdk-version": SDK_VERSION,
8377
- "x-sdk-platform": Platform16.OS,
8378
- "x-sdk-environment": this.environment
8464
+ "x-sdk-platform": Platform17.OS,
8465
+ "x-sdk-environment": this.environment,
8466
+ "User-Agent": ua,
8467
+ "x-sdk-user-agent": ua
8379
8468
  };
8380
8469
  }
8381
8470
  async clearQueue() {
@@ -8404,7 +8493,7 @@ var init_ApiClient = __esm({
8404
8493
  message,
8405
8494
  context,
8406
8495
  sdkVersion: SDK_VERSION,
8407
- platform: Platform16.OS
8496
+ platform: Platform17.OS
8408
8497
  }, true);
8409
8498
  } catch (err) {
8410
8499
  if (this.debug) console.log("[Paywallo:ApiClient] reportError failed", err);
@@ -8422,7 +8511,7 @@ var init_ApiClient = __esm({
8422
8511
  async identify(distinctId, properties, email, deviceId, pii) {
8423
8512
  const rawTraits = {
8424
8513
  email: email || void 0,
8425
- platform: Platform16.OS,
8514
+ platform: Platform17.OS,
8426
8515
  name: properties?.name,
8427
8516
  country: properties?.country,
8428
8517
  locale: properties?.locale,
@@ -8618,7 +8707,6 @@ function setupNotifications(apiClient, config, environment, eventPipeline) {
8618
8707
  notificationsManager.setupHandlers({ eventBatcher: eventPipeline });
8619
8708
  notificationsManager.initialize({
8620
8709
  appKey: config.appKey,
8621
- apiBaseUrl: DEFAULT_API_URL,
8622
8710
  debug: config.debug,
8623
8711
  environment: environment === "Sandbox" ? "sandbox" : "production"
8624
8712
  }).catch((err) => {
@@ -8660,7 +8748,6 @@ var init_PaywalloInitHelpers = __esm({
8660
8748
  init_identity();
8661
8749
  init_notifications();
8662
8750
  init_NativePushBridge();
8663
- init_constants();
8664
8751
  init_SecureStorage();
8665
8752
  }
8666
8753
  });
@@ -8751,7 +8838,7 @@ function reportInitFailure(state, config, error, reason) {
8751
8838
  error: error instanceof Error ? error.message : String(error)
8752
8839
  });
8753
8840
  } catch (err) {
8754
- console.error("[Paywallo] reportInitFailure error", err);
8841
+ if (config.debug) console.log("[Paywallo] reportInitFailure error", err);
8755
8842
  }
8756
8843
  }
8757
8844
  function reportInitSuccess(state, config, attempts) {
@@ -8760,7 +8847,7 @@ function reportInitSuccess(state, config, attempts) {
8760
8847
  void state.apiClient.reportError("INIT_RECOVERED", `Succeeded after ${attempts} retries`, { attempts });
8761
8848
  }
8762
8849
  } catch (err) {
8763
- console.error("[Paywallo] reportInitSuccess error", err);
8850
+ if (config.debug) console.log("[Paywallo] reportInitSuccess error", err);
8764
8851
  }
8765
8852
  }
8766
8853
  async function resolveSessionFlags(state, keys, apiClient, distinctId) {
@@ -8836,9 +8923,14 @@ async function doInit(state, config) {
8836
8923
  } catch (err) {
8837
8924
  if (config.debug) console.log("[Paywallo ATTR] start error", err);
8838
8925
  }
8839
- void nativeDeviceInfo.getDeviceInfo().catch(() => null);
8840
- void advertisingIdManager.collect(false).catch(() => null);
8841
- void metaBridge.getAnonymousID().catch(() => null);
8926
+ await Promise.race([
8927
+ Promise.all([
8928
+ nativeDeviceInfo.getDeviceInfo().catch(() => null),
8929
+ advertisingIdManager.collect(false).catch(() => null),
8930
+ metaBridge.getAnonymousID().catch(() => null)
8931
+ ]),
8932
+ new Promise((resolve) => setTimeout(resolve, 350))
8933
+ ]);
8842
8934
  apiClient.setEventContextProvider(() => {
8843
8935
  const attr = attributionTracker.get();
8844
8936
  const attrEntries = attr ? [
@@ -8914,12 +9006,15 @@ async function doInit(state, config) {
8914
9006
  setupNotifications(apiClient, config, environment, eventPipeline);
8915
9007
  if (config.autoStartSession !== false) {
8916
9008
  sessionManager.startSession().catch((err) => {
8917
- console.error("[Paywallo INIT] autoStart session error", err);
9009
+ if (config.debug) console.log("[Paywallo INIT] autoStart session error", err);
8918
9010
  });
8919
9011
  }
8920
9012
  new InstallTracker(config.debug, {
8921
9013
  distinctIdProvider: () => identityManager.getDistinctId()
8922
- }).trackIfNeeded(apiClient, config.requestATT === true).catch(() => {
9014
+ }).trackIfNeeded(apiClient, config.requestATT === true).then(() => {
9015
+ if (config.debug) console.log("[Paywallo INSTALL] $app_installed sent");
9016
+ }).catch(() => {
9017
+ if (config.debug) console.log("[Paywallo INSTALL] $app_installed failed \u2014 will retry on next launch");
8923
9018
  });
8924
9019
  if (config.sessionFlags && config.sessionFlags.length > 0) {
8925
9020
  await resolveSessionFlags(state, config.sessionFlags, apiClient, distinctId);
@@ -9154,6 +9249,7 @@ var init_PaywalloClient = __esm({
9154
9249
  }
9155
9250
  if (this.state.config?.debug) console.log("[Paywallo IDENTIFY]", options ? { hasEmail: "email" in options && !!options.email, hasProperties: "properties" in options } : void 0);
9156
9251
  await identityManager.identify(options);
9252
+ if (this.state.config?.debug) console.log("[Paywallo IDENTIFY] done");
9157
9253
  }
9158
9254
  async track(eventName, options) {
9159
9255
  return trackEvent(this.getApiClientOrThrow(), this.state, eventName, options);
@@ -9387,9 +9483,9 @@ __export(OfferingService_exports, {
9387
9483
  OfferingService: () => OfferingService,
9388
9484
  offeringService: () => offeringService
9389
9485
  });
9390
- import { Platform as Platform17 } from "react-native";
9486
+ import { Platform as Platform18 } from "react-native";
9391
9487
  function resolveProductId(raw) {
9392
- const isIos = Platform17.OS === "ios";
9488
+ const isIos = Platform18.OS === "ios";
9393
9489
  const productId = isIos ? raw.apple_product_id : raw.google_product_id;
9394
9490
  return productId ?? "";
9395
9491
  }
@@ -9408,7 +9504,7 @@ function mapProduct2(raw) {
9408
9504
  };
9409
9505
  }
9410
9506
  function pickPlatformProduct(products) {
9411
- const isIos = Platform17.OS === "ios";
9507
+ const isIos = Platform18.OS === "ios";
9412
9508
  const match = products.find((p) => isIos ? p.apple_product_id : p.google_product_id);
9413
9509
  return match ?? products[0] ?? null;
9414
9510
  }
@@ -9635,18 +9731,14 @@ init_onboarding();
9635
9731
  import { useCallback as useCallback9 } from "react";
9636
9732
  function useOnboarding() {
9637
9733
  const step = useCallback9(
9638
- (stepName, order) => onboardingManager.step(stepName, order),
9734
+ (stepName, order, options) => onboardingManager.step(stepName, order, options),
9639
9735
  []
9640
9736
  );
9641
9737
  const complete = useCallback9(
9642
- () => onboardingManager.complete(),
9643
- []
9644
- );
9645
- const drop = useCallback9(
9646
- (stepName) => onboardingManager.drop(stepName),
9738
+ (options) => onboardingManager.complete(options),
9647
9739
  []
9648
9740
  );
9649
- return { step, complete, drop };
9741
+ return { step, complete };
9650
9742
  }
9651
9743
 
9652
9744
  // src/hooks/usePaywallo.ts
@@ -10960,6 +11052,7 @@ function PaywalloProvider({ children, config }) {
10960
11052
  void pushAttributionToSuperwall(configRef.current.debug);
10961
11053
  setDistinctId(PaywalloClient.getDistinctId());
10962
11054
  setIsInitialized(true);
11055
+ if (configRef.current.debug) console.log("[Paywallo] SDK ready", PaywalloClient.getDistinctId()?.slice(0, 8));
10963
11056
  if (!autoPreloadTriggeredRef.current) {
10964
11057
  autoPreloadTriggeredRef.current = true;
10965
11058
  const placement = await PaywalloClient.waitForAutoPreloadedPlacement();