@virex-tech/paywallo-sdk 2.2.3 → 2.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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
  }),
@@ -2177,7 +2172,7 @@ function setupForegroundHandler(deps, onReceive) {
2177
2172
  title: data["title"] ?? message.notification?.title ?? "",
2178
2173
  body: data["body"] ?? message.notification?.body ?? ""
2179
2174
  });
2180
- if (debug) console.warn("[Paywallo PUSH] foreground message received:", payload.notificationId);
2175
+ if (debug) console.log("[Paywallo PUSH] foreground message received:", payload.notificationId);
2181
2176
  onReceive(payload);
2182
2177
  const messageId = message.messageId ?? payload.notificationId;
2183
2178
  await tracker.track("notification_delivered", {
@@ -2207,14 +2202,21 @@ function getTimezone() {
2207
2202
  function detectPlatform() {
2208
2203
  return import_react_native10.Platform.OS === "ios" ? "ios" : "android";
2209
2204
  }
2210
- var import_react_native10, SEEN_MESSAGES_KEY, SEEN_TTL_MS, MAX_SEEN_SIZE, NotificationEventTracker;
2205
+ var import_react_native10, SEEN_MESSAGES_KEY, LEGACY_SEEN_MESSAGES_KEY, SEEN_TTL_MS, MAX_SEEN_SIZE, NOTIFICATION_TYPE_TO_FAMILY, NotificationEventTracker;
2211
2206
  var init_NotificationEventTracker = __esm({
2212
2207
  "src/domains/notifications/NotificationEventTracker.ts"() {
2213
2208
  "use strict";
2214
2209
  import_react_native10 = require("react-native");
2215
- SEEN_MESSAGES_KEY = "seen_messages";
2210
+ SEEN_MESSAGES_KEY = "@paywallo:seen_messages";
2211
+ LEGACY_SEEN_MESSAGES_KEY = "seen_messages";
2216
2212
  SEEN_TTL_MS = 60 * 60 * 1e3;
2217
2213
  MAX_SEEN_SIZE = 1e3;
2214
+ NOTIFICATION_TYPE_TO_FAMILY = {
2215
+ notification_delivered: "delivered",
2216
+ notification_displayed: "displayed",
2217
+ notification_clicked: "clicked",
2218
+ notification_dismissed: "dismissed"
2219
+ };
2218
2220
  NotificationEventTracker = class {
2219
2221
  constructor(batcher, config, secureStorage2) {
2220
2222
  this.batcher = batcher;
@@ -2235,9 +2237,13 @@ var init_NotificationEventTracker = __esm({
2235
2237
  if (this.config.debug) console.log("[Paywallo PUSH] skipped (no distinctId):", eventType);
2236
2238
  return;
2237
2239
  }
2240
+ const canonicalType = NOTIFICATION_TYPE_TO_FAMILY[eventType];
2241
+ if (!canonicalType) {
2242
+ if (this.config.debug) console.log("[Paywallo PUSH] eventType not mapped to family schema, skipping ingest:", eventType);
2243
+ return;
2244
+ }
2238
2245
  const properties = {
2239
- family: "notification",
2240
- type: eventType,
2246
+ type: canonicalType,
2241
2247
  notification_id: notificationId,
2242
2248
  campaign_id: campaignId,
2243
2249
  variant_key: variantKey ?? null,
@@ -2249,7 +2255,7 @@ var init_NotificationEventTracker = __esm({
2249
2255
  if (payload.failureReason) properties.failure_reason = payload.failureReason;
2250
2256
  if (payload.appUserId) properties.app_user_id = payload.appUserId;
2251
2257
  if (payload.properties) Object.assign(properties, payload.properties);
2252
- if (this.config.debug) console.log("[Paywallo PUSH] tracking event:", eventType, notificationId);
2258
+ if (this.config.debug) console.log("[Paywallo PUSH] tracking event:", canonicalType, notificationId);
2253
2259
  await this.batcher.trackEvent("notification", distinctId, properties);
2254
2260
  }
2255
2261
  async isDuplicate(messageId, eventType) {
@@ -2294,7 +2300,10 @@ var init_NotificationEventTracker = __esm({
2294
2300
  return;
2295
2301
  }
2296
2302
  try {
2297
- const raw = await this.secureStorage.get(SEEN_MESSAGES_KEY);
2303
+ let raw = await this.secureStorage.get(SEEN_MESSAGES_KEY);
2304
+ if (!raw) {
2305
+ raw = await this.secureStorage.get(LEGACY_SEEN_MESSAGES_KEY).catch(() => null);
2306
+ }
2298
2307
  if (raw) {
2299
2308
  const parsed = JSON.parse(raw);
2300
2309
  const now = Date.now();
@@ -2368,8 +2377,8 @@ var init_NotificationHandlerSetup = __esm({
2368
2377
  // src/core/version.ts
2369
2378
  function resolveVersion() {
2370
2379
  try {
2371
- if ("2.2.3") {
2372
- return "2.2.3";
2380
+ if ("2.2.4") {
2381
+ return "2.2.4";
2373
2382
  }
2374
2383
  } catch {
2375
2384
  }
@@ -2501,7 +2510,7 @@ async function buildRegistration(token, getDeviceId, getDistinctId) {
2501
2510
  async function registerToken(deps, token) {
2502
2511
  const { httpClient, appKey, getDeviceId, getDistinctId, debug } = deps;
2503
2512
  if (!httpClient || !appKey) {
2504
- if (debug) console.warn("[Paywallo PUSH] httpClient not ready, skipping token registration");
2513
+ if (debug) console.log("[Paywallo PUSH] httpClient not ready, skipping token registration");
2505
2514
  return;
2506
2515
  }
2507
2516
  const registration = await buildRegistration(token, getDeviceId, getDistinctId);
@@ -2511,22 +2520,25 @@ async function registerToken(deps, token) {
2511
2520
  registration,
2512
2521
  { headers: { "X-App-Key": appKey } }
2513
2522
  );
2514
- if (debug) console.warn("[Paywallo PUSH] token registered successfully");
2523
+ if (debug) console.log("[Paywallo PUSH] token registered successfully");
2515
2524
  } catch (err) {
2516
- if (debug) console.warn("[Paywallo PUSH] token registration failed:", err);
2525
+ console.error("[Paywallo PUSH] token registration failed:", err);
2517
2526
  throw new RegistrationFailedError("Token registration failed", err);
2518
2527
  }
2519
2528
  }
2520
- async function unregisterToken(httpClient, appKey, debug) {
2529
+ async function unregisterToken(httpClient, appKey, debug, token, distinctId) {
2521
2530
  if (!httpClient || !appKey) return;
2531
+ if (!token || !distinctId) return;
2522
2532
  try {
2533
+ const body = JSON.stringify({ token, distinct_id: distinctId });
2523
2534
  await httpClient.request(TOKEN_ENDPOINT, {
2524
2535
  method: "DELETE",
2525
- headers: { "X-App-Key": appKey },
2536
+ headers: { "X-App-Key": appKey, "Content-Type": "application/json" },
2537
+ body,
2526
2538
  timeout: 1e4
2527
2539
  });
2528
2540
  } catch (err) {
2529
- if (debug) console.warn("[Paywallo PUSH] optOut error:", err);
2541
+ console.error("[Paywallo PUSH] optOut error:", err);
2530
2542
  }
2531
2543
  }
2532
2544
  var FCM_TOKEN_KEY, TOKEN_ENDPOINT;
@@ -2558,7 +2570,7 @@ async function registerAndPersistToken(deps, token) {
2558
2570
  }
2559
2571
  function subscribeTokenRefresh(bridge, deps, onToken) {
2560
2572
  return bridge.onTokenRefresh((newToken) => {
2561
- if (deps.debug) console.warn("[Paywallo PUSH] token refreshed");
2573
+ if (deps.debug) console.log("[Paywallo PUSH] token refreshed");
2562
2574
  onToken(newToken);
2563
2575
  persistToken(deps.secureStorage, newToken).catch(() => void 0);
2564
2576
  registerToken(
@@ -2574,8 +2586,8 @@ function subscribeTokenRefresh(bridge, deps, onToken) {
2574
2586
  ).catch(() => void 0);
2575
2587
  });
2576
2588
  }
2577
- async function optOutToken(httpClient, secureStorage2, appKey, debug) {
2578
- await unregisterToken(httpClient, appKey, debug);
2589
+ async function optOutToken(httpClient, secureStorage2, appKey, debug, token, distinctId) {
2590
+ await unregisterToken(httpClient, appKey, debug, token, distinctId);
2579
2591
  await clearPersistedToken(secureStorage2);
2580
2592
  }
2581
2593
  async function invalidateLocalTokenStorage(secureStorage2, debug) {
@@ -2595,7 +2607,7 @@ async function captureAndRegisterToken(bridge, deps, onTokenCaptured, subscribeR
2595
2607
  await waitForApnsToken(bridge, deps.debug);
2596
2608
  const token = await bridge.getToken();
2597
2609
  if (!token) {
2598
- if (deps.debug) console.warn("[Paywallo PUSH] token unavailable after grant");
2610
+ if (deps.debug) console.log("[Paywallo PUSH] token unavailable after grant");
2599
2611
  return;
2600
2612
  }
2601
2613
  onTokenCaptured(token);
@@ -2662,18 +2674,18 @@ var init_PermissionManager = __esm({
2662
2674
  }
2663
2675
  async requestPermission(options) {
2664
2676
  const platform = this.platformOverride ?? getPlatform();
2665
- if (this.debug) console.warn("[paywallo-push] platform detected:", platform.OS, "version:", String(platform.Version ?? "n/a"));
2677
+ if (this.debug) console.log("[paywallo-push] platform detected:", platform.OS, "version:", String(platform.Version ?? "n/a"));
2666
2678
  if (platform.OS === "ios") {
2667
- if (this.debug) console.warn("[paywallo-push] requestPermission iOS \u2014 calling bridge...");
2679
+ if (this.debug) console.log("[paywallo-push] requestPermission iOS \u2014 calling bridge...");
2668
2680
  try {
2669
2681
  const raw2 = await this.bridge.requestPermission(options);
2670
- if (this.debug) console.warn("[paywallo-push] bridge returned raw:", raw2);
2682
+ if (this.debug) console.log("[paywallo-push] bridge returned raw:", raw2);
2671
2683
  const status2 = mapPermissionStatus2(raw2);
2672
- if (this.debug) console.warn("[paywallo-push] mapped status:", status2);
2684
+ if (this.debug) console.log("[paywallo-push] mapped status:", status2);
2673
2685
  this.emitOsEvent(status2);
2674
2686
  return status2;
2675
2687
  } catch (err) {
2676
- if (this.debug) console.warn("[paywallo-push] requestPermission iOS CRASHED:", err instanceof Error ? err.message : String(err));
2688
+ console.error("[paywallo-push] requestPermission iOS CRASHED:", err instanceof Error ? err.message : String(err));
2677
2689
  throw err;
2678
2690
  }
2679
2691
  }
@@ -2806,11 +2818,11 @@ var init_NotificationsManager = __esm({
2806
2818
  await waitForApnsToken(this.bridge, this.debug);
2807
2819
  const token = await this.bridge.getToken();
2808
2820
  if (!token) {
2809
- if (this.debug) console.warn("[Paywallo PUSH] token unavailable, skipping registration");
2821
+ if (this.debug) console.log("[Paywallo PUSH] token unavailable, skipping registration");
2810
2822
  this.isInitialized = true;
2811
2823
  return;
2812
2824
  }
2813
- if (this.debug) console.warn("[Paywallo PUSH] token acquired:", token);
2825
+ if (this.debug) console.log("[Paywallo PUSH] token acquired:", token);
2814
2826
  this.currentToken = token;
2815
2827
  await this.doRegisterToken(token);
2816
2828
  this.subscribeTokenRefresh();
@@ -2828,7 +2840,7 @@ var init_NotificationsManager = __esm({
2828
2840
  async refreshPermissionStatus() {
2829
2841
  const permStatus = await this.bridge.hasPermission();
2830
2842
  this.permissionStatus = mapPermissionStatus(permStatus);
2831
- if (this.debug) console.warn("[Paywallo PUSH] permission status:", this.permissionStatus);
2843
+ if (this.debug) console.log("[Paywallo PUSH] permission status:", this.permissionStatus);
2832
2844
  }
2833
2845
  tokenDeps() {
2834
2846
  return {
@@ -2856,7 +2868,7 @@ var init_NotificationsManager = __esm({
2856
2868
  async optOut() {
2857
2869
  await optOutToken(this.httpClient, this.secureStorage, this.appKey, this.debug);
2858
2870
  this.currentToken = null;
2859
- if (this.debug) console.warn("[Paywallo PUSH] opted out");
2871
+ if (this.debug) console.log("[Paywallo PUSH] opted out");
2860
2872
  }
2861
2873
  /** Clears token locally without server call (used on sign-out/reset). */
2862
2874
  async invalidateLocalToken() {
@@ -2873,21 +2885,21 @@ var init_NotificationsManager = __esm({
2873
2885
  }
2874
2886
  /** Requests OS permission and, on success, captures + registers the push token. */
2875
2887
  async requestPushPermission(options) {
2876
- if (this.debug) console.warn("[Paywallo PUSH] requestPushPermission called");
2888
+ if (this.debug) console.log("[Paywallo PUSH] requestPushPermission called");
2877
2889
  try {
2878
2890
  const status = await this.permissionManager.requestPermission(options);
2879
- if (this.debug) console.warn("[Paywallo PUSH] permissionManager returned:", status);
2891
+ if (this.debug) console.log("[Paywallo PUSH] permissionManager returned:", status);
2880
2892
  this.permissionStatus = status;
2881
2893
  if (status === "granted" || status === "provisional") {
2882
- if (this.debug) console.warn("[Paywallo PUSH] permission granted \u2014 capturing token...");
2894
+ if (this.debug) console.log("[Paywallo PUSH] permission granted \u2014 capturing token...");
2883
2895
  await this.captureAndRegisterToken();
2884
- if (this.debug) console.warn("[Paywallo PUSH] token capture complete");
2896
+ if (this.debug) console.log("[Paywallo PUSH] token capture complete");
2885
2897
  } else {
2886
- if (this.debug) console.warn("[Paywallo PUSH] permission not granted, skipping token register:", status);
2898
+ if (this.debug) console.log("[Paywallo PUSH] permission not granted, skipping token register:", status);
2887
2899
  }
2888
2900
  return status;
2889
2901
  } catch (err) {
2890
- if (this.debug) console.warn("[Paywallo PUSH] requestPushPermission CRASHED:", err instanceof Error ? err.message : String(err));
2902
+ console.error("[Paywallo PUSH] requestPushPermission CRASHED:", err instanceof Error ? err.message : String(err));
2891
2903
  throw err;
2892
2904
  }
2893
2905
  }
@@ -3049,7 +3061,9 @@ function parseWebViewMessage(raw) {
3049
3061
  const safePayload = payload !== null && typeof payload === "object" ? {
3050
3062
  productId: typeof payload.productId === "string" ? String(payload.productId) : void 0,
3051
3063
  url: typeof payload.url === "string" ? String(payload.url) : void 0,
3052
- timestamp: typeof payload.timestamp === "number" ? Number(payload.timestamp) : void 0
3064
+ timestamp: typeof payload.timestamp === "number" ? Number(payload.timestamp) : void 0,
3065
+ variantKey: typeof payload.variantKey === "string" ? String(payload.variantKey) : void 0,
3066
+ style: HAPTIC_STYLES.has(String(payload.style)) ? String(payload.style) : typeof payload.style !== "undefined" ? "light" : void 0
3053
3067
  } : void 0;
3054
3068
  return {
3055
3069
  type: parsed.type,
@@ -3060,17 +3074,19 @@ function parseWebViewMessage(raw) {
3060
3074
  return null;
3061
3075
  }
3062
3076
  }
3063
- var ALLOWED_MESSAGE_TYPES;
3077
+ var HAPTIC_STYLES, ALLOWED_MESSAGE_TYPES;
3064
3078
  var init_parseWebViewMessage = __esm({
3065
3079
  "src/domains/paywall/parseWebViewMessage.ts"() {
3066
3080
  "use strict";
3081
+ HAPTIC_STYLES = /* @__PURE__ */ new Set(["light", "medium", "heavy"]);
3067
3082
  ALLOWED_MESSAGE_TYPES = /* @__PURE__ */ new Set([
3068
3083
  "purchase",
3069
3084
  "close",
3070
3085
  "restore",
3071
3086
  "select-product",
3072
3087
  "open-url",
3073
- "ready"
3088
+ "ready",
3089
+ "haptic"
3074
3090
  ]);
3075
3091
  }
3076
3092
  });
@@ -3112,7 +3128,7 @@ var init_PaywallErrorBoundary = __esm({
3112
3128
  return { hasError: true };
3113
3129
  }
3114
3130
  componentDidCatch(error) {
3115
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] Paywall render error:", error);
3131
+ console.error("[Paywallo] Paywall render error:", error);
3116
3132
  try {
3117
3133
  PaywalloClient.getApiClient()?.reportError("PAYWALL_RENDER_ERROR", error.message);
3118
3134
  } catch {
@@ -3279,7 +3295,9 @@ function buildPaywallInjectionScript(data) {
3279
3295
  craftData: data.craftData,
3280
3296
  products: data.products,
3281
3297
  primaryProductId: data.primaryProductId,
3282
- secondaryProductId: data.secondaryProductId
3298
+ secondaryProductId: data.secondaryProductId,
3299
+ currentLanguage: getCurrentLanguage(),
3300
+ defaultLanguage: getDefaultLanguage()
3283
3301
  }
3284
3302
  };
3285
3303
  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 +3305,28 @@ function buildPaywallInjectionScript(data) {
3287
3305
  function buildPurchaseStateScript(isPurchasing) {
3288
3306
  return `if(window.receiveNativeMessage){window.receiveNativeMessage({type:"purchaseState",data:{isPurchasing:${isPurchasing}}});}true;`;
3289
3307
  }
3308
+ function buildBackButtonScript(nonce = Date.now()) {
3309
+ return [
3310
+ `/*${nonce}*/`,
3311
+ "(function(){",
3312
+ "try{",
3313
+ "var h=window.__paywalloHandleBackPress;",
3314
+ "var consumed=typeof h==='function'?!!h():false;",
3315
+ "if(!consumed&&window.ReactNativeWebView){",
3316
+ 'window.ReactNativeWebView.postMessage(JSON.stringify({type:"close"}));',
3317
+ "}",
3318
+ "}catch(e){",
3319
+ "if(window.ReactNativeWebView){",
3320
+ 'window.ReactNativeWebView.postMessage(JSON.stringify({type:"close"}));',
3321
+ "}",
3322
+ "}",
3323
+ "})();true;"
3324
+ ].join("");
3325
+ }
3290
3326
  var init_paywallScripts = __esm({
3291
3327
  "src/domains/paywall/paywallScripts.ts"() {
3292
3328
  "use strict";
3329
+ init_localization();
3293
3330
  }
3294
3331
  });
3295
3332
 
@@ -3312,6 +3349,24 @@ function getWebViewEmitter() {
3312
3349
  webViewEmitter = new import_react_native14.NativeEventEmitter(mod);
3313
3350
  return webViewEmitter;
3314
3351
  }
3352
+ async function triggerNativeHaptic(style) {
3353
+ try {
3354
+ const module2 = await import("react-native-haptic-feedback");
3355
+ const hapticModule = module2.default ?? module2;
3356
+ const trigger = hapticModule.trigger;
3357
+ if (typeof trigger !== "function") return;
3358
+ const typeMap = {
3359
+ light: "impactLight",
3360
+ medium: "impactMedium",
3361
+ heavy: "impactHeavy"
3362
+ };
3363
+ trigger(typeMap[style], {
3364
+ enableVibrateFallback: true,
3365
+ ignoreAndroidSystemSettings: false
3366
+ });
3367
+ } catch {
3368
+ }
3369
+ }
3315
3370
  function parseErrorEvent(raw) {
3316
3371
  return {
3317
3372
  code: typeof raw["code"] === "number" ? raw["code"] : -1,
@@ -3336,6 +3391,7 @@ function PaywallWebView({
3336
3391
  const paywallDataSent = (0, import_react3.useRef)(false);
3337
3392
  const loadEndReceived = (0, import_react3.useRef)(false);
3338
3393
  const [scriptToInject, setScriptToInject] = (0, import_react3.useState)(void 0);
3394
+ const webViewRef = (0, import_react3.useRef)(null);
3339
3395
  const onErrorRef = (0, import_react3.useRef)(onError);
3340
3396
  onErrorRef.current = onError;
3341
3397
  const onReadyRef = (0, import_react3.useRef)(onReady);
@@ -3379,6 +3435,15 @@ function PaywallWebView({
3379
3435
  });
3380
3436
  }
3381
3437
  }, []);
3438
+ const injectScript = (0, import_react3.useCallback)((script) => {
3439
+ if (import_react_native14.Platform.OS === "android") {
3440
+ const handle = (0, import_react_native14.findNodeHandle)(webViewRef.current);
3441
+ if (handle == null) return;
3442
+ import_react_native14.UIManager.dispatchViewManagerCommand(handle, "injectJavaScript", [script]);
3443
+ return;
3444
+ }
3445
+ setScriptToInject(script);
3446
+ }, []);
3382
3447
  const buildAndSetInjectionScript = (0, import_react3.useCallback)(() => {
3383
3448
  if (!craftData || paywallDataSent.current) return;
3384
3449
  paywallDataSent.current = true;
@@ -3388,8 +3453,8 @@ function PaywallWebView({
3388
3453
  primaryProductId,
3389
3454
  secondaryProductId
3390
3455
  });
3391
- setScriptToInject(script);
3392
- }, [craftData, products, primaryProductId, secondaryProductId]);
3456
+ injectScript(script);
3457
+ }, [craftData, products, primaryProductId, secondaryProductId, injectScript]);
3393
3458
  const processedIdsRef = (0, import_react3.useRef)(/* @__PURE__ */ new Set());
3394
3459
  const dedupeTimersRef = (0, import_react3.useRef)(/* @__PURE__ */ new Map());
3395
3460
  (0, import_react3.useEffect)(() => {
@@ -3426,6 +3491,9 @@ function PaywallWebView({
3426
3491
  case "restore":
3427
3492
  onRestore();
3428
3493
  break;
3494
+ case "select-product":
3495
+ console.log("[Paywallo:PaywallWebView] select-product received", message.payload?.productId);
3496
+ break;
3429
3497
  case "open-url":
3430
3498
  if (message.payload?.url) {
3431
3499
  const url = message.payload.url;
@@ -3434,6 +3502,9 @@ function PaywallWebView({
3434
3502
  }
3435
3503
  }
3436
3504
  break;
3505
+ case "haptic":
3506
+ void triggerNativeHaptic(message.payload?.style ?? "light");
3507
+ break;
3437
3508
  }
3438
3509
  },
3439
3510
  [buildAndSetInjectionScript, onPurchase, onClose, onRestore]
@@ -3505,13 +3576,24 @@ function PaywallWebView({
3505
3576
  );
3506
3577
  (0, import_react3.useEffect)(() => {
3507
3578
  if (!paywallDataSent.current) return;
3508
- setScriptToInject(buildPurchaseStateScript(isPurchasing));
3509
- }, [isPurchasing]);
3579
+ injectScript(buildPurchaseStateScript(isPurchasing));
3580
+ }, [isPurchasing, injectScript]);
3581
+ (0, import_react3.useEffect)(() => {
3582
+ if (import_react_native14.Platform.OS !== "android") return;
3583
+ const onBackPress = () => {
3584
+ if (!paywallDataSent.current) return false;
3585
+ injectScript(buildBackButtonScript());
3586
+ return true;
3587
+ };
3588
+ const subscription = import_react_native14.BackHandler.addEventListener("hardwareBackPress", onBackPress);
3589
+ return () => subscription.remove();
3590
+ }, [injectScript]);
3510
3591
  if (!resolvedWebUrl || !NativeWebView) return null;
3511
3592
  const paywallUrl = `${resolvedWebUrl}/paywall/${paywallId}`;
3512
3593
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native14.View, { style: styles.container, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3513
3594
  NativeWebView,
3514
3595
  {
3596
+ ref: webViewRef,
3515
3597
  sourceUrl: paywallUrl,
3516
3598
  injectedJavaScript: scriptToInject,
3517
3599
  onMessage: handleMessage,
@@ -3741,7 +3823,7 @@ var init_NativeStoreKit = __esm({
3741
3823
  try {
3742
3824
  await PaywalloStoreKitNative.finishTransaction(transactionId);
3743
3825
  } catch (err) {
3744
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] finishTransaction native error", err);
3826
+ console.error("[Paywallo] finishTransaction native error", err);
3745
3827
  }
3746
3828
  },
3747
3829
  async getActiveTransactions() {
@@ -3750,7 +3832,7 @@ var init_NativeStoreKit = __esm({
3750
3832
  const results = await PaywalloStoreKitNative.getActiveTransactions();
3751
3833
  return results.map(mapNativePurchase);
3752
3834
  } catch (err) {
3753
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] getActiveTransactions failed", err);
3835
+ console.error("[Paywallo] getActiveTransactions failed", err);
3754
3836
  return [];
3755
3837
  }
3756
3838
  },
@@ -4601,7 +4683,7 @@ var init_QueueProcessor = __esm({
4601
4683
  }
4602
4684
  const allItems = offlineQueue.getQueue();
4603
4685
  const eventItems = allItems.filter(
4604
- (i) => (i.url.includes("/events") || i.url.includes("/ingest")) && !i.url.includes("/batch")
4686
+ (i) => i.url.includes("/events") || i.url.includes("/ingest")
4605
4687
  );
4606
4688
  const eventItemIds = new Set(eventItems.map((i) => i.id));
4607
4689
  const { processed: batchProcessed, failed: batchFailed, retryIds } = await this.processEventBatches(eventItems);
@@ -4641,7 +4723,7 @@ var init_QueueProcessor = __esm({
4641
4723
  const freshHeaders = this.config.getFreshHeaders?.() ?? {};
4642
4724
  const headers = { ...persistedHeaders, ...freshHeaders };
4643
4725
  this.log("Sending event batch", { size: batch.length, batchIndex: Math.floor(i / BATCH_SIZE) });
4644
- const response = await this.httpClient.request("/sdk/events/batch", {
4726
+ const response = await this.httpClient.request("/sdk/ingest/batch", {
4645
4727
  method: "POST",
4646
4728
  body: { events: bodies },
4647
4729
  headers,
@@ -4836,19 +4918,19 @@ var init_IAPValidator = __esm({
4836
4918
  const appKey = apiClient.getAppKey();
4837
4919
  const body = {
4838
4920
  platform,
4839
- receipt: purchase.receipt,
4840
- productId: purchase.productId,
4841
- transactionId: purchase.transactionId,
4842
- priceLocal: product?.priceValue ?? 0,
4921
+ receipt_data: purchase.receipt,
4922
+ product_id: purchase.productId,
4923
+ transaction_id: purchase.transactionId,
4924
+ price_local: product?.priceValue ?? 0,
4843
4925
  currency: product?.currency ?? "USD",
4844
4926
  country: this.getDeviceCountry(),
4845
- distinctId
4927
+ distinct_id: distinctId
4846
4928
  };
4847
- if (options?.paywallPlacement !== void 0) body.paywallPlacement = options.paywallPlacement;
4848
- if (options?.variantKey !== void 0) body.variantKey = options.variantKey;
4929
+ if (options?.paywallPlacement !== void 0) body.paywall_placement = options.paywallPlacement;
4930
+ if (options?.variantKey !== void 0) body.variant_key = options.variantKey;
4849
4931
  await offlineQueue.enqueue(
4850
4932
  "POST",
4851
- "/purchases/validate",
4933
+ "/sdk/purchases/validate",
4852
4934
  body,
4853
4935
  { "X-App-Key": appKey },
4854
4936
  `validate:${purchase.transactionId}`,
@@ -4856,7 +4938,7 @@ var init_IAPValidator = __esm({
4856
4938
  );
4857
4939
  if (this.getDebug()) console.log("[Paywallo PURCHASE] validation retry enqueued", purchase.transactionId);
4858
4940
  } catch (err) {
4859
- if (this.getDebug()) console.error("[Paywallo PURCHASE] failed to enqueue validation retry", err);
4941
+ console.error("[Paywallo PURCHASE] failed to enqueue validation retry", err);
4860
4942
  }
4861
4943
  }
4862
4944
  };
@@ -4935,7 +5017,7 @@ var init_IAPService = __esm({
4935
5017
  }
4936
5018
  return products;
4937
5019
  } catch (err) {
4938
- if (this.debug) console.error("[Paywallo] loadProducts failed", err);
5020
+ console.error("[Paywallo] loadProducts failed", err);
4939
5021
  return [];
4940
5022
  }
4941
5023
  }
@@ -4954,7 +5036,7 @@ var init_IAPService = __esm({
4954
5036
  await nativeStoreKit.finishTransaction(transactionId);
4955
5037
  this.finishedTransactions.add(transactionId);
4956
5038
  } catch (err) {
4957
- if (this.debug) console.error("[Paywallo] finishTransaction failed", err);
5039
+ console.error("[Paywallo] finishTransaction failed", err);
4958
5040
  }
4959
5041
  }
4960
5042
  startTransactionListener() {
@@ -5003,7 +5085,7 @@ var init_IAPService = __esm({
5003
5085
  await this.finishTransaction(purchase.transactionId);
5004
5086
  return;
5005
5087
  }
5006
- if (this.debug) console.error("[Paywallo PURCHASE] background validation failed (network) \u2014 queued for retry", err);
5088
+ console.error("[Paywallo PURCHASE] background validation failed (network) \u2014 queued for retry", err);
5007
5089
  await this.validator.enqueueValidationRetry(purchase, productId, {
5008
5090
  paywallPlacement: options?.paywallPlacement,
5009
5091
  variantKey: options?.variantKey
@@ -5011,7 +5093,7 @@ var init_IAPService = __esm({
5011
5093
  });
5012
5094
  return { success: true, purchase };
5013
5095
  } catch (error) {
5014
- if (this.debug) console.error(`[Paywallo PURCHASE] ${productId} failed`, error);
5096
+ console.error(`[Paywallo PURCHASE] ${productId} failed`, error);
5015
5097
  const e = error instanceof PurchaseError ? error : new PurchaseError(PURCHASE_ERROR_CODES.PURCHASE_FAILED, error instanceof Error ? error.message : String(error));
5016
5098
  return { success: false, error: e };
5017
5099
  } finally {
@@ -5055,7 +5137,7 @@ var init_IAPService = __esm({
5055
5137
  if (this.debug) console.log("[Paywallo RESTORE] validation rejected (4xx) \u2014 dropping", tx.transactionId);
5056
5138
  await this.finishTransaction(tx.transactionId);
5057
5139
  } else {
5058
- if (this.debug) console.error("[Paywallo RESTORE] validation failed (network) \u2014 queued for retry", r.reason);
5140
+ console.error("[Paywallo RESTORE] validation failed (network) \u2014 queued for retry", r.reason);
5059
5141
  await this.validator.enqueueValidationRetry(tx, tx.productId);
5060
5142
  }
5061
5143
  } else if (r.status === "fulfilled" && !r.value.success) {
@@ -5065,7 +5147,7 @@ var init_IAPService = __esm({
5065
5147
  }
5066
5148
  return validated;
5067
5149
  } catch (err) {
5068
- if (this.debug) console.error("[Paywallo] restore failed", err);
5150
+ console.error("[Paywallo] restore failed", err);
5069
5151
  return [];
5070
5152
  }
5071
5153
  }
@@ -5281,7 +5363,7 @@ function usePaywallLoader(placement, visible, preloadedConfig, preloadedProducts
5281
5363
  setProducts(map);
5282
5364
  }
5283
5365
  } catch (err) {
5284
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] loadProducts in paywall failed", err);
5366
+ console.error("[Paywallo] loadProducts in paywall failed", err);
5285
5367
  if (!cancelled) setProducts(/* @__PURE__ */ new Map());
5286
5368
  }
5287
5369
  } else {
@@ -6410,11 +6492,9 @@ var init_SubscriptionManager = __esm({
6410
6492
  if (!this.config) {
6411
6493
  return this.getEmptyStatus();
6412
6494
  }
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(), {
6495
+ const userId = this.userId ?? "";
6496
+ const url = `${this.config.serverUrl}/sdk/purchases/status?distinctId=${encodeURIComponent(userId)}`;
6497
+ const response = await fetch(url, {
6418
6498
  method: "GET",
6419
6499
  headers: {
6420
6500
  "Content-Type": "application/json",
@@ -6427,17 +6507,32 @@ var init_SubscriptionManager = __esm({
6427
6507
  `Failed to fetch subscription status: ${response.status}`
6428
6508
  );
6429
6509
  }
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
- }
6510
+ const envelope = await response.json();
6511
+ const raw = envelope.data ?? envelope;
6512
+ const data = {
6513
+ hasActiveSubscription: !!(raw["has_active_subscription"] ?? raw["hasActiveSubscription"]),
6514
+ subscription: null,
6515
+ entitlements: raw["entitlements"] ?? []
6516
+ };
6517
+ const sub = raw["subscription"];
6518
+ if (sub) {
6519
+ const cancellationRaw = sub["cancellationDate"] ?? sub["cancellation_date"];
6520
+ const gracePeriodRaw = sub["gracePeriodExpiresAt"] ?? sub["grace_period_expires_at"];
6521
+ data.subscription = {
6522
+ id: sub["id"],
6523
+ productId: sub["product_id"] ?? sub["productId"],
6524
+ status: sub["status"],
6525
+ expiresAt: new Date(sub["expires_at"] ?? sub["expiresAt"]),
6526
+ originalPurchaseDate: new Date(sub["originalPurchaseDate"] ?? sub["original_purchase_date"]),
6527
+ latestPurchaseDate: new Date(sub["latestPurchaseDate"] ?? sub["latest_purchase_date"]),
6528
+ willRenew: !!(sub["will_renew"] ?? sub["willRenew"] ?? sub["auto_renew_enabled"] ?? sub["autoRenewEnabled"]),
6529
+ isTrial: !!(sub["is_trial"] ?? sub["isTrial"]),
6530
+ isIntroductoryPeriod: !!(sub["is_introductory_period"] ?? sub["isIntroductoryPeriod"]),
6531
+ platform: sub["platform"],
6532
+ storeTransactionId: sub["store_transaction_id"] ?? sub["storeTransactionId"],
6533
+ cancellationDate: cancellationRaw ? new Date(cancellationRaw) : void 0,
6534
+ gracePeriodExpiresAt: gracePeriodRaw ? new Date(gracePeriodRaw) : void 0
6535
+ };
6441
6536
  }
6442
6537
  return data;
6443
6538
  }
@@ -6475,15 +6570,15 @@ async function validatePurchase(client, appKey, platform, receipt, productId, tr
6475
6570
  "/sdk/purchases/validate",
6476
6571
  {
6477
6572
  platform,
6478
- receipt,
6479
- productId,
6480
- transactionId,
6481
- priceLocal,
6573
+ receipt_data: receipt,
6574
+ product_id: productId,
6575
+ transaction_id: transactionId,
6576
+ price_local: priceLocal,
6482
6577
  currency,
6483
6578
  country,
6484
- distinctId,
6485
- ...paywallPlacement !== void 0 && { paywallPlacement },
6486
- ...variantKey !== void 0 && { variantKey }
6579
+ distinct_id: distinctId,
6580
+ ...paywallPlacement !== void 0 && { paywall_placement: paywallPlacement },
6581
+ ...variantKey !== void 0 && { variant_key: variantKey }
6487
6582
  },
6488
6583
  false
6489
6584
  );
@@ -6495,7 +6590,7 @@ async function validatePurchase(client, appKey, platform, receipt, productId, tr
6495
6590
  response.status
6496
6591
  );
6497
6592
  }
6498
- return response.data;
6593
+ return response.data.data;
6499
6594
  }
6500
6595
  async function getSubscriptionStatus(client, baseUrl, appKey, distinctId) {
6501
6596
  const url = new URL(`${baseUrl}/sdk/purchases/status`);
@@ -6504,7 +6599,7 @@ async function getSubscriptionStatus(client, baseUrl, appKey, distinctId) {
6504
6599
  if (!response.ok) {
6505
6600
  throw new PurchaseError(PURCHASE_ERROR_CODES.NETWORK_ERROR, `Subscription status check failed: HTTP ${response.status}`);
6506
6601
  }
6507
- return response.data;
6602
+ return response.data.data;
6508
6603
  }
6509
6604
  async function getEmergencyPaywall(client) {
6510
6605
  try {
@@ -6713,7 +6808,7 @@ async function evaluateFlags2(deps, keys, distinctId) {
6713
6808
  const result = {};
6714
6809
  for (const key of Object.keys(record)) {
6715
6810
  const value = record[key];
6716
- const variant = typeof value === "boolean" ? String(value) : typeof value === "string" ? value : null;
6811
+ const variant = typeof value === "string" ? value : null;
6717
6812
  result[key] = variant;
6718
6813
  const flagVariant = { variant };
6719
6814
  deps.cache.setVariant(`${key}:${distinctId}`, flagVariant);
@@ -7026,7 +7121,7 @@ var init_eventEnvelopeV2 = __esm({
7026
7121
  payload: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown())
7027
7122
  });
7028
7123
  v2ContextSchema = import_zod2.z.object({
7029
- distinct_id: import_zod2.z.string().optional(),
7124
+ distinct_id: import_zod2.z.string().min(1),
7030
7125
  session_id: import_zod2.z.string().optional(),
7031
7126
  device_id: import_zod2.z.string().optional(),
7032
7127
  app_version: import_zod2.z.string().optional(),
@@ -7069,7 +7164,7 @@ var init_events = __esm({
7069
7164
  });
7070
7165
 
7071
7166
  // 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;
7167
+ var import_react_native22, BATCH_MAX_SIZE, BATCH_FLUSH_MS, EVENT_NAME_REGEX, V2_BATCH_ENDPOINT, EventBatcher;
7073
7168
  var init_EventBatcher = __esm({
7074
7169
  "src/core/EventBatcher.ts"() {
7075
7170
  "use strict";
@@ -7080,15 +7175,12 @@ var init_EventBatcher = __esm({
7080
7175
  BATCH_FLUSH_MS = 1e4;
7081
7176
  EVENT_NAME_REGEX = /^(\$[a-zA-Z0-9_]+|[a-z][a-z0-9_]*)$/;
7082
7177
  V2_BATCH_ENDPOINT = "/sdk/ingest/batch";
7083
- V1_BATCH_ENDPOINT = "/sdk/events/batch";
7084
- V1_SINGLE_ENDPOINT = "/sdk/events";
7085
7178
  EventBatcher = class {
7086
7179
  constructor(post, debug = false, contextProvider = null) {
7087
7180
  this.criticalQueue = [];
7088
7181
  this.normalQueue = [];
7089
7182
  this.normalTimer = null;
7090
7183
  this.criticalScheduled = false;
7091
- this.v2Disabled = false;
7092
7184
  this.post = post;
7093
7185
  this.debug = debug;
7094
7186
  this.contextProvider = contextProvider;
@@ -7184,11 +7276,7 @@ var init_EventBatcher = __esm({
7184
7276
  try {
7185
7277
  await this.postBatch([event], `event_critical:${event.eventName}`, "critical");
7186
7278
  } 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
- }
7279
+ if (this.debug) console.log(`[Paywallo] critical event ${event.eventName} post failed`);
7192
7280
  }
7193
7281
  }
7194
7282
  }
@@ -7202,50 +7290,24 @@ var init_EventBatcher = __esm({
7202
7290
  try {
7203
7291
  await this.postBatch(batch, `event_batch:${batch.length}`);
7204
7292
  } catch {
7205
- for (const event of batch) {
7206
- await this.post(V1_SINGLE_ENDPOINT, event, `event:${event.eventName}`);
7207
- }
7293
+ if (this.debug) console.log(`[Paywallo] normal batch post failed \u2014 OfflineQueue will handle durability`);
7208
7294
  }
7209
7295
  }
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
7296
  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
- }
7297
+ const providerContext = resolveProviderContext(this.contextProvider);
7298
+ const inputs = events.map((e) => ({
7299
+ eventName: e.eventName,
7300
+ distinctId: e.distinctId,
7301
+ properties: e.properties,
7302
+ timestamp: e.timestamp
7303
+ }));
7304
+ const envelope = buildV2Envelope(inputs, providerContext);
7305
+ const parsed = v2EnvelopeSchema.safeParse(envelope);
7306
+ if (!parsed.success) {
7307
+ if (this.debug) console.log("[Paywallo EVENTS] V2 envelope schema validation failed:", parsed.error.issues);
7308
+ throw parsed.error;
7247
7309
  }
7248
- await this.post(V1_BATCH_ENDPOINT, { events }, label, priority);
7310
+ await this.post(V2_BATCH_ENDPOINT, parsed.data, label, priority);
7249
7311
  }
7250
7312
  };
7251
7313
  }
@@ -7632,6 +7694,26 @@ var init_ApiClient = __esm({
7632
7694
  this.batcher.dispose();
7633
7695
  }
7634
7696
  async identify(distinctId, properties, email, deviceId, pii) {
7697
+ const rawTraits = {
7698
+ email: email || void 0,
7699
+ platform: import_react_native23.Platform.OS,
7700
+ name: properties?.name,
7701
+ country: properties?.country,
7702
+ locale: properties?.locale,
7703
+ app_version: properties?.appVersion ?? properties?.["app_version"]
7704
+ };
7705
+ const traits = Object.fromEntries(
7706
+ Object.entries(rawTraits).filter(([, v]) => v !== void 0)
7707
+ );
7708
+ const attributionKeys = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content", "fbclid", "gclid", "ttclid", "referrer"];
7709
+ const rawAttribution = {};
7710
+ if (properties) {
7711
+ for (const key of attributionKeys) {
7712
+ const val = properties[key];
7713
+ if (val !== void 0) rawAttribution[key] = val;
7714
+ }
7715
+ }
7716
+ const attribution = Object.keys(rawAttribution).length > 0 ? rawAttribution : void 0;
7635
7717
  await postWithQueue(
7636
7718
  {
7637
7719
  isOfflineQueueEnabled: () => this.offlineQueueEnabled,
@@ -7639,12 +7721,11 @@ var init_ApiClient = __esm({
7639
7721
  isDebug: () => this.debug,
7640
7722
  post: (path, body, skipRetry) => this.post(path, body, skipRetry)
7641
7723
  },
7642
- "/sdk/identify",
7724
+ "/sdk/identity/identify",
7643
7725
  {
7644
- distinctId,
7645
- properties: properties ?? {},
7646
- email,
7647
- platform: import_react_native23.Platform.OS,
7726
+ distinct_id: distinctId,
7727
+ traits,
7728
+ ...attribution && { attribution },
7648
7729
  ...deviceId && { deviceId },
7649
7730
  ...pii?.phone && { phone: pii.phone },
7650
7731
  ...pii?.firstName && { firstName: pii.firstName },
@@ -7655,10 +7736,9 @@ var init_ApiClient = __esm({
7655
7736
  "identify"
7656
7737
  );
7657
7738
  }
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.
7739
+ // Session lifecycle endpoints were dropped. All session bookkeeping is now
7740
+ // derived on the server from `$session_start` and `session.end` events that
7741
+ // flow through the unified EventBatcher pipeline.
7662
7742
  async getVariant(flagKey, distinctId) {
7663
7743
  return getVariant2(this.flagDeps(), flagKey, distinctId);
7664
7744
  }
@@ -7716,7 +7796,7 @@ var init_ApiClient = __esm({
7716
7796
  const hit = this.cache.getCampaign(key);
7717
7797
  if (hit !== void 0) return hit;
7718
7798
  try {
7719
- const res = await this.get(`/sdk/campaigns/public/primary?distinctId=${encodeURIComponent(distinctId)}`);
7799
+ const res = await this.get(`/campaigns/public/primary?distinctId=${encodeURIComponent(distinctId)}`);
7720
7800
  if (res.status === 404) {
7721
7801
  this.cache.setCampaign(key, null, this.cache.nullTTL);
7722
7802
  return null;
@@ -7816,10 +7896,10 @@ function setupNotifications(apiClient, config, environment, eventPipeline) {
7816
7896
  debug: config.debug,
7817
7897
  environment: environment === "Sandbox" ? "sandbox" : "production"
7818
7898
  }).catch((err) => {
7819
- if (config.debug) console.warn("[Paywallo PUSH] initialization warning:", err instanceof Error ? err.message : String(err));
7899
+ console.error("[Paywallo PUSH] initialization warning:", err instanceof Error ? err.message : String(err));
7820
7900
  });
7821
7901
  } catch (err) {
7822
- if (config.debug) console.warn("[Paywallo PUSH] setup warning:", err instanceof Error ? err.message : String(err));
7902
+ console.error("[Paywallo PUSH] setup warning:", err instanceof Error ? err.message : String(err));
7823
7903
  }
7824
7904
  }
7825
7905
  function setupAutoPreload(state, apiClient, config, distinctId) {
@@ -7944,7 +8024,7 @@ function reportInitFailure(state, config, error, reason) {
7944
8024
  error: error instanceof Error ? error.message : String(error)
7945
8025
  });
7946
8026
  } catch (err) {
7947
- if (config.debug) console.error("[Paywallo] reportInitFailure error", err);
8027
+ console.error("[Paywallo] reportInitFailure error", err);
7948
8028
  }
7949
8029
  }
7950
8030
  function reportInitSuccess(state, config, attempts) {
@@ -7953,7 +8033,7 @@ function reportInitSuccess(state, config, attempts) {
7953
8033
  void state.apiClient.reportError("INIT_RECOVERED", `Succeeded after ${attempts} retries`, { attempts });
7954
8034
  }
7955
8035
  } catch (err) {
7956
- if (config.debug) console.error("[Paywallo] reportInitSuccess error", err);
8036
+ console.error("[Paywallo] reportInitSuccess error", err);
7957
8037
  }
7958
8038
  }
7959
8039
  async function resolveSessionFlags(state, keys, apiClient, distinctId) {
@@ -8007,7 +8087,7 @@ async function doInit(state, config) {
8007
8087
  try {
8008
8088
  await identityManager.identify(pending);
8009
8089
  } catch (err) {
8010
- if (config.debug) console.warn("[Paywallo INIT] pending identify replay failed", err);
8090
+ console.error("[Paywallo INIT] pending identify replay failed", err);
8011
8091
  }
8012
8092
  }
8013
8093
  state.pendingIdentifies = [];
@@ -8085,7 +8165,7 @@ async function doInit(state, config) {
8085
8165
  setupNotifications(apiClient, config, environment, eventPipeline);
8086
8166
  if (config.autoStartSession !== false) {
8087
8167
  sessionManager.startSession().catch((err) => {
8088
- if (config.debug) console.warn("[Paywallo INIT] autoStart session error", err);
8168
+ console.error("[Paywallo INIT] autoStart session error", err);
8089
8169
  });
8090
8170
  }
8091
8171
  new InstallTracker(config.debug, {
@@ -8103,7 +8183,7 @@ async function doInit(state, config) {
8103
8183
  state.resolveReady();
8104
8184
  state.resolveReady = null;
8105
8185
  }
8106
- if (config.debug) console.warn("[Paywallo] initialized successfully");
8186
+ if (config.debug) console.log("[Paywallo] initialized successfully");
8107
8187
  if (distinctId) {
8108
8188
  apiClient.getSubscriptionStatus(distinctId).then(() => {
8109
8189
  if (config.debug) console.log("[Paywallo SUB] preloaded on boot");
@@ -8220,7 +8300,7 @@ async function checkActiveSubscription(state) {
8220
8300
  if (state.config?.debug) console.log("[Paywallo SUB] hasActiveSubscription (server):", isActive);
8221
8301
  return isActive;
8222
8302
  } catch (error) {
8223
- if (state.config?.debug) console.error("[Paywallo SUB] hasActiveSubscription error", error);
8303
+ console.error("[Paywallo SUB] hasActiveSubscription error", error);
8224
8304
  if (state.apiClient) void state.apiClient.reportError("SUBSCRIPTION_CHECK_FAILED", error instanceof Error ? error.message : "hasActiveSubscription failed");
8225
8305
  return false;
8226
8306
  }
@@ -8665,6 +8745,7 @@ var init_OfferingService = __esm({
8665
8745
  var index_exports = {};
8666
8746
  __export(index_exports, {
8667
8747
  ApiClient: () => ApiClient,
8748
+ AttributionTracker: () => AttributionTracker,
8668
8749
  CAMPAIGN_ERROR_CODES: () => CAMPAIGN_ERROR_CODES,
8669
8750
  CampaignError: () => CampaignError,
8670
8751
  ERROR_CODES: () => PURCHASE_ERROR_CODES,
@@ -8699,6 +8780,7 @@ __export(index_exports, {
8699
8780
  SessionError: () => SessionError,
8700
8781
  SessionManager: () => SessionManager,
8701
8782
  SubscriptionCache: () => SubscriptionCache,
8783
+ attributionTracker: () => attributionTracker,
8702
8784
  createPurchaseError: () => createPurchaseError,
8703
8785
  default: () => index_default,
8704
8786
  detectDeviceLanguage: () => detectDeviceLanguage,
@@ -8761,6 +8843,7 @@ init_PurchaseError();
8761
8843
  // src/index.ts
8762
8844
  init_identity();
8763
8845
  init_identity();
8846
+ init_identity();
8764
8847
  init_notifications();
8765
8848
 
8766
8849
  // src/domains/offering/index.ts
@@ -9432,7 +9515,6 @@ function useCampaignPreload() {
9432
9515
 
9433
9516
  // src/hooks/useProductLoader.ts
9434
9517
  var import_react22 = require("react");
9435
- init_PaywalloClient();
9436
9518
  function useProductLoader() {
9437
9519
  const [products, setProducts] = (0, import_react22.useState)(/* @__PURE__ */ new Map());
9438
9520
  const [isLoadingProducts, setIsLoadingProducts] = (0, import_react22.useState)(false);
@@ -9445,7 +9527,7 @@ function useProductLoader() {
9445
9527
  for (const p of loaded) map.set(p.productId, p);
9446
9528
  setProducts(map);
9447
9529
  } catch (err) {
9448
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] refreshProducts failed", err);
9530
+ console.error("[Paywallo] refreshProducts failed", err);
9449
9531
  } finally {
9450
9532
  setIsLoadingProducts(false);
9451
9533
  }
@@ -9455,7 +9537,6 @@ function useProductLoader() {
9455
9537
 
9456
9538
  // src/hooks/usePurchaseOrchestrator.ts
9457
9539
  var import_react23 = require("react");
9458
- init_PaywalloClient();
9459
9540
  init_paywall();
9460
9541
  function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, invalidateSubscriptionCache) {
9461
9542
  const { trackDismissed, trackProductSelected, trackPurchased } = usePaywallTracking();
@@ -9489,7 +9570,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
9489
9570
  clearPreloadedWebView();
9490
9571
  resolvePreloadedPaywall({ presented: true, purchased: true, cancelled: false, restored: false });
9491
9572
  } catch (error) {
9492
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] preloaded purchase failed", error);
9573
+ console.error("[Paywallo] preloaded purchase failed", error);
9493
9574
  setShowingPreloadedWebView(false);
9494
9575
  clearPreloadedWebView();
9495
9576
  resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: false });
@@ -9510,7 +9591,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
9510
9591
  clearPreloadedWebView();
9511
9592
  resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: true });
9512
9593
  } catch (error) {
9513
- if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] preloaded restore failed", error);
9594
+ console.error("[Paywallo] preloaded restore failed", error);
9514
9595
  setShowingPreloadedWebView(false);
9515
9596
  clearPreloadedWebView();
9516
9597
  resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: false });
@@ -10163,6 +10244,7 @@ var index_default = Paywallo;
10163
10244
  // Annotate the CommonJS export names for ESM import in node:
10164
10245
  0 && (module.exports = {
10165
10246
  ApiClient,
10247
+ AttributionTracker,
10166
10248
  CAMPAIGN_ERROR_CODES,
10167
10249
  CampaignError,
10168
10250
  ERROR_CODES,
@@ -10197,6 +10279,7 @@ var index_default = Paywallo;
10197
10279
  SessionError,
10198
10280
  SessionManager,
10199
10281
  SubscriptionCache,
10282
+ attributionTracker,
10200
10283
  createPurchaseError,
10201
10284
  detectDeviceLanguage,
10202
10285
  getCurrentLanguage,