@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.js CHANGED
@@ -1067,7 +1067,7 @@ var init_sessionTracking = __esm({
1067
1067
  });
1068
1068
 
1069
1069
  // src/domains/session/SessionManager.ts
1070
- var import_react_native4, 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;
1070
+ var import_react_native4, SESSION_ID_KEY, SESSION_START_KEY, LEGACY_SESSION_ID_KEY, LEGACY_SESSION_START_KEY, SESSION_TIMEOUT_MS, SessionManager, sessionManager;
1071
1071
  var init_SessionManager = __esm({
1072
1072
  "src/domains/session/SessionManager.ts"() {
1073
1073
  "use strict";
@@ -1079,11 +1079,9 @@ var init_SessionManager = __esm({
1079
1079
  init_sessionTracking();
1080
1080
  SESSION_ID_KEY = "@paywallo:current_session_id";
1081
1081
  SESSION_START_KEY = "@paywallo:session_start";
1082
- EMERGENCY_PAYWALL_SHOWN_KEY = "@paywallo:emergency_paywall_shown";
1083
1082
  LEGACY_SESSION_ID_KEY = "current_session_id";
1084
1083
  LEGACY_SESSION_START_KEY = "session_start";
1085
1084
  SESSION_TIMEOUT_MS = 30 * 60 * 1e3;
1086
- BACKGROUND_FLUSH_TIMEOUT_MS = 2e3;
1087
1085
  SessionManager = class {
1088
1086
  constructor() {
1089
1087
  this.sessionId = null;
@@ -1145,8 +1143,7 @@ var init_SessionManager = __esm({
1145
1143
  this.emergencyPaywallShownInSession = false;
1146
1144
  await Promise.all([
1147
1145
  this.secureStorage.set(SESSION_ID_KEY, this.sessionId),
1148
- this.secureStorage.set(SESSION_START_KEY, this.sessionStartedAt.toISOString()),
1149
- this.secureStorage.remove(EMERGENCY_PAYWALL_SHOWN_KEY)
1146
+ this.secureStorage.set(SESSION_START_KEY, this.sessionStartedAt.toISOString())
1150
1147
  ]);
1151
1148
  await this.trackSessionStart();
1152
1149
  return this.sessionId;
@@ -1156,10 +1153,10 @@ var init_SessionManager = __esm({
1156
1153
  if (!this.sessionId || !this.sessionStartedAt) {
1157
1154
  return;
1158
1155
  }
1156
+ await this.trackSessionEnd();
1159
1157
  if (this.apiClient) {
1160
1158
  await this.apiClient.flushEventBatch();
1161
1159
  }
1162
- await this.trackSessionEnd();
1163
1160
  this.sessionId = null;
1164
1161
  this.sessionStartedAt = null;
1165
1162
  await Promise.all([
@@ -1244,7 +1241,6 @@ var init_SessionManager = __esm({
1244
1241
  async handleBackground() {
1245
1242
  this.backgroundTimestamp = Date.now();
1246
1243
  await this.trackAppBackground();
1247
- await this.flushEventsWithTimeout(BACKGROUND_FLUSH_TIMEOUT_MS);
1248
1244
  await this.endSession();
1249
1245
  }
1250
1246
  async flushEventsWithTimeout(timeoutMs) {
@@ -1263,7 +1259,6 @@ var init_SessionManager = __esm({
1263
1259
  const response = await this.apiClient.getEmergencyPaywall().catch(() => null);
1264
1260
  if (response?.enabled && response.paywallId) {
1265
1261
  this.emergencyPaywallShownInSession = true;
1266
- await this.secureStorage.set(EMERGENCY_PAYWALL_SHOWN_KEY, "true");
1267
1262
  await this.emergencyPaywallHandler(response.paywallId);
1268
1263
  }
1269
1264
  }
@@ -1563,7 +1558,7 @@ var init_MetaBridge = __esm({
1563
1558
  try {
1564
1559
  await NativeBridge.logEvent(name, params);
1565
1560
  } catch (error) {
1566
- if (this.debug) console.warn("[Paywallo:MetaBridge] logEvent failed", { name, error });
1561
+ console.error("[Paywallo:MetaBridge] logEvent failed", { name, error });
1567
1562
  }
1568
1563
  }
1569
1564
  async logPurchase(amount, currency, params = {}) {
@@ -1571,7 +1566,7 @@ var init_MetaBridge = __esm({
1571
1566
  try {
1572
1567
  await NativeBridge.logPurchase(amount, currency, params);
1573
1568
  } catch (error) {
1574
- if (this.debug) console.warn("[Paywallo:MetaBridge] logPurchase failed", { amount, currency, error });
1569
+ console.error("[Paywallo:MetaBridge] logPurchase failed", { amount, currency, error });
1575
1570
  }
1576
1571
  }
1577
1572
  setUserID(userId) {
@@ -1579,7 +1574,7 @@ var init_MetaBridge = __esm({
1579
1574
  try {
1580
1575
  NativeBridge.setUserID(userId);
1581
1576
  } catch (error) {
1582
- if (this.debug) console.warn("[Paywallo:MetaBridge] setUserID failed", { error });
1577
+ console.error("[Paywallo:MetaBridge] setUserID failed", { error });
1583
1578
  }
1584
1579
  }
1585
1580
  flush() {
@@ -1587,7 +1582,7 @@ var init_MetaBridge = __esm({
1587
1582
  try {
1588
1583
  NativeBridge.flush();
1589
1584
  } catch (error) {
1590
- if (this.debug) console.warn("[Paywallo:MetaBridge] flush failed", { error });
1585
+ console.error("[Paywallo:MetaBridge] flush failed", { error });
1591
1586
  }
1592
1587
  }
1593
1588
  };
@@ -1803,7 +1798,7 @@ var init_InstallTracker = __esm({
1803
1798
  screenWidth: deviceData.screenWidth,
1804
1799
  screenHeight: deviceData.screenHeight,
1805
1800
  timezone: deviceData.timezone,
1806
- locale: deviceData.locale,
1801
+ language: deviceData.locale,
1807
1802
  fbAnonId: anonId,
1808
1803
  installTimestamp: new Date(installedAt).toISOString()
1809
1804
  }),
@@ -2029,13 +2024,21 @@ var init_NativePushBridge = __esm({
2029
2024
  if (!raw || typeof raw !== "object") return null;
2030
2025
  const r = raw;
2031
2026
  const notification = r["notification"];
2027
+ const asString = (v) => typeof v === "string" ? v : void 0;
2032
2028
  return {
2033
- messageId: typeof r["messageId"] === "string" ? r["messageId"] : void 0,
2029
+ messageId: asString(r["messageId"]),
2034
2030
  data: typeof r["data"] === "object" && r["data"] !== null ? r["data"] : void 0,
2035
2031
  notification: notification && typeof notification === "object" ? {
2036
- title: typeof notification["title"] === "string" ? notification["title"] : void 0,
2037
- body: typeof notification["body"] === "string" ? notification["body"] : void 0
2032
+ title: asString(notification["title"]),
2033
+ body: asString(notification["body"]),
2034
+ imageUrl: asString(notification["imageUrl"])
2038
2035
  } : void 0,
2036
+ // Pass through top-level title/body/imageUrl emitted by the native modules
2037
+ // (both iOS and Android currently emit these at the top level rather than
2038
+ // nested under `notification`).
2039
+ title: asString(r["title"]),
2040
+ body: asString(r["body"]),
2041
+ imageUrl: asString(r["imageUrl"]),
2039
2042
  sentTime: typeof r["sentTime"] === "number" ? r["sentTime"] : void 0
2040
2043
  };
2041
2044
  } catch {
@@ -2174,10 +2177,11 @@ function setupForegroundHandler(deps, onReceive) {
2174
2177
  const data = message.data ?? {};
2175
2178
  const payload = extractPayload({
2176
2179
  ...data,
2177
- title: data["title"] ?? message.notification?.title ?? "",
2178
- body: data["body"] ?? message.notification?.body ?? ""
2180
+ title: data["title"] ?? message.notification?.title ?? message.title ?? "",
2181
+ body: data["body"] ?? message.notification?.body ?? message.body ?? "",
2182
+ imageUrl: data["imageUrl"] ?? message.notification?.imageUrl ?? message.imageUrl
2179
2183
  });
2180
- if (debug) console.warn("[Paywallo PUSH] foreground message received:", payload.notificationId);
2184
+ if (debug) console.log("[Paywallo PUSH] foreground message received:", payload.notificationId);
2181
2185
  onReceive(payload);
2182
2186
  const messageId = message.messageId ?? payload.notificationId;
2183
2187
  await tracker.track("notification_delivered", {
@@ -2207,14 +2211,21 @@ function getTimezone() {
2207
2211
  function detectPlatform() {
2208
2212
  return import_react_native10.Platform.OS === "ios" ? "ios" : "android";
2209
2213
  }
2210
- var import_react_native10, SEEN_MESSAGES_KEY, SEEN_TTL_MS, MAX_SEEN_SIZE, NotificationEventTracker;
2214
+ var import_react_native10, SEEN_MESSAGES_KEY, LEGACY_SEEN_MESSAGES_KEY, SEEN_TTL_MS, MAX_SEEN_SIZE, NOTIFICATION_TYPE_TO_FAMILY, NotificationEventTracker;
2211
2215
  var init_NotificationEventTracker = __esm({
2212
2216
  "src/domains/notifications/NotificationEventTracker.ts"() {
2213
2217
  "use strict";
2214
2218
  import_react_native10 = require("react-native");
2215
- SEEN_MESSAGES_KEY = "seen_messages";
2219
+ SEEN_MESSAGES_KEY = "@paywallo:seen_messages";
2220
+ LEGACY_SEEN_MESSAGES_KEY = "seen_messages";
2216
2221
  SEEN_TTL_MS = 60 * 60 * 1e3;
2217
2222
  MAX_SEEN_SIZE = 1e3;
2223
+ NOTIFICATION_TYPE_TO_FAMILY = {
2224
+ notification_delivered: "delivered",
2225
+ notification_displayed: "displayed",
2226
+ notification_clicked: "clicked",
2227
+ notification_dismissed: "dismissed"
2228
+ };
2218
2229
  NotificationEventTracker = class {
2219
2230
  constructor(batcher, config, secureStorage2) {
2220
2231
  this.batcher = batcher;
@@ -2235,9 +2246,13 @@ var init_NotificationEventTracker = __esm({
2235
2246
  if (this.config.debug) console.log("[Paywallo PUSH] skipped (no distinctId):", eventType);
2236
2247
  return;
2237
2248
  }
2249
+ const canonicalType = NOTIFICATION_TYPE_TO_FAMILY[eventType];
2250
+ if (!canonicalType) {
2251
+ if (this.config.debug) console.log("[Paywallo PUSH] eventType not mapped to family schema, skipping ingest:", eventType);
2252
+ return;
2253
+ }
2238
2254
  const properties = {
2239
- family: "notification",
2240
- type: eventType,
2255
+ type: canonicalType,
2241
2256
  notification_id: notificationId,
2242
2257
  campaign_id: campaignId,
2243
2258
  variant_key: variantKey ?? null,
@@ -2249,7 +2264,7 @@ var init_NotificationEventTracker = __esm({
2249
2264
  if (payload.failureReason) properties.failure_reason = payload.failureReason;
2250
2265
  if (payload.appUserId) properties.app_user_id = payload.appUserId;
2251
2266
  if (payload.properties) Object.assign(properties, payload.properties);
2252
- if (this.config.debug) console.log("[Paywallo PUSH] tracking event:", eventType, notificationId);
2267
+ if (this.config.debug) console.log("[Paywallo PUSH] tracking event:", canonicalType, notificationId);
2253
2268
  await this.batcher.trackEvent("notification", distinctId, properties);
2254
2269
  }
2255
2270
  async isDuplicate(messageId, eventType) {
@@ -2294,7 +2309,10 @@ var init_NotificationEventTracker = __esm({
2294
2309
  return;
2295
2310
  }
2296
2311
  try {
2297
- const raw = await this.secureStorage.get(SEEN_MESSAGES_KEY);
2312
+ let raw = await this.secureStorage.get(SEEN_MESSAGES_KEY);
2313
+ if (!raw) {
2314
+ raw = await this.secureStorage.get(LEGACY_SEEN_MESSAGES_KEY).catch(() => null);
2315
+ }
2298
2316
  if (raw) {
2299
2317
  const parsed = JSON.parse(raw);
2300
2318
  const now = Date.now();
@@ -2352,8 +2370,8 @@ async function getInitialNotificationPayload(bridge) {
2352
2370
  notificationId: String(data["notificationId"] ?? data["notification_id"] ?? ""),
2353
2371
  campaignId: String(data["campaignId"] ?? data["campaign_id"] ?? ""),
2354
2372
  variantKey: data["variantKey"] != null ? String(data["variantKey"]) : void 0,
2355
- title: String(data["title"] ?? message.notification?.title ?? ""),
2356
- body: String(data["body"] ?? message.notification?.body ?? ""),
2373
+ title: String(data["title"] ?? message.notification?.title ?? message.title ?? ""),
2374
+ body: String(data["body"] ?? message.notification?.body ?? message.body ?? ""),
2357
2375
  data
2358
2376
  };
2359
2377
  }
@@ -2368,8 +2386,8 @@ var init_NotificationHandlerSetup = __esm({
2368
2386
  // src/core/version.ts
2369
2387
  function resolveVersion() {
2370
2388
  try {
2371
- if ("2.2.3") {
2372
- return "2.2.3";
2389
+ if ("2.2.5") {
2390
+ return "2.2.5";
2373
2391
  }
2374
2392
  } catch {
2375
2393
  }
@@ -2501,7 +2519,7 @@ async function buildRegistration(token, getDeviceId, getDistinctId) {
2501
2519
  async function registerToken(deps, token) {
2502
2520
  const { httpClient, appKey, getDeviceId, getDistinctId, debug } = deps;
2503
2521
  if (!httpClient || !appKey) {
2504
- if (debug) console.warn("[Paywallo PUSH] httpClient not ready, skipping token registration");
2522
+ if (debug) console.log("[Paywallo PUSH] httpClient not ready, skipping token registration");
2505
2523
  return;
2506
2524
  }
2507
2525
  const registration = await buildRegistration(token, getDeviceId, getDistinctId);
@@ -2511,22 +2529,25 @@ async function registerToken(deps, token) {
2511
2529
  registration,
2512
2530
  { headers: { "X-App-Key": appKey } }
2513
2531
  );
2514
- if (debug) console.warn("[Paywallo PUSH] token registered successfully");
2532
+ if (debug) console.log("[Paywallo PUSH] token registered successfully");
2515
2533
  } catch (err) {
2516
- if (debug) console.warn("[Paywallo PUSH] token registration failed:", err);
2534
+ console.error("[Paywallo PUSH] token registration failed:", err);
2517
2535
  throw new RegistrationFailedError("Token registration failed", err);
2518
2536
  }
2519
2537
  }
2520
- async function unregisterToken(httpClient, appKey, debug) {
2538
+ async function unregisterToken(httpClient, appKey, debug, token, distinctId) {
2521
2539
  if (!httpClient || !appKey) return;
2540
+ if (!token || !distinctId) return;
2522
2541
  try {
2542
+ const body = JSON.stringify({ token, distinct_id: distinctId });
2523
2543
  await httpClient.request(TOKEN_ENDPOINT, {
2524
2544
  method: "DELETE",
2525
- headers: { "X-App-Key": appKey },
2545
+ headers: { "X-App-Key": appKey, "Content-Type": "application/json" },
2546
+ body,
2526
2547
  timeout: 1e4
2527
2548
  });
2528
2549
  } catch (err) {
2529
- if (debug) console.warn("[Paywallo PUSH] optOut error:", err);
2550
+ console.error("[Paywallo PUSH] optOut error:", err);
2530
2551
  }
2531
2552
  }
2532
2553
  var FCM_TOKEN_KEY, TOKEN_ENDPOINT;
@@ -2558,7 +2579,7 @@ async function registerAndPersistToken(deps, token) {
2558
2579
  }
2559
2580
  function subscribeTokenRefresh(bridge, deps, onToken) {
2560
2581
  return bridge.onTokenRefresh((newToken) => {
2561
- if (deps.debug) console.warn("[Paywallo PUSH] token refreshed");
2582
+ if (deps.debug) console.log("[Paywallo PUSH] token refreshed");
2562
2583
  onToken(newToken);
2563
2584
  persistToken(deps.secureStorage, newToken).catch(() => void 0);
2564
2585
  registerToken(
@@ -2574,8 +2595,8 @@ function subscribeTokenRefresh(bridge, deps, onToken) {
2574
2595
  ).catch(() => void 0);
2575
2596
  });
2576
2597
  }
2577
- async function optOutToken(httpClient, secureStorage2, appKey, debug) {
2578
- await unregisterToken(httpClient, appKey, debug);
2598
+ async function optOutToken(httpClient, secureStorage2, appKey, debug, token, distinctId) {
2599
+ await unregisterToken(httpClient, appKey, debug, token, distinctId);
2579
2600
  await clearPersistedToken(secureStorage2);
2580
2601
  }
2581
2602
  async function invalidateLocalTokenStorage(secureStorage2, debug) {
@@ -2595,7 +2616,7 @@ async function captureAndRegisterToken(bridge, deps, onTokenCaptured, subscribeR
2595
2616
  await waitForApnsToken(bridge, deps.debug);
2596
2617
  const token = await bridge.getToken();
2597
2618
  if (!token) {
2598
- if (deps.debug) console.warn("[Paywallo PUSH] token unavailable after grant");
2619
+ if (deps.debug) console.log("[Paywallo PUSH] token unavailable after grant");
2599
2620
  return;
2600
2621
  }
2601
2622
  onTokenCaptured(token);
@@ -2662,18 +2683,18 @@ var init_PermissionManager = __esm({
2662
2683
  }
2663
2684
  async requestPermission(options) {
2664
2685
  const platform = this.platformOverride ?? getPlatform();
2665
- if (this.debug) console.warn("[paywallo-push] platform detected:", platform.OS, "version:", String(platform.Version ?? "n/a"));
2686
+ if (this.debug) console.log("[paywallo-push] platform detected:", platform.OS, "version:", String(platform.Version ?? "n/a"));
2666
2687
  if (platform.OS === "ios") {
2667
- if (this.debug) console.warn("[paywallo-push] requestPermission iOS \u2014 calling bridge...");
2688
+ if (this.debug) console.log("[paywallo-push] requestPermission iOS \u2014 calling bridge...");
2668
2689
  try {
2669
2690
  const raw2 = await this.bridge.requestPermission(options);
2670
- if (this.debug) console.warn("[paywallo-push] bridge returned raw:", raw2);
2691
+ if (this.debug) console.log("[paywallo-push] bridge returned raw:", raw2);
2671
2692
  const status2 = mapPermissionStatus2(raw2);
2672
- if (this.debug) console.warn("[paywallo-push] mapped status:", status2);
2693
+ if (this.debug) console.log("[paywallo-push] mapped status:", status2);
2673
2694
  this.emitOsEvent(status2);
2674
2695
  return status2;
2675
2696
  } catch (err) {
2676
- if (this.debug) console.warn("[paywallo-push] requestPermission iOS CRASHED:", err instanceof Error ? err.message : String(err));
2697
+ console.error("[paywallo-push] requestPermission iOS CRASHED:", err instanceof Error ? err.message : String(err));
2677
2698
  throw err;
2678
2699
  }
2679
2700
  }
@@ -2806,11 +2827,11 @@ var init_NotificationsManager = __esm({
2806
2827
  await waitForApnsToken(this.bridge, this.debug);
2807
2828
  const token = await this.bridge.getToken();
2808
2829
  if (!token) {
2809
- if (this.debug) console.warn("[Paywallo PUSH] token unavailable, skipping registration");
2830
+ if (this.debug) console.log("[Paywallo PUSH] token unavailable, skipping registration");
2810
2831
  this.isInitialized = true;
2811
2832
  return;
2812
2833
  }
2813
- if (this.debug) console.warn("[Paywallo PUSH] token acquired:", token);
2834
+ if (this.debug) console.log("[Paywallo PUSH] token acquired:", token);
2814
2835
  this.currentToken = token;
2815
2836
  await this.doRegisterToken(token);
2816
2837
  this.subscribeTokenRefresh();
@@ -2828,7 +2849,7 @@ var init_NotificationsManager = __esm({
2828
2849
  async refreshPermissionStatus() {
2829
2850
  const permStatus = await this.bridge.hasPermission();
2830
2851
  this.permissionStatus = mapPermissionStatus(permStatus);
2831
- if (this.debug) console.warn("[Paywallo PUSH] permission status:", this.permissionStatus);
2852
+ if (this.debug) console.log("[Paywallo PUSH] permission status:", this.permissionStatus);
2832
2853
  }
2833
2854
  tokenDeps() {
2834
2855
  return {
@@ -2856,7 +2877,7 @@ var init_NotificationsManager = __esm({
2856
2877
  async optOut() {
2857
2878
  await optOutToken(this.httpClient, this.secureStorage, this.appKey, this.debug);
2858
2879
  this.currentToken = null;
2859
- if (this.debug) console.warn("[Paywallo PUSH] opted out");
2880
+ if (this.debug) console.log("[Paywallo PUSH] opted out");
2860
2881
  }
2861
2882
  /** Clears token locally without server call (used on sign-out/reset). */
2862
2883
  async invalidateLocalToken() {
@@ -2873,21 +2894,21 @@ var init_NotificationsManager = __esm({
2873
2894
  }
2874
2895
  /** Requests OS permission and, on success, captures + registers the push token. */
2875
2896
  async requestPushPermission(options) {
2876
- if (this.debug) console.warn("[Paywallo PUSH] requestPushPermission called");
2897
+ if (this.debug) console.log("[Paywallo PUSH] requestPushPermission called");
2877
2898
  try {
2878
2899
  const status = await this.permissionManager.requestPermission(options);
2879
- if (this.debug) console.warn("[Paywallo PUSH] permissionManager returned:", status);
2900
+ if (this.debug) console.log("[Paywallo PUSH] permissionManager returned:", status);
2880
2901
  this.permissionStatus = status;
2881
2902
  if (status === "granted" || status === "provisional") {
2882
- if (this.debug) console.warn("[Paywallo PUSH] permission granted \u2014 capturing token...");
2903
+ if (this.debug) console.log("[Paywallo PUSH] permission granted \u2014 capturing token...");
2883
2904
  await this.captureAndRegisterToken();
2884
- if (this.debug) console.warn("[Paywallo PUSH] token capture complete");
2905
+ if (this.debug) console.log("[Paywallo PUSH] token capture complete");
2885
2906
  } else {
2886
- if (this.debug) console.warn("[Paywallo PUSH] permission not granted, skipping token register:", status);
2907
+ if (this.debug) console.log("[Paywallo PUSH] permission not granted, skipping token register:", status);
2887
2908
  }
2888
2909
  return status;
2889
2910
  } catch (err) {
2890
- if (this.debug) console.warn("[Paywallo PUSH] requestPushPermission CRASHED:", err instanceof Error ? err.message : String(err));
2911
+ console.error("[Paywallo PUSH] requestPushPermission CRASHED:", err instanceof Error ? err.message : String(err));
2891
2912
  throw err;
2892
2913
  }
2893
2914
  }
@@ -3049,7 +3070,9 @@ function parseWebViewMessage(raw) {
3049
3070
  const safePayload = payload !== null && typeof payload === "object" ? {
3050
3071
  productId: typeof payload.productId === "string" ? String(payload.productId) : void 0,
3051
3072
  url: typeof payload.url === "string" ? String(payload.url) : void 0,
3052
- timestamp: typeof payload.timestamp === "number" ? Number(payload.timestamp) : void 0
3073
+ timestamp: typeof payload.timestamp === "number" ? Number(payload.timestamp) : void 0,
3074
+ variantKey: typeof payload.variantKey === "string" ? String(payload.variantKey) : void 0,
3075
+ style: HAPTIC_STYLES.has(String(payload.style)) ? String(payload.style) : typeof payload.style !== "undefined" ? "light" : void 0
3053
3076
  } : void 0;
3054
3077
  return {
3055
3078
  type: parsed.type,
@@ -3060,17 +3083,19 @@ function parseWebViewMessage(raw) {
3060
3083
  return null;
3061
3084
  }
3062
3085
  }
3063
- var ALLOWED_MESSAGE_TYPES;
3086
+ var HAPTIC_STYLES, ALLOWED_MESSAGE_TYPES;
3064
3087
  var init_parseWebViewMessage = __esm({
3065
3088
  "src/domains/paywall/parseWebViewMessage.ts"() {
3066
3089
  "use strict";
3090
+ HAPTIC_STYLES = /* @__PURE__ */ new Set(["light", "medium", "heavy"]);
3067
3091
  ALLOWED_MESSAGE_TYPES = /* @__PURE__ */ new Set([
3068
3092
  "purchase",
3069
3093
  "close",
3070
3094
  "restore",
3071
3095
  "select-product",
3072
3096
  "open-url",
3073
- "ready"
3097
+ "ready",
3098
+ "haptic"
3074
3099
  ]);
3075
3100
  }
3076
3101
  });
@@ -3112,7 +3137,7 @@ var init_PaywallErrorBoundary = __esm({
3112
3137
  return { hasError: true };
3113
3138
  }
3114
3139
  componentDidCatch(error) {
3115
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] Paywall render error:", error);
3140
+ console.error("[Paywallo] Paywall render error:", error);
3116
3141
  try {
3117
3142
  PaywalloClient.getApiClient()?.reportError("PAYWALL_RENDER_ERROR", error.message);
3118
3143
  } catch {
@@ -3279,7 +3304,9 @@ function buildPaywallInjectionScript(data) {
3279
3304
  craftData: data.craftData,
3280
3305
  products: data.products,
3281
3306
  primaryProductId: data.primaryProductId,
3282
- secondaryProductId: data.secondaryProductId
3307
+ secondaryProductId: data.secondaryProductId,
3308
+ currentLanguage: getCurrentLanguage(),
3309
+ defaultLanguage: getDefaultLanguage()
3283
3310
  }
3284
3311
  };
3285
3312
  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;`;
@@ -3287,9 +3314,28 @@ function buildPaywallInjectionScript(data) {
3287
3314
  function buildPurchaseStateScript(isPurchasing) {
3288
3315
  return `if(window.receiveNativeMessage){window.receiveNativeMessage({type:"purchaseState",data:{isPurchasing:${isPurchasing}}});}true;`;
3289
3316
  }
3317
+ function buildBackButtonScript(nonce = Date.now()) {
3318
+ return [
3319
+ `/*${nonce}*/`,
3320
+ "(function(){",
3321
+ "try{",
3322
+ "var h=window.__paywalloHandleBackPress;",
3323
+ "var consumed=typeof h==='function'?!!h():false;",
3324
+ "if(!consumed&&window.ReactNativeWebView){",
3325
+ 'window.ReactNativeWebView.postMessage(JSON.stringify({type:"close"}));',
3326
+ "}",
3327
+ "}catch(e){",
3328
+ "if(window.ReactNativeWebView){",
3329
+ 'window.ReactNativeWebView.postMessage(JSON.stringify({type:"close"}));',
3330
+ "}",
3331
+ "}",
3332
+ "})();true;"
3333
+ ].join("");
3334
+ }
3290
3335
  var init_paywallScripts = __esm({
3291
3336
  "src/domains/paywall/paywallScripts.ts"() {
3292
3337
  "use strict";
3338
+ init_localization();
3293
3339
  }
3294
3340
  });
3295
3341
 
@@ -3312,6 +3358,24 @@ function getWebViewEmitter() {
3312
3358
  webViewEmitter = new import_react_native14.NativeEventEmitter(mod);
3313
3359
  return webViewEmitter;
3314
3360
  }
3361
+ async function triggerNativeHaptic(style) {
3362
+ try {
3363
+ const module2 = await import("react-native-haptic-feedback");
3364
+ const hapticModule = module2.default ?? module2;
3365
+ const trigger = hapticModule.trigger;
3366
+ if (typeof trigger !== "function") return;
3367
+ const typeMap = {
3368
+ light: "impactLight",
3369
+ medium: "impactMedium",
3370
+ heavy: "impactHeavy"
3371
+ };
3372
+ trigger(typeMap[style], {
3373
+ enableVibrateFallback: true,
3374
+ ignoreAndroidSystemSettings: false
3375
+ });
3376
+ } catch {
3377
+ }
3378
+ }
3315
3379
  function parseErrorEvent(raw) {
3316
3380
  return {
3317
3381
  code: typeof raw["code"] === "number" ? raw["code"] : -1,
@@ -3336,6 +3400,7 @@ function PaywallWebView({
3336
3400
  const paywallDataSent = (0, import_react3.useRef)(false);
3337
3401
  const loadEndReceived = (0, import_react3.useRef)(false);
3338
3402
  const [scriptToInject, setScriptToInject] = (0, import_react3.useState)(void 0);
3403
+ const webViewRef = (0, import_react3.useRef)(null);
3339
3404
  const onErrorRef = (0, import_react3.useRef)(onError);
3340
3405
  onErrorRef.current = onError;
3341
3406
  const onReadyRef = (0, import_react3.useRef)(onReady);
@@ -3379,6 +3444,15 @@ function PaywallWebView({
3379
3444
  });
3380
3445
  }
3381
3446
  }, []);
3447
+ const injectScript = (0, import_react3.useCallback)((script) => {
3448
+ if (import_react_native14.Platform.OS === "android") {
3449
+ const handle = (0, import_react_native14.findNodeHandle)(webViewRef.current);
3450
+ if (handle == null) return;
3451
+ import_react_native14.UIManager.dispatchViewManagerCommand(handle, "injectJavaScript", [script]);
3452
+ return;
3453
+ }
3454
+ setScriptToInject(script);
3455
+ }, []);
3382
3456
  const buildAndSetInjectionScript = (0, import_react3.useCallback)(() => {
3383
3457
  if (!craftData || paywallDataSent.current) return;
3384
3458
  paywallDataSent.current = true;
@@ -3388,8 +3462,8 @@ function PaywallWebView({
3388
3462
  primaryProductId,
3389
3463
  secondaryProductId
3390
3464
  });
3391
- setScriptToInject(script);
3392
- }, [craftData, products, primaryProductId, secondaryProductId]);
3465
+ injectScript(script);
3466
+ }, [craftData, products, primaryProductId, secondaryProductId, injectScript]);
3393
3467
  const processedIdsRef = (0, import_react3.useRef)(/* @__PURE__ */ new Set());
3394
3468
  const dedupeTimersRef = (0, import_react3.useRef)(/* @__PURE__ */ new Map());
3395
3469
  (0, import_react3.useEffect)(() => {
@@ -3426,6 +3500,9 @@ function PaywallWebView({
3426
3500
  case "restore":
3427
3501
  onRestore();
3428
3502
  break;
3503
+ case "select-product":
3504
+ console.log("[Paywallo:PaywallWebView] select-product received", message.payload?.productId);
3505
+ break;
3429
3506
  case "open-url":
3430
3507
  if (message.payload?.url) {
3431
3508
  const url = message.payload.url;
@@ -3434,6 +3511,9 @@ function PaywallWebView({
3434
3511
  }
3435
3512
  }
3436
3513
  break;
3514
+ case "haptic":
3515
+ void triggerNativeHaptic(message.payload?.style ?? "light");
3516
+ break;
3437
3517
  }
3438
3518
  },
3439
3519
  [buildAndSetInjectionScript, onPurchase, onClose, onRestore]
@@ -3505,13 +3585,24 @@ function PaywallWebView({
3505
3585
  );
3506
3586
  (0, import_react3.useEffect)(() => {
3507
3587
  if (!paywallDataSent.current) return;
3508
- setScriptToInject(buildPurchaseStateScript(isPurchasing));
3509
- }, [isPurchasing]);
3588
+ injectScript(buildPurchaseStateScript(isPurchasing));
3589
+ }, [isPurchasing, injectScript]);
3590
+ (0, import_react3.useEffect)(() => {
3591
+ if (import_react_native14.Platform.OS !== "android") return;
3592
+ const onBackPress = () => {
3593
+ if (!paywallDataSent.current) return false;
3594
+ injectScript(buildBackButtonScript());
3595
+ return true;
3596
+ };
3597
+ const subscription = import_react_native14.BackHandler.addEventListener("hardwareBackPress", onBackPress);
3598
+ return () => subscription.remove();
3599
+ }, [injectScript]);
3510
3600
  if (!resolvedWebUrl || !NativeWebView) return null;
3511
3601
  const paywallUrl = `${resolvedWebUrl}/paywall/${paywallId}`;
3512
3602
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native14.View, { style: styles.container, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3513
3603
  NativeWebView,
3514
3604
  {
3605
+ ref: webViewRef,
3515
3606
  sourceUrl: paywallUrl,
3516
3607
  injectedJavaScript: scriptToInject,
3517
3608
  onMessage: handleMessage,
@@ -3741,7 +3832,7 @@ var init_NativeStoreKit = __esm({
3741
3832
  try {
3742
3833
  await PaywalloStoreKitNative.finishTransaction(transactionId);
3743
3834
  } catch (err) {
3744
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] finishTransaction native error", err);
3835
+ console.error("[Paywallo] finishTransaction native error", err);
3745
3836
  }
3746
3837
  },
3747
3838
  async getActiveTransactions() {
@@ -3750,7 +3841,7 @@ var init_NativeStoreKit = __esm({
3750
3841
  const results = await PaywalloStoreKitNative.getActiveTransactions();
3751
3842
  return results.map(mapNativePurchase);
3752
3843
  } catch (err) {
3753
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] getActiveTransactions failed", err);
3844
+ console.error("[Paywallo] getActiveTransactions failed", err);
3754
3845
  return [];
3755
3846
  }
3756
3847
  },
@@ -4601,7 +4692,7 @@ var init_QueueProcessor = __esm({
4601
4692
  }
4602
4693
  const allItems = offlineQueue.getQueue();
4603
4694
  const eventItems = allItems.filter(
4604
- (i) => (i.url.includes("/events") || i.url.includes("/ingest")) && !i.url.includes("/batch")
4695
+ (i) => i.url.includes("/events") || i.url.includes("/ingest")
4605
4696
  );
4606
4697
  const eventItemIds = new Set(eventItems.map((i) => i.id));
4607
4698
  const { processed: batchProcessed, failed: batchFailed, retryIds } = await this.processEventBatches(eventItems);
@@ -4641,12 +4732,24 @@ var init_QueueProcessor = __esm({
4641
4732
  const freshHeaders = this.config.getFreshHeaders?.() ?? {};
4642
4733
  const headers = { ...persistedHeaders, ...freshHeaders };
4643
4734
  this.log("Sending event batch", { size: batch.length, batchIndex: Math.floor(i / BATCH_SIZE) });
4644
- const response = await this.httpClient.request("/sdk/events/batch", {
4645
- method: "POST",
4646
- body: { events: bodies },
4647
- headers,
4648
- skipRetry: false
4649
- });
4735
+ let response;
4736
+ try {
4737
+ response = await this.httpClient.request("/sdk/ingest/batch", {
4738
+ method: "POST",
4739
+ body: { events: bodies },
4740
+ headers,
4741
+ skipRetry: false
4742
+ });
4743
+ } catch (error) {
4744
+ for (const item of batch) {
4745
+ retryIds.add(item.id);
4746
+ }
4747
+ this.log("Event batch threw network error - will retry", {
4748
+ size: batch.length,
4749
+ error: error instanceof Error ? error.message : String(error)
4750
+ });
4751
+ continue;
4752
+ }
4650
4753
  if (response.ok) {
4651
4754
  for (const item of batch) {
4652
4755
  await offlineQueue.removeItem(item.id);
@@ -4686,12 +4789,21 @@ var init_QueueProcessor = __esm({
4686
4789
  });
4687
4790
  const persistedHeaders = item.headers ?? {};
4688
4791
  const freshHeaders = this.config.getFreshHeaders?.() ?? {};
4689
- const response = await this.httpClient.request(item.url, {
4690
- method: item.method,
4691
- body: item.body,
4692
- headers: { ...persistedHeaders, ...freshHeaders },
4693
- skipRetry: false
4694
- });
4792
+ let response;
4793
+ try {
4794
+ response = await this.httpClient.request(item.url, {
4795
+ method: item.method,
4796
+ body: item.body,
4797
+ headers: { ...persistedHeaders, ...freshHeaders },
4798
+ skipRetry: false
4799
+ });
4800
+ } catch (error) {
4801
+ this.log("Request threw network error - will retry", {
4802
+ id: item.id,
4803
+ error: error instanceof Error ? error.message : String(error)
4804
+ });
4805
+ return { success: false, permanent: false };
4806
+ }
4695
4807
  if (response.ok) {
4696
4808
  this.log("Queue item processed successfully", { id: item.id });
4697
4809
  return { success: true, permanent: false };
@@ -4836,19 +4948,19 @@ var init_IAPValidator = __esm({
4836
4948
  const appKey = apiClient.getAppKey();
4837
4949
  const body = {
4838
4950
  platform,
4839
- receipt: purchase.receipt,
4840
- productId: purchase.productId,
4841
- transactionId: purchase.transactionId,
4842
- priceLocal: product?.priceValue ?? 0,
4951
+ receipt_data: purchase.receipt,
4952
+ product_id: purchase.productId,
4953
+ transaction_id: purchase.transactionId,
4954
+ price_local: product?.priceValue ?? 0,
4843
4955
  currency: product?.currency ?? "USD",
4844
4956
  country: this.getDeviceCountry(),
4845
- distinctId
4957
+ distinct_id: distinctId
4846
4958
  };
4847
- if (options?.paywallPlacement !== void 0) body.paywallPlacement = options.paywallPlacement;
4848
- if (options?.variantKey !== void 0) body.variantKey = options.variantKey;
4959
+ if (options?.paywallPlacement !== void 0) body.paywall_placement = options.paywallPlacement;
4960
+ if (options?.variantKey !== void 0) body.variant_key = options.variantKey;
4849
4961
  await offlineQueue.enqueue(
4850
4962
  "POST",
4851
- "/purchases/validate",
4963
+ "/sdk/purchases/validate",
4852
4964
  body,
4853
4965
  { "X-App-Key": appKey },
4854
4966
  `validate:${purchase.transactionId}`,
@@ -4856,7 +4968,7 @@ var init_IAPValidator = __esm({
4856
4968
  );
4857
4969
  if (this.getDebug()) console.log("[Paywallo PURCHASE] validation retry enqueued", purchase.transactionId);
4858
4970
  } catch (err) {
4859
- if (this.getDebug()) console.error("[Paywallo PURCHASE] failed to enqueue validation retry", err);
4971
+ console.error("[Paywallo PURCHASE] failed to enqueue validation retry", err);
4860
4972
  }
4861
4973
  }
4862
4974
  };
@@ -4935,7 +5047,7 @@ var init_IAPService = __esm({
4935
5047
  }
4936
5048
  return products;
4937
5049
  } catch (err) {
4938
- if (this.debug) console.error("[Paywallo] loadProducts failed", err);
5050
+ console.error("[Paywallo] loadProducts failed", err);
4939
5051
  return [];
4940
5052
  }
4941
5053
  }
@@ -4954,7 +5066,7 @@ var init_IAPService = __esm({
4954
5066
  await nativeStoreKit.finishTransaction(transactionId);
4955
5067
  this.finishedTransactions.add(transactionId);
4956
5068
  } catch (err) {
4957
- if (this.debug) console.error("[Paywallo] finishTransaction failed", err);
5069
+ console.error("[Paywallo] finishTransaction failed", err);
4958
5070
  }
4959
5071
  }
4960
5072
  startTransactionListener() {
@@ -5003,7 +5115,7 @@ var init_IAPService = __esm({
5003
5115
  await this.finishTransaction(purchase.transactionId);
5004
5116
  return;
5005
5117
  }
5006
- if (this.debug) console.error("[Paywallo PURCHASE] background validation failed (network) \u2014 queued for retry", err);
5118
+ console.error("[Paywallo PURCHASE] background validation failed (network) \u2014 queued for retry", err);
5007
5119
  await this.validator.enqueueValidationRetry(purchase, productId, {
5008
5120
  paywallPlacement: options?.paywallPlacement,
5009
5121
  variantKey: options?.variantKey
@@ -5011,7 +5123,7 @@ var init_IAPService = __esm({
5011
5123
  });
5012
5124
  return { success: true, purchase };
5013
5125
  } catch (error) {
5014
- if (this.debug) console.error(`[Paywallo PURCHASE] ${productId} failed`, error);
5126
+ console.error(`[Paywallo PURCHASE] ${productId} failed`, error);
5015
5127
  const e = error instanceof PurchaseError ? error : new PurchaseError(PURCHASE_ERROR_CODES.PURCHASE_FAILED, error instanceof Error ? error.message : String(error));
5016
5128
  return { success: false, error: e };
5017
5129
  } finally {
@@ -5055,7 +5167,7 @@ var init_IAPService = __esm({
5055
5167
  if (this.debug) console.log("[Paywallo RESTORE] validation rejected (4xx) \u2014 dropping", tx.transactionId);
5056
5168
  await this.finishTransaction(tx.transactionId);
5057
5169
  } else {
5058
- if (this.debug) console.error("[Paywallo RESTORE] validation failed (network) \u2014 queued for retry", r.reason);
5170
+ console.error("[Paywallo RESTORE] validation failed (network) \u2014 queued for retry", r.reason);
5059
5171
  await this.validator.enqueueValidationRetry(tx, tx.productId);
5060
5172
  }
5061
5173
  } else if (r.status === "fulfilled" && !r.value.success) {
@@ -5065,7 +5177,7 @@ var init_IAPService = __esm({
5065
5177
  }
5066
5178
  return validated;
5067
5179
  } catch (err) {
5068
- if (this.debug) console.error("[Paywallo] restore failed", err);
5180
+ console.error("[Paywallo] restore failed", err);
5069
5181
  return [];
5070
5182
  }
5071
5183
  }
@@ -5281,7 +5393,7 @@ function usePaywallLoader(placement, visible, preloadedConfig, preloadedProducts
5281
5393
  setProducts(map);
5282
5394
  }
5283
5395
  } catch (err) {
5284
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] loadProducts in paywall failed", err);
5396
+ console.error("[Paywallo] loadProducts in paywall failed", err);
5285
5397
  if (!cancelled) setProducts(/* @__PURE__ */ new Map());
5286
5398
  }
5287
5399
  } else {
@@ -6410,11 +6522,9 @@ var init_SubscriptionManager = __esm({
6410
6522
  if (!this.config) {
6411
6523
  return this.getEmptyStatus();
6412
6524
  }
6413
- const url = new URL(`${this.config.serverUrl}/sdk/purchases/status/${this.config.appKey}`);
6414
- if (this.userId) {
6415
- url.searchParams.set("distinctId", this.userId);
6416
- }
6417
- 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, {
6418
6528
  method: "GET",
6419
6529
  headers: {
6420
6530
  "Content-Type": "application/json",
@@ -6427,17 +6537,32 @@ var init_SubscriptionManager = __esm({
6427
6537
  `Failed to fetch subscription status: ${response.status}`
6428
6538
  );
6429
6539
  }
6430
- const data = await response.json();
6431
- if (data.subscription) {
6432
- data.subscription.expiresAt = new Date(data.subscription.expiresAt);
6433
- data.subscription.originalPurchaseDate = new Date(data.subscription.originalPurchaseDate);
6434
- data.subscription.latestPurchaseDate = new Date(data.subscription.latestPurchaseDate);
6435
- if (data.subscription.cancellationDate) {
6436
- data.subscription.cancellationDate = new Date(data.subscription.cancellationDate);
6437
- }
6438
- if (data.subscription.gracePeriodExpiresAt) {
6439
- data.subscription.gracePeriodExpiresAt = new Date(data.subscription.gracePeriodExpiresAt);
6440
- }
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
+ };
6441
6566
  }
6442
6567
  return data;
6443
6568
  }
@@ -6475,15 +6600,15 @@ async function validatePurchase(client, appKey, platform, receipt, productId, tr
6475
6600
  "/sdk/purchases/validate",
6476
6601
  {
6477
6602
  platform,
6478
- receipt,
6479
- productId,
6480
- transactionId,
6481
- priceLocal,
6603
+ receipt_data: receipt,
6604
+ product_id: productId,
6605
+ transaction_id: transactionId,
6606
+ price_local: priceLocal,
6482
6607
  currency,
6483
6608
  country,
6484
- distinctId,
6485
- ...paywallPlacement !== void 0 && { paywallPlacement },
6486
- ...variantKey !== void 0 && { variantKey }
6609
+ distinct_id: distinctId,
6610
+ ...paywallPlacement !== void 0 && { paywall_placement: paywallPlacement },
6611
+ ...variantKey !== void 0 && { variant_key: variantKey }
6487
6612
  },
6488
6613
  false
6489
6614
  );
@@ -6495,7 +6620,7 @@ async function validatePurchase(client, appKey, platform, receipt, productId, tr
6495
6620
  response.status
6496
6621
  );
6497
6622
  }
6498
- return response.data;
6623
+ return response.data.data;
6499
6624
  }
6500
6625
  async function getSubscriptionStatus(client, baseUrl, appKey, distinctId) {
6501
6626
  const url = new URL(`${baseUrl}/sdk/purchases/status`);
@@ -6504,7 +6629,7 @@ async function getSubscriptionStatus(client, baseUrl, appKey, distinctId) {
6504
6629
  if (!response.ok) {
6505
6630
  throw new PurchaseError(PURCHASE_ERROR_CODES.NETWORK_ERROR, `Subscription status check failed: HTTP ${response.status}`);
6506
6631
  }
6507
- return response.data;
6632
+ return response.data.data;
6508
6633
  }
6509
6634
  async function getEmergencyPaywall(client) {
6510
6635
  try {
@@ -6713,7 +6838,7 @@ async function evaluateFlags2(deps, keys, distinctId) {
6713
6838
  const result = {};
6714
6839
  for (const key of Object.keys(record)) {
6715
6840
  const value = record[key];
6716
- const variant = typeof value === "boolean" ? String(value) : typeof value === "string" ? value : null;
6841
+ const variant = typeof value === "string" ? value : null;
6717
6842
  result[key] = variant;
6718
6843
  const flagVariant = { variant };
6719
6844
  deps.cache.setVariant(`${key}:${distinctId}`, flagVariant);
@@ -7026,7 +7151,7 @@ var init_eventEnvelopeV2 = __esm({
7026
7151
  payload: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown())
7027
7152
  });
7028
7153
  v2ContextSchema = import_zod2.z.object({
7029
- distinct_id: import_zod2.z.string().optional(),
7154
+ distinct_id: import_zod2.z.string().min(1),
7030
7155
  session_id: import_zod2.z.string().optional(),
7031
7156
  device_id: import_zod2.z.string().optional(),
7032
7157
  app_version: import_zod2.z.string().optional(),
@@ -7069,7 +7194,7 @@ var init_events = __esm({
7069
7194
  });
7070
7195
 
7071
7196
  // src/core/EventBatcher.ts
7072
- var import_react_native22, BATCH_MAX_SIZE, BATCH_FLUSH_MS, EVENT_NAME_REGEX, V2_BATCH_ENDPOINT, V1_BATCH_ENDPOINT, V1_SINGLE_ENDPOINT, EventBatcher;
7197
+ var import_react_native22, BATCH_MAX_SIZE, BATCH_FLUSH_MS, EVENT_NAME_REGEX, V2_BATCH_ENDPOINT, EventBatcher;
7073
7198
  var init_EventBatcher = __esm({
7074
7199
  "src/core/EventBatcher.ts"() {
7075
7200
  "use strict";
@@ -7080,15 +7205,12 @@ var init_EventBatcher = __esm({
7080
7205
  BATCH_FLUSH_MS = 1e4;
7081
7206
  EVENT_NAME_REGEX = /^(\$[a-zA-Z0-9_]+|[a-z][a-z0-9_]*)$/;
7082
7207
  V2_BATCH_ENDPOINT = "/sdk/ingest/batch";
7083
- V1_BATCH_ENDPOINT = "/sdk/events/batch";
7084
- V1_SINGLE_ENDPOINT = "/sdk/events";
7085
7208
  EventBatcher = class {
7086
7209
  constructor(post, debug = false, contextProvider = null) {
7087
7210
  this.criticalQueue = [];
7088
7211
  this.normalQueue = [];
7089
7212
  this.normalTimer = null;
7090
7213
  this.criticalScheduled = false;
7091
- this.v2Disabled = false;
7092
7214
  this.post = post;
7093
7215
  this.debug = debug;
7094
7216
  this.contextProvider = contextProvider;
@@ -7184,11 +7306,7 @@ var init_EventBatcher = __esm({
7184
7306
  try {
7185
7307
  await this.postBatch([event], `event_critical:${event.eventName}`, "critical");
7186
7308
  } catch {
7187
- try {
7188
- await this.post(V1_SINGLE_ENDPOINT, event, `event:${event.eventName}`, "critical");
7189
- } catch {
7190
- if (this.debug) console.log(`[Paywallo] critical event ${event.eventName} post failed`);
7191
- }
7309
+ if (this.debug) console.log(`[Paywallo] critical event ${event.eventName} post failed`);
7192
7310
  }
7193
7311
  }
7194
7312
  }
@@ -7202,50 +7320,24 @@ var init_EventBatcher = __esm({
7202
7320
  try {
7203
7321
  await this.postBatch(batch, `event_batch:${batch.length}`);
7204
7322
  } catch {
7205
- for (const event of batch) {
7206
- await this.post(V1_SINGLE_ENDPOINT, event, `event:${event.eventName}`);
7207
- }
7323
+ if (this.debug) console.log(`[Paywallo] normal batch post failed \u2014 OfflineQueue will handle durability`);
7208
7324
  }
7209
7325
  }
7210
- /**
7211
- * POST the batch with V2 envelope semantics. Tries `/ingest/batch`
7212
- * first; on failure (404/network) falls back to `/events/batch`
7213
- * with the legacy per-event shape for retrocompat.
7214
- *
7215
- * After a single V2 failure we flip `v2Disabled` and stop trying V2 for
7216
- * the lifetime of this EventBatcher — avoids N retries per batch on old
7217
- * servers that will never have the endpoint. A fresh `new EventBatcher()`
7218
- * (e.g. after `Paywallo.init` on a new server URL) resets the flag.
7219
- */
7220
7326
  async postBatch(events, label, priority = "normal") {
7221
- if (!this.v2Disabled) {
7222
- try {
7223
- const providerContext = resolveProviderContext(this.contextProvider);
7224
- const inputs = events.map((e) => ({
7225
- eventName: e.eventName,
7226
- distinctId: e.distinctId,
7227
- properties: e.properties,
7228
- timestamp: e.timestamp
7229
- }));
7230
- const envelope = buildV2Envelope(inputs, providerContext);
7231
- const parsed = v2EnvelopeSchema.safeParse(envelope);
7232
- if (!parsed.success) {
7233
- if (this.debug) console.log("[Paywallo EVENTS] V2 envelope schema validation failed:", parsed.error.issues);
7234
- throw parsed.error;
7235
- }
7236
- await this.post(V2_BATCH_ENDPOINT, parsed.data, label, priority);
7237
- return;
7238
- } catch (error) {
7239
- this.v2Disabled = true;
7240
- if (this.debug) {
7241
- console.log(
7242
- "[Paywallo EVENTS] V2 ingest unavailable, falling back to /events/batch:",
7243
- error
7244
- );
7245
- }
7246
- }
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;
7247
7339
  }
7248
- await this.post(V1_BATCH_ENDPOINT, { events }, label, priority);
7340
+ await this.post(V2_BATCH_ENDPOINT, parsed.data, label, priority);
7249
7341
  }
7250
7342
  };
7251
7343
  }
@@ -7632,6 +7724,26 @@ var init_ApiClient = __esm({
7632
7724
  this.batcher.dispose();
7633
7725
  }
7634
7726
  async identify(distinctId, properties, email, deviceId, pii) {
7727
+ const rawTraits = {
7728
+ email: email || void 0,
7729
+ platform: import_react_native23.Platform.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;
7635
7747
  await postWithQueue(
7636
7748
  {
7637
7749
  isOfflineQueueEnabled: () => this.offlineQueueEnabled,
@@ -7639,12 +7751,11 @@ var init_ApiClient = __esm({
7639
7751
  isDebug: () => this.debug,
7640
7752
  post: (path, body, skipRetry) => this.post(path, body, skipRetry)
7641
7753
  },
7642
- "/sdk/identify",
7754
+ "/sdk/identity/identify",
7643
7755
  {
7644
- distinctId,
7645
- properties: properties ?? {},
7646
- email,
7647
- platform: import_react_native23.Platform.OS,
7756
+ distinct_id: distinctId,
7757
+ traits,
7758
+ ...attribution && { attribution },
7648
7759
  ...deviceId && { deviceId },
7649
7760
  ...pii?.phone && { phone: pii.phone },
7650
7761
  ...pii?.firstName && { firstName: pii.firstName },
@@ -7655,10 +7766,9 @@ var init_ApiClient = __esm({
7655
7766
  "identify"
7656
7767
  );
7657
7768
  }
7658
- // Session lifecycle endpoints (`/api/v1/sessions/start` and `/api/v1/sessions/end`)
7659
- // have been dropped. All session bookkeeping is now derived on the server from
7660
- // the `$session_start` and `session.end` events that flow through the unified
7661
- // 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.
7662
7772
  async getVariant(flagKey, distinctId) {
7663
7773
  return getVariant2(this.flagDeps(), flagKey, distinctId);
7664
7774
  }
@@ -7716,7 +7826,7 @@ var init_ApiClient = __esm({
7716
7826
  const hit = this.cache.getCampaign(key);
7717
7827
  if (hit !== void 0) return hit;
7718
7828
  try {
7719
- const res = await this.get(`/sdk/campaigns/public/primary?distinctId=${encodeURIComponent(distinctId)}`);
7829
+ const res = await this.get(`/campaigns/public/primary?distinctId=${encodeURIComponent(distinctId)}`);
7720
7830
  if (res.status === 404) {
7721
7831
  this.cache.setCampaign(key, null, this.cache.nullTTL);
7722
7832
  return null;
@@ -7816,10 +7926,10 @@ function setupNotifications(apiClient, config, environment, eventPipeline) {
7816
7926
  debug: config.debug,
7817
7927
  environment: environment === "Sandbox" ? "sandbox" : "production"
7818
7928
  }).catch((err) => {
7819
- 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));
7820
7930
  });
7821
7931
  } catch (err) {
7822
- 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));
7823
7933
  }
7824
7934
  }
7825
7935
  function setupAutoPreload(state, apiClient, config, distinctId) {
@@ -7944,7 +8054,7 @@ function reportInitFailure(state, config, error, reason) {
7944
8054
  error: error instanceof Error ? error.message : String(error)
7945
8055
  });
7946
8056
  } catch (err) {
7947
- if (config.debug) console.error("[Paywallo] reportInitFailure error", err);
8057
+ console.error("[Paywallo] reportInitFailure error", err);
7948
8058
  }
7949
8059
  }
7950
8060
  function reportInitSuccess(state, config, attempts) {
@@ -7953,7 +8063,7 @@ function reportInitSuccess(state, config, attempts) {
7953
8063
  void state.apiClient.reportError("INIT_RECOVERED", `Succeeded after ${attempts} retries`, { attempts });
7954
8064
  }
7955
8065
  } catch (err) {
7956
- if (config.debug) console.error("[Paywallo] reportInitSuccess error", err);
8066
+ console.error("[Paywallo] reportInitSuccess error", err);
7957
8067
  }
7958
8068
  }
7959
8069
  async function resolveSessionFlags(state, keys, apiClient, distinctId) {
@@ -8007,7 +8117,7 @@ async function doInit(state, config) {
8007
8117
  try {
8008
8118
  await identityManager.identify(pending);
8009
8119
  } catch (err) {
8010
- if (config.debug) console.warn("[Paywallo INIT] pending identify replay failed", err);
8120
+ console.error("[Paywallo INIT] pending identify replay failed", err);
8011
8121
  }
8012
8122
  }
8013
8123
  state.pendingIdentifies = [];
@@ -8085,7 +8195,7 @@ async function doInit(state, config) {
8085
8195
  setupNotifications(apiClient, config, environment, eventPipeline);
8086
8196
  if (config.autoStartSession !== false) {
8087
8197
  sessionManager.startSession().catch((err) => {
8088
- if (config.debug) console.warn("[Paywallo INIT] autoStart session error", err);
8198
+ console.error("[Paywallo INIT] autoStart session error", err);
8089
8199
  });
8090
8200
  }
8091
8201
  new InstallTracker(config.debug, {
@@ -8103,7 +8213,7 @@ async function doInit(state, config) {
8103
8213
  state.resolveReady();
8104
8214
  state.resolveReady = null;
8105
8215
  }
8106
- if (config.debug) console.warn("[Paywallo] initialized successfully");
8216
+ if (config.debug) console.log("[Paywallo] initialized successfully");
8107
8217
  if (distinctId) {
8108
8218
  apiClient.getSubscriptionStatus(distinctId).then(() => {
8109
8219
  if (config.debug) console.log("[Paywallo SUB] preloaded on boot");
@@ -8220,7 +8330,7 @@ async function checkActiveSubscription(state) {
8220
8330
  if (state.config?.debug) console.log("[Paywallo SUB] hasActiveSubscription (server):", isActive);
8221
8331
  return isActive;
8222
8332
  } catch (error) {
8223
- if (state.config?.debug) console.error("[Paywallo SUB] hasActiveSubscription error", error);
8333
+ console.error("[Paywallo SUB] hasActiveSubscription error", error);
8224
8334
  if (state.apiClient) void state.apiClient.reportError("SUBSCRIPTION_CHECK_FAILED", error instanceof Error ? error.message : "hasActiveSubscription failed");
8225
8335
  return false;
8226
8336
  }
@@ -8665,6 +8775,7 @@ var init_OfferingService = __esm({
8665
8775
  var index_exports = {};
8666
8776
  __export(index_exports, {
8667
8777
  ApiClient: () => ApiClient,
8778
+ AttributionTracker: () => AttributionTracker,
8668
8779
  CAMPAIGN_ERROR_CODES: () => CAMPAIGN_ERROR_CODES,
8669
8780
  CampaignError: () => CampaignError,
8670
8781
  ERROR_CODES: () => PURCHASE_ERROR_CODES,
@@ -8699,6 +8810,7 @@ __export(index_exports, {
8699
8810
  SessionError: () => SessionError,
8700
8811
  SessionManager: () => SessionManager,
8701
8812
  SubscriptionCache: () => SubscriptionCache,
8813
+ attributionTracker: () => attributionTracker,
8702
8814
  createPurchaseError: () => createPurchaseError,
8703
8815
  default: () => index_default,
8704
8816
  detectDeviceLanguage: () => detectDeviceLanguage,
@@ -8761,6 +8873,7 @@ init_PurchaseError();
8761
8873
  // src/index.ts
8762
8874
  init_identity();
8763
8875
  init_identity();
8876
+ init_identity();
8764
8877
  init_notifications();
8765
8878
 
8766
8879
  // src/domains/offering/index.ts
@@ -9432,7 +9545,6 @@ function useCampaignPreload() {
9432
9545
 
9433
9546
  // src/hooks/useProductLoader.ts
9434
9547
  var import_react22 = require("react");
9435
- init_PaywalloClient();
9436
9548
  function useProductLoader() {
9437
9549
  const [products, setProducts] = (0, import_react22.useState)(/* @__PURE__ */ new Map());
9438
9550
  const [isLoadingProducts, setIsLoadingProducts] = (0, import_react22.useState)(false);
@@ -9445,7 +9557,7 @@ function useProductLoader() {
9445
9557
  for (const p of loaded) map.set(p.productId, p);
9446
9558
  setProducts(map);
9447
9559
  } catch (err) {
9448
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] refreshProducts failed", err);
9560
+ console.error("[Paywallo] refreshProducts failed", err);
9449
9561
  } finally {
9450
9562
  setIsLoadingProducts(false);
9451
9563
  }
@@ -9455,7 +9567,6 @@ function useProductLoader() {
9455
9567
 
9456
9568
  // src/hooks/usePurchaseOrchestrator.ts
9457
9569
  var import_react23 = require("react");
9458
- init_PaywalloClient();
9459
9570
  init_paywall();
9460
9571
  function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, invalidateSubscriptionCache) {
9461
9572
  const { trackDismissed, trackProductSelected, trackPurchased } = usePaywallTracking();
@@ -9489,7 +9600,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
9489
9600
  clearPreloadedWebView();
9490
9601
  resolvePreloadedPaywall({ presented: true, purchased: true, cancelled: false, restored: false });
9491
9602
  } catch (error) {
9492
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] preloaded purchase failed", error);
9603
+ console.error("[Paywallo] preloaded purchase failed", error);
9493
9604
  setShowingPreloadedWebView(false);
9494
9605
  clearPreloadedWebView();
9495
9606
  resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: false });
@@ -9510,7 +9621,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
9510
9621
  clearPreloadedWebView();
9511
9622
  resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: true });
9512
9623
  } catch (error) {
9513
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] preloaded restore failed", error);
9624
+ console.error("[Paywallo] preloaded restore failed", error);
9514
9625
  setShowingPreloadedWebView(false);
9515
9626
  clearPreloadedWebView();
9516
9627
  resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: false });
@@ -10163,6 +10274,7 @@ var index_default = Paywallo;
10163
10274
  // Annotate the CommonJS export names for ESM import in node:
10164
10275
  0 && (module.exports = {
10165
10276
  ApiClient,
10277
+ AttributionTracker,
10166
10278
  CAMPAIGN_ERROR_CODES,
10167
10279
  CampaignError,
10168
10280
  ERROR_CODES,
@@ -10197,6 +10309,7 @@ var index_default = Paywallo;
10197
10309
  SessionError,
10198
10310
  SessionManager,
10199
10311
  SubscriptionCache,
10312
+ attributionTracker,
10200
10313
  createPurchaseError,
10201
10314
  detectDeviceLanguage,
10202
10315
  getCurrentLanguage,