@virex-tech/paywallo-sdk 2.4.1 → 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 +978 -913
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +926 -863
- 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),
|
|
@@ -4082,89 +3949,16 @@ var init_IAPTransactionEmitter = __esm({
|
|
|
4082
3949
|
}
|
|
4083
3950
|
}
|
|
4084
3951
|
/**
|
|
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";
|
|
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
|
});
|
|
@@ -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") {
|
|
@@ -6379,6 +6413,143 @@ var init_session = __esm({
|
|
|
6379
6413
|
}
|
|
6380
6414
|
});
|
|
6381
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"() {
|
|
6547
|
+
"use strict";
|
|
6548
|
+
init_OnboardingError();
|
|
6549
|
+
init_OnboardingManager();
|
|
6550
|
+
}
|
|
6551
|
+
});
|
|
6552
|
+
|
|
6382
6553
|
// src/domains/analytics/AnalyticsError.ts
|
|
6383
6554
|
var AnalyticsError, ANALYTICS_ERROR_CODES;
|
|
6384
6555
|
var init_AnalyticsError = __esm({
|
|
@@ -6592,19 +6763,7 @@ async function trackEvent(apiClient, state, eventName, options) {
|
|
|
6592
6763
|
{ ...identityProps, ...options?.properties, ...sessionProp },
|
|
6593
6764
|
options?.timestamp,
|
|
6594
6765
|
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 });
|
|
6766
|
+
);
|
|
6608
6767
|
}
|
|
6609
6768
|
function validateCoreAction(actionName) {
|
|
6610
6769
|
if (!actionName || typeof actionName !== "string") {
|
|
@@ -6669,138 +6828,6 @@ var init_PaywalloFlags = __esm({
|
|
|
6669
6828
|
}
|
|
6670
6829
|
});
|
|
6671
6830
|
|
|
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();
|
|
6792
|
-
}
|
|
6793
|
-
});
|
|
6794
|
-
|
|
6795
|
-
// src/domains/onboarding/index.ts
|
|
6796
|
-
var init_onboarding = __esm({
|
|
6797
|
-
"src/domains/onboarding/index.ts"() {
|
|
6798
|
-
"use strict";
|
|
6799
|
-
init_OnboardingError();
|
|
6800
|
-
init_OnboardingManager();
|
|
6801
|
-
}
|
|
6802
|
-
});
|
|
6803
|
-
|
|
6804
6831
|
// src/domains/paywall/paywallHeartbeatRecovery.ts
|
|
6805
6832
|
async function recoverPaywallHeartbeat(apiClient, distinctId, sessionId, debug) {
|
|
6806
6833
|
const snapshot = await readPersistedHeartbeat();
|
|
@@ -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
|
};
|
|
@@ -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) {
|
|
@@ -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();
|
|
@@ -10040,50 +10136,9 @@ function buildCallbacks() {
|
|
|
10040
10136
|
const identifier = "paywallIdentifier" in safe && typeof safe.paywallIdentifier === "string" ? safe.paywallIdentifier : productId;
|
|
10041
10137
|
log2("onPurchase", { productId, identifier });
|
|
10042
10138
|
void trackPaywallEvent("purchased", identifier, ts, { product_id: productId });
|
|
10043
|
-
},
|
|
10044
|
-
// Global Superwall event listener — used specifically for `transactionComplete`
|
|
10045
|
-
// which is the only event that exposes the full product (currency/price) +
|
|
10046
|
-
// transaction (storeTransactionId) shape needed to create a server-side
|
|
10047
|
-
// `transactions` row. Other events are skipped (paywallOpen/Dismiss already
|
|
10048
|
-
// handled above with their typed callbacks).
|
|
10049
|
-
onSuperwallEvent: (info) => {
|
|
10050
|
-
const ev = info?.event;
|
|
10051
|
-
if (!ev || ev.event !== "transactionComplete") return;
|
|
10052
|
-
const ts = Date.now();
|
|
10053
|
-
const product = ev.product;
|
|
10054
|
-
const transaction = ev.transaction;
|
|
10055
|
-
if (!product) {
|
|
10056
|
-
log2("transactionComplete missing product \u2014 skipping transaction track");
|
|
10057
|
-
return;
|
|
10058
|
-
}
|
|
10059
|
-
const productId = product.productIdentifier ?? product.id ?? "unknown";
|
|
10060
|
-
const currency = product.currencyCode ?? "USD";
|
|
10061
|
-
const price = product.price ?? 0;
|
|
10062
|
-
const transactionId = transaction?.storeTransactionId ?? transaction?.originalTransactionIdentifier ?? `${productId}_${String(ts)}`;
|
|
10063
|
-
const isTrial = product.hasFreeTrial === true || (product.trialPeriodPrice ?? 0) > 0;
|
|
10064
|
-
const paywallIdentifier = ev.paywallInfo?.identifier;
|
|
10065
|
-
log2("onSuperwallEvent: transactionComplete", { productId, currency, price, transactionId, isTrial });
|
|
10066
|
-
void (async () => {
|
|
10067
|
-
try {
|
|
10068
|
-
await PaywalloClient.track("transaction", {
|
|
10069
|
-
priority: "critical",
|
|
10070
|
-
properties: {
|
|
10071
|
-
type: isTrial ? "trial_started" : "completed",
|
|
10072
|
-
// Server's IngestTranslator looks for `transaction_id`/`product_id`
|
|
10073
|
-
// /`amount`/`currency`/`paywall_id` keys (not `tx_id`). PurchaseHandler
|
|
10074
|
-
// then resolves type=trial/new/renewal based on amount being 0 vs >0.
|
|
10075
|
-
transaction_id: transactionId,
|
|
10076
|
-
product_id: productId,
|
|
10077
|
-
amount: price,
|
|
10078
|
-
currency,
|
|
10079
|
-
...paywallIdentifier ? { paywall_id: paywallIdentifier } : {}
|
|
10080
|
-
}
|
|
10081
|
-
});
|
|
10082
|
-
} catch (err) {
|
|
10083
|
-
log2("transactionComplete track error", { err: String(err) });
|
|
10084
|
-
}
|
|
10085
|
-
})();
|
|
10086
10139
|
}
|
|
10140
|
+
// onSuperwallEvent removed — transaction sale events are sourced exclusively
|
|
10141
|
+
// from server-side webhooks (Apple/Google/Superwall/RevenueCat).
|
|
10087
10142
|
};
|
|
10088
10143
|
}
|
|
10089
10144
|
function startSuperwallAutoBridge(debug = false) {
|
|
@@ -10127,7 +10182,7 @@ init_session();
|
|
|
10127
10182
|
init_ClientError();
|
|
10128
10183
|
|
|
10129
10184
|
// src/hooks/useAppAutoEvents.ts
|
|
10130
|
-
var
|
|
10185
|
+
var import_react23 = require("react");
|
|
10131
10186
|
|
|
10132
10187
|
// src/utils/platformDetection.ts
|
|
10133
10188
|
async function detectPlatform3() {
|
|
@@ -10153,8 +10208,8 @@ var FIRST_SEEN_KEY = "@paywallo:firstSeen_v226";
|
|
|
10153
10208
|
var DEBOUNCE_MS = 1e3;
|
|
10154
10209
|
function useAppAutoEvents(config) {
|
|
10155
10210
|
const { isInitialized, getAppVersion: getAppVersion2, getSessionId, emitLifecycle, storageGet, storageSet, debug } = config;
|
|
10156
|
-
const didRunRef = (0,
|
|
10157
|
-
(0,
|
|
10211
|
+
const didRunRef = (0, import_react23.useRef)(false);
|
|
10212
|
+
(0, import_react23.useEffect)(() => {
|
|
10158
10213
|
if (debug) console.log("[Paywallo EVENTS] useEffect fired", { isInitialized, didRunRefCurrent: didRunRef.current });
|
|
10159
10214
|
if (!isInitialized || didRunRef.current) return;
|
|
10160
10215
|
const timer = setTimeout(() => {
|
|
@@ -10164,13 +10219,13 @@ function useAppAutoEvents(config) {
|
|
|
10164
10219
|
const sessionId = getSessionId();
|
|
10165
10220
|
const appVersion = getAppVersion2();
|
|
10166
10221
|
const deviceType = await detectPlatform3();
|
|
10167
|
-
const
|
|
10168
|
-
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 });
|
|
10169
10224
|
try {
|
|
10170
10225
|
if (debug) console.log("[Paywallo EVENTS] checking install \u2014 about to read FIRST_SEEN_KEY");
|
|
10171
10226
|
const firstSeen = await storageGet(FIRST_SEEN_KEY);
|
|
10172
10227
|
if (!firstSeen) {
|
|
10173
|
-
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");
|
|
10174
10229
|
await storageSet(FIRST_SEEN_KEY, (/* @__PURE__ */ new Date()).toISOString());
|
|
10175
10230
|
if (debug) console.log("[Paywallo EVENTS] install event emitted (first time)");
|
|
10176
10231
|
if (debug) console.log("[Paywallo EVENTS] lifecycle install emitted");
|
|
@@ -10182,7 +10237,7 @@ function useAppAutoEvents(config) {
|
|
|
10182
10237
|
}
|
|
10183
10238
|
try {
|
|
10184
10239
|
if (debug) console.log("[Paywallo EVENTS] emitting cold_start");
|
|
10185
|
-
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");
|
|
10186
10241
|
if (debug) console.log("[Paywallo EVENTS] lifecycle cold_start emitted");
|
|
10187
10242
|
} catch (err) {
|
|
10188
10243
|
if (debug) console.log("[Paywallo EVENTS] lifecycle cold_start failed", err);
|
|
@@ -10194,7 +10249,7 @@ function useAppAutoEvents(config) {
|
|
|
10194
10249
|
}
|
|
10195
10250
|
|
|
10196
10251
|
// src/hooks/useCampaignPreload.ts
|
|
10197
|
-
var
|
|
10252
|
+
var import_react24 = require("react");
|
|
10198
10253
|
init_PaywalloClient();
|
|
10199
10254
|
init_CampaignError();
|
|
10200
10255
|
|
|
@@ -10265,17 +10320,17 @@ function getCacheEntry(cache, placement) {
|
|
|
10265
10320
|
|
|
10266
10321
|
// src/hooks/useCampaignPreload.ts
|
|
10267
10322
|
function useCampaignPreload() {
|
|
10268
|
-
const [preloadedWebView, setPreloadedWebView] = (0,
|
|
10269
|
-
const preloadedCampaignsRef = (0,
|
|
10270
|
-
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)(
|
|
10271
10326
|
(placement) => isCacheEntryValid(preloadedCampaignsRef.current.get(placement)),
|
|
10272
10327
|
[]
|
|
10273
10328
|
);
|
|
10274
|
-
const consumePreloadedCampaign = (0,
|
|
10329
|
+
const consumePreloadedCampaign = (0, import_react24.useCallback)(
|
|
10275
10330
|
(placement) => getCacheEntry(preloadedCampaignsRef.current, placement),
|
|
10276
10331
|
[]
|
|
10277
10332
|
);
|
|
10278
|
-
const preloadCampaign = (0,
|
|
10333
|
+
const preloadCampaign = (0, import_react24.useCallback)(
|
|
10279
10334
|
async (placement, context) => {
|
|
10280
10335
|
try {
|
|
10281
10336
|
const campaign = await PaywalloClient.getCampaign(placement, context);
|
|
@@ -10344,11 +10399,11 @@ function useCampaignPreload() {
|
|
|
10344
10399
|
}
|
|
10345
10400
|
|
|
10346
10401
|
// src/hooks/useProductLoader.ts
|
|
10347
|
-
var
|
|
10402
|
+
var import_react25 = require("react");
|
|
10348
10403
|
function useProductLoader() {
|
|
10349
|
-
const [products, setProducts] = (0,
|
|
10350
|
-
const [isLoadingProducts, setIsLoadingProducts] = (0,
|
|
10351
|
-
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) => {
|
|
10352
10407
|
setIsLoadingProducts(true);
|
|
10353
10408
|
try {
|
|
10354
10409
|
const iapService = getIAPService();
|
|
@@ -10366,11 +10421,11 @@ function useProductLoader() {
|
|
|
10366
10421
|
}
|
|
10367
10422
|
|
|
10368
10423
|
// src/hooks/usePurchaseOrchestrator.ts
|
|
10369
|
-
var
|
|
10424
|
+
var import_react26 = require("react");
|
|
10370
10425
|
init_paywall();
|
|
10371
10426
|
function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, invalidateSubscriptionCache) {
|
|
10372
10427
|
const { trackDismissed, trackProductSelected, trackPurchased } = usePaywallTracking();
|
|
10373
|
-
const handlePreloadedPurchase = (0,
|
|
10428
|
+
const handlePreloadedPurchase = (0, import_react26.useCallback)(
|
|
10374
10429
|
async (productId) => {
|
|
10375
10430
|
try {
|
|
10376
10431
|
if (preloadedWebView) {
|
|
@@ -10408,7 +10463,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
|
|
|
10408
10463
|
},
|
|
10409
10464
|
[preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, trackDismissed, trackProductSelected, trackPurchased, invalidateSubscriptionCache]
|
|
10410
10465
|
);
|
|
10411
|
-
const handlePreloadedRestore = (0,
|
|
10466
|
+
const handlePreloadedRestore = (0, import_react26.useCallback)(async () => {
|
|
10412
10467
|
try {
|
|
10413
10468
|
const transactions = await getIAPService().restore();
|
|
10414
10469
|
if (transactions.length === 0) return;
|
|
@@ -10431,7 +10486,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
|
|
|
10431
10486
|
}
|
|
10432
10487
|
|
|
10433
10488
|
// src/hooks/useSubscriptionSync.ts
|
|
10434
|
-
var
|
|
10489
|
+
var import_react27 = require("react");
|
|
10435
10490
|
init_PaywalloClient();
|
|
10436
10491
|
init_SubscriptionCache();
|
|
10437
10492
|
|
|
@@ -10541,18 +10596,18 @@ async function checkPersistedCache(distinctId, onHit) {
|
|
|
10541
10596
|
|
|
10542
10597
|
// src/hooks/useSubscriptionSync.ts
|
|
10543
10598
|
function useSubscriptionSync() {
|
|
10544
|
-
const [subscription, setSubscription] = (0,
|
|
10545
|
-
const cacheRef = (0,
|
|
10546
|
-
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) => {
|
|
10547
10602
|
cacheRef.current = { data: sub, expiresAt: Date.now() + CACHE_TTL_MS2 };
|
|
10548
10603
|
setSubscription(sub);
|
|
10549
10604
|
}, []);
|
|
10550
|
-
const invalidateCache = (0,
|
|
10605
|
+
const invalidateCache = (0, import_react27.useCallback)(() => {
|
|
10551
10606
|
cacheRef.current = null;
|
|
10552
10607
|
const distinctId = PaywalloClient.getDistinctId();
|
|
10553
10608
|
if (distinctId) void subscriptionCache.invalidate(distinctId);
|
|
10554
10609
|
}, []);
|
|
10555
|
-
const fetchAndCache = (0,
|
|
10610
|
+
const fetchAndCache = (0, import_react27.useCallback)(async () => {
|
|
10556
10611
|
const apiClient = PaywalloClient.getApiClient();
|
|
10557
10612
|
const distinctId = PaywalloClient.getDistinctId();
|
|
10558
10613
|
if (!apiClient || !distinctId) return null;
|
|
@@ -10562,7 +10617,7 @@ function useSubscriptionSync() {
|
|
|
10562
10617
|
void subscriptionCache.set(distinctId, toDomainResponse(status));
|
|
10563
10618
|
return sub;
|
|
10564
10619
|
}, [setCacheAndState]);
|
|
10565
|
-
const hasActiveSubscription = (0,
|
|
10620
|
+
const hasActiveSubscription = (0, import_react27.useCallback)(async () => {
|
|
10566
10621
|
const apiClient = PaywalloClient.getApiClient();
|
|
10567
10622
|
const distinctId = PaywalloClient.getDistinctId();
|
|
10568
10623
|
if (!apiClient || !distinctId) return false;
|
|
@@ -10582,7 +10637,7 @@ function useSubscriptionSync() {
|
|
|
10582
10637
|
return resolveOfflineFromCache(distinctId);
|
|
10583
10638
|
}
|
|
10584
10639
|
}, [setCacheAndState]);
|
|
10585
|
-
const getSubscription = (0,
|
|
10640
|
+
const getSubscription = (0, import_react27.useCallback)(async () => {
|
|
10586
10641
|
const apiClient = PaywalloClient.getApiClient();
|
|
10587
10642
|
const distinctId = PaywalloClient.getDistinctId();
|
|
10588
10643
|
if (!apiClient || !distinctId) return null;
|
|
@@ -10607,20 +10662,20 @@ function useSubscriptionSync() {
|
|
|
10607
10662
|
init_localization();
|
|
10608
10663
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
10609
10664
|
function PaywalloProvider({ children, config }) {
|
|
10610
|
-
const [isInitialized, setIsInitialized] = (0,
|
|
10611
|
-
const [initError, setInitError] = (0,
|
|
10612
|
-
const [distinctId, setDistinctId] = (0,
|
|
10613
|
-
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);
|
|
10614
10669
|
const { products, isLoadingProducts, refreshProducts } = useProductLoader();
|
|
10615
10670
|
const { subscription, hasActiveSubscription, getSubscription, invalidateCache } = useSubscriptionSync();
|
|
10616
10671
|
const { preloadedWebView, setPreloadedWebView, preloadCampaign, consumePreloadedCampaign, isPreloadValid } = useCampaignPreload();
|
|
10617
|
-
const clearPreloadedWebView = (0,
|
|
10618
|
-
const autoPreloadTriggeredRef = (0,
|
|
10619
|
-
const preloadedWebViewRef = (0,
|
|
10620
|
-
const pollTimerRef = (0,
|
|
10621
|
-
const pollCancelledRef = (0,
|
|
10622
|
-
const secureStorageRef = (0,
|
|
10623
|
-
(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)(() => {
|
|
10624
10679
|
preloadedWebViewRef.current = preloadedWebView;
|
|
10625
10680
|
}, [preloadedWebView]);
|
|
10626
10681
|
useAppAutoEvents({
|
|
@@ -10646,20 +10701,20 @@ function PaywalloProvider({ children, config }) {
|
|
|
10646
10701
|
},
|
|
10647
10702
|
debug: config.debug
|
|
10648
10703
|
});
|
|
10649
|
-
const triggerRepreload = (0,
|
|
10704
|
+
const triggerRepreload = (0, import_react28.useCallback)((placement) => {
|
|
10650
10705
|
void PaywalloClient.preloadCampaign(placement).catch(() => {
|
|
10651
10706
|
});
|
|
10652
10707
|
}, []);
|
|
10653
10708
|
const { showingPreloadedWebView, presentPreloadedWebView, resolvePreloadedPaywall, handlePreloadedWebViewReady: baseHandlePreloadedWebViewReady, handlePreloadedClose: baseHandlePreloadedClose, setShowingPreloadedWebView, presentedAtRef } = usePaywallPresenter(preloadedWebView, setPreloadedWebView);
|
|
10654
|
-
const SCREEN_HEIGHT = (0,
|
|
10655
|
-
const slideAnim = (0,
|
|
10656
|
-
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)(() => {
|
|
10657
10712
|
if (PaywalloClient.getConfig()?.debug) {
|
|
10658
10713
|
console.log(`[Paywallo WEBVIEW] loaded \u2014 placement: ${preloadedWebView?.placement}`);
|
|
10659
10714
|
}
|
|
10660
10715
|
baseHandlePreloadedWebViewReady();
|
|
10661
10716
|
}, [preloadedWebView?.placement, baseHandlePreloadedWebViewReady]);
|
|
10662
|
-
const handlePreloadedClose = (0,
|
|
10717
|
+
const handlePreloadedClose = (0, import_react28.useCallback)(() => {
|
|
10663
10718
|
const placement = preloadedWebView?.placement;
|
|
10664
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(() => {
|
|
10665
10720
|
baseHandlePreloadedClose();
|
|
@@ -10674,8 +10729,8 @@ function PaywalloProvider({ children, config }) {
|
|
|
10674
10729
|
presentedAtRef,
|
|
10675
10730
|
invalidateCache
|
|
10676
10731
|
);
|
|
10677
|
-
const [isPreloadPurchasing, setIsPreloadPurchasing] = (0,
|
|
10678
|
-
const handlePreloadedPurchase = (0,
|
|
10732
|
+
const [isPreloadPurchasing, setIsPreloadPurchasing] = (0, import_react28.useState)(false);
|
|
10733
|
+
const handlePreloadedPurchase = (0, import_react28.useCallback)(async (productId) => {
|
|
10679
10734
|
setIsPreloadPurchasing(true);
|
|
10680
10735
|
try {
|
|
10681
10736
|
await rawPreloadedPurchase(productId);
|
|
@@ -10683,15 +10738,25 @@ function PaywalloProvider({ children, config }) {
|
|
|
10683
10738
|
setIsPreloadPurchasing(false);
|
|
10684
10739
|
}
|
|
10685
10740
|
}, [rawPreloadedPurchase]);
|
|
10686
|
-
const configRef = (0,
|
|
10687
|
-
(0,
|
|
10741
|
+
const configRef = (0, import_react28.useRef)(config);
|
|
10742
|
+
(0, import_react28.useEffect)(() => {
|
|
10688
10743
|
let mounted = true;
|
|
10689
|
-
|
|
10690
|
-
|
|
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
|
+
}
|
|
10691
10758
|
if (!mounted) return;
|
|
10692
10759
|
setDistinctId(PaywalloClient.getDistinctId());
|
|
10693
|
-
startSuperwallAutoBridge(configRef.current.debug);
|
|
10694
|
-
void pushAttributionToSuperwall(configRef.current.debug);
|
|
10695
10760
|
setIsInitialized(true);
|
|
10696
10761
|
if (!autoPreloadTriggeredRef.current) {
|
|
10697
10762
|
autoPreloadTriggeredRef.current = true;
|
|
@@ -10704,19 +10769,17 @@ function PaywalloProvider({ children, config }) {
|
|
|
10704
10769
|
});
|
|
10705
10770
|
}
|
|
10706
10771
|
}
|
|
10707
|
-
}
|
|
10708
|
-
|
|
10709
|
-
setInitError(err instanceof Error ? err : new ClientError(CLIENT_ERROR_CODES.NOT_INITIALIZED, String(err)));
|
|
10710
|
-
});
|
|
10772
|
+
};
|
|
10773
|
+
void bootstrap();
|
|
10711
10774
|
return () => {
|
|
10712
10775
|
mounted = false;
|
|
10713
10776
|
};
|
|
10714
10777
|
}, [preloadCampaign]);
|
|
10715
|
-
const getPaywallConfig = (0,
|
|
10778
|
+
const getPaywallConfig = (0, import_react28.useCallback)(async (p) => {
|
|
10716
10779
|
const api = PaywalloClient.getApiClient();
|
|
10717
10780
|
return api ? api.getPaywall(p) : null;
|
|
10718
10781
|
}, []);
|
|
10719
|
-
const presentPaywall = (0,
|
|
10782
|
+
const presentPaywall = (0, import_react28.useCallback)((placement) => {
|
|
10720
10783
|
const preloaded = paywallPreloadService.getPreloaded(placement);
|
|
10721
10784
|
return new Promise((resolve) => setActivePaywall({
|
|
10722
10785
|
placement,
|
|
@@ -10734,7 +10797,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
10734
10797
|
}
|
|
10735
10798
|
}));
|
|
10736
10799
|
}, []);
|
|
10737
|
-
const trackCampaignImpression = (0,
|
|
10800
|
+
const trackCampaignImpression = (0, import_react28.useCallback)((placement, variantKey, campaignId) => {
|
|
10738
10801
|
const api = PaywalloClient.getApiClient();
|
|
10739
10802
|
if (!api) return;
|
|
10740
10803
|
const sessionId = PaywalloClient.getSessionId();
|
|
@@ -10748,7 +10811,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
10748
10811
|
}).catch(() => {
|
|
10749
10812
|
});
|
|
10750
10813
|
}, []);
|
|
10751
|
-
const presentCampaign = (0,
|
|
10814
|
+
const presentCampaign = (0, import_react28.useCallback)(
|
|
10752
10815
|
async (placement, context) => {
|
|
10753
10816
|
try {
|
|
10754
10817
|
if (preloadedWebView?.placement === placement && preloadedWebView.loaded && isPreloadValid(placement)) {
|
|
@@ -10822,7 +10885,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
10822
10885
|
},
|
|
10823
10886
|
[preloadedWebView, isPreloadValid, consumePreloadedCampaign, presentPreloadedWebView, trackCampaignImpression]
|
|
10824
10887
|
);
|
|
10825
|
-
const restorePurchases = (0,
|
|
10888
|
+
const restorePurchases = (0, import_react28.useCallback)(async () => {
|
|
10826
10889
|
invalidateCache();
|
|
10827
10890
|
try {
|
|
10828
10891
|
const txs = await getIAPService().restore();
|
|
@@ -10831,13 +10894,13 @@ function PaywalloProvider({ children, config }) {
|
|
|
10831
10894
|
return { success: false, restoredProducts: [], error: error instanceof Error ? error : new PurchaseError(PURCHASE_ERROR_CODES.RESTORE_FAILED, String(error)) };
|
|
10832
10895
|
}
|
|
10833
10896
|
}, [invalidateCache]);
|
|
10834
|
-
const handlePaywallResult = (0,
|
|
10897
|
+
const handlePaywallResult = (0, import_react28.useCallback)((result) => {
|
|
10835
10898
|
if (activePaywall) {
|
|
10836
10899
|
activePaywall.resolver(result);
|
|
10837
10900
|
setActivePaywall(null);
|
|
10838
10901
|
}
|
|
10839
10902
|
}, [activePaywall]);
|
|
10840
|
-
const bridgePaywallPresenter = (0,
|
|
10903
|
+
const bridgePaywallPresenter = (0, import_react28.useCallback)((placement) => {
|
|
10841
10904
|
if (PaywalloClient.getConfig()?.debug) {
|
|
10842
10905
|
console.log(`[Paywallo PRESENT] called \u2014 preloaded: ${preloadedWebViewRef.current?.loaded === true} placement: ${placement}`);
|
|
10843
10906
|
}
|
|
@@ -10866,7 +10929,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
10866
10929
|
}
|
|
10867
10930
|
return presentCampaign(placement);
|
|
10868
10931
|
}, [presentPreloadedWebView, presentCampaign]);
|
|
10869
|
-
(0,
|
|
10932
|
+
(0, import_react28.useEffect)(() => {
|
|
10870
10933
|
if (!isInitialized) return;
|
|
10871
10934
|
const onEmergency = async (id) => {
|
|
10872
10935
|
try {
|
|
@@ -10893,9 +10956,9 @@ function PaywalloProvider({ children, config }) {
|
|
|
10893
10956
|
};
|
|
10894
10957
|
}, [isInitialized, bridgePaywallPresenter, getSubscription, hasActiveSubscription, restorePurchases]);
|
|
10895
10958
|
const isReady = isInitialized && !isLoadingProducts;
|
|
10896
|
-
const noOp = (0,
|
|
10959
|
+
const noOp = (0, import_react28.useCallback)(() => {
|
|
10897
10960
|
}, []);
|
|
10898
|
-
const contextValue = (0,
|
|
10961
|
+
const contextValue = (0, import_react28.useMemo)(() => ({
|
|
10899
10962
|
config: PaywalloClient.getConfig(),
|
|
10900
10963
|
isInitialized,
|
|
10901
10964
|
isReady,
|
|
@@ -10929,7 +10992,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
10929
10992
|
getPaywallConfig,
|
|
10930
10993
|
refreshProducts
|
|
10931
10994
|
]);
|
|
10932
|
-
(0,
|
|
10995
|
+
(0, import_react28.useLayoutEffect)(() => {
|
|
10933
10996
|
if (showingPreloadedWebView) {
|
|
10934
10997
|
slideAnim.setValue(SCREEN_HEIGHT);
|
|
10935
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();
|
|
@@ -11148,6 +11211,7 @@ var index_default = Paywallo;
|
|
|
11148
11211
|
attributionTracker,
|
|
11149
11212
|
createPurchaseError,
|
|
11150
11213
|
detectDeviceLanguage,
|
|
11214
|
+
getActiveRouteName,
|
|
11151
11215
|
getCurrentLanguage,
|
|
11152
11216
|
getDefaultLanguage,
|
|
11153
11217
|
getIAPService,
|
|
@@ -11176,6 +11240,7 @@ var index_default = Paywallo;
|
|
|
11176
11240
|
useOnboarding,
|
|
11177
11241
|
usePaywallContext,
|
|
11178
11242
|
usePaywallo,
|
|
11243
|
+
usePaywalloScreenTracking,
|
|
11179
11244
|
usePlanPurchase,
|
|
11180
11245
|
usePlans,
|
|
11181
11246
|
useProducts,
|