@virex-tech/paywallo-sdk 2.2.3 → 2.2.5

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
  }),
@@ -2013,13 +2008,21 @@ var init_NativePushBridge = __esm({
2013
2008
  if (!raw || typeof raw !== "object") return null;
2014
2009
  const r = raw;
2015
2010
  const notification = r["notification"];
2011
+ const asString = (v) => typeof v === "string" ? v : void 0;
2016
2012
  return {
2017
- messageId: typeof r["messageId"] === "string" ? r["messageId"] : void 0,
2013
+ messageId: asString(r["messageId"]),
2018
2014
  data: typeof r["data"] === "object" && r["data"] !== null ? r["data"] : void 0,
2019
2015
  notification: notification && typeof notification === "object" ? {
2020
- title: typeof notification["title"] === "string" ? notification["title"] : void 0,
2021
- body: typeof notification["body"] === "string" ? notification["body"] : void 0
2016
+ title: asString(notification["title"]),
2017
+ body: asString(notification["body"]),
2018
+ imageUrl: asString(notification["imageUrl"])
2022
2019
  } : void 0,
2020
+ // Pass through top-level title/body/imageUrl emitted by the native modules
2021
+ // (both iOS and Android currently emit these at the top level rather than
2022
+ // nested under `notification`).
2023
+ title: asString(r["title"]),
2024
+ body: asString(r["body"]),
2025
+ imageUrl: asString(r["imageUrl"]),
2023
2026
  sentTime: typeof r["sentTime"] === "number" ? r["sentTime"] : void 0
2024
2027
  };
2025
2028
  } catch {
@@ -2158,10 +2161,11 @@ function setupForegroundHandler(deps, onReceive) {
2158
2161
  const data = message.data ?? {};
2159
2162
  const payload = extractPayload({
2160
2163
  ...data,
2161
- title: data["title"] ?? message.notification?.title ?? "",
2162
- body: data["body"] ?? message.notification?.body ?? ""
2164
+ title: data["title"] ?? message.notification?.title ?? message.title ?? "",
2165
+ body: data["body"] ?? message.notification?.body ?? message.body ?? "",
2166
+ imageUrl: data["imageUrl"] ?? message.notification?.imageUrl ?? message.imageUrl
2163
2167
  });
2164
- if (debug) console.warn("[Paywallo PUSH] foreground message received:", payload.notificationId);
2168
+ if (debug) console.log("[Paywallo PUSH] foreground message received:", payload.notificationId);
2165
2169
  onReceive(payload);
2166
2170
  const messageId = message.messageId ?? payload.notificationId;
2167
2171
  await tracker.track("notification_delivered", {
@@ -2192,13 +2196,20 @@ function getTimezone() {
2192
2196
  function detectPlatform() {
2193
2197
  return Platform5.OS === "ios" ? "ios" : "android";
2194
2198
  }
2195
- var SEEN_MESSAGES_KEY, SEEN_TTL_MS, MAX_SEEN_SIZE, NotificationEventTracker;
2199
+ var SEEN_MESSAGES_KEY, LEGACY_SEEN_MESSAGES_KEY, SEEN_TTL_MS, MAX_SEEN_SIZE, NOTIFICATION_TYPE_TO_FAMILY, NotificationEventTracker;
2196
2200
  var init_NotificationEventTracker = __esm({
2197
2201
  "src/domains/notifications/NotificationEventTracker.ts"() {
2198
2202
  "use strict";
2199
- SEEN_MESSAGES_KEY = "seen_messages";
2203
+ SEEN_MESSAGES_KEY = "@paywallo:seen_messages";
2204
+ LEGACY_SEEN_MESSAGES_KEY = "seen_messages";
2200
2205
  SEEN_TTL_MS = 60 * 60 * 1e3;
2201
2206
  MAX_SEEN_SIZE = 1e3;
2207
+ NOTIFICATION_TYPE_TO_FAMILY = {
2208
+ notification_delivered: "delivered",
2209
+ notification_displayed: "displayed",
2210
+ notification_clicked: "clicked",
2211
+ notification_dismissed: "dismissed"
2212
+ };
2202
2213
  NotificationEventTracker = class {
2203
2214
  constructor(batcher, config, secureStorage2) {
2204
2215
  this.batcher = batcher;
@@ -2219,9 +2230,13 @@ var init_NotificationEventTracker = __esm({
2219
2230
  if (this.config.debug) console.log("[Paywallo PUSH] skipped (no distinctId):", eventType);
2220
2231
  return;
2221
2232
  }
2233
+ const canonicalType = NOTIFICATION_TYPE_TO_FAMILY[eventType];
2234
+ if (!canonicalType) {
2235
+ if (this.config.debug) console.log("[Paywallo PUSH] eventType not mapped to family schema, skipping ingest:", eventType);
2236
+ return;
2237
+ }
2222
2238
  const properties = {
2223
- family: "notification",
2224
- type: eventType,
2239
+ type: canonicalType,
2225
2240
  notification_id: notificationId,
2226
2241
  campaign_id: campaignId,
2227
2242
  variant_key: variantKey ?? null,
@@ -2233,7 +2248,7 @@ var init_NotificationEventTracker = __esm({
2233
2248
  if (payload.failureReason) properties.failure_reason = payload.failureReason;
2234
2249
  if (payload.appUserId) properties.app_user_id = payload.appUserId;
2235
2250
  if (payload.properties) Object.assign(properties, payload.properties);
2236
- if (this.config.debug) console.log("[Paywallo PUSH] tracking event:", eventType, notificationId);
2251
+ if (this.config.debug) console.log("[Paywallo PUSH] tracking event:", canonicalType, notificationId);
2237
2252
  await this.batcher.trackEvent("notification", distinctId, properties);
2238
2253
  }
2239
2254
  async isDuplicate(messageId, eventType) {
@@ -2278,7 +2293,10 @@ var init_NotificationEventTracker = __esm({
2278
2293
  return;
2279
2294
  }
2280
2295
  try {
2281
- const raw = await this.secureStorage.get(SEEN_MESSAGES_KEY);
2296
+ let raw = await this.secureStorage.get(SEEN_MESSAGES_KEY);
2297
+ if (!raw) {
2298
+ raw = await this.secureStorage.get(LEGACY_SEEN_MESSAGES_KEY).catch(() => null);
2299
+ }
2282
2300
  if (raw) {
2283
2301
  const parsed = JSON.parse(raw);
2284
2302
  const now = Date.now();
@@ -2336,8 +2354,8 @@ async function getInitialNotificationPayload(bridge) {
2336
2354
  notificationId: String(data["notificationId"] ?? data["notification_id"] ?? ""),
2337
2355
  campaignId: String(data["campaignId"] ?? data["campaign_id"] ?? ""),
2338
2356
  variantKey: data["variantKey"] != null ? String(data["variantKey"]) : void 0,
2339
- title: String(data["title"] ?? message.notification?.title ?? ""),
2340
- body: String(data["body"] ?? message.notification?.body ?? ""),
2357
+ title: String(data["title"] ?? message.notification?.title ?? message.title ?? ""),
2358
+ body: String(data["body"] ?? message.notification?.body ?? message.body ?? ""),
2341
2359
  data
2342
2360
  };
2343
2361
  }
@@ -2352,8 +2370,8 @@ var init_NotificationHandlerSetup = __esm({
2352
2370
  // src/core/version.ts
2353
2371
  function resolveVersion() {
2354
2372
  try {
2355
- if ("2.2.3") {
2356
- return "2.2.3";
2373
+ if ("2.2.5") {
2374
+ return "2.2.5";
2357
2375
  }
2358
2376
  } catch {
2359
2377
  }
@@ -2485,7 +2503,7 @@ async function buildRegistration(token, getDeviceId, getDistinctId) {
2485
2503
  async function registerToken(deps, token) {
2486
2504
  const { httpClient, appKey, getDeviceId, getDistinctId, debug } = deps;
2487
2505
  if (!httpClient || !appKey) {
2488
- if (debug) console.warn("[Paywallo PUSH] httpClient not ready, skipping token registration");
2506
+ if (debug) console.log("[Paywallo PUSH] httpClient not ready, skipping token registration");
2489
2507
  return;
2490
2508
  }
2491
2509
  const registration = await buildRegistration(token, getDeviceId, getDistinctId);
@@ -2495,22 +2513,25 @@ async function registerToken(deps, token) {
2495
2513
  registration,
2496
2514
  { headers: { "X-App-Key": appKey } }
2497
2515
  );
2498
- if (debug) console.warn("[Paywallo PUSH] token registered successfully");
2516
+ if (debug) console.log("[Paywallo PUSH] token registered successfully");
2499
2517
  } catch (err) {
2500
- if (debug) console.warn("[Paywallo PUSH] token registration failed:", err);
2518
+ console.error("[Paywallo PUSH] token registration failed:", err);
2501
2519
  throw new RegistrationFailedError("Token registration failed", err);
2502
2520
  }
2503
2521
  }
2504
- async function unregisterToken(httpClient, appKey, debug) {
2522
+ async function unregisterToken(httpClient, appKey, debug, token, distinctId) {
2505
2523
  if (!httpClient || !appKey) return;
2524
+ if (!token || !distinctId) return;
2506
2525
  try {
2526
+ const body = JSON.stringify({ token, distinct_id: distinctId });
2507
2527
  await httpClient.request(TOKEN_ENDPOINT, {
2508
2528
  method: "DELETE",
2509
- headers: { "X-App-Key": appKey },
2529
+ headers: { "X-App-Key": appKey, "Content-Type": "application/json" },
2530
+ body,
2510
2531
  timeout: 1e4
2511
2532
  });
2512
2533
  } catch (err) {
2513
- if (debug) console.warn("[Paywallo PUSH] optOut error:", err);
2534
+ console.error("[Paywallo PUSH] optOut error:", err);
2514
2535
  }
2515
2536
  }
2516
2537
  var FCM_TOKEN_KEY, TOKEN_ENDPOINT;
@@ -2542,7 +2563,7 @@ async function registerAndPersistToken(deps, token) {
2542
2563
  }
2543
2564
  function subscribeTokenRefresh(bridge, deps, onToken) {
2544
2565
  return bridge.onTokenRefresh((newToken) => {
2545
- if (deps.debug) console.warn("[Paywallo PUSH] token refreshed");
2566
+ if (deps.debug) console.log("[Paywallo PUSH] token refreshed");
2546
2567
  onToken(newToken);
2547
2568
  persistToken(deps.secureStorage, newToken).catch(() => void 0);
2548
2569
  registerToken(
@@ -2558,8 +2579,8 @@ function subscribeTokenRefresh(bridge, deps, onToken) {
2558
2579
  ).catch(() => void 0);
2559
2580
  });
2560
2581
  }
2561
- async function optOutToken(httpClient, secureStorage2, appKey, debug) {
2562
- await unregisterToken(httpClient, appKey, debug);
2582
+ async function optOutToken(httpClient, secureStorage2, appKey, debug, token, distinctId) {
2583
+ await unregisterToken(httpClient, appKey, debug, token, distinctId);
2563
2584
  await clearPersistedToken(secureStorage2);
2564
2585
  }
2565
2586
  async function invalidateLocalTokenStorage(secureStorage2, debug) {
@@ -2579,7 +2600,7 @@ async function captureAndRegisterToken(bridge, deps, onTokenCaptured, subscribeR
2579
2600
  await waitForApnsToken(bridge, deps.debug);
2580
2601
  const token = await bridge.getToken();
2581
2602
  if (!token) {
2582
- if (deps.debug) console.warn("[Paywallo PUSH] token unavailable after grant");
2603
+ if (deps.debug) console.log("[Paywallo PUSH] token unavailable after grant");
2583
2604
  return;
2584
2605
  }
2585
2606
  onTokenCaptured(token);
@@ -2646,18 +2667,18 @@ var init_PermissionManager = __esm({
2646
2667
  }
2647
2668
  async requestPermission(options) {
2648
2669
  const platform = this.platformOverride ?? getPlatform();
2649
- if (this.debug) console.warn("[paywallo-push] platform detected:", platform.OS, "version:", String(platform.Version ?? "n/a"));
2670
+ if (this.debug) console.log("[paywallo-push] platform detected:", platform.OS, "version:", String(platform.Version ?? "n/a"));
2650
2671
  if (platform.OS === "ios") {
2651
- if (this.debug) console.warn("[paywallo-push] requestPermission iOS \u2014 calling bridge...");
2672
+ if (this.debug) console.log("[paywallo-push] requestPermission iOS \u2014 calling bridge...");
2652
2673
  try {
2653
2674
  const raw2 = await this.bridge.requestPermission(options);
2654
- if (this.debug) console.warn("[paywallo-push] bridge returned raw:", raw2);
2675
+ if (this.debug) console.log("[paywallo-push] bridge returned raw:", raw2);
2655
2676
  const status2 = mapPermissionStatus2(raw2);
2656
- if (this.debug) console.warn("[paywallo-push] mapped status:", status2);
2677
+ if (this.debug) console.log("[paywallo-push] mapped status:", status2);
2657
2678
  this.emitOsEvent(status2);
2658
2679
  return status2;
2659
2680
  } catch (err) {
2660
- if (this.debug) console.warn("[paywallo-push] requestPermission iOS CRASHED:", err instanceof Error ? err.message : String(err));
2681
+ console.error("[paywallo-push] requestPermission iOS CRASHED:", err instanceof Error ? err.message : String(err));
2661
2682
  throw err;
2662
2683
  }
2663
2684
  }
@@ -2790,11 +2811,11 @@ var init_NotificationsManager = __esm({
2790
2811
  await waitForApnsToken(this.bridge, this.debug);
2791
2812
  const token = await this.bridge.getToken();
2792
2813
  if (!token) {
2793
- if (this.debug) console.warn("[Paywallo PUSH] token unavailable, skipping registration");
2814
+ if (this.debug) console.log("[Paywallo PUSH] token unavailable, skipping registration");
2794
2815
  this.isInitialized = true;
2795
2816
  return;
2796
2817
  }
2797
- if (this.debug) console.warn("[Paywallo PUSH] token acquired:", token);
2818
+ if (this.debug) console.log("[Paywallo PUSH] token acquired:", token);
2798
2819
  this.currentToken = token;
2799
2820
  await this.doRegisterToken(token);
2800
2821
  this.subscribeTokenRefresh();
@@ -2812,7 +2833,7 @@ var init_NotificationsManager = __esm({
2812
2833
  async refreshPermissionStatus() {
2813
2834
  const permStatus = await this.bridge.hasPermission();
2814
2835
  this.permissionStatus = mapPermissionStatus(permStatus);
2815
- if (this.debug) console.warn("[Paywallo PUSH] permission status:", this.permissionStatus);
2836
+ if (this.debug) console.log("[Paywallo PUSH] permission status:", this.permissionStatus);
2816
2837
  }
2817
2838
  tokenDeps() {
2818
2839
  return {
@@ -2840,7 +2861,7 @@ var init_NotificationsManager = __esm({
2840
2861
  async optOut() {
2841
2862
  await optOutToken(this.httpClient, this.secureStorage, this.appKey, this.debug);
2842
2863
  this.currentToken = null;
2843
- if (this.debug) console.warn("[Paywallo PUSH] opted out");
2864
+ if (this.debug) console.log("[Paywallo PUSH] opted out");
2844
2865
  }
2845
2866
  /** Clears token locally without server call (used on sign-out/reset). */
2846
2867
  async invalidateLocalToken() {
@@ -2857,21 +2878,21 @@ var init_NotificationsManager = __esm({
2857
2878
  }
2858
2879
  /** Requests OS permission and, on success, captures + registers the push token. */
2859
2880
  async requestPushPermission(options) {
2860
- if (this.debug) console.warn("[Paywallo PUSH] requestPushPermission called");
2881
+ if (this.debug) console.log("[Paywallo PUSH] requestPushPermission called");
2861
2882
  try {
2862
2883
  const status = await this.permissionManager.requestPermission(options);
2863
- if (this.debug) console.warn("[Paywallo PUSH] permissionManager returned:", status);
2884
+ if (this.debug) console.log("[Paywallo PUSH] permissionManager returned:", status);
2864
2885
  this.permissionStatus = status;
2865
2886
  if (status === "granted" || status === "provisional") {
2866
- if (this.debug) console.warn("[Paywallo PUSH] permission granted \u2014 capturing token...");
2887
+ if (this.debug) console.log("[Paywallo PUSH] permission granted \u2014 capturing token...");
2867
2888
  await this.captureAndRegisterToken();
2868
- if (this.debug) console.warn("[Paywallo PUSH] token capture complete");
2889
+ if (this.debug) console.log("[Paywallo PUSH] token capture complete");
2869
2890
  } else {
2870
- if (this.debug) console.warn("[Paywallo PUSH] permission not granted, skipping token register:", status);
2891
+ if (this.debug) console.log("[Paywallo PUSH] permission not granted, skipping token register:", status);
2871
2892
  }
2872
2893
  return status;
2873
2894
  } catch (err) {
2874
- if (this.debug) console.warn("[Paywallo PUSH] requestPushPermission CRASHED:", err instanceof Error ? err.message : String(err));
2895
+ console.error("[Paywallo PUSH] requestPushPermission CRASHED:", err instanceof Error ? err.message : String(err));
2875
2896
  throw err;
2876
2897
  }
2877
2898
  }
@@ -3033,7 +3054,9 @@ function parseWebViewMessage(raw) {
3033
3054
  const safePayload = payload !== null && typeof payload === "object" ? {
3034
3055
  productId: typeof payload.productId === "string" ? String(payload.productId) : void 0,
3035
3056
  url: typeof payload.url === "string" ? String(payload.url) : void 0,
3036
- timestamp: typeof payload.timestamp === "number" ? Number(payload.timestamp) : void 0
3057
+ timestamp: typeof payload.timestamp === "number" ? Number(payload.timestamp) : void 0,
3058
+ variantKey: typeof payload.variantKey === "string" ? String(payload.variantKey) : void 0,
3059
+ style: HAPTIC_STYLES.has(String(payload.style)) ? String(payload.style) : typeof payload.style !== "undefined" ? "light" : void 0
3037
3060
  } : void 0;
3038
3061
  return {
3039
3062
  type: parsed.type,
@@ -3044,17 +3067,19 @@ function parseWebViewMessage(raw) {
3044
3067
  return null;
3045
3068
  }
3046
3069
  }
3047
- var ALLOWED_MESSAGE_TYPES;
3070
+ var HAPTIC_STYLES, ALLOWED_MESSAGE_TYPES;
3048
3071
  var init_parseWebViewMessage = __esm({
3049
3072
  "src/domains/paywall/parseWebViewMessage.ts"() {
3050
3073
  "use strict";
3074
+ HAPTIC_STYLES = /* @__PURE__ */ new Set(["light", "medium", "heavy"]);
3051
3075
  ALLOWED_MESSAGE_TYPES = /* @__PURE__ */ new Set([
3052
3076
  "purchase",
3053
3077
  "close",
3054
3078
  "restore",
3055
3079
  "select-product",
3056
3080
  "open-url",
3057
- "ready"
3081
+ "ready",
3082
+ "haptic"
3058
3083
  ]);
3059
3084
  }
3060
3085
  });
@@ -3096,7 +3121,7 @@ var init_PaywallErrorBoundary = __esm({
3096
3121
  return { hasError: true };
3097
3122
  }
3098
3123
  componentDidCatch(error) {
3099
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] Paywall render error:", error);
3124
+ console.error("[Paywallo] Paywall render error:", error);
3100
3125
  try {
3101
3126
  PaywalloClient.getApiClient()?.reportError("PAYWALL_RENDER_ERROR", error.message);
3102
3127
  } catch {
@@ -3263,7 +3288,9 @@ function buildPaywallInjectionScript(data) {
3263
3288
  craftData: data.craftData,
3264
3289
  products: data.products,
3265
3290
  primaryProductId: data.primaryProductId,
3266
- secondaryProductId: data.secondaryProductId
3291
+ secondaryProductId: data.secondaryProductId,
3292
+ currentLanguage: getCurrentLanguage(),
3293
+ defaultLanguage: getDefaultLanguage()
3267
3294
  }
3268
3295
  };
3269
3296
  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 +3298,43 @@ function buildPaywallInjectionScript(data) {
3271
3298
  function buildPurchaseStateScript(isPurchasing) {
3272
3299
  return `if(window.receiveNativeMessage){window.receiveNativeMessage({type:"purchaseState",data:{isPurchasing:${isPurchasing}}});}true;`;
3273
3300
  }
3301
+ function buildBackButtonScript(nonce = Date.now()) {
3302
+ return [
3303
+ `/*${nonce}*/`,
3304
+ "(function(){",
3305
+ "try{",
3306
+ "var h=window.__paywalloHandleBackPress;",
3307
+ "var consumed=typeof h==='function'?!!h():false;",
3308
+ "if(!consumed&&window.ReactNativeWebView){",
3309
+ 'window.ReactNativeWebView.postMessage(JSON.stringify({type:"close"}));',
3310
+ "}",
3311
+ "}catch(e){",
3312
+ "if(window.ReactNativeWebView){",
3313
+ 'window.ReactNativeWebView.postMessage(JSON.stringify({type:"close"}));',
3314
+ "}",
3315
+ "}",
3316
+ "})();true;"
3317
+ ].join("");
3318
+ }
3274
3319
  var init_paywallScripts = __esm({
3275
3320
  "src/domains/paywall/paywallScripts.ts"() {
3276
3321
  "use strict";
3322
+ init_localization();
3277
3323
  }
3278
3324
  });
3279
3325
 
3280
3326
  // src/domains/paywall/PaywallWebView.tsx
3281
3327
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3282
3328
  import {
3329
+ BackHandler,
3330
+ findNodeHandle,
3283
3331
  Linking as Linking2,
3284
3332
  NativeEventEmitter as NativeEventEmitter2,
3285
3333
  NativeModules as NativeModules6,
3334
+ Platform as Platform9,
3286
3335
  requireNativeComponent,
3287
3336
  StyleSheet,
3337
+ UIManager,
3288
3338
  View
3289
3339
  } from "react-native";
3290
3340
  import { jsx } from "react/jsx-runtime";
@@ -3306,6 +3356,24 @@ function getWebViewEmitter() {
3306
3356
  webViewEmitter = new NativeEventEmitter2(mod);
3307
3357
  return webViewEmitter;
3308
3358
  }
3359
+ async function triggerNativeHaptic(style) {
3360
+ try {
3361
+ const module = await import("react-native-haptic-feedback");
3362
+ const hapticModule = module.default ?? module;
3363
+ const trigger = hapticModule.trigger;
3364
+ if (typeof trigger !== "function") return;
3365
+ const typeMap = {
3366
+ light: "impactLight",
3367
+ medium: "impactMedium",
3368
+ heavy: "impactHeavy"
3369
+ };
3370
+ trigger(typeMap[style], {
3371
+ enableVibrateFallback: true,
3372
+ ignoreAndroidSystemSettings: false
3373
+ });
3374
+ } catch {
3375
+ }
3376
+ }
3309
3377
  function parseErrorEvent(raw) {
3310
3378
  return {
3311
3379
  code: typeof raw["code"] === "number" ? raw["code"] : -1,
@@ -3330,6 +3398,7 @@ function PaywallWebView({
3330
3398
  const paywallDataSent = useRef(false);
3331
3399
  const loadEndReceived = useRef(false);
3332
3400
  const [scriptToInject, setScriptToInject] = useState(void 0);
3401
+ const webViewRef = useRef(null);
3333
3402
  const onErrorRef = useRef(onError);
3334
3403
  onErrorRef.current = onError;
3335
3404
  const onReadyRef = useRef(onReady);
@@ -3373,6 +3442,15 @@ function PaywallWebView({
3373
3442
  });
3374
3443
  }
3375
3444
  }, []);
3445
+ const injectScript = useCallback((script) => {
3446
+ if (Platform9.OS === "android") {
3447
+ const handle = findNodeHandle(webViewRef.current);
3448
+ if (handle == null) return;
3449
+ UIManager.dispatchViewManagerCommand(handle, "injectJavaScript", [script]);
3450
+ return;
3451
+ }
3452
+ setScriptToInject(script);
3453
+ }, []);
3376
3454
  const buildAndSetInjectionScript = useCallback(() => {
3377
3455
  if (!craftData || paywallDataSent.current) return;
3378
3456
  paywallDataSent.current = true;
@@ -3382,8 +3460,8 @@ function PaywallWebView({
3382
3460
  primaryProductId,
3383
3461
  secondaryProductId
3384
3462
  });
3385
- setScriptToInject(script);
3386
- }, [craftData, products, primaryProductId, secondaryProductId]);
3463
+ injectScript(script);
3464
+ }, [craftData, products, primaryProductId, secondaryProductId, injectScript]);
3387
3465
  const processedIdsRef = useRef(/* @__PURE__ */ new Set());
3388
3466
  const dedupeTimersRef = useRef(/* @__PURE__ */ new Map());
3389
3467
  useEffect(() => {
@@ -3420,6 +3498,9 @@ function PaywallWebView({
3420
3498
  case "restore":
3421
3499
  onRestore();
3422
3500
  break;
3501
+ case "select-product":
3502
+ console.log("[Paywallo:PaywallWebView] select-product received", message.payload?.productId);
3503
+ break;
3423
3504
  case "open-url":
3424
3505
  if (message.payload?.url) {
3425
3506
  const url = message.payload.url;
@@ -3428,6 +3509,9 @@ function PaywallWebView({
3428
3509
  }
3429
3510
  }
3430
3511
  break;
3512
+ case "haptic":
3513
+ void triggerNativeHaptic(message.payload?.style ?? "light");
3514
+ break;
3431
3515
  }
3432
3516
  },
3433
3517
  [buildAndSetInjectionScript, onPurchase, onClose, onRestore]
@@ -3499,13 +3583,24 @@ function PaywallWebView({
3499
3583
  );
3500
3584
  useEffect(() => {
3501
3585
  if (!paywallDataSent.current) return;
3502
- setScriptToInject(buildPurchaseStateScript(isPurchasing));
3503
- }, [isPurchasing]);
3586
+ injectScript(buildPurchaseStateScript(isPurchasing));
3587
+ }, [isPurchasing, injectScript]);
3588
+ useEffect(() => {
3589
+ if (Platform9.OS !== "android") return;
3590
+ const onBackPress = () => {
3591
+ if (!paywallDataSent.current) return false;
3592
+ injectScript(buildBackButtonScript());
3593
+ return true;
3594
+ };
3595
+ const subscription = BackHandler.addEventListener("hardwareBackPress", onBackPress);
3596
+ return () => subscription.remove();
3597
+ }, [injectScript]);
3504
3598
  if (!resolvedWebUrl || !NativeWebView) return null;
3505
3599
  const paywallUrl = `${resolvedWebUrl}/paywall/${paywallId}`;
3506
3600
  return /* @__PURE__ */ jsx(View, { style: styles.container, children: /* @__PURE__ */ jsx(
3507
3601
  NativeWebView,
3508
3602
  {
3603
+ ref: webViewRef,
3509
3604
  sourceUrl: paywallUrl,
3510
3605
  injectedJavaScript: scriptToInject,
3511
3606
  onMessage: handleMessage,
@@ -3588,7 +3683,7 @@ __export(NativeStoreKit_exports, {
3588
3683
  NativeStoreKit: () => NativeStoreKit,
3589
3684
  nativeStoreKit: () => nativeStoreKit
3590
3685
  });
3591
- import { NativeEventEmitter as NativeEventEmitter3, NativeModules as NativeModules7, Platform as Platform9 } from "react-native";
3686
+ import { NativeEventEmitter as NativeEventEmitter3, NativeModules as NativeModules7, Platform as Platform10 } from "react-native";
3592
3687
  function isAvailable3() {
3593
3688
  return PaywalloStoreKitNative != null;
3594
3689
  }
@@ -3661,7 +3756,7 @@ function mapNativePurchase(native) {
3661
3756
  transactionId: native.transactionId,
3662
3757
  transactionDate: native.transactionDate,
3663
3758
  receipt: native.receipt,
3664
- platform: Platform9.OS
3759
+ platform: Platform10.OS
3665
3760
  };
3666
3761
  }
3667
3762
  function normalizeTransactionUpdate(raw) {
@@ -3732,7 +3827,7 @@ var init_NativeStoreKit = __esm({
3732
3827
  try {
3733
3828
  await PaywalloStoreKitNative.finishTransaction(transactionId);
3734
3829
  } catch (err) {
3735
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] finishTransaction native error", err);
3830
+ console.error("[Paywallo] finishTransaction native error", err);
3736
3831
  }
3737
3832
  },
3738
3833
  async getActiveTransactions() {
@@ -3741,7 +3836,7 @@ var init_NativeStoreKit = __esm({
3741
3836
  const results = await PaywalloStoreKitNative.getActiveTransactions();
3742
3837
  return results.map(mapNativePurchase);
3743
3838
  } catch (err) {
3744
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] getActiveTransactions failed", err);
3839
+ console.error("[Paywallo] getActiveTransactions failed", err);
3745
3840
  return [];
3746
3841
  }
3747
3842
  },
@@ -4592,7 +4687,7 @@ var init_QueueProcessor = __esm({
4592
4687
  }
4593
4688
  const allItems = offlineQueue.getQueue();
4594
4689
  const eventItems = allItems.filter(
4595
- (i) => (i.url.includes("/events") || i.url.includes("/ingest")) && !i.url.includes("/batch")
4690
+ (i) => i.url.includes("/events") || i.url.includes("/ingest")
4596
4691
  );
4597
4692
  const eventItemIds = new Set(eventItems.map((i) => i.id));
4598
4693
  const { processed: batchProcessed, failed: batchFailed, retryIds } = await this.processEventBatches(eventItems);
@@ -4632,12 +4727,24 @@ var init_QueueProcessor = __esm({
4632
4727
  const freshHeaders = this.config.getFreshHeaders?.() ?? {};
4633
4728
  const headers = { ...persistedHeaders, ...freshHeaders };
4634
4729
  this.log("Sending event batch", { size: batch.length, batchIndex: Math.floor(i / BATCH_SIZE) });
4635
- const response = await this.httpClient.request("/sdk/events/batch", {
4636
- method: "POST",
4637
- body: { events: bodies },
4638
- headers,
4639
- skipRetry: false
4640
- });
4730
+ let response;
4731
+ try {
4732
+ response = await this.httpClient.request("/sdk/ingest/batch", {
4733
+ method: "POST",
4734
+ body: { events: bodies },
4735
+ headers,
4736
+ skipRetry: false
4737
+ });
4738
+ } catch (error) {
4739
+ for (const item of batch) {
4740
+ retryIds.add(item.id);
4741
+ }
4742
+ this.log("Event batch threw network error - will retry", {
4743
+ size: batch.length,
4744
+ error: error instanceof Error ? error.message : String(error)
4745
+ });
4746
+ continue;
4747
+ }
4641
4748
  if (response.ok) {
4642
4749
  for (const item of batch) {
4643
4750
  await offlineQueue.removeItem(item.id);
@@ -4677,12 +4784,21 @@ var init_QueueProcessor = __esm({
4677
4784
  });
4678
4785
  const persistedHeaders = item.headers ?? {};
4679
4786
  const freshHeaders = this.config.getFreshHeaders?.() ?? {};
4680
- const response = await this.httpClient.request(item.url, {
4681
- method: item.method,
4682
- body: item.body,
4683
- headers: { ...persistedHeaders, ...freshHeaders },
4684
- skipRetry: false
4685
- });
4787
+ let response;
4788
+ try {
4789
+ response = await this.httpClient.request(item.url, {
4790
+ method: item.method,
4791
+ body: item.body,
4792
+ headers: { ...persistedHeaders, ...freshHeaders },
4793
+ skipRetry: false
4794
+ });
4795
+ } catch (error) {
4796
+ this.log("Request threw network error - will retry", {
4797
+ id: item.id,
4798
+ error: error instanceof Error ? error.message : String(error)
4799
+ });
4800
+ return { success: false, permanent: false };
4801
+ }
4686
4802
  if (response.ok) {
4687
4803
  this.log("Queue item processed successfully", { id: item.id });
4688
4804
  return { success: true, permanent: false };
@@ -4757,7 +4873,7 @@ var init_queue = __esm({
4757
4873
  });
4758
4874
 
4759
4875
  // src/domains/iap/IAPValidator.ts
4760
- import { Platform as Platform10 } from "react-native";
4876
+ import { Platform as Platform11 } from "react-native";
4761
4877
  var IAPValidator;
4762
4878
  var init_IAPValidator = __esm({
4763
4879
  "src/domains/iap/IAPValidator.ts"() {
@@ -4787,7 +4903,7 @@ var init_IAPValidator = __esm({
4787
4903
  async validateWithServer(purchase, productId, options) {
4788
4904
  const apiClient = this.getApiClientFn();
4789
4905
  const product = this.getProductFromCache(productId);
4790
- const platform = Platform10.OS;
4906
+ const platform = Platform11.OS;
4791
4907
  const distinctId = PaywalloClient.getDistinctId();
4792
4908
  return this.validateWithRetry(
4793
4909
  () => apiClient.validatePurchase(
@@ -4822,24 +4938,24 @@ var init_IAPValidator = __esm({
4822
4938
  try {
4823
4939
  const apiClient = this.getApiClientFn();
4824
4940
  const product = this.getProductFromCache(productId);
4825
- const platform = Platform10.OS;
4941
+ const platform = Platform11.OS;
4826
4942
  const distinctId = PaywalloClient.getDistinctId();
4827
4943
  const appKey = apiClient.getAppKey();
4828
4944
  const body = {
4829
4945
  platform,
4830
- receipt: purchase.receipt,
4831
- productId: purchase.productId,
4832
- transactionId: purchase.transactionId,
4833
- priceLocal: product?.priceValue ?? 0,
4946
+ receipt_data: purchase.receipt,
4947
+ product_id: purchase.productId,
4948
+ transaction_id: purchase.transactionId,
4949
+ price_local: product?.priceValue ?? 0,
4834
4950
  currency: product?.currency ?? "USD",
4835
4951
  country: this.getDeviceCountry(),
4836
- distinctId
4952
+ distinct_id: distinctId
4837
4953
  };
4838
- if (options?.paywallPlacement !== void 0) body.paywallPlacement = options.paywallPlacement;
4839
- if (options?.variantKey !== void 0) body.variantKey = options.variantKey;
4954
+ if (options?.paywallPlacement !== void 0) body.paywall_placement = options.paywallPlacement;
4955
+ if (options?.variantKey !== void 0) body.variant_key = options.variantKey;
4840
4956
  await offlineQueue.enqueue(
4841
4957
  "POST",
4842
- "/purchases/validate",
4958
+ "/sdk/purchases/validate",
4843
4959
  body,
4844
4960
  { "X-App-Key": appKey },
4845
4961
  `validate:${purchase.transactionId}`,
@@ -4847,7 +4963,7 @@ var init_IAPValidator = __esm({
4847
4963
  );
4848
4964
  if (this.getDebug()) console.log("[Paywallo PURCHASE] validation retry enqueued", purchase.transactionId);
4849
4965
  } catch (err) {
4850
- if (this.getDebug()) console.error("[Paywallo PURCHASE] failed to enqueue validation retry", err);
4966
+ console.error("[Paywallo PURCHASE] failed to enqueue validation retry", err);
4851
4967
  }
4852
4968
  }
4853
4969
  };
@@ -4855,7 +4971,7 @@ var init_IAPValidator = __esm({
4855
4971
  });
4856
4972
 
4857
4973
  // src/domains/iap/IAPService.ts
4858
- import { Platform as Platform11 } from "react-native";
4974
+ import { Platform as Platform12 } from "react-native";
4859
4975
  function getIAPService() {
4860
4976
  if (!_instance) {
4861
4977
  _instance = new IAPService();
@@ -4926,7 +5042,7 @@ var init_IAPService = __esm({
4926
5042
  }
4927
5043
  return products;
4928
5044
  } catch (err) {
4929
- if (this.debug) console.error("[Paywallo] loadProducts failed", err);
5045
+ console.error("[Paywallo] loadProducts failed", err);
4930
5046
  return [];
4931
5047
  }
4932
5048
  }
@@ -4945,7 +5061,7 @@ var init_IAPService = __esm({
4945
5061
  await nativeStoreKit.finishTransaction(transactionId);
4946
5062
  this.finishedTransactions.add(transactionId);
4947
5063
  } catch (err) {
4948
- if (this.debug) console.error("[Paywallo] finishTransaction failed", err);
5064
+ console.error("[Paywallo] finishTransaction failed", err);
4949
5065
  }
4950
5066
  }
4951
5067
  startTransactionListener() {
@@ -4994,7 +5110,7 @@ var init_IAPService = __esm({
4994
5110
  await this.finishTransaction(purchase.transactionId);
4995
5111
  return;
4996
5112
  }
4997
- if (this.debug) console.error("[Paywallo PURCHASE] background validation failed (network) \u2014 queued for retry", err);
5113
+ console.error("[Paywallo PURCHASE] background validation failed (network) \u2014 queued for retry", err);
4998
5114
  await this.validator.enqueueValidationRetry(purchase, productId, {
4999
5115
  paywallPlacement: options?.paywallPlacement,
5000
5116
  variantKey: options?.variantKey
@@ -5002,7 +5118,7 @@ var init_IAPService = __esm({
5002
5118
  });
5003
5119
  return { success: true, purchase };
5004
5120
  } catch (error) {
5005
- if (this.debug) console.error(`[Paywallo PURCHASE] ${productId} failed`, error);
5121
+ console.error(`[Paywallo PURCHASE] ${productId} failed`, error);
5006
5122
  const e = error instanceof PurchaseError ? error : new PurchaseError(PURCHASE_ERROR_CODES.PURCHASE_FAILED, error instanceof Error ? error.message : String(error));
5007
5123
  return { success: false, error: e };
5008
5124
  } finally {
@@ -5015,7 +5131,7 @@ var init_IAPService = __esm({
5015
5131
  const transactions = await nativeStoreKit.getActiveTransactions();
5016
5132
  const apiClient = this.getApiClient();
5017
5133
  const distinctId = PaywalloClient.getDistinctId();
5018
- const platform = Platform11.OS;
5134
+ const platform = Platform12.OS;
5019
5135
  const results = await Promise.allSettled(
5020
5136
  transactions.map((tx) => {
5021
5137
  const product = this.productsCache.get(tx.productId);
@@ -5046,7 +5162,7 @@ var init_IAPService = __esm({
5046
5162
  if (this.debug) console.log("[Paywallo RESTORE] validation rejected (4xx) \u2014 dropping", tx.transactionId);
5047
5163
  await this.finishTransaction(tx.transactionId);
5048
5164
  } else {
5049
- if (this.debug) console.error("[Paywallo RESTORE] validation failed (network) \u2014 queued for retry", r.reason);
5165
+ console.error("[Paywallo RESTORE] validation failed (network) \u2014 queued for retry", r.reason);
5050
5166
  await this.validator.enqueueValidationRetry(tx, tx.productId);
5051
5167
  }
5052
5168
  } else if (r.status === "fulfilled" && !r.value.success) {
@@ -5056,7 +5172,7 @@ var init_IAPService = __esm({
5056
5172
  }
5057
5173
  return validated;
5058
5174
  } catch (err) {
5059
- if (this.debug) console.error("[Paywallo] restore failed", err);
5175
+ console.error("[Paywallo] restore failed", err);
5060
5176
  return [];
5061
5177
  }
5062
5178
  }
@@ -5272,7 +5388,7 @@ function usePaywallLoader(placement, visible, preloadedConfig, preloadedProducts
5272
5388
  setProducts(map);
5273
5389
  }
5274
5390
  } catch (err) {
5275
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] loadProducts in paywall failed", err);
5391
+ console.error("[Paywallo] loadProducts in paywall failed", err);
5276
5392
  if (!cancelled) setProducts(/* @__PURE__ */ new Map());
5277
5393
  }
5278
5394
  } else {
@@ -6406,11 +6522,9 @@ var init_SubscriptionManager = __esm({
6406
6522
  if (!this.config) {
6407
6523
  return this.getEmptyStatus();
6408
6524
  }
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(), {
6525
+ const userId = this.userId ?? "";
6526
+ const url = `${this.config.serverUrl}/sdk/purchases/status?distinctId=${encodeURIComponent(userId)}`;
6527
+ const response = await fetch(url, {
6414
6528
  method: "GET",
6415
6529
  headers: {
6416
6530
  "Content-Type": "application/json",
@@ -6423,17 +6537,32 @@ var init_SubscriptionManager = __esm({
6423
6537
  `Failed to fetch subscription status: ${response.status}`
6424
6538
  );
6425
6539
  }
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
- }
6540
+ const envelope = await response.json();
6541
+ const raw = envelope.data ?? envelope;
6542
+ const data = {
6543
+ hasActiveSubscription: !!(raw["has_active_subscription"] ?? raw["hasActiveSubscription"]),
6544
+ subscription: null,
6545
+ entitlements: raw["entitlements"] ?? []
6546
+ };
6547
+ const sub = raw["subscription"];
6548
+ if (sub) {
6549
+ const cancellationRaw = sub["cancellationDate"] ?? sub["cancellation_date"];
6550
+ const gracePeriodRaw = sub["gracePeriodExpiresAt"] ?? sub["grace_period_expires_at"];
6551
+ data.subscription = {
6552
+ id: sub["id"],
6553
+ productId: sub["product_id"] ?? sub["productId"],
6554
+ status: sub["status"],
6555
+ expiresAt: new Date(sub["expires_at"] ?? sub["expiresAt"]),
6556
+ originalPurchaseDate: new Date(sub["originalPurchaseDate"] ?? sub["original_purchase_date"]),
6557
+ latestPurchaseDate: new Date(sub["latestPurchaseDate"] ?? sub["latest_purchase_date"]),
6558
+ willRenew: !!(sub["will_renew"] ?? sub["willRenew"] ?? sub["auto_renew_enabled"] ?? sub["autoRenewEnabled"]),
6559
+ isTrial: !!(sub["is_trial"] ?? sub["isTrial"]),
6560
+ isIntroductoryPeriod: !!(sub["is_introductory_period"] ?? sub["isIntroductoryPeriod"]),
6561
+ platform: sub["platform"],
6562
+ storeTransactionId: sub["store_transaction_id"] ?? sub["storeTransactionId"],
6563
+ cancellationDate: cancellationRaw ? new Date(cancellationRaw) : void 0,
6564
+ gracePeriodExpiresAt: gracePeriodRaw ? new Date(gracePeriodRaw) : void 0
6565
+ };
6437
6566
  }
6438
6567
  return data;
6439
6568
  }
@@ -6471,15 +6600,15 @@ async function validatePurchase(client, appKey, platform, receipt, productId, tr
6471
6600
  "/sdk/purchases/validate",
6472
6601
  {
6473
6602
  platform,
6474
- receipt,
6475
- productId,
6476
- transactionId,
6477
- priceLocal,
6603
+ receipt_data: receipt,
6604
+ product_id: productId,
6605
+ transaction_id: transactionId,
6606
+ price_local: priceLocal,
6478
6607
  currency,
6479
6608
  country,
6480
- distinctId,
6481
- ...paywallPlacement !== void 0 && { paywallPlacement },
6482
- ...variantKey !== void 0 && { variantKey }
6609
+ distinct_id: distinctId,
6610
+ ...paywallPlacement !== void 0 && { paywall_placement: paywallPlacement },
6611
+ ...variantKey !== void 0 && { variant_key: variantKey }
6483
6612
  },
6484
6613
  false
6485
6614
  );
@@ -6491,7 +6620,7 @@ async function validatePurchase(client, appKey, platform, receipt, productId, tr
6491
6620
  response.status
6492
6621
  );
6493
6622
  }
6494
- return response.data;
6623
+ return response.data.data;
6495
6624
  }
6496
6625
  async function getSubscriptionStatus(client, baseUrl, appKey, distinctId) {
6497
6626
  const url = new URL(`${baseUrl}/sdk/purchases/status`);
@@ -6500,7 +6629,7 @@ async function getSubscriptionStatus(client, baseUrl, appKey, distinctId) {
6500
6629
  if (!response.ok) {
6501
6630
  throw new PurchaseError(PURCHASE_ERROR_CODES.NETWORK_ERROR, `Subscription status check failed: HTTP ${response.status}`);
6502
6631
  }
6503
- return response.data;
6632
+ return response.data.data;
6504
6633
  }
6505
6634
  async function getEmergencyPaywall(client) {
6506
6635
  try {
@@ -6709,7 +6838,7 @@ async function evaluateFlags2(deps, keys, distinctId) {
6709
6838
  const result = {};
6710
6839
  for (const key of Object.keys(record)) {
6711
6840
  const value = record[key];
6712
- const variant = typeof value === "boolean" ? String(value) : typeof value === "string" ? value : null;
6841
+ const variant = typeof value === "string" ? value : null;
6713
6842
  result[key] = variant;
6714
6843
  const flagVariant = { variant };
6715
6844
  deps.cache.setVariant(`${key}:${distinctId}`, flagVariant);
@@ -6947,7 +7076,7 @@ var init_eventFamilies = __esm({
6947
7076
  });
6948
7077
 
6949
7078
  // src/core/eventEnvelopeV2.ts
6950
- import { Platform as Platform12 } from "react-native";
7079
+ import { Platform as Platform13 } from "react-native";
6951
7080
  import { z as z2 } from "zod";
6952
7081
  function buildV2Envelope(events, providerContext) {
6953
7082
  const context = { ...providerContext };
@@ -6971,7 +7100,7 @@ function buildV2Envelope(events, providerContext) {
6971
7100
  };
6972
7101
  });
6973
7102
  if (context.sdk_version === void 0) context.sdk_version = SDK_VERSION;
6974
- if (context.platform === void 0) context.platform = Platform12.OS;
7103
+ if (context.platform === void 0) context.platform = Platform13.OS;
6975
7104
  if (context.distinct_id === void 0 && events.length > 0) {
6976
7105
  context.distinct_id = events[0].distinctId;
6977
7106
  }
@@ -7022,7 +7151,7 @@ var init_eventEnvelopeV2 = __esm({
7022
7151
  payload: z2.record(z2.string(), z2.unknown())
7023
7152
  });
7024
7153
  v2ContextSchema = z2.object({
7025
- distinct_id: z2.string().optional(),
7154
+ distinct_id: z2.string().min(1),
7026
7155
  session_id: z2.string().optional(),
7027
7156
  device_id: z2.string().optional(),
7028
7157
  app_version: z2.string().optional(),
@@ -7065,8 +7194,8 @@ var init_events = __esm({
7065
7194
  });
7066
7195
 
7067
7196
  // 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;
7197
+ import { Platform as Platform14 } from "react-native";
7198
+ var BATCH_MAX_SIZE, BATCH_FLUSH_MS, EVENT_NAME_REGEX, V2_BATCH_ENDPOINT, EventBatcher;
7070
7199
  var init_EventBatcher = __esm({
7071
7200
  "src/core/EventBatcher.ts"() {
7072
7201
  "use strict";
@@ -7076,15 +7205,12 @@ var init_EventBatcher = __esm({
7076
7205
  BATCH_FLUSH_MS = 1e4;
7077
7206
  EVENT_NAME_REGEX = /^(\$[a-zA-Z0-9_]+|[a-z][a-z0-9_]*)$/;
7078
7207
  V2_BATCH_ENDPOINT = "/sdk/ingest/batch";
7079
- V1_BATCH_ENDPOINT = "/sdk/events/batch";
7080
- V1_SINGLE_ENDPOINT = "/sdk/events";
7081
7208
  EventBatcher = class {
7082
7209
  constructor(post, debug = false, contextProvider = null) {
7083
7210
  this.criticalQueue = [];
7084
7211
  this.normalQueue = [];
7085
7212
  this.normalTimer = null;
7086
7213
  this.criticalScheduled = false;
7087
- this.v2Disabled = false;
7088
7214
  this.post = post;
7089
7215
  this.debug = debug;
7090
7216
  this.contextProvider = contextProvider;
@@ -7121,7 +7247,7 @@ var init_EventBatcher = __esm({
7121
7247
  const event = {
7122
7248
  eventName,
7123
7249
  distinctId,
7124
- properties: { ...properties, platform: Platform13.OS },
7250
+ properties: { ...properties, platform: Platform14.OS },
7125
7251
  timestamp: timestamp ?? Date.now(),
7126
7252
  environment: environment.toLowerCase()
7127
7253
  };
@@ -7180,11 +7306,7 @@ var init_EventBatcher = __esm({
7180
7306
  try {
7181
7307
  await this.postBatch([event], `event_critical:${event.eventName}`, "critical");
7182
7308
  } 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
- }
7309
+ if (this.debug) console.log(`[Paywallo] critical event ${event.eventName} post failed`);
7188
7310
  }
7189
7311
  }
7190
7312
  }
@@ -7198,50 +7320,24 @@ var init_EventBatcher = __esm({
7198
7320
  try {
7199
7321
  await this.postBatch(batch, `event_batch:${batch.length}`);
7200
7322
  } catch {
7201
- for (const event of batch) {
7202
- await this.post(V1_SINGLE_ENDPOINT, event, `event:${event.eventName}`);
7203
- }
7323
+ if (this.debug) console.log(`[Paywallo] normal batch post failed \u2014 OfflineQueue will handle durability`);
7204
7324
  }
7205
7325
  }
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
7326
  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
- }
7327
+ const providerContext = resolveProviderContext(this.contextProvider);
7328
+ const inputs = events.map((e) => ({
7329
+ eventName: e.eventName,
7330
+ distinctId: e.distinctId,
7331
+ properties: e.properties,
7332
+ timestamp: e.timestamp
7333
+ }));
7334
+ const envelope = buildV2Envelope(inputs, providerContext);
7335
+ const parsed = v2EnvelopeSchema.safeParse(envelope);
7336
+ if (!parsed.success) {
7337
+ if (this.debug) console.log("[Paywallo EVENTS] V2 envelope schema validation failed:", parsed.error.issues);
7338
+ throw parsed.error;
7243
7339
  }
7244
- await this.post(V1_BATCH_ENDPOINT, { events }, label, priority);
7340
+ await this.post(V2_BATCH_ENDPOINT, parsed.data, label, priority);
7245
7341
  }
7246
7342
  };
7247
7343
  }
@@ -7476,7 +7572,7 @@ var init_http = __esm({
7476
7572
  });
7477
7573
 
7478
7574
  // src/core/ApiClient.ts
7479
- import { Platform as Platform14 } from "react-native";
7575
+ import { Platform as Platform15 } from "react-native";
7480
7576
  var ApiClient;
7481
7577
  var init_ApiClient = __esm({
7482
7578
  "src/core/ApiClient.ts"() {
@@ -7582,7 +7678,7 @@ var init_ApiClient = __esm({
7582
7678
  buildSdkHeaders() {
7583
7679
  return {
7584
7680
  "x-sdk-version": SDK_VERSION,
7585
- "x-sdk-platform": Platform14.OS,
7681
+ "x-sdk-platform": Platform15.OS,
7586
7682
  "x-sdk-environment": this.environment
7587
7683
  };
7588
7684
  }
@@ -7612,7 +7708,7 @@ var init_ApiClient = __esm({
7612
7708
  message,
7613
7709
  context,
7614
7710
  sdkVersion: SDK_VERSION,
7615
- platform: Platform14.OS
7711
+ platform: Platform15.OS
7616
7712
  }, true);
7617
7713
  } catch (err) {
7618
7714
  if (this.debug) console.log("[Paywallo:ApiClient] reportError failed", err);
@@ -7628,6 +7724,26 @@ var init_ApiClient = __esm({
7628
7724
  this.batcher.dispose();
7629
7725
  }
7630
7726
  async identify(distinctId, properties, email, deviceId, pii) {
7727
+ const rawTraits = {
7728
+ email: email || void 0,
7729
+ platform: Platform15.OS,
7730
+ name: properties?.name,
7731
+ country: properties?.country,
7732
+ locale: properties?.locale,
7733
+ app_version: properties?.appVersion ?? properties?.["app_version"]
7734
+ };
7735
+ const traits = Object.fromEntries(
7736
+ Object.entries(rawTraits).filter(([, v]) => v !== void 0)
7737
+ );
7738
+ const attributionKeys = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content", "fbclid", "gclid", "ttclid", "referrer"];
7739
+ const rawAttribution = {};
7740
+ if (properties) {
7741
+ for (const key of attributionKeys) {
7742
+ const val = properties[key];
7743
+ if (val !== void 0) rawAttribution[key] = val;
7744
+ }
7745
+ }
7746
+ const attribution = Object.keys(rawAttribution).length > 0 ? rawAttribution : void 0;
7631
7747
  await postWithQueue(
7632
7748
  {
7633
7749
  isOfflineQueueEnabled: () => this.offlineQueueEnabled,
@@ -7635,12 +7751,11 @@ var init_ApiClient = __esm({
7635
7751
  isDebug: () => this.debug,
7636
7752
  post: (path, body, skipRetry) => this.post(path, body, skipRetry)
7637
7753
  },
7638
- "/sdk/identify",
7754
+ "/sdk/identity/identify",
7639
7755
  {
7640
- distinctId,
7641
- properties: properties ?? {},
7642
- email,
7643
- platform: Platform14.OS,
7756
+ distinct_id: distinctId,
7757
+ traits,
7758
+ ...attribution && { attribution },
7644
7759
  ...deviceId && { deviceId },
7645
7760
  ...pii?.phone && { phone: pii.phone },
7646
7761
  ...pii?.firstName && { firstName: pii.firstName },
@@ -7651,10 +7766,9 @@ var init_ApiClient = __esm({
7651
7766
  "identify"
7652
7767
  );
7653
7768
  }
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.
7769
+ // Session lifecycle endpoints were dropped. All session bookkeeping is now
7770
+ // derived on the server from `$session_start` and `session.end` events that
7771
+ // flow through the unified EventBatcher pipeline.
7658
7772
  async getVariant(flagKey, distinctId) {
7659
7773
  return getVariant2(this.flagDeps(), flagKey, distinctId);
7660
7774
  }
@@ -7712,7 +7826,7 @@ var init_ApiClient = __esm({
7712
7826
  const hit = this.cache.getCampaign(key);
7713
7827
  if (hit !== void 0) return hit;
7714
7828
  try {
7715
- const res = await this.get(`/sdk/campaigns/public/primary?distinctId=${encodeURIComponent(distinctId)}`);
7829
+ const res = await this.get(`/campaigns/public/primary?distinctId=${encodeURIComponent(distinctId)}`);
7716
7830
  if (res.status === 404) {
7717
7831
  this.cache.setCampaign(key, null, this.cache.nullTTL);
7718
7832
  return null;
@@ -7812,10 +7926,10 @@ function setupNotifications(apiClient, config, environment, eventPipeline) {
7812
7926
  debug: config.debug,
7813
7927
  environment: environment === "Sandbox" ? "sandbox" : "production"
7814
7928
  }).catch((err) => {
7815
- if (config.debug) console.warn("[Paywallo PUSH] initialization warning:", err instanceof Error ? err.message : String(err));
7929
+ console.error("[Paywallo PUSH] initialization warning:", err instanceof Error ? err.message : String(err));
7816
7930
  });
7817
7931
  } catch (err) {
7818
- if (config.debug) console.warn("[Paywallo PUSH] setup warning:", err instanceof Error ? err.message : String(err));
7932
+ console.error("[Paywallo PUSH] setup warning:", err instanceof Error ? err.message : String(err));
7819
7933
  }
7820
7934
  }
7821
7935
  function setupAutoPreload(state, apiClient, config, distinctId) {
@@ -7940,7 +8054,7 @@ function reportInitFailure(state, config, error, reason) {
7940
8054
  error: error instanceof Error ? error.message : String(error)
7941
8055
  });
7942
8056
  } catch (err) {
7943
- if (config.debug) console.error("[Paywallo] reportInitFailure error", err);
8057
+ console.error("[Paywallo] reportInitFailure error", err);
7944
8058
  }
7945
8059
  }
7946
8060
  function reportInitSuccess(state, config, attempts) {
@@ -7949,7 +8063,7 @@ function reportInitSuccess(state, config, attempts) {
7949
8063
  void state.apiClient.reportError("INIT_RECOVERED", `Succeeded after ${attempts} retries`, { attempts });
7950
8064
  }
7951
8065
  } catch (err) {
7952
- if (config.debug) console.error("[Paywallo] reportInitSuccess error", err);
8066
+ console.error("[Paywallo] reportInitSuccess error", err);
7953
8067
  }
7954
8068
  }
7955
8069
  async function resolveSessionFlags(state, keys, apiClient, distinctId) {
@@ -8003,7 +8117,7 @@ async function doInit(state, config) {
8003
8117
  try {
8004
8118
  await identityManager.identify(pending);
8005
8119
  } catch (err) {
8006
- if (config.debug) console.warn("[Paywallo INIT] pending identify replay failed", err);
8120
+ console.error("[Paywallo INIT] pending identify replay failed", err);
8007
8121
  }
8008
8122
  }
8009
8123
  state.pendingIdentifies = [];
@@ -8081,7 +8195,7 @@ async function doInit(state, config) {
8081
8195
  setupNotifications(apiClient, config, environment, eventPipeline);
8082
8196
  if (config.autoStartSession !== false) {
8083
8197
  sessionManager.startSession().catch((err) => {
8084
- if (config.debug) console.warn("[Paywallo INIT] autoStart session error", err);
8198
+ console.error("[Paywallo INIT] autoStart session error", err);
8085
8199
  });
8086
8200
  }
8087
8201
  new InstallTracker(config.debug, {
@@ -8099,7 +8213,7 @@ async function doInit(state, config) {
8099
8213
  state.resolveReady();
8100
8214
  state.resolveReady = null;
8101
8215
  }
8102
- if (config.debug) console.warn("[Paywallo] initialized successfully");
8216
+ if (config.debug) console.log("[Paywallo] initialized successfully");
8103
8217
  if (distinctId) {
8104
8218
  apiClient.getSubscriptionStatus(distinctId).then(() => {
8105
8219
  if (config.debug) console.log("[Paywallo SUB] preloaded on boot");
@@ -8216,7 +8330,7 @@ async function checkActiveSubscription(state) {
8216
8330
  if (state.config?.debug) console.log("[Paywallo SUB] hasActiveSubscription (server):", isActive);
8217
8331
  return isActive;
8218
8332
  } catch (error) {
8219
- if (state.config?.debug) console.error("[Paywallo SUB] hasActiveSubscription error", error);
8333
+ console.error("[Paywallo SUB] hasActiveSubscription error", error);
8220
8334
  if (state.apiClient) void state.apiClient.reportError("SUBSCRIPTION_CHECK_FAILED", error instanceof Error ? error.message : "hasActiveSubscription failed");
8221
8335
  return false;
8222
8336
  }
@@ -8537,9 +8651,9 @@ __export(OfferingService_exports, {
8537
8651
  OfferingService: () => OfferingService,
8538
8652
  offeringService: () => offeringService
8539
8653
  });
8540
- import { Platform as Platform15 } from "react-native";
8654
+ import { Platform as Platform16 } from "react-native";
8541
8655
  function resolveProductId(raw) {
8542
- const isIos = Platform15.OS === "ios";
8656
+ const isIos = Platform16.OS === "ios";
8543
8657
  const productId = isIos ? raw.apple_product_id : raw.google_product_id;
8544
8658
  return productId ?? "";
8545
8659
  }
@@ -8558,7 +8672,7 @@ function mapProduct2(raw) {
8558
8672
  };
8559
8673
  }
8560
8674
  function pickPlatformProduct(products) {
8561
- const isIos = Platform15.OS === "ios";
8675
+ const isIos = Platform16.OS === "ios";
8562
8676
  const match = products.find((p) => isIos ? p.apple_product_id : p.google_product_id);
8563
8677
  return match ?? products[0] ?? null;
8564
8678
  }
@@ -8682,6 +8796,7 @@ init_PurchaseError();
8682
8796
  // src/index.ts
8683
8797
  init_identity();
8684
8798
  init_identity();
8799
+ init_identity();
8685
8800
  init_notifications();
8686
8801
 
8687
8802
  // src/domains/offering/index.ts
@@ -9352,7 +9467,6 @@ function useCampaignPreload() {
9352
9467
  }
9353
9468
 
9354
9469
  // src/hooks/useProductLoader.ts
9355
- init_PaywalloClient();
9356
9470
  import { useCallback as useCallback16, useState as useState14 } from "react";
9357
9471
  function useProductLoader() {
9358
9472
  const [products, setProducts] = useState14(/* @__PURE__ */ new Map());
@@ -9366,7 +9480,7 @@ function useProductLoader() {
9366
9480
  for (const p of loaded) map.set(p.productId, p);
9367
9481
  setProducts(map);
9368
9482
  } catch (err) {
9369
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] refreshProducts failed", err);
9483
+ console.error("[Paywallo] refreshProducts failed", err);
9370
9484
  } finally {
9371
9485
  setIsLoadingProducts(false);
9372
9486
  }
@@ -9375,7 +9489,6 @@ function useProductLoader() {
9375
9489
  }
9376
9490
 
9377
9491
  // src/hooks/usePurchaseOrchestrator.ts
9378
- init_PaywalloClient();
9379
9492
  import { useCallback as useCallback17 } from "react";
9380
9493
  init_paywall();
9381
9494
  function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, invalidateSubscriptionCache) {
@@ -9410,7 +9523,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
9410
9523
  clearPreloadedWebView();
9411
9524
  resolvePreloadedPaywall({ presented: true, purchased: true, cancelled: false, restored: false });
9412
9525
  } catch (error) {
9413
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] preloaded purchase failed", error);
9526
+ console.error("[Paywallo] preloaded purchase failed", error);
9414
9527
  setShowingPreloadedWebView(false);
9415
9528
  clearPreloadedWebView();
9416
9529
  resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: false });
@@ -9431,7 +9544,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
9431
9544
  clearPreloadedWebView();
9432
9545
  resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: true });
9433
9546
  } catch (error) {
9434
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] preloaded restore failed", error);
9547
+ console.error("[Paywallo] preloaded restore failed", error);
9435
9548
  setShowingPreloadedWebView(false);
9436
9549
  clearPreloadedWebView();
9437
9550
  resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: false });
@@ -10083,6 +10196,7 @@ var Paywallo = PaywalloClient;
10083
10196
  var index_default = Paywallo;
10084
10197
  export {
10085
10198
  ApiClient,
10199
+ AttributionTracker,
10086
10200
  CAMPAIGN_ERROR_CODES,
10087
10201
  CampaignError,
10088
10202
  PURCHASE_ERROR_CODES as ERROR_CODES,
@@ -10117,6 +10231,7 @@ export {
10117
10231
  SessionError,
10118
10232
  SessionManager,
10119
10233
  SubscriptionCache,
10234
+ attributionTracker,
10120
10235
  createPurchaseError,
10121
10236
  index_default as default,
10122
10237
  detectDeviceLanguage,