@virex-tech/paywallo-sdk 1.4.1 → 1.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/android/src/main/java/com/paywallo/sdk/PaywalloStoreKitModule.kt +17 -7
- package/dist/index.d.mts +12 -107
- package/dist/index.d.ts +12 -107
- package/dist/index.js +247 -273
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +243 -264
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -231,6 +231,7 @@ var SecureStorage = class {
|
|
|
231
231
|
var secureStorage = new SecureStorage();
|
|
232
232
|
|
|
233
233
|
// src/domains/identity/IdentityManager.ts
|
|
234
|
+
var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
234
235
|
var DEVICE_ID_KEY = "device_id";
|
|
235
236
|
var ANON_ID_KEY = "anon_id";
|
|
236
237
|
var USER_EMAIL_KEY = "user_email";
|
|
@@ -287,8 +288,15 @@ var IdentityManager = class {
|
|
|
287
288
|
}
|
|
288
289
|
}
|
|
289
290
|
if (email) {
|
|
290
|
-
|
|
291
|
-
|
|
291
|
+
if (!EMAIL_REGEX.test(email)) {
|
|
292
|
+
if (this.debug) {
|
|
293
|
+
console.warn("[Paywallo] Invalid email format, skipping email field");
|
|
294
|
+
}
|
|
295
|
+
email = void 0;
|
|
296
|
+
} else {
|
|
297
|
+
this.email = email;
|
|
298
|
+
await this.secureStorage.set(USER_EMAIL_KEY, email);
|
|
299
|
+
}
|
|
292
300
|
}
|
|
293
301
|
if (properties) {
|
|
294
302
|
this.properties = { ...this.properties, ...properties };
|
|
@@ -1502,227 +1510,6 @@ function usePaywallPresenter(preloadedWebView, setPreloadedWebView) {
|
|
|
1502
1510
|
return { showingPreloadedWebView, presentPreloadedWebView, resolvePreloadedPaywall, handlePreloadedWebViewReady, handlePreloadedClose, setShowingPreloadedWebView, presentedAtRef };
|
|
1503
1511
|
}
|
|
1504
1512
|
|
|
1505
|
-
// src/domains/affiliate/AffiliateError.ts
|
|
1506
|
-
var AffiliateError = class extends PaywalloError {
|
|
1507
|
-
constructor(code, message) {
|
|
1508
|
-
super("affiliate", code, message);
|
|
1509
|
-
this.name = "AffiliateError";
|
|
1510
|
-
}
|
|
1511
|
-
};
|
|
1512
|
-
var AFFILIATE_ERROR_CODES = {
|
|
1513
|
-
NOT_INITIALIZED: "AFFILIATE_NOT_INITIALIZED",
|
|
1514
|
-
COUPON_REGISTER_FAILED: "AFFILIATE_COUPON_REGISTER_FAILED",
|
|
1515
|
-
COUPON_APPLY_FAILED: "AFFILIATE_COUPON_APPLY_FAILED",
|
|
1516
|
-
BALANCE_FETCH_FAILED: "AFFILIATE_BALANCE_FETCH_FAILED",
|
|
1517
|
-
WITHDRAWAL_FAILED: "AFFILIATE_WITHDRAWAL_FAILED",
|
|
1518
|
-
NETWORK_ERROR: "AFFILIATE_NETWORK_ERROR"
|
|
1519
|
-
};
|
|
1520
|
-
|
|
1521
|
-
// src/domains/affiliate/affiliateHttp.ts
|
|
1522
|
-
function buildQueryString(params) {
|
|
1523
|
-
if (!params) return "";
|
|
1524
|
-
const entries = Object.entries(params).filter(([, v]) => v !== void 0 && v !== null);
|
|
1525
|
-
if (entries.length === 0) return "";
|
|
1526
|
-
return `?${entries.map(([k, v]) => `${k}=${encodeURIComponent(String(v))}`).join("&")}`;
|
|
1527
|
-
}
|
|
1528
|
-
async function affiliateGet(apiClient, path, params) {
|
|
1529
|
-
const response = await apiClient.get(`${path}${buildQueryString(params)}`);
|
|
1530
|
-
return response.data;
|
|
1531
|
-
}
|
|
1532
|
-
async function affiliatePost(apiClient, path, body) {
|
|
1533
|
-
const response = await apiClient.post(path, body);
|
|
1534
|
-
return response.data;
|
|
1535
|
-
}
|
|
1536
|
-
|
|
1537
|
-
// src/domains/affiliate/affiliateDateMappers.ts
|
|
1538
|
-
function mapRawCoupon(raw) {
|
|
1539
|
-
return { code: raw.code, status: raw.status, createdAt: new Date(raw.createdAt) };
|
|
1540
|
-
}
|
|
1541
|
-
function mapRawCommission(raw) {
|
|
1542
|
-
return { ...raw, createdAt: new Date(raw.createdAt) };
|
|
1543
|
-
}
|
|
1544
|
-
function mapRawWithdrawal(raw) {
|
|
1545
|
-
return { ...raw, createdAt: new Date(raw.createdAt) };
|
|
1546
|
-
}
|
|
1547
|
-
|
|
1548
|
-
// src/domains/affiliate/AffiliateManager.ts
|
|
1549
|
-
var AffiliateManager = class {
|
|
1550
|
-
constructor() {
|
|
1551
|
-
this.apiClient = null;
|
|
1552
|
-
this.debug = false;
|
|
1553
|
-
}
|
|
1554
|
-
initialize(apiClient, debug = false) {
|
|
1555
|
-
this.apiClient = apiClient;
|
|
1556
|
-
this.debug = debug;
|
|
1557
|
-
this.log("AffiliateManager initialized");
|
|
1558
|
-
}
|
|
1559
|
-
async registerCoupon(code) {
|
|
1560
|
-
this.ensureInitialized();
|
|
1561
|
-
const distinctId = identityManager.getDistinctId();
|
|
1562
|
-
try {
|
|
1563
|
-
const response = await this.post("/api/v1/affiliate/register-coupon", {
|
|
1564
|
-
code,
|
|
1565
|
-
distinctId
|
|
1566
|
-
});
|
|
1567
|
-
if (response.success && response.coupon) {
|
|
1568
|
-
this.log("Coupon registered:", code);
|
|
1569
|
-
return { success: true, coupon: mapRawCoupon(response.coupon) };
|
|
1570
|
-
}
|
|
1571
|
-
return {
|
|
1572
|
-
success: false,
|
|
1573
|
-
error: response.error
|
|
1574
|
-
};
|
|
1575
|
-
} catch (error) {
|
|
1576
|
-
this.log("Failed to register coupon:", error);
|
|
1577
|
-
return {
|
|
1578
|
-
success: false,
|
|
1579
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
1580
|
-
};
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1583
|
-
async applyCoupon(code) {
|
|
1584
|
-
this.ensureInitialized();
|
|
1585
|
-
const distinctId = identityManager.getDistinctId();
|
|
1586
|
-
try {
|
|
1587
|
-
const response = await this.post("/api/v1/affiliate/apply-coupon", {
|
|
1588
|
-
code,
|
|
1589
|
-
distinctId
|
|
1590
|
-
});
|
|
1591
|
-
if (response.success) {
|
|
1592
|
-
this.log("Coupon applied:", code);
|
|
1593
|
-
}
|
|
1594
|
-
return response;
|
|
1595
|
-
} catch (error) {
|
|
1596
|
-
this.log("Failed to apply coupon:", error);
|
|
1597
|
-
return {
|
|
1598
|
-
success: false,
|
|
1599
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
1600
|
-
};
|
|
1601
|
-
}
|
|
1602
|
-
}
|
|
1603
|
-
async getMyCoupon() {
|
|
1604
|
-
this.ensureInitialized();
|
|
1605
|
-
const distinctId = identityManager.getDistinctId();
|
|
1606
|
-
try {
|
|
1607
|
-
const response = await this.get("/api/v1/affiliate/my-coupon", { distinctId });
|
|
1608
|
-
if (response.coupon) {
|
|
1609
|
-
return { coupon: mapRawCoupon(response.coupon) };
|
|
1610
|
-
}
|
|
1611
|
-
return { coupon: null };
|
|
1612
|
-
} catch (error) {
|
|
1613
|
-
this.log("Failed to get my coupon:", error);
|
|
1614
|
-
return { coupon: null };
|
|
1615
|
-
}
|
|
1616
|
-
}
|
|
1617
|
-
async getAppliedCoupon() {
|
|
1618
|
-
this.ensureInitialized();
|
|
1619
|
-
const distinctId = identityManager.getDistinctId();
|
|
1620
|
-
try {
|
|
1621
|
-
const response = await this.get("/api/v1/affiliate/applied-coupon", { distinctId });
|
|
1622
|
-
return response;
|
|
1623
|
-
} catch (error) {
|
|
1624
|
-
this.log("Failed to get applied coupon:", error);
|
|
1625
|
-
return { coupon: null };
|
|
1626
|
-
}
|
|
1627
|
-
}
|
|
1628
|
-
async getBalance() {
|
|
1629
|
-
this.ensureInitialized();
|
|
1630
|
-
const distinctId = identityManager.getDistinctId();
|
|
1631
|
-
try {
|
|
1632
|
-
const response = await this.get("/api/v1/affiliate/balance", {
|
|
1633
|
-
distinctId
|
|
1634
|
-
});
|
|
1635
|
-
return response;
|
|
1636
|
-
} catch (error) {
|
|
1637
|
-
this.log("Failed to get balance:", error);
|
|
1638
|
-
return {
|
|
1639
|
-
total: 0,
|
|
1640
|
-
pending: 0,
|
|
1641
|
-
available: 0,
|
|
1642
|
-
withdrawn: 0
|
|
1643
|
-
};
|
|
1644
|
-
}
|
|
1645
|
-
}
|
|
1646
|
-
async getCommissions() {
|
|
1647
|
-
this.ensureInitialized();
|
|
1648
|
-
const distinctId = identityManager.getDistinctId();
|
|
1649
|
-
try {
|
|
1650
|
-
const response = await this.get("/api/v1/affiliate/commissions", { distinctId });
|
|
1651
|
-
return response.commissions.map(mapRawCommission);
|
|
1652
|
-
} catch (error) {
|
|
1653
|
-
this.log("Failed to get commissions:", error);
|
|
1654
|
-
return [];
|
|
1655
|
-
}
|
|
1656
|
-
}
|
|
1657
|
-
async requestWithdrawal(amount) {
|
|
1658
|
-
this.ensureInitialized();
|
|
1659
|
-
const distinctId = identityManager.getDistinctId();
|
|
1660
|
-
try {
|
|
1661
|
-
const response = await this.post("/api/v1/affiliate/withdraw", {
|
|
1662
|
-
amount,
|
|
1663
|
-
distinctId
|
|
1664
|
-
});
|
|
1665
|
-
if (response.success && response.withdrawal) {
|
|
1666
|
-
this.log("Withdrawal requested:", amount);
|
|
1667
|
-
return { success: true, withdrawal: mapRawWithdrawal(response.withdrawal) };
|
|
1668
|
-
}
|
|
1669
|
-
return {
|
|
1670
|
-
success: false,
|
|
1671
|
-
error: response.error
|
|
1672
|
-
};
|
|
1673
|
-
} catch (error) {
|
|
1674
|
-
this.log("Failed to request withdrawal:", error);
|
|
1675
|
-
return {
|
|
1676
|
-
success: false,
|
|
1677
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
1678
|
-
};
|
|
1679
|
-
}
|
|
1680
|
-
}
|
|
1681
|
-
async getWithdrawals() {
|
|
1682
|
-
this.ensureInitialized();
|
|
1683
|
-
const distinctId = identityManager.getDistinctId();
|
|
1684
|
-
try {
|
|
1685
|
-
const response = await this.get("/api/v1/affiliate/withdrawals", { distinctId });
|
|
1686
|
-
return response.withdrawals.map(mapRawWithdrawal);
|
|
1687
|
-
} catch (error) {
|
|
1688
|
-
this.log("Failed to get withdrawals:", error);
|
|
1689
|
-
return [];
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
async get(path, params) {
|
|
1693
|
-
this.ensureInitialized();
|
|
1694
|
-
return affiliateGet(this.apiClient, path, params);
|
|
1695
|
-
}
|
|
1696
|
-
async post(path, body) {
|
|
1697
|
-
this.ensureInitialized();
|
|
1698
|
-
return affiliatePost(this.apiClient, path, body);
|
|
1699
|
-
}
|
|
1700
|
-
ensureInitialized() {
|
|
1701
|
-
if (!this.apiClient) {
|
|
1702
|
-
throw new AffiliateError(
|
|
1703
|
-
AFFILIATE_ERROR_CODES.NOT_INITIALIZED,
|
|
1704
|
-
"AffiliateManager not initialized. Call initialize() first"
|
|
1705
|
-
);
|
|
1706
|
-
}
|
|
1707
|
-
}
|
|
1708
|
-
log(...args) {
|
|
1709
|
-
if (this.debug) console.log("[Paywallo:Affiliate]", ...args);
|
|
1710
|
-
}
|
|
1711
|
-
};
|
|
1712
|
-
var affiliateManager = new AffiliateManager();
|
|
1713
|
-
|
|
1714
|
-
// src/domains/affiliate/PaywalloAffiliate.ts
|
|
1715
|
-
var PaywalloAffiliate = {
|
|
1716
|
-
registerCoupon: (code) => affiliateManager.registerCoupon(code),
|
|
1717
|
-
applyCoupon: (code) => affiliateManager.applyCoupon(code),
|
|
1718
|
-
getMyCoupon: () => affiliateManager.getMyCoupon(),
|
|
1719
|
-
getAppliedCoupon: () => affiliateManager.getAppliedCoupon(),
|
|
1720
|
-
getBalance: () => affiliateManager.getBalance(),
|
|
1721
|
-
getCommissions: () => affiliateManager.getCommissions(),
|
|
1722
|
-
requestWithdrawal: (amount) => affiliateManager.requestWithdrawal(amount),
|
|
1723
|
-
getWithdrawals: () => affiliateManager.getWithdrawals()
|
|
1724
|
-
};
|
|
1725
|
-
|
|
1726
1513
|
// src/domains/subscription/SubscriptionCache.ts
|
|
1727
1514
|
var LEGACY_CACHE_KEY = "subscription_cache";
|
|
1728
1515
|
var CACHE_KEY_PREFIX = "subscription_cache:";
|
|
@@ -1832,9 +1619,6 @@ var SubscriptionCache = class {
|
|
|
1832
1619
|
};
|
|
1833
1620
|
var subscriptionCache = new SubscriptionCache();
|
|
1834
1621
|
|
|
1835
|
-
// src/domains/subscription/SubscriptionManager.ts
|
|
1836
|
-
import { Platform as Platform4 } from "react-native";
|
|
1837
|
-
|
|
1838
1622
|
// src/domains/session/SessionError.ts
|
|
1839
1623
|
var SessionError = class extends PaywalloError {
|
|
1840
1624
|
constructor(code, message) {
|
|
@@ -1922,11 +1706,10 @@ var SubscriptionManagerClass = class {
|
|
|
1922
1706
|
if (!this.config) {
|
|
1923
1707
|
return this.getEmptyStatus();
|
|
1924
1708
|
}
|
|
1925
|
-
const url = new URL(`${this.config.serverUrl}/
|
|
1709
|
+
const url = new URL(`${this.config.serverUrl}/purchases/status/${encodeURIComponent(this.config.appKey)}`);
|
|
1926
1710
|
if (this.userId) {
|
|
1927
|
-
url.searchParams.set("
|
|
1711
|
+
url.searchParams.set("distinctId", this.userId);
|
|
1928
1712
|
}
|
|
1929
|
-
url.searchParams.set("platform", Platform4.OS);
|
|
1930
1713
|
const response = await fetch(url.toString(), {
|
|
1931
1714
|
method: "GET",
|
|
1932
1715
|
headers: {
|
|
@@ -1981,7 +1764,7 @@ var SubscriptionManagerClass = class {
|
|
|
1981
1764
|
var subscriptionManager = new SubscriptionManagerClass();
|
|
1982
1765
|
|
|
1983
1766
|
// src/core/ApiClient.ts
|
|
1984
|
-
import { Platform as
|
|
1767
|
+
import { Platform as Platform4 } from "react-native";
|
|
1985
1768
|
|
|
1986
1769
|
// src/core/apiUrlUtils.ts
|
|
1987
1770
|
var DEFAULT_WEB_URL = "https://paywallo.com.br";
|
|
@@ -2664,9 +2447,78 @@ var QueueProcessorClass = class {
|
|
|
2664
2447
|
this.log("Offline, skipping queue processing");
|
|
2665
2448
|
return { processed: 0, failed: 0 };
|
|
2666
2449
|
}
|
|
2667
|
-
|
|
2450
|
+
const allItems = offlineQueue.getQueue();
|
|
2451
|
+
const eventItems = allItems.filter(
|
|
2452
|
+
(i) => i.url.includes("/api/v1/events") && !i.url.includes("/batch")
|
|
2453
|
+
);
|
|
2454
|
+
const eventItemIds = new Set(eventItems.map((i) => i.id));
|
|
2455
|
+
const { processed: batchProcessed, failed: batchFailed, retryIds } = await this.processEventBatches(eventItems);
|
|
2456
|
+
const batchResult = { processed: batchProcessed, failed: batchFailed };
|
|
2457
|
+
const individualResult = await offlineQueue.processQueue(async (item) => {
|
|
2458
|
+
if (eventItemIds.has(item.id)) {
|
|
2459
|
+
if (retryIds.has(item.id)) {
|
|
2460
|
+
return { success: false, permanent: false };
|
|
2461
|
+
}
|
|
2462
|
+
return { success: true, permanent: false };
|
|
2463
|
+
}
|
|
2668
2464
|
return this.processItem(item);
|
|
2669
2465
|
});
|
|
2466
|
+
this.log("Queue processing complete", {
|
|
2467
|
+
batchProcessed: batchResult.processed,
|
|
2468
|
+
batchFailed: batchResult.failed,
|
|
2469
|
+
individualProcessed: individualResult.processed,
|
|
2470
|
+
individualFailed: individualResult.failed
|
|
2471
|
+
});
|
|
2472
|
+
return {
|
|
2473
|
+
processed: batchResult.processed + individualResult.processed,
|
|
2474
|
+
failed: batchResult.failed + individualResult.failed
|
|
2475
|
+
};
|
|
2476
|
+
}
|
|
2477
|
+
async processEventBatches(eventItems) {
|
|
2478
|
+
if (eventItems.length === 0 || !this.httpClient) {
|
|
2479
|
+
return { processed: 0, failed: 0, retryIds: /* @__PURE__ */ new Set() };
|
|
2480
|
+
}
|
|
2481
|
+
const BATCH_SIZE = 50;
|
|
2482
|
+
let processed = 0;
|
|
2483
|
+
let failed = 0;
|
|
2484
|
+
const retryIds = /* @__PURE__ */ new Set();
|
|
2485
|
+
for (let i = 0; i < eventItems.length; i += BATCH_SIZE) {
|
|
2486
|
+
const batch = eventItems.slice(i, i + BATCH_SIZE);
|
|
2487
|
+
const bodies = batch.map((item) => item.body);
|
|
2488
|
+
const headers = batch[0].headers;
|
|
2489
|
+
this.log("Sending event batch", { size: batch.length, batchIndex: Math.floor(i / BATCH_SIZE) });
|
|
2490
|
+
const response = await this.httpClient.request("/api/v1/events/batch", {
|
|
2491
|
+
method: "POST",
|
|
2492
|
+
body: { events: bodies },
|
|
2493
|
+
headers,
|
|
2494
|
+
skipRetry: false
|
|
2495
|
+
});
|
|
2496
|
+
if (response.ok) {
|
|
2497
|
+
for (const item of batch) {
|
|
2498
|
+
await offlineQueue.removeItem(item.id);
|
|
2499
|
+
processed++;
|
|
2500
|
+
}
|
|
2501
|
+
this.log("Event batch processed successfully", { size: batch.length });
|
|
2502
|
+
} else if (response.status >= 400 && response.status < 500) {
|
|
2503
|
+
for (const item of batch) {
|
|
2504
|
+
await offlineQueue.removeItem(item.id);
|
|
2505
|
+
failed++;
|
|
2506
|
+
}
|
|
2507
|
+
this.log("Event batch failed permanently (4xx) - removing from queue", {
|
|
2508
|
+
status: response.status,
|
|
2509
|
+
size: batch.length
|
|
2510
|
+
});
|
|
2511
|
+
} else {
|
|
2512
|
+
for (const item of batch) {
|
|
2513
|
+
retryIds.add(item.id);
|
|
2514
|
+
}
|
|
2515
|
+
this.log("Event batch failed with server error - will retry", {
|
|
2516
|
+
status: response.status,
|
|
2517
|
+
size: batch.length
|
|
2518
|
+
});
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
return { processed, failed, retryIds };
|
|
2670
2522
|
}
|
|
2671
2523
|
async processItem(item) {
|
|
2672
2524
|
if (!this.httpClient) {
|
|
@@ -2817,10 +2669,14 @@ var ApiCache = class {
|
|
|
2817
2669
|
};
|
|
2818
2670
|
|
|
2819
2671
|
// src/core/ApiClient.ts
|
|
2820
|
-
var SDK_VERSION = "1.
|
|
2672
|
+
var SDK_VERSION = "1.5.0";
|
|
2821
2673
|
var _ApiClient = class _ApiClient {
|
|
2822
2674
|
constructor(appKey, apiUrl, debug = false, environment = "Production", offlineQueueEnabled = true, timeout = 3e4) {
|
|
2823
2675
|
this.cache = new ApiCache();
|
|
2676
|
+
this.eventBatch = [];
|
|
2677
|
+
this.batchTimer = null;
|
|
2678
|
+
this.BATCH_MAX_SIZE = 10;
|
|
2679
|
+
this.BATCH_FLUSH_MS = 5e3;
|
|
2824
2680
|
this.appKey = appKey;
|
|
2825
2681
|
this.baseUrl = apiUrl;
|
|
2826
2682
|
this.webUrl = deriveWebUrl(this.baseUrl);
|
|
@@ -2873,30 +2729,79 @@ var _ApiClient = class _ApiClient {
|
|
|
2873
2729
|
message,
|
|
2874
2730
|
context,
|
|
2875
2731
|
sdkVersion: SDK_VERSION,
|
|
2876
|
-
platform:
|
|
2732
|
+
platform: Platform4.OS
|
|
2877
2733
|
}, true);
|
|
2878
2734
|
} catch (err) {
|
|
2879
2735
|
if (this.debug) console.warn("[Paywallo:ApiClient] reportError failed", err);
|
|
2880
2736
|
}
|
|
2881
2737
|
}
|
|
2882
2738
|
async trackEvent(eventName, distinctId, properties, timestamp) {
|
|
2883
|
-
|
|
2739
|
+
const EVENT_NAME_REGEX = /^(\$[a-zA-Z0-9_]+|[a-z][a-z0-9_]*)$/;
|
|
2740
|
+
if (!EVENT_NAME_REGEX.test(eventName)) {
|
|
2741
|
+
if (this.debug) {
|
|
2742
|
+
console.warn(`[Paywallo] Invalid event name "${eventName}". Must be snake_case or start with $`);
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2745
|
+
const event = {
|
|
2884
2746
|
eventName,
|
|
2885
2747
|
distinctId,
|
|
2886
|
-
properties: { ...properties, platform:
|
|
2748
|
+
properties: { ...properties, platform: Platform4.OS },
|
|
2887
2749
|
timestamp: timestamp ?? Date.now(),
|
|
2888
2750
|
environment: this.environment.toLowerCase()
|
|
2889
|
-
}
|
|
2751
|
+
};
|
|
2752
|
+
this.eventBatch.push(event);
|
|
2753
|
+
if (this.eventBatch.length >= this.BATCH_MAX_SIZE) {
|
|
2754
|
+
if (this.batchTimer !== null) {
|
|
2755
|
+
clearTimeout(this.batchTimer);
|
|
2756
|
+
this.batchTimer = null;
|
|
2757
|
+
}
|
|
2758
|
+
await this.flushEventBatch();
|
|
2759
|
+
return;
|
|
2760
|
+
}
|
|
2761
|
+
if (this.batchTimer !== null) {
|
|
2762
|
+
clearTimeout(this.batchTimer);
|
|
2763
|
+
}
|
|
2764
|
+
this.batchTimer = setTimeout(() => {
|
|
2765
|
+
this.batchTimer = null;
|
|
2766
|
+
void this.flushEventBatch();
|
|
2767
|
+
}, this.BATCH_FLUSH_MS);
|
|
2768
|
+
}
|
|
2769
|
+
async flushEventBatch() {
|
|
2770
|
+
if (this.eventBatch.length === 0) return;
|
|
2771
|
+
const batch = this.eventBatch.splice(0, this.eventBatch.length);
|
|
2772
|
+
if (this.batchTimer !== null) {
|
|
2773
|
+
clearTimeout(this.batchTimer);
|
|
2774
|
+
this.batchTimer = null;
|
|
2775
|
+
}
|
|
2776
|
+
try {
|
|
2777
|
+
await this.postWithQueue(
|
|
2778
|
+
"/api/v1/events/batch",
|
|
2779
|
+
{ events: batch },
|
|
2780
|
+
`event_batch:${batch.length}`
|
|
2781
|
+
);
|
|
2782
|
+
} catch (error) {
|
|
2783
|
+
this.log("Batch send failed, falling back to individual sends:", error);
|
|
2784
|
+
for (const event of batch) {
|
|
2785
|
+
await this.postWithQueue(
|
|
2786
|
+
"/api/v1/events",
|
|
2787
|
+
event,
|
|
2788
|
+
`event:${event.eventName}`
|
|
2789
|
+
);
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2890
2792
|
}
|
|
2891
2793
|
async identify(distinctId, properties, email, deviceId) {
|
|
2892
|
-
await this.postWithQueue("/api/v1/identify", { distinctId, properties: properties ?? {}, email, platform:
|
|
2794
|
+
await this.postWithQueue("/api/v1/identify", { distinctId, properties: properties ?? {}, email, platform: Platform4.OS, ...deviceId && { deviceId } }, "identify");
|
|
2893
2795
|
}
|
|
2894
|
-
async startSession(distinctId, sessionId, platform) {
|
|
2796
|
+
async startSession(distinctId, sessionId, platform, appVersion, deviceModel, osVersion) {
|
|
2895
2797
|
await this.postWithQueue("/api/v1/sessions/start", {
|
|
2896
2798
|
distinctId,
|
|
2897
2799
|
sessionId,
|
|
2898
|
-
platform: platform ??
|
|
2899
|
-
environment: this.environment.toLowerCase()
|
|
2800
|
+
platform: platform ?? Platform4.OS,
|
|
2801
|
+
environment: this.environment.toLowerCase(),
|
|
2802
|
+
appVersion: appVersion ?? "unknown",
|
|
2803
|
+
deviceModel: deviceModel ?? "unknown",
|
|
2804
|
+
osVersion: osVersion ?? String(Platform4.Version)
|
|
2900
2805
|
}, `session.start:${sessionId}`);
|
|
2901
2806
|
return { success: true };
|
|
2902
2807
|
}
|
|
@@ -3204,7 +3109,7 @@ var CampaignGateService = class {
|
|
|
3204
3109
|
const isActive = await hasActive();
|
|
3205
3110
|
if (PaywalloClient.getConfig()?.debug) console.info(`[Paywallo] requireSubscription \u2014 hasActive: ${isActive}`);
|
|
3206
3111
|
if (isActive) return true;
|
|
3207
|
-
const r = await this.presentCampaign(apiClient, placement, hasActive, paywallPresenter, campaignPresenter, context);
|
|
3112
|
+
const r = await this.presentCampaign(apiClient, placement, hasActive, paywallPresenter, campaignPresenter, { ...context, forceShow: true });
|
|
3208
3113
|
return r.purchased || r.restored;
|
|
3209
3114
|
}
|
|
3210
3115
|
async preloadCampaign(apiClient, placement, context) {
|
|
@@ -3242,7 +3147,7 @@ var CampaignGateService = class {
|
|
|
3242
3147
|
var campaignGateService = new CampaignGateService();
|
|
3243
3148
|
|
|
3244
3149
|
// src/domains/identity/AdvertisingIdManager.ts
|
|
3245
|
-
import { Platform as
|
|
3150
|
+
import { Platform as Platform5 } from "react-native";
|
|
3246
3151
|
var ZERO_IDFA = "00000000-0000-0000-0000-000000000000";
|
|
3247
3152
|
var AdvertisingIdManager = class {
|
|
3248
3153
|
constructor() {
|
|
@@ -3253,7 +3158,7 @@ var AdvertisingIdManager = class {
|
|
|
3253
3158
|
const fallback = { idfv: null, idfa: null, gaid: null, attStatus: "unavailable" };
|
|
3254
3159
|
try {
|
|
3255
3160
|
const tracking = await import("expo-tracking-transparency");
|
|
3256
|
-
if (
|
|
3161
|
+
if (Platform5.OS === "ios") {
|
|
3257
3162
|
if (!tracking.isAvailable()) {
|
|
3258
3163
|
const idfv2 = await this.collectIdfv();
|
|
3259
3164
|
this.cached = { ...fallback, idfv: idfv2 };
|
|
@@ -3272,7 +3177,7 @@ var AdvertisingIdManager = class {
|
|
|
3272
3177
|
this.cached = { idfv, idfa, gaid: null, attStatus: status };
|
|
3273
3178
|
return this.cached;
|
|
3274
3179
|
}
|
|
3275
|
-
if (
|
|
3180
|
+
if (Platform5.OS === "android") {
|
|
3276
3181
|
const androidStatus = await tracking.getTrackingPermissionsAsync();
|
|
3277
3182
|
const optedIn = androidStatus.granted;
|
|
3278
3183
|
const rawGaid = optedIn ? tracking.getAdvertisingId() : null;
|
|
@@ -3358,11 +3263,11 @@ var MetaBridge = class {
|
|
|
3358
3263
|
var metaBridge = new MetaBridge();
|
|
3359
3264
|
|
|
3360
3265
|
// src/domains/identity/InstallReferrerManager.ts
|
|
3361
|
-
import { Platform as
|
|
3266
|
+
import { Platform as Platform6 } from "react-native";
|
|
3362
3267
|
var REFERRER_TIMEOUT_MS = 5e3;
|
|
3363
3268
|
var InstallReferrerManager = class {
|
|
3364
3269
|
async getReferrer() {
|
|
3365
|
-
if (
|
|
3270
|
+
if (Platform6.OS !== "android") {
|
|
3366
3271
|
return this.emptyResult();
|
|
3367
3272
|
}
|
|
3368
3273
|
let timerId;
|
|
@@ -3425,14 +3330,77 @@ var installReferrerManager = new InstallReferrerManager();
|
|
|
3425
3330
|
import { AppState as AppState2 } from "react-native";
|
|
3426
3331
|
init_uuid();
|
|
3427
3332
|
|
|
3333
|
+
// src/utils/deviceInfo.ts
|
|
3334
|
+
import { Platform as Platform7 } from "react-native";
|
|
3335
|
+
async function getAppVersion() {
|
|
3336
|
+
try {
|
|
3337
|
+
const app = await import("expo-application");
|
|
3338
|
+
if (app.nativeApplicationVersion) {
|
|
3339
|
+
return app.nativeApplicationVersion;
|
|
3340
|
+
}
|
|
3341
|
+
} catch {
|
|
3342
|
+
}
|
|
3343
|
+
try {
|
|
3344
|
+
const DeviceInfo2 = (await import("react-native-device-info")).default;
|
|
3345
|
+
const v = DeviceInfo2.getVersion();
|
|
3346
|
+
if (v) return v;
|
|
3347
|
+
} catch {
|
|
3348
|
+
}
|
|
3349
|
+
return "unknown";
|
|
3350
|
+
}
|
|
3351
|
+
async function getDeviceModel() {
|
|
3352
|
+
try {
|
|
3353
|
+
const DeviceInfo2 = (await import("react-native-device-info")).default;
|
|
3354
|
+
const model = DeviceInfo2.getModel();
|
|
3355
|
+
if (model) return model;
|
|
3356
|
+
} catch {
|
|
3357
|
+
}
|
|
3358
|
+
return "unknown";
|
|
3359
|
+
}
|
|
3360
|
+
function getOsVersion() {
|
|
3361
|
+
const v = Platform7.Version;
|
|
3362
|
+
if (typeof v === "number") return String(v);
|
|
3363
|
+
if (typeof v === "string" && v.length > 0) return v;
|
|
3364
|
+
return "unknown";
|
|
3365
|
+
}
|
|
3366
|
+
async function collectDeviceInfo() {
|
|
3367
|
+
const [appVersion, deviceModel] = await Promise.all([
|
|
3368
|
+
getAppVersion(),
|
|
3369
|
+
getDeviceModel()
|
|
3370
|
+
]);
|
|
3371
|
+
return {
|
|
3372
|
+
appVersion,
|
|
3373
|
+
deviceModel,
|
|
3374
|
+
osVersion: getOsVersion()
|
|
3375
|
+
};
|
|
3376
|
+
}
|
|
3377
|
+
|
|
3428
3378
|
// src/domains/session/sessionTracking.ts
|
|
3429
3379
|
async function trackSessionStart(apiClient, sessionId, sessionStartedAt) {
|
|
3430
3380
|
const distinctId = identityManager.getDistinctId();
|
|
3431
|
-
await
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
});
|
|
3381
|
+
const deviceInfo = await collectDeviceInfo().catch(() => ({
|
|
3382
|
+
appVersion: "unknown",
|
|
3383
|
+
deviceModel: "unknown",
|
|
3384
|
+
osVersion: "unknown"
|
|
3385
|
+
}));
|
|
3386
|
+
await Promise.all([
|
|
3387
|
+
apiClient.startSession(
|
|
3388
|
+
distinctId,
|
|
3389
|
+
sessionId,
|
|
3390
|
+
void 0,
|
|
3391
|
+
deviceInfo.appVersion,
|
|
3392
|
+
deviceInfo.deviceModel,
|
|
3393
|
+
deviceInfo.osVersion
|
|
3394
|
+
),
|
|
3395
|
+
apiClient.trackEvent("$session_start", distinctId, {
|
|
3396
|
+
sessionId,
|
|
3397
|
+
timestamp: sessionStartedAt?.toISOString() ?? null,
|
|
3398
|
+
appVersion: deviceInfo.appVersion,
|
|
3399
|
+
deviceModel: deviceInfo.deviceModel,
|
|
3400
|
+
osVersion: deviceInfo.osVersion
|
|
3401
|
+
}).catch(() => {
|
|
3402
|
+
})
|
|
3403
|
+
]);
|
|
3436
3404
|
}
|
|
3437
3405
|
async function trackSessionEnd(apiClient, sessionId, durationSeconds, sessionStartedAt) {
|
|
3438
3406
|
const distinctId = identityManager.getDistinctId();
|
|
@@ -3529,6 +3497,9 @@ var SessionManager = class {
|
|
|
3529
3497
|
if (!this.sessionId || !this.sessionStartedAt) {
|
|
3530
3498
|
return;
|
|
3531
3499
|
}
|
|
3500
|
+
if (this.apiClient) {
|
|
3501
|
+
await this.apiClient.flushEventBatch();
|
|
3502
|
+
}
|
|
3532
3503
|
await this.trackSessionEnd();
|
|
3533
3504
|
this.sessionId = null;
|
|
3534
3505
|
this.sessionStartedAt = null;
|
|
@@ -3613,6 +3584,9 @@ var SessionManager = class {
|
|
|
3613
3584
|
async handleBackground() {
|
|
3614
3585
|
this.backgroundTimestamp = Date.now();
|
|
3615
3586
|
await this.trackAppBackground();
|
|
3587
|
+
if (this.apiClient) {
|
|
3588
|
+
await this.apiClient.flushEventBatch();
|
|
3589
|
+
}
|
|
3616
3590
|
}
|
|
3617
3591
|
async checkEmergencyPaywall() {
|
|
3618
3592
|
if (!this.apiClient || !this.emergencyPaywallHandler) {
|
|
@@ -3968,7 +3942,6 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3968
3942
|
]);
|
|
3969
3943
|
this.apiClient = new ApiClient(config.appKey, config.apiUrl ?? "https://paywallo.com.br", config.debug, environment, offlineQueueEnabled, config.timeout);
|
|
3970
3944
|
queueProcessor.initialize(this.apiClient.getHttpClient(), { debug: config.debug });
|
|
3971
|
-
affiliateManager.initialize(this.apiClient, config.debug);
|
|
3972
3945
|
subscriptionManager.init({
|
|
3973
3946
|
serverUrl: this.apiClient.getBaseUrl(),
|
|
3974
3947
|
appKey: config.appKey,
|
|
@@ -3995,6 +3968,12 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3995
3968
|
this.resolveReady = null;
|
|
3996
3969
|
}
|
|
3997
3970
|
if (config.debug) console.info("[Paywallo INIT] complete");
|
|
3971
|
+
if (distinctId) {
|
|
3972
|
+
this.apiClient.getSubscriptionStatus(distinctId).then(() => {
|
|
3973
|
+
if (config.debug) console.info("[Paywallo SUB] preloaded on boot");
|
|
3974
|
+
}).catch(() => {
|
|
3975
|
+
});
|
|
3976
|
+
}
|
|
3998
3977
|
if (this.apiClient) {
|
|
3999
3978
|
const apiClientRef = this.apiClient;
|
|
4000
3979
|
if (config.autoPreloadCampaign) {
|
|
@@ -4279,6 +4258,10 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
4279
4258
|
registerEmergencyPaywallHandler(h) {
|
|
4280
4259
|
sessionManager.registerEmergencyPaywallHandler(h);
|
|
4281
4260
|
}
|
|
4261
|
+
async getEmergencyPaywall() {
|
|
4262
|
+
if (!this.apiClient) throw new ClientError(CLIENT_ERROR_CODES.NOT_INITIALIZED, "SDK not initialized");
|
|
4263
|
+
return this.apiClient.getEmergencyPaywall();
|
|
4264
|
+
}
|
|
4282
4265
|
async requireSubscription(paywallPlacement) {
|
|
4283
4266
|
this.ensureInitialized();
|
|
4284
4267
|
if (await this.hasActiveSubscription()) return true;
|
|
@@ -4823,7 +4806,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
|
|
|
4823
4806
|
// src/hooks/useSubscriptionSync.ts
|
|
4824
4807
|
import { useCallback as useCallback12, useRef as useRef7, useState as useState11 } from "react";
|
|
4825
4808
|
var CACHE_TTL_MS2 = 5 * 60 * 1e3;
|
|
4826
|
-
var ACTIVE_STATUSES = ["active", "
|
|
4809
|
+
var ACTIVE_STATUSES = ["active", "in_grace_period"];
|
|
4827
4810
|
function toDomainSubscriptionInfo(sub) {
|
|
4828
4811
|
return {
|
|
4829
4812
|
id: "",
|
|
@@ -4842,7 +4825,7 @@ function toDomainResponse(status) {
|
|
|
4842
4825
|
return {
|
|
4843
4826
|
hasActiveSubscription: status.hasActiveSubscription,
|
|
4844
4827
|
subscription: status.subscription ? toDomainSubscriptionInfo(status.subscription) : null,
|
|
4845
|
-
entitlements: []
|
|
4828
|
+
entitlements: status.entitlements ?? []
|
|
4846
4829
|
};
|
|
4847
4830
|
}
|
|
4848
4831
|
function isSubscriptionStillActive(sub) {
|
|
@@ -4898,13 +4881,14 @@ function useSubscriptionSync() {
|
|
|
4898
4881
|
const persisted = await subscriptionCache.get(distinctId);
|
|
4899
4882
|
if (persisted && !persisted.isStale) {
|
|
4900
4883
|
const persistedSub = persisted.data.subscription;
|
|
4884
|
+
const persistedStatus = persistedSub?.status;
|
|
4901
4885
|
const sub = persistedSub ? {
|
|
4902
4886
|
productId: persistedSub.productId,
|
|
4903
|
-
status:
|
|
4887
|
+
status: persistedStatus,
|
|
4904
4888
|
expiresAt: persistedSub.expiresAt ?? null,
|
|
4905
4889
|
platform: persistedSub.platform,
|
|
4906
4890
|
autoRenewEnabled: persistedSub.willRenew,
|
|
4907
|
-
inGracePeriod:
|
|
4891
|
+
inGracePeriod: persistedStatus === "in_grace_period"
|
|
4908
4892
|
} : null;
|
|
4909
4893
|
const stillActive = isSubscriptionStillActive(sub);
|
|
4910
4894
|
if (stillActive) {
|
|
@@ -4942,14 +4926,14 @@ function useSubscriptionSync() {
|
|
|
4942
4926
|
const persisted = await subscriptionCache.get(distinctId);
|
|
4943
4927
|
if (!persisted?.data.subscription) return null;
|
|
4944
4928
|
const s = persisted.data.subscription;
|
|
4945
|
-
const
|
|
4929
|
+
const status = s.status;
|
|
4946
4930
|
return {
|
|
4947
4931
|
productId: s.productId,
|
|
4948
|
-
status
|
|
4932
|
+
status,
|
|
4949
4933
|
expiresAt: s.expiresAt ?? null,
|
|
4950
4934
|
platform: s.platform,
|
|
4951
4935
|
autoRenewEnabled: s.willRenew,
|
|
4952
|
-
inGracePeriod:
|
|
4936
|
+
inGracePeriod: status === "in_grace_period"
|
|
4953
4937
|
};
|
|
4954
4938
|
} catch {
|
|
4955
4939
|
return null;
|
|
@@ -5374,10 +5358,7 @@ function hasVariables(text) {
|
|
|
5374
5358
|
var Paywallo = PaywalloClient;
|
|
5375
5359
|
var index_default = Paywallo;
|
|
5376
5360
|
export {
|
|
5377
|
-
AFFILIATE_ERROR_CODES,
|
|
5378
5361
|
ANALYTICS_ERROR_CODES,
|
|
5379
|
-
AffiliateError,
|
|
5380
|
-
AffiliateManager,
|
|
5381
5362
|
AnalyticsError,
|
|
5382
5363
|
ApiClient,
|
|
5383
5364
|
CAMPAIGN_ERROR_CODES,
|
|
@@ -5395,7 +5376,6 @@ export {
|
|
|
5395
5376
|
PaywallError,
|
|
5396
5377
|
PaywallModal,
|
|
5397
5378
|
Paywallo,
|
|
5398
|
-
PaywalloAffiliate,
|
|
5399
5379
|
PaywalloClient,
|
|
5400
5380
|
PaywalloContext,
|
|
5401
5381
|
PaywalloError,
|
|
@@ -5405,7 +5385,6 @@ export {
|
|
|
5405
5385
|
SessionError,
|
|
5406
5386
|
SessionManager,
|
|
5407
5387
|
SubscriptionCache,
|
|
5408
|
-
affiliateManager,
|
|
5409
5388
|
createPurchaseError,
|
|
5410
5389
|
index_default as default,
|
|
5411
5390
|
detectDeviceLanguage,
|