@virex-tech/paywallo-sdk 2.5.0 → 2.5.2
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/README.md +23 -2
- package/android/src/main/AndroidManifest.xml +8 -0
- package/android/src/main/java/com/paywallo/sdk/PaywalloStoreKitModule.kt +14 -2
- package/dist/index.d.mts +40 -29
- package/dist/index.d.ts +40 -29
- package/dist/index.js +457 -304
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +444 -291
- package/dist/index.mjs.map +1 -1
- package/ios/PaywalloAPNSProxy.m +138 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -348,13 +348,13 @@ function getCachedDeviceInfo() {
|
|
|
348
348
|
return null;
|
|
349
349
|
}
|
|
350
350
|
}
|
|
351
|
-
var import_react_native2,
|
|
351
|
+
var import_react_native2, Application, Device, Localization, _debug2, cachedDeviceInfo, nativeDeviceInfo;
|
|
352
352
|
var init_NativeDeviceInfo = __esm({
|
|
353
353
|
"src/utils/NativeDeviceInfo.ts"() {
|
|
354
354
|
"use strict";
|
|
355
355
|
import_react_native2 = require("react-native");
|
|
356
|
-
Device = __toESM(require("expo-device"));
|
|
357
356
|
Application = __toESM(require("expo-application"));
|
|
357
|
+
Device = __toESM(require("expo-device"));
|
|
358
358
|
Localization = __toESM(require("expo-localization"));
|
|
359
359
|
init_ClientError();
|
|
360
360
|
_debug2 = false;
|
|
@@ -968,7 +968,9 @@ var init_AdvertisingIdManager = __esm({
|
|
|
968
968
|
return this.cached;
|
|
969
969
|
}
|
|
970
970
|
async collect(requestATT = false) {
|
|
971
|
-
if (this.cached
|
|
971
|
+
if (this.cached && !(requestATT && this.cached.attStatus === "undetermined")) {
|
|
972
|
+
return this.cached;
|
|
973
|
+
}
|
|
972
974
|
const fallback = { idfv: null, idfa: null, gaid: null, attStatus: "unavailable" };
|
|
973
975
|
try {
|
|
974
976
|
const tracking = await import("expo-tracking-transparency");
|
|
@@ -1391,6 +1393,7 @@ var init_AttributionTracker = __esm({
|
|
|
1391
1393
|
constructor(debug = false) {
|
|
1392
1394
|
this.cache = null;
|
|
1393
1395
|
this.captureInProgress = false;
|
|
1396
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
1394
1397
|
this.storage = new SecureStorage(debug);
|
|
1395
1398
|
}
|
|
1396
1399
|
/**
|
|
@@ -1430,10 +1433,31 @@ var init_AttributionTracker = __esm({
|
|
|
1430
1433
|
};
|
|
1431
1434
|
await this.storage.set(ATTRIBUTION_STORAGE_KEY, JSON.stringify(capture));
|
|
1432
1435
|
this.cache = capture;
|
|
1436
|
+
this.notifyListeners(capture);
|
|
1433
1437
|
} finally {
|
|
1434
1438
|
this.captureInProgress = false;
|
|
1435
1439
|
}
|
|
1436
1440
|
}
|
|
1441
|
+
/**
|
|
1442
|
+
* Subscribes to the first real attribution write (first-write-wins — no-op
|
|
1443
|
+
* captures never fire). Useful for late enrichment consumers (e.g. re-push
|
|
1444
|
+
* of Superwall attributes when a warm deep link / deferred match lands after
|
|
1445
|
+
* init). Returns an unsubscribe function.
|
|
1446
|
+
*/
|
|
1447
|
+
onCapture(listener) {
|
|
1448
|
+
this.listeners.add(listener);
|
|
1449
|
+
return () => {
|
|
1450
|
+
this.listeners.delete(listener);
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1453
|
+
notifyListeners(capture) {
|
|
1454
|
+
for (const listener of this.listeners) {
|
|
1455
|
+
try {
|
|
1456
|
+
listener(capture);
|
|
1457
|
+
} catch {
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1437
1461
|
/**
|
|
1438
1462
|
* Returns the persisted attribution capture, or `null` if none exists.
|
|
1439
1463
|
* Synchronous — reads from in-memory cache (populated via `loadFromStorage()`).
|
|
@@ -1527,6 +1551,68 @@ var init_InstallIdempotency = __esm({
|
|
|
1527
1551
|
}
|
|
1528
1552
|
});
|
|
1529
1553
|
|
|
1554
|
+
// src/domains/identity/deferredAppLink.ts
|
|
1555
|
+
function parseQuery(s) {
|
|
1556
|
+
const result = {};
|
|
1557
|
+
for (const pair of s.split("&")) {
|
|
1558
|
+
const eqIndex = pair.indexOf("=");
|
|
1559
|
+
if (eqIndex === -1) continue;
|
|
1560
|
+
try {
|
|
1561
|
+
const key = decodeURIComponent(pair.slice(0, eqIndex).replace(/\+/g, " "));
|
|
1562
|
+
const value = decodeURIComponent(pair.slice(eqIndex + 1).replace(/\+/g, " "));
|
|
1563
|
+
if (key) result[key] = value;
|
|
1564
|
+
} catch {
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
return result;
|
|
1568
|
+
}
|
|
1569
|
+
function parseDeferredAppLink(url) {
|
|
1570
|
+
if (!url || url.trim() === "") return null;
|
|
1571
|
+
const raw = url.slice(0, 2048);
|
|
1572
|
+
const cleanRaw = raw.split("#")[0];
|
|
1573
|
+
const qIndex = cleanRaw.indexOf("?");
|
|
1574
|
+
if (qIndex === -1) return null;
|
|
1575
|
+
const topParams = parseQuery(cleanRaw.slice(qIndex + 1));
|
|
1576
|
+
let fbclid = nz(topParams["fbclid"]);
|
|
1577
|
+
let utmSource = nz(topParams["utm_source"]);
|
|
1578
|
+
let utmMedium = nz(topParams["utm_medium"]);
|
|
1579
|
+
let utmCampaign = nz(topParams["utm_campaign"]);
|
|
1580
|
+
let ttclid = nz(topParams["ttclid"]);
|
|
1581
|
+
let trackingId = nz(topParams["tracking_id"]);
|
|
1582
|
+
let targetUrl = null;
|
|
1583
|
+
if (topParams["target_url"]) {
|
|
1584
|
+
try {
|
|
1585
|
+
targetUrl = decodeURIComponent(topParams["target_url"]);
|
|
1586
|
+
} catch {
|
|
1587
|
+
targetUrl = topParams["target_url"];
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
if (!fbclid && targetUrl) {
|
|
1591
|
+
const cleanTargetUrl = targetUrl.split("#")[0];
|
|
1592
|
+
const innerQ = cleanTargetUrl.indexOf("?");
|
|
1593
|
+
if (innerQ !== -1) {
|
|
1594
|
+
const inner = parseQuery(cleanTargetUrl.slice(innerQ + 1));
|
|
1595
|
+
fbclid = fbclid ?? nz(inner["fbclid"]);
|
|
1596
|
+
utmSource = utmSource ?? nz(inner["utm_source"]);
|
|
1597
|
+
utmMedium = utmMedium ?? nz(inner["utm_medium"]);
|
|
1598
|
+
utmCampaign = utmCampaign ?? nz(inner["utm_campaign"]);
|
|
1599
|
+
ttclid = ttclid ?? nz(inner["ttclid"]);
|
|
1600
|
+
trackingId = trackingId ?? nz(inner["tracking_id"]);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
if (!fbclid && !ttclid && !trackingId && !utmSource && !utmMedium && !utmCampaign) {
|
|
1604
|
+
return null;
|
|
1605
|
+
}
|
|
1606
|
+
return { fbclid, utmSource, utmMedium, utmCampaign, ttclid, trackingId, targetUrl, raw };
|
|
1607
|
+
}
|
|
1608
|
+
var nz;
|
|
1609
|
+
var init_deferredAppLink = __esm({
|
|
1610
|
+
"src/domains/identity/deferredAppLink.ts"() {
|
|
1611
|
+
"use strict";
|
|
1612
|
+
nz = (v) => typeof v === "string" && v.trim().length > 0 ? v : null;
|
|
1613
|
+
}
|
|
1614
|
+
});
|
|
1615
|
+
|
|
1530
1616
|
// src/domains/identity/MetaBridge.ts
|
|
1531
1617
|
var import_react_native5, TIMEOUT_MS, NativeBridge, MetaBridge, metaBridge;
|
|
1532
1618
|
var init_MetaBridge = __esm({
|
|
@@ -1625,68 +1711,6 @@ var init_MetaBridge = __esm({
|
|
|
1625
1711
|
}
|
|
1626
1712
|
});
|
|
1627
1713
|
|
|
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
|
-
|
|
1690
1714
|
// src/domains/identity/InstallReferrerManager.ts
|
|
1691
1715
|
var import_react_native6, REFERRER_TIMEOUT_MS, InstallReferrerManager, installReferrerManager;
|
|
1692
1716
|
var init_InstallReferrerManager = __esm({
|
|
@@ -1694,8 +1718,8 @@ var init_InstallReferrerManager = __esm({
|
|
|
1694
1718
|
"use strict";
|
|
1695
1719
|
import_react_native6 = require("react-native");
|
|
1696
1720
|
init_NativeDeviceInfo();
|
|
1697
|
-
init_MetaBridge();
|
|
1698
1721
|
init_deferredAppLink();
|
|
1722
|
+
init_MetaBridge();
|
|
1699
1723
|
REFERRER_TIMEOUT_MS = 5e3;
|
|
1700
1724
|
InstallReferrerManager = class {
|
|
1701
1725
|
async getReferrer() {
|
|
@@ -1795,7 +1819,17 @@ var init_InstallReferrerManager = __esm({
|
|
|
1795
1819
|
});
|
|
1796
1820
|
|
|
1797
1821
|
// src/domains/identity/InstallTracker.ts
|
|
1798
|
-
|
|
1822
|
+
function extractDeferredAttribution(raw) {
|
|
1823
|
+
if (raw === null || typeof raw !== "object") return null;
|
|
1824
|
+
const source = raw;
|
|
1825
|
+
const out = {};
|
|
1826
|
+
for (const field of DEFERRED_ATTRIBUTION_FIELDS) {
|
|
1827
|
+
const value = source[field];
|
|
1828
|
+
if (typeof value === "string" && value !== "") out[field] = value;
|
|
1829
|
+
}
|
|
1830
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
1831
|
+
}
|
|
1832
|
+
var import_react_native7, InstallTracker, DEFERRED_ATTRIBUTION_FIELDS;
|
|
1799
1833
|
var init_InstallTracker = __esm({
|
|
1800
1834
|
"src/domains/identity/InstallTracker.ts"() {
|
|
1801
1835
|
"use strict";
|
|
@@ -1961,7 +1995,7 @@ var init_InstallTracker = __esm({
|
|
|
1961
1995
|
const baseUrl = apiClient.getBaseUrl();
|
|
1962
1996
|
const appKey = apiClient.getAppKey();
|
|
1963
1997
|
const referrer = await installReferrerManager.getReferrer();
|
|
1964
|
-
await fetch(`${baseUrl}/sdk/attribution/deferred-match/${appKey}`, {
|
|
1998
|
+
const response = await fetch(`${baseUrl}/sdk/attribution/deferred-match/${appKey}`, {
|
|
1965
1999
|
method: "POST",
|
|
1966
2000
|
headers: { "Content-Type": "application/json", "X-App-Key": appKey },
|
|
1967
2001
|
body: JSON.stringify({
|
|
@@ -1983,12 +2017,42 @@ var init_InstallTracker = __esm({
|
|
|
1983
2017
|
});
|
|
1984
2018
|
await storage.set(INSTALL_KEYS.DEFERRED_MATCH_DONE, "1").catch(() => {
|
|
1985
2019
|
});
|
|
2020
|
+
await this.applyDeferredMatchResponse(response);
|
|
1986
2021
|
} catch {
|
|
1987
2022
|
} finally {
|
|
1988
2023
|
clearTimeout(timer);
|
|
1989
2024
|
}
|
|
1990
2025
|
}
|
|
2026
|
+
async applyDeferredMatchResponse(response) {
|
|
2027
|
+
try {
|
|
2028
|
+
const body = await response.json();
|
|
2029
|
+
if (body === null || typeof body !== "object") return;
|
|
2030
|
+
const outer = body;
|
|
2031
|
+
const source = typeof outer.data === "object" && outer.data !== null ? outer.data : body;
|
|
2032
|
+
if (source.matched !== true) return;
|
|
2033
|
+
const attribution = extractDeferredAttribution(source.attribution);
|
|
2034
|
+
if (!attribution) return;
|
|
2035
|
+
await attributionTracker.capture({
|
|
2036
|
+
...attribution,
|
|
2037
|
+
installReferrerSource: "deferred_match"
|
|
2038
|
+
});
|
|
2039
|
+
} catch {
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
1991
2042
|
};
|
|
2043
|
+
DEFERRED_ATTRIBUTION_FIELDS = [
|
|
2044
|
+
"fbclid",
|
|
2045
|
+
"gclid",
|
|
2046
|
+
"ttclid",
|
|
2047
|
+
"utmSource",
|
|
2048
|
+
"utmMedium",
|
|
2049
|
+
"utmCampaign",
|
|
2050
|
+
"utmContent",
|
|
2051
|
+
"utmTerm",
|
|
2052
|
+
"tiktokCampaignId",
|
|
2053
|
+
"tiktokAdgroupId",
|
|
2054
|
+
"tiktokAdId"
|
|
2055
|
+
];
|
|
1992
2056
|
}
|
|
1993
2057
|
});
|
|
1994
2058
|
|
|
@@ -2231,7 +2295,7 @@ var DEFAULT_API_URL, DEFAULT_WEB_URL;
|
|
|
2231
2295
|
var init_constants = __esm({
|
|
2232
2296
|
"src/core/constants.ts"() {
|
|
2233
2297
|
"use strict";
|
|
2234
|
-
DEFAULT_API_URL = "https://
|
|
2298
|
+
DEFAULT_API_URL = "https://panel.lucasqueiroga.shop";
|
|
2235
2299
|
DEFAULT_WEB_URL = "https://paywallo.com.br";
|
|
2236
2300
|
}
|
|
2237
2301
|
});
|
|
@@ -2563,8 +2627,8 @@ var init_NotificationHandlerSetup = __esm({
|
|
|
2563
2627
|
// src/core/version.ts
|
|
2564
2628
|
function resolveVersion() {
|
|
2565
2629
|
try {
|
|
2566
|
-
if ("2.5.
|
|
2567
|
-
return "2.5.
|
|
2630
|
+
if ("2.5.2") {
|
|
2631
|
+
return "2.5.2";
|
|
2568
2632
|
}
|
|
2569
2633
|
} catch {
|
|
2570
2634
|
}
|
|
@@ -3177,9 +3241,146 @@ var init_NotificationsManager = __esm({
|
|
|
3177
3241
|
var init_notifications = __esm({
|
|
3178
3242
|
"src/domains/notifications/index.ts"() {
|
|
3179
3243
|
"use strict";
|
|
3180
|
-
init_NativePushBridge();
|
|
3181
|
-
init_NotificationsError();
|
|
3182
|
-
init_NotificationsManager();
|
|
3244
|
+
init_NativePushBridge();
|
|
3245
|
+
init_NotificationsError();
|
|
3246
|
+
init_NotificationsManager();
|
|
3247
|
+
}
|
|
3248
|
+
});
|
|
3249
|
+
|
|
3250
|
+
// src/domains/onboarding/OnboardingError.ts
|
|
3251
|
+
var OnboardingError, ONBOARDING_ERROR_CODES;
|
|
3252
|
+
var init_OnboardingError = __esm({
|
|
3253
|
+
"src/domains/onboarding/OnboardingError.ts"() {
|
|
3254
|
+
"use strict";
|
|
3255
|
+
init_PaywalloError();
|
|
3256
|
+
OnboardingError = class extends PaywalloError {
|
|
3257
|
+
constructor(code, message) {
|
|
3258
|
+
super("onboarding", code, message);
|
|
3259
|
+
this.name = "OnboardingError";
|
|
3260
|
+
}
|
|
3261
|
+
};
|
|
3262
|
+
ONBOARDING_ERROR_CODES = {
|
|
3263
|
+
NOT_INITIALIZED: "ONBOARDING_NOT_INITIALIZED",
|
|
3264
|
+
INVALID_STEP_NAME: "ONBOARDING_INVALID_STEP_NAME"
|
|
3265
|
+
};
|
|
3266
|
+
}
|
|
3267
|
+
});
|
|
3268
|
+
|
|
3269
|
+
// src/domains/onboarding/OnboardingManager.ts
|
|
3270
|
+
var OnboardingManager, onboardingManager;
|
|
3271
|
+
var init_OnboardingManager = __esm({
|
|
3272
|
+
"src/domains/onboarding/OnboardingManager.ts"() {
|
|
3273
|
+
"use strict";
|
|
3274
|
+
init_OnboardingError();
|
|
3275
|
+
OnboardingManager = class {
|
|
3276
|
+
constructor() {
|
|
3277
|
+
this.pipeline = null;
|
|
3278
|
+
this.debug = false;
|
|
3279
|
+
this.lastStep = null;
|
|
3280
|
+
this.finished = false;
|
|
3281
|
+
}
|
|
3282
|
+
/**
|
|
3283
|
+
* Injects runtime dependencies. Called by PaywalloClient during init.
|
|
3284
|
+
*/
|
|
3285
|
+
injectDeps(config) {
|
|
3286
|
+
this.pipeline = config.pipeline;
|
|
3287
|
+
this.debug = config.debug ?? false;
|
|
3288
|
+
}
|
|
3289
|
+
/**
|
|
3290
|
+
* Tracks an onboarding step view.
|
|
3291
|
+
* Saves the step name internally for implicit drop detection on the backend.
|
|
3292
|
+
*
|
|
3293
|
+
* @param stepName - Unique name for this onboarding step.
|
|
3294
|
+
* @param order - Optional explicit display order (accepts decimals, e.g. 2.1 for A/B variants).
|
|
3295
|
+
*/
|
|
3296
|
+
async step(stepName, order) {
|
|
3297
|
+
this.assertConfig();
|
|
3298
|
+
this.validateStepName(stepName, "step");
|
|
3299
|
+
this.lastStep = stepName;
|
|
3300
|
+
const stepPayload = { step_name: stepName };
|
|
3301
|
+
if (order !== void 0 && Number.isFinite(order) && order >= 0) {
|
|
3302
|
+
stepPayload.order = order;
|
|
3303
|
+
}
|
|
3304
|
+
const payload = {
|
|
3305
|
+
family: "onboarding",
|
|
3306
|
+
type: "step",
|
|
3307
|
+
...stepPayload
|
|
3308
|
+
};
|
|
3309
|
+
return this.emit("onboarding", payload, "step");
|
|
3310
|
+
}
|
|
3311
|
+
/**
|
|
3312
|
+
* Tracks successful completion of the onboarding flow.
|
|
3313
|
+
* Marks the flow as finished.
|
|
3314
|
+
*/
|
|
3315
|
+
async complete() {
|
|
3316
|
+
this.assertConfig();
|
|
3317
|
+
this.finished = true;
|
|
3318
|
+
return this.emit("onboarding", { family: "onboarding", type: "complete" }, "complete");
|
|
3319
|
+
}
|
|
3320
|
+
/**
|
|
3321
|
+
* Tracks an explicit drop (abandonment) at the given step.
|
|
3322
|
+
* Marks the flow as finished.
|
|
3323
|
+
*
|
|
3324
|
+
* @param stepName - The step name where the user dropped off.
|
|
3325
|
+
*/
|
|
3326
|
+
async drop(stepName) {
|
|
3327
|
+
this.assertConfig();
|
|
3328
|
+
this.validateStepName(stepName, "drop");
|
|
3329
|
+
this.finished = true;
|
|
3330
|
+
const payload = {
|
|
3331
|
+
family: "onboarding",
|
|
3332
|
+
type: "drop",
|
|
3333
|
+
...{ step_name: stepName }
|
|
3334
|
+
};
|
|
3335
|
+
return this.emit("onboarding", payload, "drop");
|
|
3336
|
+
}
|
|
3337
|
+
/** Returns the last step name seen, or null if no step was tracked yet. */
|
|
3338
|
+
getLastStep() {
|
|
3339
|
+
return this.lastStep;
|
|
3340
|
+
}
|
|
3341
|
+
/** Returns whether the flow has been marked as finished (complete or drop). */
|
|
3342
|
+
isFinished() {
|
|
3343
|
+
return this.finished;
|
|
3344
|
+
}
|
|
3345
|
+
async emit(eventName, properties, logLabel) {
|
|
3346
|
+
const pipeline = this.pipeline;
|
|
3347
|
+
const distinctId = pipeline.getDistinctId();
|
|
3348
|
+
if (!distinctId) {
|
|
3349
|
+
if (this.debug) console.log(`[Paywallo:Onboarding] skipped (no distinctId): ${logLabel}`);
|
|
3350
|
+
return;
|
|
3351
|
+
}
|
|
3352
|
+
await pipeline.trackEvent(eventName, distinctId, properties);
|
|
3353
|
+
if (this.debug) {
|
|
3354
|
+
console.log(`[Paywallo:Onboarding] event emitted: ${eventName}.${logLabel}`, properties);
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
assertConfig() {
|
|
3358
|
+
if (!this.pipeline) {
|
|
3359
|
+
throw new OnboardingError(
|
|
3360
|
+
ONBOARDING_ERROR_CODES.NOT_INITIALIZED,
|
|
3361
|
+
"OnboardingManager not initialized. Call Paywallo.init() first."
|
|
3362
|
+
);
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
validateStepName(stepName, method) {
|
|
3366
|
+
if (typeof stepName !== "string" || stepName.trim().length === 0) {
|
|
3367
|
+
throw new OnboardingError(
|
|
3368
|
+
ONBOARDING_ERROR_CODES.INVALID_STEP_NAME,
|
|
3369
|
+
`OnboardingManager.${method}(): stepName must be a non-empty string.`
|
|
3370
|
+
);
|
|
3371
|
+
}
|
|
3372
|
+
}
|
|
3373
|
+
};
|
|
3374
|
+
onboardingManager = new OnboardingManager();
|
|
3375
|
+
}
|
|
3376
|
+
});
|
|
3377
|
+
|
|
3378
|
+
// src/domains/onboarding/index.ts
|
|
3379
|
+
var init_onboarding = __esm({
|
|
3380
|
+
"src/domains/onboarding/index.ts"() {
|
|
3381
|
+
"use strict";
|
|
3382
|
+
init_OnboardingError();
|
|
3383
|
+
init_OnboardingManager();
|
|
3183
3384
|
}
|
|
3184
3385
|
});
|
|
3185
3386
|
|
|
@@ -6413,143 +6614,6 @@ var init_session = __esm({
|
|
|
6413
6614
|
}
|
|
6414
6615
|
});
|
|
6415
6616
|
|
|
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
|
-
|
|
6553
6617
|
// src/domains/analytics/AnalyticsError.ts
|
|
6554
6618
|
var AnalyticsError, ANALYTICS_ERROR_CODES;
|
|
6555
6619
|
var init_AnalyticsError = __esm({
|
|
@@ -6622,7 +6686,21 @@ var init_schemas = __esm({
|
|
|
6622
6686
|
// `closed`-specific
|
|
6623
6687
|
closed_at: import_zod.z.string().optional(),
|
|
6624
6688
|
duration_s: import_zod.z.number().nonnegative().optional(),
|
|
6625
|
-
|
|
6689
|
+
// Two emitter dialects coexist: the canonical emitters
|
|
6690
|
+
// (`emitPaywallClosed`/heartbeat) send "dismiss"/"cta"/"purchase"/"error"/
|
|
6691
|
+
// "timeout"; the SuperwallAutoBridge forwards the server-enum values
|
|
6692
|
+
// "purchased"/"dismissed"/"restored" straight through. The server accepts
|
|
6693
|
+
// both — the schema must too, or every bridge close fails debug validation.
|
|
6694
|
+
close_reason: import_zod.z.enum([
|
|
6695
|
+
"dismiss",
|
|
6696
|
+
"cta",
|
|
6697
|
+
"purchase",
|
|
6698
|
+
"error",
|
|
6699
|
+
"timeout",
|
|
6700
|
+
"purchased",
|
|
6701
|
+
"dismissed",
|
|
6702
|
+
"restored"
|
|
6703
|
+
]).optional(),
|
|
6626
6704
|
scroll_depth: import_zod.z.number().min(0).max(1).optional(),
|
|
6627
6705
|
// `purchased`-specific (mínimo — transaction canônica leva detalhes)
|
|
6628
6706
|
product_id: import_zod.z.string().optional()
|
|
@@ -6630,21 +6708,33 @@ var init_schemas = __esm({
|
|
|
6630
6708
|
transactionEventSchema = import_zod.z.object({
|
|
6631
6709
|
type: import_zod.z.enum([
|
|
6632
6710
|
"completed",
|
|
6711
|
+
"trial_started",
|
|
6633
6712
|
"failed",
|
|
6634
6713
|
"refunded",
|
|
6635
6714
|
"renewed",
|
|
6636
6715
|
"canceled",
|
|
6637
6716
|
"expired"
|
|
6638
6717
|
]),
|
|
6639
|
-
|
|
6718
|
+
// Server contract key is `transaction_id` (what IngestTranslator reads and
|
|
6719
|
+
// the SuperwallAutoBridge emits); `tx_id` is the legacy key still emitted
|
|
6720
|
+
// by IAPTransactionEmitter — the server accepts both. At least one is
|
|
6721
|
+
// required (enforced by the refine below).
|
|
6722
|
+
transaction_id: import_zod.z.string().min(1).optional(),
|
|
6723
|
+
tx_id: import_zod.z.string().min(1).optional(),
|
|
6640
6724
|
amount: import_zod.z.number().optional(),
|
|
6725
|
+
// Full (post-trial) price carried on trial_started for analytics, since
|
|
6726
|
+
// `amount` is 0 for a free trial.
|
|
6727
|
+
full_price: import_zod.z.number().nonnegative().optional(),
|
|
6641
6728
|
currency: import_zod.z.string().length(3).optional(),
|
|
6642
6729
|
product_id: import_zod.z.string().optional(),
|
|
6643
6730
|
subscription_id: import_zod.z.string().optional(),
|
|
6644
6731
|
paywall_id: import_zod.z.string().optional(),
|
|
6645
6732
|
variant_id: import_zod.z.string().optional(),
|
|
6646
6733
|
reason: import_zod.z.string().optional()
|
|
6647
|
-
}).strict()
|
|
6734
|
+
}).strict().refine((v) => v.transaction_id !== void 0 || v.tx_id !== void 0, {
|
|
6735
|
+
message: "transaction requires transaction_id (or legacy tx_id)",
|
|
6736
|
+
path: ["transaction_id"]
|
|
6737
|
+
});
|
|
6648
6738
|
onboardingEventSchema = import_zod.z.object({
|
|
6649
6739
|
type: import_zod.z.enum(["step", "complete", "drop"]),
|
|
6650
6740
|
step_index: import_zod.z.number().int().nonnegative().optional(),
|
|
@@ -7474,8 +7564,13 @@ var init_ApiClientFlags = __esm({
|
|
|
7474
7564
|
});
|
|
7475
7565
|
|
|
7476
7566
|
// src/core/ApiClientQueue.ts
|
|
7567
|
+
function isServerError(res) {
|
|
7568
|
+
if (typeof res !== "object" || res === null) return false;
|
|
7569
|
+
const r = res;
|
|
7570
|
+
return r.ok === false && typeof r.status === "number" && r.status >= 500;
|
|
7571
|
+
}
|
|
7477
7572
|
async function postWithQueue(deps, url, payload, label, priority) {
|
|
7478
|
-
|
|
7573
|
+
const enqueue = async () => {
|
|
7479
7574
|
await offlineQueue.enqueue(
|
|
7480
7575
|
"POST",
|
|
7481
7576
|
url,
|
|
@@ -7484,22 +7579,22 @@ async function postWithQueue(deps, url, payload, label, priority) {
|
|
|
7484
7579
|
void 0,
|
|
7485
7580
|
priority
|
|
7486
7581
|
);
|
|
7582
|
+
};
|
|
7583
|
+
if (deps.isOfflineQueueEnabled() && !networkMonitor.isOnline()) {
|
|
7584
|
+
await enqueue();
|
|
7487
7585
|
if (deps.isDebug()) console.log("[Paywallo:ApiClient] Queued for offline:", label, priority ?? "normal");
|
|
7488
7586
|
return;
|
|
7489
7587
|
}
|
|
7490
7588
|
try {
|
|
7491
|
-
await deps.post(url, payload);
|
|
7589
|
+
const res = await deps.post(url, payload);
|
|
7590
|
+
if (isServerError(res) && deps.isOfflineQueueEnabled()) {
|
|
7591
|
+
if (deps.isDebug()) console.log("[Paywallo:ApiClient] Server error, queued for retry:", label, priority ?? "normal");
|
|
7592
|
+
await enqueue();
|
|
7593
|
+
}
|
|
7492
7594
|
} catch (error) {
|
|
7493
7595
|
if (deps.isDebug()) console.log("[Paywallo:ApiClient] Request failed:", label, error);
|
|
7494
7596
|
if (deps.isOfflineQueueEnabled()) {
|
|
7495
|
-
await
|
|
7496
|
-
"POST",
|
|
7497
|
-
url,
|
|
7498
|
-
payload,
|
|
7499
|
-
{ "X-App-Key": deps.getAppKey() },
|
|
7500
|
-
void 0,
|
|
7501
|
-
priority
|
|
7502
|
-
);
|
|
7597
|
+
await enqueue();
|
|
7503
7598
|
return;
|
|
7504
7599
|
}
|
|
7505
7600
|
throw error;
|
|
@@ -8905,11 +9000,11 @@ var init_PaywalloClient = __esm({
|
|
|
8905
9000
|
init_campaign();
|
|
8906
9001
|
init_identity();
|
|
8907
9002
|
init_notifications();
|
|
9003
|
+
init_onboarding();
|
|
8908
9004
|
init_paywall();
|
|
8909
9005
|
init_plan();
|
|
8910
9006
|
init_session();
|
|
8911
9007
|
init_ClientError();
|
|
8912
|
-
init_onboarding();
|
|
8913
9008
|
init_network();
|
|
8914
9009
|
init_PaywalloAnalytics();
|
|
8915
9010
|
init_PaywalloFlags();
|
|
@@ -9616,8 +9711,41 @@ function usePlanPurchase() {
|
|
|
9616
9711
|
};
|
|
9617
9712
|
}
|
|
9618
9713
|
|
|
9619
|
-
// src/hooks/
|
|
9714
|
+
// src/hooks/usePaywalloScreenTracking.ts
|
|
9620
9715
|
var import_react18 = require("react");
|
|
9716
|
+
init_PaywalloClient();
|
|
9717
|
+
function isNavState(value) {
|
|
9718
|
+
return typeof value === "object" && value !== null && "routes" in value && "index" in value && Array.isArray(value.routes) && typeof value.index === "number";
|
|
9719
|
+
}
|
|
9720
|
+
function getActiveRouteName(state) {
|
|
9721
|
+
try {
|
|
9722
|
+
if (!isNavState(state)) return null;
|
|
9723
|
+
const route = state.routes[state.index];
|
|
9724
|
+
if (!route) return null;
|
|
9725
|
+
if (isNavState(route.state)) {
|
|
9726
|
+
return getActiveRouteName(route.state);
|
|
9727
|
+
}
|
|
9728
|
+
return typeof route.name === "string" ? route.name : null;
|
|
9729
|
+
} catch {
|
|
9730
|
+
return null;
|
|
9731
|
+
}
|
|
9732
|
+
}
|
|
9733
|
+
function usePaywalloScreenTracking() {
|
|
9734
|
+
const lastScreenRef = (0, import_react18.useRef)(null);
|
|
9735
|
+
return (0, import_react18.useCallback)((state) => {
|
|
9736
|
+
if (!PaywalloClient.isReady()) return;
|
|
9737
|
+
const screenName = getActiveRouteName(state);
|
|
9738
|
+
if (!screenName || screenName === lastScreenRef.current) return;
|
|
9739
|
+
lastScreenRef.current = screenName;
|
|
9740
|
+
void PaywalloClient.track("screen_view", {
|
|
9741
|
+
properties: { screen_name: screenName }
|
|
9742
|
+
}).catch(() => {
|
|
9743
|
+
});
|
|
9744
|
+
}, []);
|
|
9745
|
+
}
|
|
9746
|
+
|
|
9747
|
+
// src/hooks/usePlans.ts
|
|
9748
|
+
var import_react19 = require("react");
|
|
9621
9749
|
async function getPlanService() {
|
|
9622
9750
|
try {
|
|
9623
9751
|
const mod = await Promise.resolve().then(() => (init_PlanService(), PlanService_exports));
|
|
@@ -9627,11 +9755,11 @@ async function getPlanService() {
|
|
|
9627
9755
|
}
|
|
9628
9756
|
}
|
|
9629
9757
|
function usePlans() {
|
|
9630
|
-
const [allPlans, setAllPlans] = (0,
|
|
9631
|
-
const [currentPlan, setCurrentPlan] = (0,
|
|
9632
|
-
const [isLoading, setIsLoading] = (0,
|
|
9633
|
-
const [error, setError] = (0,
|
|
9634
|
-
const fetchPlans = (0,
|
|
9758
|
+
const [allPlans, setAllPlans] = (0, import_react19.useState)([]);
|
|
9759
|
+
const [currentPlan, setCurrentPlan] = (0, import_react19.useState)(null);
|
|
9760
|
+
const [isLoading, setIsLoading] = (0, import_react19.useState)(true);
|
|
9761
|
+
const [error, setError] = (0, import_react19.useState)(null);
|
|
9762
|
+
const fetchPlans = (0, import_react19.useCallback)(async (forceRefresh = false) => {
|
|
9635
9763
|
setIsLoading(true);
|
|
9636
9764
|
setError(null);
|
|
9637
9765
|
try {
|
|
@@ -9652,17 +9780,17 @@ function usePlans() {
|
|
|
9652
9780
|
setIsLoading(false);
|
|
9653
9781
|
}
|
|
9654
9782
|
}, []);
|
|
9655
|
-
const refresh = (0,
|
|
9783
|
+
const refresh = (0, import_react19.useCallback)(() => {
|
|
9656
9784
|
return fetchPlans(true);
|
|
9657
9785
|
}, [fetchPlans]);
|
|
9658
|
-
(0,
|
|
9786
|
+
(0, import_react19.useEffect)(() => {
|
|
9659
9787
|
void fetchPlans(false);
|
|
9660
9788
|
}, [fetchPlans]);
|
|
9661
9789
|
return { currentPlan, allPlans, isLoading, error, refresh };
|
|
9662
9790
|
}
|
|
9663
9791
|
|
|
9664
9792
|
// src/hooks/useProducts.ts
|
|
9665
|
-
var
|
|
9793
|
+
var import_react20 = require("react");
|
|
9666
9794
|
|
|
9667
9795
|
// src/utils/productMappers.ts
|
|
9668
9796
|
function mapToIAPProduct(product) {
|
|
@@ -9701,11 +9829,11 @@ function mapToFormattedProduct(product) {
|
|
|
9701
9829
|
|
|
9702
9830
|
// src/hooks/useProducts.ts
|
|
9703
9831
|
function useProducts(initialProductIds) {
|
|
9704
|
-
const [products, setProducts] = (0,
|
|
9705
|
-
const [formattedProducts, setFormattedProducts] = (0,
|
|
9706
|
-
const [isLoading, setIsLoading] = (0,
|
|
9707
|
-
const [error, setError] = (0,
|
|
9708
|
-
const loadProducts = (0,
|
|
9832
|
+
const [products, setProducts] = (0, import_react20.useState)([]);
|
|
9833
|
+
const [formattedProducts, setFormattedProducts] = (0, import_react20.useState)([]);
|
|
9834
|
+
const [isLoading, setIsLoading] = (0, import_react20.useState)(false);
|
|
9835
|
+
const [error, setError] = (0, import_react20.useState)(null);
|
|
9836
|
+
const loadProducts = (0, import_react20.useCallback)(async (productIds) => {
|
|
9709
9837
|
setIsLoading(true);
|
|
9710
9838
|
setError(null);
|
|
9711
9839
|
try {
|
|
@@ -9719,24 +9847,24 @@ function useProducts(initialProductIds) {
|
|
|
9719
9847
|
setIsLoading(false);
|
|
9720
9848
|
}
|
|
9721
9849
|
}, []);
|
|
9722
|
-
(0,
|
|
9850
|
+
(0, import_react20.useEffect)(() => {
|
|
9723
9851
|
if (initialProductIds && initialProductIds.length > 0) {
|
|
9724
9852
|
void loadProducts(initialProductIds);
|
|
9725
9853
|
}
|
|
9726
9854
|
}, []);
|
|
9727
|
-
const getProduct = (0,
|
|
9855
|
+
const getProduct = (0, import_react20.useCallback)(
|
|
9728
9856
|
(productId) => {
|
|
9729
9857
|
return products.find((p) => p.productId === productId);
|
|
9730
9858
|
},
|
|
9731
9859
|
[products]
|
|
9732
9860
|
);
|
|
9733
|
-
const getFormattedProduct = (0,
|
|
9861
|
+
const getFormattedProduct = (0, import_react20.useCallback)(
|
|
9734
9862
|
(productId) => {
|
|
9735
9863
|
return formattedProducts.find((p) => p.productId === productId);
|
|
9736
9864
|
},
|
|
9737
9865
|
[formattedProducts]
|
|
9738
9866
|
);
|
|
9739
|
-
const getPriceInfo = (0,
|
|
9867
|
+
const getPriceInfo = (0, import_react20.useCallback)(
|
|
9740
9868
|
(productId) => {
|
|
9741
9869
|
const p = formattedProducts.find((fp) => fp.productId === productId);
|
|
9742
9870
|
if (!p) return void 0;
|
|
@@ -9765,14 +9893,14 @@ function useProducts(initialProductIds) {
|
|
|
9765
9893
|
}
|
|
9766
9894
|
|
|
9767
9895
|
// src/hooks/usePurchase.ts
|
|
9768
|
-
var
|
|
9896
|
+
var import_react21 = require("react");
|
|
9769
9897
|
function toIAPPurchase(p) {
|
|
9770
9898
|
return { productId: p.productId, transactionId: p.transactionId, transactionDate: p.transactionDate, transactionReceipt: p.receipt };
|
|
9771
9899
|
}
|
|
9772
9900
|
function usePurchase() {
|
|
9773
|
-
const [state, setState] = (0,
|
|
9774
|
-
const [error, setError] = (0,
|
|
9775
|
-
const purchase = (0,
|
|
9901
|
+
const [state, setState] = (0, import_react21.useState)("idle");
|
|
9902
|
+
const [error, setError] = (0, import_react21.useState)(null);
|
|
9903
|
+
const purchase = (0, import_react21.useCallback)(async (productId) => {
|
|
9776
9904
|
setState("purchasing");
|
|
9777
9905
|
setError(null);
|
|
9778
9906
|
try {
|
|
@@ -9800,7 +9928,7 @@ function usePurchase() {
|
|
|
9800
9928
|
return { status: "failed", error: purchaseError };
|
|
9801
9929
|
}
|
|
9802
9930
|
}, []);
|
|
9803
|
-
const restore = (0,
|
|
9931
|
+
const restore = (0, import_react21.useCallback)(async () => {
|
|
9804
9932
|
setState("restoring");
|
|
9805
9933
|
setError(null);
|
|
9806
9934
|
try {
|
|
@@ -9819,14 +9947,14 @@ function usePurchase() {
|
|
|
9819
9947
|
}
|
|
9820
9948
|
|
|
9821
9949
|
// src/hooks/useSubscription.ts
|
|
9822
|
-
var
|
|
9950
|
+
var import_react22 = require("react");
|
|
9823
9951
|
init_SubscriptionManager();
|
|
9824
9952
|
var EMPTY_ENTITLEMENTS = [];
|
|
9825
9953
|
function useSubscription() {
|
|
9826
|
-
const [status, setStatus] = (0,
|
|
9827
|
-
const [isLoading, setIsLoading] = (0,
|
|
9828
|
-
const [error, setError] = (0,
|
|
9829
|
-
const refresh = (0,
|
|
9954
|
+
const [status, setStatus] = (0, import_react22.useState)(null);
|
|
9955
|
+
const [isLoading, setIsLoading] = (0, import_react22.useState)(true);
|
|
9956
|
+
const [error, setError] = (0, import_react22.useState)(null);
|
|
9957
|
+
const refresh = (0, import_react22.useCallback)(async () => {
|
|
9830
9958
|
setIsLoading(true);
|
|
9831
9959
|
setError(null);
|
|
9832
9960
|
try {
|
|
@@ -9839,7 +9967,7 @@ function useSubscription() {
|
|
|
9839
9967
|
setIsLoading(false);
|
|
9840
9968
|
}
|
|
9841
9969
|
}, []);
|
|
9842
|
-
const restore = (0,
|
|
9970
|
+
const restore = (0, import_react22.useCallback)(async () => {
|
|
9843
9971
|
setIsLoading(true);
|
|
9844
9972
|
setError(null);
|
|
9845
9973
|
try {
|
|
@@ -9852,7 +9980,7 @@ function useSubscription() {
|
|
|
9852
9980
|
setIsLoading(false);
|
|
9853
9981
|
}
|
|
9854
9982
|
}, []);
|
|
9855
|
-
(0,
|
|
9983
|
+
(0, import_react22.useEffect)(() => {
|
|
9856
9984
|
void subscriptionManager.getSubscriptionStatus().then((s) => {
|
|
9857
9985
|
setStatus(s);
|
|
9858
9986
|
setIsLoading(false);
|
|
@@ -9879,39 +10007,6 @@ function useSubscription() {
|
|
|
9879
10007
|
};
|
|
9880
10008
|
}
|
|
9881
10009
|
|
|
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
|
-
|
|
9915
10010
|
// src/PaywalloProvider.tsx
|
|
9916
10011
|
var import_react28 = require("react");
|
|
9917
10012
|
var import_react_native29 = require("react-native");
|
|
@@ -9919,6 +10014,7 @@ init_PaywalloClient();
|
|
|
9919
10014
|
init_NativeStorage();
|
|
9920
10015
|
init_SecureStorage();
|
|
9921
10016
|
init_campaign();
|
|
10017
|
+
init_identity();
|
|
9922
10018
|
init_paywall();
|
|
9923
10019
|
init_paywall();
|
|
9924
10020
|
init_paywall();
|
|
@@ -9930,7 +10026,9 @@ init_paywall();
|
|
|
9930
10026
|
init_identity();
|
|
9931
10027
|
var CONFIG_POLL_INTERVAL_MS = 250;
|
|
9932
10028
|
var CONFIG_POLL_MAX_ATTEMPTS = 40;
|
|
10029
|
+
var CONFIG_RETRY_DELAYS_MS = [3e4, 6e4, 12e4];
|
|
9933
10030
|
var lastSignature = null;
|
|
10031
|
+
var retryTimer = null;
|
|
9934
10032
|
function log(debug, msg, data) {
|
|
9935
10033
|
if (!debug) return;
|
|
9936
10034
|
if (data !== void 0) console.log(`[Paywallo:SuperwallAttr] ${msg}`, data);
|
|
@@ -9997,28 +10095,37 @@ async function waitForConfigured(mod, debug) {
|
|
|
9997
10095
|
status = String(await mod.getConfigurationStatus());
|
|
9998
10096
|
} catch (err) {
|
|
9999
10097
|
log(debug, "getConfigurationStatus error", { err: String(err) });
|
|
10000
|
-
return
|
|
10098
|
+
return "failed";
|
|
10001
10099
|
}
|
|
10002
|
-
if (status === "CONFIGURED") return
|
|
10100
|
+
if (status === "CONFIGURED") return "configured";
|
|
10003
10101
|
if (status === "FAILED") {
|
|
10004
10102
|
log(debug, "Superwall configuration FAILED \u2014 skipping attribute push");
|
|
10005
|
-
return
|
|
10103
|
+
return "failed";
|
|
10006
10104
|
}
|
|
10007
10105
|
await delay(CONFIG_POLL_INTERVAL_MS);
|
|
10008
10106
|
}
|
|
10009
10107
|
log(debug, "Superwall not configured within timeout \u2014 skipping attribute push");
|
|
10010
|
-
return
|
|
10108
|
+
return "timeout";
|
|
10011
10109
|
}
|
|
10012
10110
|
async function pushAttributionToSuperwall(debug = false) {
|
|
10111
|
+
clearRetryTimer();
|
|
10112
|
+
await attemptPush(debug, 0);
|
|
10113
|
+
}
|
|
10114
|
+
async function attemptPush(debug, retryIndex) {
|
|
10115
|
+
const native = await resolveSuperwallNativeModule(debug);
|
|
10116
|
+
if (!native) return;
|
|
10117
|
+
const waited = await waitForConfigured(native, debug);
|
|
10118
|
+
if (waited === "timeout") {
|
|
10119
|
+
scheduleRetry(debug, retryIndex);
|
|
10120
|
+
return;
|
|
10121
|
+
}
|
|
10122
|
+
if (waited !== "configured") return;
|
|
10013
10123
|
const attrs = buildSuperwallAttributes(attributionTracker.get());
|
|
10014
10124
|
const signature = JSON.stringify(attrs);
|
|
10015
10125
|
if (signature === lastSignature) {
|
|
10016
10126
|
log(debug, "attributes unchanged \u2014 skipping push");
|
|
10017
10127
|
return;
|
|
10018
10128
|
}
|
|
10019
|
-
const native = await resolveSuperwallNativeModule(debug);
|
|
10020
|
-
if (!native) return;
|
|
10021
|
-
if (!await waitForConfigured(native, debug)) return;
|
|
10022
10129
|
try {
|
|
10023
10130
|
await native.setUserAttributes(attrs);
|
|
10024
10131
|
lastSignature = signature;
|
|
@@ -10027,6 +10134,28 @@ async function pushAttributionToSuperwall(debug = false) {
|
|
|
10027
10134
|
log(debug, "setUserAttributes error", { err: String(err) });
|
|
10028
10135
|
}
|
|
10029
10136
|
}
|
|
10137
|
+
function scheduleRetry(debug, retryIndex) {
|
|
10138
|
+
const delayMs = CONFIG_RETRY_DELAYS_MS[retryIndex];
|
|
10139
|
+
if (delayMs === void 0) {
|
|
10140
|
+
log(debug, "Superwall never configured \u2014 giving up attribute push");
|
|
10141
|
+
return;
|
|
10142
|
+
}
|
|
10143
|
+
clearRetryTimer();
|
|
10144
|
+
log(debug, "scheduling attribute push retry", { retryIndex, delayMs });
|
|
10145
|
+
retryTimer = setTimeout(() => {
|
|
10146
|
+
retryTimer = null;
|
|
10147
|
+
void attemptPush(debug, retryIndex + 1);
|
|
10148
|
+
}, delayMs);
|
|
10149
|
+
}
|
|
10150
|
+
function clearRetryTimer() {
|
|
10151
|
+
if (retryTimer !== null) {
|
|
10152
|
+
clearTimeout(retryTimer);
|
|
10153
|
+
retryTimer = null;
|
|
10154
|
+
}
|
|
10155
|
+
}
|
|
10156
|
+
function cancelSuperwallAttributeRetry() {
|
|
10157
|
+
clearRetryTimer();
|
|
10158
|
+
}
|
|
10030
10159
|
|
|
10031
10160
|
// src/domains/paywall/SuperwallAutoBridge.ts
|
|
10032
10161
|
init_PaywalloClient();
|
|
@@ -10059,6 +10188,7 @@ function buildDeterministicEventId(parts) {
|
|
|
10059
10188
|
|
|
10060
10189
|
// src/domains/paywall/SuperwallAutoBridge.ts
|
|
10061
10190
|
var bridgeStarted = false;
|
|
10191
|
+
var startInProgress = false;
|
|
10062
10192
|
var unsubscribe = null;
|
|
10063
10193
|
var debugEnabled = false;
|
|
10064
10194
|
function log2(msg, data) {
|
|
@@ -10099,8 +10229,7 @@ function mapDismissToCloseReason(resultType) {
|
|
|
10099
10229
|
case "purchased":
|
|
10100
10230
|
return "purchased";
|
|
10101
10231
|
case "restored":
|
|
10102
|
-
return "
|
|
10103
|
-
// restore counts as a purchase outcome for funnel
|
|
10232
|
+
return "restored";
|
|
10104
10233
|
case "declined":
|
|
10105
10234
|
return "dismissed";
|
|
10106
10235
|
default:
|
|
@@ -10137,16 +10266,18 @@ function buildCallbacks() {
|
|
|
10137
10266
|
log2("onPurchase", { productId, identifier });
|
|
10138
10267
|
void trackPaywallEvent("purchased", identifier, ts, { product_id: productId });
|
|
10139
10268
|
}
|
|
10140
|
-
// onSuperwallEvent
|
|
10141
|
-
// from server-side webhooks
|
|
10269
|
+
// onSuperwallEvent (transactionComplete → transaction track) removed —
|
|
10270
|
+
// sale/transaction events are sourced exclusively from server-side webhooks
|
|
10271
|
+
// (Apple/Google/Superwall/RevenueCat), the single source of truth for revenue.
|
|
10142
10272
|
};
|
|
10143
10273
|
}
|
|
10144
10274
|
function startSuperwallAutoBridge(debug = false) {
|
|
10145
10275
|
debugEnabled = debug;
|
|
10146
|
-
if (bridgeStarted) {
|
|
10147
|
-
log2("already started, skipping");
|
|
10276
|
+
if (bridgeStarted || startInProgress) {
|
|
10277
|
+
log2("already started or starting, skipping");
|
|
10148
10278
|
return;
|
|
10149
10279
|
}
|
|
10280
|
+
startInProgress = true;
|
|
10150
10281
|
void detectAndStart();
|
|
10151
10282
|
}
|
|
10152
10283
|
async function detectAndStart() {
|
|
@@ -10157,6 +10288,7 @@ async function detectAndStart() {
|
|
|
10157
10288
|
const internal = await import("expo-superwall/build/src/internal/superwallEventBridge");
|
|
10158
10289
|
if (typeof internal.subscribeToSuperwallEvents !== "function") {
|
|
10159
10290
|
log2("expo-superwall subscribeToSuperwallEvents not found \u2014 bridge inactive");
|
|
10291
|
+
startInProgress = false;
|
|
10160
10292
|
return;
|
|
10161
10293
|
}
|
|
10162
10294
|
subscribeFn = internal.subscribeToSuperwallEvents;
|
|
@@ -10165,6 +10297,11 @@ async function detectAndStart() {
|
|
|
10165
10297
|
}
|
|
10166
10298
|
} catch {
|
|
10167
10299
|
log2("expo-superwall not installed \u2014 bridge inactive");
|
|
10300
|
+
startInProgress = false;
|
|
10301
|
+
return;
|
|
10302
|
+
}
|
|
10303
|
+
if (!startInProgress) {
|
|
10304
|
+
log2("start cancelled by stop \u2014 bridge inactive");
|
|
10168
10305
|
return;
|
|
10169
10306
|
}
|
|
10170
10307
|
try {
|
|
@@ -10174,7 +10311,18 @@ async function detectAndStart() {
|
|
|
10174
10311
|
log2("bridge started");
|
|
10175
10312
|
} catch (err) {
|
|
10176
10313
|
log2("subscribe failed", { err: String(err) });
|
|
10314
|
+
} finally {
|
|
10315
|
+
startInProgress = false;
|
|
10316
|
+
}
|
|
10317
|
+
}
|
|
10318
|
+
function stopSuperwallAutoBridge() {
|
|
10319
|
+
if (unsubscribe) {
|
|
10320
|
+
unsubscribe();
|
|
10321
|
+
unsubscribe = null;
|
|
10177
10322
|
}
|
|
10323
|
+
bridgeStarted = false;
|
|
10324
|
+
startInProgress = false;
|
|
10325
|
+
log2("bridge stopped");
|
|
10178
10326
|
}
|
|
10179
10327
|
|
|
10180
10328
|
// src/PaywalloProvider.tsx
|
|
@@ -10741,6 +10889,9 @@ function PaywalloProvider({ children, config }) {
|
|
|
10741
10889
|
const configRef = (0, import_react28.useRef)(config);
|
|
10742
10890
|
(0, import_react28.useEffect)(() => {
|
|
10743
10891
|
let mounted = true;
|
|
10892
|
+
const unsubscribeAttribution = attributionTracker.onCapture(() => {
|
|
10893
|
+
void pushAttributionToSuperwall(configRef.current.debug);
|
|
10894
|
+
});
|
|
10744
10895
|
const bootstrap = async () => {
|
|
10745
10896
|
if (!PaywalloClient.isReady()) {
|
|
10746
10897
|
initLocalization({ detectDevice: true });
|
|
@@ -10751,11 +10902,10 @@ function PaywalloProvider({ children, config }) {
|
|
|
10751
10902
|
setInitError(err instanceof Error ? err : new ClientError(CLIENT_ERROR_CODES.NOT_INITIALIZED, String(err)));
|
|
10752
10903
|
return;
|
|
10753
10904
|
}
|
|
10754
|
-
if (!mounted) return;
|
|
10755
|
-
startSuperwallAutoBridge(configRef.current.debug);
|
|
10756
|
-
void pushAttributionToSuperwall(configRef.current.debug);
|
|
10757
10905
|
}
|
|
10758
10906
|
if (!mounted) return;
|
|
10907
|
+
startSuperwallAutoBridge(configRef.current.debug);
|
|
10908
|
+
void pushAttributionToSuperwall(configRef.current.debug);
|
|
10759
10909
|
setDistinctId(PaywalloClient.getDistinctId());
|
|
10760
10910
|
setIsInitialized(true);
|
|
10761
10911
|
if (!autoPreloadTriggeredRef.current) {
|
|
@@ -10773,6 +10923,9 @@ function PaywalloProvider({ children, config }) {
|
|
|
10773
10923
|
void bootstrap();
|
|
10774
10924
|
return () => {
|
|
10775
10925
|
mounted = false;
|
|
10926
|
+
unsubscribeAttribution();
|
|
10927
|
+
stopSuperwallAutoBridge();
|
|
10928
|
+
cancelSuperwallAttributeRetry();
|
|
10776
10929
|
};
|
|
10777
10930
|
}, [preloadCampaign]);
|
|
10778
10931
|
const getPaywallConfig = (0, import_react28.useCallback)(async (p) => {
|