@virex-tech/paywallo-sdk 2.4.0 → 2.5.0
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/PaywalloSdk.podspec +1 -1
- package/README.md +45 -4
- package/android/build.gradle +0 -1
- package/android/src/main/java/com/paywallo/sdk/PaywalloFBBridgeModule.kt +7 -0
- package/android/src/main/java/com/paywallo/sdk/PaywalloSdkPackage.kt +1 -2
- package/dist/index.d.mts +298 -176
- package/dist/index.d.ts +298 -176
- package/dist/index.js +1146 -1061
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1175 -1092
- package/dist/index.mjs.map +1 -1
- package/ios/PaywalloFBBridge.m +3 -0
- package/ios/PaywalloFBBridge.swift +20 -0
- package/package.json +4 -1
- package/android/src/main/java/com/paywallo/sdk/PaywalloDeviceModule.kt +0 -148
- package/ios/PaywalloDevice.m +0 -8
- package/ios/PaywalloDevice.swift +0 -119
package/dist/index.js
CHANGED
|
@@ -262,77 +262,102 @@ var init_StorageMigration = __esm({
|
|
|
262
262
|
}
|
|
263
263
|
});
|
|
264
264
|
|
|
265
|
+
// src/errors/ClientError.ts
|
|
266
|
+
var ClientError, CLIENT_ERROR_CODES;
|
|
267
|
+
var init_ClientError = __esm({
|
|
268
|
+
"src/errors/ClientError.ts"() {
|
|
269
|
+
"use strict";
|
|
270
|
+
init_PaywalloError();
|
|
271
|
+
ClientError = class extends PaywalloError {
|
|
272
|
+
constructor(code, message) {
|
|
273
|
+
super("client", code, message);
|
|
274
|
+
this.name = "ClientError";
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
CLIENT_ERROR_CODES = {
|
|
278
|
+
NOT_INITIALIZED: "CLIENT_NOT_INITIALIZED",
|
|
279
|
+
MISSING_APP_KEY: "CLIENT_MISSING_APP_KEY",
|
|
280
|
+
INVALID_EVENT_NAME: "CLIENT_INVALID_EVENT_NAME",
|
|
281
|
+
PROVIDER_MISSING: "CLIENT_PROVIDER_MISSING",
|
|
282
|
+
INSECURE_REQUEST: "CLIENT_INSECURE_REQUEST",
|
|
283
|
+
UNKNOWN: "CLIENT_UNKNOWN"
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
|
|
265
288
|
// src/utils/NativeDeviceInfo.ts
|
|
289
|
+
function assertExpoModules() {
|
|
290
|
+
if (!Device || Device.osName === void 0) {
|
|
291
|
+
throw new ClientError(
|
|
292
|
+
"MISSING_EXPO_DEPS",
|
|
293
|
+
"Paywallo SDK 3.x requires expo-device, expo-localization and expo-application. Run: npx expo install expo-device expo-localization expo-application"
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
266
297
|
function setNativeDeviceInfoDebug(debug) {
|
|
267
298
|
_debug2 = debug;
|
|
268
299
|
}
|
|
269
|
-
function
|
|
270
|
-
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
300
|
+
function buildSyncFields(idfv) {
|
|
301
|
+
const locale = Localization.getLocales()[0] ?? null;
|
|
302
|
+
const calendar = Localization.getCalendars()[0] ?? null;
|
|
303
|
+
const androidId = import_react_native2.Platform.OS === "android" ? Application.getAndroidId() ?? "unknown" : "unknown";
|
|
304
|
+
return {
|
|
305
|
+
deviceId: import_react_native2.Platform.OS === "ios" ? idfv ?? "unknown" : androidId,
|
|
306
|
+
model: Device.modelName ?? "unknown",
|
|
307
|
+
modelId: Device.modelId ?? Device.modelName ?? "unknown",
|
|
308
|
+
osName: Device.osName ?? import_react_native2.Platform.OS,
|
|
309
|
+
systemVersion: Device.osVersion ?? "unknown",
|
|
310
|
+
appVersion: Application.nativeApplicationVersion ?? "unknown",
|
|
311
|
+
buildNumber: Application.nativeBuildVersion ?? "0",
|
|
312
|
+
bundleId: Application.applicationId ?? "unknown",
|
|
313
|
+
brand: Device.brand ?? "unknown",
|
|
314
|
+
idfv,
|
|
315
|
+
regionCode: locale?.regionCode ?? null,
|
|
316
|
+
languageTag: locale?.languageTag ?? null,
|
|
317
|
+
languageCode: locale?.languageCode ?? null,
|
|
318
|
+
timeZone: calendar?.timeZone ?? null
|
|
319
|
+
};
|
|
284
320
|
}
|
|
285
321
|
async function getDeviceInfo() {
|
|
286
322
|
if (cachedDeviceInfo !== null) return cachedDeviceInfo;
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
cachedDeviceInfo
|
|
292
|
-
return raw;
|
|
323
|
+
assertExpoModules();
|
|
324
|
+
const idfv = import_react_native2.Platform.OS === "ios" ? await Application.getIosIdForVendorAsync() : null;
|
|
325
|
+
if (_debug2) console.log("[Paywallo] device info collected, idfv present:", idfv != null);
|
|
326
|
+
cachedDeviceInfo = buildSyncFields(idfv);
|
|
327
|
+
return cachedDeviceInfo;
|
|
293
328
|
}
|
|
294
329
|
async function getInstallReferrer() {
|
|
295
|
-
const mod = getNativeModule();
|
|
296
|
-
if (!mod) return null;
|
|
297
330
|
try {
|
|
298
|
-
return await
|
|
331
|
+
return await Application.getInstallReferrerAsync();
|
|
299
332
|
} catch {
|
|
300
333
|
return null;
|
|
301
334
|
}
|
|
302
335
|
}
|
|
303
336
|
function isAvailable2() {
|
|
304
|
-
return
|
|
337
|
+
return typeof Device !== "undefined" && Device.osName !== void 0;
|
|
305
338
|
}
|
|
306
339
|
function clearCache() {
|
|
307
340
|
cachedDeviceInfo = null;
|
|
308
341
|
}
|
|
309
342
|
function getCachedDeviceInfo() {
|
|
310
|
-
return cachedDeviceInfo;
|
|
343
|
+
if (cachedDeviceInfo !== null) return cachedDeviceInfo;
|
|
344
|
+
try {
|
|
345
|
+
assertExpoModules();
|
|
346
|
+
return buildSyncFields(null);
|
|
347
|
+
} catch {
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
311
350
|
}
|
|
312
|
-
var import_react_native2,
|
|
351
|
+
var import_react_native2, Device, Application, Localization, _debug2, cachedDeviceInfo, nativeDeviceInfo;
|
|
313
352
|
var init_NativeDeviceInfo = __esm({
|
|
314
353
|
"src/utils/NativeDeviceInfo.ts"() {
|
|
315
354
|
"use strict";
|
|
316
355
|
import_react_native2 = require("react-native");
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
systemVersion: "0.0.0",
|
|
322
|
-
appVersion: "0.0.0",
|
|
323
|
-
buildNumber: "0",
|
|
324
|
-
bundleId: "unknown",
|
|
325
|
-
brand: "unknown",
|
|
326
|
-
systemName: "unknown",
|
|
327
|
-
totalDisk: 0,
|
|
328
|
-
freeDisk: 0,
|
|
329
|
-
totalRam: 0,
|
|
330
|
-
carrier: "unknown",
|
|
331
|
-
installerPackage: null,
|
|
332
|
-
idfv: null
|
|
333
|
-
};
|
|
356
|
+
Device = __toESM(require("expo-device"));
|
|
357
|
+
Application = __toESM(require("expo-application"));
|
|
358
|
+
Localization = __toESM(require("expo-localization"));
|
|
359
|
+
init_ClientError();
|
|
334
360
|
_debug2 = false;
|
|
335
|
-
warnedOnce = false;
|
|
336
361
|
cachedDeviceInfo = null;
|
|
337
362
|
nativeDeviceInfo = {
|
|
338
363
|
getDeviceInfo,
|
|
@@ -347,6 +372,7 @@ var init_NativeDeviceInfo = __esm({
|
|
|
347
372
|
// src/utils/uuid.ts
|
|
348
373
|
var uuid_exports = {};
|
|
349
374
|
__export(uuid_exports, {
|
|
375
|
+
deterministicUUID: () => deterministicUUID,
|
|
350
376
|
generateUUID: () => generateUUID,
|
|
351
377
|
isValidUUIDv4: () => isValidUUIDv4
|
|
352
378
|
});
|
|
@@ -358,6 +384,28 @@ function generateUUID() {
|
|
|
358
384
|
return v.toString(16);
|
|
359
385
|
});
|
|
360
386
|
}
|
|
387
|
+
function deterministicUUID(seed) {
|
|
388
|
+
const fnv1a32 = (s, basis) => {
|
|
389
|
+
let h = basis >>> 0;
|
|
390
|
+
for (let i = 0; i < s.length; i++) {
|
|
391
|
+
h = Math.imul(h ^ s.charCodeAt(i), 16777619) >>> 0;
|
|
392
|
+
}
|
|
393
|
+
return h;
|
|
394
|
+
};
|
|
395
|
+
const B = 2166136261;
|
|
396
|
+
const h0 = fnv1a32(seed, B);
|
|
397
|
+
const h1 = fnv1a32(seed, B ^ 252645135);
|
|
398
|
+
const h2 = fnv1a32(seed, B ^ 4042322160);
|
|
399
|
+
const h3 = fnv1a32(seed, B ^ 2863311530);
|
|
400
|
+
const x = (n, len) => (n >>> 0).toString(16).padStart(len, "0").slice(-len);
|
|
401
|
+
const s1 = x(h0, 8);
|
|
402
|
+
const s2 = x(h1 >>> 16, 4);
|
|
403
|
+
const s3 = "4" + x(h1 & 4095, 3);
|
|
404
|
+
const vb = (8 | h2 >>> 30 & 3).toString(16);
|
|
405
|
+
const s4 = vb + x(h2 >>> 18 & 4095, 3);
|
|
406
|
+
const s5 = x(h2 & 65535, 4) + x(h3, 8);
|
|
407
|
+
return `${s1}-${s2}-${s3}-${s4}-${s5}`;
|
|
408
|
+
}
|
|
361
409
|
function isValidUUIDv4(value) {
|
|
362
410
|
if (!value) return false;
|
|
363
411
|
return UUID_V4_REGEX.test(value);
|
|
@@ -625,6 +673,10 @@ var init_IdentityManager = __esm({
|
|
|
625
673
|
if (!this.initialized) return "";
|
|
626
674
|
return this.distinctId ?? this.anonId ?? "";
|
|
627
675
|
}
|
|
676
|
+
/** Public stable identifier — returns the same value as `getDistinctId()`. */
|
|
677
|
+
getId() {
|
|
678
|
+
return this.getDistinctId();
|
|
679
|
+
}
|
|
628
680
|
getDeviceId() {
|
|
629
681
|
return this.deviceId;
|
|
630
682
|
}
|
|
@@ -1028,10 +1080,8 @@ async function collectDeviceInfo() {
|
|
|
1028
1080
|
const result = await nativeDeviceInfo.getDeviceInfo();
|
|
1029
1081
|
return {
|
|
1030
1082
|
appVersion: result.appVersion,
|
|
1031
|
-
deviceModel: result.
|
|
1032
|
-
osVersion: result.systemVersion
|
|
1033
|
-
darwinVersion: result.darwinVersion,
|
|
1034
|
-
webkitVersion: result.webkitVersion
|
|
1083
|
+
deviceModel: result.modelId,
|
|
1084
|
+
osVersion: result.systemVersion
|
|
1035
1085
|
};
|
|
1036
1086
|
}
|
|
1037
1087
|
var init_deviceInfo = __esm({
|
|
@@ -1426,6 +1476,12 @@ async function markInstallTracked(storage, installedAt) {
|
|
|
1426
1476
|
} catch {
|
|
1427
1477
|
}
|
|
1428
1478
|
}
|
|
1479
|
+
async function resolveInstallEventId(deviceKey, appKey) {
|
|
1480
|
+
if (deviceKey && deviceKey.trim().length > 0) {
|
|
1481
|
+
return deterministicUUID(`${appKey}:${deviceKey}`);
|
|
1482
|
+
}
|
|
1483
|
+
return getOrCreateInstallEventId();
|
|
1484
|
+
}
|
|
1429
1485
|
async function getOrCreateInstallEventId() {
|
|
1430
1486
|
const existing = await nativeStorage.get(INSTALL_KEYS.INSTALL_EVENT_ID);
|
|
1431
1487
|
if (existing) return existing;
|
|
@@ -1446,6 +1502,7 @@ var init_InstallIdempotency = __esm({
|
|
|
1446
1502
|
"src/domains/identity/InstallIdempotency.ts"() {
|
|
1447
1503
|
"use strict";
|
|
1448
1504
|
init_NativeStorage();
|
|
1505
|
+
init_uuid();
|
|
1449
1506
|
INSTALL_KEYS = {
|
|
1450
1507
|
/** Primary flag — set AFTER a successful $app_installed dispatch. */
|
|
1451
1508
|
INSTALL_TRACKED: "@paywallo:install_tracked",
|
|
@@ -1470,18 +1527,180 @@ var init_InstallIdempotency = __esm({
|
|
|
1470
1527
|
}
|
|
1471
1528
|
});
|
|
1472
1529
|
|
|
1530
|
+
// src/domains/identity/MetaBridge.ts
|
|
1531
|
+
var import_react_native5, TIMEOUT_MS, NativeBridge, MetaBridge, metaBridge;
|
|
1532
|
+
var init_MetaBridge = __esm({
|
|
1533
|
+
"src/domains/identity/MetaBridge.ts"() {
|
|
1534
|
+
"use strict";
|
|
1535
|
+
import_react_native5 = require("react-native");
|
|
1536
|
+
TIMEOUT_MS = 2e3;
|
|
1537
|
+
NativeBridge = import_react_native5.NativeModules.PaywalloFBBridge ?? null;
|
|
1538
|
+
MetaBridge = class {
|
|
1539
|
+
constructor() {
|
|
1540
|
+
this.cachedAnonId = null;
|
|
1541
|
+
this.cachedDeferredLink = null;
|
|
1542
|
+
this.deferredLinkFetched = false;
|
|
1543
|
+
this.debug = false;
|
|
1544
|
+
}
|
|
1545
|
+
setDebug(debug) {
|
|
1546
|
+
this.debug = debug;
|
|
1547
|
+
}
|
|
1548
|
+
/** Sync read of the cached anonymous ID. Returns null until `getAnonymousID()` resolves once. */
|
|
1549
|
+
getCachedAnonymousID() {
|
|
1550
|
+
return this.cachedAnonId;
|
|
1551
|
+
}
|
|
1552
|
+
async getAnonymousID() {
|
|
1553
|
+
if (this.cachedAnonId) return this.cachedAnonId;
|
|
1554
|
+
if (!NativeBridge) return null;
|
|
1555
|
+
try {
|
|
1556
|
+
let timerId;
|
|
1557
|
+
const timeout = new Promise((resolve) => {
|
|
1558
|
+
timerId = setTimeout(() => resolve(null), TIMEOUT_MS);
|
|
1559
|
+
});
|
|
1560
|
+
const result = await Promise.race([NativeBridge.getAnonymousID(), timeout]);
|
|
1561
|
+
clearTimeout(timerId);
|
|
1562
|
+
if (result) this.cachedAnonId = result;
|
|
1563
|
+
return result;
|
|
1564
|
+
} catch {
|
|
1565
|
+
return null;
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
async fetchDeferredAppLink() {
|
|
1569
|
+
if (this.deferredLinkFetched) return this.cachedDeferredLink;
|
|
1570
|
+
if (!NativeBridge) return null;
|
|
1571
|
+
if (typeof NativeBridge.fetchDeferredAppLink !== "function") return null;
|
|
1572
|
+
try {
|
|
1573
|
+
let timerId;
|
|
1574
|
+
const timeout = new Promise((resolve) => {
|
|
1575
|
+
timerId = setTimeout(() => resolve(null), TIMEOUT_MS);
|
|
1576
|
+
});
|
|
1577
|
+
const result = await Promise.race([
|
|
1578
|
+
NativeBridge.fetchDeferredAppLink().catch(() => null),
|
|
1579
|
+
timeout
|
|
1580
|
+
]);
|
|
1581
|
+
clearTimeout(timerId);
|
|
1582
|
+
this.deferredLinkFetched = true;
|
|
1583
|
+
this.cachedDeferredLink = result;
|
|
1584
|
+
return result;
|
|
1585
|
+
} catch {
|
|
1586
|
+
this.deferredLinkFetched = true;
|
|
1587
|
+
this.cachedDeferredLink = null;
|
|
1588
|
+
return null;
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
async logEvent(name, params = {}) {
|
|
1592
|
+
if (!NativeBridge) return;
|
|
1593
|
+
try {
|
|
1594
|
+
await NativeBridge.logEvent(name, params);
|
|
1595
|
+
} catch (error) {
|
|
1596
|
+
console.error("[Paywallo:MetaBridge] logEvent failed", { name, error });
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
async logPurchase(amount, currency, params = {}) {
|
|
1600
|
+
if (!NativeBridge) return;
|
|
1601
|
+
try {
|
|
1602
|
+
await NativeBridge.logPurchase(amount, currency, params);
|
|
1603
|
+
} catch (error) {
|
|
1604
|
+
console.error("[Paywallo:MetaBridge] logPurchase failed", { amount, currency, error });
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
setUserID(userId) {
|
|
1608
|
+
if (!NativeBridge) return;
|
|
1609
|
+
try {
|
|
1610
|
+
NativeBridge.setUserID(userId);
|
|
1611
|
+
} catch (error) {
|
|
1612
|
+
console.error("[Paywallo:MetaBridge] setUserID failed", { error });
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
flush() {
|
|
1616
|
+
if (!NativeBridge) return;
|
|
1617
|
+
try {
|
|
1618
|
+
NativeBridge.flush();
|
|
1619
|
+
} catch (error) {
|
|
1620
|
+
console.error("[Paywallo:MetaBridge] flush failed", { error });
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
};
|
|
1624
|
+
metaBridge = new MetaBridge();
|
|
1625
|
+
}
|
|
1626
|
+
});
|
|
1627
|
+
|
|
1628
|
+
// src/domains/identity/deferredAppLink.ts
|
|
1629
|
+
function parseQuery(s) {
|
|
1630
|
+
const result = {};
|
|
1631
|
+
for (const pair of s.split("&")) {
|
|
1632
|
+
const eqIndex = pair.indexOf("=");
|
|
1633
|
+
if (eqIndex === -1) continue;
|
|
1634
|
+
try {
|
|
1635
|
+
const key = decodeURIComponent(pair.slice(0, eqIndex).replace(/\+/g, " "));
|
|
1636
|
+
const value = decodeURIComponent(pair.slice(eqIndex + 1).replace(/\+/g, " "));
|
|
1637
|
+
if (key) result[key] = value;
|
|
1638
|
+
} catch {
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
return result;
|
|
1642
|
+
}
|
|
1643
|
+
function parseDeferredAppLink(url) {
|
|
1644
|
+
if (!url || url.trim() === "") return null;
|
|
1645
|
+
const raw = url.slice(0, 2048);
|
|
1646
|
+
const cleanRaw = raw.split("#")[0];
|
|
1647
|
+
const qIndex = cleanRaw.indexOf("?");
|
|
1648
|
+
if (qIndex === -1) return null;
|
|
1649
|
+
const topParams = parseQuery(cleanRaw.slice(qIndex + 1));
|
|
1650
|
+
let fbclid = nz(topParams["fbclid"]);
|
|
1651
|
+
let utmSource = nz(topParams["utm_source"]);
|
|
1652
|
+
let utmMedium = nz(topParams["utm_medium"]);
|
|
1653
|
+
let utmCampaign = nz(topParams["utm_campaign"]);
|
|
1654
|
+
let ttclid = nz(topParams["ttclid"]);
|
|
1655
|
+
let trackingId = nz(topParams["tracking_id"]);
|
|
1656
|
+
let targetUrl = null;
|
|
1657
|
+
if (topParams["target_url"]) {
|
|
1658
|
+
try {
|
|
1659
|
+
targetUrl = decodeURIComponent(topParams["target_url"]);
|
|
1660
|
+
} catch {
|
|
1661
|
+
targetUrl = topParams["target_url"];
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
if (!fbclid && targetUrl) {
|
|
1665
|
+
const cleanTargetUrl = targetUrl.split("#")[0];
|
|
1666
|
+
const innerQ = cleanTargetUrl.indexOf("?");
|
|
1667
|
+
if (innerQ !== -1) {
|
|
1668
|
+
const inner = parseQuery(cleanTargetUrl.slice(innerQ + 1));
|
|
1669
|
+
fbclid = fbclid ?? nz(inner["fbclid"]);
|
|
1670
|
+
utmSource = utmSource ?? nz(inner["utm_source"]);
|
|
1671
|
+
utmMedium = utmMedium ?? nz(inner["utm_medium"]);
|
|
1672
|
+
utmCampaign = utmCampaign ?? nz(inner["utm_campaign"]);
|
|
1673
|
+
ttclid = ttclid ?? nz(inner["ttclid"]);
|
|
1674
|
+
trackingId = trackingId ?? nz(inner["tracking_id"]);
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
if (!fbclid && !ttclid && !trackingId && !utmSource && !utmMedium && !utmCampaign) {
|
|
1678
|
+
return null;
|
|
1679
|
+
}
|
|
1680
|
+
return { fbclid, utmSource, utmMedium, utmCampaign, ttclid, trackingId, targetUrl, raw };
|
|
1681
|
+
}
|
|
1682
|
+
var nz;
|
|
1683
|
+
var init_deferredAppLink = __esm({
|
|
1684
|
+
"src/domains/identity/deferredAppLink.ts"() {
|
|
1685
|
+
"use strict";
|
|
1686
|
+
nz = (v) => typeof v === "string" && v.trim().length > 0 ? v : null;
|
|
1687
|
+
}
|
|
1688
|
+
});
|
|
1689
|
+
|
|
1473
1690
|
// src/domains/identity/InstallReferrerManager.ts
|
|
1474
|
-
var
|
|
1691
|
+
var import_react_native6, REFERRER_TIMEOUT_MS, InstallReferrerManager, installReferrerManager;
|
|
1475
1692
|
var init_InstallReferrerManager = __esm({
|
|
1476
1693
|
"src/domains/identity/InstallReferrerManager.ts"() {
|
|
1477
1694
|
"use strict";
|
|
1478
|
-
|
|
1695
|
+
import_react_native6 = require("react-native");
|
|
1479
1696
|
init_NativeDeviceInfo();
|
|
1697
|
+
init_MetaBridge();
|
|
1698
|
+
init_deferredAppLink();
|
|
1480
1699
|
REFERRER_TIMEOUT_MS = 5e3;
|
|
1481
1700
|
InstallReferrerManager = class {
|
|
1482
1701
|
async getReferrer() {
|
|
1483
|
-
if (
|
|
1484
|
-
return this.
|
|
1702
|
+
if (import_react_native6.Platform.OS !== "android") {
|
|
1703
|
+
return this.fetchDeferredReferrer();
|
|
1485
1704
|
}
|
|
1486
1705
|
let timerId;
|
|
1487
1706
|
const timeoutPromise = new Promise((resolve) => {
|
|
@@ -1492,6 +1711,29 @@ var init_InstallReferrerManager = __esm({
|
|
|
1492
1711
|
clearTimeout(timerId);
|
|
1493
1712
|
return result;
|
|
1494
1713
|
}
|
|
1714
|
+
async fetchDeferredReferrer() {
|
|
1715
|
+
try {
|
|
1716
|
+
const url = await metaBridge.fetchDeferredAppLink();
|
|
1717
|
+
const parsed = parseDeferredAppLink(url);
|
|
1718
|
+
if (!parsed) return this.emptyResult();
|
|
1719
|
+
return {
|
|
1720
|
+
rawReferrer: parsed.targetUrl ?? parsed.raw,
|
|
1721
|
+
trackingId: parsed.trackingId,
|
|
1722
|
+
fbclid: parsed.fbclid,
|
|
1723
|
+
utmSource: parsed.utmSource,
|
|
1724
|
+
utmMedium: parsed.utmMedium,
|
|
1725
|
+
utmCampaign: parsed.utmCampaign,
|
|
1726
|
+
ttclid: parsed.ttclid,
|
|
1727
|
+
tiktokCampaignId: null,
|
|
1728
|
+
tiktokAdgroupId: null,
|
|
1729
|
+
tiktokAdId: null,
|
|
1730
|
+
installReferrerRaw: parsed.raw,
|
|
1731
|
+
installReferrerSource: "meta_deferred"
|
|
1732
|
+
};
|
|
1733
|
+
} catch {
|
|
1734
|
+
return this.emptyResult();
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1495
1737
|
async fetchReferrer() {
|
|
1496
1738
|
try {
|
|
1497
1739
|
const referrer = await nativeDeviceInfo.getInstallReferrer();
|
|
@@ -1552,79 +1794,6 @@ var init_InstallReferrerManager = __esm({
|
|
|
1552
1794
|
}
|
|
1553
1795
|
});
|
|
1554
1796
|
|
|
1555
|
-
// src/domains/identity/MetaBridge.ts
|
|
1556
|
-
var import_react_native6, TIMEOUT_MS, NativeBridge, MetaBridge, metaBridge;
|
|
1557
|
-
var init_MetaBridge = __esm({
|
|
1558
|
-
"src/domains/identity/MetaBridge.ts"() {
|
|
1559
|
-
"use strict";
|
|
1560
|
-
import_react_native6 = require("react-native");
|
|
1561
|
-
TIMEOUT_MS = 2e3;
|
|
1562
|
-
NativeBridge = import_react_native6.NativeModules.PaywalloFBBridge ?? null;
|
|
1563
|
-
MetaBridge = class {
|
|
1564
|
-
constructor() {
|
|
1565
|
-
this.cachedAnonId = null;
|
|
1566
|
-
this.debug = false;
|
|
1567
|
-
}
|
|
1568
|
-
setDebug(debug) {
|
|
1569
|
-
this.debug = debug;
|
|
1570
|
-
}
|
|
1571
|
-
/** Sync read of the cached anonymous ID. Returns null until `getAnonymousID()` resolves once. */
|
|
1572
|
-
getCachedAnonymousID() {
|
|
1573
|
-
return this.cachedAnonId;
|
|
1574
|
-
}
|
|
1575
|
-
async getAnonymousID() {
|
|
1576
|
-
if (this.cachedAnonId) return this.cachedAnonId;
|
|
1577
|
-
if (!NativeBridge) return null;
|
|
1578
|
-
try {
|
|
1579
|
-
let timerId;
|
|
1580
|
-
const timeout = new Promise((resolve) => {
|
|
1581
|
-
timerId = setTimeout(() => resolve(null), TIMEOUT_MS);
|
|
1582
|
-
});
|
|
1583
|
-
const result = await Promise.race([NativeBridge.getAnonymousID(), timeout]);
|
|
1584
|
-
clearTimeout(timerId);
|
|
1585
|
-
if (result) this.cachedAnonId = result;
|
|
1586
|
-
return result;
|
|
1587
|
-
} catch {
|
|
1588
|
-
return null;
|
|
1589
|
-
}
|
|
1590
|
-
}
|
|
1591
|
-
async logEvent(name, params = {}) {
|
|
1592
|
-
if (!NativeBridge) return;
|
|
1593
|
-
try {
|
|
1594
|
-
await NativeBridge.logEvent(name, params);
|
|
1595
|
-
} catch (error) {
|
|
1596
|
-
console.error("[Paywallo:MetaBridge] logEvent failed", { name, error });
|
|
1597
|
-
}
|
|
1598
|
-
}
|
|
1599
|
-
async logPurchase(amount, currency, params = {}) {
|
|
1600
|
-
if (!NativeBridge) return;
|
|
1601
|
-
try {
|
|
1602
|
-
await NativeBridge.logPurchase(amount, currency, params);
|
|
1603
|
-
} catch (error) {
|
|
1604
|
-
console.error("[Paywallo:MetaBridge] logPurchase failed", { amount, currency, error });
|
|
1605
|
-
}
|
|
1606
|
-
}
|
|
1607
|
-
setUserID(userId) {
|
|
1608
|
-
if (!NativeBridge) return;
|
|
1609
|
-
try {
|
|
1610
|
-
NativeBridge.setUserID(userId);
|
|
1611
|
-
} catch (error) {
|
|
1612
|
-
console.error("[Paywallo:MetaBridge] setUserID failed", { error });
|
|
1613
|
-
}
|
|
1614
|
-
}
|
|
1615
|
-
flush() {
|
|
1616
|
-
if (!NativeBridge) return;
|
|
1617
|
-
try {
|
|
1618
|
-
NativeBridge.flush();
|
|
1619
|
-
} catch (error) {
|
|
1620
|
-
console.error("[Paywallo:MetaBridge] flush failed", { error });
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
};
|
|
1624
|
-
metaBridge = new MetaBridge();
|
|
1625
|
-
}
|
|
1626
|
-
});
|
|
1627
|
-
|
|
1628
1797
|
// src/domains/identity/InstallTracker.ts
|
|
1629
1798
|
var import_react_native7, InstallTracker;
|
|
1630
1799
|
var init_InstallTracker = __esm({
|
|
@@ -1694,44 +1863,17 @@ var init_InstallTracker = __esm({
|
|
|
1694
1863
|
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone ?? "unknown";
|
|
1695
1864
|
const info = await nativeDeviceInfo.getDeviceInfo();
|
|
1696
1865
|
const deviceModel = info.modelId || info.model || "unknown";
|
|
1697
|
-
const
|
|
1866
|
+
const osVersion2 = info.systemVersion || "unknown";
|
|
1698
1867
|
const appVersion = info.appVersion || "unknown";
|
|
1699
1868
|
const buildNumber = info.buildNumber || "0";
|
|
1700
|
-
let
|
|
1701
|
-
try {
|
|
1702
|
-
carrier = info.carrier ?? "unknown";
|
|
1703
|
-
} catch {
|
|
1704
|
-
}
|
|
1705
|
-
let totalDisk = 0;
|
|
1706
|
-
try {
|
|
1707
|
-
totalDisk = Number(info.totalDisk) || 0;
|
|
1708
|
-
} catch {
|
|
1709
|
-
}
|
|
1710
|
-
let freeDisk = 0;
|
|
1869
|
+
let brand2 = "unknown";
|
|
1711
1870
|
try {
|
|
1712
|
-
|
|
1713
|
-
} catch {
|
|
1714
|
-
}
|
|
1715
|
-
let totalRam = 0;
|
|
1716
|
-
try {
|
|
1717
|
-
totalRam = Number(info.totalRam) || 0;
|
|
1718
|
-
} catch {
|
|
1719
|
-
}
|
|
1720
|
-
let brand = "unknown";
|
|
1721
|
-
try {
|
|
1722
|
-
brand = info.brand ?? "unknown";
|
|
1723
|
-
} catch {
|
|
1724
|
-
}
|
|
1725
|
-
let installerPackage = null;
|
|
1726
|
-
try {
|
|
1727
|
-
if (import_react_native7.Platform.OS === "android") {
|
|
1728
|
-
installerPackage = info.installerPackage ?? null;
|
|
1729
|
-
}
|
|
1871
|
+
brand2 = info.brand ?? "unknown";
|
|
1730
1872
|
} catch {
|
|
1731
1873
|
}
|
|
1732
1874
|
deviceData = {
|
|
1733
1875
|
deviceModel,
|
|
1734
|
-
osVersion,
|
|
1876
|
+
osVersion: osVersion2,
|
|
1735
1877
|
appVersion,
|
|
1736
1878
|
buildNumber,
|
|
1737
1879
|
...screenWidth > 0 && { screenWidth },
|
|
@@ -1739,21 +1881,20 @@ var init_InstallTracker = __esm({
|
|
|
1739
1881
|
...screenDensity > 0 && { screenDensity },
|
|
1740
1882
|
timezone,
|
|
1741
1883
|
locale,
|
|
1742
|
-
...
|
|
1743
|
-
...
|
|
1744
|
-
...freeDisk > 0 && { freeDisk },
|
|
1745
|
-
...totalRam > 0 && { totalRam },
|
|
1746
|
-
...brand !== "unknown" && { brand },
|
|
1747
|
-
...installerPackage && { installerPackage }
|
|
1884
|
+
...brand2 !== "unknown" && { brand: brand2 },
|
|
1885
|
+
...info.regionCode && { country: info.regionCode }
|
|
1748
1886
|
};
|
|
1749
1887
|
} catch {
|
|
1750
1888
|
}
|
|
1751
|
-
const [fbAnonId, referrer, adIds
|
|
1889
|
+
const [fbAnonId, referrer, adIds] = await Promise.all([
|
|
1752
1890
|
metaBridge.getAnonymousID(),
|
|
1753
1891
|
installReferrerManager.getReferrer(),
|
|
1754
|
-
this.advertisingIdManager.collect(requestATT)
|
|
1755
|
-
getOrCreateInstallEventId()
|
|
1892
|
+
this.advertisingIdManager.collect(requestATT)
|
|
1756
1893
|
]);
|
|
1894
|
+
const installEventId = await resolveInstallEventId(
|
|
1895
|
+
adIds.idfv ?? adIds.gaid ?? null,
|
|
1896
|
+
apiClient.getAppKey()
|
|
1897
|
+
);
|
|
1757
1898
|
if (referrer.fbclid || referrer.utmSource || referrer.utmMedium || referrer.utmCampaign || referrer.ttclid || referrer.tiktokCampaignId || referrer.tiktokAdgroupId || referrer.tiktokAdId) {
|
|
1758
1899
|
attributionTracker.capture({
|
|
1759
1900
|
...referrer.fbclid && { fbclid: referrer.fbclid },
|
|
@@ -1834,6 +1975,7 @@ var init_InstallTracker = __esm({
|
|
|
1834
1975
|
screenHeight: deviceData.screenHeight,
|
|
1835
1976
|
timezone: deviceData.timezone,
|
|
1836
1977
|
language: deviceData.locale,
|
|
1978
|
+
...deviceData.country && { country: deviceData.country },
|
|
1837
1979
|
fbAnonId: anonId,
|
|
1838
1980
|
installTimestamp: new Date(installedAt).toISOString()
|
|
1839
1981
|
}),
|
|
@@ -2089,7 +2231,7 @@ var DEFAULT_API_URL, DEFAULT_WEB_URL;
|
|
|
2089
2231
|
var init_constants = __esm({
|
|
2090
2232
|
"src/core/constants.ts"() {
|
|
2091
2233
|
"use strict";
|
|
2092
|
-
DEFAULT_API_URL = "https://
|
|
2234
|
+
DEFAULT_API_URL = "https://incommensurably-unprotrudent-brooklynn.ngrok-free.dev";
|
|
2093
2235
|
DEFAULT_WEB_URL = "https://paywallo.com.br";
|
|
2094
2236
|
}
|
|
2095
2237
|
});
|
|
@@ -2421,8 +2563,8 @@ var init_NotificationHandlerSetup = __esm({
|
|
|
2421
2563
|
// src/core/version.ts
|
|
2422
2564
|
function resolveVersion() {
|
|
2423
2565
|
try {
|
|
2424
|
-
if ("2.
|
|
2425
|
-
return "2.
|
|
2566
|
+
if ("2.5.0") {
|
|
2567
|
+
return "2.5.0";
|
|
2426
2568
|
}
|
|
2427
2569
|
} catch {
|
|
2428
2570
|
}
|
|
@@ -2464,8 +2606,8 @@ async function getLocale() {
|
|
|
2464
2606
|
return clampLocale(raw);
|
|
2465
2607
|
} catch {
|
|
2466
2608
|
try {
|
|
2467
|
-
const { getLocales } = require("expo-localization");
|
|
2468
|
-
const code =
|
|
2609
|
+
const { getLocales: getLocales2 } = require("expo-localization");
|
|
2610
|
+
const code = getLocales2()[0]?.languageCode ?? "en";
|
|
2469
2611
|
return clampLocale(code);
|
|
2470
2612
|
} catch {
|
|
2471
2613
|
return "en";
|
|
@@ -3768,292 +3910,17 @@ var init_PaywallWebView = __esm({
|
|
|
3768
3910
|
}
|
|
3769
3911
|
});
|
|
3770
3912
|
|
|
3771
|
-
// src/domains/iap/PurchaseError.ts
|
|
3772
|
-
function createPurchaseError(code, message) {
|
|
3773
|
-
return new PurchaseError(
|
|
3774
|
-
PURCHASE_ERROR_CODES[code],
|
|
3775
|
-
message ?? DEFAULT_MESSAGES[code] ?? "Unknown purchase error",
|
|
3776
|
-
code === "USER_CANCELLED"
|
|
3777
|
-
);
|
|
3778
|
-
}
|
|
3779
|
-
var PurchaseError, PURCHASE_ERROR_CODES, DEFAULT_MESSAGES;
|
|
3780
|
-
var init_PurchaseError = __esm({
|
|
3781
|
-
"src/domains/iap/PurchaseError.ts"() {
|
|
3782
|
-
"use strict";
|
|
3783
|
-
init_PaywalloError();
|
|
3784
|
-
PurchaseError = class extends PaywalloError {
|
|
3785
|
-
constructor(code, message, userCancelled = false, httpStatus) {
|
|
3786
|
-
super("purchase", code, message);
|
|
3787
|
-
this.name = "PurchaseError";
|
|
3788
|
-
this.userCancelled = userCancelled;
|
|
3789
|
-
this.httpStatus = httpStatus;
|
|
3790
|
-
}
|
|
3791
|
-
};
|
|
3792
|
-
PURCHASE_ERROR_CODES = {
|
|
3793
|
-
NOT_INITIALIZED: "PURCHASE_NOT_INITIALIZED",
|
|
3794
|
-
PRODUCT_NOT_FOUND: "PRODUCT_NOT_FOUND",
|
|
3795
|
-
PURCHASE_FAILED: "PURCHASE_FAILED",
|
|
3796
|
-
RESTORE_FAILED: "RESTORE_FAILED",
|
|
3797
|
-
VALIDATION_FAILED: "VALIDATION_FAILED",
|
|
3798
|
-
USER_CANCELLED: "USER_CANCELLED",
|
|
3799
|
-
NETWORK_ERROR: "PURCHASE_NETWORK_ERROR",
|
|
3800
|
-
STORE_ERROR: "PURCHASE_STORE_ERROR",
|
|
3801
|
-
PENDING_PURCHASE: "PURCHASE_PENDING",
|
|
3802
|
-
DEFERRED_PURCHASE: "PURCHASE_DEFERRED",
|
|
3803
|
-
STORE_NOT_AVAILABLE: "PURCHASE_STORE_NOT_AVAILABLE"
|
|
3804
|
-
};
|
|
3805
|
-
DEFAULT_MESSAGES = {
|
|
3806
|
-
NOT_INITIALIZED: "Purchase controller not initialized. Call init() first.",
|
|
3807
|
-
PRODUCT_NOT_FOUND: "Product not found in store.",
|
|
3808
|
-
PURCHASE_FAILED: "Purchase failed. Please try again.",
|
|
3809
|
-
RESTORE_FAILED: "Failed to restore purchases. Please try again.",
|
|
3810
|
-
VALIDATION_FAILED: "Failed to validate purchase with server.",
|
|
3811
|
-
USER_CANCELLED: "Purchase was cancelled.",
|
|
3812
|
-
NETWORK_ERROR: "Network error. Please check your connection.",
|
|
3813
|
-
STORE_ERROR: "Store error. Please try again later.",
|
|
3814
|
-
PENDING_PURCHASE: "Purchase is pending approval.",
|
|
3815
|
-
DEFERRED_PURCHASE: "Purchase is deferred (e.g., parental approval required).",
|
|
3816
|
-
STORE_NOT_AVAILABLE: "In-app purchases are not available on this device."
|
|
3817
|
-
};
|
|
3818
|
-
}
|
|
3819
|
-
});
|
|
3820
|
-
|
|
3821
|
-
// src/domains/iap/NativeStoreKit.ts
|
|
3822
|
-
var NativeStoreKit_exports = {};
|
|
3823
|
-
__export(NativeStoreKit_exports, {
|
|
3824
|
-
NativeStoreKit: () => NativeStoreKit,
|
|
3825
|
-
nativeStoreKit: () => nativeStoreKit
|
|
3826
|
-
});
|
|
3827
|
-
function isAvailable3() {
|
|
3828
|
-
return PaywalloStoreKitNative != null;
|
|
3829
|
-
}
|
|
3830
|
-
function mapNativeProduct(native) {
|
|
3831
|
-
const product = {
|
|
3832
|
-
productId: native.productId,
|
|
3833
|
-
title: native.title,
|
|
3834
|
-
description: native.description,
|
|
3835
|
-
price: native.price,
|
|
3836
|
-
priceValue: native.priceValue,
|
|
3837
|
-
currency: native.currency ?? "USD",
|
|
3838
|
-
localizedPrice: native.localizedPrice,
|
|
3839
|
-
type: mapProductType(native.type),
|
|
3840
|
-
subscriptionPeriod: native.subscriptionPeriod,
|
|
3841
|
-
introductoryPrice: native.introductoryPrice,
|
|
3842
|
-
introductoryPriceValue: native.introductoryPriceValue,
|
|
3843
|
-
freeTrialPeriod: native.freeTrialPeriod
|
|
3844
|
-
};
|
|
3845
|
-
if (native.subscriptionPeriod) {
|
|
3846
|
-
const prices = calculatePerPeriodPrices(native.priceValue, native.subscriptionPeriod);
|
|
3847
|
-
product.pricePerMonth = prices.perMonth;
|
|
3848
|
-
product.pricePerWeek = prices.perWeek;
|
|
3849
|
-
product.pricePerDay = prices.perDay;
|
|
3850
|
-
}
|
|
3851
|
-
return product;
|
|
3852
|
-
}
|
|
3853
|
-
function mapProductType(type) {
|
|
3854
|
-
switch (type) {
|
|
3855
|
-
case "subscription":
|
|
3856
|
-
case "autoRenewable":
|
|
3857
|
-
return "subscription";
|
|
3858
|
-
case "consumable":
|
|
3859
|
-
return "consumable";
|
|
3860
|
-
default:
|
|
3861
|
-
return "non_consumable";
|
|
3862
|
-
}
|
|
3863
|
-
}
|
|
3864
|
-
function calculatePerPeriodPrices(priceValue, period) {
|
|
3865
|
-
const totalDays = periodToDays(period);
|
|
3866
|
-
if (totalDays <= 0) return {};
|
|
3867
|
-
const perDay = priceValue / totalDays;
|
|
3868
|
-
const perWeek = perDay * 7;
|
|
3869
|
-
const perMonth = perDay * 30;
|
|
3870
|
-
return {
|
|
3871
|
-
perDay: perDay.toFixed(2),
|
|
3872
|
-
perWeek: perWeek.toFixed(2),
|
|
3873
|
-
perMonth: perMonth.toFixed(2)
|
|
3874
|
-
};
|
|
3875
|
-
}
|
|
3876
|
-
function periodToDays(period) {
|
|
3877
|
-
const match = period.match(/^P(\d+)([DWMY])$/);
|
|
3878
|
-
if (!match) return 0;
|
|
3879
|
-
const value = parseInt(match[1], 10);
|
|
3880
|
-
switch (match[2]) {
|
|
3881
|
-
case "D":
|
|
3882
|
-
return value;
|
|
3883
|
-
case "W":
|
|
3884
|
-
return value * 7;
|
|
3885
|
-
case "M":
|
|
3886
|
-
return value * 30;
|
|
3887
|
-
case "Y":
|
|
3888
|
-
return value * 365;
|
|
3889
|
-
default:
|
|
3890
|
-
return 0;
|
|
3891
|
-
}
|
|
3892
|
-
}
|
|
3893
|
-
function mapNativePurchase(native) {
|
|
3894
|
-
return {
|
|
3895
|
-
productId: native.productId,
|
|
3896
|
-
transactionId: native.transactionId,
|
|
3897
|
-
transactionDate: native.transactionDate,
|
|
3898
|
-
receipt: native.receipt,
|
|
3899
|
-
platform: import_react_native16.Platform.OS
|
|
3900
|
-
};
|
|
3901
|
-
}
|
|
3902
|
-
function normalizeTransactionUpdate(raw) {
|
|
3903
|
-
if (!raw || typeof raw !== "object") return null;
|
|
3904
|
-
const r = raw;
|
|
3905
|
-
const type = r.type;
|
|
3906
|
-
if (type !== "renewed" && type !== "refunded" && type !== "canceled") return null;
|
|
3907
|
-
const transactionId = typeof r.transactionId === "string" ? r.transactionId : null;
|
|
3908
|
-
const productId = typeof r.productId === "string" ? r.productId : null;
|
|
3909
|
-
if (!transactionId) return null;
|
|
3910
|
-
const update = {
|
|
3911
|
-
type,
|
|
3912
|
-
transactionId,
|
|
3913
|
-
productId: productId ?? ""
|
|
3914
|
-
};
|
|
3915
|
-
if (typeof r.amount === "number") update.amount = r.amount;
|
|
3916
|
-
if (typeof r.currency === "string") update.currency = r.currency;
|
|
3917
|
-
return update;
|
|
3918
|
-
}
|
|
3919
|
-
var import_react_native16, TRANSACTION_UPDATE_EVENT, PaywalloStoreKitNative, nativeStoreKit, NativeStoreKit;
|
|
3920
|
-
var init_NativeStoreKit = __esm({
|
|
3921
|
-
"src/domains/iap/NativeStoreKit.ts"() {
|
|
3922
|
-
"use strict";
|
|
3923
|
-
import_react_native16 = require("react-native");
|
|
3924
|
-
init_PaywalloClient();
|
|
3925
|
-
init_uuid();
|
|
3926
|
-
init_IdentityManager();
|
|
3927
|
-
init_PurchaseError();
|
|
3928
|
-
TRANSACTION_UPDATE_EVENT = "PaywalloTransactionUpdate";
|
|
3929
|
-
PaywalloStoreKitNative = import_react_native16.NativeModules.PaywalloStoreKit ?? null;
|
|
3930
|
-
nativeStoreKit = {
|
|
3931
|
-
isAvailable: isAvailable3,
|
|
3932
|
-
async getProducts(productIds) {
|
|
3933
|
-
if (!PaywalloStoreKitNative) {
|
|
3934
|
-
return [];
|
|
3935
|
-
}
|
|
3936
|
-
const nativeProducts = await PaywalloStoreKitNative.getProducts(productIds);
|
|
3937
|
-
return nativeProducts.map(mapNativeProduct);
|
|
3938
|
-
},
|
|
3939
|
-
async purchase(productId) {
|
|
3940
|
-
if (!PaywalloStoreKitNative) {
|
|
3941
|
-
throw new PurchaseError(
|
|
3942
|
-
PURCHASE_ERROR_CODES.STORE_NOT_AVAILABLE,
|
|
3943
|
-
"StoreKit native module not available"
|
|
3944
|
-
);
|
|
3945
|
-
}
|
|
3946
|
-
const distinctId = identityManager.getDistinctId();
|
|
3947
|
-
const rawUuid = distinctId?.startsWith("$paywallo_anon:") ? distinctId.slice("$paywallo_anon:".length) : distinctId;
|
|
3948
|
-
const appAccountToken = isValidUUIDv4(rawUuid) ? rawUuid : null;
|
|
3949
|
-
if (distinctId && appAccountToken === null) {
|
|
3950
|
-
if (PaywalloClient.getConfig()?.debug) console.log("[Paywallo] distinctId is not a valid UUID v4 \u2014 passing null to appAccountToken:", distinctId);
|
|
3951
|
-
}
|
|
3952
|
-
try {
|
|
3953
|
-
const result = await PaywalloStoreKitNative.purchase(productId, appAccountToken);
|
|
3954
|
-
if (!result || result.pending) {
|
|
3955
|
-
return null;
|
|
3956
|
-
}
|
|
3957
|
-
return mapNativePurchase(result);
|
|
3958
|
-
} catch (err) {
|
|
3959
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
3960
|
-
if (message.toLowerCase().includes("cancel")) {
|
|
3961
|
-
throw new PurchaseError(PURCHASE_ERROR_CODES.USER_CANCELLED, message, true);
|
|
3962
|
-
}
|
|
3963
|
-
throw new PurchaseError(PURCHASE_ERROR_CODES.PURCHASE_FAILED, message);
|
|
3964
|
-
}
|
|
3965
|
-
},
|
|
3966
|
-
async finishTransaction(transactionId) {
|
|
3967
|
-
if (!PaywalloStoreKitNative) return;
|
|
3968
|
-
try {
|
|
3969
|
-
await PaywalloStoreKitNative.finishTransaction(transactionId);
|
|
3970
|
-
} catch (err) {
|
|
3971
|
-
console.error("[Paywallo] finishTransaction native error", err);
|
|
3972
|
-
}
|
|
3973
|
-
},
|
|
3974
|
-
async getActiveTransactions() {
|
|
3975
|
-
if (!PaywalloStoreKitNative) return [];
|
|
3976
|
-
try {
|
|
3977
|
-
const results = await PaywalloStoreKitNative.getActiveTransactions();
|
|
3978
|
-
return results.map(mapNativePurchase);
|
|
3979
|
-
} catch (err) {
|
|
3980
|
-
console.error("[Paywallo] getActiveTransactions failed", err);
|
|
3981
|
-
return [];
|
|
3982
|
-
}
|
|
3983
|
-
},
|
|
3984
|
-
/**
|
|
3985
|
-
* Subscribes to native transaction update events (renewals, refunds,
|
|
3986
|
-
* cancellations) from StoreKit 2 (`Transaction.updates`) on iOS or
|
|
3987
|
-
* `PurchasesUpdatedListener` + refund diff on Android.
|
|
3988
|
-
*
|
|
3989
|
-
* Returns a subscription with a `remove()` method. The caller is
|
|
3990
|
-
* responsible for cleanup (typically on `Paywallo.reset()`).
|
|
3991
|
-
*
|
|
3992
|
-
* Returns `null` when the native module isn't available (non-mobile env,
|
|
3993
|
-
* native module not linked) — callers should treat null as a no-op.
|
|
3994
|
-
*/
|
|
3995
|
-
subscribeToTransactionUpdates(listener) {
|
|
3996
|
-
if (!PaywalloStoreKitNative) return null;
|
|
3997
|
-
try {
|
|
3998
|
-
const EmitterCtor = import_react_native16.NativeEventEmitter;
|
|
3999
|
-
const emitter = new EmitterCtor(PaywalloStoreKitNative);
|
|
4000
|
-
const sub = emitter.addListener(TRANSACTION_UPDATE_EVENT, (payload) => {
|
|
4001
|
-
const update = normalizeTransactionUpdate(payload);
|
|
4002
|
-
if (update) listener(update);
|
|
4003
|
-
});
|
|
4004
|
-
return { remove: () => sub.remove() };
|
|
4005
|
-
} catch (err) {
|
|
4006
|
-
if (PaywalloClient.getConfig()?.debug) console.log("[Paywallo] subscribeToTransactionUpdates failed", err);
|
|
4007
|
-
return null;
|
|
4008
|
-
}
|
|
4009
|
-
}
|
|
4010
|
-
};
|
|
4011
|
-
NativeStoreKit = nativeStoreKit;
|
|
4012
|
-
}
|
|
4013
|
-
});
|
|
4014
|
-
|
|
4015
3913
|
// src/domains/iap/IAPTransactionEmitter.ts
|
|
4016
3914
|
var IAPTransactionEmitter;
|
|
4017
3915
|
var init_IAPTransactionEmitter = __esm({
|
|
4018
3916
|
"src/domains/iap/IAPTransactionEmitter.ts"() {
|
|
4019
3917
|
"use strict";
|
|
4020
3918
|
init_PaywalloClient();
|
|
4021
|
-
init_NativeStoreKit();
|
|
4022
3919
|
IAPTransactionEmitter = class {
|
|
4023
3920
|
constructor(getProductFromCache, getApiClientFn, getDebug) {
|
|
4024
3921
|
this.getProductFromCache = getProductFromCache;
|
|
4025
3922
|
this.getApiClientFn = getApiClientFn;
|
|
4026
3923
|
this.getDebug = getDebug;
|
|
4027
|
-
this.transactionUpdateSubscription = null;
|
|
4028
|
-
}
|
|
4029
|
-
/**
|
|
4030
|
-
* Emits `transaction {type: "completed"}` after a server-validated purchase.
|
|
4031
|
-
* Fire-and-forget — a tracking failure must never poison the purchase flow.
|
|
4032
|
-
*/
|
|
4033
|
-
async emitTransactionCompleted(purchase, productId, options) {
|
|
4034
|
-
try {
|
|
4035
|
-
const apiClient = this.getApiClientFn();
|
|
4036
|
-
if (!apiClient) return;
|
|
4037
|
-
const distinctId = PaywalloClient.getDistinctId();
|
|
4038
|
-
if (!distinctId) return;
|
|
4039
|
-
const product = this.getProductFromCache(productId);
|
|
4040
|
-
const properties = {
|
|
4041
|
-
type: "completed",
|
|
4042
|
-
tx_id: purchase.transactionId,
|
|
4043
|
-
product_id: purchase.productId
|
|
4044
|
-
};
|
|
4045
|
-
if (product?.priceValue !== void 0 && product.priceValue > 0) {
|
|
4046
|
-
properties.amount = product.priceValue;
|
|
4047
|
-
}
|
|
4048
|
-
if (product?.currency) {
|
|
4049
|
-
properties.currency = product.currency;
|
|
4050
|
-
}
|
|
4051
|
-
if (options?.paywallId) properties.paywall_id = options.paywallId;
|
|
4052
|
-
if (options?.variantId) properties.variant_id = options.variantId;
|
|
4053
|
-
await apiClient.trackEvent("transaction", distinctId, properties, void 0, { priority: "critical" });
|
|
4054
|
-
} catch (err) {
|
|
4055
|
-
if (this.getDebug()) console.log("[Paywallo PURCHASE] transaction event emission failed", err);
|
|
4056
|
-
}
|
|
4057
3924
|
}
|
|
4058
3925
|
/**
|
|
4059
3926
|
* Emits `checkout_started` quando o usuário inicia a compra (toca em comprar),
|
|
@@ -4075,96 +3942,23 @@ var init_IAPTransactionEmitter = __esm({
|
|
|
4075
3942
|
};
|
|
4076
3943
|
await apiClient.trackEvent("checkout_started", distinctId, properties, void 0, {
|
|
4077
3944
|
priority: "critical"
|
|
4078
|
-
});
|
|
4079
|
-
if (this.getDebug()) console.log("[Paywallo PURCHASE] emitted checkout_started", productId);
|
|
4080
|
-
} catch (err) {
|
|
4081
|
-
if (this.getDebug()) console.log("[Paywallo PURCHASE] checkout_started emission failed", err);
|
|
4082
|
-
}
|
|
4083
|
-
}
|
|
4084
|
-
/**
|
|
4085
|
-
*
|
|
4086
|
-
*
|
|
4087
|
-
*/
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
}
|
|
4095
|
-
const distinctId = PaywalloClient.getDistinctId();
|
|
4096
|
-
if (!distinctId) {
|
|
4097
|
-
if (this.getDebug()) console.log("[Paywallo IAP] transaction update dropped (no distinctId)", update.type);
|
|
4098
|
-
return;
|
|
4099
|
-
}
|
|
4100
|
-
const properties = {
|
|
4101
|
-
type: update.type,
|
|
4102
|
-
tx_id: update.transactionId,
|
|
4103
|
-
product_id: update.productId
|
|
4104
|
-
};
|
|
4105
|
-
if (update.type !== "canceled") {
|
|
4106
|
-
const cached2 = this.getProductFromCache(update.productId);
|
|
4107
|
-
const amount = update.amount ?? cached2?.priceValue;
|
|
4108
|
-
const currency = update.currency ?? cached2?.currency;
|
|
4109
|
-
if (typeof amount === "number" && amount > 0) properties.amount = amount;
|
|
4110
|
-
if (currency) properties.currency = currency;
|
|
4111
|
-
}
|
|
4112
|
-
await apiClient.trackEvent("transaction", distinctId, properties, void 0, { priority: "critical" });
|
|
4113
|
-
if (this.getDebug()) console.log(`[Paywallo IAP] emitted transaction {${update.type}}`, update.transactionId);
|
|
4114
|
-
} catch (err) {
|
|
4115
|
-
if (this.getDebug()) console.log("[Paywallo IAP] transaction update emission failed", err);
|
|
4116
|
-
}
|
|
4117
|
-
}
|
|
4118
|
-
/**
|
|
4119
|
-
* Starts listening to native Transaction.updates. Idempotent.
|
|
4120
|
-
*/
|
|
4121
|
-
startListener() {
|
|
4122
|
-
if (this.transactionUpdateSubscription) return;
|
|
4123
|
-
this.transactionUpdateSubscription = nativeStoreKit.subscribeToTransactionUpdates(
|
|
4124
|
-
(update) => {
|
|
4125
|
-
void this.emitTransactionUpdate(update);
|
|
4126
|
-
}
|
|
4127
|
-
);
|
|
4128
|
-
if (this.getDebug() && this.transactionUpdateSubscription) {
|
|
4129
|
-
console.log("[Paywallo IAP] transaction update listener started");
|
|
4130
|
-
}
|
|
4131
|
-
}
|
|
4132
|
-
/**
|
|
4133
|
-
* Removes the native listener. Safe to call multiple times.
|
|
4134
|
-
*/
|
|
4135
|
-
stopListener() {
|
|
4136
|
-
if (!this.transactionUpdateSubscription) return;
|
|
4137
|
-
try {
|
|
4138
|
-
this.transactionUpdateSubscription.remove();
|
|
4139
|
-
} catch (err) {
|
|
4140
|
-
if (this.getDebug()) console.log("[Paywallo IAP] transaction listener removal failed", err);
|
|
4141
|
-
}
|
|
4142
|
-
this.transactionUpdateSubscription = null;
|
|
4143
|
-
if (this.getDebug()) console.log("[Paywallo IAP] transaction update listener stopped");
|
|
4144
|
-
}
|
|
4145
|
-
};
|
|
4146
|
-
}
|
|
4147
|
-
});
|
|
4148
|
-
|
|
4149
|
-
// src/errors/ClientError.ts
|
|
4150
|
-
var ClientError, CLIENT_ERROR_CODES;
|
|
4151
|
-
var init_ClientError = __esm({
|
|
4152
|
-
"src/errors/ClientError.ts"() {
|
|
4153
|
-
"use strict";
|
|
4154
|
-
init_PaywalloError();
|
|
4155
|
-
ClientError = class extends PaywalloError {
|
|
4156
|
-
constructor(code, message) {
|
|
4157
|
-
super("client", code, message);
|
|
4158
|
-
this.name = "ClientError";
|
|
3945
|
+
});
|
|
3946
|
+
if (this.getDebug()) console.log("[Paywallo PURCHASE] emitted checkout_started", productId);
|
|
3947
|
+
} catch (err) {
|
|
3948
|
+
if (this.getDebug()) console.log("[Paywallo PURCHASE] checkout_started emission failed", err);
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
/**
|
|
3952
|
+
* No-op. Native transaction update listener removed — sale events are sourced
|
|
3953
|
+
* exclusively from server-side webhooks (Apple/Google/Superwall/RevenueCat).
|
|
3954
|
+
*/
|
|
3955
|
+
startListener() {
|
|
3956
|
+
}
|
|
3957
|
+
/**
|
|
3958
|
+
* No-op. See startListener.
|
|
3959
|
+
*/
|
|
3960
|
+
stopListener() {
|
|
4159
3961
|
}
|
|
4160
|
-
};
|
|
4161
|
-
CLIENT_ERROR_CODES = {
|
|
4162
|
-
NOT_INITIALIZED: "CLIENT_NOT_INITIALIZED",
|
|
4163
|
-
MISSING_APP_KEY: "CLIENT_MISSING_APP_KEY",
|
|
4164
|
-
INVALID_EVENT_NAME: "CLIENT_INVALID_EVENT_NAME",
|
|
4165
|
-
PROVIDER_MISSING: "CLIENT_PROVIDER_MISSING",
|
|
4166
|
-
INSECURE_REQUEST: "CLIENT_INSECURE_REQUEST",
|
|
4167
|
-
UNKNOWN: "CLIENT_UNKNOWN"
|
|
4168
3962
|
};
|
|
4169
3963
|
}
|
|
4170
3964
|
});
|
|
@@ -4536,11 +4330,11 @@ var init_OfflineQueue = __esm({
|
|
|
4536
4330
|
});
|
|
4537
4331
|
return true;
|
|
4538
4332
|
}
|
|
4539
|
-
const
|
|
4333
|
+
const delay2 = Math.min(
|
|
4540
4334
|
this.config.baseRetryDelayMs * Math.pow(2, item.attempts - 1),
|
|
4541
4335
|
this.config.maxRetryDelayMs
|
|
4542
4336
|
);
|
|
4543
|
-
item.nextRetryAt = Date.now() +
|
|
4337
|
+
item.nextRetryAt = Date.now() + delay2;
|
|
4544
4338
|
await this.storage.saveToStorage(this.queue);
|
|
4545
4339
|
return false;
|
|
4546
4340
|
}
|
|
@@ -4651,11 +4445,11 @@ var init_types2 = __esm({
|
|
|
4651
4445
|
});
|
|
4652
4446
|
|
|
4653
4447
|
// src/core/network/NetworkMonitor.ts
|
|
4654
|
-
var
|
|
4448
|
+
var import_react_native16, NetworkMonitorClass, networkMonitor;
|
|
4655
4449
|
var init_NetworkMonitor = __esm({
|
|
4656
4450
|
"src/core/network/NetworkMonitor.ts"() {
|
|
4657
4451
|
"use strict";
|
|
4658
|
-
|
|
4452
|
+
import_react_native16 = require("react-native");
|
|
4659
4453
|
init_types2();
|
|
4660
4454
|
NetworkMonitorClass = class {
|
|
4661
4455
|
constructor() {
|
|
@@ -4684,7 +4478,7 @@ var init_NetworkMonitor = __esm({
|
|
|
4684
4478
|
}, this.config.checkInterval);
|
|
4685
4479
|
}
|
|
4686
4480
|
setupAppStateListener() {
|
|
4687
|
-
this.appStateSubscription =
|
|
4481
|
+
this.appStateSubscription = import_react_native16.AppState.addEventListener("change", (state) => {
|
|
4688
4482
|
if (state === "active") {
|
|
4689
4483
|
if (!this.checkIntervalId) {
|
|
4690
4484
|
this.checkIntervalId = setInterval(() => {
|
|
@@ -5052,12 +4846,62 @@ var init_queue = __esm({
|
|
|
5052
4846
|
}
|
|
5053
4847
|
});
|
|
5054
4848
|
|
|
4849
|
+
// src/domains/iap/PurchaseError.ts
|
|
4850
|
+
function createPurchaseError(code, message) {
|
|
4851
|
+
return new PurchaseError(
|
|
4852
|
+
PURCHASE_ERROR_CODES[code],
|
|
4853
|
+
message ?? DEFAULT_MESSAGES[code] ?? "Unknown purchase error",
|
|
4854
|
+
code === "USER_CANCELLED"
|
|
4855
|
+
);
|
|
4856
|
+
}
|
|
4857
|
+
var PurchaseError, PURCHASE_ERROR_CODES, DEFAULT_MESSAGES;
|
|
4858
|
+
var init_PurchaseError = __esm({
|
|
4859
|
+
"src/domains/iap/PurchaseError.ts"() {
|
|
4860
|
+
"use strict";
|
|
4861
|
+
init_PaywalloError();
|
|
4862
|
+
PurchaseError = class extends PaywalloError {
|
|
4863
|
+
constructor(code, message, userCancelled = false, httpStatus) {
|
|
4864
|
+
super("purchase", code, message);
|
|
4865
|
+
this.name = "PurchaseError";
|
|
4866
|
+
this.userCancelled = userCancelled;
|
|
4867
|
+
this.httpStatus = httpStatus;
|
|
4868
|
+
}
|
|
4869
|
+
};
|
|
4870
|
+
PURCHASE_ERROR_CODES = {
|
|
4871
|
+
NOT_INITIALIZED: "PURCHASE_NOT_INITIALIZED",
|
|
4872
|
+
PRODUCT_NOT_FOUND: "PRODUCT_NOT_FOUND",
|
|
4873
|
+
PURCHASE_FAILED: "PURCHASE_FAILED",
|
|
4874
|
+
RESTORE_FAILED: "RESTORE_FAILED",
|
|
4875
|
+
VALIDATION_FAILED: "VALIDATION_FAILED",
|
|
4876
|
+
USER_CANCELLED: "USER_CANCELLED",
|
|
4877
|
+
NETWORK_ERROR: "PURCHASE_NETWORK_ERROR",
|
|
4878
|
+
STORE_ERROR: "PURCHASE_STORE_ERROR",
|
|
4879
|
+
PENDING_PURCHASE: "PURCHASE_PENDING",
|
|
4880
|
+
DEFERRED_PURCHASE: "PURCHASE_DEFERRED",
|
|
4881
|
+
STORE_NOT_AVAILABLE: "PURCHASE_STORE_NOT_AVAILABLE"
|
|
4882
|
+
};
|
|
4883
|
+
DEFAULT_MESSAGES = {
|
|
4884
|
+
NOT_INITIALIZED: "Purchase controller not initialized. Call init() first.",
|
|
4885
|
+
PRODUCT_NOT_FOUND: "Product not found in store.",
|
|
4886
|
+
PURCHASE_FAILED: "Purchase failed. Please try again.",
|
|
4887
|
+
RESTORE_FAILED: "Failed to restore purchases. Please try again.",
|
|
4888
|
+
VALIDATION_FAILED: "Failed to validate purchase with server.",
|
|
4889
|
+
USER_CANCELLED: "Purchase was cancelled.",
|
|
4890
|
+
NETWORK_ERROR: "Network error. Please check your connection.",
|
|
4891
|
+
STORE_ERROR: "Store error. Please try again later.",
|
|
4892
|
+
PENDING_PURCHASE: "Purchase is pending approval.",
|
|
4893
|
+
DEFERRED_PURCHASE: "Purchase is deferred (e.g., parental approval required).",
|
|
4894
|
+
STORE_NOT_AVAILABLE: "In-app purchases are not available on this device."
|
|
4895
|
+
};
|
|
4896
|
+
}
|
|
4897
|
+
});
|
|
4898
|
+
|
|
5055
4899
|
// src/domains/iap/IAPValidator.ts
|
|
5056
|
-
var
|
|
4900
|
+
var import_react_native17, IAPValidator;
|
|
5057
4901
|
var init_IAPValidator = __esm({
|
|
5058
4902
|
"src/domains/iap/IAPValidator.ts"() {
|
|
5059
4903
|
"use strict";
|
|
5060
|
-
|
|
4904
|
+
import_react_native17 = require("react-native");
|
|
5061
4905
|
init_PaywalloClient();
|
|
5062
4906
|
init_queue();
|
|
5063
4907
|
init_PurchaseError();
|
|
@@ -5078,75 +4922,269 @@ var init_IAPValidator = __esm({
|
|
|
5078
4922
|
if (i < maxRetries) await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
5079
4923
|
}
|
|
5080
4924
|
}
|
|
5081
|
-
throw lastError;
|
|
5082
|
-
}
|
|
5083
|
-
async validateWithServer(purchase, productId, options) {
|
|
5084
|
-
const apiClient = this.getApiClientFn();
|
|
5085
|
-
const product = this.getProductFromCache(productId);
|
|
5086
|
-
const platform =
|
|
5087
|
-
const distinctId = PaywalloClient.getDistinctId();
|
|
5088
|
-
return this.validateWithRetry(
|
|
5089
|
-
() => apiClient.validatePurchase(
|
|
5090
|
-
platform,
|
|
5091
|
-
purchase.receipt,
|
|
5092
|
-
purchase.productId,
|
|
5093
|
-
purchase.transactionId,
|
|
5094
|
-
product?.priceValue ?? 0,
|
|
5095
|
-
product?.currency ?? "USD",
|
|
5096
|
-
this.getDeviceCountry(),
|
|
5097
|
-
distinctId,
|
|
5098
|
-
options?.paywallPlacement,
|
|
5099
|
-
options?.variantKey
|
|
5100
|
-
)
|
|
5101
|
-
);
|
|
5102
|
-
}
|
|
5103
|
-
/**
|
|
5104
|
-
* Bucketizes a validation failure into `server4xx` (permanent) vs `network` (transient).
|
|
5105
|
-
*/
|
|
5106
|
-
classifyValidationError(error) {
|
|
5107
|
-
if (error instanceof PurchaseError && typeof error.httpStatus === "number") {
|
|
5108
|
-
const status = error.httpStatus;
|
|
5109
|
-
if (status >= 400 && status < 500) return "server4xx";
|
|
4925
|
+
throw lastError;
|
|
4926
|
+
}
|
|
4927
|
+
async validateWithServer(purchase, productId, options) {
|
|
4928
|
+
const apiClient = this.getApiClientFn();
|
|
4929
|
+
const product = this.getProductFromCache(productId);
|
|
4930
|
+
const platform = import_react_native17.Platform.OS;
|
|
4931
|
+
const distinctId = PaywalloClient.getDistinctId();
|
|
4932
|
+
return this.validateWithRetry(
|
|
4933
|
+
() => apiClient.validatePurchase(
|
|
4934
|
+
platform,
|
|
4935
|
+
purchase.receipt,
|
|
4936
|
+
purchase.productId,
|
|
4937
|
+
purchase.transactionId,
|
|
4938
|
+
product?.priceValue ?? 0,
|
|
4939
|
+
product?.currency ?? "USD",
|
|
4940
|
+
this.getDeviceCountry(),
|
|
4941
|
+
distinctId,
|
|
4942
|
+
options?.paywallPlacement,
|
|
4943
|
+
options?.variantKey
|
|
4944
|
+
)
|
|
4945
|
+
);
|
|
4946
|
+
}
|
|
4947
|
+
/**
|
|
4948
|
+
* Bucketizes a validation failure into `server4xx` (permanent) vs `network` (transient).
|
|
4949
|
+
*/
|
|
4950
|
+
classifyValidationError(error) {
|
|
4951
|
+
if (error instanceof PurchaseError && typeof error.httpStatus === "number") {
|
|
4952
|
+
const status = error.httpStatus;
|
|
4953
|
+
if (status >= 400 && status < 500) return "server4xx";
|
|
4954
|
+
}
|
|
4955
|
+
return "network";
|
|
4956
|
+
}
|
|
4957
|
+
/**
|
|
4958
|
+
* Pushes the validation payload into the offline queue for durable retry.
|
|
4959
|
+
* The StoreKit transaction is intentionally left un-finished.
|
|
4960
|
+
*/
|
|
4961
|
+
async enqueueValidationRetry(purchase, productId, options) {
|
|
4962
|
+
try {
|
|
4963
|
+
const apiClient = this.getApiClientFn();
|
|
4964
|
+
const product = this.getProductFromCache(productId);
|
|
4965
|
+
const platform = import_react_native17.Platform.OS;
|
|
4966
|
+
const distinctId = PaywalloClient.getDistinctId();
|
|
4967
|
+
const appKey = apiClient.getAppKey();
|
|
4968
|
+
const body = {
|
|
4969
|
+
platform,
|
|
4970
|
+
receipt_data: purchase.receipt,
|
|
4971
|
+
product_id: purchase.productId,
|
|
4972
|
+
transaction_id: purchase.transactionId,
|
|
4973
|
+
price_local: product?.priceValue ?? 0,
|
|
4974
|
+
currency: product?.currency ?? "USD",
|
|
4975
|
+
country: this.getDeviceCountry(),
|
|
4976
|
+
distinct_id: distinctId
|
|
4977
|
+
};
|
|
4978
|
+
if (options?.paywallPlacement !== void 0) body.paywall_placement = options.paywallPlacement;
|
|
4979
|
+
if (options?.variantKey !== void 0) body.variant_key = options.variantKey;
|
|
4980
|
+
await offlineQueue.enqueue(
|
|
4981
|
+
"POST",
|
|
4982
|
+
"/sdk/purchases/validate",
|
|
4983
|
+
body,
|
|
4984
|
+
{ "X-App-Key": appKey },
|
|
4985
|
+
`validate:${purchase.transactionId}`,
|
|
4986
|
+
"critical"
|
|
4987
|
+
);
|
|
4988
|
+
if (this.getDebug()) console.log("[Paywallo PURCHASE] validation retry enqueued", purchase.transactionId);
|
|
4989
|
+
} catch (err) {
|
|
4990
|
+
console.error("[Paywallo PURCHASE] failed to enqueue validation retry", err);
|
|
4991
|
+
}
|
|
4992
|
+
}
|
|
4993
|
+
};
|
|
4994
|
+
}
|
|
4995
|
+
});
|
|
4996
|
+
|
|
4997
|
+
// src/domains/iap/NativeStoreKit.ts
|
|
4998
|
+
var NativeStoreKit_exports = {};
|
|
4999
|
+
__export(NativeStoreKit_exports, {
|
|
5000
|
+
NativeStoreKit: () => NativeStoreKit,
|
|
5001
|
+
nativeStoreKit: () => nativeStoreKit
|
|
5002
|
+
});
|
|
5003
|
+
function isAvailable3() {
|
|
5004
|
+
return PaywalloStoreKitNative != null;
|
|
5005
|
+
}
|
|
5006
|
+
function mapNativeProduct(native) {
|
|
5007
|
+
const product = {
|
|
5008
|
+
productId: native.productId,
|
|
5009
|
+
title: native.title,
|
|
5010
|
+
description: native.description,
|
|
5011
|
+
price: native.price,
|
|
5012
|
+
priceValue: native.priceValue,
|
|
5013
|
+
currency: native.currency ?? "USD",
|
|
5014
|
+
localizedPrice: native.localizedPrice,
|
|
5015
|
+
type: mapProductType(native.type),
|
|
5016
|
+
subscriptionPeriod: native.subscriptionPeriod,
|
|
5017
|
+
introductoryPrice: native.introductoryPrice,
|
|
5018
|
+
introductoryPriceValue: native.introductoryPriceValue,
|
|
5019
|
+
freeTrialPeriod: native.freeTrialPeriod
|
|
5020
|
+
};
|
|
5021
|
+
if (native.subscriptionPeriod) {
|
|
5022
|
+
const prices = calculatePerPeriodPrices(native.priceValue, native.subscriptionPeriod);
|
|
5023
|
+
product.pricePerMonth = prices.perMonth;
|
|
5024
|
+
product.pricePerWeek = prices.perWeek;
|
|
5025
|
+
product.pricePerDay = prices.perDay;
|
|
5026
|
+
}
|
|
5027
|
+
return product;
|
|
5028
|
+
}
|
|
5029
|
+
function mapProductType(type) {
|
|
5030
|
+
switch (type) {
|
|
5031
|
+
case "subscription":
|
|
5032
|
+
case "autoRenewable":
|
|
5033
|
+
return "subscription";
|
|
5034
|
+
case "consumable":
|
|
5035
|
+
return "consumable";
|
|
5036
|
+
default:
|
|
5037
|
+
return "non_consumable";
|
|
5038
|
+
}
|
|
5039
|
+
}
|
|
5040
|
+
function calculatePerPeriodPrices(priceValue, period) {
|
|
5041
|
+
const totalDays = periodToDays(period);
|
|
5042
|
+
if (totalDays <= 0) return {};
|
|
5043
|
+
const perDay = priceValue / totalDays;
|
|
5044
|
+
const perWeek = perDay * 7;
|
|
5045
|
+
const perMonth = perDay * 30;
|
|
5046
|
+
return {
|
|
5047
|
+
perDay: perDay.toFixed(2),
|
|
5048
|
+
perWeek: perWeek.toFixed(2),
|
|
5049
|
+
perMonth: perMonth.toFixed(2)
|
|
5050
|
+
};
|
|
5051
|
+
}
|
|
5052
|
+
function periodToDays(period) {
|
|
5053
|
+
const match = period.match(/^P(\d+)([DWMY])$/);
|
|
5054
|
+
if (!match) return 0;
|
|
5055
|
+
const value = parseInt(match[1], 10);
|
|
5056
|
+
switch (match[2]) {
|
|
5057
|
+
case "D":
|
|
5058
|
+
return value;
|
|
5059
|
+
case "W":
|
|
5060
|
+
return value * 7;
|
|
5061
|
+
case "M":
|
|
5062
|
+
return value * 30;
|
|
5063
|
+
case "Y":
|
|
5064
|
+
return value * 365;
|
|
5065
|
+
default:
|
|
5066
|
+
return 0;
|
|
5067
|
+
}
|
|
5068
|
+
}
|
|
5069
|
+
function mapNativePurchase(native) {
|
|
5070
|
+
return {
|
|
5071
|
+
productId: native.productId,
|
|
5072
|
+
transactionId: native.transactionId,
|
|
5073
|
+
transactionDate: native.transactionDate,
|
|
5074
|
+
receipt: native.receipt,
|
|
5075
|
+
platform: import_react_native18.Platform.OS
|
|
5076
|
+
};
|
|
5077
|
+
}
|
|
5078
|
+
function normalizeTransactionUpdate(raw) {
|
|
5079
|
+
if (!raw || typeof raw !== "object") return null;
|
|
5080
|
+
const r = raw;
|
|
5081
|
+
const type = r.type;
|
|
5082
|
+
if (type !== "renewed" && type !== "refunded" && type !== "canceled") return null;
|
|
5083
|
+
const transactionId = typeof r.transactionId === "string" ? r.transactionId : null;
|
|
5084
|
+
const productId = typeof r.productId === "string" ? r.productId : null;
|
|
5085
|
+
if (!transactionId) return null;
|
|
5086
|
+
const update = {
|
|
5087
|
+
type,
|
|
5088
|
+
transactionId,
|
|
5089
|
+
productId: productId ?? ""
|
|
5090
|
+
};
|
|
5091
|
+
if (typeof r.amount === "number") update.amount = r.amount;
|
|
5092
|
+
if (typeof r.currency === "string") update.currency = r.currency;
|
|
5093
|
+
return update;
|
|
5094
|
+
}
|
|
5095
|
+
var import_react_native18, TRANSACTION_UPDATE_EVENT, PaywalloStoreKitNative, nativeStoreKit, NativeStoreKit;
|
|
5096
|
+
var init_NativeStoreKit = __esm({
|
|
5097
|
+
"src/domains/iap/NativeStoreKit.ts"() {
|
|
5098
|
+
"use strict";
|
|
5099
|
+
import_react_native18 = require("react-native");
|
|
5100
|
+
init_PaywalloClient();
|
|
5101
|
+
init_uuid();
|
|
5102
|
+
init_IdentityManager();
|
|
5103
|
+
init_PurchaseError();
|
|
5104
|
+
TRANSACTION_UPDATE_EVENT = "PaywalloTransactionUpdate";
|
|
5105
|
+
PaywalloStoreKitNative = import_react_native18.NativeModules.PaywalloStoreKit ?? null;
|
|
5106
|
+
nativeStoreKit = {
|
|
5107
|
+
isAvailable: isAvailable3,
|
|
5108
|
+
async getProducts(productIds) {
|
|
5109
|
+
if (!PaywalloStoreKitNative) {
|
|
5110
|
+
return [];
|
|
5111
|
+
}
|
|
5112
|
+
const nativeProducts = await PaywalloStoreKitNative.getProducts(productIds);
|
|
5113
|
+
return nativeProducts.map(mapNativeProduct);
|
|
5114
|
+
},
|
|
5115
|
+
async purchase(productId) {
|
|
5116
|
+
if (!PaywalloStoreKitNative) {
|
|
5117
|
+
throw new PurchaseError(
|
|
5118
|
+
PURCHASE_ERROR_CODES.STORE_NOT_AVAILABLE,
|
|
5119
|
+
"StoreKit native module not available"
|
|
5120
|
+
);
|
|
5121
|
+
}
|
|
5122
|
+
const distinctId = identityManager.getDistinctId();
|
|
5123
|
+
const rawUuid = distinctId?.startsWith("$paywallo_anon:") ? distinctId.slice("$paywallo_anon:".length) : distinctId;
|
|
5124
|
+
const appAccountToken = isValidUUIDv4(rawUuid) ? rawUuid : null;
|
|
5125
|
+
if (distinctId && appAccountToken === null) {
|
|
5126
|
+
if (PaywalloClient.getConfig()?.debug) console.log("[Paywallo] distinctId is not a valid UUID v4 \u2014 passing null to appAccountToken:", distinctId);
|
|
5127
|
+
}
|
|
5128
|
+
try {
|
|
5129
|
+
const result = await PaywalloStoreKitNative.purchase(productId, appAccountToken);
|
|
5130
|
+
if (!result || result.pending) {
|
|
5131
|
+
return null;
|
|
5132
|
+
}
|
|
5133
|
+
return mapNativePurchase(result);
|
|
5134
|
+
} catch (err) {
|
|
5135
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5136
|
+
if (message.toLowerCase().includes("cancel")) {
|
|
5137
|
+
throw new PurchaseError(PURCHASE_ERROR_CODES.USER_CANCELLED, message, true);
|
|
5138
|
+
}
|
|
5139
|
+
throw new PurchaseError(PURCHASE_ERROR_CODES.PURCHASE_FAILED, message);
|
|
5140
|
+
}
|
|
5141
|
+
},
|
|
5142
|
+
async finishTransaction(transactionId) {
|
|
5143
|
+
if (!PaywalloStoreKitNative) return;
|
|
5144
|
+
try {
|
|
5145
|
+
await PaywalloStoreKitNative.finishTransaction(transactionId);
|
|
5146
|
+
} catch (err) {
|
|
5147
|
+
console.error("[Paywallo] finishTransaction native error", err);
|
|
5110
5148
|
}
|
|
5111
|
-
|
|
5112
|
-
|
|
5149
|
+
},
|
|
5150
|
+
async getActiveTransactions() {
|
|
5151
|
+
if (!PaywalloStoreKitNative) return [];
|
|
5152
|
+
try {
|
|
5153
|
+
const results = await PaywalloStoreKitNative.getActiveTransactions();
|
|
5154
|
+
return results.map(mapNativePurchase);
|
|
5155
|
+
} catch (err) {
|
|
5156
|
+
console.error("[Paywallo] getActiveTransactions failed", err);
|
|
5157
|
+
return [];
|
|
5158
|
+
}
|
|
5159
|
+
},
|
|
5113
5160
|
/**
|
|
5114
|
-
*
|
|
5115
|
-
*
|
|
5161
|
+
* Subscribes to native transaction update events (renewals, refunds,
|
|
5162
|
+
* cancellations) from StoreKit 2 (`Transaction.updates`) on iOS or
|
|
5163
|
+
* `PurchasesUpdatedListener` + refund diff on Android.
|
|
5164
|
+
*
|
|
5165
|
+
* Returns a subscription with a `remove()` method. The caller is
|
|
5166
|
+
* responsible for cleanup (typically on `Paywallo.reset()`).
|
|
5167
|
+
*
|
|
5168
|
+
* Returns `null` when the native module isn't available (non-mobile env,
|
|
5169
|
+
* native module not linked) — callers should treat null as a no-op.
|
|
5116
5170
|
*/
|
|
5117
|
-
|
|
5171
|
+
subscribeToTransactionUpdates(listener) {
|
|
5172
|
+
if (!PaywalloStoreKitNative) return null;
|
|
5118
5173
|
try {
|
|
5119
|
-
const
|
|
5120
|
-
const
|
|
5121
|
-
const
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
receipt_data: purchase.receipt,
|
|
5127
|
-
product_id: purchase.productId,
|
|
5128
|
-
transaction_id: purchase.transactionId,
|
|
5129
|
-
price_local: product?.priceValue ?? 0,
|
|
5130
|
-
currency: product?.currency ?? "USD",
|
|
5131
|
-
country: this.getDeviceCountry(),
|
|
5132
|
-
distinct_id: distinctId
|
|
5133
|
-
};
|
|
5134
|
-
if (options?.paywallPlacement !== void 0) body.paywall_placement = options.paywallPlacement;
|
|
5135
|
-
if (options?.variantKey !== void 0) body.variant_key = options.variantKey;
|
|
5136
|
-
await offlineQueue.enqueue(
|
|
5137
|
-
"POST",
|
|
5138
|
-
"/sdk/purchases/validate",
|
|
5139
|
-
body,
|
|
5140
|
-
{ "X-App-Key": appKey },
|
|
5141
|
-
`validate:${purchase.transactionId}`,
|
|
5142
|
-
"critical"
|
|
5143
|
-
);
|
|
5144
|
-
if (this.getDebug()) console.log("[Paywallo PURCHASE] validation retry enqueued", purchase.transactionId);
|
|
5174
|
+
const EmitterCtor = import_react_native18.NativeEventEmitter;
|
|
5175
|
+
const emitter = new EmitterCtor(PaywalloStoreKitNative);
|
|
5176
|
+
const sub = emitter.addListener(TRANSACTION_UPDATE_EVENT, (payload) => {
|
|
5177
|
+
const update = normalizeTransactionUpdate(payload);
|
|
5178
|
+
if (update) listener(update);
|
|
5179
|
+
});
|
|
5180
|
+
return { remove: () => sub.remove() };
|
|
5145
5181
|
} catch (err) {
|
|
5146
|
-
console.
|
|
5182
|
+
if (PaywalloClient.getConfig()?.debug) console.log("[Paywallo] subscribeToTransactionUpdates failed", err);
|
|
5183
|
+
return null;
|
|
5147
5184
|
}
|
|
5148
5185
|
}
|
|
5149
5186
|
};
|
|
5187
|
+
NativeStoreKit = nativeStoreKit;
|
|
5150
5188
|
}
|
|
5151
5189
|
});
|
|
5152
5190
|
|
|
@@ -5250,8 +5288,8 @@ var init_IAPService = __esm({
|
|
|
5250
5288
|
stopTransactionListener() {
|
|
5251
5289
|
this.emitter.stopListener();
|
|
5252
5290
|
}
|
|
5253
|
-
|
|
5254
|
-
|
|
5291
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
5292
|
+
async emitTransactionUpdate(_update) {
|
|
5255
5293
|
}
|
|
5256
5294
|
async purchase(productId, options) {
|
|
5257
5295
|
if (!nativeStoreKit.isAvailable()) {
|
|
@@ -5280,10 +5318,6 @@ var init_IAPService = __esm({
|
|
|
5280
5318
|
if (this.debug) console.log(`[Paywallo PURCHASE] ${productId} server validated`);
|
|
5281
5319
|
await this.finishTransaction(purchase.transactionId);
|
|
5282
5320
|
if (this.debug) console.log(`[Paywallo PURCHASE] ${productId} finished`);
|
|
5283
|
-
await this.emitter.emitTransactionCompleted(purchase, productId, {
|
|
5284
|
-
paywallId: options?.paywallId,
|
|
5285
|
-
variantId: options?.variantId
|
|
5286
|
-
});
|
|
5287
5321
|
}).catch(async (err) => {
|
|
5288
5322
|
const kind = this.validator.classifyValidationError(err);
|
|
5289
5323
|
if (kind === "server4xx") {
|
|
@@ -6370,12 +6404,149 @@ var init_plan = __esm({
|
|
|
6370
6404
|
}
|
|
6371
6405
|
});
|
|
6372
6406
|
|
|
6373
|
-
// src/domains/session/index.ts
|
|
6374
|
-
var init_session = __esm({
|
|
6375
|
-
"src/domains/session/index.ts"() {
|
|
6407
|
+
// src/domains/session/index.ts
|
|
6408
|
+
var init_session = __esm({
|
|
6409
|
+
"src/domains/session/index.ts"() {
|
|
6410
|
+
"use strict";
|
|
6411
|
+
init_SessionError();
|
|
6412
|
+
init_SessionManager();
|
|
6413
|
+
}
|
|
6414
|
+
});
|
|
6415
|
+
|
|
6416
|
+
// src/domains/onboarding/OnboardingError.ts
|
|
6417
|
+
var OnboardingError, ONBOARDING_ERROR_CODES;
|
|
6418
|
+
var init_OnboardingError = __esm({
|
|
6419
|
+
"src/domains/onboarding/OnboardingError.ts"() {
|
|
6420
|
+
"use strict";
|
|
6421
|
+
init_PaywalloError();
|
|
6422
|
+
OnboardingError = class extends PaywalloError {
|
|
6423
|
+
constructor(code, message) {
|
|
6424
|
+
super("onboarding", code, message);
|
|
6425
|
+
this.name = "OnboardingError";
|
|
6426
|
+
}
|
|
6427
|
+
};
|
|
6428
|
+
ONBOARDING_ERROR_CODES = {
|
|
6429
|
+
NOT_INITIALIZED: "ONBOARDING_NOT_INITIALIZED",
|
|
6430
|
+
INVALID_STEP_NAME: "ONBOARDING_INVALID_STEP_NAME"
|
|
6431
|
+
};
|
|
6432
|
+
}
|
|
6433
|
+
});
|
|
6434
|
+
|
|
6435
|
+
// src/domains/onboarding/OnboardingManager.ts
|
|
6436
|
+
var OnboardingManager, onboardingManager;
|
|
6437
|
+
var init_OnboardingManager = __esm({
|
|
6438
|
+
"src/domains/onboarding/OnboardingManager.ts"() {
|
|
6439
|
+
"use strict";
|
|
6440
|
+
init_OnboardingError();
|
|
6441
|
+
OnboardingManager = class {
|
|
6442
|
+
constructor() {
|
|
6443
|
+
this.pipeline = null;
|
|
6444
|
+
this.debug = false;
|
|
6445
|
+
this.lastStep = null;
|
|
6446
|
+
this.finished = false;
|
|
6447
|
+
}
|
|
6448
|
+
/**
|
|
6449
|
+
* Injects runtime dependencies. Called by PaywalloClient during init.
|
|
6450
|
+
*/
|
|
6451
|
+
injectDeps(config) {
|
|
6452
|
+
this.pipeline = config.pipeline;
|
|
6453
|
+
this.debug = config.debug ?? false;
|
|
6454
|
+
}
|
|
6455
|
+
/**
|
|
6456
|
+
* Tracks an onboarding step view.
|
|
6457
|
+
* Saves the step name internally for implicit drop detection on the backend.
|
|
6458
|
+
*
|
|
6459
|
+
* @param stepName - Unique name for this onboarding step.
|
|
6460
|
+
* @param order - Optional explicit display order (accepts decimals, e.g. 2.1 for A/B variants).
|
|
6461
|
+
*/
|
|
6462
|
+
async step(stepName, order) {
|
|
6463
|
+
this.assertConfig();
|
|
6464
|
+
this.validateStepName(stepName, "step");
|
|
6465
|
+
this.lastStep = stepName;
|
|
6466
|
+
const stepPayload = { step_name: stepName };
|
|
6467
|
+
if (order !== void 0 && Number.isFinite(order) && order >= 0) {
|
|
6468
|
+
stepPayload.order = order;
|
|
6469
|
+
}
|
|
6470
|
+
const payload = {
|
|
6471
|
+
family: "onboarding",
|
|
6472
|
+
type: "step",
|
|
6473
|
+
...stepPayload
|
|
6474
|
+
};
|
|
6475
|
+
return this.emit("onboarding", payload, "step");
|
|
6476
|
+
}
|
|
6477
|
+
/**
|
|
6478
|
+
* Tracks successful completion of the onboarding flow.
|
|
6479
|
+
* Marks the flow as finished.
|
|
6480
|
+
*/
|
|
6481
|
+
async complete() {
|
|
6482
|
+
this.assertConfig();
|
|
6483
|
+
this.finished = true;
|
|
6484
|
+
return this.emit("onboarding", { family: "onboarding", type: "complete" }, "complete");
|
|
6485
|
+
}
|
|
6486
|
+
/**
|
|
6487
|
+
* Tracks an explicit drop (abandonment) at the given step.
|
|
6488
|
+
* Marks the flow as finished.
|
|
6489
|
+
*
|
|
6490
|
+
* @param stepName - The step name where the user dropped off.
|
|
6491
|
+
*/
|
|
6492
|
+
async drop(stepName) {
|
|
6493
|
+
this.assertConfig();
|
|
6494
|
+
this.validateStepName(stepName, "drop");
|
|
6495
|
+
this.finished = true;
|
|
6496
|
+
const payload = {
|
|
6497
|
+
family: "onboarding",
|
|
6498
|
+
type: "drop",
|
|
6499
|
+
...{ step_name: stepName }
|
|
6500
|
+
};
|
|
6501
|
+
return this.emit("onboarding", payload, "drop");
|
|
6502
|
+
}
|
|
6503
|
+
/** Returns the last step name seen, or null if no step was tracked yet. */
|
|
6504
|
+
getLastStep() {
|
|
6505
|
+
return this.lastStep;
|
|
6506
|
+
}
|
|
6507
|
+
/** Returns whether the flow has been marked as finished (complete or drop). */
|
|
6508
|
+
isFinished() {
|
|
6509
|
+
return this.finished;
|
|
6510
|
+
}
|
|
6511
|
+
async emit(eventName, properties, logLabel) {
|
|
6512
|
+
const pipeline = this.pipeline;
|
|
6513
|
+
const distinctId = pipeline.getDistinctId();
|
|
6514
|
+
if (!distinctId) {
|
|
6515
|
+
if (this.debug) console.log(`[Paywallo:Onboarding] skipped (no distinctId): ${logLabel}`);
|
|
6516
|
+
return;
|
|
6517
|
+
}
|
|
6518
|
+
await pipeline.trackEvent(eventName, distinctId, properties);
|
|
6519
|
+
if (this.debug) {
|
|
6520
|
+
console.log(`[Paywallo:Onboarding] event emitted: ${eventName}.${logLabel}`, properties);
|
|
6521
|
+
}
|
|
6522
|
+
}
|
|
6523
|
+
assertConfig() {
|
|
6524
|
+
if (!this.pipeline) {
|
|
6525
|
+
throw new OnboardingError(
|
|
6526
|
+
ONBOARDING_ERROR_CODES.NOT_INITIALIZED,
|
|
6527
|
+
"OnboardingManager not initialized. Call Paywallo.init() first."
|
|
6528
|
+
);
|
|
6529
|
+
}
|
|
6530
|
+
}
|
|
6531
|
+
validateStepName(stepName, method) {
|
|
6532
|
+
if (typeof stepName !== "string" || stepName.trim().length === 0) {
|
|
6533
|
+
throw new OnboardingError(
|
|
6534
|
+
ONBOARDING_ERROR_CODES.INVALID_STEP_NAME,
|
|
6535
|
+
`OnboardingManager.${method}(): stepName must be a non-empty string.`
|
|
6536
|
+
);
|
|
6537
|
+
}
|
|
6538
|
+
}
|
|
6539
|
+
};
|
|
6540
|
+
onboardingManager = new OnboardingManager();
|
|
6541
|
+
}
|
|
6542
|
+
});
|
|
6543
|
+
|
|
6544
|
+
// src/domains/onboarding/index.ts
|
|
6545
|
+
var init_onboarding = __esm({
|
|
6546
|
+
"src/domains/onboarding/index.ts"() {
|
|
6376
6547
|
"use strict";
|
|
6377
|
-
|
|
6378
|
-
|
|
6548
|
+
init_OnboardingError();
|
|
6549
|
+
init_OnboardingManager();
|
|
6379
6550
|
}
|
|
6380
6551
|
});
|
|
6381
6552
|
|
|
@@ -6536,268 +6707,124 @@ function validateEvent(eventName, properties) {
|
|
|
6536
6707
|
return { ok: false, family, error: parsed.error };
|
|
6537
6708
|
}
|
|
6538
6709
|
function formatValidationError(error) {
|
|
6539
|
-
return error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
|
|
6540
|
-
}
|
|
6541
|
-
var CANONICAL_FAMILIES, DEPRECATED_EVENT_NAMES;
|
|
6542
|
-
var init_eventFamilies = __esm({
|
|
6543
|
-
"src/core/events/eventFamilies.ts"() {
|
|
6544
|
-
"use strict";
|
|
6545
|
-
init_schemas();
|
|
6546
|
-
CANONICAL_FAMILIES = [
|
|
6547
|
-
"lifecycle",
|
|
6548
|
-
"identify",
|
|
6549
|
-
"paywall",
|
|
6550
|
-
"transaction",
|
|
6551
|
-
"onboarding",
|
|
6552
|
-
"notification",
|
|
6553
|
-
"custom"
|
|
6554
|
-
];
|
|
6555
|
-
DEPRECATED_EVENT_NAMES = [
|
|
6556
|
-
"$paywall_purchased",
|
|
6557
|
-
"$paywall_product_selected",
|
|
6558
|
-
"$core_action",
|
|
6559
|
-
"$campaign_impression",
|
|
6560
|
-
"$app_open",
|
|
6561
|
-
"$app_background",
|
|
6562
|
-
"$app_foreground",
|
|
6563
|
-
"session.end"
|
|
6564
|
-
];
|
|
6565
|
-
}
|
|
6566
|
-
});
|
|
6567
|
-
|
|
6568
|
-
// src/core/PaywalloAnalytics.ts
|
|
6569
|
-
function isValidEventName(name) {
|
|
6570
|
-
if (name.startsWith("$")) return /^\$[a-z][a-z0-9]*(_[a-z0-9]+)*$/.test(name);
|
|
6571
|
-
return /^[a-z][a-z0-9]*(_[a-z0-9]+)*$/.test(name);
|
|
6572
|
-
}
|
|
6573
|
-
async function trackEvent(apiClient, state, eventName, options) {
|
|
6574
|
-
const distinctId = identityManager.getDistinctId();
|
|
6575
|
-
if (!distinctId) {
|
|
6576
|
-
console.error("[Paywallo:DROP] event dropped \u2014 no distinctId available yet", { eventName });
|
|
6577
|
-
return;
|
|
6578
|
-
}
|
|
6579
|
-
if (!isValidEventName(eventName)) {
|
|
6580
|
-
const msg = `Invalid event name: "${eventName}". Use snake_case or $reserved_name.`;
|
|
6581
|
-
throw new ClientError(CLIENT_ERROR_CODES.INVALID_EVENT_NAME, msg);
|
|
6582
|
-
}
|
|
6583
|
-
if (state.config?.debug && eventName.startsWith("$purchase")) console.log(`[Paywallo TRACK] ${eventName}`, options?.properties ?? {});
|
|
6584
|
-
const sessionId = sessionManager.getSessionId();
|
|
6585
|
-
const family = detectFamily(eventName);
|
|
6586
|
-
const isCustomFamily = family === "custom";
|
|
6587
|
-
const identityProps = isCustomFamily ? identityManager.getProperties() : {};
|
|
6588
|
-
const sessionProp = isCustomFamily && sessionId ? { sessionId } : {};
|
|
6589
|
-
await apiClient.trackEvent(
|
|
6590
|
-
eventName,
|
|
6591
|
-
distinctId,
|
|
6592
|
-
{ ...identityProps, ...options?.properties, ...sessionProp },
|
|
6593
|
-
options?.timestamp,
|
|
6594
|
-
options?.priority ? { priority: options.priority } : void 0
|
|
6595
|
-
);
|
|
6596
|
-
}
|
|
6597
|
-
async function trackOnboardingStep(apiClient, state, index, name) {
|
|
6598
|
-
const distinctId = identityManager.getDistinctId();
|
|
6599
|
-
if (!distinctId) return;
|
|
6600
|
-
if (!Number.isInteger(index) || index < 0) {
|
|
6601
|
-
const msg = `Invalid stepIndex: "${index}". Must be a non-negative integer.`;
|
|
6602
|
-
throw new AnalyticsError(ANALYTICS_ERROR_CODES.INVALID_STEP_INDEX, msg);
|
|
6603
|
-
}
|
|
6604
|
-
const now = Date.now();
|
|
6605
|
-
const timeOnPreviousStep = state.lastOnboardingStepTimestamp !== null ? Math.round((now - state.lastOnboardingStepTimestamp) / 1e3) : 0;
|
|
6606
|
-
state.lastOnboardingStepTimestamp = now;
|
|
6607
|
-
await apiClient.trackEvent("$onboarding_step", distinctId, { stepIndex: index, stepName: name, timeOnPreviousStep });
|
|
6608
|
-
}
|
|
6609
|
-
function validateCoreAction(actionName) {
|
|
6610
|
-
if (!actionName || typeof actionName !== "string") {
|
|
6611
|
-
const msg = `Invalid actionName: "${actionName}". Must be a non-empty string.`;
|
|
6612
|
-
throw new AnalyticsError(ANALYTICS_ERROR_CODES.INVALID_ACTION_NAME, msg);
|
|
6613
|
-
}
|
|
6614
|
-
}
|
|
6615
|
-
var init_PaywalloAnalytics = __esm({
|
|
6616
|
-
"src/core/PaywalloAnalytics.ts"() {
|
|
6617
|
-
"use strict";
|
|
6618
|
-
init_analytics();
|
|
6619
|
-
init_identity();
|
|
6620
|
-
init_session();
|
|
6621
|
-
init_ClientError();
|
|
6622
|
-
init_eventFamilies();
|
|
6623
|
-
}
|
|
6624
|
-
});
|
|
6625
|
-
|
|
6626
|
-
// src/core/PaywalloFlags.ts
|
|
6627
|
-
async function getVariantCached(apiClient, flagKey, defaultValue) {
|
|
6628
|
-
const distinctId = identityManager.getDistinctId();
|
|
6629
|
-
const result = await apiClient.getVariantFromCache(flagKey, distinctId);
|
|
6630
|
-
if (result) return result;
|
|
6631
|
-
void apiClient.getVariant(flagKey, distinctId).catch(() => {
|
|
6632
|
-
});
|
|
6633
|
-
return { variant: defaultValue ?? null, payload: void 0 };
|
|
6634
|
-
}
|
|
6635
|
-
async function getVariant(apiClient, state, flagKey) {
|
|
6636
|
-
try {
|
|
6637
|
-
const distinctId = identityManager.getDistinctId();
|
|
6638
|
-
if (!distinctId) {
|
|
6639
|
-
if (state.config?.debug) console.log(`[Paywallo FLAG] ${flagKey} \u2192 null (no distinctId)`);
|
|
6640
|
-
return { variant: null };
|
|
6641
|
-
}
|
|
6642
|
-
const result = await apiClient.getVariant(flagKey, distinctId);
|
|
6643
|
-
if (state.config?.debug) console.log(`[Paywallo FLAG] ${flagKey} \u2192`, result.variant);
|
|
6644
|
-
return result;
|
|
6645
|
-
} catch (error) {
|
|
6646
|
-
if (state.config?.debug) console.log(`[Paywallo FLAG] ${flagKey} \u2192 error`, error);
|
|
6647
|
-
if (state.apiClient) void state.apiClient.reportError("GET_VARIANT_FAILED", error instanceof Error ? error.message : String(error));
|
|
6648
|
-
return { variant: null };
|
|
6649
|
-
}
|
|
6650
|
-
}
|
|
6651
|
-
function evaluateFlags(apiClient, keys) {
|
|
6652
|
-
return apiClient.evaluateFlags(keys, identityManager.getDistinctId());
|
|
6653
|
-
}
|
|
6654
|
-
async function getConditionalFlag(apiClient, state, flagKey, context) {
|
|
6655
|
-
try {
|
|
6656
|
-
const distinctId = identityManager.getDistinctId();
|
|
6657
|
-
if (!distinctId) return false;
|
|
6658
|
-
const result = await apiClient.getConditionalFlag(flagKey, { distinctId, ...context });
|
|
6659
|
-
return result.value;
|
|
6660
|
-
} catch (error) {
|
|
6661
|
-
if (state.apiClient) void state.apiClient.reportError("GET_CONDITIONAL_FLAG_FAILED", error instanceof Error ? error.message : String(error));
|
|
6662
|
-
return false;
|
|
6663
|
-
}
|
|
6664
|
-
}
|
|
6665
|
-
var init_PaywalloFlags = __esm({
|
|
6666
|
-
"src/core/PaywalloFlags.ts"() {
|
|
6667
|
-
"use strict";
|
|
6668
|
-
init_identity();
|
|
6669
|
-
}
|
|
6670
|
-
});
|
|
6671
|
-
|
|
6672
|
-
// src/domains/onboarding/OnboardingError.ts
|
|
6673
|
-
var OnboardingError, ONBOARDING_ERROR_CODES;
|
|
6674
|
-
var init_OnboardingError = __esm({
|
|
6675
|
-
"src/domains/onboarding/OnboardingError.ts"() {
|
|
6676
|
-
"use strict";
|
|
6677
|
-
init_PaywalloError();
|
|
6678
|
-
OnboardingError = class extends PaywalloError {
|
|
6679
|
-
constructor(code, message) {
|
|
6680
|
-
super("onboarding", code, message);
|
|
6681
|
-
this.name = "OnboardingError";
|
|
6682
|
-
}
|
|
6683
|
-
};
|
|
6684
|
-
ONBOARDING_ERROR_CODES = {
|
|
6685
|
-
NOT_INITIALIZED: "ONBOARDING_NOT_INITIALIZED",
|
|
6686
|
-
INVALID_STEP_NAME: "ONBOARDING_INVALID_STEP_NAME"
|
|
6687
|
-
};
|
|
6688
|
-
}
|
|
6689
|
-
});
|
|
6690
|
-
|
|
6691
|
-
// src/domains/onboarding/OnboardingManager.ts
|
|
6692
|
-
var OnboardingManager, onboardingManager;
|
|
6693
|
-
var init_OnboardingManager = __esm({
|
|
6694
|
-
"src/domains/onboarding/OnboardingManager.ts"() {
|
|
6695
|
-
"use strict";
|
|
6696
|
-
init_OnboardingError();
|
|
6697
|
-
OnboardingManager = class {
|
|
6698
|
-
constructor() {
|
|
6699
|
-
this.pipeline = null;
|
|
6700
|
-
this.debug = false;
|
|
6701
|
-
this.lastStep = null;
|
|
6702
|
-
this.finished = false;
|
|
6703
|
-
}
|
|
6704
|
-
/**
|
|
6705
|
-
* Injects runtime dependencies. Called by PaywalloClient during init.
|
|
6706
|
-
*/
|
|
6707
|
-
injectDeps(config) {
|
|
6708
|
-
this.pipeline = config.pipeline;
|
|
6709
|
-
this.debug = config.debug ?? false;
|
|
6710
|
-
}
|
|
6711
|
-
/**
|
|
6712
|
-
* Tracks an onboarding step view.
|
|
6713
|
-
* Saves the step name internally for implicit drop detection on the backend.
|
|
6714
|
-
*
|
|
6715
|
-
* @param stepName - Unique name for this onboarding step.
|
|
6716
|
-
*/
|
|
6717
|
-
async step(stepName) {
|
|
6718
|
-
this.assertConfig();
|
|
6719
|
-
this.validateStepName(stepName, "step");
|
|
6720
|
-
this.lastStep = stepName;
|
|
6721
|
-
const payload = {
|
|
6722
|
-
family: "onboarding",
|
|
6723
|
-
type: "step",
|
|
6724
|
-
...{ step_name: stepName }
|
|
6725
|
-
};
|
|
6726
|
-
return this.emit("onboarding", payload, "step");
|
|
6727
|
-
}
|
|
6728
|
-
/**
|
|
6729
|
-
* Tracks successful completion of the onboarding flow.
|
|
6730
|
-
* Marks the flow as finished.
|
|
6731
|
-
*/
|
|
6732
|
-
async complete() {
|
|
6733
|
-
this.assertConfig();
|
|
6734
|
-
this.finished = true;
|
|
6735
|
-
return this.emit("onboarding", { family: "onboarding", type: "complete" }, "complete");
|
|
6736
|
-
}
|
|
6737
|
-
/**
|
|
6738
|
-
* Tracks an explicit drop (abandonment) at the given step.
|
|
6739
|
-
* Marks the flow as finished.
|
|
6740
|
-
*
|
|
6741
|
-
* @param stepName - The step name where the user dropped off.
|
|
6742
|
-
*/
|
|
6743
|
-
async drop(stepName) {
|
|
6744
|
-
this.assertConfig();
|
|
6745
|
-
this.validateStepName(stepName, "drop");
|
|
6746
|
-
this.finished = true;
|
|
6747
|
-
const payload = {
|
|
6748
|
-
family: "onboarding",
|
|
6749
|
-
type: "drop",
|
|
6750
|
-
...{ step_name: stepName }
|
|
6751
|
-
};
|
|
6752
|
-
return this.emit("onboarding", payload, "drop");
|
|
6753
|
-
}
|
|
6754
|
-
/** Returns the last step name seen, or null if no step was tracked yet. */
|
|
6755
|
-
getLastStep() {
|
|
6756
|
-
return this.lastStep;
|
|
6757
|
-
}
|
|
6758
|
-
/** Returns whether the flow has been marked as finished (complete or drop). */
|
|
6759
|
-
isFinished() {
|
|
6760
|
-
return this.finished;
|
|
6761
|
-
}
|
|
6762
|
-
async emit(eventName, properties, logLabel) {
|
|
6763
|
-
const pipeline = this.pipeline;
|
|
6764
|
-
const distinctId = pipeline.getDistinctId();
|
|
6765
|
-
if (!distinctId) {
|
|
6766
|
-
if (this.debug) console.log(`[Paywallo:Onboarding] skipped (no distinctId): ${logLabel}`);
|
|
6767
|
-
return;
|
|
6768
|
-
}
|
|
6769
|
-
await pipeline.trackEvent(eventName, distinctId, properties);
|
|
6770
|
-
if (this.debug) {
|
|
6771
|
-
console.log(`[Paywallo:Onboarding] event emitted: ${eventName}.${logLabel}`, properties);
|
|
6772
|
-
}
|
|
6773
|
-
}
|
|
6774
|
-
assertConfig() {
|
|
6775
|
-
if (!this.pipeline) {
|
|
6776
|
-
throw new OnboardingError(
|
|
6777
|
-
ONBOARDING_ERROR_CODES.NOT_INITIALIZED,
|
|
6778
|
-
"OnboardingManager not initialized. Call Paywallo.init() first."
|
|
6779
|
-
);
|
|
6780
|
-
}
|
|
6781
|
-
}
|
|
6782
|
-
validateStepName(stepName, method) {
|
|
6783
|
-
if (typeof stepName !== "string" || stepName.trim().length === 0) {
|
|
6784
|
-
throw new OnboardingError(
|
|
6785
|
-
ONBOARDING_ERROR_CODES.INVALID_STEP_NAME,
|
|
6786
|
-
`OnboardingManager.${method}(): stepName must be a non-empty string.`
|
|
6787
|
-
);
|
|
6788
|
-
}
|
|
6789
|
-
}
|
|
6790
|
-
};
|
|
6791
|
-
onboardingManager = new OnboardingManager();
|
|
6710
|
+
return error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
|
|
6711
|
+
}
|
|
6712
|
+
var CANONICAL_FAMILIES, DEPRECATED_EVENT_NAMES;
|
|
6713
|
+
var init_eventFamilies = __esm({
|
|
6714
|
+
"src/core/events/eventFamilies.ts"() {
|
|
6715
|
+
"use strict";
|
|
6716
|
+
init_schemas();
|
|
6717
|
+
CANONICAL_FAMILIES = [
|
|
6718
|
+
"lifecycle",
|
|
6719
|
+
"identify",
|
|
6720
|
+
"paywall",
|
|
6721
|
+
"transaction",
|
|
6722
|
+
"onboarding",
|
|
6723
|
+
"notification",
|
|
6724
|
+
"custom"
|
|
6725
|
+
];
|
|
6726
|
+
DEPRECATED_EVENT_NAMES = [
|
|
6727
|
+
"$paywall_purchased",
|
|
6728
|
+
"$paywall_product_selected",
|
|
6729
|
+
"$core_action",
|
|
6730
|
+
"$campaign_impression",
|
|
6731
|
+
"$app_open",
|
|
6732
|
+
"$app_background",
|
|
6733
|
+
"$app_foreground",
|
|
6734
|
+
"session.end"
|
|
6735
|
+
];
|
|
6792
6736
|
}
|
|
6793
6737
|
});
|
|
6794
6738
|
|
|
6795
|
-
// src/
|
|
6796
|
-
|
|
6797
|
-
|
|
6739
|
+
// src/core/PaywalloAnalytics.ts
|
|
6740
|
+
function isValidEventName(name) {
|
|
6741
|
+
if (name.startsWith("$")) return /^\$[a-z][a-z0-9]*(_[a-z0-9]+)*$/.test(name);
|
|
6742
|
+
return /^[a-z][a-z0-9]*(_[a-z0-9]+)*$/.test(name);
|
|
6743
|
+
}
|
|
6744
|
+
async function trackEvent(apiClient, state, eventName, options) {
|
|
6745
|
+
const distinctId = identityManager.getDistinctId();
|
|
6746
|
+
if (!distinctId) {
|
|
6747
|
+
console.error("[Paywallo:DROP] event dropped \u2014 no distinctId available yet", { eventName });
|
|
6748
|
+
return;
|
|
6749
|
+
}
|
|
6750
|
+
if (!isValidEventName(eventName)) {
|
|
6751
|
+
const msg = `Invalid event name: "${eventName}". Use snake_case or $reserved_name.`;
|
|
6752
|
+
throw new ClientError(CLIENT_ERROR_CODES.INVALID_EVENT_NAME, msg);
|
|
6753
|
+
}
|
|
6754
|
+
if (state.config?.debug && eventName.startsWith("$purchase")) console.log(`[Paywallo TRACK] ${eventName}`, options?.properties ?? {});
|
|
6755
|
+
const sessionId = sessionManager.getSessionId();
|
|
6756
|
+
const family = detectFamily(eventName);
|
|
6757
|
+
const isCustomFamily = family === "custom";
|
|
6758
|
+
const identityProps = isCustomFamily ? identityManager.getProperties() : {};
|
|
6759
|
+
const sessionProp = isCustomFamily && sessionId ? { sessionId } : {};
|
|
6760
|
+
await apiClient.trackEvent(
|
|
6761
|
+
eventName,
|
|
6762
|
+
distinctId,
|
|
6763
|
+
{ ...identityProps, ...options?.properties, ...sessionProp },
|
|
6764
|
+
options?.timestamp,
|
|
6765
|
+
options?.priority ? { priority: options.priority } : void 0
|
|
6766
|
+
);
|
|
6767
|
+
}
|
|
6768
|
+
function validateCoreAction(actionName) {
|
|
6769
|
+
if (!actionName || typeof actionName !== "string") {
|
|
6770
|
+
const msg = `Invalid actionName: "${actionName}". Must be a non-empty string.`;
|
|
6771
|
+
throw new AnalyticsError(ANALYTICS_ERROR_CODES.INVALID_ACTION_NAME, msg);
|
|
6772
|
+
}
|
|
6773
|
+
}
|
|
6774
|
+
var init_PaywalloAnalytics = __esm({
|
|
6775
|
+
"src/core/PaywalloAnalytics.ts"() {
|
|
6798
6776
|
"use strict";
|
|
6799
|
-
|
|
6800
|
-
|
|
6777
|
+
init_analytics();
|
|
6778
|
+
init_identity();
|
|
6779
|
+
init_session();
|
|
6780
|
+
init_ClientError();
|
|
6781
|
+
init_eventFamilies();
|
|
6782
|
+
}
|
|
6783
|
+
});
|
|
6784
|
+
|
|
6785
|
+
// src/core/PaywalloFlags.ts
|
|
6786
|
+
async function getVariantCached(apiClient, flagKey, defaultValue) {
|
|
6787
|
+
const distinctId = identityManager.getDistinctId();
|
|
6788
|
+
const result = await apiClient.getVariantFromCache(flagKey, distinctId);
|
|
6789
|
+
if (result) return result;
|
|
6790
|
+
void apiClient.getVariant(flagKey, distinctId).catch(() => {
|
|
6791
|
+
});
|
|
6792
|
+
return { variant: defaultValue ?? null, payload: void 0 };
|
|
6793
|
+
}
|
|
6794
|
+
async function getVariant(apiClient, state, flagKey) {
|
|
6795
|
+
try {
|
|
6796
|
+
const distinctId = identityManager.getDistinctId();
|
|
6797
|
+
if (!distinctId) {
|
|
6798
|
+
if (state.config?.debug) console.log(`[Paywallo FLAG] ${flagKey} \u2192 null (no distinctId)`);
|
|
6799
|
+
return { variant: null };
|
|
6800
|
+
}
|
|
6801
|
+
const result = await apiClient.getVariant(flagKey, distinctId);
|
|
6802
|
+
if (state.config?.debug) console.log(`[Paywallo FLAG] ${flagKey} \u2192`, result.variant);
|
|
6803
|
+
return result;
|
|
6804
|
+
} catch (error) {
|
|
6805
|
+
if (state.config?.debug) console.log(`[Paywallo FLAG] ${flagKey} \u2192 error`, error);
|
|
6806
|
+
if (state.apiClient) void state.apiClient.reportError("GET_VARIANT_FAILED", error instanceof Error ? error.message : String(error));
|
|
6807
|
+
return { variant: null };
|
|
6808
|
+
}
|
|
6809
|
+
}
|
|
6810
|
+
function evaluateFlags(apiClient, keys) {
|
|
6811
|
+
return apiClient.evaluateFlags(keys, identityManager.getDistinctId());
|
|
6812
|
+
}
|
|
6813
|
+
async function getConditionalFlag(apiClient, state, flagKey, context) {
|
|
6814
|
+
try {
|
|
6815
|
+
const distinctId = identityManager.getDistinctId();
|
|
6816
|
+
if (!distinctId) return false;
|
|
6817
|
+
const result = await apiClient.getConditionalFlag(flagKey, { distinctId, ...context });
|
|
6818
|
+
return result.value;
|
|
6819
|
+
} catch (error) {
|
|
6820
|
+
if (state.apiClient) void state.apiClient.reportError("GET_CONDITIONAL_FLAG_FAILED", error instanceof Error ? error.message : String(error));
|
|
6821
|
+
return false;
|
|
6822
|
+
}
|
|
6823
|
+
}
|
|
6824
|
+
var init_PaywalloFlags = __esm({
|
|
6825
|
+
"src/core/PaywalloFlags.ts"() {
|
|
6826
|
+
"use strict";
|
|
6827
|
+
init_identity();
|
|
6801
6828
|
}
|
|
6802
6829
|
});
|
|
6803
6830
|
|
|
@@ -7132,6 +7159,36 @@ var init_SubscriptionManager = __esm({
|
|
|
7132
7159
|
}
|
|
7133
7160
|
});
|
|
7134
7161
|
|
|
7162
|
+
// src/utils/detectEnvironment.ts
|
|
7163
|
+
async function detectEnvironment() {
|
|
7164
|
+
if (cached !== null) return cached;
|
|
7165
|
+
if (typeof globalThis.__DEV__ !== "undefined" && globalThis.__DEV__) {
|
|
7166
|
+
cached = "sandbox";
|
|
7167
|
+
return cached;
|
|
7168
|
+
}
|
|
7169
|
+
if (!nativeModule) {
|
|
7170
|
+
cached = "production";
|
|
7171
|
+
return cached;
|
|
7172
|
+
}
|
|
7173
|
+
try {
|
|
7174
|
+
const result = await nativeModule.detect();
|
|
7175
|
+
cached = result === "sandbox" ? "sandbox" : "production";
|
|
7176
|
+
return cached;
|
|
7177
|
+
} catch {
|
|
7178
|
+
cached = "production";
|
|
7179
|
+
return cached;
|
|
7180
|
+
}
|
|
7181
|
+
}
|
|
7182
|
+
var import_react_native23, nativeModule, cached;
|
|
7183
|
+
var init_detectEnvironment = __esm({
|
|
7184
|
+
"src/utils/detectEnvironment.ts"() {
|
|
7185
|
+
"use strict";
|
|
7186
|
+
import_react_native23 = require("react-native");
|
|
7187
|
+
nativeModule = import_react_native23.NativeModules.PaywalloEnv ?? null;
|
|
7188
|
+
cached = null;
|
|
7189
|
+
}
|
|
7190
|
+
});
|
|
7191
|
+
|
|
7135
7192
|
// src/domains/iap/apiPurchaseMethods.ts
|
|
7136
7193
|
async function validatePurchase(client, appKey, platform, receipt, productId, transactionId, priceLocal, currency, country, distinctId, paywallPlacement, variantKey) {
|
|
7137
7194
|
const response = await client.post(
|
|
@@ -7471,21 +7528,42 @@ function buildV2Envelope(events, providerContext) {
|
|
|
7471
7528
|
if (family === "custom") {
|
|
7472
7529
|
payload.event_name = event.eventName;
|
|
7473
7530
|
}
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
7478
|
-
payload
|
|
7479
|
-
}
|
|
7531
|
+
const candidateId = payload.installEventId;
|
|
7532
|
+
let id;
|
|
7533
|
+
if (typeof candidateId === "string" && isValidUUIDv4(candidateId)) {
|
|
7534
|
+
id = candidateId;
|
|
7535
|
+
delete payload.installEventId;
|
|
7536
|
+
} else {
|
|
7537
|
+
id = generateUUID();
|
|
7538
|
+
}
|
|
7539
|
+
return { id, ts: event.timestamp, name: family, payload };
|
|
7480
7540
|
});
|
|
7481
7541
|
if (context.sdk_version === void 0) context.sdk_version = SDK_VERSION;
|
|
7482
|
-
if (context.platform === void 0) context.platform =
|
|
7542
|
+
if (context.platform === void 0) context.platform = import_react_native24.Platform.OS;
|
|
7483
7543
|
if (context.distinct_id === void 0 && events.length > 0) {
|
|
7484
7544
|
context.distinct_id = events[0].distinctId;
|
|
7485
7545
|
}
|
|
7486
7546
|
if (!context.distinct_id || context.distinct_id.length === 0) {
|
|
7487
7547
|
console.error("[Paywallo:ENVELOPE] distinct_id is empty after resolution \u2014 envelope will fail schema", { providerContext, eventCount: events.length, firstEventName: events[0]?.eventName });
|
|
7488
7548
|
}
|
|
7549
|
+
const idsFromProps = {};
|
|
7550
|
+
const existingIds = context.ids;
|
|
7551
|
+
for (const event of events) {
|
|
7552
|
+
for (const { key, aliases } of IDS_PROMO_ALIASES) {
|
|
7553
|
+
if (existingIds?.[key] !== void 0) continue;
|
|
7554
|
+
if (idsFromProps[key] !== void 0) continue;
|
|
7555
|
+
for (const alias of aliases) {
|
|
7556
|
+
const val = event.properties[alias];
|
|
7557
|
+
if (typeof val === "string" && val.length > 0) {
|
|
7558
|
+
idsFromProps[key] = val;
|
|
7559
|
+
break;
|
|
7560
|
+
}
|
|
7561
|
+
}
|
|
7562
|
+
}
|
|
7563
|
+
}
|
|
7564
|
+
if (Object.keys(idsFromProps).length > 0) {
|
|
7565
|
+
context.ids = { ...idsFromProps, ...context.ids };
|
|
7566
|
+
}
|
|
7489
7567
|
return { context, events: v2Events };
|
|
7490
7568
|
}
|
|
7491
7569
|
function resolveProviderContext(provider) {
|
|
@@ -7519,11 +7597,11 @@ function splitPayloadAndContext(properties) {
|
|
|
7519
7597
|
}
|
|
7520
7598
|
return { payload, promoted };
|
|
7521
7599
|
}
|
|
7522
|
-
var
|
|
7600
|
+
var import_react_native24, import_zod2, v2EventSchema, v2ContextSchema, v2EnvelopeSchema, IDS_PROMO_ALIASES, CONTEXT_KEY_ALIASES;
|
|
7523
7601
|
var init_eventEnvelopeV2 = __esm({
|
|
7524
7602
|
"src/core/eventEnvelopeV2.ts"() {
|
|
7525
7603
|
"use strict";
|
|
7526
|
-
|
|
7604
|
+
import_react_native24 = require("react-native");
|
|
7527
7605
|
import_zod2 = require("zod");
|
|
7528
7606
|
init_uuid();
|
|
7529
7607
|
init_eventFamilies();
|
|
@@ -7547,6 +7625,7 @@ var init_eventEnvelopeV2 = __esm({
|
|
|
7547
7625
|
device_model: import_zod2.z.string().optional(),
|
|
7548
7626
|
timezone: import_zod2.z.string().optional(),
|
|
7549
7627
|
locale: import_zod2.z.string().optional(),
|
|
7628
|
+
country: import_zod2.z.string().optional(),
|
|
7550
7629
|
carrier: import_zod2.z.string().optional(),
|
|
7551
7630
|
screen_width: import_zod2.z.number().optional(),
|
|
7552
7631
|
screen_height: import_zod2.z.number().optional(),
|
|
@@ -7558,6 +7637,12 @@ var init_eventEnvelopeV2 = __esm({
|
|
|
7558
7637
|
context: v2ContextSchema,
|
|
7559
7638
|
events: import_zod2.z.array(v2EventSchema).min(1)
|
|
7560
7639
|
});
|
|
7640
|
+
IDS_PROMO_ALIASES = [
|
|
7641
|
+
{ key: "idfv", aliases: ["idfv"] },
|
|
7642
|
+
{ key: "gaid", aliases: ["gaid"] },
|
|
7643
|
+
{ key: "fb_anon_id", aliases: ["fbAnonId", "fb_anon_id"] },
|
|
7644
|
+
{ key: "idfa", aliases: ["idfa"] }
|
|
7645
|
+
];
|
|
7561
7646
|
CONTEXT_KEY_ALIASES = {
|
|
7562
7647
|
distinct_id: ["distinct_id", "distinctId"],
|
|
7563
7648
|
session_id: ["session_id", "sessionId"],
|
|
@@ -7574,6 +7659,7 @@ var init_eventEnvelopeV2 = __esm({
|
|
|
7574
7659
|
device_model: ["device_model", "deviceModel", "model"],
|
|
7575
7660
|
timezone: ["timezone", "timeZone"],
|
|
7576
7661
|
locale: ["locale"],
|
|
7662
|
+
country: ["country", "regionCode"],
|
|
7577
7663
|
carrier: ["carrier_ctx"],
|
|
7578
7664
|
screen_width: ["screen_width"],
|
|
7579
7665
|
screen_height: ["screen_height"],
|
|
@@ -7593,11 +7679,11 @@ var init_events = __esm({
|
|
|
7593
7679
|
});
|
|
7594
7680
|
|
|
7595
7681
|
// src/core/EventBatcher.ts
|
|
7596
|
-
var
|
|
7682
|
+
var import_react_native25, BATCH_MAX_SIZE, BATCH_FLUSH_MS, EVENT_NAME_REGEX, V2_BATCH_ENDPOINT, EventBatcher;
|
|
7597
7683
|
var init_EventBatcher = __esm({
|
|
7598
7684
|
"src/core/EventBatcher.ts"() {
|
|
7599
7685
|
"use strict";
|
|
7600
|
-
|
|
7686
|
+
import_react_native25 = require("react-native");
|
|
7601
7687
|
init_eventEnvelopeV2();
|
|
7602
7688
|
init_events();
|
|
7603
7689
|
BATCH_MAX_SIZE = 25;
|
|
@@ -7646,7 +7732,7 @@ var init_EventBatcher = __esm({
|
|
|
7646
7732
|
const event = {
|
|
7647
7733
|
eventName,
|
|
7648
7734
|
distinctId,
|
|
7649
|
-
properties: { ...properties, platform:
|
|
7735
|
+
properties: { ...properties, platform: import_react_native25.Platform.OS },
|
|
7650
7736
|
timestamp: timestamp ?? Date.now(),
|
|
7651
7737
|
environment: environment.toLowerCase()
|
|
7652
7738
|
};
|
|
@@ -7813,8 +7899,8 @@ var init_HttpClient = __esm({
|
|
|
7813
7899
|
if (this.shouldRetry(response.status, retryConfig)) {
|
|
7814
7900
|
if (state.attempt < retryConfig.maxRetries) {
|
|
7815
7901
|
state.attempt++;
|
|
7816
|
-
const
|
|
7817
|
-
await this.sleep(
|
|
7902
|
+
const delay2 = this.calculateDelayForResponse(state.attempt, response.headers, retryConfig);
|
|
7903
|
+
await this.sleep(delay2);
|
|
7818
7904
|
continue;
|
|
7819
7905
|
}
|
|
7820
7906
|
}
|
|
@@ -7823,8 +7909,8 @@ var init_HttpClient = __esm({
|
|
|
7823
7909
|
state.lastError = error instanceof Error ? error : new ClientError(CLIENT_ERROR_CODES.UNKNOWN, String(error));
|
|
7824
7910
|
if (this.isNetworkError(state.lastError) && state.attempt < retryConfig.maxRetries) {
|
|
7825
7911
|
state.attempt++;
|
|
7826
|
-
const
|
|
7827
|
-
await this.sleep(
|
|
7912
|
+
const delay2 = this.calculateDelay(state.attempt, retryConfig);
|
|
7913
|
+
await this.sleep(delay2);
|
|
7828
7914
|
continue;
|
|
7829
7915
|
}
|
|
7830
7916
|
throw state.lastError;
|
|
@@ -8001,11 +8087,11 @@ function normalizeGender(raw) {
|
|
|
8001
8087
|
if (lower === "f" || lower === "female") return "f";
|
|
8002
8088
|
return void 0;
|
|
8003
8089
|
}
|
|
8004
|
-
var
|
|
8090
|
+
var import_react_native26, DATE_OF_BIRTH_RE, ApiClient;
|
|
8005
8091
|
var init_ApiClient = __esm({
|
|
8006
8092
|
"src/core/ApiClient.ts"() {
|
|
8007
8093
|
"use strict";
|
|
8008
|
-
|
|
8094
|
+
import_react_native26 = require("react-native");
|
|
8009
8095
|
init_apiPurchaseMethods();
|
|
8010
8096
|
init_ApiCache();
|
|
8011
8097
|
init_ApiClientFlags();
|
|
@@ -8108,7 +8194,7 @@ var init_ApiClient = __esm({
|
|
|
8108
8194
|
buildSdkHeaders() {
|
|
8109
8195
|
return {
|
|
8110
8196
|
"x-sdk-version": SDK_VERSION,
|
|
8111
|
-
"x-sdk-platform":
|
|
8197
|
+
"x-sdk-platform": import_react_native26.Platform.OS,
|
|
8112
8198
|
"x-sdk-environment": this.environment
|
|
8113
8199
|
};
|
|
8114
8200
|
}
|
|
@@ -8138,7 +8224,7 @@ var init_ApiClient = __esm({
|
|
|
8138
8224
|
message,
|
|
8139
8225
|
context,
|
|
8140
8226
|
sdkVersion: SDK_VERSION,
|
|
8141
|
-
platform:
|
|
8227
|
+
platform: import_react_native26.Platform.OS
|
|
8142
8228
|
}, true);
|
|
8143
8229
|
} catch (err) {
|
|
8144
8230
|
if (this.debug) console.log("[Paywallo:ApiClient] reportError failed", err);
|
|
@@ -8156,7 +8242,7 @@ var init_ApiClient = __esm({
|
|
|
8156
8242
|
async identify(distinctId, properties, email, deviceId, pii) {
|
|
8157
8243
|
const rawTraits = {
|
|
8158
8244
|
email: email || void 0,
|
|
8159
|
-
platform:
|
|
8245
|
+
platform: import_react_native26.Platform.OS,
|
|
8160
8246
|
name: properties?.name,
|
|
8161
8247
|
country: properties?.country,
|
|
8162
8248
|
locale: properties?.locale,
|
|
@@ -8352,7 +8438,7 @@ function setupNotifications(apiClient, config, environment, eventPipeline) {
|
|
|
8352
8438
|
notificationsManager.setupHandlers({ eventBatcher: eventPipeline });
|
|
8353
8439
|
notificationsManager.initialize({
|
|
8354
8440
|
appKey: config.appKey,
|
|
8355
|
-
apiBaseUrl:
|
|
8441
|
+
apiBaseUrl: DEFAULT_API_URL,
|
|
8356
8442
|
debug: config.debug,
|
|
8357
8443
|
environment: environment === "Sandbox" ? "sandbox" : "production"
|
|
8358
8444
|
}).catch((err) => {
|
|
@@ -8431,36 +8517,6 @@ var init_PaywalloState = __esm({
|
|
|
8431
8517
|
}
|
|
8432
8518
|
});
|
|
8433
8519
|
|
|
8434
|
-
// src/utils/detectEnvironment.ts
|
|
8435
|
-
async function detectEnvironment() {
|
|
8436
|
-
if (cached !== null) return cached;
|
|
8437
|
-
if (typeof globalThis.__DEV__ !== "undefined" && globalThis.__DEV__) {
|
|
8438
|
-
cached = "sandbox";
|
|
8439
|
-
return cached;
|
|
8440
|
-
}
|
|
8441
|
-
if (!nativeModule) {
|
|
8442
|
-
cached = "production";
|
|
8443
|
-
return cached;
|
|
8444
|
-
}
|
|
8445
|
-
try {
|
|
8446
|
-
const result = await nativeModule.detect();
|
|
8447
|
-
cached = result === "sandbox" ? "sandbox" : "production";
|
|
8448
|
-
return cached;
|
|
8449
|
-
} catch {
|
|
8450
|
-
cached = "production";
|
|
8451
|
-
return cached;
|
|
8452
|
-
}
|
|
8453
|
-
}
|
|
8454
|
-
var import_react_native26, nativeModule, cached;
|
|
8455
|
-
var init_detectEnvironment = __esm({
|
|
8456
|
-
"src/utils/detectEnvironment.ts"() {
|
|
8457
|
-
"use strict";
|
|
8458
|
-
import_react_native26 = require("react-native");
|
|
8459
|
-
nativeModule = import_react_native26.NativeModules.PaywalloEnv ?? null;
|
|
8460
|
-
cached = null;
|
|
8461
|
-
}
|
|
8462
|
-
});
|
|
8463
|
-
|
|
8464
8520
|
// src/core/PaywalloInitializer.ts
|
|
8465
8521
|
async function initWithRetry(state, config) {
|
|
8466
8522
|
while (state.initAttempts <= MAX_INIT_RETRIES) {
|
|
@@ -8475,8 +8531,8 @@ async function initWithRetry(state, config) {
|
|
|
8475
8531
|
} catch (error) {
|
|
8476
8532
|
state.initAttempts++;
|
|
8477
8533
|
if (state.initAttempts > MAX_INIT_RETRIES) throw error;
|
|
8478
|
-
const
|
|
8479
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
8534
|
+
const delay2 = RETRY_DELAYS[state.initAttempts - 1] ?? 1e4;
|
|
8535
|
+
await new Promise((resolve) => setTimeout(resolve, delay2));
|
|
8480
8536
|
}
|
|
8481
8537
|
}
|
|
8482
8538
|
}
|
|
@@ -8506,7 +8562,7 @@ function reportInitFailure(state, config, error, reason) {
|
|
|
8506
8562
|
try {
|
|
8507
8563
|
const tempClient = new ApiClient(
|
|
8508
8564
|
config.appKey,
|
|
8509
|
-
|
|
8565
|
+
DEFAULT_API_URL,
|
|
8510
8566
|
config.debug
|
|
8511
8567
|
);
|
|
8512
8568
|
void tempClient.reportError("INIT_FAILED", reason, {
|
|
@@ -8556,7 +8612,7 @@ async function doInit(state, config) {
|
|
|
8556
8612
|
]);
|
|
8557
8613
|
const apiClient = new ApiClient(
|
|
8558
8614
|
config.appKey,
|
|
8559
|
-
|
|
8615
|
+
DEFAULT_API_URL,
|
|
8560
8616
|
config.debug,
|
|
8561
8617
|
environment,
|
|
8562
8618
|
offlineQueueEnabled,
|
|
@@ -8630,8 +8686,8 @@ async function doInit(state, config) {
|
|
|
8630
8686
|
let timezone;
|
|
8631
8687
|
try {
|
|
8632
8688
|
const opts = Intl.DateTimeFormat().resolvedOptions();
|
|
8633
|
-
locale = opts.locale || void 0;
|
|
8634
|
-
timezone = opts.timeZone || void 0;
|
|
8689
|
+
locale = device?.languageTag || opts.locale || void 0;
|
|
8690
|
+
timezone = device?.timeZone || opts.timeZone || void 0;
|
|
8635
8691
|
} catch {
|
|
8636
8692
|
}
|
|
8637
8693
|
let screenWidth;
|
|
@@ -8655,7 +8711,7 @@ async function doInit(state, config) {
|
|
|
8655
8711
|
device_model: device?.modelId || device?.model || void 0,
|
|
8656
8712
|
timezone,
|
|
8657
8713
|
locale,
|
|
8658
|
-
|
|
8714
|
+
...device?.regionCode && { country: device.regionCode },
|
|
8659
8715
|
screen_width: screenWidth,
|
|
8660
8716
|
screen_height: screenHeight,
|
|
8661
8717
|
screen_density: screenDensity,
|
|
@@ -8723,6 +8779,7 @@ var init_PaywalloInitializer = __esm({
|
|
|
8723
8779
|
init_plan();
|
|
8724
8780
|
init_session();
|
|
8725
8781
|
init_SubscriptionManager();
|
|
8782
|
+
init_detectEnvironment();
|
|
8726
8783
|
init_NativeDeviceInfo();
|
|
8727
8784
|
init_ApiClient();
|
|
8728
8785
|
init_constants();
|
|
@@ -8732,7 +8789,6 @@ var init_PaywalloInitializer = __esm({
|
|
|
8732
8789
|
init_PaywalloState();
|
|
8733
8790
|
init_queue();
|
|
8734
8791
|
init_NativeStorage();
|
|
8735
|
-
init_detectEnvironment();
|
|
8736
8792
|
}
|
|
8737
8793
|
});
|
|
8738
8794
|
|
|
@@ -8853,6 +8909,7 @@ var init_PaywalloClient = __esm({
|
|
|
8853
8909
|
init_plan();
|
|
8854
8910
|
init_session();
|
|
8855
8911
|
init_ClientError();
|
|
8912
|
+
init_onboarding();
|
|
8856
8913
|
init_network();
|
|
8857
8914
|
init_PaywalloAnalytics();
|
|
8858
8915
|
init_PaywalloFlags();
|
|
@@ -8922,8 +8979,9 @@ var init_PaywalloClient = __esm({
|
|
|
8922
8979
|
async track(eventName, options) {
|
|
8923
8980
|
return trackEvent(this.getApiClientOrThrow(), this.state, eventName, options);
|
|
8924
8981
|
}
|
|
8925
|
-
async onboardingStep(
|
|
8926
|
-
|
|
8982
|
+
async onboardingStep(order, name) {
|
|
8983
|
+
this.ensureInitialized();
|
|
8984
|
+
return onboardingManager.step(name, order);
|
|
8927
8985
|
}
|
|
8928
8986
|
async coreAction(actionName) {
|
|
8929
8987
|
this.ensureInitialized();
|
|
@@ -9010,6 +9068,9 @@ var init_PaywalloClient = __esm({
|
|
|
9010
9068
|
getConfig() {
|
|
9011
9069
|
return this.state.config;
|
|
9012
9070
|
}
|
|
9071
|
+
getId() {
|
|
9072
|
+
return identityManager.getDistinctId();
|
|
9073
|
+
}
|
|
9013
9074
|
getDistinctId() {
|
|
9014
9075
|
return identityManager.getDistinctId();
|
|
9015
9076
|
}
|
|
@@ -9310,6 +9371,7 @@ __export(index_exports, {
|
|
|
9310
9371
|
createPurchaseError: () => createPurchaseError,
|
|
9311
9372
|
default: () => index_default,
|
|
9312
9373
|
detectDeviceLanguage: () => detectDeviceLanguage,
|
|
9374
|
+
getActiveRouteName: () => getActiveRouteName,
|
|
9313
9375
|
getCurrentLanguage: () => getCurrentLanguage,
|
|
9314
9376
|
getDefaultLanguage: () => getDefaultLanguage,
|
|
9315
9377
|
getIAPService: () => getIAPService,
|
|
@@ -9338,6 +9400,7 @@ __export(index_exports, {
|
|
|
9338
9400
|
useOnboarding: () => useOnboarding,
|
|
9339
9401
|
usePaywallContext: () => usePaywallContext,
|
|
9340
9402
|
usePaywallo: () => usePaywallo,
|
|
9403
|
+
usePaywalloScreenTracking: () => usePaywalloScreenTracking,
|
|
9341
9404
|
usePlanPurchase: () => usePlanPurchase,
|
|
9342
9405
|
usePlans: () => usePlans,
|
|
9343
9406
|
useProducts: () => useProducts,
|
|
@@ -9472,7 +9535,7 @@ var import_react15 = require("react");
|
|
|
9472
9535
|
init_onboarding();
|
|
9473
9536
|
function useOnboarding() {
|
|
9474
9537
|
const step = (0, import_react15.useCallback)(
|
|
9475
|
-
(stepName) => onboardingManager.step(stepName),
|
|
9538
|
+
(stepName, order) => onboardingManager.step(stepName, order),
|
|
9476
9539
|
[]
|
|
9477
9540
|
);
|
|
9478
9541
|
const complete = (0, import_react15.useCallback)(
|
|
@@ -9816,8 +9879,41 @@ function useSubscription() {
|
|
|
9816
9879
|
};
|
|
9817
9880
|
}
|
|
9818
9881
|
|
|
9882
|
+
// src/hooks/usePaywalloScreenTracking.ts
|
|
9883
|
+
var import_react22 = require("react");
|
|
9884
|
+
init_PaywalloClient();
|
|
9885
|
+
function isNavState(value) {
|
|
9886
|
+
return typeof value === "object" && value !== null && "routes" in value && "index" in value && Array.isArray(value.routes) && typeof value.index === "number";
|
|
9887
|
+
}
|
|
9888
|
+
function getActiveRouteName(state) {
|
|
9889
|
+
try {
|
|
9890
|
+
if (!isNavState(state)) return null;
|
|
9891
|
+
const route = state.routes[state.index];
|
|
9892
|
+
if (!route) return null;
|
|
9893
|
+
if (isNavState(route.state)) {
|
|
9894
|
+
return getActiveRouteName(route.state);
|
|
9895
|
+
}
|
|
9896
|
+
return typeof route.name === "string" ? route.name : null;
|
|
9897
|
+
} catch {
|
|
9898
|
+
return null;
|
|
9899
|
+
}
|
|
9900
|
+
}
|
|
9901
|
+
function usePaywalloScreenTracking() {
|
|
9902
|
+
const lastScreenRef = (0, import_react22.useRef)(null);
|
|
9903
|
+
return (0, import_react22.useCallback)((state) => {
|
|
9904
|
+
if (!PaywalloClient.isReady()) return;
|
|
9905
|
+
const screenName = getActiveRouteName(state);
|
|
9906
|
+
if (!screenName || screenName === lastScreenRef.current) return;
|
|
9907
|
+
lastScreenRef.current = screenName;
|
|
9908
|
+
void PaywalloClient.track("screen_view", {
|
|
9909
|
+
properties: { screen_name: screenName }
|
|
9910
|
+
}).catch(() => {
|
|
9911
|
+
});
|
|
9912
|
+
}, []);
|
|
9913
|
+
}
|
|
9914
|
+
|
|
9819
9915
|
// src/PaywalloProvider.tsx
|
|
9820
|
-
var
|
|
9916
|
+
var import_react28 = require("react");
|
|
9821
9917
|
var import_react_native29 = require("react-native");
|
|
9822
9918
|
init_PaywalloClient();
|
|
9823
9919
|
init_NativeStorage();
|
|
@@ -9832,12 +9928,17 @@ init_paywall();
|
|
|
9832
9928
|
|
|
9833
9929
|
// src/domains/paywall/SuperwallAttributeBridge.ts
|
|
9834
9930
|
init_identity();
|
|
9931
|
+
var CONFIG_POLL_INTERVAL_MS = 250;
|
|
9932
|
+
var CONFIG_POLL_MAX_ATTEMPTS = 40;
|
|
9835
9933
|
var lastSignature = null;
|
|
9836
9934
|
function log(debug, msg, data) {
|
|
9837
9935
|
if (!debug) return;
|
|
9838
9936
|
if (data !== void 0) console.log(`[Paywallo:SuperwallAttr] ${msg}`, data);
|
|
9839
9937
|
else console.log(`[Paywallo:SuperwallAttr] ${msg}`);
|
|
9840
9938
|
}
|
|
9939
|
+
function delay(ms) {
|
|
9940
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
9941
|
+
}
|
|
9841
9942
|
var PAID_MEDIUM = /cpc|ppc|cpm|cpa|paid|display/i;
|
|
9842
9943
|
function deriveIsPaid(a) {
|
|
9843
9944
|
if (a.fbclid || a.gclid || a.ttclid || a.tiktokCampaignId) return true;
|
|
@@ -9876,24 +9977,38 @@ function buildSuperwallAttributes(a) {
|
|
|
9876
9977
|
out.pw_attributed_at = a.capturedAt;
|
|
9877
9978
|
return out;
|
|
9878
9979
|
}
|
|
9879
|
-
function
|
|
9880
|
-
const
|
|
9881
|
-
return
|
|
9980
|
+
function pickNative(mod) {
|
|
9981
|
+
const native = mod?.SuperwallExpoModule ?? mod?.default?.SuperwallExpoModule;
|
|
9982
|
+
return native && typeof native.setUserAttributes === "function" && typeof native.getConfigurationStatus === "function" ? native : null;
|
|
9882
9983
|
}
|
|
9883
|
-
async function
|
|
9884
|
-
try {
|
|
9885
|
-
const shared = pickShared(await import("expo-superwall/compat"));
|
|
9886
|
-
if (shared) return shared;
|
|
9887
|
-
} catch {
|
|
9888
|
-
}
|
|
9984
|
+
async function resolveSuperwallNativeModule(debug) {
|
|
9889
9985
|
try {
|
|
9890
|
-
const
|
|
9891
|
-
if (
|
|
9986
|
+
const native = pickNative(await import("expo-superwall"));
|
|
9987
|
+
if (native) return native;
|
|
9892
9988
|
} catch {
|
|
9893
9989
|
}
|
|
9894
|
-
log(debug, "expo-superwall not available \u2014 attribute push skipped");
|
|
9990
|
+
log(debug, "expo-superwall native module not available \u2014 attribute push skipped");
|
|
9895
9991
|
return null;
|
|
9896
9992
|
}
|
|
9993
|
+
async function waitForConfigured(mod, debug) {
|
|
9994
|
+
for (let attempt = 0; attempt < CONFIG_POLL_MAX_ATTEMPTS; attempt++) {
|
|
9995
|
+
let status;
|
|
9996
|
+
try {
|
|
9997
|
+
status = String(await mod.getConfigurationStatus());
|
|
9998
|
+
} catch (err) {
|
|
9999
|
+
log(debug, "getConfigurationStatus error", { err: String(err) });
|
|
10000
|
+
return false;
|
|
10001
|
+
}
|
|
10002
|
+
if (status === "CONFIGURED") return true;
|
|
10003
|
+
if (status === "FAILED") {
|
|
10004
|
+
log(debug, "Superwall configuration FAILED \u2014 skipping attribute push");
|
|
10005
|
+
return false;
|
|
10006
|
+
}
|
|
10007
|
+
await delay(CONFIG_POLL_INTERVAL_MS);
|
|
10008
|
+
}
|
|
10009
|
+
log(debug, "Superwall not configured within timeout \u2014 skipping attribute push");
|
|
10010
|
+
return false;
|
|
10011
|
+
}
|
|
9897
10012
|
async function pushAttributionToSuperwall(debug = false) {
|
|
9898
10013
|
const attrs = buildSuperwallAttributes(attributionTracker.get());
|
|
9899
10014
|
const signature = JSON.stringify(attrs);
|
|
@@ -9901,10 +10016,11 @@ async function pushAttributionToSuperwall(debug = false) {
|
|
|
9901
10016
|
log(debug, "attributes unchanged \u2014 skipping push");
|
|
9902
10017
|
return;
|
|
9903
10018
|
}
|
|
9904
|
-
const
|
|
9905
|
-
if (!
|
|
10019
|
+
const native = await resolveSuperwallNativeModule(debug);
|
|
10020
|
+
if (!native) return;
|
|
10021
|
+
if (!await waitForConfigured(native, debug)) return;
|
|
9906
10022
|
try {
|
|
9907
|
-
await
|
|
10023
|
+
await native.setUserAttributes(attrs);
|
|
9908
10024
|
lastSignature = signature;
|
|
9909
10025
|
log(debug, "attributes pushed to Superwall", attrs);
|
|
9910
10026
|
} catch (err) {
|
|
@@ -10020,50 +10136,9 @@ function buildCallbacks() {
|
|
|
10020
10136
|
const identifier = "paywallIdentifier" in safe && typeof safe.paywallIdentifier === "string" ? safe.paywallIdentifier : productId;
|
|
10021
10137
|
log2("onPurchase", { productId, identifier });
|
|
10022
10138
|
void trackPaywallEvent("purchased", identifier, ts, { product_id: productId });
|
|
10023
|
-
},
|
|
10024
|
-
// Global Superwall event listener — used specifically for `transactionComplete`
|
|
10025
|
-
// which is the only event that exposes the full product (currency/price) +
|
|
10026
|
-
// transaction (storeTransactionId) shape needed to create a server-side
|
|
10027
|
-
// `transactions` row. Other events are skipped (paywallOpen/Dismiss already
|
|
10028
|
-
// handled above with their typed callbacks).
|
|
10029
|
-
onSuperwallEvent: (info) => {
|
|
10030
|
-
const ev = info?.event;
|
|
10031
|
-
if (!ev || ev.event !== "transactionComplete") return;
|
|
10032
|
-
const ts = Date.now();
|
|
10033
|
-
const product = ev.product;
|
|
10034
|
-
const transaction = ev.transaction;
|
|
10035
|
-
if (!product) {
|
|
10036
|
-
log2("transactionComplete missing product \u2014 skipping transaction track");
|
|
10037
|
-
return;
|
|
10038
|
-
}
|
|
10039
|
-
const productId = product.productIdentifier ?? product.id ?? "unknown";
|
|
10040
|
-
const currency = product.currencyCode ?? "USD";
|
|
10041
|
-
const price = product.price ?? 0;
|
|
10042
|
-
const transactionId = transaction?.storeTransactionId ?? transaction?.originalTransactionIdentifier ?? `${productId}_${String(ts)}`;
|
|
10043
|
-
const isTrial = product.hasFreeTrial === true || (product.trialPeriodPrice ?? 0) > 0;
|
|
10044
|
-
const paywallIdentifier = ev.paywallInfo?.identifier;
|
|
10045
|
-
log2("onSuperwallEvent: transactionComplete", { productId, currency, price, transactionId, isTrial });
|
|
10046
|
-
void (async () => {
|
|
10047
|
-
try {
|
|
10048
|
-
await PaywalloClient.track("transaction", {
|
|
10049
|
-
priority: "critical",
|
|
10050
|
-
properties: {
|
|
10051
|
-
type: isTrial ? "trial_started" : "completed",
|
|
10052
|
-
// Server's IngestTranslator looks for `transaction_id`/`product_id`
|
|
10053
|
-
// /`amount`/`currency`/`paywall_id` keys (not `tx_id`). PurchaseHandler
|
|
10054
|
-
// then resolves type=trial/new/renewal based on amount being 0 vs >0.
|
|
10055
|
-
transaction_id: transactionId,
|
|
10056
|
-
product_id: productId,
|
|
10057
|
-
amount: price,
|
|
10058
|
-
currency,
|
|
10059
|
-
...paywallIdentifier ? { paywall_id: paywallIdentifier } : {}
|
|
10060
|
-
}
|
|
10061
|
-
});
|
|
10062
|
-
} catch (err) {
|
|
10063
|
-
log2("transactionComplete track error", { err: String(err) });
|
|
10064
|
-
}
|
|
10065
|
-
})();
|
|
10066
10139
|
}
|
|
10140
|
+
// onSuperwallEvent removed — transaction sale events are sourced exclusively
|
|
10141
|
+
// from server-side webhooks (Apple/Google/Superwall/RevenueCat).
|
|
10067
10142
|
};
|
|
10068
10143
|
}
|
|
10069
10144
|
function startSuperwallAutoBridge(debug = false) {
|
|
@@ -10107,7 +10182,7 @@ init_session();
|
|
|
10107
10182
|
init_ClientError();
|
|
10108
10183
|
|
|
10109
10184
|
// src/hooks/useAppAutoEvents.ts
|
|
10110
|
-
var
|
|
10185
|
+
var import_react23 = require("react");
|
|
10111
10186
|
|
|
10112
10187
|
// src/utils/platformDetection.ts
|
|
10113
10188
|
async function detectPlatform3() {
|
|
@@ -10133,8 +10208,8 @@ var FIRST_SEEN_KEY = "@paywallo:firstSeen_v226";
|
|
|
10133
10208
|
var DEBOUNCE_MS = 1e3;
|
|
10134
10209
|
function useAppAutoEvents(config) {
|
|
10135
10210
|
const { isInitialized, getAppVersion: getAppVersion2, getSessionId, emitLifecycle, storageGet, storageSet, debug } = config;
|
|
10136
|
-
const didRunRef = (0,
|
|
10137
|
-
(0,
|
|
10211
|
+
const didRunRef = (0, import_react23.useRef)(false);
|
|
10212
|
+
(0, import_react23.useEffect)(() => {
|
|
10138
10213
|
if (debug) console.log("[Paywallo EVENTS] useEffect fired", { isInitialized, didRunRefCurrent: didRunRef.current });
|
|
10139
10214
|
if (!isInitialized || didRunRef.current) return;
|
|
10140
10215
|
const timer = setTimeout(() => {
|
|
@@ -10144,13 +10219,13 @@ function useAppAutoEvents(config) {
|
|
|
10144
10219
|
const sessionId = getSessionId();
|
|
10145
10220
|
const appVersion = getAppVersion2();
|
|
10146
10221
|
const deviceType = await detectPlatform3();
|
|
10147
|
-
const
|
|
10148
|
-
if (debug) console.log("[Paywallo EVENTS] computed device context", { deviceType, osVersion, appVersion, sessionId, hasSessionId: !!sessionId });
|
|
10222
|
+
const osVersion2 = await detectOsVersion();
|
|
10223
|
+
if (debug) console.log("[Paywallo EVENTS] computed device context", { deviceType, osVersion: osVersion2, appVersion, sessionId, hasSessionId: !!sessionId });
|
|
10149
10224
|
try {
|
|
10150
10225
|
if (debug) console.log("[Paywallo EVENTS] checking install \u2014 about to read FIRST_SEEN_KEY");
|
|
10151
10226
|
const firstSeen = await storageGet(FIRST_SEEN_KEY);
|
|
10152
10227
|
if (!firstSeen) {
|
|
10153
|
-
await emitLifecycle({ type: "install", device_type: deviceType, os_version:
|
|
10228
|
+
await emitLifecycle({ type: "install", device_type: deviceType, os_version: osVersion2, app_version: appVersion, ...sessionId ? { session_id: sessionId } : {} }, "critical");
|
|
10154
10229
|
await storageSet(FIRST_SEEN_KEY, (/* @__PURE__ */ new Date()).toISOString());
|
|
10155
10230
|
if (debug) console.log("[Paywallo EVENTS] install event emitted (first time)");
|
|
10156
10231
|
if (debug) console.log("[Paywallo EVENTS] lifecycle install emitted");
|
|
@@ -10162,7 +10237,7 @@ function useAppAutoEvents(config) {
|
|
|
10162
10237
|
}
|
|
10163
10238
|
try {
|
|
10164
10239
|
if (debug) console.log("[Paywallo EVENTS] emitting cold_start");
|
|
10165
|
-
await emitLifecycle({ type: "cold_start", app_version: appVersion, device_type: deviceType, os_version:
|
|
10240
|
+
await emitLifecycle({ type: "cold_start", app_version: appVersion, device_type: deviceType, os_version: osVersion2, ...sessionId ? { session_id: sessionId } : {} }, "normal");
|
|
10166
10241
|
if (debug) console.log("[Paywallo EVENTS] lifecycle cold_start emitted");
|
|
10167
10242
|
} catch (err) {
|
|
10168
10243
|
if (debug) console.log("[Paywallo EVENTS] lifecycle cold_start failed", err);
|
|
@@ -10174,7 +10249,7 @@ function useAppAutoEvents(config) {
|
|
|
10174
10249
|
}
|
|
10175
10250
|
|
|
10176
10251
|
// src/hooks/useCampaignPreload.ts
|
|
10177
|
-
var
|
|
10252
|
+
var import_react24 = require("react");
|
|
10178
10253
|
init_PaywalloClient();
|
|
10179
10254
|
init_CampaignError();
|
|
10180
10255
|
|
|
@@ -10245,17 +10320,17 @@ function getCacheEntry(cache, placement) {
|
|
|
10245
10320
|
|
|
10246
10321
|
// src/hooks/useCampaignPreload.ts
|
|
10247
10322
|
function useCampaignPreload() {
|
|
10248
|
-
const [preloadedWebView, setPreloadedWebView] = (0,
|
|
10249
|
-
const preloadedCampaignsRef = (0,
|
|
10250
|
-
const isPreloadValid = (0,
|
|
10323
|
+
const [preloadedWebView, setPreloadedWebView] = (0, import_react24.useState)(null);
|
|
10324
|
+
const preloadedCampaignsRef = (0, import_react24.useRef)(/* @__PURE__ */ new Map());
|
|
10325
|
+
const isPreloadValid = (0, import_react24.useCallback)(
|
|
10251
10326
|
(placement) => isCacheEntryValid(preloadedCampaignsRef.current.get(placement)),
|
|
10252
10327
|
[]
|
|
10253
10328
|
);
|
|
10254
|
-
const consumePreloadedCampaign = (0,
|
|
10329
|
+
const consumePreloadedCampaign = (0, import_react24.useCallback)(
|
|
10255
10330
|
(placement) => getCacheEntry(preloadedCampaignsRef.current, placement),
|
|
10256
10331
|
[]
|
|
10257
10332
|
);
|
|
10258
|
-
const preloadCampaign = (0,
|
|
10333
|
+
const preloadCampaign = (0, import_react24.useCallback)(
|
|
10259
10334
|
async (placement, context) => {
|
|
10260
10335
|
try {
|
|
10261
10336
|
const campaign = await PaywalloClient.getCampaign(placement, context);
|
|
@@ -10324,11 +10399,11 @@ function useCampaignPreload() {
|
|
|
10324
10399
|
}
|
|
10325
10400
|
|
|
10326
10401
|
// src/hooks/useProductLoader.ts
|
|
10327
|
-
var
|
|
10402
|
+
var import_react25 = require("react");
|
|
10328
10403
|
function useProductLoader() {
|
|
10329
|
-
const [products, setProducts] = (0,
|
|
10330
|
-
const [isLoadingProducts, setIsLoadingProducts] = (0,
|
|
10331
|
-
const refreshProducts = (0,
|
|
10404
|
+
const [products, setProducts] = (0, import_react25.useState)(/* @__PURE__ */ new Map());
|
|
10405
|
+
const [isLoadingProducts, setIsLoadingProducts] = (0, import_react25.useState)(false);
|
|
10406
|
+
const refreshProducts = (0, import_react25.useCallback)(async (productIds) => {
|
|
10332
10407
|
setIsLoadingProducts(true);
|
|
10333
10408
|
try {
|
|
10334
10409
|
const iapService = getIAPService();
|
|
@@ -10346,11 +10421,11 @@ function useProductLoader() {
|
|
|
10346
10421
|
}
|
|
10347
10422
|
|
|
10348
10423
|
// src/hooks/usePurchaseOrchestrator.ts
|
|
10349
|
-
var
|
|
10424
|
+
var import_react26 = require("react");
|
|
10350
10425
|
init_paywall();
|
|
10351
10426
|
function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, invalidateSubscriptionCache) {
|
|
10352
10427
|
const { trackDismissed, trackProductSelected, trackPurchased } = usePaywallTracking();
|
|
10353
|
-
const handlePreloadedPurchase = (0,
|
|
10428
|
+
const handlePreloadedPurchase = (0, import_react26.useCallback)(
|
|
10354
10429
|
async (productId) => {
|
|
10355
10430
|
try {
|
|
10356
10431
|
if (preloadedWebView) {
|
|
@@ -10388,7 +10463,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
|
|
|
10388
10463
|
},
|
|
10389
10464
|
[preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, trackDismissed, trackProductSelected, trackPurchased, invalidateSubscriptionCache]
|
|
10390
10465
|
);
|
|
10391
|
-
const handlePreloadedRestore = (0,
|
|
10466
|
+
const handlePreloadedRestore = (0, import_react26.useCallback)(async () => {
|
|
10392
10467
|
try {
|
|
10393
10468
|
const transactions = await getIAPService().restore();
|
|
10394
10469
|
if (transactions.length === 0) return;
|
|
@@ -10411,7 +10486,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
|
|
|
10411
10486
|
}
|
|
10412
10487
|
|
|
10413
10488
|
// src/hooks/useSubscriptionSync.ts
|
|
10414
|
-
var
|
|
10489
|
+
var import_react27 = require("react");
|
|
10415
10490
|
init_PaywalloClient();
|
|
10416
10491
|
init_SubscriptionCache();
|
|
10417
10492
|
|
|
@@ -10521,18 +10596,18 @@ async function checkPersistedCache(distinctId, onHit) {
|
|
|
10521
10596
|
|
|
10522
10597
|
// src/hooks/useSubscriptionSync.ts
|
|
10523
10598
|
function useSubscriptionSync() {
|
|
10524
|
-
const [subscription, setSubscription] = (0,
|
|
10525
|
-
const cacheRef = (0,
|
|
10526
|
-
const setCacheAndState = (0,
|
|
10599
|
+
const [subscription, setSubscription] = (0, import_react27.useState)(null);
|
|
10600
|
+
const cacheRef = (0, import_react27.useRef)(null);
|
|
10601
|
+
const setCacheAndState = (0, import_react27.useCallback)((sub) => {
|
|
10527
10602
|
cacheRef.current = { data: sub, expiresAt: Date.now() + CACHE_TTL_MS2 };
|
|
10528
10603
|
setSubscription(sub);
|
|
10529
10604
|
}, []);
|
|
10530
|
-
const invalidateCache = (0,
|
|
10605
|
+
const invalidateCache = (0, import_react27.useCallback)(() => {
|
|
10531
10606
|
cacheRef.current = null;
|
|
10532
10607
|
const distinctId = PaywalloClient.getDistinctId();
|
|
10533
10608
|
if (distinctId) void subscriptionCache.invalidate(distinctId);
|
|
10534
10609
|
}, []);
|
|
10535
|
-
const fetchAndCache = (0,
|
|
10610
|
+
const fetchAndCache = (0, import_react27.useCallback)(async () => {
|
|
10536
10611
|
const apiClient = PaywalloClient.getApiClient();
|
|
10537
10612
|
const distinctId = PaywalloClient.getDistinctId();
|
|
10538
10613
|
if (!apiClient || !distinctId) return null;
|
|
@@ -10542,7 +10617,7 @@ function useSubscriptionSync() {
|
|
|
10542
10617
|
void subscriptionCache.set(distinctId, toDomainResponse(status));
|
|
10543
10618
|
return sub;
|
|
10544
10619
|
}, [setCacheAndState]);
|
|
10545
|
-
const hasActiveSubscription = (0,
|
|
10620
|
+
const hasActiveSubscription = (0, import_react27.useCallback)(async () => {
|
|
10546
10621
|
const apiClient = PaywalloClient.getApiClient();
|
|
10547
10622
|
const distinctId = PaywalloClient.getDistinctId();
|
|
10548
10623
|
if (!apiClient || !distinctId) return false;
|
|
@@ -10562,7 +10637,7 @@ function useSubscriptionSync() {
|
|
|
10562
10637
|
return resolveOfflineFromCache(distinctId);
|
|
10563
10638
|
}
|
|
10564
10639
|
}, [setCacheAndState]);
|
|
10565
|
-
const getSubscription = (0,
|
|
10640
|
+
const getSubscription = (0, import_react27.useCallback)(async () => {
|
|
10566
10641
|
const apiClient = PaywalloClient.getApiClient();
|
|
10567
10642
|
const distinctId = PaywalloClient.getDistinctId();
|
|
10568
10643
|
if (!apiClient || !distinctId) return null;
|
|
@@ -10587,20 +10662,20 @@ function useSubscriptionSync() {
|
|
|
10587
10662
|
init_localization();
|
|
10588
10663
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
10589
10664
|
function PaywalloProvider({ children, config }) {
|
|
10590
|
-
const [isInitialized, setIsInitialized] = (0,
|
|
10591
|
-
const [initError, setInitError] = (0,
|
|
10592
|
-
const [distinctId, setDistinctId] = (0,
|
|
10593
|
-
const [activePaywall, setActivePaywall] = (0,
|
|
10665
|
+
const [isInitialized, setIsInitialized] = (0, import_react28.useState)(false);
|
|
10666
|
+
const [initError, setInitError] = (0, import_react28.useState)(null);
|
|
10667
|
+
const [distinctId, setDistinctId] = (0, import_react28.useState)(null);
|
|
10668
|
+
const [activePaywall, setActivePaywall] = (0, import_react28.useState)(null);
|
|
10594
10669
|
const { products, isLoadingProducts, refreshProducts } = useProductLoader();
|
|
10595
10670
|
const { subscription, hasActiveSubscription, getSubscription, invalidateCache } = useSubscriptionSync();
|
|
10596
10671
|
const { preloadedWebView, setPreloadedWebView, preloadCampaign, consumePreloadedCampaign, isPreloadValid } = useCampaignPreload();
|
|
10597
|
-
const clearPreloadedWebView = (0,
|
|
10598
|
-
const autoPreloadTriggeredRef = (0,
|
|
10599
|
-
const preloadedWebViewRef = (0,
|
|
10600
|
-
const pollTimerRef = (0,
|
|
10601
|
-
const pollCancelledRef = (0,
|
|
10602
|
-
const secureStorageRef = (0,
|
|
10603
|
-
(0,
|
|
10672
|
+
const clearPreloadedWebView = (0, import_react28.useCallback)(() => setPreloadedWebView(null), [setPreloadedWebView]);
|
|
10673
|
+
const autoPreloadTriggeredRef = (0, import_react28.useRef)(false);
|
|
10674
|
+
const preloadedWebViewRef = (0, import_react28.useRef)(preloadedWebView);
|
|
10675
|
+
const pollTimerRef = (0, import_react28.useRef)(null);
|
|
10676
|
+
const pollCancelledRef = (0, import_react28.useRef)(false);
|
|
10677
|
+
const secureStorageRef = (0, import_react28.useRef)(new SecureStorage(config.debug));
|
|
10678
|
+
(0, import_react28.useEffect)(() => {
|
|
10604
10679
|
preloadedWebViewRef.current = preloadedWebView;
|
|
10605
10680
|
}, [preloadedWebView]);
|
|
10606
10681
|
useAppAutoEvents({
|
|
@@ -10626,20 +10701,20 @@ function PaywalloProvider({ children, config }) {
|
|
|
10626
10701
|
},
|
|
10627
10702
|
debug: config.debug
|
|
10628
10703
|
});
|
|
10629
|
-
const triggerRepreload = (0,
|
|
10704
|
+
const triggerRepreload = (0, import_react28.useCallback)((placement) => {
|
|
10630
10705
|
void PaywalloClient.preloadCampaign(placement).catch(() => {
|
|
10631
10706
|
});
|
|
10632
10707
|
}, []);
|
|
10633
10708
|
const { showingPreloadedWebView, presentPreloadedWebView, resolvePreloadedPaywall, handlePreloadedWebViewReady: baseHandlePreloadedWebViewReady, handlePreloadedClose: baseHandlePreloadedClose, setShowingPreloadedWebView, presentedAtRef } = usePaywallPresenter(preloadedWebView, setPreloadedWebView);
|
|
10634
|
-
const SCREEN_HEIGHT = (0,
|
|
10635
|
-
const slideAnim = (0,
|
|
10636
|
-
const handlePreloadedWebViewReady = (0,
|
|
10709
|
+
const SCREEN_HEIGHT = (0, import_react28.useMemo)(() => import_react_native29.Dimensions.get("window").height, []);
|
|
10710
|
+
const slideAnim = (0, import_react28.useRef)(new import_react_native29.Animated.Value(SCREEN_HEIGHT)).current;
|
|
10711
|
+
const handlePreloadedWebViewReady = (0, import_react28.useCallback)(() => {
|
|
10637
10712
|
if (PaywalloClient.getConfig()?.debug) {
|
|
10638
10713
|
console.log(`[Paywallo WEBVIEW] loaded \u2014 placement: ${preloadedWebView?.placement}`);
|
|
10639
10714
|
}
|
|
10640
10715
|
baseHandlePreloadedWebViewReady();
|
|
10641
10716
|
}, [preloadedWebView?.placement, baseHandlePreloadedWebViewReady]);
|
|
10642
|
-
const handlePreloadedClose = (0,
|
|
10717
|
+
const handlePreloadedClose = (0, import_react28.useCallback)(() => {
|
|
10643
10718
|
const placement = preloadedWebView?.placement;
|
|
10644
10719
|
import_react_native29.Animated.timing(slideAnim, { toValue: SCREEN_HEIGHT, duration: 200, easing: import_react_native29.Easing.in(import_react_native29.Easing.cubic), useNativeDriver: true }).start(() => {
|
|
10645
10720
|
baseHandlePreloadedClose();
|
|
@@ -10654,8 +10729,8 @@ function PaywalloProvider({ children, config }) {
|
|
|
10654
10729
|
presentedAtRef,
|
|
10655
10730
|
invalidateCache
|
|
10656
10731
|
);
|
|
10657
|
-
const [isPreloadPurchasing, setIsPreloadPurchasing] = (0,
|
|
10658
|
-
const handlePreloadedPurchase = (0,
|
|
10732
|
+
const [isPreloadPurchasing, setIsPreloadPurchasing] = (0, import_react28.useState)(false);
|
|
10733
|
+
const handlePreloadedPurchase = (0, import_react28.useCallback)(async (productId) => {
|
|
10659
10734
|
setIsPreloadPurchasing(true);
|
|
10660
10735
|
try {
|
|
10661
10736
|
await rawPreloadedPurchase(productId);
|
|
@@ -10663,15 +10738,25 @@ function PaywalloProvider({ children, config }) {
|
|
|
10663
10738
|
setIsPreloadPurchasing(false);
|
|
10664
10739
|
}
|
|
10665
10740
|
}, [rawPreloadedPurchase]);
|
|
10666
|
-
const configRef = (0,
|
|
10667
|
-
(0,
|
|
10741
|
+
const configRef = (0, import_react28.useRef)(config);
|
|
10742
|
+
(0, import_react28.useEffect)(() => {
|
|
10668
10743
|
let mounted = true;
|
|
10669
|
-
|
|
10670
|
-
|
|
10744
|
+
const bootstrap = async () => {
|
|
10745
|
+
if (!PaywalloClient.isReady()) {
|
|
10746
|
+
initLocalization({ detectDevice: true });
|
|
10747
|
+
try {
|
|
10748
|
+
await PaywalloClient.init(configRef.current);
|
|
10749
|
+
} catch (err) {
|
|
10750
|
+
if (!mounted) return;
|
|
10751
|
+
setInitError(err instanceof Error ? err : new ClientError(CLIENT_ERROR_CODES.NOT_INITIALIZED, String(err)));
|
|
10752
|
+
return;
|
|
10753
|
+
}
|
|
10754
|
+
if (!mounted) return;
|
|
10755
|
+
startSuperwallAutoBridge(configRef.current.debug);
|
|
10756
|
+
void pushAttributionToSuperwall(configRef.current.debug);
|
|
10757
|
+
}
|
|
10671
10758
|
if (!mounted) return;
|
|
10672
10759
|
setDistinctId(PaywalloClient.getDistinctId());
|
|
10673
|
-
startSuperwallAutoBridge(configRef.current.debug);
|
|
10674
|
-
void pushAttributionToSuperwall(configRef.current.debug);
|
|
10675
10760
|
setIsInitialized(true);
|
|
10676
10761
|
if (!autoPreloadTriggeredRef.current) {
|
|
10677
10762
|
autoPreloadTriggeredRef.current = true;
|
|
@@ -10684,19 +10769,17 @@ function PaywalloProvider({ children, config }) {
|
|
|
10684
10769
|
});
|
|
10685
10770
|
}
|
|
10686
10771
|
}
|
|
10687
|
-
}
|
|
10688
|
-
|
|
10689
|
-
setInitError(err instanceof Error ? err : new ClientError(CLIENT_ERROR_CODES.NOT_INITIALIZED, String(err)));
|
|
10690
|
-
});
|
|
10772
|
+
};
|
|
10773
|
+
void bootstrap();
|
|
10691
10774
|
return () => {
|
|
10692
10775
|
mounted = false;
|
|
10693
10776
|
};
|
|
10694
10777
|
}, [preloadCampaign]);
|
|
10695
|
-
const getPaywallConfig = (0,
|
|
10778
|
+
const getPaywallConfig = (0, import_react28.useCallback)(async (p) => {
|
|
10696
10779
|
const api = PaywalloClient.getApiClient();
|
|
10697
10780
|
return api ? api.getPaywall(p) : null;
|
|
10698
10781
|
}, []);
|
|
10699
|
-
const presentPaywall = (0,
|
|
10782
|
+
const presentPaywall = (0, import_react28.useCallback)((placement) => {
|
|
10700
10783
|
const preloaded = paywallPreloadService.getPreloaded(placement);
|
|
10701
10784
|
return new Promise((resolve) => setActivePaywall({
|
|
10702
10785
|
placement,
|
|
@@ -10714,7 +10797,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
10714
10797
|
}
|
|
10715
10798
|
}));
|
|
10716
10799
|
}, []);
|
|
10717
|
-
const trackCampaignImpression = (0,
|
|
10800
|
+
const trackCampaignImpression = (0, import_react28.useCallback)((placement, variantKey, campaignId) => {
|
|
10718
10801
|
const api = PaywalloClient.getApiClient();
|
|
10719
10802
|
if (!api) return;
|
|
10720
10803
|
const sessionId = PaywalloClient.getSessionId();
|
|
@@ -10728,7 +10811,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
10728
10811
|
}).catch(() => {
|
|
10729
10812
|
});
|
|
10730
10813
|
}, []);
|
|
10731
|
-
const presentCampaign = (0,
|
|
10814
|
+
const presentCampaign = (0, import_react28.useCallback)(
|
|
10732
10815
|
async (placement, context) => {
|
|
10733
10816
|
try {
|
|
10734
10817
|
if (preloadedWebView?.placement === placement && preloadedWebView.loaded && isPreloadValid(placement)) {
|
|
@@ -10802,7 +10885,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
10802
10885
|
},
|
|
10803
10886
|
[preloadedWebView, isPreloadValid, consumePreloadedCampaign, presentPreloadedWebView, trackCampaignImpression]
|
|
10804
10887
|
);
|
|
10805
|
-
const restorePurchases = (0,
|
|
10888
|
+
const restorePurchases = (0, import_react28.useCallback)(async () => {
|
|
10806
10889
|
invalidateCache();
|
|
10807
10890
|
try {
|
|
10808
10891
|
const txs = await getIAPService().restore();
|
|
@@ -10811,13 +10894,13 @@ function PaywalloProvider({ children, config }) {
|
|
|
10811
10894
|
return { success: false, restoredProducts: [], error: error instanceof Error ? error : new PurchaseError(PURCHASE_ERROR_CODES.RESTORE_FAILED, String(error)) };
|
|
10812
10895
|
}
|
|
10813
10896
|
}, [invalidateCache]);
|
|
10814
|
-
const handlePaywallResult = (0,
|
|
10897
|
+
const handlePaywallResult = (0, import_react28.useCallback)((result) => {
|
|
10815
10898
|
if (activePaywall) {
|
|
10816
10899
|
activePaywall.resolver(result);
|
|
10817
10900
|
setActivePaywall(null);
|
|
10818
10901
|
}
|
|
10819
10902
|
}, [activePaywall]);
|
|
10820
|
-
const bridgePaywallPresenter = (0,
|
|
10903
|
+
const bridgePaywallPresenter = (0, import_react28.useCallback)((placement) => {
|
|
10821
10904
|
if (PaywalloClient.getConfig()?.debug) {
|
|
10822
10905
|
console.log(`[Paywallo PRESENT] called \u2014 preloaded: ${preloadedWebViewRef.current?.loaded === true} placement: ${placement}`);
|
|
10823
10906
|
}
|
|
@@ -10846,7 +10929,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
10846
10929
|
}
|
|
10847
10930
|
return presentCampaign(placement);
|
|
10848
10931
|
}, [presentPreloadedWebView, presentCampaign]);
|
|
10849
|
-
(0,
|
|
10932
|
+
(0, import_react28.useEffect)(() => {
|
|
10850
10933
|
if (!isInitialized) return;
|
|
10851
10934
|
const onEmergency = async (id) => {
|
|
10852
10935
|
try {
|
|
@@ -10873,9 +10956,9 @@ function PaywalloProvider({ children, config }) {
|
|
|
10873
10956
|
};
|
|
10874
10957
|
}, [isInitialized, bridgePaywallPresenter, getSubscription, hasActiveSubscription, restorePurchases]);
|
|
10875
10958
|
const isReady = isInitialized && !isLoadingProducts;
|
|
10876
|
-
const noOp = (0,
|
|
10959
|
+
const noOp = (0, import_react28.useCallback)(() => {
|
|
10877
10960
|
}, []);
|
|
10878
|
-
const contextValue = (0,
|
|
10961
|
+
const contextValue = (0, import_react28.useMemo)(() => ({
|
|
10879
10962
|
config: PaywalloClient.getConfig(),
|
|
10880
10963
|
isInitialized,
|
|
10881
10964
|
isReady,
|
|
@@ -10909,7 +10992,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
10909
10992
|
getPaywallConfig,
|
|
10910
10993
|
refreshProducts
|
|
10911
10994
|
]);
|
|
10912
|
-
(0,
|
|
10995
|
+
(0, import_react28.useLayoutEffect)(() => {
|
|
10913
10996
|
if (showingPreloadedWebView) {
|
|
10914
10997
|
slideAnim.setValue(SCREEN_HEIGHT);
|
|
10915
10998
|
import_react_native29.Animated.timing(slideAnim, { toValue: 0, duration: 200, easing: import_react_native29.Easing.out(import_react_native29.Easing.cubic), useNativeDriver: true }).start();
|
|
@@ -11128,6 +11211,7 @@ var index_default = Paywallo;
|
|
|
11128
11211
|
attributionTracker,
|
|
11129
11212
|
createPurchaseError,
|
|
11130
11213
|
detectDeviceLanguage,
|
|
11214
|
+
getActiveRouteName,
|
|
11131
11215
|
getCurrentLanguage,
|
|
11132
11216
|
getDefaultLanguage,
|
|
11133
11217
|
getIAPService,
|
|
@@ -11156,6 +11240,7 @@ var index_default = Paywallo;
|
|
|
11156
11240
|
useOnboarding,
|
|
11157
11241
|
usePaywallContext,
|
|
11158
11242
|
usePaywallo,
|
|
11243
|
+
usePaywalloScreenTracking,
|
|
11159
11244
|
usePlanPurchase,
|
|
11160
11245
|
usePlans,
|
|
11161
11246
|
useProducts,
|