@virex-tech/paywallo-sdk 1.0.0 → 1.1.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/LICENSE +1 -1
- package/{MonetySdk.podspec → PaywalloSdk.podspec} +4 -4
- package/README.md +19 -19
- package/android/build.gradle +1 -1
- package/dist/index.d.mts +1 -3
- package/dist/index.d.ts +1 -3
- package/dist/index.js +202 -152
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +202 -152
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -5
- package/react-native.config.js +3 -3
- package/android/src/main/java/com/monety/sdk/MonetySdkPackage.kt +0 -16
- package/android/src/main/java/com/monety/sdk/PanelStoreKitModule.kt +0 -315
- package/android/src/main/java/com/monety/sdk/PanelWebView.kt +0 -74
- package/android/src/main/java/com/monety/sdk/PanelWebViewManager.kt +0 -52
- package/ios/PanelStoreKit.m +0 -21
- package/ios/PanelStoreKit.swift +0 -245
- package/ios/PanelWebView.h +0 -15
- package/ios/PanelWebView.m +0 -182
- package/ios/PanelWebViewManager.m +0 -41
- package/ios/PanelWebViewModule.h +0 -8
- package/ios/PanelWebViewModule.m +0 -42
package/dist/index.js
CHANGED
|
@@ -224,8 +224,8 @@ async function loadKeychain() {
|
|
|
224
224
|
}
|
|
225
225
|
var KEYCHAIN_SERVICE_PREFIX = "com.paywallo.sdk.";
|
|
226
226
|
var SecureStorage = class {
|
|
227
|
-
|
|
228
|
-
|
|
227
|
+
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
|
|
228
|
+
constructor(_debug = false) {
|
|
229
229
|
}
|
|
230
230
|
/**
|
|
231
231
|
* Get a value, trying Keychain first (survives reinstalls on iOS), then AsyncStorage.
|
|
@@ -233,18 +233,15 @@ var SecureStorage = class {
|
|
|
233
233
|
async get(key) {
|
|
234
234
|
const keychainValue = await this.getFromKeychain(key);
|
|
235
235
|
if (keychainValue) {
|
|
236
|
-
this.log(`Retrieved ${key} from keychain`);
|
|
237
236
|
return keychainValue;
|
|
238
237
|
}
|
|
239
238
|
try {
|
|
240
239
|
const value = await import_async_storage.default.getItem(`@paywallo:${key}`);
|
|
241
240
|
if (value) {
|
|
242
|
-
this.log(`Retrieved ${key} from async storage`);
|
|
243
241
|
await this.setToKeychain(key, value);
|
|
244
242
|
}
|
|
245
243
|
return value;
|
|
246
|
-
} catch
|
|
247
|
-
this.log(`Storage read failed for ${key}:`, error);
|
|
244
|
+
} catch {
|
|
248
245
|
return null;
|
|
249
246
|
}
|
|
250
247
|
}
|
|
@@ -257,10 +254,8 @@ var SecureStorage = class {
|
|
|
257
254
|
import_async_storage.default.setItem(`@paywallo:${key}`, value),
|
|
258
255
|
this.setToKeychain(key, value)
|
|
259
256
|
]);
|
|
260
|
-
this.log(`Stored ${key} in storage`);
|
|
261
257
|
return true;
|
|
262
|
-
} catch
|
|
263
|
-
this.log(`Storage write failed for ${key}:`, error);
|
|
258
|
+
} catch {
|
|
264
259
|
return false;
|
|
265
260
|
}
|
|
266
261
|
}
|
|
@@ -270,10 +265,8 @@ var SecureStorage = class {
|
|
|
270
265
|
import_async_storage.default.removeItem(`@paywallo:${key}`),
|
|
271
266
|
this.removeFromKeychain(key)
|
|
272
267
|
]);
|
|
273
|
-
this.log(`Removed ${key} from storage`);
|
|
274
268
|
return true;
|
|
275
|
-
} catch
|
|
276
|
-
this.log(`Storage remove failed for ${key}:`, error);
|
|
269
|
+
} catch {
|
|
277
270
|
return false;
|
|
278
271
|
}
|
|
279
272
|
}
|
|
@@ -313,11 +306,6 @@ var SecureStorage = class {
|
|
|
313
306
|
return false;
|
|
314
307
|
}
|
|
315
308
|
}
|
|
316
|
-
log(...args) {
|
|
317
|
-
if (this.debug) {
|
|
318
|
-
console.warn("[Panel Storage]", ...args);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
309
|
};
|
|
322
310
|
var secureStorage = new SecureStorage();
|
|
323
311
|
|
|
@@ -642,21 +630,13 @@ var IAPService = class {
|
|
|
642
630
|
}
|
|
643
631
|
async loadProducts(productIds) {
|
|
644
632
|
if (!nativeStoreKit.isAvailable()) {
|
|
645
|
-
if (this.debug) console.warn("[Paywallo][Products] StoreKit not available");
|
|
646
633
|
return [];
|
|
647
634
|
}
|
|
648
635
|
try {
|
|
649
|
-
if (this.debug) console.warn("[Paywallo][Products] Loading from StoreKit:", productIds);
|
|
650
636
|
const products = await nativeStoreKit.getProducts(productIds);
|
|
651
|
-
if (this.debug) console.warn("[Paywallo][Products] StoreKit returned:", products.length, "products");
|
|
652
637
|
for (const product of products) {
|
|
653
|
-
if (this.debug) console.warn("[Paywallo][Products]", product.productId, "\u2192", product.localizedPrice, "(", product.currency, ")");
|
|
654
638
|
this.productsCache.set(product.productId, product);
|
|
655
639
|
}
|
|
656
|
-
if (products.length < productIds.length) {
|
|
657
|
-
const missing = productIds.filter((id) => !products.find((p) => p.productId === id));
|
|
658
|
-
if (this.debug) console.warn("[Paywallo][Products] Missing from StoreKit:", missing);
|
|
659
|
-
}
|
|
660
640
|
return products;
|
|
661
641
|
} catch (error) {
|
|
662
642
|
if (this.debug) console.warn("[Paywallo][Products] StoreKit error:", error);
|
|
@@ -674,7 +654,6 @@ var IAPService = class {
|
|
|
674
654
|
const product = this.productsCache.get(productId);
|
|
675
655
|
const platform = import_react_native2.Platform.OS;
|
|
676
656
|
const distinctId = PaywalloClient.getDistinctId();
|
|
677
|
-
if (this.debug) console.warn("[Paywallo][Purchase] Validating with server:", purchase.productId, "tx:", purchase.transactionId, "price:", product?.priceValue, product?.currency);
|
|
678
657
|
const validation = await apiClient.validatePurchase(
|
|
679
658
|
platform,
|
|
680
659
|
purchase.receipt,
|
|
@@ -687,33 +666,28 @@ var IAPService = class {
|
|
|
687
666
|
options?.paywallPlacement,
|
|
688
667
|
options?.variantKey
|
|
689
668
|
);
|
|
690
|
-
if (this.debug) console.warn("[Paywallo][Purchase] Server validation result:", JSON.stringify(validation));
|
|
691
669
|
return validation;
|
|
692
670
|
}
|
|
693
671
|
async finishTransaction(transactionId) {
|
|
694
672
|
try {
|
|
695
|
-
if (this.debug) console.warn("[Paywallo][Purchase] Finishing transaction:", transactionId);
|
|
696
673
|
await nativeStoreKit.finishTransaction(transactionId);
|
|
697
|
-
if (this.debug) console.warn("[Paywallo][Purchase] Transaction finished OK");
|
|
698
674
|
} catch (finishError) {
|
|
699
675
|
if (this.debug) console.warn("[Paywallo][Purchase] Failed to finish transaction:", finishError instanceof Error ? finishError.message : finishError);
|
|
700
676
|
}
|
|
701
677
|
}
|
|
702
678
|
async purchase(productId, options) {
|
|
703
679
|
if (!nativeStoreKit.isAvailable()) {
|
|
680
|
+
if (this.debug) console.warn("[Paywallo][Purchase] StoreKit not available");
|
|
704
681
|
return {
|
|
705
682
|
success: false,
|
|
706
683
|
error: new PurchaseError(PURCHASE_ERROR_CODES.STORE_NOT_AVAILABLE, "In-app purchases are not available on this device")
|
|
707
684
|
};
|
|
708
685
|
}
|
|
709
686
|
try {
|
|
710
|
-
if (this.debug) console.warn("[Paywallo][Purchase] StoreKit purchase starting:", productId);
|
|
711
687
|
const purchase = await nativeStoreKit.purchase(productId);
|
|
712
688
|
if (!purchase) {
|
|
713
|
-
if (this.debug) console.warn("[Paywallo][Purchase] StoreKit returned null (user cancelled or pending)");
|
|
714
689
|
return { success: false };
|
|
715
690
|
}
|
|
716
|
-
if (this.debug) console.warn("[Paywallo][Purchase] StoreKit OK \u2014 tx:", purchase.transactionId, "product:", purchase.productId, "receipt length:", purchase.receipt?.length);
|
|
717
691
|
let validation = null;
|
|
718
692
|
try {
|
|
719
693
|
validation = await this.validateWithServer(purchase, productId, options);
|
|
@@ -725,14 +699,12 @@ var IAPService = class {
|
|
|
725
699
|
};
|
|
726
700
|
}
|
|
727
701
|
if (!validation?.success) {
|
|
728
|
-
if (this.debug) console.warn("[Paywallo][Purchase] Validation returned failure:", validation?.error || "no validation result");
|
|
729
702
|
return {
|
|
730
703
|
success: false,
|
|
731
704
|
error: new PurchaseError(PURCHASE_ERROR_CODES.VALIDATION_FAILED, validation?.error ? `Server validation: ${validation.error}` : "Server validation failed")
|
|
732
705
|
};
|
|
733
706
|
}
|
|
734
707
|
await this.finishTransaction(purchase.transactionId);
|
|
735
|
-
if (this.debug) console.warn("[Paywallo][Purchase] COMPLETE \u2014 validated & finished:", purchase.productId);
|
|
736
708
|
return { success: true, purchase };
|
|
737
709
|
} catch (error) {
|
|
738
710
|
const e = error instanceof PurchaseError ? error : new PurchaseError(PURCHASE_ERROR_CODES.PURCHASE_FAILED, error instanceof Error ? error.message : String(error));
|
|
@@ -742,20 +714,16 @@ var IAPService = class {
|
|
|
742
714
|
}
|
|
743
715
|
async restore() {
|
|
744
716
|
if (!nativeStoreKit.isAvailable()) {
|
|
745
|
-
if (this.debug) console.warn("[Paywallo][Restore] StoreKit not available");
|
|
746
717
|
return [];
|
|
747
718
|
}
|
|
748
719
|
try {
|
|
749
|
-
if (this.debug) console.warn("[Paywallo][Restore] Getting active transactions...");
|
|
750
720
|
const transactions = await nativeStoreKit.getActiveTransactions();
|
|
751
|
-
if (this.debug) console.warn("[Paywallo][Restore] Found", transactions.length, "active transactions");
|
|
752
721
|
const apiClient = this.getApiClient();
|
|
753
722
|
const distinctId = PaywalloClient.getDistinctId();
|
|
754
723
|
const platform = import_react_native2.Platform.OS;
|
|
755
724
|
const results = await Promise.allSettled(
|
|
756
725
|
transactions.map((tx) => {
|
|
757
726
|
const product = this.productsCache.get(tx.productId);
|
|
758
|
-
if (this.debug) console.warn("[Paywallo][Restore] Validating tx:", tx.transactionId, "product:", tx.productId);
|
|
759
727
|
return apiClient.validatePurchase(
|
|
760
728
|
platform,
|
|
761
729
|
tx.receipt,
|
|
@@ -772,22 +740,18 @@ var IAPService = class {
|
|
|
772
740
|
for (let i = 0; i < results.length; i++) {
|
|
773
741
|
const r = results[i];
|
|
774
742
|
const tx = transactions[i];
|
|
775
|
-
if (this.debug) console.warn("[Paywallo][Restore] Validation:", r.status, r.status === "fulfilled" ? JSON.stringify(r.value) : r.reason?.message);
|
|
776
743
|
if (r.status === "fulfilled" && r.value.success) {
|
|
777
744
|
try {
|
|
778
745
|
await nativeStoreKit.finishTransaction(tx.transactionId);
|
|
779
|
-
if (this.debug) console.warn("[Paywallo][Restore] Finished tx:", tx.transactionId);
|
|
780
746
|
} catch (finishError) {
|
|
781
747
|
if (this.debug) console.warn("[Paywallo][Restore] Failed to finish tx:", tx.transactionId, finishError instanceof Error ? finishError.message : finishError);
|
|
782
748
|
}
|
|
783
749
|
validated.push(tx);
|
|
784
|
-
} else {
|
|
785
|
-
if (this.debug) console.warn("[Paywallo][Restore] Skipping unvalidated tx:", tx.transactionId);
|
|
786
750
|
}
|
|
787
751
|
}
|
|
788
752
|
return validated;
|
|
789
753
|
} catch (error) {
|
|
790
|
-
if (this.debug) console.warn("[Paywallo][Restore] FAILED:", error instanceof Error ? error.message : error);
|
|
754
|
+
if (this.debug) console.warn("[Paywallo][Restore] \u274C FAILED:", error instanceof Error ? error.message : error);
|
|
791
755
|
return [];
|
|
792
756
|
}
|
|
793
757
|
}
|
|
@@ -909,7 +873,6 @@ function usePaywallActions(placement, paywallConfig, onResult, variantKey, campa
|
|
|
909
873
|
}
|
|
910
874
|
onResult({ presented: true, purchased: false, cancelled: false, restored: true });
|
|
911
875
|
} else {
|
|
912
|
-
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] No purchases to restore");
|
|
913
876
|
onResult({ presented: true, purchased: false, cancelled: false, restored: false });
|
|
914
877
|
}
|
|
915
878
|
} catch (error) {
|
|
@@ -1045,7 +1008,7 @@ function buildPaywallInjectionScript(data) {
|
|
|
1045
1008
|
secondaryProductId: data.secondaryProductId
|
|
1046
1009
|
}
|
|
1047
1010
|
};
|
|
1048
|
-
return `(function(){var d=${JSON.stringify(paywallData)},a=0,m=50;function t(){a++;if(window.receiveNativeMessage){window.receiveNativeMessage(d);return;}if(a<m){setTimeout(t,100);}else{console.warn('[
|
|
1011
|
+
return `(function(){var d=${JSON.stringify(paywallData)},a=0,m=50;function t(){a++;if(window.receiveNativeMessage){window.receiveNativeMessage(d);return;}if(a<m){setTimeout(t,100);}else{console.warn('[Paywallo WV] FAILED to inject after '+m+' attempts');}}t();})();true;`;
|
|
1049
1012
|
}
|
|
1050
1013
|
function buildPurchaseStateScript(isPurchasing) {
|
|
1051
1014
|
return `if(window.receiveNativeMessage){window.receiveNativeMessage({type:"purchaseState",data:{isPurchasing:${isPurchasing}}});}true;`;
|
|
@@ -1204,16 +1167,13 @@ function PaywallModal({
|
|
|
1204
1167
|
const openAnim = (0, import_react5.useRef)(new import_react_native5.Animated.Value(SCREEN_HEIGHT2)).current;
|
|
1205
1168
|
(0, import_react5.useEffect)(() => {
|
|
1206
1169
|
if (visible) {
|
|
1207
|
-
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] PaywallModal opening \u2014 starting slide-up animation (550ms)");
|
|
1208
1170
|
openAnim.setValue(SCREEN_HEIGHT2);
|
|
1209
1171
|
import_react_native5.Animated.timing(openAnim, {
|
|
1210
1172
|
toValue: 0,
|
|
1211
1173
|
duration: 550,
|
|
1212
1174
|
easing: import_react_native5.Easing.out(import_react_native5.Easing.cubic),
|
|
1213
1175
|
useNativeDriver: true
|
|
1214
|
-
}).start(
|
|
1215
|
-
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] PaywallModal slide-up animation completed");
|
|
1216
|
-
});
|
|
1176
|
+
}).start();
|
|
1217
1177
|
}
|
|
1218
1178
|
}, [visible, openAnim]);
|
|
1219
1179
|
if (!visible) return null;
|
|
@@ -1617,8 +1577,7 @@ function buildConditionalFlagUrl(baseUrl, flagKey, context) {
|
|
|
1617
1577
|
}
|
|
1618
1578
|
|
|
1619
1579
|
// src/domains/iap/apiPurchaseMethods.ts
|
|
1620
|
-
async function validatePurchase(client, appKey, platform, receipt, productId, transactionId, priceLocal, currency, country, distinctId, paywallPlacement, variantKey
|
|
1621
|
-
if (debug) console.warn("[Paywallo][API] POST /purchases/validate", { platform, productId, transactionId, priceLocal, currency, country, distinctId, paywallPlacement, variantKey, receiptLen: receipt?.length });
|
|
1580
|
+
async function validatePurchase(client, appKey, platform, receipt, productId, transactionId, priceLocal, currency, country, distinctId, paywallPlacement, variantKey) {
|
|
1622
1581
|
const response = await client.post(
|
|
1623
1582
|
`/purchases/validate/${appKey}`,
|
|
1624
1583
|
{
|
|
@@ -1635,7 +1594,6 @@ async function validatePurchase(client, appKey, platform, receipt, productId, tr
|
|
|
1635
1594
|
},
|
|
1636
1595
|
false
|
|
1637
1596
|
);
|
|
1638
|
-
if (debug) console.warn("[Paywallo][API] Validation response:", JSON.stringify(response.data));
|
|
1639
1597
|
return response.data;
|
|
1640
1598
|
}
|
|
1641
1599
|
async function getSubscriptionStatus(client, baseUrl, appKey, distinctId) {
|
|
@@ -1713,11 +1671,6 @@ var HttpClient = class {
|
|
|
1713
1671
|
if (state.attempt < retryConfig.maxRetries) {
|
|
1714
1672
|
state.attempt++;
|
|
1715
1673
|
const delay = this.calculateDelay(state.attempt, retryConfig);
|
|
1716
|
-
this.log(`Retrying request (attempt ${state.attempt}/${retryConfig.maxRetries})`, {
|
|
1717
|
-
url,
|
|
1718
|
-
status: response.status,
|
|
1719
|
-
delay
|
|
1720
|
-
});
|
|
1721
1674
|
await this.sleep(delay);
|
|
1722
1675
|
continue;
|
|
1723
1676
|
}
|
|
@@ -1728,11 +1681,6 @@ var HttpClient = class {
|
|
|
1728
1681
|
if (this.isNetworkError(state.lastError) && state.attempt < retryConfig.maxRetries) {
|
|
1729
1682
|
state.attempt++;
|
|
1730
1683
|
const delay = this.calculateDelay(state.attempt, retryConfig);
|
|
1731
|
-
this.log(`Network error, retrying (attempt ${state.attempt}/${retryConfig.maxRetries})`, {
|
|
1732
|
-
url,
|
|
1733
|
-
error: state.lastError.message,
|
|
1734
|
-
delay
|
|
1735
|
-
});
|
|
1736
1684
|
await this.sleep(delay);
|
|
1737
1685
|
continue;
|
|
1738
1686
|
}
|
|
@@ -1750,11 +1698,6 @@ var HttpClient = class {
|
|
|
1750
1698
|
controller.abort();
|
|
1751
1699
|
}
|
|
1752
1700
|
}, timeout);
|
|
1753
|
-
this.log("Request starting", {
|
|
1754
|
-
url,
|
|
1755
|
-
method: options.method,
|
|
1756
|
-
headers: options.headers
|
|
1757
|
-
});
|
|
1758
1701
|
try {
|
|
1759
1702
|
const response = await fetch(url, {
|
|
1760
1703
|
...options,
|
|
@@ -1767,20 +1710,8 @@ var HttpClient = class {
|
|
|
1767
1710
|
data = await response.json();
|
|
1768
1711
|
} else {
|
|
1769
1712
|
const textData = await response.text();
|
|
1770
|
-
this.log("Non-JSON response received", {
|
|
1771
|
-
url,
|
|
1772
|
-
status: response.status,
|
|
1773
|
-
contentType,
|
|
1774
|
-
bodyPreview: textData.substring(0, 200)
|
|
1775
|
-
});
|
|
1776
1713
|
data = textData;
|
|
1777
1714
|
}
|
|
1778
|
-
this.log("Request completed", {
|
|
1779
|
-
url,
|
|
1780
|
-
status: response.status,
|
|
1781
|
-
ok: response.ok,
|
|
1782
|
-
contentType
|
|
1783
|
-
});
|
|
1784
1715
|
const headers = {};
|
|
1785
1716
|
response.headers.forEach((value, key) => {
|
|
1786
1717
|
headers[key] = value;
|
|
@@ -2063,7 +1994,6 @@ var OfflineQueueClass = class {
|
|
|
2063
1994
|
this.cleanupExpired();
|
|
2064
1995
|
this.cleanupTimer = setInterval(() => this.cleanupExpired(), 60 * 60 * 1e3);
|
|
2065
1996
|
this.initialized = true;
|
|
2066
|
-
this.log("OfflineQueue initialized", { queueSize: this.queue.length });
|
|
2067
1997
|
}
|
|
2068
1998
|
async loadFromStorage() {
|
|
2069
1999
|
try {
|
|
@@ -2094,7 +2024,6 @@ var OfflineQueueClass = class {
|
|
|
2094
2024
|
const before = this.queue.length;
|
|
2095
2025
|
this.queue = this.queue.filter((item) => Date.now() - item.timestamp < this.config.maxAge);
|
|
2096
2026
|
if (before !== this.queue.length) {
|
|
2097
|
-
this.log("Cleaned up expired items", { removed: before - this.queue.length });
|
|
2098
2027
|
void this.saveToStorage();
|
|
2099
2028
|
}
|
|
2100
2029
|
}
|
|
@@ -2111,7 +2040,6 @@ var OfflineQueueClass = class {
|
|
|
2111
2040
|
};
|
|
2112
2041
|
const existingIndex = this.findDuplicateIndex(item);
|
|
2113
2042
|
if (existingIndex !== -1) {
|
|
2114
|
-
this.log("Duplicate item found, updating timestamp", { id: this.queue[existingIndex].id });
|
|
2115
2043
|
this.queue[existingIndex].timestamp = Date.now();
|
|
2116
2044
|
await this.saveToStorage();
|
|
2117
2045
|
return this.queue[existingIndex];
|
|
@@ -2119,14 +2047,12 @@ var OfflineQueueClass = class {
|
|
|
2119
2047
|
if (this.queue.length >= this.config.maxItems) {
|
|
2120
2048
|
const removed = this.queue.shift();
|
|
2121
2049
|
if (removed) {
|
|
2122
|
-
this.log("Queue full, evicted oldest item:", removed.url);
|
|
2123
2050
|
this.emit({ type: "item:evicted", item: removed, queueSize: this.queue.length });
|
|
2124
2051
|
}
|
|
2125
2052
|
}
|
|
2126
2053
|
this.queue.push(item);
|
|
2127
2054
|
await this.saveToStorage();
|
|
2128
2055
|
this.emit({ type: "item:added", item, queueSize: this.queue.length });
|
|
2129
|
-
this.log("Item enqueued", { id: item.id, url: item.url });
|
|
2130
2056
|
return item;
|
|
2131
2057
|
}
|
|
2132
2058
|
findDuplicateIndex(item) {
|
|
@@ -2155,7 +2081,6 @@ var OfflineQueueClass = class {
|
|
|
2155
2081
|
this.processingLock = true;
|
|
2156
2082
|
this.processing = true;
|
|
2157
2083
|
this.emit({ type: "processing:start", queueSize: this.queue.length });
|
|
2158
|
-
this.log("Processing queue", { items: this.queue.length });
|
|
2159
2084
|
let processed = 0;
|
|
2160
2085
|
let failed = 0;
|
|
2161
2086
|
const delayMs = options?.delayBetweenItems ?? 100;
|
|
@@ -2172,7 +2097,6 @@ var OfflineQueueClass = class {
|
|
|
2172
2097
|
await this.removeItem(item.id);
|
|
2173
2098
|
processed++;
|
|
2174
2099
|
this.emit({ type: "item:success", item, queueSize: this.queue.length });
|
|
2175
|
-
this.log("Item processed successfully", { id: item.id });
|
|
2176
2100
|
} else {
|
|
2177
2101
|
const didFail = await this.handleItemFailure(item, void 0, permanent);
|
|
2178
2102
|
if (didFail) failed++;
|
|
@@ -2187,7 +2111,6 @@ var OfflineQueueClass = class {
|
|
|
2187
2111
|
this.processing = false;
|
|
2188
2112
|
this.processingLock = false;
|
|
2189
2113
|
this.emit({ type: "processing:end", queueSize: this.queue.length });
|
|
2190
|
-
this.log("Queue processing complete", { processed, failed, remaining: this.queue.length });
|
|
2191
2114
|
return { processed, failed };
|
|
2192
2115
|
}
|
|
2193
2116
|
/** Returns true if the item was permanently removed (counted as failed). */
|
|
@@ -2200,14 +2123,9 @@ var OfflineQueueClass = class {
|
|
|
2200
2123
|
error: error ?? new ClientError(CLIENT_ERROR_CODES.UNKNOWN, permanent ? "Permanent failure" : "Max attempts reached"),
|
|
2201
2124
|
queueSize: this.queue.length
|
|
2202
2125
|
});
|
|
2203
|
-
this.log(
|
|
2204
|
-
permanent ? "Item failed permanently - removed" : "Item failed after max attempts",
|
|
2205
|
-
{ id: item.id, attempts: item.attempts }
|
|
2206
|
-
);
|
|
2207
2126
|
return true;
|
|
2208
2127
|
}
|
|
2209
2128
|
await this.saveToStorage();
|
|
2210
|
-
this.log("Item will be retried", { id: item.id, attempts: item.attempts });
|
|
2211
2129
|
return false;
|
|
2212
2130
|
}
|
|
2213
2131
|
sleep(ms) {
|
|
@@ -2228,7 +2146,6 @@ var OfflineQueueClass = class {
|
|
|
2228
2146
|
async clear() {
|
|
2229
2147
|
this.queue = [];
|
|
2230
2148
|
await this.saveToStorage();
|
|
2231
|
-
this.log("Queue cleared");
|
|
2232
2149
|
}
|
|
2233
2150
|
async clearItemsWithInvalidAppKey(validAppKey) {
|
|
2234
2151
|
const before = this.queue.length;
|
|
@@ -2239,7 +2156,6 @@ var OfflineQueueClass = class {
|
|
|
2239
2156
|
const removed = before - this.queue.length;
|
|
2240
2157
|
if (removed > 0) {
|
|
2241
2158
|
await this.saveToStorage();
|
|
2242
|
-
this.log("Cleared items with invalid app key", { removed, validAppKey });
|
|
2243
2159
|
}
|
|
2244
2160
|
return removed;
|
|
2245
2161
|
}
|
|
@@ -2420,6 +2336,8 @@ var ApiCache = class {
|
|
|
2420
2336
|
constructor() {
|
|
2421
2337
|
this.CACHE_TTL = 3e5;
|
|
2422
2338
|
// 5 minutes
|
|
2339
|
+
this.CACHE_NULL_TTL = 3e4;
|
|
2340
|
+
// 30 seconds — for null/404 variants
|
|
2423
2341
|
this.MAX_SIZE = 200;
|
|
2424
2342
|
this.variantCache = /* @__PURE__ */ new Map();
|
|
2425
2343
|
this.paywallCache = /* @__PURE__ */ new Map();
|
|
@@ -2428,27 +2346,34 @@ var ApiCache = class {
|
|
|
2428
2346
|
getCached(cache, key) {
|
|
2429
2347
|
const entry = cache.get(key);
|
|
2430
2348
|
if (!entry) return void 0;
|
|
2431
|
-
|
|
2349
|
+
const ttl = entry.ttl ?? this.CACHE_TTL;
|
|
2350
|
+
if (Date.now() - entry.ts < ttl) return entry.value;
|
|
2432
2351
|
cache.delete(key);
|
|
2433
2352
|
return void 0;
|
|
2434
2353
|
}
|
|
2435
|
-
setCached(cache, key, value) {
|
|
2436
|
-
|
|
2354
|
+
setCached(cache, key, value, ttlOverride) {
|
|
2355
|
+
const entry = { value, ts: Date.now() };
|
|
2356
|
+
if (ttlOverride !== void 0) entry.ttl = ttlOverride;
|
|
2357
|
+
cache.set(key, entry);
|
|
2437
2358
|
}
|
|
2438
2359
|
evictExpiredEntries(cache) {
|
|
2439
2360
|
if (cache.size <= this.MAX_SIZE) return;
|
|
2440
2361
|
const now = Date.now();
|
|
2441
2362
|
for (const [key, entry] of cache) {
|
|
2442
|
-
|
|
2363
|
+
const ttl = entry.ttl ?? this.CACHE_TTL;
|
|
2364
|
+
if (now - entry.ts > ttl) cache.delete(key);
|
|
2443
2365
|
}
|
|
2444
2366
|
}
|
|
2445
2367
|
getVariant(key) {
|
|
2446
2368
|
return this.getCached(this.variantCache, key);
|
|
2447
2369
|
}
|
|
2448
|
-
setVariant(key, value) {
|
|
2449
|
-
this.setCached(this.variantCache, key, value);
|
|
2370
|
+
setVariant(key, value, ttlOverride) {
|
|
2371
|
+
this.setCached(this.variantCache, key, value, ttlOverride);
|
|
2450
2372
|
this.evictExpiredEntries(this.variantCache);
|
|
2451
2373
|
}
|
|
2374
|
+
get nullTTL() {
|
|
2375
|
+
return this.CACHE_NULL_TTL;
|
|
2376
|
+
}
|
|
2452
2377
|
getStaleVariant(key) {
|
|
2453
2378
|
const entry = this.variantCache.get(key);
|
|
2454
2379
|
if (entry && Date.now() - entry.ts < 864e5) return entry.value;
|
|
@@ -2457,8 +2382,8 @@ var ApiCache = class {
|
|
|
2457
2382
|
getPaywall(key) {
|
|
2458
2383
|
return this.getCached(this.paywallCache, key);
|
|
2459
2384
|
}
|
|
2460
|
-
setPaywall(key, value) {
|
|
2461
|
-
this.setCached(this.paywallCache, key, value);
|
|
2385
|
+
setPaywall(key, value, ttlOverride) {
|
|
2386
|
+
this.setCached(this.paywallCache, key, value, ttlOverride);
|
|
2462
2387
|
this.evictExpiredEntries(this.paywallCache);
|
|
2463
2388
|
}
|
|
2464
2389
|
getStalePaywall(key) {
|
|
@@ -2484,16 +2409,15 @@ var ApiCache = class {
|
|
|
2484
2409
|
var SDK_VERSION = "1.0.0";
|
|
2485
2410
|
var DEFAULT_BASE_URL = "https://panel.lucasqueiroga.shop";
|
|
2486
2411
|
var ApiClient = class {
|
|
2487
|
-
constructor(appKey,
|
|
2412
|
+
constructor(appKey, debug = false, environment = "Production", offlineQueueEnabled = true, timeout = 3e4) {
|
|
2488
2413
|
this.cache = new ApiCache();
|
|
2489
2414
|
this.appKey = appKey;
|
|
2490
|
-
this.baseUrl =
|
|
2415
|
+
this.baseUrl = DEFAULT_BASE_URL;
|
|
2491
2416
|
this.webUrl = deriveWebUrl(this.baseUrl);
|
|
2492
2417
|
this.debug = debug;
|
|
2493
2418
|
this.environment = environment;
|
|
2494
2419
|
this.offlineQueueEnabled = offlineQueueEnabled;
|
|
2495
2420
|
this.httpClient = new HttpClient(this.baseUrl, { timeout, debug });
|
|
2496
|
-
this.log("ApiClient initialized", { baseUrl: this.baseUrl, environment, offlineQueueEnabled });
|
|
2497
2421
|
}
|
|
2498
2422
|
// ─── Infrastructure ──────────────────────────────────────────────────────────
|
|
2499
2423
|
/** Exposed only for QueueProcessor initialization. Prefer get()/post() for requests. */
|
|
@@ -2517,7 +2441,6 @@ var ApiClient = class {
|
|
|
2517
2441
|
}
|
|
2518
2442
|
setEnvironment(environment) {
|
|
2519
2443
|
this.environment = environment;
|
|
2520
|
-
this.log("Environment set to:", environment);
|
|
2521
2444
|
}
|
|
2522
2445
|
setDebug(debug) {
|
|
2523
2446
|
this.debug = debug;
|
|
@@ -2547,7 +2470,6 @@ var ApiClient = class {
|
|
|
2547
2470
|
}
|
|
2548
2471
|
// ─── Business methods ────────────────────────────────────────────────────────
|
|
2549
2472
|
async trackEvent(eventName, distinctId, properties, timestamp) {
|
|
2550
|
-
this.log("Tracking event:", eventName);
|
|
2551
2473
|
await this.postWithQueue("/api/v1/events", {
|
|
2552
2474
|
eventName,
|
|
2553
2475
|
distinctId,
|
|
@@ -2558,7 +2480,6 @@ var ApiClient = class {
|
|
|
2558
2480
|
}
|
|
2559
2481
|
async identify(distinctId, properties, email) {
|
|
2560
2482
|
await this.postWithQueue("/api/v1/identify", { distinctId, properties: properties ?? {}, email, platform: import_react_native7.Platform.OS }, "identify");
|
|
2561
|
-
this.log("Identity updated:", distinctId);
|
|
2562
2483
|
}
|
|
2563
2484
|
async startSession(distinctId, sessionId, platform) {
|
|
2564
2485
|
await this.postWithQueue("/api/v1/sessions/start", {
|
|
@@ -2567,12 +2488,10 @@ var ApiClient = class {
|
|
|
2567
2488
|
platform: platform ?? import_react_native7.Platform.OS,
|
|
2568
2489
|
environment: this.environment.toLowerCase()
|
|
2569
2490
|
}, `session.start:${sessionId}`);
|
|
2570
|
-
this.log("Session started:", sessionId);
|
|
2571
2491
|
return { success: true };
|
|
2572
2492
|
}
|
|
2573
2493
|
async endSession(sessionId, durationSeconds) {
|
|
2574
2494
|
await this.postWithQueue("/api/v1/sessions/end", { sessionId, durationSeconds }, `session.end:${sessionId}`);
|
|
2575
|
-
this.log("Session ended:", sessionId);
|
|
2576
2495
|
return { success: true };
|
|
2577
2496
|
}
|
|
2578
2497
|
async getVariant(flagKey, distinctId) {
|
|
@@ -2580,15 +2499,18 @@ var ApiClient = class {
|
|
|
2580
2499
|
const hit = this.cache.getVariant(key);
|
|
2581
2500
|
if (hit !== void 0) return hit;
|
|
2582
2501
|
try {
|
|
2583
|
-
const
|
|
2584
|
-
url.searchParams.set("distinctId", distinctId);
|
|
2585
|
-
const res = await this.get(url.toString());
|
|
2502
|
+
const res = await this.get(`/api/v1/flags/${flagKey}?distinctId=${encodeURIComponent(distinctId)}`);
|
|
2586
2503
|
if (res.status === 404) {
|
|
2587
2504
|
const v = { variant: null };
|
|
2588
|
-
this.cache.setVariant(key, v);
|
|
2505
|
+
this.cache.setVariant(key, v, this.cache.nullTTL);
|
|
2589
2506
|
return v;
|
|
2590
2507
|
}
|
|
2591
|
-
|
|
2508
|
+
if (!res.ok) {
|
|
2509
|
+
this.log("Non-OK response for variant:", flagKey, res.status);
|
|
2510
|
+
const stale = this.cache.getStaleVariant(key);
|
|
2511
|
+
if (stale !== void 0) return stale;
|
|
2512
|
+
return { variant: null };
|
|
2513
|
+
}
|
|
2592
2514
|
this.cache.setVariant(key, res.data);
|
|
2593
2515
|
return res.data;
|
|
2594
2516
|
} catch (error) {
|
|
@@ -2604,10 +2526,15 @@ var ApiClient = class {
|
|
|
2604
2526
|
try {
|
|
2605
2527
|
const res = await this.get(`/api/v1/paywalls/${placement}`);
|
|
2606
2528
|
if (res.status === 404) {
|
|
2607
|
-
this.cache.setPaywall(placement, null);
|
|
2529
|
+
this.cache.setPaywall(placement, null, this.cache.nullTTL);
|
|
2530
|
+
return null;
|
|
2531
|
+
}
|
|
2532
|
+
if (!res.ok) {
|
|
2533
|
+
this.log("Non-OK response for paywall:", placement, res.status);
|
|
2534
|
+
const stale = this.cache.getStalePaywall(placement);
|
|
2535
|
+
if (stale !== void 0) return stale;
|
|
2608
2536
|
return null;
|
|
2609
2537
|
}
|
|
2610
|
-
this.log("Got paywall:", placement);
|
|
2611
2538
|
this.cache.setPaywall(placement, res.data);
|
|
2612
2539
|
return res.data;
|
|
2613
2540
|
} catch (error) {
|
|
@@ -2656,8 +2583,7 @@ var ApiClient = class {
|
|
|
2656
2583
|
country,
|
|
2657
2584
|
distinctId,
|
|
2658
2585
|
paywallPlacement,
|
|
2659
|
-
variantKey
|
|
2660
|
-
this.debug
|
|
2586
|
+
variantKey
|
|
2661
2587
|
);
|
|
2662
2588
|
this.log("Purchase validated:", result.success);
|
|
2663
2589
|
return result;
|
|
@@ -2672,6 +2598,10 @@ var ApiClient = class {
|
|
|
2672
2598
|
const url = buildConditionalFlagUrl(this.baseUrl, flagKey, context);
|
|
2673
2599
|
const response = await this.get(url);
|
|
2674
2600
|
if (response.status === 404) return { value: false, flagKey };
|
|
2601
|
+
if (!response.ok) {
|
|
2602
|
+
this.log("Non-OK response for conditional flag:", flagKey, response.status);
|
|
2603
|
+
return { value: false, flagKey };
|
|
2604
|
+
}
|
|
2675
2605
|
this.log("Got conditional flag:", flagKey, response.data.value);
|
|
2676
2606
|
return { value: response.data.value, flagKey };
|
|
2677
2607
|
} catch {
|
|
@@ -2703,7 +2633,7 @@ var ApiClient = class {
|
|
|
2703
2633
|
}
|
|
2704
2634
|
}
|
|
2705
2635
|
log(...args) {
|
|
2706
|
-
if (this.debug) console.warn("[
|
|
2636
|
+
if (this.debug) console.warn("[Paywallo API]", ...args);
|
|
2707
2637
|
}
|
|
2708
2638
|
};
|
|
2709
2639
|
|
|
@@ -3029,7 +2959,6 @@ var SessionManager = class {
|
|
|
3029
2959
|
this.setupAppStateListener();
|
|
3030
2960
|
}
|
|
3031
2961
|
this.initialized = true;
|
|
3032
|
-
this.log("SessionManager initialized");
|
|
3033
2962
|
}
|
|
3034
2963
|
async startSession() {
|
|
3035
2964
|
this.ensureInitialized();
|
|
@@ -3045,7 +2974,6 @@ var SessionManager = class {
|
|
|
3045
2974
|
this.secureStorage.remove(EMERGENCY_PAYWALL_SHOWN_KEY)
|
|
3046
2975
|
]);
|
|
3047
2976
|
await this.trackSessionStart();
|
|
3048
|
-
this.log("Session started:", this.sessionId);
|
|
3049
2977
|
return this.sessionId;
|
|
3050
2978
|
}
|
|
3051
2979
|
async endSession() {
|
|
@@ -3060,7 +2988,6 @@ var SessionManager = class {
|
|
|
3060
2988
|
this.secureStorage.remove(SESSION_ID_KEY),
|
|
3061
2989
|
this.secureStorage.remove(SESSION_START_KEY)
|
|
3062
2990
|
]);
|
|
3063
|
-
this.log("Session ended");
|
|
3064
2991
|
}
|
|
3065
2992
|
getSessionId() {
|
|
3066
2993
|
return this.sessionId;
|
|
@@ -3195,6 +3122,13 @@ var InstallTracker = class {
|
|
|
3195
3122
|
const storage = new SecureStorage(this.debug);
|
|
3196
3123
|
const alreadyTracked = await storage.get("install_tracked");
|
|
3197
3124
|
if (alreadyTracked) return;
|
|
3125
|
+
let fallbackTracked = null;
|
|
3126
|
+
try {
|
|
3127
|
+
const AsyncStorage3 = (await import("@react-native-async-storage/async-storage")).default;
|
|
3128
|
+
fallbackTracked = await AsyncStorage3.getItem("@paywallo:install_tracked");
|
|
3129
|
+
} catch {
|
|
3130
|
+
}
|
|
3131
|
+
if (fallbackTracked) return;
|
|
3198
3132
|
const distinctId = identityManager.getDistinctId();
|
|
3199
3133
|
const sessionId = sessionManager.getSessionId();
|
|
3200
3134
|
const installedAt = Date.now();
|
|
@@ -3256,12 +3190,13 @@ var InstallTracker = class {
|
|
|
3256
3190
|
attStatus: adIds.attStatus
|
|
3257
3191
|
});
|
|
3258
3192
|
const stored = await storage.set("install_tracked", String(installedAt));
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3193
|
+
try {
|
|
3194
|
+
const AsyncStorage3 = (await import("@react-native-async-storage/async-storage")).default;
|
|
3195
|
+
await AsyncStorage3.setItem("@paywallo:install_tracked", String(installedAt));
|
|
3196
|
+
} catch {
|
|
3197
|
+
}
|
|
3198
|
+
if (this.debug && !stored) {
|
|
3199
|
+
console.warn("[Paywallo] Install event tracked but flag storage failed \u2014 may re-track on next launch");
|
|
3265
3200
|
}
|
|
3266
3201
|
} catch (error) {
|
|
3267
3202
|
if (this.debug) {
|
|
@@ -3276,7 +3211,7 @@ function isValidEventName(name) {
|
|
|
3276
3211
|
if (name.startsWith("$")) return /^\$[a-z][a-z0-9]*(_[a-z0-9]+)*$/.test(name);
|
|
3277
3212
|
return /^[a-z][a-z0-9]*(_[a-z0-9]+)*$/.test(name);
|
|
3278
3213
|
}
|
|
3279
|
-
var
|
|
3214
|
+
var _PaywalloClientClass = class _PaywalloClientClass {
|
|
3280
3215
|
constructor() {
|
|
3281
3216
|
this.config = null;
|
|
3282
3217
|
this.apiClient = null;
|
|
@@ -3287,45 +3222,121 @@ var PaywalloClientClass = class {
|
|
|
3287
3222
|
this.activeChecker = null;
|
|
3288
3223
|
this.restoreHandler = null;
|
|
3289
3224
|
this.lastOnboardingStepTimestamp = null;
|
|
3225
|
+
this.pendingConfig = null;
|
|
3226
|
+
this.networkCleanup = null;
|
|
3227
|
+
this.initAttempts = 0;
|
|
3290
3228
|
}
|
|
3291
3229
|
async init(config) {
|
|
3292
|
-
|
|
3230
|
+
console.warn("[Paywallo INIT]", "init() called \u2014 appKey:", config.appKey ? config.appKey.substring(0, 8) + "..." : "MISSING");
|
|
3231
|
+
if (this.config && this.apiClient) {
|
|
3232
|
+
console.warn("[Paywallo INIT]", "Already initialized \u2014 skipping");
|
|
3233
|
+
return;
|
|
3234
|
+
}
|
|
3293
3235
|
if (this.initPromise) {
|
|
3236
|
+
console.warn("[Paywallo INIT]", "Init already in progress \u2014 awaiting...");
|
|
3294
3237
|
await this.initPromise;
|
|
3238
|
+
console.warn("[Paywallo INIT]", "Awaited existing init \u2014 done");
|
|
3295
3239
|
return;
|
|
3296
3240
|
}
|
|
3297
3241
|
if (!config.appKey) throw new ClientError(CLIENT_ERROR_CODES.MISSING_APP_KEY, "appKey is required");
|
|
3298
|
-
|
|
3242
|
+
console.warn("[Paywallo INIT]", "Starting fresh init...");
|
|
3243
|
+
this.pendingConfig = config;
|
|
3244
|
+
this.initAttempts = 0;
|
|
3245
|
+
this.initPromise = this.initWithRetry(config);
|
|
3299
3246
|
try {
|
|
3300
3247
|
await this.initPromise;
|
|
3248
|
+
console.warn("[Paywallo INIT]", "init() SUCCESS");
|
|
3301
3249
|
} catch (error) {
|
|
3250
|
+
console.warn("[Paywallo INIT]", "init() FAILED:", error instanceof Error ? error.message : String(error));
|
|
3302
3251
|
this.initPromise = null;
|
|
3303
3252
|
this.config = null;
|
|
3304
3253
|
this.apiClient = null;
|
|
3254
|
+
this.reportInitFailure(config, error, "ALL_RETRIES_EXHAUSTED");
|
|
3255
|
+
this.setupNetworkRecovery(config);
|
|
3256
|
+
throw error;
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
async initWithRetry(config) {
|
|
3260
|
+
while (this.initAttempts <= _PaywalloClientClass.MAX_INIT_RETRIES) {
|
|
3305
3261
|
try {
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3262
|
+
await this.doInit(config);
|
|
3263
|
+
if (this.initAttempts > 0) {
|
|
3264
|
+
this.reportInitSuccess(config, this.initAttempts);
|
|
3265
|
+
}
|
|
3266
|
+
this.cleanupNetworkRecovery();
|
|
3267
|
+
this.pendingConfig = null;
|
|
3268
|
+
return;
|
|
3269
|
+
} catch (error) {
|
|
3270
|
+
this.initAttempts++;
|
|
3271
|
+
if (this.initAttempts > _PaywalloClientClass.MAX_INIT_RETRIES) throw error;
|
|
3272
|
+
const delay = _PaywalloClientClass.RETRY_DELAYS[this.initAttempts - 1] ?? 1e4;
|
|
3273
|
+
if (config.debug) console.warn("[Paywallo]", `Init attempt ${this.initAttempts} failed, retrying in ${delay}ms`, error);
|
|
3274
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
3309
3275
|
}
|
|
3310
|
-
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3278
|
+
setupNetworkRecovery(config) {
|
|
3279
|
+
if (this.networkCleanup) return;
|
|
3280
|
+
if (!networkMonitor.isInitialized()) return;
|
|
3281
|
+
this.networkCleanup = networkMonitor.addListener((state) => {
|
|
3282
|
+
if (state !== "online") return;
|
|
3283
|
+
if (this.config && this.apiClient) return;
|
|
3284
|
+
if (config.debug) console.warn("[Paywallo]", "Network recovered \u2014 retrying init");
|
|
3285
|
+
this.initPromise = this.initWithRetry(config);
|
|
3286
|
+
this.initPromise.then(() => {
|
|
3287
|
+
if (config.debug) console.warn("[Paywallo]", "Init succeeded after network recovery");
|
|
3288
|
+
}).catch(() => {
|
|
3289
|
+
this.reportInitFailure(config, null, "NETWORK_RECOVERY_FAILED");
|
|
3290
|
+
});
|
|
3291
|
+
});
|
|
3292
|
+
}
|
|
3293
|
+
cleanupNetworkRecovery() {
|
|
3294
|
+
if (this.networkCleanup) {
|
|
3295
|
+
this.networkCleanup();
|
|
3296
|
+
this.networkCleanup = null;
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
reportInitFailure(config, error, reason) {
|
|
3300
|
+
try {
|
|
3301
|
+
const tempClient = new ApiClient(config.appKey, config.debug);
|
|
3302
|
+
void tempClient.reportError("INIT_FAILED", reason, {
|
|
3303
|
+
attempts: this.initAttempts,
|
|
3304
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3305
|
+
});
|
|
3306
|
+
} catch {
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
reportInitSuccess(config, attempts) {
|
|
3310
|
+
try {
|
|
3311
|
+
if (this.apiClient) {
|
|
3312
|
+
void this.apiClient.reportError("INIT_RECOVERED", `Succeeded after ${attempts} retries`, { attempts });
|
|
3313
|
+
}
|
|
3314
|
+
} catch {
|
|
3311
3315
|
}
|
|
3312
3316
|
}
|
|
3313
3317
|
async doInit(config) {
|
|
3314
3318
|
const environment = config.environment ?? (typeof __DEV__ !== "undefined" && __DEV__ ? "Sandbox" : "Production");
|
|
3315
3319
|
const offlineQueueEnabled = config.offlineQueueEnabled !== false;
|
|
3320
|
+
console.warn("[Paywallo INIT]", "Step 1: networkMonitor + offlineQueue...");
|
|
3316
3321
|
await Promise.all([
|
|
3317
3322
|
networkMonitor.initialize({ debug: config.debug }),
|
|
3318
3323
|
offlineQueue.initialize({ debug: config.debug }).then(
|
|
3319
3324
|
() => offlineQueue.clearItemsWithInvalidAppKey(config.appKey)
|
|
3320
3325
|
)
|
|
3321
3326
|
]);
|
|
3322
|
-
|
|
3327
|
+
console.warn("[Paywallo INIT]", "Step 1 DONE");
|
|
3328
|
+
console.warn("[Paywallo INIT]", "Step 2: ApiClient + queueProcessor + affiliate...");
|
|
3329
|
+
this.apiClient = new ApiClient(config.appKey, config.debug, environment, offlineQueueEnabled, config.timeout);
|
|
3323
3330
|
queueProcessor.initialize(this.apiClient.getHttpClient(), { debug: config.debug });
|
|
3324
3331
|
affiliateManager.initialize(this.apiClient, config.debug);
|
|
3332
|
+
console.warn("[Paywallo INIT]", "Step 2 DONE");
|
|
3333
|
+
console.warn("[Paywallo INIT]", "Step 3: identityManager + sessionManager...");
|
|
3325
3334
|
await Promise.all([
|
|
3326
3335
|
identityManager.initialize(this.apiClient, config.debug),
|
|
3327
3336
|
sessionManager.initialize(this.apiClient, config.sessionConfig, config.debug)
|
|
3328
3337
|
]);
|
|
3338
|
+
const distinctId = identityManager.getDistinctId();
|
|
3339
|
+
console.warn("[Paywallo INIT]", "Step 3 DONE \u2014 distinctId:", distinctId ? distinctId.substring(0, 8) + "..." : "EMPTY");
|
|
3329
3340
|
if (config.autoStartSession !== false) {
|
|
3330
3341
|
sessionManager.startSession().catch(() => {
|
|
3331
3342
|
if (config.debug) console.warn("[Paywallo]", "Session start failed during init \u2014 continuing");
|
|
@@ -3334,6 +3345,7 @@ var PaywalloClientClass = class {
|
|
|
3334
3345
|
new InstallTracker(config.debug).trackIfNeeded(this.apiClient, config.requestATT === true).catch(() => {
|
|
3335
3346
|
});
|
|
3336
3347
|
this.config = config;
|
|
3348
|
+
console.warn("[Paywallo INIT]", "COMPLETE \u2014 SDK ready, environment:", environment);
|
|
3337
3349
|
}
|
|
3338
3350
|
async identify(options) {
|
|
3339
3351
|
this.ensureInitialized();
|
|
@@ -3343,15 +3355,33 @@ var PaywalloClientClass = class {
|
|
|
3343
3355
|
const apiClient = this.getApiClientOrThrow();
|
|
3344
3356
|
const distinctId = identityManager.getDistinctId();
|
|
3345
3357
|
if (!distinctId) {
|
|
3346
|
-
|
|
3358
|
+
console.warn("[Paywallo TRACK]", "SKIPPED \u2014 no distinctId", eventName);
|
|
3347
3359
|
return;
|
|
3348
3360
|
}
|
|
3349
3361
|
if (!isValidEventName(eventName)) {
|
|
3350
3362
|
const msg = `Invalid event name: "${eventName}". Use snake_case or $reserved_name.`;
|
|
3351
|
-
if (this.config?.debug) console.warn("[Paywallo]", msg);
|
|
3352
3363
|
throw new ClientError(CLIENT_ERROR_CODES.INVALID_EVENT_NAME, msg);
|
|
3353
3364
|
}
|
|
3354
3365
|
const sessionId = sessionManager.getSessionId();
|
|
3366
|
+
if (eventName === "$purchase_completed") {
|
|
3367
|
+
console.warn("[Paywallo PURCHASE]", "tracking purchase:", JSON.stringify({
|
|
3368
|
+
distinctId: distinctId.substring(0, 8) + "...",
|
|
3369
|
+
productId: options?.properties?.productId,
|
|
3370
|
+
priceUsd: options?.properties?.priceUsd,
|
|
3371
|
+
price: options?.properties?.price,
|
|
3372
|
+
currency: options?.properties?.currency,
|
|
3373
|
+
placement: options?.properties?.placement
|
|
3374
|
+
}));
|
|
3375
|
+
} else if (eventName === "$paywall_viewed" || eventName === "$paywall_closed") {
|
|
3376
|
+
console.warn("[Paywallo PAYWALL]", eventName, JSON.stringify({
|
|
3377
|
+
distinctId: distinctId.substring(0, 8) + "...",
|
|
3378
|
+
placement: options?.properties?.placement,
|
|
3379
|
+
variantKey: options?.properties?.variantKey,
|
|
3380
|
+
timeOnPaywall: options?.properties?.timeOnPaywall
|
|
3381
|
+
}));
|
|
3382
|
+
} else {
|
|
3383
|
+
console.warn("[Paywallo TRACK]", eventName, "distinctId:", distinctId.substring(0, 8) + "...");
|
|
3384
|
+
}
|
|
3355
3385
|
await apiClient.trackEvent(eventName, distinctId, {
|
|
3356
3386
|
...identityManager.getProperties(),
|
|
3357
3387
|
...options?.properties,
|
|
@@ -3367,7 +3397,6 @@ var PaywalloClientClass = class {
|
|
|
3367
3397
|
}
|
|
3368
3398
|
if (!Number.isInteger(index) || index < 0) {
|
|
3369
3399
|
const msg = `Invalid stepIndex: "${index}". Must be a non-negative integer.`;
|
|
3370
|
-
if (this.config?.debug) console.warn("[Paywallo]", msg);
|
|
3371
3400
|
throw new AnalyticsError(ANALYTICS_ERROR_CODES.INVALID_STEP_INDEX, msg);
|
|
3372
3401
|
}
|
|
3373
3402
|
const now = Date.now();
|
|
@@ -3392,7 +3421,6 @@ var PaywalloClientClass = class {
|
|
|
3392
3421
|
}
|
|
3393
3422
|
if (!actionName || typeof actionName !== "string") {
|
|
3394
3423
|
const msg = `Invalid actionName: "${actionName}". Must be a non-empty string.`;
|
|
3395
|
-
if (this.config?.debug) console.warn("[Paywallo]", msg);
|
|
3396
3424
|
throw new AnalyticsError(ANALYTICS_ERROR_CODES.INVALID_ACTION_NAME, msg);
|
|
3397
3425
|
}
|
|
3398
3426
|
const sessionId = sessionManager.getSessionId();
|
|
@@ -3404,15 +3432,32 @@ var PaywalloClientClass = class {
|
|
|
3404
3432
|
});
|
|
3405
3433
|
}
|
|
3406
3434
|
async getVariant(flagKey) {
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3435
|
+
try {
|
|
3436
|
+
const distinctId = identityManager.getDistinctId();
|
|
3437
|
+
if (!distinctId) {
|
|
3438
|
+
console.warn("[Paywallo AB]", "getVariant SKIPPED \u2014 no distinctId", flagKey);
|
|
3439
|
+
return { variant: null };
|
|
3440
|
+
}
|
|
3441
|
+
const result = await this.getApiClientOrThrow().getVariant(flagKey, distinctId);
|
|
3442
|
+
console.warn("[Paywallo AB]", `getVariant("${flagKey}") =`, result.variant);
|
|
3443
|
+
return result;
|
|
3444
|
+
} catch (error) {
|
|
3445
|
+
if (this.config?.debug) console.warn("[Paywallo]", "getVariant failed \u2014 returning null", error);
|
|
3446
|
+
if (this.apiClient) void this.apiClient.reportError("GET_VARIANT_FAILED", error instanceof Error ? error.message : String(error));
|
|
3447
|
+
return { variant: null };
|
|
3448
|
+
}
|
|
3410
3449
|
}
|
|
3411
3450
|
async getConditionalFlag(flagKey, context) {
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3451
|
+
try {
|
|
3452
|
+
const distinctId = identityManager.getDistinctId();
|
|
3453
|
+
if (!distinctId) return false;
|
|
3454
|
+
const result = await this.getApiClientOrThrow().getConditionalFlag(flagKey, { distinctId, ...context });
|
|
3455
|
+
return result.value;
|
|
3456
|
+
} catch (error) {
|
|
3457
|
+
if (this.config?.debug) console.warn("[Paywallo]", "getConditionalFlag failed \u2014 returning false", error);
|
|
3458
|
+
if (this.apiClient) void this.apiClient.reportError("GET_CONDITIONAL_FLAG_FAILED", error instanceof Error ? error.message : String(error));
|
|
3459
|
+
return false;
|
|
3460
|
+
}
|
|
3416
3461
|
}
|
|
3417
3462
|
async getPaywall(placement) {
|
|
3418
3463
|
return this.getApiClientOrThrow().getPaywall(placement);
|
|
@@ -3472,9 +3517,12 @@ var PaywalloClientClass = class {
|
|
|
3472
3517
|
offlineQueue.dispose();
|
|
3473
3518
|
networkMonitor.dispose();
|
|
3474
3519
|
campaignGateService.clearPreloaded();
|
|
3520
|
+
this.cleanupNetworkRecovery();
|
|
3475
3521
|
this.apiClient = null;
|
|
3476
3522
|
this.config = null;
|
|
3477
3523
|
this.initPromise = null;
|
|
3524
|
+
this.pendingConfig = null;
|
|
3525
|
+
this.initAttempts = 0;
|
|
3478
3526
|
this.paywallPresenter = null;
|
|
3479
3527
|
this.campaignPresenter = null;
|
|
3480
3528
|
this.subscriptionGetter = null;
|
|
@@ -3590,6 +3638,9 @@ var PaywalloClientClass = class {
|
|
|
3590
3638
|
if (!this.config || !this.apiClient) throw new ClientError(CLIENT_ERROR_CODES.NOT_INITIALIZED, "SDK not initialized. Call Paywallo.init() first");
|
|
3591
3639
|
}
|
|
3592
3640
|
};
|
|
3641
|
+
_PaywalloClientClass.MAX_INIT_RETRIES = 3;
|
|
3642
|
+
_PaywalloClientClass.RETRY_DELAYS = [2e3, 5e3, 1e4];
|
|
3643
|
+
var PaywalloClientClass = _PaywalloClientClass;
|
|
3593
3644
|
var PaywalloClient = new PaywalloClientClass();
|
|
3594
3645
|
|
|
3595
3646
|
// src/context/PaywalloContext.ts
|
|
@@ -3910,7 +3961,6 @@ var SubscriptionManagerClass = class {
|
|
|
3910
3961
|
if (config.cacheTTL) {
|
|
3911
3962
|
this.cache.setTTL(config.cacheTTL);
|
|
3912
3963
|
}
|
|
3913
|
-
this.log("SubscriptionManager initialized");
|
|
3914
3964
|
}
|
|
3915
3965
|
setUserId(userId) {
|
|
3916
3966
|
if (this.userId !== userId) {
|
|
@@ -4243,7 +4293,7 @@ function useProductLoader() {
|
|
|
4243
4293
|
for (const p of loaded) map.set(p.productId, p);
|
|
4244
4294
|
setProducts(map);
|
|
4245
4295
|
} catch (error) {
|
|
4246
|
-
if (PaywalloClient.getConfig()?.debug) console.warn("[
|
|
4296
|
+
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] Product load failed:", error);
|
|
4247
4297
|
} finally {
|
|
4248
4298
|
setIsLoadingProducts(false);
|
|
4249
4299
|
}
|
|
@@ -4282,7 +4332,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
|
|
|
4282
4332
|
clearPreloadedWebView();
|
|
4283
4333
|
resolvePreloadedPaywall({ presented: true, purchased: true, cancelled: false, restored: false });
|
|
4284
4334
|
} catch (error) {
|
|
4285
|
-
if (PaywalloClient.getConfig()?.debug) console.warn("[
|
|
4335
|
+
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] Preloaded purchase error:", error);
|
|
4286
4336
|
setShowingPreloadedWebView(false);
|
|
4287
4337
|
clearPreloadedWebView();
|
|
4288
4338
|
resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: false });
|
|
@@ -4302,7 +4352,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
|
|
|
4302
4352
|
clearPreloadedWebView();
|
|
4303
4353
|
resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: true });
|
|
4304
4354
|
} catch (error) {
|
|
4305
|
-
if (PaywalloClient.getConfig()?.debug) console.warn("[
|
|
4355
|
+
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] Preloaded restore error:", error);
|
|
4306
4356
|
setShowingPreloadedWebView(false);
|
|
4307
4357
|
clearPreloadedWebView();
|
|
4308
4358
|
resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: false });
|