capacitor-plugin-cdv-purchase 13.17.2 → 13.18.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/dist/index.js +272 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/www/store.d.ts +136 -1
- package/www/store.js +272 -1
package/dist/index.js
CHANGED
|
@@ -1656,6 +1656,276 @@ var CdvPurchase;
|
|
|
1656
1656
|
Utils.md5toUUID = md5toUUID;
|
|
1657
1657
|
})(CdvPurchase.Utils || (CdvPurchase.Utils = {}));
|
|
1658
1658
|
})(CdvPurchase || (CdvPurchase = {}));
|
|
1659
|
+
var CdvPurchase;
|
|
1660
|
+
(function (CdvPurchase) {
|
|
1661
|
+
/**
|
|
1662
|
+
* Persist a subset of {@link VerifiedPurchase} to device storage so that
|
|
1663
|
+
* `store.owned()` works when the device is offline or has just restarted
|
|
1664
|
+
* without connectivity.
|
|
1665
|
+
*
|
|
1666
|
+
* Phase 1 — unsigned cache. No JWT, no crypto, no server changes.
|
|
1667
|
+
*
|
|
1668
|
+
* @example
|
|
1669
|
+
* ```typescript
|
|
1670
|
+
* const offline = new CdvPurchase.OfflineEntitlements(store, {
|
|
1671
|
+
* gracePeriodMs: 30 * 24 * 60 * 60 * 1000, // 30 days
|
|
1672
|
+
* onExpiredOffline: 'readonly',
|
|
1673
|
+
* detectClockRollback: true,
|
|
1674
|
+
* });
|
|
1675
|
+
*
|
|
1676
|
+
* await offline.ready();
|
|
1677
|
+
*
|
|
1678
|
+
* function isUserPremium(): boolean {
|
|
1679
|
+
* return store.owned('premium') || offline.isOwned('premium');
|
|
1680
|
+
* }
|
|
1681
|
+
* ```
|
|
1682
|
+
*
|
|
1683
|
+
* **Durability warning:** for long-offline deployments (weeks without
|
|
1684
|
+
* connectivity), pass a file-based or secure-storage adapter. `localStorage`
|
|
1685
|
+
* (the default) can be evicted by the WebView under storage pressure, which
|
|
1686
|
+
* would silently lose the cached entitlements.
|
|
1687
|
+
*/
|
|
1688
|
+
class OfflineEntitlements {
|
|
1689
|
+
constructor(store, options = {}) {
|
|
1690
|
+
var _a, _b, _c, _d;
|
|
1691
|
+
/** In-memory cache of persisted entitlements, keyed by `platform:productId`. */
|
|
1692
|
+
this.cache = {};
|
|
1693
|
+
/** Last seen timestamp persisted with the data, used for clock rollback detection. */
|
|
1694
|
+
this.lastSeenTimestamp = 0;
|
|
1695
|
+
/** True once `ready()` has resolved. */
|
|
1696
|
+
this.isReady = false;
|
|
1697
|
+
/** Last event fired per productId, to deduplicate events on repeated isOwned() calls. */
|
|
1698
|
+
this.lastEventPerProduct = {};
|
|
1699
|
+
this.store = store;
|
|
1700
|
+
this.storage = (_a = options.storage) !== null && _a !== void 0 ? _a : OfflineEntitlements.createLocalStorageAdapter();
|
|
1701
|
+
this.gracePeriodMs = (_b = options.gracePeriodMs) !== null && _b !== void 0 ? _b : OfflineEntitlements.DEFAULT_GRACE_PERIOD_MS;
|
|
1702
|
+
this.onExpiredOffline = (_c = options.onExpiredOffline) !== null && _c !== void 0 ? _c : 'readonly';
|
|
1703
|
+
this.detectClockRollback = (_d = options.detectClockRollback) !== null && _d !== void 0 ? _d : false;
|
|
1704
|
+
this.eventCallbacks = new CdvPurchase.Internal.Callbacks(store.log, 'OfflineEntitlements');
|
|
1705
|
+
this.store.when().verified((receipt) => { void this.onVerified(receipt); });
|
|
1706
|
+
}
|
|
1707
|
+
/** Wrap the global `localStorage` as an async `OfflineStorageAdapter`. */
|
|
1708
|
+
static createLocalStorageAdapter() {
|
|
1709
|
+
return {
|
|
1710
|
+
getItem: (key) => Promise.resolve(localStorage.getItem(key)),
|
|
1711
|
+
setItem: (key, value) => { localStorage.setItem(key, value); return Promise.resolve(); },
|
|
1712
|
+
removeItem: (key) => { localStorage.removeItem(key); return Promise.resolve(); },
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
/** Load persisted entitlements from storage into the in-memory cache. Idempotent. */
|
|
1716
|
+
ready() {
|
|
1717
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1718
|
+
if (this.isReady)
|
|
1719
|
+
return;
|
|
1720
|
+
yield this.loadFromStorage();
|
|
1721
|
+
this.isReady = true;
|
|
1722
|
+
});
|
|
1723
|
+
}
|
|
1724
|
+
/** Reload from storage and re-evaluate. Resolves when the reload is complete. Call after reconnecting or manually. */
|
|
1725
|
+
refresh() {
|
|
1726
|
+
return this.loadFromStorage();
|
|
1727
|
+
}
|
|
1728
|
+
/** Register a callback for {@link OfflineEntitlementEvent}s. */
|
|
1729
|
+
onEvent(callback) {
|
|
1730
|
+
this.eventCallbacks.push(callback);
|
|
1731
|
+
}
|
|
1732
|
+
/** Remove all persisted entitlements from storage and clear the in-memory cache. For user logout. */
|
|
1733
|
+
clear() {
|
|
1734
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1735
|
+
this.cache = {};
|
|
1736
|
+
this.lastSeenTimestamp = 0;
|
|
1737
|
+
this.isReady = false;
|
|
1738
|
+
this.lastEventPerProduct = {};
|
|
1739
|
+
yield this.storage.removeItem(OfflineEntitlements.STORAGE_KEY);
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1742
|
+
/**
|
|
1743
|
+
* Return `true` if the product is owned, using the persisted cache when
|
|
1744
|
+
* the in-memory `verifiedReceipts` don't grant ownership.
|
|
1745
|
+
*
|
|
1746
|
+
* Returns `false` for all products until `ready()` has resolved.
|
|
1747
|
+
*/
|
|
1748
|
+
isOwned(productId) {
|
|
1749
|
+
if (!this.isReady) {
|
|
1750
|
+
this.store.log.warn('OfflineEntitlements.isOwned("' + productId + '") called before ready() — returning false. Call await offline.ready() at startup.');
|
|
1751
|
+
return false;
|
|
1752
|
+
}
|
|
1753
|
+
// 1. If store.verifiedReceipts has a valid (non-expired) entry, return true.
|
|
1754
|
+
const online = CdvPurchase.Internal.VerifiedReceipts.isOwned(this.store.verifiedReceipts, { id: productId });
|
|
1755
|
+
if (online)
|
|
1756
|
+
return true;
|
|
1757
|
+
const now = +new Date();
|
|
1758
|
+
// 2. Find persisted purchase for this productId across all platforms.
|
|
1759
|
+
const persisted = this.findPersisted(productId);
|
|
1760
|
+
if (!persisted) {
|
|
1761
|
+
// No persisted entitlement.
|
|
1762
|
+
this.fireEvent('entitlement_missing', productId, 'No persisted entitlement for this product.');
|
|
1763
|
+
return false;
|
|
1764
|
+
}
|
|
1765
|
+
// 3. Branch by product type.
|
|
1766
|
+
// Subscriptions have an expiryDate; non-consumables don't.
|
|
1767
|
+
if (persisted.expiryDate !== undefined && persisted.expiryDate !== null) {
|
|
1768
|
+
// Subscription
|
|
1769
|
+
// Clock rollback detection — scoped to subscriptions (non-consumables have no time component).
|
|
1770
|
+
if (this.detectClockRollback && this.lastSeenTimestamp > now) {
|
|
1771
|
+
this.fireEvent('clock_rollback', persisted.id, 'Clock appears to have rolled back; denying offline entitlement.');
|
|
1772
|
+
return false;
|
|
1773
|
+
}
|
|
1774
|
+
if (persisted.isExpired) {
|
|
1775
|
+
this.fireEvent('expired', persisted.id, 'Subscription is marked as expired.');
|
|
1776
|
+
return false;
|
|
1777
|
+
}
|
|
1778
|
+
if (now < persisted.expiryDate) {
|
|
1779
|
+
return true;
|
|
1780
|
+
}
|
|
1781
|
+
const renewalIntent = persisted.renewalIntent;
|
|
1782
|
+
if (renewalIntent === CdvPurchase.RenewalIntent.RENEW && now < persisted.expiryDate + this.gracePeriodMs) {
|
|
1783
|
+
this.fireEvent('grace', persisted.id, 'Subscription expired but within grace period.');
|
|
1784
|
+
return true;
|
|
1785
|
+
}
|
|
1786
|
+
if (renewalIntent === CdvPurchase.RenewalIntent.LAPSE) {
|
|
1787
|
+
// Lapsed subscriptions deny at expiryDate.
|
|
1788
|
+
return false;
|
|
1789
|
+
}
|
|
1790
|
+
// Unknown/undefined renewalIntent: fall through to onExpiredOffline.
|
|
1791
|
+
if (this.onExpiredOffline === 'readonly') {
|
|
1792
|
+
this.fireEvent('readonly', persisted.id, 'Subscription expired and grace period elapsed; granting readonly access.');
|
|
1793
|
+
return true;
|
|
1794
|
+
}
|
|
1795
|
+
return false;
|
|
1796
|
+
}
|
|
1797
|
+
else {
|
|
1798
|
+
// Non-consumable: never hard-expires, no clock rollback check.
|
|
1799
|
+
return true;
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
/**
|
|
1803
|
+
* Retrieve the persisted entitlement for a product, analogous to
|
|
1804
|
+
* `store.findInVerifiedReceipts()`.
|
|
1805
|
+
*
|
|
1806
|
+
* Returns the {@link PersistedPurchase} (with `expiryDate`,
|
|
1807
|
+
* `renewalIntent`, `lastRenewalDate`, etc.) if one has been cached,
|
|
1808
|
+
* or `undefined` if no offline entitlement exists for this product.
|
|
1809
|
+
*
|
|
1810
|
+
* Returns `undefined` for all products until `ready()` has resolved.
|
|
1811
|
+
*
|
|
1812
|
+
* @example
|
|
1813
|
+
* ```typescript
|
|
1814
|
+
* const entitlement = offline.find('premium');
|
|
1815
|
+
* if (entitlement?.expiryDate) {
|
|
1816
|
+
* const daysLeft = Math.ceil((entitlement.expiryDate - Date.now()) / 86400000);
|
|
1817
|
+
* showRenewalBanner(daysLeft);
|
|
1818
|
+
* }
|
|
1819
|
+
* ```
|
|
1820
|
+
*/
|
|
1821
|
+
find(productId) {
|
|
1822
|
+
if (!this.isReady)
|
|
1823
|
+
return undefined;
|
|
1824
|
+
return this.findPersisted(productId);
|
|
1825
|
+
}
|
|
1826
|
+
/** Persist all `VerifiedPurchase`s in a verified receipt to storage. */
|
|
1827
|
+
onVerified(receipt) {
|
|
1828
|
+
var _a;
|
|
1829
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1830
|
+
for (const purchase of receipt.collection) {
|
|
1831
|
+
// Skip consumables — they don't need offline entitlement.
|
|
1832
|
+
if (purchase.isConsumed)
|
|
1833
|
+
continue;
|
|
1834
|
+
const platform = (_a = purchase.platform) !== null && _a !== void 0 ? _a : receipt.platform;
|
|
1835
|
+
const product = this.store.get(purchase.id, platform);
|
|
1836
|
+
if (product && product.type === CdvPurchase.ProductType.CONSUMABLE)
|
|
1837
|
+
continue;
|
|
1838
|
+
const key = platform + ':' + purchase.id;
|
|
1839
|
+
this.cache[key] = {
|
|
1840
|
+
id: purchase.id,
|
|
1841
|
+
platform,
|
|
1842
|
+
expiryDate: purchase.expiryDate,
|
|
1843
|
+
isExpired: purchase.isExpired,
|
|
1844
|
+
renewalIntent: purchase.renewalIntent,
|
|
1845
|
+
lastRenewalDate: purchase.lastRenewalDate,
|
|
1846
|
+
purchaseDate: purchase.purchaseDate,
|
|
1847
|
+
cancelationReason: purchase.cancelationReason,
|
|
1848
|
+
isBillingRetryPeriod: purchase.isBillingRetryPeriod,
|
|
1849
|
+
};
|
|
1850
|
+
}
|
|
1851
|
+
this.lastSeenTimestamp = +new Date();
|
|
1852
|
+
yield this.saveToStorage();
|
|
1853
|
+
});
|
|
1854
|
+
}
|
|
1855
|
+
/** Find the persisted purchase for a productId across all platforms. */
|
|
1856
|
+
findPersisted(productId) {
|
|
1857
|
+
var _a, _b, _c, _d;
|
|
1858
|
+
let found;
|
|
1859
|
+
for (const key of Object.keys(this.cache)) {
|
|
1860
|
+
const entry = this.cache[key];
|
|
1861
|
+
if (entry.id === productId) {
|
|
1862
|
+
if (!found || ((_b = (_a = found.lastRenewalDate) !== null && _a !== void 0 ? _a : found.purchaseDate) !== null && _b !== void 0 ? _b : 0) < ((_d = (_c = entry.lastRenewalDate) !== null && _c !== void 0 ? _c : entry.purchaseDate) !== null && _d !== void 0 ? _d : 0)) {
|
|
1863
|
+
found = entry;
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
return found;
|
|
1868
|
+
}
|
|
1869
|
+
/** Load the persisted payload from storage into the in-memory cache. */
|
|
1870
|
+
loadFromStorage() {
|
|
1871
|
+
var _a, _b;
|
|
1872
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1873
|
+
try {
|
|
1874
|
+
const raw = yield this.storage.getItem(OfflineEntitlements.STORAGE_KEY);
|
|
1875
|
+
if (!raw) {
|
|
1876
|
+
this.cache = {};
|
|
1877
|
+
this.lastSeenTimestamp = 0;
|
|
1878
|
+
return;
|
|
1879
|
+
}
|
|
1880
|
+
const parsed = JSON.parse(raw);
|
|
1881
|
+
if (parsed.schemaVersion !== OfflineEntitlements.SCHEMA_VERSION) {
|
|
1882
|
+
this.store.log.warn('OfflineEntitlements: schema version mismatch, ignoring cached data.');
|
|
1883
|
+
this.cache = {};
|
|
1884
|
+
this.lastSeenTimestamp = 0;
|
|
1885
|
+
return;
|
|
1886
|
+
}
|
|
1887
|
+
this.cache = (_a = parsed.receipts) !== null && _a !== void 0 ? _a : {};
|
|
1888
|
+
this.lastSeenTimestamp = (_b = parsed.lastSeenTimestamp) !== null && _b !== void 0 ? _b : 0;
|
|
1889
|
+
}
|
|
1890
|
+
catch (err) {
|
|
1891
|
+
this.store.log.warn('OfflineEntitlements: failed to load from storage: ' + err.message);
|
|
1892
|
+
this.cache = {};
|
|
1893
|
+
this.lastSeenTimestamp = 0;
|
|
1894
|
+
}
|
|
1895
|
+
});
|
|
1896
|
+
}
|
|
1897
|
+
/** Serialize the in-memory cache to storage. */
|
|
1898
|
+
saveToStorage() {
|
|
1899
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1900
|
+
const payload = {
|
|
1901
|
+
receipts: this.cache,
|
|
1902
|
+
lastSeenTimestamp: this.lastSeenTimestamp,
|
|
1903
|
+
schemaVersion: OfflineEntitlements.SCHEMA_VERSION,
|
|
1904
|
+
};
|
|
1905
|
+
try {
|
|
1906
|
+
yield this.storage.setItem(OfflineEntitlements.STORAGE_KEY, JSON.stringify(payload));
|
|
1907
|
+
}
|
|
1908
|
+
catch (err) {
|
|
1909
|
+
this.store.log.warn('OfflineEntitlements: failed to save to storage: ' + err.message);
|
|
1910
|
+
}
|
|
1911
|
+
});
|
|
1912
|
+
}
|
|
1913
|
+
/** Fire an event to all registered callbacks, deduplicating per productId. */
|
|
1914
|
+
fireEvent(type, productId, message) {
|
|
1915
|
+
if (this.lastEventPerProduct[productId] === type)
|
|
1916
|
+
return;
|
|
1917
|
+
this.lastEventPerProduct[productId] = type;
|
|
1918
|
+
this.eventCallbacks.trigger({ type, productId, message }, 'offline_entitlements');
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
/** Storage key used in the storage adapter. */
|
|
1922
|
+
OfflineEntitlements.STORAGE_KEY = 'cdvpurchase.offline_entitlements';
|
|
1923
|
+
/** Schema version for the persisted payload. */
|
|
1924
|
+
OfflineEntitlements.SCHEMA_VERSION = 1;
|
|
1925
|
+
/** Default grace period: 30 days. */
|
|
1926
|
+
OfflineEntitlements.DEFAULT_GRACE_PERIOD_MS = 30 * 24 * 60 * 60 * 1000;
|
|
1927
|
+
CdvPurchase.OfflineEntitlements = OfflineEntitlements;
|
|
1928
|
+
})(CdvPurchase || (CdvPurchase = {}));
|
|
1659
1929
|
/// <reference path="types.ts" />
|
|
1660
1930
|
/// <reference path="utils/compatibility.ts" />
|
|
1661
1931
|
/// <reference path="utils/platform-name.ts" />
|
|
@@ -1671,6 +1941,7 @@ var CdvPurchase;
|
|
|
1671
1941
|
/// <reference path="internal/receipts-monitor.ts" />
|
|
1672
1942
|
/// <reference path="internal/expiry-monitor.ts" />
|
|
1673
1943
|
/// <reference path="utils/to-uuid.ts" />
|
|
1944
|
+
/// <reference path="offline-entitlements.ts" />
|
|
1674
1945
|
/**
|
|
1675
1946
|
* Namespace for the cordova-plugin-purchase plugin.
|
|
1676
1947
|
*
|
|
@@ -1704,7 +1975,7 @@ var CdvPurchase;
|
|
|
1704
1975
|
/**
|
|
1705
1976
|
* Current release number of the plugin.
|
|
1706
1977
|
*/
|
|
1707
|
-
CdvPurchase.PLUGIN_VERSION = '13.
|
|
1978
|
+
CdvPurchase.PLUGIN_VERSION = '13.18.0';
|
|
1708
1979
|
/**
|
|
1709
1980
|
* Entry class of the plugin.
|
|
1710
1981
|
*/
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["esm/index.js","../build/entry.mjs"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\n// Set the marker so the shared store.js knows Capacitor native bridges are available\nif (typeof window !== 'undefined') {\n window.CdvPurchaseCapacitor = { installed: true };\n}\nconst PurchasePlugin = registerPlugin('PurchasePlugin');\nexport { PurchasePlugin };\n","// Capacitor bridge — sets CdvPurchaseCapacitor marker and registers native plugin\nexport { PurchasePlugin } from '../dist/esm/index.js';\n\n// Store runtime — executes the IIFE, creates window.CdvPurchase\nimport { CdvPurchase } from 'virtual:store-js';\n\n// Re-export the full namespace\nexport { CdvPurchase };\n\n// Convenience named exports\nexport const store = CdvPurchase.store;\nexport const Store = CdvPurchase.Store;\nexport const ProductType = CdvPurchase.ProductType;\nexport const Platform = CdvPurchase.Platform;\nexport const LogLevel = CdvPurchase.LogLevel;\nexport const ErrorCode = CdvPurchase.ErrorCode;\nexport const Logger = CdvPurchase.Logger;\nexport const Iaptic = CdvPurchase.Iaptic;\n"],"names":[],"mappings":";;AACA;AACA,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;AACnC,IAAI,MAAM,CAAC,oBAAoB,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE;AACrD;AACK,MAAC,cAAc,GAAG,cAAc,CAAC,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACLtD;;AASA;AACY,MAAC,KAAK,GAAG,WAAW,CAAC;AACrB,MAAC,KAAK,GAAG,WAAW,CAAC;AACrB,MAAC,WAAW,GAAG,WAAW,CAAC;AAC3B,MAAC,QAAQ,GAAG,WAAW,CAAC;AACxB,MAAC,QAAQ,GAAG,WAAW,CAAC;AACxB,MAAC,SAAS,GAAG,WAAW,CAAC;AACzB,MAAC,MAAM,GAAG,WAAW,CAAC;AACtB,MAAC,MAAM,GAAG,WAAW,CAAC;;;;"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["esm/index.js","../build/entry.mjs"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\n// Set the marker so the shared store.js knows Capacitor native bridges are available\nif (typeof window !== 'undefined') {\n window.CdvPurchaseCapacitor = { installed: true };\n}\nconst PurchasePlugin = registerPlugin('PurchasePlugin');\nexport { PurchasePlugin };\n","// Capacitor bridge — sets CdvPurchaseCapacitor marker and registers native plugin\nexport { PurchasePlugin } from '../dist/esm/index.js';\n\n// Store runtime — executes the IIFE, creates window.CdvPurchase\nimport { CdvPurchase } from 'virtual:store-js';\n\n// Re-export the full namespace\nexport { CdvPurchase };\n\n// Convenience named exports\nexport const store = CdvPurchase.store;\nexport const Store = CdvPurchase.Store;\nexport const ProductType = CdvPurchase.ProductType;\nexport const Platform = CdvPurchase.Platform;\nexport const LogLevel = CdvPurchase.LogLevel;\nexport const ErrorCode = CdvPurchase.ErrorCode;\nexport const Logger = CdvPurchase.Logger;\nexport const Iaptic = CdvPurchase.Iaptic;\n"],"names":[],"mappings":";;AACA;AACA,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;AACnC,IAAI,MAAM,CAAC,oBAAoB,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE;AACrD;AACK,MAAC,cAAc,GAAG,cAAc,CAAC,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACLtD;;AASA;AACY,MAAC,KAAK,GAAG,WAAW,CAAC;AACrB,MAAC,KAAK,GAAG,WAAW,CAAC;AACrB,MAAC,WAAW,GAAG,WAAW,CAAC;AAC3B,MAAC,QAAQ,GAAG,WAAW,CAAC;AACxB,MAAC,QAAQ,GAAG,WAAW,CAAC;AACxB,MAAC,SAAS,GAAG,WAAW,CAAC;AACzB,MAAC,MAAM,GAAG,WAAW,CAAC;AACtB,MAAC,MAAM,GAAG,WAAW,CAAC;;;;"}
|
package/package.json
CHANGED
package/www/store.d.ts
CHANGED
|
@@ -879,6 +879,141 @@ declare namespace CdvPurchase {
|
|
|
879
879
|
function md5toUUID(str: string): string;
|
|
880
880
|
}
|
|
881
881
|
}
|
|
882
|
+
declare namespace CdvPurchase {
|
|
883
|
+
/** Storage adapter for persisting offline entitlements. */
|
|
884
|
+
interface OfflineStorageAdapter {
|
|
885
|
+
getItem(key: string): Promise<string | null>;
|
|
886
|
+
setItem(key: string, value: string): Promise<void>;
|
|
887
|
+
removeItem(key: string): Promise<void>;
|
|
888
|
+
}
|
|
889
|
+
/** Options for {@link OfflineEntitlements}. */
|
|
890
|
+
interface OfflineEntitlementsOptions {
|
|
891
|
+
/** Storage adapter. Defaults to a `localStorage` wrapper. */
|
|
892
|
+
storage?: OfflineStorageAdapter;
|
|
893
|
+
/** Grace period in milliseconds after a subscription's expiryDate during which it's still considered owned. Defaults to 30 days. */
|
|
894
|
+
gracePeriodMs?: number;
|
|
895
|
+
/** Behavior when the grace period has elapsed and the device is still offline. `'readonly'` keeps granting access; `'deny'` revokes it. Defaults to `'readonly'`. */
|
|
896
|
+
onExpiredOffline?: 'deny' | 'readonly';
|
|
897
|
+
/** If true, detect clock rollback (persisted lastSeenTimestamp is in the future relative to now) and deny access. */
|
|
898
|
+
detectClockRollback?: boolean;
|
|
899
|
+
}
|
|
900
|
+
/** Event emitted by {@link OfflineEntitlements} when evaluating ownership offline. */
|
|
901
|
+
interface OfflineEntitlementEvent {
|
|
902
|
+
type: 'grace' | 'readonly' | 'clock_rollback' | 'entitlement_missing' | 'expired';
|
|
903
|
+
productId: string;
|
|
904
|
+
message: string;
|
|
905
|
+
}
|
|
906
|
+
/**
|
|
907
|
+
* Persist a subset of {@link VerifiedPurchase} to device storage so that
|
|
908
|
+
* `store.owned()` works when the device is offline or has just restarted
|
|
909
|
+
* without connectivity.
|
|
910
|
+
*
|
|
911
|
+
* Phase 1 — unsigned cache. No JWT, no crypto, no server changes.
|
|
912
|
+
*
|
|
913
|
+
* @example
|
|
914
|
+
* ```typescript
|
|
915
|
+
* const offline = new CdvPurchase.OfflineEntitlements(store, {
|
|
916
|
+
* gracePeriodMs: 30 * 24 * 60 * 60 * 1000, // 30 days
|
|
917
|
+
* onExpiredOffline: 'readonly',
|
|
918
|
+
* detectClockRollback: true,
|
|
919
|
+
* });
|
|
920
|
+
*
|
|
921
|
+
* await offline.ready();
|
|
922
|
+
*
|
|
923
|
+
* function isUserPremium(): boolean {
|
|
924
|
+
* return store.owned('premium') || offline.isOwned('premium');
|
|
925
|
+
* }
|
|
926
|
+
* ```
|
|
927
|
+
*
|
|
928
|
+
* **Durability warning:** for long-offline deployments (weeks without
|
|
929
|
+
* connectivity), pass a file-based or secure-storage adapter. `localStorage`
|
|
930
|
+
* (the default) can be evicted by the WebView under storage pressure, which
|
|
931
|
+
* would silently lose the cached entitlements.
|
|
932
|
+
*/
|
|
933
|
+
class OfflineEntitlements {
|
|
934
|
+
/** Storage key used in the storage adapter. */
|
|
935
|
+
private static STORAGE_KEY;
|
|
936
|
+
/** Schema version for the persisted payload. */
|
|
937
|
+
private static SCHEMA_VERSION;
|
|
938
|
+
/** Default grace period: 30 days. */
|
|
939
|
+
private static DEFAULT_GRACE_PERIOD_MS;
|
|
940
|
+
private store;
|
|
941
|
+
private storage;
|
|
942
|
+
private gracePeriodMs;
|
|
943
|
+
private onExpiredOffline;
|
|
944
|
+
private detectClockRollback;
|
|
945
|
+
/** In-memory cache of persisted entitlements, keyed by `platform:productId`. */
|
|
946
|
+
private cache;
|
|
947
|
+
/** Last seen timestamp persisted with the data, used for clock rollback detection. */
|
|
948
|
+
private lastSeenTimestamp;
|
|
949
|
+
/** True once `ready()` has resolved. */
|
|
950
|
+
private isReady;
|
|
951
|
+
/** Event callbacks. */
|
|
952
|
+
private eventCallbacks;
|
|
953
|
+
/** Last event fired per productId, to deduplicate events on repeated isOwned() calls. */
|
|
954
|
+
private lastEventPerProduct;
|
|
955
|
+
constructor(store: Store, options?: OfflineEntitlementsOptions);
|
|
956
|
+
/** Wrap the global `localStorage` as an async `OfflineStorageAdapter`. */
|
|
957
|
+
private static createLocalStorageAdapter;
|
|
958
|
+
/** Load persisted entitlements from storage into the in-memory cache. Idempotent. */
|
|
959
|
+
ready(): Promise<void>;
|
|
960
|
+
/** Reload from storage and re-evaluate. Resolves when the reload is complete. Call after reconnecting or manually. */
|
|
961
|
+
refresh(): Promise<void>;
|
|
962
|
+
/** Register a callback for {@link OfflineEntitlementEvent}s. */
|
|
963
|
+
onEvent(callback: Callback<OfflineEntitlementEvent>): void;
|
|
964
|
+
/** Remove all persisted entitlements from storage and clear the in-memory cache. For user logout. */
|
|
965
|
+
clear(): Promise<void>;
|
|
966
|
+
/**
|
|
967
|
+
* Return `true` if the product is owned, using the persisted cache when
|
|
968
|
+
* the in-memory `verifiedReceipts` don't grant ownership.
|
|
969
|
+
*
|
|
970
|
+
* Returns `false` for all products until `ready()` has resolved.
|
|
971
|
+
*/
|
|
972
|
+
isOwned(productId: string): boolean;
|
|
973
|
+
/**
|
|
974
|
+
* Retrieve the persisted entitlement for a product, analogous to
|
|
975
|
+
* `store.findInVerifiedReceipts()`.
|
|
976
|
+
*
|
|
977
|
+
* Returns the {@link PersistedPurchase} (with `expiryDate`,
|
|
978
|
+
* `renewalIntent`, `lastRenewalDate`, etc.) if one has been cached,
|
|
979
|
+
* or `undefined` if no offline entitlement exists for this product.
|
|
980
|
+
*
|
|
981
|
+
* Returns `undefined` for all products until `ready()` has resolved.
|
|
982
|
+
*
|
|
983
|
+
* @example
|
|
984
|
+
* ```typescript
|
|
985
|
+
* const entitlement = offline.find('premium');
|
|
986
|
+
* if (entitlement?.expiryDate) {
|
|
987
|
+
* const daysLeft = Math.ceil((entitlement.expiryDate - Date.now()) / 86400000);
|
|
988
|
+
* showRenewalBanner(daysLeft);
|
|
989
|
+
* }
|
|
990
|
+
* ```
|
|
991
|
+
*/
|
|
992
|
+
find(productId: string): PersistedPurchase | undefined;
|
|
993
|
+
/** Persist all `VerifiedPurchase`s in a verified receipt to storage. */
|
|
994
|
+
private onVerified;
|
|
995
|
+
/** Find the persisted purchase for a productId across all platforms. */
|
|
996
|
+
private findPersisted;
|
|
997
|
+
/** Load the persisted payload from storage into the in-memory cache. */
|
|
998
|
+
private loadFromStorage;
|
|
999
|
+
/** Serialize the in-memory cache to storage. */
|
|
1000
|
+
private saveToStorage;
|
|
1001
|
+
/** Fire an event to all registered callbacks, deduplicating per productId. */
|
|
1002
|
+
private fireEvent;
|
|
1003
|
+
}
|
|
1004
|
+
/** Persisted subset of {@link VerifiedPurchase}, stored by the offline entitlements layer. */
|
|
1005
|
+
interface PersistedPurchase {
|
|
1006
|
+
id: string;
|
|
1007
|
+
platform: Platform;
|
|
1008
|
+
expiryDate?: number;
|
|
1009
|
+
isExpired?: boolean;
|
|
1010
|
+
renewalIntent?: string;
|
|
1011
|
+
lastRenewalDate?: number;
|
|
1012
|
+
purchaseDate?: number;
|
|
1013
|
+
cancelationReason?: CancelationReason;
|
|
1014
|
+
isBillingRetryPeriod?: boolean;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
882
1017
|
/**
|
|
883
1018
|
* Namespace for the cordova-plugin-purchase plugin.
|
|
884
1019
|
*
|
|
@@ -911,7 +1046,7 @@ declare namespace CdvPurchase {
|
|
|
911
1046
|
/**
|
|
912
1047
|
* Current release number of the plugin.
|
|
913
1048
|
*/
|
|
914
|
-
const PLUGIN_VERSION = "13.
|
|
1049
|
+
const PLUGIN_VERSION = "13.18.0";
|
|
915
1050
|
/**
|
|
916
1051
|
* Entry class of the plugin.
|
|
917
1052
|
*/
|
package/www/store.js
CHANGED
|
@@ -1674,6 +1674,276 @@ var CdvPurchase;
|
|
|
1674
1674
|
Utils.md5toUUID = md5toUUID;
|
|
1675
1675
|
})(Utils = CdvPurchase.Utils || (CdvPurchase.Utils = {}));
|
|
1676
1676
|
})(CdvPurchase || (CdvPurchase = {}));
|
|
1677
|
+
var CdvPurchase;
|
|
1678
|
+
(function (CdvPurchase) {
|
|
1679
|
+
/**
|
|
1680
|
+
* Persist a subset of {@link VerifiedPurchase} to device storage so that
|
|
1681
|
+
* `store.owned()` works when the device is offline or has just restarted
|
|
1682
|
+
* without connectivity.
|
|
1683
|
+
*
|
|
1684
|
+
* Phase 1 — unsigned cache. No JWT, no crypto, no server changes.
|
|
1685
|
+
*
|
|
1686
|
+
* @example
|
|
1687
|
+
* ```typescript
|
|
1688
|
+
* const offline = new CdvPurchase.OfflineEntitlements(store, {
|
|
1689
|
+
* gracePeriodMs: 30 * 24 * 60 * 60 * 1000, // 30 days
|
|
1690
|
+
* onExpiredOffline: 'readonly',
|
|
1691
|
+
* detectClockRollback: true,
|
|
1692
|
+
* });
|
|
1693
|
+
*
|
|
1694
|
+
* await offline.ready();
|
|
1695
|
+
*
|
|
1696
|
+
* function isUserPremium(): boolean {
|
|
1697
|
+
* return store.owned('premium') || offline.isOwned('premium');
|
|
1698
|
+
* }
|
|
1699
|
+
* ```
|
|
1700
|
+
*
|
|
1701
|
+
* **Durability warning:** for long-offline deployments (weeks without
|
|
1702
|
+
* connectivity), pass a file-based or secure-storage adapter. `localStorage`
|
|
1703
|
+
* (the default) can be evicted by the WebView under storage pressure, which
|
|
1704
|
+
* would silently lose the cached entitlements.
|
|
1705
|
+
*/
|
|
1706
|
+
class OfflineEntitlements {
|
|
1707
|
+
constructor(store, options = {}) {
|
|
1708
|
+
var _a, _b, _c, _d;
|
|
1709
|
+
/** In-memory cache of persisted entitlements, keyed by `platform:productId`. */
|
|
1710
|
+
this.cache = {};
|
|
1711
|
+
/** Last seen timestamp persisted with the data, used for clock rollback detection. */
|
|
1712
|
+
this.lastSeenTimestamp = 0;
|
|
1713
|
+
/** True once `ready()` has resolved. */
|
|
1714
|
+
this.isReady = false;
|
|
1715
|
+
/** Last event fired per productId, to deduplicate events on repeated isOwned() calls. */
|
|
1716
|
+
this.lastEventPerProduct = {};
|
|
1717
|
+
this.store = store;
|
|
1718
|
+
this.storage = (_a = options.storage) !== null && _a !== void 0 ? _a : OfflineEntitlements.createLocalStorageAdapter();
|
|
1719
|
+
this.gracePeriodMs = (_b = options.gracePeriodMs) !== null && _b !== void 0 ? _b : OfflineEntitlements.DEFAULT_GRACE_PERIOD_MS;
|
|
1720
|
+
this.onExpiredOffline = (_c = options.onExpiredOffline) !== null && _c !== void 0 ? _c : 'readonly';
|
|
1721
|
+
this.detectClockRollback = (_d = options.detectClockRollback) !== null && _d !== void 0 ? _d : false;
|
|
1722
|
+
this.eventCallbacks = new CdvPurchase.Internal.Callbacks(store.log, 'OfflineEntitlements');
|
|
1723
|
+
this.store.when().verified((receipt) => { void this.onVerified(receipt); });
|
|
1724
|
+
}
|
|
1725
|
+
/** Wrap the global `localStorage` as an async `OfflineStorageAdapter`. */
|
|
1726
|
+
static createLocalStorageAdapter() {
|
|
1727
|
+
return {
|
|
1728
|
+
getItem: (key) => Promise.resolve(localStorage.getItem(key)),
|
|
1729
|
+
setItem: (key, value) => { localStorage.setItem(key, value); return Promise.resolve(); },
|
|
1730
|
+
removeItem: (key) => { localStorage.removeItem(key); return Promise.resolve(); },
|
|
1731
|
+
};
|
|
1732
|
+
}
|
|
1733
|
+
/** Load persisted entitlements from storage into the in-memory cache. Idempotent. */
|
|
1734
|
+
ready() {
|
|
1735
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1736
|
+
if (this.isReady)
|
|
1737
|
+
return;
|
|
1738
|
+
yield this.loadFromStorage();
|
|
1739
|
+
this.isReady = true;
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1742
|
+
/** Reload from storage and re-evaluate. Resolves when the reload is complete. Call after reconnecting or manually. */
|
|
1743
|
+
refresh() {
|
|
1744
|
+
return this.loadFromStorage();
|
|
1745
|
+
}
|
|
1746
|
+
/** Register a callback for {@link OfflineEntitlementEvent}s. */
|
|
1747
|
+
onEvent(callback) {
|
|
1748
|
+
this.eventCallbacks.push(callback);
|
|
1749
|
+
}
|
|
1750
|
+
/** Remove all persisted entitlements from storage and clear the in-memory cache. For user logout. */
|
|
1751
|
+
clear() {
|
|
1752
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1753
|
+
this.cache = {};
|
|
1754
|
+
this.lastSeenTimestamp = 0;
|
|
1755
|
+
this.isReady = false;
|
|
1756
|
+
this.lastEventPerProduct = {};
|
|
1757
|
+
yield this.storage.removeItem(OfflineEntitlements.STORAGE_KEY);
|
|
1758
|
+
});
|
|
1759
|
+
}
|
|
1760
|
+
/**
|
|
1761
|
+
* Return `true` if the product is owned, using the persisted cache when
|
|
1762
|
+
* the in-memory `verifiedReceipts` don't grant ownership.
|
|
1763
|
+
*
|
|
1764
|
+
* Returns `false` for all products until `ready()` has resolved.
|
|
1765
|
+
*/
|
|
1766
|
+
isOwned(productId) {
|
|
1767
|
+
if (!this.isReady) {
|
|
1768
|
+
this.store.log.warn('OfflineEntitlements.isOwned("' + productId + '") called before ready() — returning false. Call await offline.ready() at startup.');
|
|
1769
|
+
return false;
|
|
1770
|
+
}
|
|
1771
|
+
// 1. If store.verifiedReceipts has a valid (non-expired) entry, return true.
|
|
1772
|
+
const online = CdvPurchase.Internal.VerifiedReceipts.isOwned(this.store.verifiedReceipts, { id: productId });
|
|
1773
|
+
if (online)
|
|
1774
|
+
return true;
|
|
1775
|
+
const now = +new Date();
|
|
1776
|
+
// 2. Find persisted purchase for this productId across all platforms.
|
|
1777
|
+
const persisted = this.findPersisted(productId);
|
|
1778
|
+
if (!persisted) {
|
|
1779
|
+
// No persisted entitlement.
|
|
1780
|
+
this.fireEvent('entitlement_missing', productId, 'No persisted entitlement for this product.');
|
|
1781
|
+
return false;
|
|
1782
|
+
}
|
|
1783
|
+
// 3. Branch by product type.
|
|
1784
|
+
// Subscriptions have an expiryDate; non-consumables don't.
|
|
1785
|
+
if (persisted.expiryDate !== undefined && persisted.expiryDate !== null) {
|
|
1786
|
+
// Subscription
|
|
1787
|
+
// Clock rollback detection — scoped to subscriptions (non-consumables have no time component).
|
|
1788
|
+
if (this.detectClockRollback && this.lastSeenTimestamp > now) {
|
|
1789
|
+
this.fireEvent('clock_rollback', persisted.id, 'Clock appears to have rolled back; denying offline entitlement.');
|
|
1790
|
+
return false;
|
|
1791
|
+
}
|
|
1792
|
+
if (persisted.isExpired) {
|
|
1793
|
+
this.fireEvent('expired', persisted.id, 'Subscription is marked as expired.');
|
|
1794
|
+
return false;
|
|
1795
|
+
}
|
|
1796
|
+
if (now < persisted.expiryDate) {
|
|
1797
|
+
return true;
|
|
1798
|
+
}
|
|
1799
|
+
const renewalIntent = persisted.renewalIntent;
|
|
1800
|
+
if (renewalIntent === CdvPurchase.RenewalIntent.RENEW && now < persisted.expiryDate + this.gracePeriodMs) {
|
|
1801
|
+
this.fireEvent('grace', persisted.id, 'Subscription expired but within grace period.');
|
|
1802
|
+
return true;
|
|
1803
|
+
}
|
|
1804
|
+
if (renewalIntent === CdvPurchase.RenewalIntent.LAPSE) {
|
|
1805
|
+
// Lapsed subscriptions deny at expiryDate.
|
|
1806
|
+
return false;
|
|
1807
|
+
}
|
|
1808
|
+
// Unknown/undefined renewalIntent: fall through to onExpiredOffline.
|
|
1809
|
+
if (this.onExpiredOffline === 'readonly') {
|
|
1810
|
+
this.fireEvent('readonly', persisted.id, 'Subscription expired and grace period elapsed; granting readonly access.');
|
|
1811
|
+
return true;
|
|
1812
|
+
}
|
|
1813
|
+
return false;
|
|
1814
|
+
}
|
|
1815
|
+
else {
|
|
1816
|
+
// Non-consumable: never hard-expires, no clock rollback check.
|
|
1817
|
+
return true;
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
/**
|
|
1821
|
+
* Retrieve the persisted entitlement for a product, analogous to
|
|
1822
|
+
* `store.findInVerifiedReceipts()`.
|
|
1823
|
+
*
|
|
1824
|
+
* Returns the {@link PersistedPurchase} (with `expiryDate`,
|
|
1825
|
+
* `renewalIntent`, `lastRenewalDate`, etc.) if one has been cached,
|
|
1826
|
+
* or `undefined` if no offline entitlement exists for this product.
|
|
1827
|
+
*
|
|
1828
|
+
* Returns `undefined` for all products until `ready()` has resolved.
|
|
1829
|
+
*
|
|
1830
|
+
* @example
|
|
1831
|
+
* ```typescript
|
|
1832
|
+
* const entitlement = offline.find('premium');
|
|
1833
|
+
* if (entitlement?.expiryDate) {
|
|
1834
|
+
* const daysLeft = Math.ceil((entitlement.expiryDate - Date.now()) / 86400000);
|
|
1835
|
+
* showRenewalBanner(daysLeft);
|
|
1836
|
+
* }
|
|
1837
|
+
* ```
|
|
1838
|
+
*/
|
|
1839
|
+
find(productId) {
|
|
1840
|
+
if (!this.isReady)
|
|
1841
|
+
return undefined;
|
|
1842
|
+
return this.findPersisted(productId);
|
|
1843
|
+
}
|
|
1844
|
+
/** Persist all `VerifiedPurchase`s in a verified receipt to storage. */
|
|
1845
|
+
onVerified(receipt) {
|
|
1846
|
+
var _a;
|
|
1847
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1848
|
+
for (const purchase of receipt.collection) {
|
|
1849
|
+
// Skip consumables — they don't need offline entitlement.
|
|
1850
|
+
if (purchase.isConsumed)
|
|
1851
|
+
continue;
|
|
1852
|
+
const platform = (_a = purchase.platform) !== null && _a !== void 0 ? _a : receipt.platform;
|
|
1853
|
+
const product = this.store.get(purchase.id, platform);
|
|
1854
|
+
if (product && product.type === CdvPurchase.ProductType.CONSUMABLE)
|
|
1855
|
+
continue;
|
|
1856
|
+
const key = platform + ':' + purchase.id;
|
|
1857
|
+
this.cache[key] = {
|
|
1858
|
+
id: purchase.id,
|
|
1859
|
+
platform,
|
|
1860
|
+
expiryDate: purchase.expiryDate,
|
|
1861
|
+
isExpired: purchase.isExpired,
|
|
1862
|
+
renewalIntent: purchase.renewalIntent,
|
|
1863
|
+
lastRenewalDate: purchase.lastRenewalDate,
|
|
1864
|
+
purchaseDate: purchase.purchaseDate,
|
|
1865
|
+
cancelationReason: purchase.cancelationReason,
|
|
1866
|
+
isBillingRetryPeriod: purchase.isBillingRetryPeriod,
|
|
1867
|
+
};
|
|
1868
|
+
}
|
|
1869
|
+
this.lastSeenTimestamp = +new Date();
|
|
1870
|
+
yield this.saveToStorage();
|
|
1871
|
+
});
|
|
1872
|
+
}
|
|
1873
|
+
/** Find the persisted purchase for a productId across all platforms. */
|
|
1874
|
+
findPersisted(productId) {
|
|
1875
|
+
var _a, _b, _c, _d;
|
|
1876
|
+
let found;
|
|
1877
|
+
for (const key of Object.keys(this.cache)) {
|
|
1878
|
+
const entry = this.cache[key];
|
|
1879
|
+
if (entry.id === productId) {
|
|
1880
|
+
if (!found || ((_b = (_a = found.lastRenewalDate) !== null && _a !== void 0 ? _a : found.purchaseDate) !== null && _b !== void 0 ? _b : 0) < ((_d = (_c = entry.lastRenewalDate) !== null && _c !== void 0 ? _c : entry.purchaseDate) !== null && _d !== void 0 ? _d : 0)) {
|
|
1881
|
+
found = entry;
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
return found;
|
|
1886
|
+
}
|
|
1887
|
+
/** Load the persisted payload from storage into the in-memory cache. */
|
|
1888
|
+
loadFromStorage() {
|
|
1889
|
+
var _a, _b;
|
|
1890
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1891
|
+
try {
|
|
1892
|
+
const raw = yield this.storage.getItem(OfflineEntitlements.STORAGE_KEY);
|
|
1893
|
+
if (!raw) {
|
|
1894
|
+
this.cache = {};
|
|
1895
|
+
this.lastSeenTimestamp = 0;
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1898
|
+
const parsed = JSON.parse(raw);
|
|
1899
|
+
if (parsed.schemaVersion !== OfflineEntitlements.SCHEMA_VERSION) {
|
|
1900
|
+
this.store.log.warn('OfflineEntitlements: schema version mismatch, ignoring cached data.');
|
|
1901
|
+
this.cache = {};
|
|
1902
|
+
this.lastSeenTimestamp = 0;
|
|
1903
|
+
return;
|
|
1904
|
+
}
|
|
1905
|
+
this.cache = (_a = parsed.receipts) !== null && _a !== void 0 ? _a : {};
|
|
1906
|
+
this.lastSeenTimestamp = (_b = parsed.lastSeenTimestamp) !== null && _b !== void 0 ? _b : 0;
|
|
1907
|
+
}
|
|
1908
|
+
catch (err) {
|
|
1909
|
+
this.store.log.warn('OfflineEntitlements: failed to load from storage: ' + err.message);
|
|
1910
|
+
this.cache = {};
|
|
1911
|
+
this.lastSeenTimestamp = 0;
|
|
1912
|
+
}
|
|
1913
|
+
});
|
|
1914
|
+
}
|
|
1915
|
+
/** Serialize the in-memory cache to storage. */
|
|
1916
|
+
saveToStorage() {
|
|
1917
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1918
|
+
const payload = {
|
|
1919
|
+
receipts: this.cache,
|
|
1920
|
+
lastSeenTimestamp: this.lastSeenTimestamp,
|
|
1921
|
+
schemaVersion: OfflineEntitlements.SCHEMA_VERSION,
|
|
1922
|
+
};
|
|
1923
|
+
try {
|
|
1924
|
+
yield this.storage.setItem(OfflineEntitlements.STORAGE_KEY, JSON.stringify(payload));
|
|
1925
|
+
}
|
|
1926
|
+
catch (err) {
|
|
1927
|
+
this.store.log.warn('OfflineEntitlements: failed to save to storage: ' + err.message);
|
|
1928
|
+
}
|
|
1929
|
+
});
|
|
1930
|
+
}
|
|
1931
|
+
/** Fire an event to all registered callbacks, deduplicating per productId. */
|
|
1932
|
+
fireEvent(type, productId, message) {
|
|
1933
|
+
if (this.lastEventPerProduct[productId] === type)
|
|
1934
|
+
return;
|
|
1935
|
+
this.lastEventPerProduct[productId] = type;
|
|
1936
|
+
this.eventCallbacks.trigger({ type, productId, message }, 'offline_entitlements');
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
/** Storage key used in the storage adapter. */
|
|
1940
|
+
OfflineEntitlements.STORAGE_KEY = 'cdvpurchase.offline_entitlements';
|
|
1941
|
+
/** Schema version for the persisted payload. */
|
|
1942
|
+
OfflineEntitlements.SCHEMA_VERSION = 1;
|
|
1943
|
+
/** Default grace period: 30 days. */
|
|
1944
|
+
OfflineEntitlements.DEFAULT_GRACE_PERIOD_MS = 30 * 24 * 60 * 60 * 1000;
|
|
1945
|
+
CdvPurchase.OfflineEntitlements = OfflineEntitlements;
|
|
1946
|
+
})(CdvPurchase || (CdvPurchase = {}));
|
|
1677
1947
|
/// <reference path="types.ts" />
|
|
1678
1948
|
/// <reference path="utils/compatibility.ts" />
|
|
1679
1949
|
/// <reference path="utils/platform-name.ts" />
|
|
@@ -1689,6 +1959,7 @@ var CdvPurchase;
|
|
|
1689
1959
|
/// <reference path="internal/receipts-monitor.ts" />
|
|
1690
1960
|
/// <reference path="internal/expiry-monitor.ts" />
|
|
1691
1961
|
/// <reference path="utils/to-uuid.ts" />
|
|
1962
|
+
/// <reference path="offline-entitlements.ts" />
|
|
1692
1963
|
/**
|
|
1693
1964
|
* Namespace for the cordova-plugin-purchase plugin.
|
|
1694
1965
|
*
|
|
@@ -1722,7 +1993,7 @@ var CdvPurchase;
|
|
|
1722
1993
|
/**
|
|
1723
1994
|
* Current release number of the plugin.
|
|
1724
1995
|
*/
|
|
1725
|
-
CdvPurchase.PLUGIN_VERSION = '13.
|
|
1996
|
+
CdvPurchase.PLUGIN_VERSION = '13.18.0';
|
|
1726
1997
|
/**
|
|
1727
1998
|
* Entry class of the plugin.
|
|
1728
1999
|
*/
|