@virex-tech/paywallo-sdk 2.2.3 → 2.2.4

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
@@ -1052,7 +1052,7 @@ var init_sessionTracking = __esm({
1052
1052
 
1053
1053
  // src/domains/session/SessionManager.ts
1054
1054
  import { AppState } from "react-native";
1055
- var SESSION_ID_KEY, SESSION_START_KEY, EMERGENCY_PAYWALL_SHOWN_KEY, LEGACY_SESSION_ID_KEY, LEGACY_SESSION_START_KEY, SESSION_TIMEOUT_MS, BACKGROUND_FLUSH_TIMEOUT_MS, SessionManager, sessionManager;
1055
+ var SESSION_ID_KEY, SESSION_START_KEY, LEGACY_SESSION_ID_KEY, LEGACY_SESSION_START_KEY, SESSION_TIMEOUT_MS, SessionManager, sessionManager;
1056
1056
  var init_SessionManager = __esm({
1057
1057
  "src/domains/session/SessionManager.ts"() {
1058
1058
  "use strict";
@@ -1063,11 +1063,9 @@ var init_SessionManager = __esm({
1063
1063
  init_sessionTracking();
1064
1064
  SESSION_ID_KEY = "@paywallo:current_session_id";
1065
1065
  SESSION_START_KEY = "@paywallo:session_start";
1066
- EMERGENCY_PAYWALL_SHOWN_KEY = "@paywallo:emergency_paywall_shown";
1067
1066
  LEGACY_SESSION_ID_KEY = "current_session_id";
1068
1067
  LEGACY_SESSION_START_KEY = "session_start";
1069
1068
  SESSION_TIMEOUT_MS = 30 * 60 * 1e3;
1070
- BACKGROUND_FLUSH_TIMEOUT_MS = 2e3;
1071
1069
  SessionManager = class {
1072
1070
  constructor() {
1073
1071
  this.sessionId = null;
@@ -1129,8 +1127,7 @@ var init_SessionManager = __esm({
1129
1127
  this.emergencyPaywallShownInSession = false;
1130
1128
  await Promise.all([
1131
1129
  this.secureStorage.set(SESSION_ID_KEY, this.sessionId),
1132
- this.secureStorage.set(SESSION_START_KEY, this.sessionStartedAt.toISOString()),
1133
- this.secureStorage.remove(EMERGENCY_PAYWALL_SHOWN_KEY)
1130
+ this.secureStorage.set(SESSION_START_KEY, this.sessionStartedAt.toISOString())
1134
1131
  ]);
1135
1132
  await this.trackSessionStart();
1136
1133
  return this.sessionId;
@@ -1140,10 +1137,10 @@ var init_SessionManager = __esm({
1140
1137
  if (!this.sessionId || !this.sessionStartedAt) {
1141
1138
  return;
1142
1139
  }
1140
+ await this.trackSessionEnd();
1143
1141
  if (this.apiClient) {
1144
1142
  await this.apiClient.flushEventBatch();
1145
1143
  }
1146
- await this.trackSessionEnd();
1147
1144
  this.sessionId = null;
1148
1145
  this.sessionStartedAt = null;
1149
1146
  await Promise.all([
@@ -1228,7 +1225,6 @@ var init_SessionManager = __esm({
1228
1225
  async handleBackground() {
1229
1226
  this.backgroundTimestamp = Date.now();
1230
1227
  await this.trackAppBackground();
1231
- await this.flushEventsWithTimeout(BACKGROUND_FLUSH_TIMEOUT_MS);
1232
1228
  await this.endSession();
1233
1229
  }
1234
1230
  async flushEventsWithTimeout(timeoutMs) {
@@ -1247,7 +1243,6 @@ var init_SessionManager = __esm({
1247
1243
  const response = await this.apiClient.getEmergencyPaywall().catch(() => null);
1248
1244
  if (response?.enabled && response.paywallId) {
1249
1245
  this.emergencyPaywallShownInSession = true;
1250
- await this.secureStorage.set(EMERGENCY_PAYWALL_SHOWN_KEY, "true");
1251
1246
  await this.emergencyPaywallHandler(response.paywallId);
1252
1247
  }
1253
1248
  }
@@ -1547,7 +1542,7 @@ var init_MetaBridge = __esm({
1547
1542
  try {
1548
1543
  await NativeBridge.logEvent(name, params);
1549
1544
  } catch (error) {
1550
- if (this.debug) console.warn("[Paywallo:MetaBridge] logEvent failed", { name, error });
1545
+ console.error("[Paywallo:MetaBridge] logEvent failed", { name, error });
1551
1546
  }
1552
1547
  }
1553
1548
  async logPurchase(amount, currency, params = {}) {
@@ -1555,7 +1550,7 @@ var init_MetaBridge = __esm({
1555
1550
  try {
1556
1551
  await NativeBridge.logPurchase(amount, currency, params);
1557
1552
  } catch (error) {
1558
- if (this.debug) console.warn("[Paywallo:MetaBridge] logPurchase failed", { amount, currency, error });
1553
+ console.error("[Paywallo:MetaBridge] logPurchase failed", { amount, currency, error });
1559
1554
  }
1560
1555
  }
1561
1556
  setUserID(userId) {
@@ -1563,7 +1558,7 @@ var init_MetaBridge = __esm({
1563
1558
  try {
1564
1559
  NativeBridge.setUserID(userId);
1565
1560
  } catch (error) {
1566
- if (this.debug) console.warn("[Paywallo:MetaBridge] setUserID failed", { error });
1561
+ console.error("[Paywallo:MetaBridge] setUserID failed", { error });
1567
1562
  }
1568
1563
  }
1569
1564
  flush() {
@@ -1571,7 +1566,7 @@ var init_MetaBridge = __esm({
1571
1566
  try {
1572
1567
  NativeBridge.flush();
1573
1568
  } catch (error) {
1574
- if (this.debug) console.warn("[Paywallo:MetaBridge] flush failed", { error });
1569
+ console.error("[Paywallo:MetaBridge] flush failed", { error });
1575
1570
  }
1576
1571
  }
1577
1572
  };
@@ -1787,7 +1782,7 @@ var init_InstallTracker = __esm({
1787
1782
  screenWidth: deviceData.screenWidth,
1788
1783
  screenHeight: deviceData.screenHeight,
1789
1784
  timezone: deviceData.timezone,
1790
- locale: deviceData.locale,
1785
+ language: deviceData.locale,
1791
1786
  fbAnonId: anonId,
1792
1787
  installTimestamp: new Date(installedAt).toISOString()
1793
1788
  }),
@@ -2161,7 +2156,7 @@ function setupForegroundHandler(deps, onReceive) {
2161
2156
  title: data["title"] ?? message.notification?.title ?? "",
2162
2157
  body: data["body"] ?? message.notification?.body ?? ""
2163
2158
  });
2164
- if (debug) console.warn("[Paywallo PUSH] foreground message received:", payload.notificationId);
2159
+ if (debug) console.log("[Paywallo PUSH] foreground message received:", payload.notificationId);
2165
2160
  onReceive(payload);
2166
2161
  const messageId = message.messageId ?? payload.notificationId;
2167
2162
  await tracker.track("notification_delivered", {
@@ -2192,13 +2187,20 @@ function getTimezone() {
2192
2187
  function detectPlatform() {
2193
2188
  return Platform5.OS === "ios" ? "ios" : "android";
2194
2189
  }
2195
- var SEEN_MESSAGES_KEY, SEEN_TTL_MS, MAX_SEEN_SIZE, NotificationEventTracker;
2190
+ var SEEN_MESSAGES_KEY, LEGACY_SEEN_MESSAGES_KEY, SEEN_TTL_MS, MAX_SEEN_SIZE, NOTIFICATION_TYPE_TO_FAMILY, NotificationEventTracker;
2196
2191
  var init_NotificationEventTracker = __esm({
2197
2192
  "src/domains/notifications/NotificationEventTracker.ts"() {
2198
2193
  "use strict";
2199
- SEEN_MESSAGES_KEY = "seen_messages";
2194
+ SEEN_MESSAGES_KEY = "@paywallo:seen_messages";
2195
+ LEGACY_SEEN_MESSAGES_KEY = "seen_messages";
2200
2196
  SEEN_TTL_MS = 60 * 60 * 1e3;
2201
2197
  MAX_SEEN_SIZE = 1e3;
2198
+ NOTIFICATION_TYPE_TO_FAMILY = {
2199
+ notification_delivered: "delivered",
2200
+ notification_displayed: "displayed",
2201
+ notification_clicked: "clicked",
2202
+ notification_dismissed: "dismissed"
2203
+ };
2202
2204
  NotificationEventTracker = class {
2203
2205
  constructor(batcher, config, secureStorage2) {
2204
2206
  this.batcher = batcher;
@@ -2219,9 +2221,13 @@ var init_NotificationEventTracker = __esm({
2219
2221
  if (this.config.debug) console.log("[Paywallo PUSH] skipped (no distinctId):", eventType);
2220
2222
  return;
2221
2223
  }
2224
+ const canonicalType = NOTIFICATION_TYPE_TO_FAMILY[eventType];
2225
+ if (!canonicalType) {
2226
+ if (this.config.debug) console.log("[Paywallo PUSH] eventType not mapped to family schema, skipping ingest:", eventType);
2227
+ return;
2228
+ }
2222
2229
  const properties = {
2223
- family: "notification",
2224
- type: eventType,
2230
+ type: canonicalType,
2225
2231
  notification_id: notificationId,
2226
2232
  campaign_id: campaignId,
2227
2233
  variant_key: variantKey ?? null,
@@ -2233,7 +2239,7 @@ var init_NotificationEventTracker = __esm({
2233
2239
  if (payload.failureReason) properties.failure_reason = payload.failureReason;
2234
2240
  if (payload.appUserId) properties.app_user_id = payload.appUserId;
2235
2241
  if (payload.properties) Object.assign(properties, payload.properties);
2236
- if (this.config.debug) console.log("[Paywallo PUSH] tracking event:", eventType, notificationId);
2242
+ if (this.config.debug) console.log("[Paywallo PUSH] tracking event:", canonicalType, notificationId);
2237
2243
  await this.batcher.trackEvent("notification", distinctId, properties);
2238
2244
  }
2239
2245
  async isDuplicate(messageId, eventType) {
@@ -2278,7 +2284,10 @@ var init_NotificationEventTracker = __esm({
2278
2284
  return;
2279
2285
  }
2280
2286
  try {
2281
- const raw = await this.secureStorage.get(SEEN_MESSAGES_KEY);
2287
+ let raw = await this.secureStorage.get(SEEN_MESSAGES_KEY);
2288
+ if (!raw) {
2289
+ raw = await this.secureStorage.get(LEGACY_SEEN_MESSAGES_KEY).catch(() => null);
2290
+ }
2282
2291
  if (raw) {
2283
2292
  const parsed = JSON.parse(raw);
2284
2293
  const now = Date.now();
@@ -2352,8 +2361,8 @@ var init_NotificationHandlerSetup = __esm({
2352
2361
  // src/core/version.ts
2353
2362
  function resolveVersion() {
2354
2363
  try {
2355
- if ("2.2.3") {
2356
- return "2.2.3";
2364
+ if ("2.2.4") {
2365
+ return "2.2.4";
2357
2366
  }
2358
2367
  } catch {
2359
2368
  }
@@ -2485,7 +2494,7 @@ async function buildRegistration(token, getDeviceId, getDistinctId) {
2485
2494
  async function registerToken(deps, token) {
2486
2495
  const { httpClient, appKey, getDeviceId, getDistinctId, debug } = deps;
2487
2496
  if (!httpClient || !appKey) {
2488
- if (debug) console.warn("[Paywallo PUSH] httpClient not ready, skipping token registration");
2497
+ if (debug) console.log("[Paywallo PUSH] httpClient not ready, skipping token registration");
2489
2498
  return;
2490
2499
  }
2491
2500
  const registration = await buildRegistration(token, getDeviceId, getDistinctId);
@@ -2495,22 +2504,25 @@ async function registerToken(deps, token) {
2495
2504
  registration,
2496
2505
  { headers: { "X-App-Key": appKey } }
2497
2506
  );
2498
- if (debug) console.warn("[Paywallo PUSH] token registered successfully");
2507
+ if (debug) console.log("[Paywallo PUSH] token registered successfully");
2499
2508
  } catch (err) {
2500
- if (debug) console.warn("[Paywallo PUSH] token registration failed:", err);
2509
+ console.error("[Paywallo PUSH] token registration failed:", err);
2501
2510
  throw new RegistrationFailedError("Token registration failed", err);
2502
2511
  }
2503
2512
  }
2504
- async function unregisterToken(httpClient, appKey, debug) {
2513
+ async function unregisterToken(httpClient, appKey, debug, token, distinctId) {
2505
2514
  if (!httpClient || !appKey) return;
2515
+ if (!token || !distinctId) return;
2506
2516
  try {
2517
+ const body = JSON.stringify({ token, distinct_id: distinctId });
2507
2518
  await httpClient.request(TOKEN_ENDPOINT, {
2508
2519
  method: "DELETE",
2509
- headers: { "X-App-Key": appKey },
2520
+ headers: { "X-App-Key": appKey, "Content-Type": "application/json" },
2521
+ body,
2510
2522
  timeout: 1e4
2511
2523
  });
2512
2524
  } catch (err) {
2513
- if (debug) console.warn("[Paywallo PUSH] optOut error:", err);
2525
+ console.error("[Paywallo PUSH] optOut error:", err);
2514
2526
  }
2515
2527
  }
2516
2528
  var FCM_TOKEN_KEY, TOKEN_ENDPOINT;
@@ -2542,7 +2554,7 @@ async function registerAndPersistToken(deps, token) {
2542
2554
  }
2543
2555
  function subscribeTokenRefresh(bridge, deps, onToken) {
2544
2556
  return bridge.onTokenRefresh((newToken) => {
2545
- if (deps.debug) console.warn("[Paywallo PUSH] token refreshed");
2557
+ if (deps.debug) console.log("[Paywallo PUSH] token refreshed");
2546
2558
  onToken(newToken);
2547
2559
  persistToken(deps.secureStorage, newToken).catch(() => void 0);
2548
2560
  registerToken(
@@ -2558,8 +2570,8 @@ function subscribeTokenRefresh(bridge, deps, onToken) {
2558
2570
  ).catch(() => void 0);
2559
2571
  });
2560
2572
  }
2561
- async function optOutToken(httpClient, secureStorage2, appKey, debug) {
2562
- await unregisterToken(httpClient, appKey, debug);
2573
+ async function optOutToken(httpClient, secureStorage2, appKey, debug, token, distinctId) {
2574
+ await unregisterToken(httpClient, appKey, debug, token, distinctId);
2563
2575
  await clearPersistedToken(secureStorage2);
2564
2576
  }
2565
2577
  async function invalidateLocalTokenStorage(secureStorage2, debug) {
@@ -2579,7 +2591,7 @@ async function captureAndRegisterToken(bridge, deps, onTokenCaptured, subscribeR
2579
2591
  await waitForApnsToken(bridge, deps.debug);
2580
2592
  const token = await bridge.getToken();
2581
2593
  if (!token) {
2582
- if (deps.debug) console.warn("[Paywallo PUSH] token unavailable after grant");
2594
+ if (deps.debug) console.log("[Paywallo PUSH] token unavailable after grant");
2583
2595
  return;
2584
2596
  }
2585
2597
  onTokenCaptured(token);
@@ -2646,18 +2658,18 @@ var init_PermissionManager = __esm({
2646
2658
  }
2647
2659
  async requestPermission(options) {
2648
2660
  const platform = this.platformOverride ?? getPlatform();
2649
- if (this.debug) console.warn("[paywallo-push] platform detected:", platform.OS, "version:", String(platform.Version ?? "n/a"));
2661
+ if (this.debug) console.log("[paywallo-push] platform detected:", platform.OS, "version:", String(platform.Version ?? "n/a"));
2650
2662
  if (platform.OS === "ios") {
2651
- if (this.debug) console.warn("[paywallo-push] requestPermission iOS \u2014 calling bridge...");
2663
+ if (this.debug) console.log("[paywallo-push] requestPermission iOS \u2014 calling bridge...");
2652
2664
  try {
2653
2665
  const raw2 = await this.bridge.requestPermission(options);
2654
- if (this.debug) console.warn("[paywallo-push] bridge returned raw:", raw2);
2666
+ if (this.debug) console.log("[paywallo-push] bridge returned raw:", raw2);
2655
2667
  const status2 = mapPermissionStatus2(raw2);
2656
- if (this.debug) console.warn("[paywallo-push] mapped status:", status2);
2668
+ if (this.debug) console.log("[paywallo-push] mapped status:", status2);
2657
2669
  this.emitOsEvent(status2);
2658
2670
  return status2;
2659
2671
  } catch (err) {
2660
- if (this.debug) console.warn("[paywallo-push] requestPermission iOS CRASHED:", err instanceof Error ? err.message : String(err));
2672
+ console.error("[paywallo-push] requestPermission iOS CRASHED:", err instanceof Error ? err.message : String(err));
2661
2673
  throw err;
2662
2674
  }
2663
2675
  }
@@ -2790,11 +2802,11 @@ var init_NotificationsManager = __esm({
2790
2802
  await waitForApnsToken(this.bridge, this.debug);
2791
2803
  const token = await this.bridge.getToken();
2792
2804
  if (!token) {
2793
- if (this.debug) console.warn("[Paywallo PUSH] token unavailable, skipping registration");
2805
+ if (this.debug) console.log("[Paywallo PUSH] token unavailable, skipping registration");
2794
2806
  this.isInitialized = true;
2795
2807
  return;
2796
2808
  }
2797
- if (this.debug) console.warn("[Paywallo PUSH] token acquired:", token);
2809
+ if (this.debug) console.log("[Paywallo PUSH] token acquired:", token);
2798
2810
  this.currentToken = token;
2799
2811
  await this.doRegisterToken(token);
2800
2812
  this.subscribeTokenRefresh();
@@ -2812,7 +2824,7 @@ var init_NotificationsManager = __esm({
2812
2824
  async refreshPermissionStatus() {
2813
2825
  const permStatus = await this.bridge.hasPermission();
2814
2826
  this.permissionStatus = mapPermissionStatus(permStatus);
2815
- if (this.debug) console.warn("[Paywallo PUSH] permission status:", this.permissionStatus);
2827
+ if (this.debug) console.log("[Paywallo PUSH] permission status:", this.permissionStatus);
2816
2828
  }
2817
2829
  tokenDeps() {
2818
2830
  return {
@@ -2840,7 +2852,7 @@ var init_NotificationsManager = __esm({
2840
2852
  async optOut() {
2841
2853
  await optOutToken(this.httpClient, this.secureStorage, this.appKey, this.debug);
2842
2854
  this.currentToken = null;
2843
- if (this.debug) console.warn("[Paywallo PUSH] opted out");
2855
+ if (this.debug) console.log("[Paywallo PUSH] opted out");
2844
2856
  }
2845
2857
  /** Clears token locally without server call (used on sign-out/reset). */
2846
2858
  async invalidateLocalToken() {
@@ -2857,21 +2869,21 @@ var init_NotificationsManager = __esm({
2857
2869
  }
2858
2870
  /** Requests OS permission and, on success, captures + registers the push token. */
2859
2871
  async requestPushPermission(options) {
2860
- if (this.debug) console.warn("[Paywallo PUSH] requestPushPermission called");
2872
+ if (this.debug) console.log("[Paywallo PUSH] requestPushPermission called");
2861
2873
  try {
2862
2874
  const status = await this.permissionManager.requestPermission(options);
2863
- if (this.debug) console.warn("[Paywallo PUSH] permissionManager returned:", status);
2875
+ if (this.debug) console.log("[Paywallo PUSH] permissionManager returned:", status);
2864
2876
  this.permissionStatus = status;
2865
2877
  if (status === "granted" || status === "provisional") {
2866
- if (this.debug) console.warn("[Paywallo PUSH] permission granted \u2014 capturing token...");
2878
+ if (this.debug) console.log("[Paywallo PUSH] permission granted \u2014 capturing token...");
2867
2879
  await this.captureAndRegisterToken();
2868
- if (this.debug) console.warn("[Paywallo PUSH] token capture complete");
2880
+ if (this.debug) console.log("[Paywallo PUSH] token capture complete");
2869
2881
  } else {
2870
- if (this.debug) console.warn("[Paywallo PUSH] permission not granted, skipping token register:", status);
2882
+ if (this.debug) console.log("[Paywallo PUSH] permission not granted, skipping token register:", status);
2871
2883
  }
2872
2884
  return status;
2873
2885
  } catch (err) {
2874
- if (this.debug) console.warn("[Paywallo PUSH] requestPushPermission CRASHED:", err instanceof Error ? err.message : String(err));
2886
+ console.error("[Paywallo PUSH] requestPushPermission CRASHED:", err instanceof Error ? err.message : String(err));
2875
2887
  throw err;
2876
2888
  }
2877
2889
  }
@@ -3033,7 +3045,9 @@ function parseWebViewMessage(raw) {
3033
3045
  const safePayload = payload !== null && typeof payload === "object" ? {
3034
3046
  productId: typeof payload.productId === "string" ? String(payload.productId) : void 0,
3035
3047
  url: typeof payload.url === "string" ? String(payload.url) : void 0,
3036
- timestamp: typeof payload.timestamp === "number" ? Number(payload.timestamp) : void 0
3048
+ timestamp: typeof payload.timestamp === "number" ? Number(payload.timestamp) : void 0,
3049
+ variantKey: typeof payload.variantKey === "string" ? String(payload.variantKey) : void 0,
3050
+ style: HAPTIC_STYLES.has(String(payload.style)) ? String(payload.style) : typeof payload.style !== "undefined" ? "light" : void 0
3037
3051
  } : void 0;
3038
3052
  return {
3039
3053
  type: parsed.type,
@@ -3044,17 +3058,19 @@ function parseWebViewMessage(raw) {
3044
3058
  return null;
3045
3059
  }
3046
3060
  }
3047
- var ALLOWED_MESSAGE_TYPES;
3061
+ var HAPTIC_STYLES, ALLOWED_MESSAGE_TYPES;
3048
3062
  var init_parseWebViewMessage = __esm({
3049
3063
  "src/domains/paywall/parseWebViewMessage.ts"() {
3050
3064
  "use strict";
3065
+ HAPTIC_STYLES = /* @__PURE__ */ new Set(["light", "medium", "heavy"]);
3051
3066
  ALLOWED_MESSAGE_TYPES = /* @__PURE__ */ new Set([
3052
3067
  "purchase",
3053
3068
  "close",
3054
3069
  "restore",
3055
3070
  "select-product",
3056
3071
  "open-url",
3057
- "ready"
3072
+ "ready",
3073
+ "haptic"
3058
3074
  ]);
3059
3075
  }
3060
3076
  });
@@ -3096,7 +3112,7 @@ var init_PaywallErrorBoundary = __esm({
3096
3112
  return { hasError: true };
3097
3113
  }
3098
3114
  componentDidCatch(error) {
3099
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] Paywall render error:", error);
3115
+ console.error("[Paywallo] Paywall render error:", error);
3100
3116
  try {
3101
3117
  PaywalloClient.getApiClient()?.reportError("PAYWALL_RENDER_ERROR", error.message);
3102
3118
  } catch {
@@ -3263,7 +3279,9 @@ function buildPaywallInjectionScript(data) {
3263
3279
  craftData: data.craftData,
3264
3280
  products: data.products,
3265
3281
  primaryProductId: data.primaryProductId,
3266
- secondaryProductId: data.secondaryProductId
3282
+ secondaryProductId: data.secondaryProductId,
3283
+ currentLanguage: getCurrentLanguage(),
3284
+ defaultLanguage: getDefaultLanguage()
3267
3285
  }
3268
3286
  };
3269
3287
  return `(function(){var d=${JSON.stringify(paywallData)},a=0,m=50;function t(){a++;if(window.receiveNativeMessage){window.receiveNativeMessage(d);return;}if(a<m){setTimeout(t,100);}}t();})();true;`;
@@ -3271,20 +3289,43 @@ function buildPaywallInjectionScript(data) {
3271
3289
  function buildPurchaseStateScript(isPurchasing) {
3272
3290
  return `if(window.receiveNativeMessage){window.receiveNativeMessage({type:"purchaseState",data:{isPurchasing:${isPurchasing}}});}true;`;
3273
3291
  }
3292
+ function buildBackButtonScript(nonce = Date.now()) {
3293
+ return [
3294
+ `/*${nonce}*/`,
3295
+ "(function(){",
3296
+ "try{",
3297
+ "var h=window.__paywalloHandleBackPress;",
3298
+ "var consumed=typeof h==='function'?!!h():false;",
3299
+ "if(!consumed&&window.ReactNativeWebView){",
3300
+ 'window.ReactNativeWebView.postMessage(JSON.stringify({type:"close"}));',
3301
+ "}",
3302
+ "}catch(e){",
3303
+ "if(window.ReactNativeWebView){",
3304
+ 'window.ReactNativeWebView.postMessage(JSON.stringify({type:"close"}));',
3305
+ "}",
3306
+ "}",
3307
+ "})();true;"
3308
+ ].join("");
3309
+ }
3274
3310
  var init_paywallScripts = __esm({
3275
3311
  "src/domains/paywall/paywallScripts.ts"() {
3276
3312
  "use strict";
3313
+ init_localization();
3277
3314
  }
3278
3315
  });
3279
3316
 
3280
3317
  // src/domains/paywall/PaywallWebView.tsx
3281
3318
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3282
3319
  import {
3320
+ BackHandler,
3321
+ findNodeHandle,
3283
3322
  Linking as Linking2,
3284
3323
  NativeEventEmitter as NativeEventEmitter2,
3285
3324
  NativeModules as NativeModules6,
3325
+ Platform as Platform9,
3286
3326
  requireNativeComponent,
3287
3327
  StyleSheet,
3328
+ UIManager,
3288
3329
  View
3289
3330
  } from "react-native";
3290
3331
  import { jsx } from "react/jsx-runtime";
@@ -3306,6 +3347,24 @@ function getWebViewEmitter() {
3306
3347
  webViewEmitter = new NativeEventEmitter2(mod);
3307
3348
  return webViewEmitter;
3308
3349
  }
3350
+ async function triggerNativeHaptic(style) {
3351
+ try {
3352
+ const module = await import("react-native-haptic-feedback");
3353
+ const hapticModule = module.default ?? module;
3354
+ const trigger = hapticModule.trigger;
3355
+ if (typeof trigger !== "function") return;
3356
+ const typeMap = {
3357
+ light: "impactLight",
3358
+ medium: "impactMedium",
3359
+ heavy: "impactHeavy"
3360
+ };
3361
+ trigger(typeMap[style], {
3362
+ enableVibrateFallback: true,
3363
+ ignoreAndroidSystemSettings: false
3364
+ });
3365
+ } catch {
3366
+ }
3367
+ }
3309
3368
  function parseErrorEvent(raw) {
3310
3369
  return {
3311
3370
  code: typeof raw["code"] === "number" ? raw["code"] : -1,
@@ -3330,6 +3389,7 @@ function PaywallWebView({
3330
3389
  const paywallDataSent = useRef(false);
3331
3390
  const loadEndReceived = useRef(false);
3332
3391
  const [scriptToInject, setScriptToInject] = useState(void 0);
3392
+ const webViewRef = useRef(null);
3333
3393
  const onErrorRef = useRef(onError);
3334
3394
  onErrorRef.current = onError;
3335
3395
  const onReadyRef = useRef(onReady);
@@ -3373,6 +3433,15 @@ function PaywallWebView({
3373
3433
  });
3374
3434
  }
3375
3435
  }, []);
3436
+ const injectScript = useCallback((script) => {
3437
+ if (Platform9.OS === "android") {
3438
+ const handle = findNodeHandle(webViewRef.current);
3439
+ if (handle == null) return;
3440
+ UIManager.dispatchViewManagerCommand(handle, "injectJavaScript", [script]);
3441
+ return;
3442
+ }
3443
+ setScriptToInject(script);
3444
+ }, []);
3376
3445
  const buildAndSetInjectionScript = useCallback(() => {
3377
3446
  if (!craftData || paywallDataSent.current) return;
3378
3447
  paywallDataSent.current = true;
@@ -3382,8 +3451,8 @@ function PaywallWebView({
3382
3451
  primaryProductId,
3383
3452
  secondaryProductId
3384
3453
  });
3385
- setScriptToInject(script);
3386
- }, [craftData, products, primaryProductId, secondaryProductId]);
3454
+ injectScript(script);
3455
+ }, [craftData, products, primaryProductId, secondaryProductId, injectScript]);
3387
3456
  const processedIdsRef = useRef(/* @__PURE__ */ new Set());
3388
3457
  const dedupeTimersRef = useRef(/* @__PURE__ */ new Map());
3389
3458
  useEffect(() => {
@@ -3420,6 +3489,9 @@ function PaywallWebView({
3420
3489
  case "restore":
3421
3490
  onRestore();
3422
3491
  break;
3492
+ case "select-product":
3493
+ console.log("[Paywallo:PaywallWebView] select-product received", message.payload?.productId);
3494
+ break;
3423
3495
  case "open-url":
3424
3496
  if (message.payload?.url) {
3425
3497
  const url = message.payload.url;
@@ -3428,6 +3500,9 @@ function PaywallWebView({
3428
3500
  }
3429
3501
  }
3430
3502
  break;
3503
+ case "haptic":
3504
+ void triggerNativeHaptic(message.payload?.style ?? "light");
3505
+ break;
3431
3506
  }
3432
3507
  },
3433
3508
  [buildAndSetInjectionScript, onPurchase, onClose, onRestore]
@@ -3499,13 +3574,24 @@ function PaywallWebView({
3499
3574
  );
3500
3575
  useEffect(() => {
3501
3576
  if (!paywallDataSent.current) return;
3502
- setScriptToInject(buildPurchaseStateScript(isPurchasing));
3503
- }, [isPurchasing]);
3577
+ injectScript(buildPurchaseStateScript(isPurchasing));
3578
+ }, [isPurchasing, injectScript]);
3579
+ useEffect(() => {
3580
+ if (Platform9.OS !== "android") return;
3581
+ const onBackPress = () => {
3582
+ if (!paywallDataSent.current) return false;
3583
+ injectScript(buildBackButtonScript());
3584
+ return true;
3585
+ };
3586
+ const subscription = BackHandler.addEventListener("hardwareBackPress", onBackPress);
3587
+ return () => subscription.remove();
3588
+ }, [injectScript]);
3504
3589
  if (!resolvedWebUrl || !NativeWebView) return null;
3505
3590
  const paywallUrl = `${resolvedWebUrl}/paywall/${paywallId}`;
3506
3591
  return /* @__PURE__ */ jsx(View, { style: styles.container, children: /* @__PURE__ */ jsx(
3507
3592
  NativeWebView,
3508
3593
  {
3594
+ ref: webViewRef,
3509
3595
  sourceUrl: paywallUrl,
3510
3596
  injectedJavaScript: scriptToInject,
3511
3597
  onMessage: handleMessage,
@@ -3588,7 +3674,7 @@ __export(NativeStoreKit_exports, {
3588
3674
  NativeStoreKit: () => NativeStoreKit,
3589
3675
  nativeStoreKit: () => nativeStoreKit
3590
3676
  });
3591
- import { NativeEventEmitter as NativeEventEmitter3, NativeModules as NativeModules7, Platform as Platform9 } from "react-native";
3677
+ import { NativeEventEmitter as NativeEventEmitter3, NativeModules as NativeModules7, Platform as Platform10 } from "react-native";
3592
3678
  function isAvailable3() {
3593
3679
  return PaywalloStoreKitNative != null;
3594
3680
  }
@@ -3661,7 +3747,7 @@ function mapNativePurchase(native) {
3661
3747
  transactionId: native.transactionId,
3662
3748
  transactionDate: native.transactionDate,
3663
3749
  receipt: native.receipt,
3664
- platform: Platform9.OS
3750
+ platform: Platform10.OS
3665
3751
  };
3666
3752
  }
3667
3753
  function normalizeTransactionUpdate(raw) {
@@ -3732,7 +3818,7 @@ var init_NativeStoreKit = __esm({
3732
3818
  try {
3733
3819
  await PaywalloStoreKitNative.finishTransaction(transactionId);
3734
3820
  } catch (err) {
3735
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] finishTransaction native error", err);
3821
+ console.error("[Paywallo] finishTransaction native error", err);
3736
3822
  }
3737
3823
  },
3738
3824
  async getActiveTransactions() {
@@ -3741,7 +3827,7 @@ var init_NativeStoreKit = __esm({
3741
3827
  const results = await PaywalloStoreKitNative.getActiveTransactions();
3742
3828
  return results.map(mapNativePurchase);
3743
3829
  } catch (err) {
3744
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] getActiveTransactions failed", err);
3830
+ console.error("[Paywallo] getActiveTransactions failed", err);
3745
3831
  return [];
3746
3832
  }
3747
3833
  },
@@ -4592,7 +4678,7 @@ var init_QueueProcessor = __esm({
4592
4678
  }
4593
4679
  const allItems = offlineQueue.getQueue();
4594
4680
  const eventItems = allItems.filter(
4595
- (i) => (i.url.includes("/events") || i.url.includes("/ingest")) && !i.url.includes("/batch")
4681
+ (i) => i.url.includes("/events") || i.url.includes("/ingest")
4596
4682
  );
4597
4683
  const eventItemIds = new Set(eventItems.map((i) => i.id));
4598
4684
  const { processed: batchProcessed, failed: batchFailed, retryIds } = await this.processEventBatches(eventItems);
@@ -4632,7 +4718,7 @@ var init_QueueProcessor = __esm({
4632
4718
  const freshHeaders = this.config.getFreshHeaders?.() ?? {};
4633
4719
  const headers = { ...persistedHeaders, ...freshHeaders };
4634
4720
  this.log("Sending event batch", { size: batch.length, batchIndex: Math.floor(i / BATCH_SIZE) });
4635
- const response = await this.httpClient.request("/sdk/events/batch", {
4721
+ const response = await this.httpClient.request("/sdk/ingest/batch", {
4636
4722
  method: "POST",
4637
4723
  body: { events: bodies },
4638
4724
  headers,
@@ -4757,7 +4843,7 @@ var init_queue = __esm({
4757
4843
  });
4758
4844
 
4759
4845
  // src/domains/iap/IAPValidator.ts
4760
- import { Platform as Platform10 } from "react-native";
4846
+ import { Platform as Platform11 } from "react-native";
4761
4847
  var IAPValidator;
4762
4848
  var init_IAPValidator = __esm({
4763
4849
  "src/domains/iap/IAPValidator.ts"() {
@@ -4787,7 +4873,7 @@ var init_IAPValidator = __esm({
4787
4873
  async validateWithServer(purchase, productId, options) {
4788
4874
  const apiClient = this.getApiClientFn();
4789
4875
  const product = this.getProductFromCache(productId);
4790
- const platform = Platform10.OS;
4876
+ const platform = Platform11.OS;
4791
4877
  const distinctId = PaywalloClient.getDistinctId();
4792
4878
  return this.validateWithRetry(
4793
4879
  () => apiClient.validatePurchase(
@@ -4822,24 +4908,24 @@ var init_IAPValidator = __esm({
4822
4908
  try {
4823
4909
  const apiClient = this.getApiClientFn();
4824
4910
  const product = this.getProductFromCache(productId);
4825
- const platform = Platform10.OS;
4911
+ const platform = Platform11.OS;
4826
4912
  const distinctId = PaywalloClient.getDistinctId();
4827
4913
  const appKey = apiClient.getAppKey();
4828
4914
  const body = {
4829
4915
  platform,
4830
- receipt: purchase.receipt,
4831
- productId: purchase.productId,
4832
- transactionId: purchase.transactionId,
4833
- priceLocal: product?.priceValue ?? 0,
4916
+ receipt_data: purchase.receipt,
4917
+ product_id: purchase.productId,
4918
+ transaction_id: purchase.transactionId,
4919
+ price_local: product?.priceValue ?? 0,
4834
4920
  currency: product?.currency ?? "USD",
4835
4921
  country: this.getDeviceCountry(),
4836
- distinctId
4922
+ distinct_id: distinctId
4837
4923
  };
4838
- if (options?.paywallPlacement !== void 0) body.paywallPlacement = options.paywallPlacement;
4839
- if (options?.variantKey !== void 0) body.variantKey = options.variantKey;
4924
+ if (options?.paywallPlacement !== void 0) body.paywall_placement = options.paywallPlacement;
4925
+ if (options?.variantKey !== void 0) body.variant_key = options.variantKey;
4840
4926
  await offlineQueue.enqueue(
4841
4927
  "POST",
4842
- "/purchases/validate",
4928
+ "/sdk/purchases/validate",
4843
4929
  body,
4844
4930
  { "X-App-Key": appKey },
4845
4931
  `validate:${purchase.transactionId}`,
@@ -4847,7 +4933,7 @@ var init_IAPValidator = __esm({
4847
4933
  );
4848
4934
  if (this.getDebug()) console.log("[Paywallo PURCHASE] validation retry enqueued", purchase.transactionId);
4849
4935
  } catch (err) {
4850
- if (this.getDebug()) console.error("[Paywallo PURCHASE] failed to enqueue validation retry", err);
4936
+ console.error("[Paywallo PURCHASE] failed to enqueue validation retry", err);
4851
4937
  }
4852
4938
  }
4853
4939
  };
@@ -4855,7 +4941,7 @@ var init_IAPValidator = __esm({
4855
4941
  });
4856
4942
 
4857
4943
  // src/domains/iap/IAPService.ts
4858
- import { Platform as Platform11 } from "react-native";
4944
+ import { Platform as Platform12 } from "react-native";
4859
4945
  function getIAPService() {
4860
4946
  if (!_instance) {
4861
4947
  _instance = new IAPService();
@@ -4926,7 +5012,7 @@ var init_IAPService = __esm({
4926
5012
  }
4927
5013
  return products;
4928
5014
  } catch (err) {
4929
- if (this.debug) console.error("[Paywallo] loadProducts failed", err);
5015
+ console.error("[Paywallo] loadProducts failed", err);
4930
5016
  return [];
4931
5017
  }
4932
5018
  }
@@ -4945,7 +5031,7 @@ var init_IAPService = __esm({
4945
5031
  await nativeStoreKit.finishTransaction(transactionId);
4946
5032
  this.finishedTransactions.add(transactionId);
4947
5033
  } catch (err) {
4948
- if (this.debug) console.error("[Paywallo] finishTransaction failed", err);
5034
+ console.error("[Paywallo] finishTransaction failed", err);
4949
5035
  }
4950
5036
  }
4951
5037
  startTransactionListener() {
@@ -4994,7 +5080,7 @@ var init_IAPService = __esm({
4994
5080
  await this.finishTransaction(purchase.transactionId);
4995
5081
  return;
4996
5082
  }
4997
- if (this.debug) console.error("[Paywallo PURCHASE] background validation failed (network) \u2014 queued for retry", err);
5083
+ console.error("[Paywallo PURCHASE] background validation failed (network) \u2014 queued for retry", err);
4998
5084
  await this.validator.enqueueValidationRetry(purchase, productId, {
4999
5085
  paywallPlacement: options?.paywallPlacement,
5000
5086
  variantKey: options?.variantKey
@@ -5002,7 +5088,7 @@ var init_IAPService = __esm({
5002
5088
  });
5003
5089
  return { success: true, purchase };
5004
5090
  } catch (error) {
5005
- if (this.debug) console.error(`[Paywallo PURCHASE] ${productId} failed`, error);
5091
+ console.error(`[Paywallo PURCHASE] ${productId} failed`, error);
5006
5092
  const e = error instanceof PurchaseError ? error : new PurchaseError(PURCHASE_ERROR_CODES.PURCHASE_FAILED, error instanceof Error ? error.message : String(error));
5007
5093
  return { success: false, error: e };
5008
5094
  } finally {
@@ -5015,7 +5101,7 @@ var init_IAPService = __esm({
5015
5101
  const transactions = await nativeStoreKit.getActiveTransactions();
5016
5102
  const apiClient = this.getApiClient();
5017
5103
  const distinctId = PaywalloClient.getDistinctId();
5018
- const platform = Platform11.OS;
5104
+ const platform = Platform12.OS;
5019
5105
  const results = await Promise.allSettled(
5020
5106
  transactions.map((tx) => {
5021
5107
  const product = this.productsCache.get(tx.productId);
@@ -5046,7 +5132,7 @@ var init_IAPService = __esm({
5046
5132
  if (this.debug) console.log("[Paywallo RESTORE] validation rejected (4xx) \u2014 dropping", tx.transactionId);
5047
5133
  await this.finishTransaction(tx.transactionId);
5048
5134
  } else {
5049
- if (this.debug) console.error("[Paywallo RESTORE] validation failed (network) \u2014 queued for retry", r.reason);
5135
+ console.error("[Paywallo RESTORE] validation failed (network) \u2014 queued for retry", r.reason);
5050
5136
  await this.validator.enqueueValidationRetry(tx, tx.productId);
5051
5137
  }
5052
5138
  } else if (r.status === "fulfilled" && !r.value.success) {
@@ -5056,7 +5142,7 @@ var init_IAPService = __esm({
5056
5142
  }
5057
5143
  return validated;
5058
5144
  } catch (err) {
5059
- if (this.debug) console.error("[Paywallo] restore failed", err);
5145
+ console.error("[Paywallo] restore failed", err);
5060
5146
  return [];
5061
5147
  }
5062
5148
  }
@@ -5272,7 +5358,7 @@ function usePaywallLoader(placement, visible, preloadedConfig, preloadedProducts
5272
5358
  setProducts(map);
5273
5359
  }
5274
5360
  } catch (err) {
5275
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] loadProducts in paywall failed", err);
5361
+ console.error("[Paywallo] loadProducts in paywall failed", err);
5276
5362
  if (!cancelled) setProducts(/* @__PURE__ */ new Map());
5277
5363
  }
5278
5364
  } else {
@@ -6406,11 +6492,9 @@ var init_SubscriptionManager = __esm({
6406
6492
  if (!this.config) {
6407
6493
  return this.getEmptyStatus();
6408
6494
  }
6409
- const url = new URL(`${this.config.serverUrl}/sdk/purchases/status/${this.config.appKey}`);
6410
- if (this.userId) {
6411
- url.searchParams.set("distinctId", this.userId);
6412
- }
6413
- const response = await fetch(url.toString(), {
6495
+ const userId = this.userId ?? "";
6496
+ const url = `${this.config.serverUrl}/sdk/purchases/status?distinctId=${encodeURIComponent(userId)}`;
6497
+ const response = await fetch(url, {
6414
6498
  method: "GET",
6415
6499
  headers: {
6416
6500
  "Content-Type": "application/json",
@@ -6423,17 +6507,32 @@ var init_SubscriptionManager = __esm({
6423
6507
  `Failed to fetch subscription status: ${response.status}`
6424
6508
  );
6425
6509
  }
6426
- const data = await response.json();
6427
- if (data.subscription) {
6428
- data.subscription.expiresAt = new Date(data.subscription.expiresAt);
6429
- data.subscription.originalPurchaseDate = new Date(data.subscription.originalPurchaseDate);
6430
- data.subscription.latestPurchaseDate = new Date(data.subscription.latestPurchaseDate);
6431
- if (data.subscription.cancellationDate) {
6432
- data.subscription.cancellationDate = new Date(data.subscription.cancellationDate);
6433
- }
6434
- if (data.subscription.gracePeriodExpiresAt) {
6435
- data.subscription.gracePeriodExpiresAt = new Date(data.subscription.gracePeriodExpiresAt);
6436
- }
6510
+ const envelope = await response.json();
6511
+ const raw = envelope.data ?? envelope;
6512
+ const data = {
6513
+ hasActiveSubscription: !!(raw["has_active_subscription"] ?? raw["hasActiveSubscription"]),
6514
+ subscription: null,
6515
+ entitlements: raw["entitlements"] ?? []
6516
+ };
6517
+ const sub = raw["subscription"];
6518
+ if (sub) {
6519
+ const cancellationRaw = sub["cancellationDate"] ?? sub["cancellation_date"];
6520
+ const gracePeriodRaw = sub["gracePeriodExpiresAt"] ?? sub["grace_period_expires_at"];
6521
+ data.subscription = {
6522
+ id: sub["id"],
6523
+ productId: sub["product_id"] ?? sub["productId"],
6524
+ status: sub["status"],
6525
+ expiresAt: new Date(sub["expires_at"] ?? sub["expiresAt"]),
6526
+ originalPurchaseDate: new Date(sub["originalPurchaseDate"] ?? sub["original_purchase_date"]),
6527
+ latestPurchaseDate: new Date(sub["latestPurchaseDate"] ?? sub["latest_purchase_date"]),
6528
+ willRenew: !!(sub["will_renew"] ?? sub["willRenew"] ?? sub["auto_renew_enabled"] ?? sub["autoRenewEnabled"]),
6529
+ isTrial: !!(sub["is_trial"] ?? sub["isTrial"]),
6530
+ isIntroductoryPeriod: !!(sub["is_introductory_period"] ?? sub["isIntroductoryPeriod"]),
6531
+ platform: sub["platform"],
6532
+ storeTransactionId: sub["store_transaction_id"] ?? sub["storeTransactionId"],
6533
+ cancellationDate: cancellationRaw ? new Date(cancellationRaw) : void 0,
6534
+ gracePeriodExpiresAt: gracePeriodRaw ? new Date(gracePeriodRaw) : void 0
6535
+ };
6437
6536
  }
6438
6537
  return data;
6439
6538
  }
@@ -6471,15 +6570,15 @@ async function validatePurchase(client, appKey, platform, receipt, productId, tr
6471
6570
  "/sdk/purchases/validate",
6472
6571
  {
6473
6572
  platform,
6474
- receipt,
6475
- productId,
6476
- transactionId,
6477
- priceLocal,
6573
+ receipt_data: receipt,
6574
+ product_id: productId,
6575
+ transaction_id: transactionId,
6576
+ price_local: priceLocal,
6478
6577
  currency,
6479
6578
  country,
6480
- distinctId,
6481
- ...paywallPlacement !== void 0 && { paywallPlacement },
6482
- ...variantKey !== void 0 && { variantKey }
6579
+ distinct_id: distinctId,
6580
+ ...paywallPlacement !== void 0 && { paywall_placement: paywallPlacement },
6581
+ ...variantKey !== void 0 && { variant_key: variantKey }
6483
6582
  },
6484
6583
  false
6485
6584
  );
@@ -6491,7 +6590,7 @@ async function validatePurchase(client, appKey, platform, receipt, productId, tr
6491
6590
  response.status
6492
6591
  );
6493
6592
  }
6494
- return response.data;
6593
+ return response.data.data;
6495
6594
  }
6496
6595
  async function getSubscriptionStatus(client, baseUrl, appKey, distinctId) {
6497
6596
  const url = new URL(`${baseUrl}/sdk/purchases/status`);
@@ -6500,7 +6599,7 @@ async function getSubscriptionStatus(client, baseUrl, appKey, distinctId) {
6500
6599
  if (!response.ok) {
6501
6600
  throw new PurchaseError(PURCHASE_ERROR_CODES.NETWORK_ERROR, `Subscription status check failed: HTTP ${response.status}`);
6502
6601
  }
6503
- return response.data;
6602
+ return response.data.data;
6504
6603
  }
6505
6604
  async function getEmergencyPaywall(client) {
6506
6605
  try {
@@ -6709,7 +6808,7 @@ async function evaluateFlags2(deps, keys, distinctId) {
6709
6808
  const result = {};
6710
6809
  for (const key of Object.keys(record)) {
6711
6810
  const value = record[key];
6712
- const variant = typeof value === "boolean" ? String(value) : typeof value === "string" ? value : null;
6811
+ const variant = typeof value === "string" ? value : null;
6713
6812
  result[key] = variant;
6714
6813
  const flagVariant = { variant };
6715
6814
  deps.cache.setVariant(`${key}:${distinctId}`, flagVariant);
@@ -6947,7 +7046,7 @@ var init_eventFamilies = __esm({
6947
7046
  });
6948
7047
 
6949
7048
  // src/core/eventEnvelopeV2.ts
6950
- import { Platform as Platform12 } from "react-native";
7049
+ import { Platform as Platform13 } from "react-native";
6951
7050
  import { z as z2 } from "zod";
6952
7051
  function buildV2Envelope(events, providerContext) {
6953
7052
  const context = { ...providerContext };
@@ -6971,7 +7070,7 @@ function buildV2Envelope(events, providerContext) {
6971
7070
  };
6972
7071
  });
6973
7072
  if (context.sdk_version === void 0) context.sdk_version = SDK_VERSION;
6974
- if (context.platform === void 0) context.platform = Platform12.OS;
7073
+ if (context.platform === void 0) context.platform = Platform13.OS;
6975
7074
  if (context.distinct_id === void 0 && events.length > 0) {
6976
7075
  context.distinct_id = events[0].distinctId;
6977
7076
  }
@@ -7022,7 +7121,7 @@ var init_eventEnvelopeV2 = __esm({
7022
7121
  payload: z2.record(z2.string(), z2.unknown())
7023
7122
  });
7024
7123
  v2ContextSchema = z2.object({
7025
- distinct_id: z2.string().optional(),
7124
+ distinct_id: z2.string().min(1),
7026
7125
  session_id: z2.string().optional(),
7027
7126
  device_id: z2.string().optional(),
7028
7127
  app_version: z2.string().optional(),
@@ -7065,8 +7164,8 @@ var init_events = __esm({
7065
7164
  });
7066
7165
 
7067
7166
  // src/core/EventBatcher.ts
7068
- import { Platform as Platform13 } from "react-native";
7069
- var BATCH_MAX_SIZE, BATCH_FLUSH_MS, EVENT_NAME_REGEX, V2_BATCH_ENDPOINT, V1_BATCH_ENDPOINT, V1_SINGLE_ENDPOINT, EventBatcher;
7167
+ import { Platform as Platform14 } from "react-native";
7168
+ var BATCH_MAX_SIZE, BATCH_FLUSH_MS, EVENT_NAME_REGEX, V2_BATCH_ENDPOINT, EventBatcher;
7070
7169
  var init_EventBatcher = __esm({
7071
7170
  "src/core/EventBatcher.ts"() {
7072
7171
  "use strict";
@@ -7076,15 +7175,12 @@ var init_EventBatcher = __esm({
7076
7175
  BATCH_FLUSH_MS = 1e4;
7077
7176
  EVENT_NAME_REGEX = /^(\$[a-zA-Z0-9_]+|[a-z][a-z0-9_]*)$/;
7078
7177
  V2_BATCH_ENDPOINT = "/sdk/ingest/batch";
7079
- V1_BATCH_ENDPOINT = "/sdk/events/batch";
7080
- V1_SINGLE_ENDPOINT = "/sdk/events";
7081
7178
  EventBatcher = class {
7082
7179
  constructor(post, debug = false, contextProvider = null) {
7083
7180
  this.criticalQueue = [];
7084
7181
  this.normalQueue = [];
7085
7182
  this.normalTimer = null;
7086
7183
  this.criticalScheduled = false;
7087
- this.v2Disabled = false;
7088
7184
  this.post = post;
7089
7185
  this.debug = debug;
7090
7186
  this.contextProvider = contextProvider;
@@ -7121,7 +7217,7 @@ var init_EventBatcher = __esm({
7121
7217
  const event = {
7122
7218
  eventName,
7123
7219
  distinctId,
7124
- properties: { ...properties, platform: Platform13.OS },
7220
+ properties: { ...properties, platform: Platform14.OS },
7125
7221
  timestamp: timestamp ?? Date.now(),
7126
7222
  environment: environment.toLowerCase()
7127
7223
  };
@@ -7180,11 +7276,7 @@ var init_EventBatcher = __esm({
7180
7276
  try {
7181
7277
  await this.postBatch([event], `event_critical:${event.eventName}`, "critical");
7182
7278
  } catch {
7183
- try {
7184
- await this.post(V1_SINGLE_ENDPOINT, event, `event:${event.eventName}`, "critical");
7185
- } catch {
7186
- if (this.debug) console.log(`[Paywallo] critical event ${event.eventName} post failed`);
7187
- }
7279
+ if (this.debug) console.log(`[Paywallo] critical event ${event.eventName} post failed`);
7188
7280
  }
7189
7281
  }
7190
7282
  }
@@ -7198,50 +7290,24 @@ var init_EventBatcher = __esm({
7198
7290
  try {
7199
7291
  await this.postBatch(batch, `event_batch:${batch.length}`);
7200
7292
  } catch {
7201
- for (const event of batch) {
7202
- await this.post(V1_SINGLE_ENDPOINT, event, `event:${event.eventName}`);
7203
- }
7293
+ if (this.debug) console.log(`[Paywallo] normal batch post failed \u2014 OfflineQueue will handle durability`);
7204
7294
  }
7205
7295
  }
7206
- /**
7207
- * POST the batch with V2 envelope semantics. Tries `/ingest/batch`
7208
- * first; on failure (404/network) falls back to `/events/batch`
7209
- * with the legacy per-event shape for retrocompat.
7210
- *
7211
- * After a single V2 failure we flip `v2Disabled` and stop trying V2 for
7212
- * the lifetime of this EventBatcher — avoids N retries per batch on old
7213
- * servers that will never have the endpoint. A fresh `new EventBatcher()`
7214
- * (e.g. after `Paywallo.init` on a new server URL) resets the flag.
7215
- */
7216
7296
  async postBatch(events, label, priority = "normal") {
7217
- if (!this.v2Disabled) {
7218
- try {
7219
- const providerContext = resolveProviderContext(this.contextProvider);
7220
- const inputs = events.map((e) => ({
7221
- eventName: e.eventName,
7222
- distinctId: e.distinctId,
7223
- properties: e.properties,
7224
- timestamp: e.timestamp
7225
- }));
7226
- const envelope = buildV2Envelope(inputs, providerContext);
7227
- const parsed = v2EnvelopeSchema.safeParse(envelope);
7228
- if (!parsed.success) {
7229
- if (this.debug) console.log("[Paywallo EVENTS] V2 envelope schema validation failed:", parsed.error.issues);
7230
- throw parsed.error;
7231
- }
7232
- await this.post(V2_BATCH_ENDPOINT, parsed.data, label, priority);
7233
- return;
7234
- } catch (error) {
7235
- this.v2Disabled = true;
7236
- if (this.debug) {
7237
- console.log(
7238
- "[Paywallo EVENTS] V2 ingest unavailable, falling back to /events/batch:",
7239
- error
7240
- );
7241
- }
7242
- }
7297
+ const providerContext = resolveProviderContext(this.contextProvider);
7298
+ const inputs = events.map((e) => ({
7299
+ eventName: e.eventName,
7300
+ distinctId: e.distinctId,
7301
+ properties: e.properties,
7302
+ timestamp: e.timestamp
7303
+ }));
7304
+ const envelope = buildV2Envelope(inputs, providerContext);
7305
+ const parsed = v2EnvelopeSchema.safeParse(envelope);
7306
+ if (!parsed.success) {
7307
+ if (this.debug) console.log("[Paywallo EVENTS] V2 envelope schema validation failed:", parsed.error.issues);
7308
+ throw parsed.error;
7243
7309
  }
7244
- await this.post(V1_BATCH_ENDPOINT, { events }, label, priority);
7310
+ await this.post(V2_BATCH_ENDPOINT, parsed.data, label, priority);
7245
7311
  }
7246
7312
  };
7247
7313
  }
@@ -7476,7 +7542,7 @@ var init_http = __esm({
7476
7542
  });
7477
7543
 
7478
7544
  // src/core/ApiClient.ts
7479
- import { Platform as Platform14 } from "react-native";
7545
+ import { Platform as Platform15 } from "react-native";
7480
7546
  var ApiClient;
7481
7547
  var init_ApiClient = __esm({
7482
7548
  "src/core/ApiClient.ts"() {
@@ -7582,7 +7648,7 @@ var init_ApiClient = __esm({
7582
7648
  buildSdkHeaders() {
7583
7649
  return {
7584
7650
  "x-sdk-version": SDK_VERSION,
7585
- "x-sdk-platform": Platform14.OS,
7651
+ "x-sdk-platform": Platform15.OS,
7586
7652
  "x-sdk-environment": this.environment
7587
7653
  };
7588
7654
  }
@@ -7612,7 +7678,7 @@ var init_ApiClient = __esm({
7612
7678
  message,
7613
7679
  context,
7614
7680
  sdkVersion: SDK_VERSION,
7615
- platform: Platform14.OS
7681
+ platform: Platform15.OS
7616
7682
  }, true);
7617
7683
  } catch (err) {
7618
7684
  if (this.debug) console.log("[Paywallo:ApiClient] reportError failed", err);
@@ -7628,6 +7694,26 @@ var init_ApiClient = __esm({
7628
7694
  this.batcher.dispose();
7629
7695
  }
7630
7696
  async identify(distinctId, properties, email, deviceId, pii) {
7697
+ const rawTraits = {
7698
+ email: email || void 0,
7699
+ platform: Platform15.OS,
7700
+ name: properties?.name,
7701
+ country: properties?.country,
7702
+ locale: properties?.locale,
7703
+ app_version: properties?.appVersion ?? properties?.["app_version"]
7704
+ };
7705
+ const traits = Object.fromEntries(
7706
+ Object.entries(rawTraits).filter(([, v]) => v !== void 0)
7707
+ );
7708
+ const attributionKeys = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content", "fbclid", "gclid", "ttclid", "referrer"];
7709
+ const rawAttribution = {};
7710
+ if (properties) {
7711
+ for (const key of attributionKeys) {
7712
+ const val = properties[key];
7713
+ if (val !== void 0) rawAttribution[key] = val;
7714
+ }
7715
+ }
7716
+ const attribution = Object.keys(rawAttribution).length > 0 ? rawAttribution : void 0;
7631
7717
  await postWithQueue(
7632
7718
  {
7633
7719
  isOfflineQueueEnabled: () => this.offlineQueueEnabled,
@@ -7635,12 +7721,11 @@ var init_ApiClient = __esm({
7635
7721
  isDebug: () => this.debug,
7636
7722
  post: (path, body, skipRetry) => this.post(path, body, skipRetry)
7637
7723
  },
7638
- "/sdk/identify",
7724
+ "/sdk/identity/identify",
7639
7725
  {
7640
- distinctId,
7641
- properties: properties ?? {},
7642
- email,
7643
- platform: Platform14.OS,
7726
+ distinct_id: distinctId,
7727
+ traits,
7728
+ ...attribution && { attribution },
7644
7729
  ...deviceId && { deviceId },
7645
7730
  ...pii?.phone && { phone: pii.phone },
7646
7731
  ...pii?.firstName && { firstName: pii.firstName },
@@ -7651,10 +7736,9 @@ var init_ApiClient = __esm({
7651
7736
  "identify"
7652
7737
  );
7653
7738
  }
7654
- // Session lifecycle endpoints (`/api/v1/sessions/start` and `/api/v1/sessions/end`)
7655
- // have been dropped. All session bookkeeping is now derived on the server from
7656
- // the `$session_start` and `session.end` events that flow through the unified
7657
- // EventBatcher pipeline.
7739
+ // Session lifecycle endpoints were dropped. All session bookkeeping is now
7740
+ // derived on the server from `$session_start` and `session.end` events that
7741
+ // flow through the unified EventBatcher pipeline.
7658
7742
  async getVariant(flagKey, distinctId) {
7659
7743
  return getVariant2(this.flagDeps(), flagKey, distinctId);
7660
7744
  }
@@ -7712,7 +7796,7 @@ var init_ApiClient = __esm({
7712
7796
  const hit = this.cache.getCampaign(key);
7713
7797
  if (hit !== void 0) return hit;
7714
7798
  try {
7715
- const res = await this.get(`/sdk/campaigns/public/primary?distinctId=${encodeURIComponent(distinctId)}`);
7799
+ const res = await this.get(`/campaigns/public/primary?distinctId=${encodeURIComponent(distinctId)}`);
7716
7800
  if (res.status === 404) {
7717
7801
  this.cache.setCampaign(key, null, this.cache.nullTTL);
7718
7802
  return null;
@@ -7812,10 +7896,10 @@ function setupNotifications(apiClient, config, environment, eventPipeline) {
7812
7896
  debug: config.debug,
7813
7897
  environment: environment === "Sandbox" ? "sandbox" : "production"
7814
7898
  }).catch((err) => {
7815
- if (config.debug) console.warn("[Paywallo PUSH] initialization warning:", err instanceof Error ? err.message : String(err));
7899
+ console.error("[Paywallo PUSH] initialization warning:", err instanceof Error ? err.message : String(err));
7816
7900
  });
7817
7901
  } catch (err) {
7818
- if (config.debug) console.warn("[Paywallo PUSH] setup warning:", err instanceof Error ? err.message : String(err));
7902
+ console.error("[Paywallo PUSH] setup warning:", err instanceof Error ? err.message : String(err));
7819
7903
  }
7820
7904
  }
7821
7905
  function setupAutoPreload(state, apiClient, config, distinctId) {
@@ -7940,7 +8024,7 @@ function reportInitFailure(state, config, error, reason) {
7940
8024
  error: error instanceof Error ? error.message : String(error)
7941
8025
  });
7942
8026
  } catch (err) {
7943
- if (config.debug) console.error("[Paywallo] reportInitFailure error", err);
8027
+ console.error("[Paywallo] reportInitFailure error", err);
7944
8028
  }
7945
8029
  }
7946
8030
  function reportInitSuccess(state, config, attempts) {
@@ -7949,7 +8033,7 @@ function reportInitSuccess(state, config, attempts) {
7949
8033
  void state.apiClient.reportError("INIT_RECOVERED", `Succeeded after ${attempts} retries`, { attempts });
7950
8034
  }
7951
8035
  } catch (err) {
7952
- if (config.debug) console.error("[Paywallo] reportInitSuccess error", err);
8036
+ console.error("[Paywallo] reportInitSuccess error", err);
7953
8037
  }
7954
8038
  }
7955
8039
  async function resolveSessionFlags(state, keys, apiClient, distinctId) {
@@ -8003,7 +8087,7 @@ async function doInit(state, config) {
8003
8087
  try {
8004
8088
  await identityManager.identify(pending);
8005
8089
  } catch (err) {
8006
- if (config.debug) console.warn("[Paywallo INIT] pending identify replay failed", err);
8090
+ console.error("[Paywallo INIT] pending identify replay failed", err);
8007
8091
  }
8008
8092
  }
8009
8093
  state.pendingIdentifies = [];
@@ -8081,7 +8165,7 @@ async function doInit(state, config) {
8081
8165
  setupNotifications(apiClient, config, environment, eventPipeline);
8082
8166
  if (config.autoStartSession !== false) {
8083
8167
  sessionManager.startSession().catch((err) => {
8084
- if (config.debug) console.warn("[Paywallo INIT] autoStart session error", err);
8168
+ console.error("[Paywallo INIT] autoStart session error", err);
8085
8169
  });
8086
8170
  }
8087
8171
  new InstallTracker(config.debug, {
@@ -8099,7 +8183,7 @@ async function doInit(state, config) {
8099
8183
  state.resolveReady();
8100
8184
  state.resolveReady = null;
8101
8185
  }
8102
- if (config.debug) console.warn("[Paywallo] initialized successfully");
8186
+ if (config.debug) console.log("[Paywallo] initialized successfully");
8103
8187
  if (distinctId) {
8104
8188
  apiClient.getSubscriptionStatus(distinctId).then(() => {
8105
8189
  if (config.debug) console.log("[Paywallo SUB] preloaded on boot");
@@ -8216,7 +8300,7 @@ async function checkActiveSubscription(state) {
8216
8300
  if (state.config?.debug) console.log("[Paywallo SUB] hasActiveSubscription (server):", isActive);
8217
8301
  return isActive;
8218
8302
  } catch (error) {
8219
- if (state.config?.debug) console.error("[Paywallo SUB] hasActiveSubscription error", error);
8303
+ console.error("[Paywallo SUB] hasActiveSubscription error", error);
8220
8304
  if (state.apiClient) void state.apiClient.reportError("SUBSCRIPTION_CHECK_FAILED", error instanceof Error ? error.message : "hasActiveSubscription failed");
8221
8305
  return false;
8222
8306
  }
@@ -8537,9 +8621,9 @@ __export(OfferingService_exports, {
8537
8621
  OfferingService: () => OfferingService,
8538
8622
  offeringService: () => offeringService
8539
8623
  });
8540
- import { Platform as Platform15 } from "react-native";
8624
+ import { Platform as Platform16 } from "react-native";
8541
8625
  function resolveProductId(raw) {
8542
- const isIos = Platform15.OS === "ios";
8626
+ const isIos = Platform16.OS === "ios";
8543
8627
  const productId = isIos ? raw.apple_product_id : raw.google_product_id;
8544
8628
  return productId ?? "";
8545
8629
  }
@@ -8558,7 +8642,7 @@ function mapProduct2(raw) {
8558
8642
  };
8559
8643
  }
8560
8644
  function pickPlatformProduct(products) {
8561
- const isIos = Platform15.OS === "ios";
8645
+ const isIos = Platform16.OS === "ios";
8562
8646
  const match = products.find((p) => isIos ? p.apple_product_id : p.google_product_id);
8563
8647
  return match ?? products[0] ?? null;
8564
8648
  }
@@ -8682,6 +8766,7 @@ init_PurchaseError();
8682
8766
  // src/index.ts
8683
8767
  init_identity();
8684
8768
  init_identity();
8769
+ init_identity();
8685
8770
  init_notifications();
8686
8771
 
8687
8772
  // src/domains/offering/index.ts
@@ -9352,7 +9437,6 @@ function useCampaignPreload() {
9352
9437
  }
9353
9438
 
9354
9439
  // src/hooks/useProductLoader.ts
9355
- init_PaywalloClient();
9356
9440
  import { useCallback as useCallback16, useState as useState14 } from "react";
9357
9441
  function useProductLoader() {
9358
9442
  const [products, setProducts] = useState14(/* @__PURE__ */ new Map());
@@ -9366,7 +9450,7 @@ function useProductLoader() {
9366
9450
  for (const p of loaded) map.set(p.productId, p);
9367
9451
  setProducts(map);
9368
9452
  } catch (err) {
9369
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] refreshProducts failed", err);
9453
+ console.error("[Paywallo] refreshProducts failed", err);
9370
9454
  } finally {
9371
9455
  setIsLoadingProducts(false);
9372
9456
  }
@@ -9375,7 +9459,6 @@ function useProductLoader() {
9375
9459
  }
9376
9460
 
9377
9461
  // src/hooks/usePurchaseOrchestrator.ts
9378
- init_PaywalloClient();
9379
9462
  import { useCallback as useCallback17 } from "react";
9380
9463
  init_paywall();
9381
9464
  function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, invalidateSubscriptionCache) {
@@ -9410,7 +9493,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
9410
9493
  clearPreloadedWebView();
9411
9494
  resolvePreloadedPaywall({ presented: true, purchased: true, cancelled: false, restored: false });
9412
9495
  } catch (error) {
9413
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] preloaded purchase failed", error);
9496
+ console.error("[Paywallo] preloaded purchase failed", error);
9414
9497
  setShowingPreloadedWebView(false);
9415
9498
  clearPreloadedWebView();
9416
9499
  resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: false });
@@ -9431,7 +9514,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
9431
9514
  clearPreloadedWebView();
9432
9515
  resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: true });
9433
9516
  } catch (error) {
9434
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] preloaded restore failed", error);
9517
+ console.error("[Paywallo] preloaded restore failed", error);
9435
9518
  setShowingPreloadedWebView(false);
9436
9519
  clearPreloadedWebView();
9437
9520
  resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: false });
@@ -10083,6 +10166,7 @@ var Paywallo = PaywalloClient;
10083
10166
  var index_default = Paywallo;
10084
10167
  export {
10085
10168
  ApiClient,
10169
+ AttributionTracker,
10086
10170
  CAMPAIGN_ERROR_CODES,
10087
10171
  CampaignError,
10088
10172
  PURCHASE_ERROR_CODES as ERROR_CODES,
@@ -10117,6 +10201,7 @@ export {
10117
10201
  SessionError,
10118
10202
  SessionManager,
10119
10203
  SubscriptionCache,
10204
+ attributionTracker,
10120
10205
  createPurchaseError,
10121
10206
  index_default as default,
10122
10207
  detectDeviceLanguage,