@virex-tech/paywallo-sdk 1.4.0 → 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 +291 -287
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +287 -278
- package/dist/index.mjs.map +1 -1
- package/ios/PaywalloFBBridge.swift +1 -1
- package/ios/PaywalloStoreKit.swift +17 -1
- package/package.json +2 -2
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 };
|
|
@@ -619,17 +627,25 @@ var IAPService = class {
|
|
|
619
627
|
error: new PurchaseError(PURCHASE_ERROR_CODES.STORE_NOT_AVAILABLE, "In-app purchases are not available on this device")
|
|
620
628
|
};
|
|
621
629
|
}
|
|
630
|
+
const debug = PaywalloClient.getConfig()?.debug;
|
|
631
|
+
if (debug) console.info(`[Paywallo PURCHASE] starting ${productId}`);
|
|
622
632
|
try {
|
|
623
633
|
const purchase = await nativeStoreKit.purchase(productId);
|
|
624
634
|
if (!purchase) {
|
|
635
|
+
if (debug) console.info(`[Paywallo PURCHASE] ${productId} cancelled`);
|
|
625
636
|
return { success: false };
|
|
626
637
|
}
|
|
638
|
+
if (debug) console.info(`[Paywallo PURCHASE] ${productId} approved`, { transactionId: purchase.transactionId });
|
|
627
639
|
await this.finishTransaction(purchase.transactionId);
|
|
628
|
-
|
|
629
|
-
|
|
640
|
+
if (debug) console.info(`[Paywallo PURCHASE] ${productId} finished`);
|
|
641
|
+
this.validateWithServer(purchase, productId, options).then(() => {
|
|
642
|
+
if (debug) console.info(`[Paywallo PURCHASE] ${productId} server validated`);
|
|
643
|
+
}).catch((err) => {
|
|
644
|
+
if (debug) console.warn("[Paywallo PURCHASE] background validation failed", err);
|
|
630
645
|
});
|
|
631
646
|
return { success: true, purchase };
|
|
632
647
|
} catch (error) {
|
|
648
|
+
if (debug) console.warn(`[Paywallo PURCHASE] ${productId} failed`, error);
|
|
633
649
|
const e = error instanceof PurchaseError ? error : new PurchaseError(PURCHASE_ERROR_CODES.PURCHASE_FAILED, error instanceof Error ? error.message : String(error));
|
|
634
650
|
return { success: false, error: e };
|
|
635
651
|
}
|
|
@@ -1494,227 +1510,6 @@ function usePaywallPresenter(preloadedWebView, setPreloadedWebView) {
|
|
|
1494
1510
|
return { showingPreloadedWebView, presentPreloadedWebView, resolvePreloadedPaywall, handlePreloadedWebViewReady, handlePreloadedClose, setShowingPreloadedWebView, presentedAtRef };
|
|
1495
1511
|
}
|
|
1496
1512
|
|
|
1497
|
-
// src/domains/affiliate/AffiliateError.ts
|
|
1498
|
-
var AffiliateError = class extends PaywalloError {
|
|
1499
|
-
constructor(code, message) {
|
|
1500
|
-
super("affiliate", code, message);
|
|
1501
|
-
this.name = "AffiliateError";
|
|
1502
|
-
}
|
|
1503
|
-
};
|
|
1504
|
-
var AFFILIATE_ERROR_CODES = {
|
|
1505
|
-
NOT_INITIALIZED: "AFFILIATE_NOT_INITIALIZED",
|
|
1506
|
-
COUPON_REGISTER_FAILED: "AFFILIATE_COUPON_REGISTER_FAILED",
|
|
1507
|
-
COUPON_APPLY_FAILED: "AFFILIATE_COUPON_APPLY_FAILED",
|
|
1508
|
-
BALANCE_FETCH_FAILED: "AFFILIATE_BALANCE_FETCH_FAILED",
|
|
1509
|
-
WITHDRAWAL_FAILED: "AFFILIATE_WITHDRAWAL_FAILED",
|
|
1510
|
-
NETWORK_ERROR: "AFFILIATE_NETWORK_ERROR"
|
|
1511
|
-
};
|
|
1512
|
-
|
|
1513
|
-
// src/domains/affiliate/affiliateHttp.ts
|
|
1514
|
-
function buildQueryString(params) {
|
|
1515
|
-
if (!params) return "";
|
|
1516
|
-
const entries = Object.entries(params).filter(([, v]) => v !== void 0 && v !== null);
|
|
1517
|
-
if (entries.length === 0) return "";
|
|
1518
|
-
return `?${entries.map(([k, v]) => `${k}=${encodeURIComponent(String(v))}`).join("&")}`;
|
|
1519
|
-
}
|
|
1520
|
-
async function affiliateGet(apiClient, path, params) {
|
|
1521
|
-
const response = await apiClient.get(`${path}${buildQueryString(params)}`);
|
|
1522
|
-
return response.data;
|
|
1523
|
-
}
|
|
1524
|
-
async function affiliatePost(apiClient, path, body) {
|
|
1525
|
-
const response = await apiClient.post(path, body);
|
|
1526
|
-
return response.data;
|
|
1527
|
-
}
|
|
1528
|
-
|
|
1529
|
-
// src/domains/affiliate/affiliateDateMappers.ts
|
|
1530
|
-
function mapRawCoupon(raw) {
|
|
1531
|
-
return { code: raw.code, status: raw.status, createdAt: new Date(raw.createdAt) };
|
|
1532
|
-
}
|
|
1533
|
-
function mapRawCommission(raw) {
|
|
1534
|
-
return { ...raw, createdAt: new Date(raw.createdAt) };
|
|
1535
|
-
}
|
|
1536
|
-
function mapRawWithdrawal(raw) {
|
|
1537
|
-
return { ...raw, createdAt: new Date(raw.createdAt) };
|
|
1538
|
-
}
|
|
1539
|
-
|
|
1540
|
-
// src/domains/affiliate/AffiliateManager.ts
|
|
1541
|
-
var AffiliateManager = class {
|
|
1542
|
-
constructor() {
|
|
1543
|
-
this.apiClient = null;
|
|
1544
|
-
this.debug = false;
|
|
1545
|
-
}
|
|
1546
|
-
initialize(apiClient, debug = false) {
|
|
1547
|
-
this.apiClient = apiClient;
|
|
1548
|
-
this.debug = debug;
|
|
1549
|
-
this.log("AffiliateManager initialized");
|
|
1550
|
-
}
|
|
1551
|
-
async registerCoupon(code) {
|
|
1552
|
-
this.ensureInitialized();
|
|
1553
|
-
const distinctId = identityManager.getDistinctId();
|
|
1554
|
-
try {
|
|
1555
|
-
const response = await this.post("/api/v1/affiliate/register-coupon", {
|
|
1556
|
-
code,
|
|
1557
|
-
distinctId
|
|
1558
|
-
});
|
|
1559
|
-
if (response.success && response.coupon) {
|
|
1560
|
-
this.log("Coupon registered:", code);
|
|
1561
|
-
return { success: true, coupon: mapRawCoupon(response.coupon) };
|
|
1562
|
-
}
|
|
1563
|
-
return {
|
|
1564
|
-
success: false,
|
|
1565
|
-
error: response.error
|
|
1566
|
-
};
|
|
1567
|
-
} catch (error) {
|
|
1568
|
-
this.log("Failed to register coupon:", error);
|
|
1569
|
-
return {
|
|
1570
|
-
success: false,
|
|
1571
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
1572
|
-
};
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
async applyCoupon(code) {
|
|
1576
|
-
this.ensureInitialized();
|
|
1577
|
-
const distinctId = identityManager.getDistinctId();
|
|
1578
|
-
try {
|
|
1579
|
-
const response = await this.post("/api/v1/affiliate/apply-coupon", {
|
|
1580
|
-
code,
|
|
1581
|
-
distinctId
|
|
1582
|
-
});
|
|
1583
|
-
if (response.success) {
|
|
1584
|
-
this.log("Coupon applied:", code);
|
|
1585
|
-
}
|
|
1586
|
-
return response;
|
|
1587
|
-
} catch (error) {
|
|
1588
|
-
this.log("Failed to apply coupon:", error);
|
|
1589
|
-
return {
|
|
1590
|
-
success: false,
|
|
1591
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
1592
|
-
};
|
|
1593
|
-
}
|
|
1594
|
-
}
|
|
1595
|
-
async getMyCoupon() {
|
|
1596
|
-
this.ensureInitialized();
|
|
1597
|
-
const distinctId = identityManager.getDistinctId();
|
|
1598
|
-
try {
|
|
1599
|
-
const response = await this.get("/api/v1/affiliate/my-coupon", { distinctId });
|
|
1600
|
-
if (response.coupon) {
|
|
1601
|
-
return { coupon: mapRawCoupon(response.coupon) };
|
|
1602
|
-
}
|
|
1603
|
-
return { coupon: null };
|
|
1604
|
-
} catch (error) {
|
|
1605
|
-
this.log("Failed to get my coupon:", error);
|
|
1606
|
-
return { coupon: null };
|
|
1607
|
-
}
|
|
1608
|
-
}
|
|
1609
|
-
async getAppliedCoupon() {
|
|
1610
|
-
this.ensureInitialized();
|
|
1611
|
-
const distinctId = identityManager.getDistinctId();
|
|
1612
|
-
try {
|
|
1613
|
-
const response = await this.get("/api/v1/affiliate/applied-coupon", { distinctId });
|
|
1614
|
-
return response;
|
|
1615
|
-
} catch (error) {
|
|
1616
|
-
this.log("Failed to get applied coupon:", error);
|
|
1617
|
-
return { coupon: null };
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
async getBalance() {
|
|
1621
|
-
this.ensureInitialized();
|
|
1622
|
-
const distinctId = identityManager.getDistinctId();
|
|
1623
|
-
try {
|
|
1624
|
-
const response = await this.get("/api/v1/affiliate/balance", {
|
|
1625
|
-
distinctId
|
|
1626
|
-
});
|
|
1627
|
-
return response;
|
|
1628
|
-
} catch (error) {
|
|
1629
|
-
this.log("Failed to get balance:", error);
|
|
1630
|
-
return {
|
|
1631
|
-
total: 0,
|
|
1632
|
-
pending: 0,
|
|
1633
|
-
available: 0,
|
|
1634
|
-
withdrawn: 0
|
|
1635
|
-
};
|
|
1636
|
-
}
|
|
1637
|
-
}
|
|
1638
|
-
async getCommissions() {
|
|
1639
|
-
this.ensureInitialized();
|
|
1640
|
-
const distinctId = identityManager.getDistinctId();
|
|
1641
|
-
try {
|
|
1642
|
-
const response = await this.get("/api/v1/affiliate/commissions", { distinctId });
|
|
1643
|
-
return response.commissions.map(mapRawCommission);
|
|
1644
|
-
} catch (error) {
|
|
1645
|
-
this.log("Failed to get commissions:", error);
|
|
1646
|
-
return [];
|
|
1647
|
-
}
|
|
1648
|
-
}
|
|
1649
|
-
async requestWithdrawal(amount) {
|
|
1650
|
-
this.ensureInitialized();
|
|
1651
|
-
const distinctId = identityManager.getDistinctId();
|
|
1652
|
-
try {
|
|
1653
|
-
const response = await this.post("/api/v1/affiliate/withdraw", {
|
|
1654
|
-
amount,
|
|
1655
|
-
distinctId
|
|
1656
|
-
});
|
|
1657
|
-
if (response.success && response.withdrawal) {
|
|
1658
|
-
this.log("Withdrawal requested:", amount);
|
|
1659
|
-
return { success: true, withdrawal: mapRawWithdrawal(response.withdrawal) };
|
|
1660
|
-
}
|
|
1661
|
-
return {
|
|
1662
|
-
success: false,
|
|
1663
|
-
error: response.error
|
|
1664
|
-
};
|
|
1665
|
-
} catch (error) {
|
|
1666
|
-
this.log("Failed to request withdrawal:", error);
|
|
1667
|
-
return {
|
|
1668
|
-
success: false,
|
|
1669
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
1670
|
-
};
|
|
1671
|
-
}
|
|
1672
|
-
}
|
|
1673
|
-
async getWithdrawals() {
|
|
1674
|
-
this.ensureInitialized();
|
|
1675
|
-
const distinctId = identityManager.getDistinctId();
|
|
1676
|
-
try {
|
|
1677
|
-
const response = await this.get("/api/v1/affiliate/withdrawals", { distinctId });
|
|
1678
|
-
return response.withdrawals.map(mapRawWithdrawal);
|
|
1679
|
-
} catch (error) {
|
|
1680
|
-
this.log("Failed to get withdrawals:", error);
|
|
1681
|
-
return [];
|
|
1682
|
-
}
|
|
1683
|
-
}
|
|
1684
|
-
async get(path, params) {
|
|
1685
|
-
this.ensureInitialized();
|
|
1686
|
-
return affiliateGet(this.apiClient, path, params);
|
|
1687
|
-
}
|
|
1688
|
-
async post(path, body) {
|
|
1689
|
-
this.ensureInitialized();
|
|
1690
|
-
return affiliatePost(this.apiClient, path, body);
|
|
1691
|
-
}
|
|
1692
|
-
ensureInitialized() {
|
|
1693
|
-
if (!this.apiClient) {
|
|
1694
|
-
throw new AffiliateError(
|
|
1695
|
-
AFFILIATE_ERROR_CODES.NOT_INITIALIZED,
|
|
1696
|
-
"AffiliateManager not initialized. Call initialize() first"
|
|
1697
|
-
);
|
|
1698
|
-
}
|
|
1699
|
-
}
|
|
1700
|
-
log(...args) {
|
|
1701
|
-
if (this.debug) console.log("[Paywallo:Affiliate]", ...args);
|
|
1702
|
-
}
|
|
1703
|
-
};
|
|
1704
|
-
var affiliateManager = new AffiliateManager();
|
|
1705
|
-
|
|
1706
|
-
// src/domains/affiliate/PaywalloAffiliate.ts
|
|
1707
|
-
var PaywalloAffiliate = {
|
|
1708
|
-
registerCoupon: (code) => affiliateManager.registerCoupon(code),
|
|
1709
|
-
applyCoupon: (code) => affiliateManager.applyCoupon(code),
|
|
1710
|
-
getMyCoupon: () => affiliateManager.getMyCoupon(),
|
|
1711
|
-
getAppliedCoupon: () => affiliateManager.getAppliedCoupon(),
|
|
1712
|
-
getBalance: () => affiliateManager.getBalance(),
|
|
1713
|
-
getCommissions: () => affiliateManager.getCommissions(),
|
|
1714
|
-
requestWithdrawal: (amount) => affiliateManager.requestWithdrawal(amount),
|
|
1715
|
-
getWithdrawals: () => affiliateManager.getWithdrawals()
|
|
1716
|
-
};
|
|
1717
|
-
|
|
1718
1513
|
// src/domains/subscription/SubscriptionCache.ts
|
|
1719
1514
|
var LEGACY_CACHE_KEY = "subscription_cache";
|
|
1720
1515
|
var CACHE_KEY_PREFIX = "subscription_cache:";
|
|
@@ -1824,9 +1619,6 @@ var SubscriptionCache = class {
|
|
|
1824
1619
|
};
|
|
1825
1620
|
var subscriptionCache = new SubscriptionCache();
|
|
1826
1621
|
|
|
1827
|
-
// src/domains/subscription/SubscriptionManager.ts
|
|
1828
|
-
import { Platform as Platform4 } from "react-native";
|
|
1829
|
-
|
|
1830
1622
|
// src/domains/session/SessionError.ts
|
|
1831
1623
|
var SessionError = class extends PaywalloError {
|
|
1832
1624
|
constructor(code, message) {
|
|
@@ -1914,11 +1706,10 @@ var SubscriptionManagerClass = class {
|
|
|
1914
1706
|
if (!this.config) {
|
|
1915
1707
|
return this.getEmptyStatus();
|
|
1916
1708
|
}
|
|
1917
|
-
const url = new URL(`${this.config.serverUrl}/
|
|
1709
|
+
const url = new URL(`${this.config.serverUrl}/purchases/status/${encodeURIComponent(this.config.appKey)}`);
|
|
1918
1710
|
if (this.userId) {
|
|
1919
|
-
url.searchParams.set("
|
|
1711
|
+
url.searchParams.set("distinctId", this.userId);
|
|
1920
1712
|
}
|
|
1921
|
-
url.searchParams.set("platform", Platform4.OS);
|
|
1922
1713
|
const response = await fetch(url.toString(), {
|
|
1923
1714
|
method: "GET",
|
|
1924
1715
|
headers: {
|
|
@@ -1973,7 +1764,7 @@ var SubscriptionManagerClass = class {
|
|
|
1973
1764
|
var subscriptionManager = new SubscriptionManagerClass();
|
|
1974
1765
|
|
|
1975
1766
|
// src/core/ApiClient.ts
|
|
1976
|
-
import { Platform as
|
|
1767
|
+
import { Platform as Platform4 } from "react-native";
|
|
1977
1768
|
|
|
1978
1769
|
// src/core/apiUrlUtils.ts
|
|
1979
1770
|
var DEFAULT_WEB_URL = "https://paywallo.com.br";
|
|
@@ -2656,9 +2447,78 @@ var QueueProcessorClass = class {
|
|
|
2656
2447
|
this.log("Offline, skipping queue processing");
|
|
2657
2448
|
return { processed: 0, failed: 0 };
|
|
2658
2449
|
}
|
|
2659
|
-
|
|
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
|
+
}
|
|
2660
2464
|
return this.processItem(item);
|
|
2661
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 };
|
|
2662
2522
|
}
|
|
2663
2523
|
async processItem(item) {
|
|
2664
2524
|
if (!this.httpClient) {
|
|
@@ -2809,10 +2669,14 @@ var ApiCache = class {
|
|
|
2809
2669
|
};
|
|
2810
2670
|
|
|
2811
2671
|
// src/core/ApiClient.ts
|
|
2812
|
-
var SDK_VERSION = "1.
|
|
2672
|
+
var SDK_VERSION = "1.5.0";
|
|
2813
2673
|
var _ApiClient = class _ApiClient {
|
|
2814
2674
|
constructor(appKey, apiUrl, debug = false, environment = "Production", offlineQueueEnabled = true, timeout = 3e4) {
|
|
2815
2675
|
this.cache = new ApiCache();
|
|
2676
|
+
this.eventBatch = [];
|
|
2677
|
+
this.batchTimer = null;
|
|
2678
|
+
this.BATCH_MAX_SIZE = 10;
|
|
2679
|
+
this.BATCH_FLUSH_MS = 5e3;
|
|
2816
2680
|
this.appKey = appKey;
|
|
2817
2681
|
this.baseUrl = apiUrl;
|
|
2818
2682
|
this.webUrl = deriveWebUrl(this.baseUrl);
|
|
@@ -2865,30 +2729,79 @@ var _ApiClient = class _ApiClient {
|
|
|
2865
2729
|
message,
|
|
2866
2730
|
context,
|
|
2867
2731
|
sdkVersion: SDK_VERSION,
|
|
2868
|
-
platform:
|
|
2732
|
+
platform: Platform4.OS
|
|
2869
2733
|
}, true);
|
|
2870
2734
|
} catch (err) {
|
|
2871
2735
|
if (this.debug) console.warn("[Paywallo:ApiClient] reportError failed", err);
|
|
2872
2736
|
}
|
|
2873
2737
|
}
|
|
2874
2738
|
async trackEvent(eventName, distinctId, properties, timestamp) {
|
|
2875
|
-
|
|
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 = {
|
|
2876
2746
|
eventName,
|
|
2877
2747
|
distinctId,
|
|
2878
|
-
properties: { ...properties, platform:
|
|
2748
|
+
properties: { ...properties, platform: Platform4.OS },
|
|
2879
2749
|
timestamp: timestamp ?? Date.now(),
|
|
2880
2750
|
environment: this.environment.toLowerCase()
|
|
2881
|
-
}
|
|
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
|
+
}
|
|
2882
2792
|
}
|
|
2883
2793
|
async identify(distinctId, properties, email, deviceId) {
|
|
2884
|
-
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");
|
|
2885
2795
|
}
|
|
2886
|
-
async startSession(distinctId, sessionId, platform) {
|
|
2796
|
+
async startSession(distinctId, sessionId, platform, appVersion, deviceModel, osVersion) {
|
|
2887
2797
|
await this.postWithQueue("/api/v1/sessions/start", {
|
|
2888
2798
|
distinctId,
|
|
2889
2799
|
sessionId,
|
|
2890
|
-
platform: platform ??
|
|
2891
|
-
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)
|
|
2892
2805
|
}, `session.start:${sessionId}`);
|
|
2893
2806
|
return { success: true };
|
|
2894
2807
|
}
|
|
@@ -2908,6 +2821,7 @@ var _ApiClient = class _ApiClient {
|
|
|
2908
2821
|
try {
|
|
2909
2822
|
const res = await this.get(`/api/v1/flags/${flagKey}?distinctId=${encodeURIComponent(distinctId)}`);
|
|
2910
2823
|
if (res.status === 404) {
|
|
2824
|
+
this.log("Flag not found (404):", flagKey);
|
|
2911
2825
|
const v = { variant: null };
|
|
2912
2826
|
this.cache.setVariant(key, v, this.cache.nullTTL);
|
|
2913
2827
|
return v;
|
|
@@ -2934,6 +2848,7 @@ var _ApiClient = class _ApiClient {
|
|
|
2934
2848
|
try {
|
|
2935
2849
|
const res = await this.get(`/api/v1/paywalls/${placement}`);
|
|
2936
2850
|
if (res.status === 404) {
|
|
2851
|
+
this.log("Paywall not found (404):", placement);
|
|
2937
2852
|
this.cache.setPaywall(placement, null, this.cache.nullTTL);
|
|
2938
2853
|
return null;
|
|
2939
2854
|
}
|
|
@@ -2985,6 +2900,7 @@ var _ApiClient = class _ApiClient {
|
|
|
2985
2900
|
try {
|
|
2986
2901
|
const res = await this.get(`/campaigns/public/primary?distinctId=${encodeURIComponent(distinctId)}`);
|
|
2987
2902
|
if (res.status === 404) {
|
|
2903
|
+
this.log("Primary campaign not found (404)");
|
|
2988
2904
|
this.cache.setCampaign(key, null, this.cache.nullTTL);
|
|
2989
2905
|
return null;
|
|
2990
2906
|
}
|
|
@@ -3035,7 +2951,7 @@ var _ApiClient = class _ApiClient {
|
|
|
3035
2951
|
headers: { "X-App-Key": this.appKey, "x-distinct-id": distinctId }
|
|
3036
2952
|
});
|
|
3037
2953
|
if (!res.ok) {
|
|
3038
|
-
this.log("
|
|
2954
|
+
this.log("evaluateFlags failed:", res.status, keys);
|
|
3039
2955
|
return {};
|
|
3040
2956
|
}
|
|
3041
2957
|
const raw = res.data;
|
|
@@ -3193,7 +3109,7 @@ var CampaignGateService = class {
|
|
|
3193
3109
|
const isActive = await hasActive();
|
|
3194
3110
|
if (PaywalloClient.getConfig()?.debug) console.info(`[Paywallo] requireSubscription \u2014 hasActive: ${isActive}`);
|
|
3195
3111
|
if (isActive) return true;
|
|
3196
|
-
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 });
|
|
3197
3113
|
return r.purchased || r.restored;
|
|
3198
3114
|
}
|
|
3199
3115
|
async preloadCampaign(apiClient, placement, context) {
|
|
@@ -3231,7 +3147,7 @@ var CampaignGateService = class {
|
|
|
3231
3147
|
var campaignGateService = new CampaignGateService();
|
|
3232
3148
|
|
|
3233
3149
|
// src/domains/identity/AdvertisingIdManager.ts
|
|
3234
|
-
import { Platform as
|
|
3150
|
+
import { Platform as Platform5 } from "react-native";
|
|
3235
3151
|
var ZERO_IDFA = "00000000-0000-0000-0000-000000000000";
|
|
3236
3152
|
var AdvertisingIdManager = class {
|
|
3237
3153
|
constructor() {
|
|
@@ -3242,7 +3158,7 @@ var AdvertisingIdManager = class {
|
|
|
3242
3158
|
const fallback = { idfv: null, idfa: null, gaid: null, attStatus: "unavailable" };
|
|
3243
3159
|
try {
|
|
3244
3160
|
const tracking = await import("expo-tracking-transparency");
|
|
3245
|
-
if (
|
|
3161
|
+
if (Platform5.OS === "ios") {
|
|
3246
3162
|
if (!tracking.isAvailable()) {
|
|
3247
3163
|
const idfv2 = await this.collectIdfv();
|
|
3248
3164
|
this.cached = { ...fallback, idfv: idfv2 };
|
|
@@ -3261,7 +3177,7 @@ var AdvertisingIdManager = class {
|
|
|
3261
3177
|
this.cached = { idfv, idfa, gaid: null, attStatus: status };
|
|
3262
3178
|
return this.cached;
|
|
3263
3179
|
}
|
|
3264
|
-
if (
|
|
3180
|
+
if (Platform5.OS === "android") {
|
|
3265
3181
|
const androidStatus = await tracking.getTrackingPermissionsAsync();
|
|
3266
3182
|
const optedIn = androidStatus.granted;
|
|
3267
3183
|
const rawGaid = optedIn ? tracking.getAdvertisingId() : null;
|
|
@@ -3347,11 +3263,11 @@ var MetaBridge = class {
|
|
|
3347
3263
|
var metaBridge = new MetaBridge();
|
|
3348
3264
|
|
|
3349
3265
|
// src/domains/identity/InstallReferrerManager.ts
|
|
3350
|
-
import { Platform as
|
|
3266
|
+
import { Platform as Platform6 } from "react-native";
|
|
3351
3267
|
var REFERRER_TIMEOUT_MS = 5e3;
|
|
3352
3268
|
var InstallReferrerManager = class {
|
|
3353
3269
|
async getReferrer() {
|
|
3354
|
-
if (
|
|
3270
|
+
if (Platform6.OS !== "android") {
|
|
3355
3271
|
return this.emptyResult();
|
|
3356
3272
|
}
|
|
3357
3273
|
let timerId;
|
|
@@ -3414,14 +3330,77 @@ var installReferrerManager = new InstallReferrerManager();
|
|
|
3414
3330
|
import { AppState as AppState2 } from "react-native";
|
|
3415
3331
|
init_uuid();
|
|
3416
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
|
+
|
|
3417
3378
|
// src/domains/session/sessionTracking.ts
|
|
3418
3379
|
async function trackSessionStart(apiClient, sessionId, sessionStartedAt) {
|
|
3419
3380
|
const distinctId = identityManager.getDistinctId();
|
|
3420
|
-
await
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
});
|
|
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
|
+
]);
|
|
3425
3404
|
}
|
|
3426
3405
|
async function trackSessionEnd(apiClient, sessionId, durationSeconds, sessionStartedAt) {
|
|
3427
3406
|
const distinctId = identityManager.getDistinctId();
|
|
@@ -3518,6 +3497,9 @@ var SessionManager = class {
|
|
|
3518
3497
|
if (!this.sessionId || !this.sessionStartedAt) {
|
|
3519
3498
|
return;
|
|
3520
3499
|
}
|
|
3500
|
+
if (this.apiClient) {
|
|
3501
|
+
await this.apiClient.flushEventBatch();
|
|
3502
|
+
}
|
|
3521
3503
|
await this.trackSessionEnd();
|
|
3522
3504
|
this.sessionId = null;
|
|
3523
3505
|
this.sessionStartedAt = null;
|
|
@@ -3602,6 +3584,9 @@ var SessionManager = class {
|
|
|
3602
3584
|
async handleBackground() {
|
|
3603
3585
|
this.backgroundTimestamp = Date.now();
|
|
3604
3586
|
await this.trackAppBackground();
|
|
3587
|
+
if (this.apiClient) {
|
|
3588
|
+
await this.apiClient.flushEventBatch();
|
|
3589
|
+
}
|
|
3605
3590
|
}
|
|
3606
3591
|
async checkEmergencyPaywall() {
|
|
3607
3592
|
if (!this.apiClient || !this.emergencyPaywallHandler) {
|
|
@@ -3957,7 +3942,6 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3957
3942
|
]);
|
|
3958
3943
|
this.apiClient = new ApiClient(config.appKey, config.apiUrl ?? "https://paywallo.com.br", config.debug, environment, offlineQueueEnabled, config.timeout);
|
|
3959
3944
|
queueProcessor.initialize(this.apiClient.getHttpClient(), { debug: config.debug });
|
|
3960
|
-
affiliateManager.initialize(this.apiClient, config.debug);
|
|
3961
3945
|
subscriptionManager.init({
|
|
3962
3946
|
serverUrl: this.apiClient.getBaseUrl(),
|
|
3963
3947
|
appKey: config.appKey,
|
|
@@ -3984,6 +3968,12 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3984
3968
|
this.resolveReady = null;
|
|
3985
3969
|
}
|
|
3986
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
|
+
}
|
|
3987
3977
|
if (this.apiClient) {
|
|
3988
3978
|
const apiClientRef = this.apiClient;
|
|
3989
3979
|
if (config.autoPreloadCampaign) {
|
|
@@ -4009,18 +3999,21 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
4009
3999
|
}
|
|
4010
4000
|
async identify(options) {
|
|
4011
4001
|
this.ensureInitialized();
|
|
4002
|
+
if (this.config?.debug) console.info("[Paywallo IDENTIFY]", options);
|
|
4012
4003
|
await identityManager.identify(options);
|
|
4013
4004
|
}
|
|
4014
4005
|
async track(eventName, options) {
|
|
4015
4006
|
const apiClient = this.getApiClientOrThrow();
|
|
4016
4007
|
const distinctId = identityManager.getDistinctId();
|
|
4017
4008
|
if (!distinctId) {
|
|
4009
|
+
if (this.config?.debug) console.info(`[Paywallo TRACK] ${eventName} skipped (no distinctId)`);
|
|
4018
4010
|
return;
|
|
4019
4011
|
}
|
|
4020
4012
|
if (!isValidEventName(eventName)) {
|
|
4021
4013
|
const msg = `Invalid event name: "${eventName}". Use snake_case or $reserved_name.`;
|
|
4022
4014
|
throw new ClientError(CLIENT_ERROR_CODES.INVALID_EVENT_NAME, msg);
|
|
4023
4015
|
}
|
|
4016
|
+
if (this.config?.debug && eventName.startsWith("$purchase")) console.info(`[Paywallo TRACK] ${eventName}`, options?.properties ?? {});
|
|
4024
4017
|
const sessionId = sessionManager.getSessionId();
|
|
4025
4018
|
await apiClient.trackEvent(eventName, distinctId, {
|
|
4026
4019
|
...identityManager.getProperties(),
|
|
@@ -4041,14 +4034,10 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
4041
4034
|
const now = Date.now();
|
|
4042
4035
|
const timeOnPreviousStep = this.lastOnboardingStepTimestamp !== null ? Math.round((now - this.lastOnboardingStepTimestamp) / 1e3) : 0;
|
|
4043
4036
|
this.lastOnboardingStepTimestamp = now;
|
|
4044
|
-
const sessionId = sessionManager.getSessionId();
|
|
4045
4037
|
await apiClient.trackEvent("$onboarding_step", distinctId, {
|
|
4046
|
-
...identityManager.getProperties(),
|
|
4047
4038
|
stepIndex: index,
|
|
4048
4039
|
stepName: name,
|
|
4049
|
-
|
|
4050
|
-
timeOnPreviousStep,
|
|
4051
|
-
...sessionId && { sessionId }
|
|
4040
|
+
timeOnPreviousStep
|
|
4052
4041
|
});
|
|
4053
4042
|
}
|
|
4054
4043
|
async coreAction(actionName) {
|
|
@@ -4083,10 +4072,14 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
4083
4072
|
try {
|
|
4084
4073
|
const distinctId = identityManager.getDistinctId();
|
|
4085
4074
|
if (!distinctId) {
|
|
4075
|
+
if (this.config?.debug) console.info(`[Paywallo FLAG] ${flagKey} \u2192 null (no distinctId)`);
|
|
4086
4076
|
return { variant: null };
|
|
4087
4077
|
}
|
|
4088
|
-
|
|
4078
|
+
const result = await this.getApiClientOrThrow().getVariant(flagKey, distinctId);
|
|
4079
|
+
if (this.config?.debug) console.info(`[Paywallo FLAG] ${flagKey} \u2192`, result.variant);
|
|
4080
|
+
return result;
|
|
4089
4081
|
} catch (error) {
|
|
4082
|
+
if (this.config?.debug) console.warn(`[Paywallo FLAG] ${flagKey} \u2192 error`, error);
|
|
4090
4083
|
if (this.apiClient) void this.apiClient.reportError("GET_VARIANT_FAILED", error instanceof Error ? error.message : String(error));
|
|
4091
4084
|
return { variant: null };
|
|
4092
4085
|
}
|
|
@@ -4119,21 +4112,34 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
4119
4112
|
}
|
|
4120
4113
|
async presentCampaign(placement, context) {
|
|
4121
4114
|
this.ensureInitialized();
|
|
4122
|
-
|
|
4115
|
+
if (this.config?.debug) console.info(`[Paywallo CAMPAIGN] presenting ${placement}`);
|
|
4116
|
+
const result = await campaignGateService.presentCampaign(this.getApiClientOrThrow(), placement, () => this.hasActiveSubscription(), this.paywallPresenter, this.campaignPresenter, context);
|
|
4117
|
+
if (this.config?.debug) {
|
|
4118
|
+
if (result.error) console.warn(`[Paywallo CAMPAIGN] ${placement} error:`, result.error.message);
|
|
4119
|
+
else console.info(`[Paywallo CAMPAIGN] ${placement} result:`, { purchased: result.purchased, cancelled: result.cancelled, restored: result.restored, skippedReason: result.skippedReason });
|
|
4120
|
+
}
|
|
4121
|
+
return result;
|
|
4123
4122
|
}
|
|
4124
4123
|
async hasActiveSubscription() {
|
|
4125
4124
|
this.ensureInitialized();
|
|
4126
4125
|
try {
|
|
4127
4126
|
if (this.activeChecker) {
|
|
4128
|
-
|
|
4127
|
+
const result = await this.activeChecker();
|
|
4128
|
+
const isActive2 = result === true;
|
|
4129
|
+
if (this.config?.debug) console.info("[Paywallo SUB] hasActiveSubscription (checker):", isActive2);
|
|
4130
|
+
return isActive2;
|
|
4129
4131
|
}
|
|
4130
4132
|
const distinctId = identityManager.getDistinctId();
|
|
4131
4133
|
if (!distinctId || !this.apiClient) {
|
|
4134
|
+
if (this.config?.debug) console.info("[Paywallo SUB] hasActiveSubscription: false (no distinctId/apiClient)");
|
|
4132
4135
|
return false;
|
|
4133
4136
|
}
|
|
4134
4137
|
const status = await this.apiClient.getSubscriptionStatus(distinctId);
|
|
4135
|
-
|
|
4138
|
+
const isActive = status.hasActiveSubscription === true;
|
|
4139
|
+
if (this.config?.debug) console.info("[Paywallo SUB] hasActiveSubscription (server):", isActive);
|
|
4140
|
+
return isActive;
|
|
4136
4141
|
} catch (error) {
|
|
4142
|
+
if (this.config?.debug) console.warn("[Paywallo SUB] hasActiveSubscription error", error);
|
|
4137
4143
|
if (this.apiClient) void this.apiClient.reportError("SUBSCRIPTION_CHECK_FAILED", error instanceof Error ? error.message : "hasActiveSubscription failed");
|
|
4138
4144
|
return false;
|
|
4139
4145
|
}
|
|
@@ -4154,7 +4160,10 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
4154
4160
|
async restorePurchases() {
|
|
4155
4161
|
this.ensureInitialized();
|
|
4156
4162
|
if (!this.restoreHandler) throw new PaywallError(PAYWALL_ERROR_CODES.NOT_INITIALIZED, "PaywalloProvider not mounted. Wrap your app with <PaywalloProvider>");
|
|
4157
|
-
|
|
4163
|
+
if (this.config?.debug) console.info("[Paywallo RESTORE] starting");
|
|
4164
|
+
const result = await this.restoreHandler();
|
|
4165
|
+
if (this.config?.debug) console.info("[Paywallo RESTORE] result:", { success: result.success, restoredProducts: result.restoredProducts?.length ?? 0 });
|
|
4166
|
+
return result;
|
|
4158
4167
|
}
|
|
4159
4168
|
async reset() {
|
|
4160
4169
|
await identityManager.reset();
|
|
@@ -4249,6 +4258,10 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
4249
4258
|
registerEmergencyPaywallHandler(h) {
|
|
4250
4259
|
sessionManager.registerEmergencyPaywallHandler(h);
|
|
4251
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
|
+
}
|
|
4252
4265
|
async requireSubscription(paywallPlacement) {
|
|
4253
4266
|
this.ensureInitialized();
|
|
4254
4267
|
if (await this.hasActiveSubscription()) return true;
|
|
@@ -4793,7 +4806,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
|
|
|
4793
4806
|
// src/hooks/useSubscriptionSync.ts
|
|
4794
4807
|
import { useCallback as useCallback12, useRef as useRef7, useState as useState11 } from "react";
|
|
4795
4808
|
var CACHE_TTL_MS2 = 5 * 60 * 1e3;
|
|
4796
|
-
var ACTIVE_STATUSES = ["active", "
|
|
4809
|
+
var ACTIVE_STATUSES = ["active", "in_grace_period"];
|
|
4797
4810
|
function toDomainSubscriptionInfo(sub) {
|
|
4798
4811
|
return {
|
|
4799
4812
|
id: "",
|
|
@@ -4812,7 +4825,7 @@ function toDomainResponse(status) {
|
|
|
4812
4825
|
return {
|
|
4813
4826
|
hasActiveSubscription: status.hasActiveSubscription,
|
|
4814
4827
|
subscription: status.subscription ? toDomainSubscriptionInfo(status.subscription) : null,
|
|
4815
|
-
entitlements: []
|
|
4828
|
+
entitlements: status.entitlements ?? []
|
|
4816
4829
|
};
|
|
4817
4830
|
}
|
|
4818
4831
|
function isSubscriptionStillActive(sub) {
|
|
@@ -4868,13 +4881,14 @@ function useSubscriptionSync() {
|
|
|
4868
4881
|
const persisted = await subscriptionCache.get(distinctId);
|
|
4869
4882
|
if (persisted && !persisted.isStale) {
|
|
4870
4883
|
const persistedSub = persisted.data.subscription;
|
|
4884
|
+
const persistedStatus = persistedSub?.status;
|
|
4871
4885
|
const sub = persistedSub ? {
|
|
4872
4886
|
productId: persistedSub.productId,
|
|
4873
|
-
status:
|
|
4887
|
+
status: persistedStatus,
|
|
4874
4888
|
expiresAt: persistedSub.expiresAt ?? null,
|
|
4875
4889
|
platform: persistedSub.platform,
|
|
4876
4890
|
autoRenewEnabled: persistedSub.willRenew,
|
|
4877
|
-
inGracePeriod:
|
|
4891
|
+
inGracePeriod: persistedStatus === "in_grace_period"
|
|
4878
4892
|
} : null;
|
|
4879
4893
|
const stillActive = isSubscriptionStillActive(sub);
|
|
4880
4894
|
if (stillActive) {
|
|
@@ -4894,7 +4908,7 @@ function useSubscriptionSync() {
|
|
|
4894
4908
|
cacheRef.current = { data: sub, expiresAt: Date.now() + CACHE_TTL_MS2 };
|
|
4895
4909
|
setSubscription(sub);
|
|
4896
4910
|
void subscriptionCache.set(distinctId, toDomainResponse(status));
|
|
4897
|
-
return status.hasActiveSubscription;
|
|
4911
|
+
return status.hasActiveSubscription === true;
|
|
4898
4912
|
} catch {
|
|
4899
4913
|
return await resolveOfflineFromCache(distinctId);
|
|
4900
4914
|
}
|
|
@@ -4912,14 +4926,14 @@ function useSubscriptionSync() {
|
|
|
4912
4926
|
const persisted = await subscriptionCache.get(distinctId);
|
|
4913
4927
|
if (!persisted?.data.subscription) return null;
|
|
4914
4928
|
const s = persisted.data.subscription;
|
|
4915
|
-
const
|
|
4929
|
+
const status = s.status;
|
|
4916
4930
|
return {
|
|
4917
4931
|
productId: s.productId,
|
|
4918
|
-
status
|
|
4932
|
+
status,
|
|
4919
4933
|
expiresAt: s.expiresAt ?? null,
|
|
4920
4934
|
platform: s.platform,
|
|
4921
4935
|
autoRenewEnabled: s.willRenew,
|
|
4922
|
-
inGracePeriod:
|
|
4936
|
+
inGracePeriod: status === "in_grace_period"
|
|
4923
4937
|
};
|
|
4924
4938
|
} catch {
|
|
4925
4939
|
return null;
|
|
@@ -5344,10 +5358,7 @@ function hasVariables(text) {
|
|
|
5344
5358
|
var Paywallo = PaywalloClient;
|
|
5345
5359
|
var index_default = Paywallo;
|
|
5346
5360
|
export {
|
|
5347
|
-
AFFILIATE_ERROR_CODES,
|
|
5348
5361
|
ANALYTICS_ERROR_CODES,
|
|
5349
|
-
AffiliateError,
|
|
5350
|
-
AffiliateManager,
|
|
5351
5362
|
AnalyticsError,
|
|
5352
5363
|
ApiClient,
|
|
5353
5364
|
CAMPAIGN_ERROR_CODES,
|
|
@@ -5365,7 +5376,6 @@ export {
|
|
|
5365
5376
|
PaywallError,
|
|
5366
5377
|
PaywallModal,
|
|
5367
5378
|
Paywallo,
|
|
5368
|
-
PaywalloAffiliate,
|
|
5369
5379
|
PaywalloClient,
|
|
5370
5380
|
PaywalloContext,
|
|
5371
5381
|
PaywalloError,
|
|
@@ -5375,7 +5385,6 @@ export {
|
|
|
5375
5385
|
SessionError,
|
|
5376
5386
|
SessionManager,
|
|
5377
5387
|
SubscriptionCache,
|
|
5378
|
-
affiliateManager,
|
|
5379
5388
|
createPurchaseError,
|
|
5380
5389
|
index_default as default,
|
|
5381
5390
|
detectDeviceLanguage,
|