@virex-tech/paywallo-sdk 1.1.1 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/paywallo/sdk/PaywalloWebView.kt +45 -0
- package/android/src/main/java/com/paywallo/sdk/PaywalloWebViewManager.kt +1 -0
- package/dist/index.d.mts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +427 -145
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +498 -207
- package/dist/index.mjs.map +1 -1
- package/ios/PaywalloWebView.h +1 -0
- package/ios/PaywalloWebView.m +20 -0
- package/ios/PaywalloWebViewManager.m +1 -0
- package/ios/PaywalloWebViewModule.h +1 -0
- package/ios/PaywalloWebViewModule.m +8 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -38,8 +38,17 @@ var CLIENT_ERROR_CODES = {
|
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
// src/domains/paywall/PaywallModal.tsx
|
|
41
|
-
import { useEffect as useEffect3, useRef as useRef4 } from "react";
|
|
42
|
-
import {
|
|
41
|
+
import { useCallback as useCallback4, useEffect as useEffect3, useRef as useRef4, useState as useState4 } from "react";
|
|
42
|
+
import {
|
|
43
|
+
Animated as Animated2,
|
|
44
|
+
Dimensions as Dimensions2,
|
|
45
|
+
Easing,
|
|
46
|
+
Modal,
|
|
47
|
+
Pressable,
|
|
48
|
+
StyleSheet as StyleSheet2,
|
|
49
|
+
Text,
|
|
50
|
+
View as View2
|
|
51
|
+
} from "react-native";
|
|
43
52
|
|
|
44
53
|
// src/domains/paywall/usePaywallActions.ts
|
|
45
54
|
import { useCallback as useCallback2, useRef, useState } from "react";
|
|
@@ -860,11 +869,6 @@ function usePaywallLoader(placement, visible, preloadedConfig, preloadedProducts
|
|
|
860
869
|
const storeProducts = await getIAPService().loadProducts(productIds);
|
|
861
870
|
const map = /* @__PURE__ */ new Map();
|
|
862
871
|
for (const p of storeProducts) map.set(p.productId, p);
|
|
863
|
-
if (PaywalloClient.getConfig()?.debug) {
|
|
864
|
-
for (const [id, p] of map) {
|
|
865
|
-
console.warn(`[Paywallo][CURRENCY-CHECK] product=${id} currency=${p.currency} price="${p.price}" localizedPrice="${p.localizedPrice}" priceValue=${p.priceValue} fallbackUSD=${!p.currency || p.currency === "USD" ? "POSSIBLE" : "NO"}`);
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
872
|
setProducts(map);
|
|
869
873
|
} catch {
|
|
870
874
|
setProducts(/* @__PURE__ */ new Map());
|
|
@@ -951,6 +955,13 @@ function getWebViewEmitter() {
|
|
|
951
955
|
}
|
|
952
956
|
return webViewEmitter;
|
|
953
957
|
}
|
|
958
|
+
function parseErrorEvent(raw) {
|
|
959
|
+
return {
|
|
960
|
+
code: typeof raw["code"] === "number" ? raw["code"] : -1,
|
|
961
|
+
description: typeof raw["description"] === "string" ? raw["description"] : "WebView error",
|
|
962
|
+
url: typeof raw["url"] === "string" ? raw["url"] : void 0
|
|
963
|
+
};
|
|
964
|
+
}
|
|
954
965
|
function PaywallWebView({
|
|
955
966
|
paywallId,
|
|
956
967
|
craftData,
|
|
@@ -961,11 +972,14 @@ function PaywallWebView({
|
|
|
961
972
|
onPurchase,
|
|
962
973
|
onClose,
|
|
963
974
|
onRestore,
|
|
964
|
-
onReady
|
|
975
|
+
onReady,
|
|
976
|
+
onError
|
|
965
977
|
}) {
|
|
966
978
|
const paywallDataSent = useRef3(false);
|
|
967
979
|
const loadEndReceived = useRef3(false);
|
|
968
980
|
const [scriptToInject, setScriptToInject] = useState3(void 0);
|
|
981
|
+
const onErrorRef = useRef3(onError);
|
|
982
|
+
onErrorRef.current = onError;
|
|
969
983
|
const NativeWebView = useMemo(() => getNativeWebView(), []);
|
|
970
984
|
const webUrl = useMemo(() => PaywalloClient.getWebUrl() ?? "http://localhost:3000", []);
|
|
971
985
|
const buildAndSetInjectionScript = useCallback3(() => {
|
|
@@ -1025,17 +1039,51 @@ function PaywallWebView({
|
|
|
1025
1039
|
const message = parseWebViewMessage(event.data);
|
|
1026
1040
|
if (message) handleParsedMessage(message);
|
|
1027
1041
|
});
|
|
1042
|
+
const errorSub = emitter.addListener(
|
|
1043
|
+
"PaywalloWebViewError",
|
|
1044
|
+
(event) => {
|
|
1045
|
+
onErrorRef.current?.(parseErrorEvent(event));
|
|
1046
|
+
}
|
|
1047
|
+
);
|
|
1028
1048
|
return () => {
|
|
1029
1049
|
loadEndSub.remove();
|
|
1030
1050
|
messageSub.remove();
|
|
1051
|
+
errorSub.remove();
|
|
1031
1052
|
};
|
|
1032
1053
|
}, [buildAndSetInjectionScript, handleParsedMessage]);
|
|
1054
|
+
useEffect2(() => {
|
|
1055
|
+
const timer = setTimeout(() => {
|
|
1056
|
+
if (!paywallDataSent.current) {
|
|
1057
|
+
onErrorRef.current?.({
|
|
1058
|
+
code: -1001,
|
|
1059
|
+
description: "Paywall load timed out after 10 seconds"
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
}, 1e4);
|
|
1063
|
+
return () => clearTimeout(timer);
|
|
1064
|
+
}, []);
|
|
1033
1065
|
const handleLoadEnd = useCallback3(() => {
|
|
1034
1066
|
if (!loadEndReceived.current) {
|
|
1035
1067
|
loadEndReceived.current = true;
|
|
1036
1068
|
buildAndSetInjectionScript();
|
|
1037
1069
|
}
|
|
1038
1070
|
}, [buildAndSetInjectionScript]);
|
|
1071
|
+
const handleNativeError = useCallback3((event) => {
|
|
1072
|
+
const ne = event.nativeEvent;
|
|
1073
|
+
let errorData;
|
|
1074
|
+
if (ne["payload"] && typeof ne["payload"] === "object") {
|
|
1075
|
+
errorData = ne["payload"];
|
|
1076
|
+
} else if (typeof ne["data"] === "string") {
|
|
1077
|
+
try {
|
|
1078
|
+
errorData = JSON.parse(ne["data"]);
|
|
1079
|
+
} catch {
|
|
1080
|
+
errorData = {};
|
|
1081
|
+
}
|
|
1082
|
+
} else {
|
|
1083
|
+
errorData = {};
|
|
1084
|
+
}
|
|
1085
|
+
onErrorRef.current?.(parseErrorEvent(errorData));
|
|
1086
|
+
}, []);
|
|
1039
1087
|
const handleMessage = useCallback3(
|
|
1040
1088
|
(event) => {
|
|
1041
1089
|
const message = parseWebViewMessage(event.nativeEvent.data);
|
|
@@ -1055,6 +1103,7 @@ function PaywallWebView({
|
|
|
1055
1103
|
injectedJavaScript: scriptToInject,
|
|
1056
1104
|
onMessage: handleMessage,
|
|
1057
1105
|
onLoadEnd: handleLoadEnd,
|
|
1106
|
+
onError: handleNativeError,
|
|
1058
1107
|
style: styles.webview
|
|
1059
1108
|
}
|
|
1060
1109
|
) });
|
|
@@ -1065,7 +1114,7 @@ var styles = StyleSheet.create({
|
|
|
1065
1114
|
});
|
|
1066
1115
|
|
|
1067
1116
|
// src/domains/paywall/PaywallModal.tsx
|
|
1068
|
-
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
1117
|
+
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
1069
1118
|
var SCREEN_HEIGHT2 = Dimensions2.get("window").height;
|
|
1070
1119
|
function PaywallModal({
|
|
1071
1120
|
placement,
|
|
@@ -1085,6 +1134,25 @@ function PaywallModal({
|
|
|
1085
1134
|
campaignId
|
|
1086
1135
|
);
|
|
1087
1136
|
const { isPurchasing, slideAnim, handleClose, handlePurchase, handleRestore } = usePaywallActions(placement, paywallConfig, onResult, variantKey, campaignId);
|
|
1137
|
+
const [webViewError, setWebViewError] = useState4(null);
|
|
1138
|
+
const [retryCount, setRetryCount] = useState4(0);
|
|
1139
|
+
const retryCountRef = useRef4(0);
|
|
1140
|
+
const handleWebViewError = useCallback4(
|
|
1141
|
+
(err) => {
|
|
1142
|
+
if (retryCountRef.current >= 1) {
|
|
1143
|
+
setWebViewError({ code: err.code, description: err.description });
|
|
1144
|
+
} else {
|
|
1145
|
+
retryCountRef.current += 1;
|
|
1146
|
+
setRetryCount(retryCountRef.current);
|
|
1147
|
+
}
|
|
1148
|
+
},
|
|
1149
|
+
[]
|
|
1150
|
+
);
|
|
1151
|
+
const handleRetry = useCallback4(() => {
|
|
1152
|
+
retryCountRef.current = 0;
|
|
1153
|
+
setWebViewError(null);
|
|
1154
|
+
setRetryCount(0);
|
|
1155
|
+
}, []);
|
|
1088
1156
|
const openAnim = useRef4(new Animated2.Value(SCREEN_HEIGHT2)).current;
|
|
1089
1157
|
useEffect3(() => {
|
|
1090
1158
|
if (visible) {
|
|
@@ -1109,7 +1177,12 @@ function PaywallModal({
|
|
|
1109
1177
|
statusBarTranslucent: true,
|
|
1110
1178
|
children: /* @__PURE__ */ jsx2(View2, { style: styles2.overlay, children: /* @__PURE__ */ jsx2(Animated2.View, { style: [styles2.animatedContainer, { transform: [{ translateY: openAnim }] }], children: /* @__PURE__ */ jsxs(View2, { style: styles2.container, children: [
|
|
1111
1179
|
error && /* @__PURE__ */ jsx2(View2, { style: styles2.errorContainer, children: /* @__PURE__ */ jsx2(Text, { style: styles2.errorText, children: error.message }) }),
|
|
1112
|
-
!error && paywallConfig && "craftData" in paywallConfig.config && /* @__PURE__ */ jsx2(
|
|
1180
|
+
!error && paywallConfig && "craftData" in paywallConfig.config && /* @__PURE__ */ jsx2(Fragment, { children: webViewError ? /* @__PURE__ */ jsx2(View2, { style: styles2.fallbackContainer, children: /* @__PURE__ */ jsxs(View2, { style: styles2.fallbackCard, children: [
|
|
1181
|
+
/* @__PURE__ */ jsx2(Text, { style: styles2.fallbackTitle, children: "Algo deu errado" }),
|
|
1182
|
+
/* @__PURE__ */ jsx2(Text, { style: styles2.fallbackDescription, children: webViewError.description }),
|
|
1183
|
+
/* @__PURE__ */ jsx2(Pressable, { style: styles2.retryButton, onPress: handleRetry, children: /* @__PURE__ */ jsx2(Text, { style: styles2.retryButtonText, children: "Tentar novamente" }) }),
|
|
1184
|
+
/* @__PURE__ */ jsx2(Pressable, { style: styles2.closeButton, onPress: handleClose, children: /* @__PURE__ */ jsx2(Text, { style: styles2.closeButtonText, children: "Fechar" }) })
|
|
1185
|
+
] }) }) : /* @__PURE__ */ jsx2(
|
|
1113
1186
|
PaywallWebView,
|
|
1114
1187
|
{
|
|
1115
1188
|
paywallId: paywallConfig.id,
|
|
@@ -1120,39 +1193,67 @@ function PaywallModal({
|
|
|
1120
1193
|
isPurchasing,
|
|
1121
1194
|
onClose: handleClose,
|
|
1122
1195
|
onPurchase: handlePurchase,
|
|
1123
|
-
onRestore: handleRestore
|
|
1124
|
-
|
|
1125
|
-
|
|
1196
|
+
onRestore: handleRestore,
|
|
1197
|
+
onError: handleWebViewError
|
|
1198
|
+
},
|
|
1199
|
+
retryCount
|
|
1200
|
+
) })
|
|
1126
1201
|
] }) }) })
|
|
1127
1202
|
}
|
|
1128
1203
|
);
|
|
1129
1204
|
}
|
|
1130
1205
|
var styles2 = StyleSheet2.create({
|
|
1131
|
-
overlay: {
|
|
1206
|
+
overlay: { flex: 1, backgroundColor: "rgba(0,0,0,0.3)" },
|
|
1207
|
+
animatedContainer: { flex: 1, backgroundColor: "#fff" },
|
|
1208
|
+
container: { flex: 1, backgroundColor: "#fff" },
|
|
1209
|
+
errorContainer: { flex: 1, justifyContent: "center", alignItems: "center", padding: 20 },
|
|
1210
|
+
errorText: { fontSize: 16, color: "#ef4444", textAlign: "center" },
|
|
1211
|
+
fallbackContainer: {
|
|
1132
1212
|
flex: 1,
|
|
1133
|
-
|
|
1213
|
+
justifyContent: "center",
|
|
1214
|
+
alignItems: "center",
|
|
1215
|
+
backgroundColor: "#111010",
|
|
1216
|
+
padding: 24
|
|
1134
1217
|
},
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1218
|
+
fallbackCard: {
|
|
1219
|
+
width: "100%",
|
|
1220
|
+
maxWidth: 360,
|
|
1221
|
+
alignItems: "center",
|
|
1222
|
+
backgroundColor: "rgba(255,255,255,0.07)",
|
|
1223
|
+
borderRadius: 16,
|
|
1224
|
+
padding: 28
|
|
1140
1225
|
},
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1226
|
+
fallbackTitle: {
|
|
1227
|
+
fontSize: 18,
|
|
1228
|
+
fontWeight: "600",
|
|
1229
|
+
color: "#f5f5f5",
|
|
1230
|
+
marginBottom: 10,
|
|
1231
|
+
textAlign: "center"
|
|
1144
1232
|
},
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1233
|
+
fallbackDescription: {
|
|
1234
|
+
fontSize: 14,
|
|
1235
|
+
color: "rgba(255,255,255,0.55)",
|
|
1236
|
+
textAlign: "center",
|
|
1237
|
+
marginBottom: 28,
|
|
1238
|
+
lineHeight: 20
|
|
1239
|
+
},
|
|
1240
|
+
retryButton: {
|
|
1241
|
+
width: "100%",
|
|
1242
|
+
backgroundColor: "#ffffff",
|
|
1243
|
+
borderRadius: 10,
|
|
1244
|
+
paddingVertical: 14,
|
|
1148
1245
|
alignItems: "center",
|
|
1149
|
-
|
|
1246
|
+
marginBottom: 12
|
|
1150
1247
|
},
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1248
|
+
retryButtonText: { fontSize: 15, fontWeight: "600", color: "#111010" },
|
|
1249
|
+
closeButton: {
|
|
1250
|
+
width: "100%",
|
|
1251
|
+
borderRadius: 10,
|
|
1252
|
+
paddingVertical: 14,
|
|
1253
|
+
alignItems: "center",
|
|
1254
|
+
backgroundColor: "rgba(255,255,255,0.1)"
|
|
1255
|
+
},
|
|
1256
|
+
closeButtonText: { fontSize: 15, fontWeight: "500", color: "rgba(255,255,255,0.7)" }
|
|
1156
1257
|
});
|
|
1157
1258
|
|
|
1158
1259
|
// src/domains/paywall/PaywallErrorBoundary.tsx
|
|
@@ -1188,9 +1289,9 @@ function usePaywallContext() {
|
|
|
1188
1289
|
}
|
|
1189
1290
|
|
|
1190
1291
|
// src/domains/paywall/usePaywallPresenter.ts
|
|
1191
|
-
import { useCallback as
|
|
1292
|
+
import { useCallback as useCallback5, useEffect as useEffect4, useRef as useRef5, useState as useState5 } from "react";
|
|
1192
1293
|
function usePaywallPresenter(preloadedWebView, clearPreloadedWebView, setPreloadedWebView) {
|
|
1193
|
-
const [showingPreloadedWebView, setShowingPreloadedWebView] =
|
|
1294
|
+
const [showingPreloadedWebView, setShowingPreloadedWebView] = useState5(false);
|
|
1194
1295
|
const resolverRef = useRef5(null);
|
|
1195
1296
|
const presentedAtRef = useRef5(Date.now());
|
|
1196
1297
|
useEffect4(() => {
|
|
@@ -1201,23 +1302,23 @@ function usePaywallPresenter(preloadedWebView, clearPreloadedWebView, setPreload
|
|
|
1201
1302
|
}
|
|
1202
1303
|
};
|
|
1203
1304
|
}, []);
|
|
1204
|
-
const presentPreloadedWebView =
|
|
1305
|
+
const presentPreloadedWebView = useCallback5(() => {
|
|
1205
1306
|
return new Promise((resolve) => {
|
|
1206
1307
|
resolverRef.current = resolve;
|
|
1207
1308
|
presentedAtRef.current = Date.now();
|
|
1208
1309
|
setShowingPreloadedWebView(true);
|
|
1209
1310
|
});
|
|
1210
1311
|
}, []);
|
|
1211
|
-
const resolvePreloadedPaywall =
|
|
1312
|
+
const resolvePreloadedPaywall = useCallback5((result) => {
|
|
1212
1313
|
if (resolverRef.current) {
|
|
1213
1314
|
resolverRef.current(result);
|
|
1214
1315
|
resolverRef.current = null;
|
|
1215
1316
|
}
|
|
1216
1317
|
}, []);
|
|
1217
|
-
const handlePreloadedWebViewReady =
|
|
1318
|
+
const handlePreloadedWebViewReady = useCallback5(() => {
|
|
1218
1319
|
setPreloadedWebView((prev) => prev ? { ...prev, loaded: true } : null);
|
|
1219
1320
|
}, [setPreloadedWebView]);
|
|
1220
|
-
const handlePreloadedClose =
|
|
1321
|
+
const handlePreloadedClose = useCallback5(() => {
|
|
1221
1322
|
const track = async () => {
|
|
1222
1323
|
try {
|
|
1223
1324
|
const apiClient = PaywalloClient.getApiClient();
|
|
@@ -1467,6 +1568,92 @@ var PaywalloAffiliate = {
|
|
|
1467
1568
|
getWithdrawals: () => affiliateManager.getWithdrawals()
|
|
1468
1569
|
};
|
|
1469
1570
|
|
|
1571
|
+
// src/domains/subscription/SubscriptionCache.ts
|
|
1572
|
+
var CACHE_KEY = "subscription_cache";
|
|
1573
|
+
var DEFAULT_TTL = 24 * 60 * 60 * 1e3;
|
|
1574
|
+
var SubscriptionCache = class {
|
|
1575
|
+
constructor(ttl = DEFAULT_TTL) {
|
|
1576
|
+
this.memoryCache = null;
|
|
1577
|
+
this.ttl = ttl;
|
|
1578
|
+
this.storage = new SecureStorage();
|
|
1579
|
+
}
|
|
1580
|
+
async get() {
|
|
1581
|
+
if (this.memoryCache && !this.isExpired(this.memoryCache)) {
|
|
1582
|
+
return this.memoryCache;
|
|
1583
|
+
}
|
|
1584
|
+
try {
|
|
1585
|
+
const stored = await this.storage.get(CACHE_KEY);
|
|
1586
|
+
if (!stored) {
|
|
1587
|
+
return null;
|
|
1588
|
+
}
|
|
1589
|
+
const parsed = JSON.parse(stored);
|
|
1590
|
+
if (typeof parsed !== "object" || parsed === null || !("data" in parsed) || typeof parsed.data !== "object" || parsed.data === null || !("cachedAt" in parsed) || typeof parsed.cachedAt !== "number") {
|
|
1591
|
+
await this.storage.remove(CACHE_KEY);
|
|
1592
|
+
return null;
|
|
1593
|
+
}
|
|
1594
|
+
const cached = parsed;
|
|
1595
|
+
if (cached.data.subscription?.expiresAt) {
|
|
1596
|
+
cached.data.subscription.expiresAt = new Date(cached.data.subscription.expiresAt);
|
|
1597
|
+
}
|
|
1598
|
+
if (cached.data.subscription?.originalPurchaseDate) {
|
|
1599
|
+
cached.data.subscription.originalPurchaseDate = new Date(
|
|
1600
|
+
cached.data.subscription.originalPurchaseDate
|
|
1601
|
+
);
|
|
1602
|
+
}
|
|
1603
|
+
if (cached.data.subscription?.latestPurchaseDate) {
|
|
1604
|
+
cached.data.subscription.latestPurchaseDate = new Date(
|
|
1605
|
+
cached.data.subscription.latestPurchaseDate
|
|
1606
|
+
);
|
|
1607
|
+
}
|
|
1608
|
+
if (cached.data.subscription?.cancellationDate) {
|
|
1609
|
+
cached.data.subscription.cancellationDate = new Date(
|
|
1610
|
+
cached.data.subscription.cancellationDate
|
|
1611
|
+
);
|
|
1612
|
+
}
|
|
1613
|
+
if (cached.data.subscription?.gracePeriodExpiresAt) {
|
|
1614
|
+
cached.data.subscription.gracePeriodExpiresAt = new Date(
|
|
1615
|
+
cached.data.subscription.gracePeriodExpiresAt
|
|
1616
|
+
);
|
|
1617
|
+
}
|
|
1618
|
+
cached.isStale = this.isExpired(cached);
|
|
1619
|
+
if (!cached.isStale) {
|
|
1620
|
+
this.memoryCache = cached;
|
|
1621
|
+
}
|
|
1622
|
+
return cached;
|
|
1623
|
+
} catch {
|
|
1624
|
+
return null;
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
async set(data) {
|
|
1628
|
+
const cached = {
|
|
1629
|
+
data,
|
|
1630
|
+
cachedAt: Date.now(),
|
|
1631
|
+
isStale: false
|
|
1632
|
+
};
|
|
1633
|
+
this.memoryCache = cached;
|
|
1634
|
+
try {
|
|
1635
|
+
await this.storage.set(CACHE_KEY, JSON.stringify(cached));
|
|
1636
|
+
} catch (error) {
|
|
1637
|
+
if (__DEV__) console.warn("[SubscriptionCache] set() failed:", error instanceof Error ? error.message : String(error));
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
async invalidate() {
|
|
1641
|
+
this.memoryCache = null;
|
|
1642
|
+
try {
|
|
1643
|
+
await this.storage.remove(CACHE_KEY);
|
|
1644
|
+
} catch (error) {
|
|
1645
|
+
if (__DEV__) console.warn("[SubscriptionCache] invalidate() failed:", error instanceof Error ? error.message : String(error));
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
isExpired(cached) {
|
|
1649
|
+
return Date.now() - cached.cachedAt > this.ttl;
|
|
1650
|
+
}
|
|
1651
|
+
setTTL(ttl) {
|
|
1652
|
+
this.ttl = ttl;
|
|
1653
|
+
}
|
|
1654
|
+
};
|
|
1655
|
+
var subscriptionCache = new SubscriptionCache();
|
|
1656
|
+
|
|
1470
1657
|
// src/core/ApiClient.ts
|
|
1471
1658
|
import { Platform as Platform3 } from "react-native";
|
|
1472
1659
|
|
|
@@ -2329,7 +2516,7 @@ var ApiCache = class {
|
|
|
2329
2516
|
// src/core/ApiClient.ts
|
|
2330
2517
|
var SDK_VERSION = "1.0.0";
|
|
2331
2518
|
var DEFAULT_BASE_URL = "https://panel.lucasqueiroga.shop";
|
|
2332
|
-
var
|
|
2519
|
+
var _ApiClient = class _ApiClient {
|
|
2333
2520
|
constructor(appKey, debug = false, environment = "Production", offlineQueueEnabled = true, timeout = 3e4) {
|
|
2334
2521
|
this.cache = new ApiCache();
|
|
2335
2522
|
this.appKey = appKey;
|
|
@@ -2419,6 +2606,11 @@ var ApiClient = class {
|
|
|
2419
2606
|
const key = `${flagKey}:${distinctId}`;
|
|
2420
2607
|
const hit = this.cache.getVariant(key);
|
|
2421
2608
|
if (hit !== void 0) return hit;
|
|
2609
|
+
const persisted = await this.readFlagFromStorage(flagKey, distinctId);
|
|
2610
|
+
if (persisted !== null) {
|
|
2611
|
+
this.cache.setVariant(key, persisted);
|
|
2612
|
+
return persisted;
|
|
2613
|
+
}
|
|
2422
2614
|
try {
|
|
2423
2615
|
const res = await this.get(`/api/v1/flags/${flagKey}?distinctId=${encodeURIComponent(distinctId)}`);
|
|
2424
2616
|
if (res.status === 404) {
|
|
@@ -2433,6 +2625,7 @@ var ApiClient = class {
|
|
|
2433
2625
|
return { variant: null };
|
|
2434
2626
|
}
|
|
2435
2627
|
this.cache.setVariant(key, res.data);
|
|
2628
|
+
await this.writeFlagToStorage(flagKey, distinctId, res.data);
|
|
2436
2629
|
return res.data;
|
|
2437
2630
|
} catch (error) {
|
|
2438
2631
|
this.log("Failed to get variant:", flagKey, error);
|
|
@@ -2514,6 +2707,35 @@ var ApiClient = class {
|
|
|
2514
2707
|
this.log("Subscription status:", result.hasActiveSubscription);
|
|
2515
2708
|
return result;
|
|
2516
2709
|
}
|
|
2710
|
+
async evaluateFlags(keys, distinctId) {
|
|
2711
|
+
const query = keys.map(encodeURIComponent).join(",");
|
|
2712
|
+
const path = `/api/v1/flags/evaluate?keys=${query}`;
|
|
2713
|
+
try {
|
|
2714
|
+
const res = await this.httpClient.get(path, {
|
|
2715
|
+
headers: { "X-App-Key": this.appKey, "x-distinct-id": distinctId }
|
|
2716
|
+
});
|
|
2717
|
+
if (!res.ok) {
|
|
2718
|
+
this.log("Non-OK response for evaluateFlags:", res.status);
|
|
2719
|
+
return {};
|
|
2720
|
+
}
|
|
2721
|
+
const raw = res.data;
|
|
2722
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return {};
|
|
2723
|
+
const record = raw;
|
|
2724
|
+
const result = {};
|
|
2725
|
+
for (const key of Object.keys(record)) {
|
|
2726
|
+
const value = record[key];
|
|
2727
|
+
const variant = typeof value === "boolean" ? String(value) : typeof value === "string" ? value : null;
|
|
2728
|
+
result[key] = variant;
|
|
2729
|
+
const flagVariant = { variant };
|
|
2730
|
+
this.cache.setVariant(`${key}:${distinctId}`, flagVariant);
|
|
2731
|
+
await this.writeFlagToStorage(key, distinctId, flagVariant);
|
|
2732
|
+
}
|
|
2733
|
+
return result;
|
|
2734
|
+
} catch (error) {
|
|
2735
|
+
this.log("Failed to evaluateFlags:", error);
|
|
2736
|
+
return {};
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2517
2739
|
async getConditionalFlag(flagKey, context) {
|
|
2518
2740
|
try {
|
|
2519
2741
|
const url = buildConditionalFlagUrl(this.baseUrl, flagKey, context);
|
|
@@ -2530,7 +2752,43 @@ var ApiClient = class {
|
|
|
2530
2752
|
return { value: false, flagKey };
|
|
2531
2753
|
}
|
|
2532
2754
|
}
|
|
2533
|
-
// ───
|
|
2755
|
+
// ─── Cache-only reads ────────────────────────────────────────────────────────
|
|
2756
|
+
/**
|
|
2757
|
+
* Reads a flag variant from cache layers only — no network call.
|
|
2758
|
+
* Checks in-memory cache first, then SecureStorage.
|
|
2759
|
+
* Returns null when both layers miss.
|
|
2760
|
+
*/
|
|
2761
|
+
async getVariantFromCache(flagKey, distinctId) {
|
|
2762
|
+
const key = `${flagKey}:${distinctId}`;
|
|
2763
|
+
const hit = this.cache.getVariant(key);
|
|
2764
|
+
if (hit !== void 0) return hit;
|
|
2765
|
+
const persisted = await this.readFlagFromStorage(flagKey, distinctId);
|
|
2766
|
+
if (persisted !== null) {
|
|
2767
|
+
this.cache.setVariant(key, persisted);
|
|
2768
|
+
return persisted;
|
|
2769
|
+
}
|
|
2770
|
+
return null;
|
|
2771
|
+
}
|
|
2772
|
+
async readFlagFromStorage(flagKey, distinctId) {
|
|
2773
|
+
try {
|
|
2774
|
+
const raw = await secureStorage.get(`flag:${flagKey}:${distinctId}`);
|
|
2775
|
+
if (!raw) return null;
|
|
2776
|
+
const parsed = JSON.parse(raw);
|
|
2777
|
+
if (typeof parsed !== "object" || parsed === null || !("variant" in parsed) || !("cachedAt" in parsed) || typeof parsed.cachedAt !== "number") return null;
|
|
2778
|
+
const { variant, payload, cachedAt } = parsed;
|
|
2779
|
+
if (Date.now() - cachedAt > _ApiClient.FLAG_MAX_AGE_MS) return null;
|
|
2780
|
+
return { variant, payload };
|
|
2781
|
+
} catch {
|
|
2782
|
+
return null;
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
async writeFlagToStorage(flagKey, distinctId, flag) {
|
|
2786
|
+
try {
|
|
2787
|
+
const value = JSON.stringify({ variant: flag.variant, payload: flag.payload, cachedAt: Date.now() });
|
|
2788
|
+
await secureStorage.set(`flag:${flagKey}:${distinctId}`, value);
|
|
2789
|
+
} catch {
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2534
2792
|
/**
|
|
2535
2793
|
* POST with offline queue fallback. Enqueues when offline or on network failure.
|
|
2536
2794
|
* Throws only when offlineQueueEnabled is false and the request fails.
|
|
@@ -2557,6 +2815,9 @@ var ApiClient = class {
|
|
|
2557
2815
|
if (this.debug) console.warn("[Paywallo API]", ...args);
|
|
2558
2816
|
}
|
|
2559
2817
|
};
|
|
2818
|
+
// ─── Private helpers ─────────────────────────────────────────────────────────
|
|
2819
|
+
_ApiClient.FLAG_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
2820
|
+
var ApiClient = _ApiClient;
|
|
2560
2821
|
|
|
2561
2822
|
// src/domains/campaign/CampaignError.ts
|
|
2562
2823
|
var CampaignError = class extends PaywalloError {
|
|
@@ -2584,18 +2845,21 @@ var CampaignGateService = class {
|
|
|
2584
2845
|
if (!distinctId) return null;
|
|
2585
2846
|
return apiClient.getCampaign(placement, distinctId, context);
|
|
2586
2847
|
}
|
|
2587
|
-
async presentCampaign(apiClient, placement, paywallPresenter, campaignPresenter, context) {
|
|
2848
|
+
async presentCampaign(apiClient, placement, hasActive, paywallPresenter, campaignPresenter, context) {
|
|
2588
2849
|
const campaign = await this.getCampaign(apiClient, placement, context);
|
|
2589
2850
|
if (!campaign) {
|
|
2590
2851
|
return { presented: false, purchased: false, cancelled: false, restored: false, error: new CampaignError(CAMPAIGN_ERROR_CODES.NOT_FOUND, "No campaign found") };
|
|
2591
2852
|
}
|
|
2853
|
+
if (!context?.forceShow && await hasActive()) {
|
|
2854
|
+
return { presented: false, purchased: false, cancelled: false, restored: false, skippedReason: "subscriber" };
|
|
2855
|
+
}
|
|
2592
2856
|
if (campaignPresenter) return campaignPresenter(campaign);
|
|
2593
2857
|
if (!paywallPresenter) throw new PaywallError(PAYWALL_ERROR_CODES.NOT_INITIALIZED, "PaywalloProvider not mounted. Wrap your app with <PaywalloProvider>");
|
|
2594
2858
|
return { ...await paywallPresenter(campaign.paywall.placement), campaignId: campaign.campaignId, variantKey: campaign.variantKey };
|
|
2595
2859
|
}
|
|
2596
2860
|
async requireSubscriptionWithCampaign(apiClient, placement, hasActive, paywallPresenter, campaignPresenter, context) {
|
|
2597
2861
|
if (await hasActive()) return true;
|
|
2598
|
-
const r = await this.presentCampaign(apiClient, placement, paywallPresenter, campaignPresenter, context);
|
|
2862
|
+
const r = await this.presentCampaign(apiClient, placement, hasActive, paywallPresenter, campaignPresenter, context);
|
|
2599
2863
|
return r.purchased || r.restored;
|
|
2600
2864
|
}
|
|
2601
2865
|
async preloadCampaign(apiClient, placement, context) {
|
|
@@ -3132,6 +3396,20 @@ function isValidEventName(name) {
|
|
|
3132
3396
|
if (name.startsWith("$")) return /^\$[a-z][a-z0-9]*(_[a-z0-9]+)*$/.test(name);
|
|
3133
3397
|
return /^[a-z][a-z0-9]*(_[a-z0-9]+)*$/.test(name);
|
|
3134
3398
|
}
|
|
3399
|
+
var OFFLINE_ACTIVE_STATUSES = ["active", "grace_period"];
|
|
3400
|
+
async function resolveClientOfflineFromCache() {
|
|
3401
|
+
try {
|
|
3402
|
+
const cached = await subscriptionCache.get();
|
|
3403
|
+
if (!cached) return false;
|
|
3404
|
+
const sub = cached.data.subscription;
|
|
3405
|
+
if (!sub) return false;
|
|
3406
|
+
if (!OFFLINE_ACTIVE_STATUSES.includes(sub.status)) return false;
|
|
3407
|
+
if (!sub.expiresAt) return true;
|
|
3408
|
+
return sub.expiresAt > /* @__PURE__ */ new Date();
|
|
3409
|
+
} catch {
|
|
3410
|
+
return false;
|
|
3411
|
+
}
|
|
3412
|
+
}
|
|
3135
3413
|
var _PaywalloClientClass = class _PaywalloClientClass {
|
|
3136
3414
|
constructor() {
|
|
3137
3415
|
this.config = null;
|
|
@@ -3148,27 +3426,27 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3148
3426
|
this.initAttempts = 0;
|
|
3149
3427
|
}
|
|
3150
3428
|
async init(config) {
|
|
3151
|
-
console.warn("[Paywallo INIT]", "init() called \u2014 appKey:", config.appKey ? config.appKey.substring(0, 8) + "..." : "MISSING");
|
|
3429
|
+
if (config.debug) console.warn("[Paywallo INIT]", "init() called \u2014 appKey:", config.appKey ? config.appKey.substring(0, 8) + "..." : "MISSING");
|
|
3152
3430
|
if (this.config && this.apiClient) {
|
|
3153
|
-
console.warn("[Paywallo INIT]", "Already initialized \u2014 skipping");
|
|
3431
|
+
if (config.debug) console.warn("[Paywallo INIT]", "Already initialized \u2014 skipping");
|
|
3154
3432
|
return;
|
|
3155
3433
|
}
|
|
3156
3434
|
if (this.initPromise) {
|
|
3157
|
-
console.warn("[Paywallo INIT]", "Init already in progress \u2014 awaiting...");
|
|
3435
|
+
if (config.debug) console.warn("[Paywallo INIT]", "Init already in progress \u2014 awaiting...");
|
|
3158
3436
|
await this.initPromise;
|
|
3159
|
-
console.warn("[Paywallo INIT]", "Awaited existing init \u2014 done");
|
|
3437
|
+
if (config.debug) console.warn("[Paywallo INIT]", "Awaited existing init \u2014 done");
|
|
3160
3438
|
return;
|
|
3161
3439
|
}
|
|
3162
3440
|
if (!config.appKey) throw new ClientError(CLIENT_ERROR_CODES.MISSING_APP_KEY, "appKey is required");
|
|
3163
|
-
console.warn("[Paywallo INIT]", "Starting fresh init...");
|
|
3441
|
+
if (config.debug) console.warn("[Paywallo INIT]", "Starting fresh init...");
|
|
3164
3442
|
this.pendingConfig = config;
|
|
3165
3443
|
this.initAttempts = 0;
|
|
3166
3444
|
this.initPromise = this.initWithRetry(config);
|
|
3167
3445
|
try {
|
|
3168
3446
|
await this.initPromise;
|
|
3169
|
-
console.warn("[Paywallo INIT]", "init() SUCCESS");
|
|
3447
|
+
if (config.debug) console.warn("[Paywallo INIT]", "init() SUCCESS");
|
|
3170
3448
|
} catch (error) {
|
|
3171
|
-
console.warn("[Paywallo INIT]", "init() FAILED:", error instanceof Error ? error.message : String(error));
|
|
3449
|
+
if (config.debug) console.warn("[Paywallo INIT]", "init() FAILED:", error instanceof Error ? error.message : String(error));
|
|
3172
3450
|
this.initPromise = null;
|
|
3173
3451
|
this.config = null;
|
|
3174
3452
|
this.apiClient = null;
|
|
@@ -3238,26 +3516,26 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3238
3516
|
async doInit(config) {
|
|
3239
3517
|
const environment = config.environment ?? (typeof __DEV__ !== "undefined" && __DEV__ ? "Sandbox" : "Production");
|
|
3240
3518
|
const offlineQueueEnabled = config.offlineQueueEnabled !== false;
|
|
3241
|
-
console.warn("[Paywallo INIT]", "Step 1: networkMonitor + offlineQueue...");
|
|
3519
|
+
if (config.debug) console.warn("[Paywallo INIT]", "Step 1: networkMonitor + offlineQueue...");
|
|
3242
3520
|
await Promise.all([
|
|
3243
3521
|
networkMonitor.initialize({ debug: config.debug }),
|
|
3244
3522
|
offlineQueue.initialize({ debug: config.debug }).then(
|
|
3245
3523
|
() => offlineQueue.clearItemsWithInvalidAppKey(config.appKey)
|
|
3246
3524
|
)
|
|
3247
3525
|
]);
|
|
3248
|
-
console.warn("[Paywallo INIT]", "Step 1 DONE");
|
|
3249
|
-
console.warn("[Paywallo INIT]", "Step 2: ApiClient + queueProcessor + affiliate...");
|
|
3526
|
+
if (config.debug) console.warn("[Paywallo INIT]", "Step 1 DONE");
|
|
3527
|
+
if (config.debug) console.warn("[Paywallo INIT]", "Step 2: ApiClient + queueProcessor + affiliate...");
|
|
3250
3528
|
this.apiClient = new ApiClient(config.appKey, config.debug, environment, offlineQueueEnabled, config.timeout);
|
|
3251
3529
|
queueProcessor.initialize(this.apiClient.getHttpClient(), { debug: config.debug });
|
|
3252
3530
|
affiliateManager.initialize(this.apiClient, config.debug);
|
|
3253
|
-
console.warn("[Paywallo INIT]", "Step 2 DONE");
|
|
3254
|
-
console.warn("[Paywallo INIT]", "Step 3: identityManager + sessionManager...");
|
|
3531
|
+
if (config.debug) console.warn("[Paywallo INIT]", "Step 2 DONE");
|
|
3532
|
+
if (config.debug) console.warn("[Paywallo INIT]", "Step 3: identityManager + sessionManager...");
|
|
3255
3533
|
await Promise.all([
|
|
3256
3534
|
identityManager.initialize(this.apiClient, config.debug),
|
|
3257
3535
|
sessionManager.initialize(this.apiClient, config.sessionConfig, config.debug)
|
|
3258
3536
|
]);
|
|
3259
3537
|
const distinctId = identityManager.getDistinctId();
|
|
3260
|
-
console.warn("[Paywallo INIT]", "Step 3 DONE \u2014 distinctId:", distinctId ? distinctId.substring(0, 8) + "..." : "EMPTY");
|
|
3538
|
+
if (config.debug) console.warn("[Paywallo INIT]", "Step 3 DONE \u2014 distinctId:", distinctId ? distinctId.substring(0, 8) + "..." : "EMPTY");
|
|
3261
3539
|
if (config.autoStartSession !== false) {
|
|
3262
3540
|
sessionManager.startSession().catch(() => {
|
|
3263
3541
|
if (config.debug) console.warn("[Paywallo]", "Session start failed during init \u2014 continuing");
|
|
@@ -3266,7 +3544,7 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3266
3544
|
new InstallTracker(config.debug).trackIfNeeded(this.apiClient, config.requestATT === true).catch(() => {
|
|
3267
3545
|
});
|
|
3268
3546
|
this.config = config;
|
|
3269
|
-
console.warn("[Paywallo INIT]", "COMPLETE \u2014 SDK ready, environment:", environment);
|
|
3547
|
+
if (config.debug) console.warn("[Paywallo INIT]", "COMPLETE \u2014 SDK ready, environment:", environment);
|
|
3270
3548
|
}
|
|
3271
3549
|
async identify(options) {
|
|
3272
3550
|
this.ensureInitialized();
|
|
@@ -3276,7 +3554,7 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3276
3554
|
const apiClient = this.getApiClientOrThrow();
|
|
3277
3555
|
const distinctId = identityManager.getDistinctId();
|
|
3278
3556
|
if (!distinctId) {
|
|
3279
|
-
console.warn("[Paywallo TRACK]", "SKIPPED \u2014 no distinctId", eventName);
|
|
3557
|
+
if (this.config?.debug) console.warn("[Paywallo TRACK]", "SKIPPED \u2014 no distinctId", eventName);
|
|
3280
3558
|
return;
|
|
3281
3559
|
}
|
|
3282
3560
|
if (!isValidEventName(eventName)) {
|
|
@@ -3285,7 +3563,7 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3285
3563
|
}
|
|
3286
3564
|
const sessionId = sessionManager.getSessionId();
|
|
3287
3565
|
if (eventName === "$purchase_completed") {
|
|
3288
|
-
console.warn("[Paywallo PURCHASE]", "tracking purchase:", JSON.stringify({
|
|
3566
|
+
if (this.config?.debug) console.warn("[Paywallo PURCHASE]", "tracking purchase:", JSON.stringify({
|
|
3289
3567
|
distinctId: distinctId.substring(0, 8) + "...",
|
|
3290
3568
|
productId: options?.properties?.productId,
|
|
3291
3569
|
priceUsd: options?.properties?.priceUsd,
|
|
@@ -3294,14 +3572,14 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3294
3572
|
placement: options?.properties?.placement
|
|
3295
3573
|
}));
|
|
3296
3574
|
} else if (eventName === "$paywall_viewed" || eventName === "$paywall_closed") {
|
|
3297
|
-
console.warn("[Paywallo PAYWALL]", eventName, JSON.stringify({
|
|
3575
|
+
if (this.config?.debug) console.warn("[Paywallo PAYWALL]", eventName, JSON.stringify({
|
|
3298
3576
|
distinctId: distinctId.substring(0, 8) + "...",
|
|
3299
3577
|
placement: options?.properties?.placement,
|
|
3300
3578
|
variantKey: options?.properties?.variantKey,
|
|
3301
3579
|
timeOnPaywall: options?.properties?.timeOnPaywall
|
|
3302
3580
|
}));
|
|
3303
3581
|
} else {
|
|
3304
|
-
console.warn("[Paywallo TRACK]", eventName, "distinctId:", distinctId.substring(0, 8) + "...");
|
|
3582
|
+
if (this.config?.debug) console.warn("[Paywallo TRACK]", eventName, "distinctId:", distinctId.substring(0, 8) + "...");
|
|
3305
3583
|
}
|
|
3306
3584
|
await apiClient.trackEvent(eventName, distinctId, {
|
|
3307
3585
|
...identityManager.getProperties(),
|
|
@@ -3352,15 +3630,25 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3352
3630
|
...sessionId && { sessionId }
|
|
3353
3631
|
});
|
|
3354
3632
|
}
|
|
3633
|
+
async getVariantCached(flagKey, defaultValue) {
|
|
3634
|
+
this.ensureInitialized();
|
|
3635
|
+
const apiClient = this.getApiClientOrThrow();
|
|
3636
|
+
const distinctId = identityManager.getDistinctId();
|
|
3637
|
+
const result = await apiClient.getVariantFromCache(flagKey, distinctId);
|
|
3638
|
+
if (result) return result;
|
|
3639
|
+
void apiClient.getVariant(flagKey, distinctId).catch(() => {
|
|
3640
|
+
});
|
|
3641
|
+
return { variant: defaultValue ?? null, payload: void 0 };
|
|
3642
|
+
}
|
|
3355
3643
|
async getVariant(flagKey) {
|
|
3356
3644
|
try {
|
|
3357
3645
|
const distinctId = identityManager.getDistinctId();
|
|
3358
3646
|
if (!distinctId) {
|
|
3359
|
-
console.warn("[Paywallo AB]", "getVariant SKIPPED \u2014 no distinctId", flagKey);
|
|
3647
|
+
if (this.config?.debug) console.warn("[Paywallo AB]", "getVariant SKIPPED \u2014 no distinctId", flagKey);
|
|
3360
3648
|
return { variant: null };
|
|
3361
3649
|
}
|
|
3362
3650
|
const result = await this.getApiClientOrThrow().getVariant(flagKey, distinctId);
|
|
3363
|
-
console.warn("[Paywallo AB]", `getVariant("${flagKey}") =`, result.variant);
|
|
3651
|
+
if (this.config?.debug) console.warn("[Paywallo AB]", `getVariant("${flagKey}") =`, result.variant);
|
|
3364
3652
|
return result;
|
|
3365
3653
|
} catch (error) {
|
|
3366
3654
|
if (this.config?.debug) console.warn("[Paywallo]", "getVariant failed \u2014 returning null", error);
|
|
@@ -3368,6 +3656,10 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3368
3656
|
return { variant: null };
|
|
3369
3657
|
}
|
|
3370
3658
|
}
|
|
3659
|
+
async evaluateFlags(keys) {
|
|
3660
|
+
this.ensureInitialized();
|
|
3661
|
+
return this.getApiClientOrThrow().evaluateFlags(keys, identityManager.getDistinctId());
|
|
3662
|
+
}
|
|
3371
3663
|
async getConditionalFlag(flagKey, context) {
|
|
3372
3664
|
try {
|
|
3373
3665
|
const distinctId = identityManager.getDistinctId();
|
|
@@ -3393,7 +3685,7 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3393
3685
|
}
|
|
3394
3686
|
async presentCampaign(placement, context) {
|
|
3395
3687
|
this.ensureInitialized();
|
|
3396
|
-
return campaignGateService.presentCampaign(this.getApiClientOrThrow(), placement, this.paywallPresenter, this.campaignPresenter, context);
|
|
3688
|
+
return campaignGateService.presentCampaign(this.getApiClientOrThrow(), placement, () => this.hasActiveSubscription(), this.paywallPresenter, this.campaignPresenter, context);
|
|
3397
3689
|
}
|
|
3398
3690
|
async hasActiveSubscription() {
|
|
3399
3691
|
this.ensureInitialized();
|
|
@@ -3401,11 +3693,12 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3401
3693
|
if (this.activeChecker) return await this.activeChecker();
|
|
3402
3694
|
const distinctId = identityManager.getDistinctId();
|
|
3403
3695
|
if (!distinctId || !this.apiClient) return false;
|
|
3404
|
-
|
|
3696
|
+
const status = await this.apiClient.getSubscriptionStatus(distinctId);
|
|
3697
|
+
return status.hasActiveSubscription;
|
|
3405
3698
|
} catch {
|
|
3406
|
-
if (this.config?.debug) console.warn("[Paywallo]", "hasActiveSubscription failed \u2014
|
|
3699
|
+
if (this.config?.debug) console.warn("[Paywallo]", "hasActiveSubscription failed \u2014 using persistent cache");
|
|
3407
3700
|
if (this.apiClient) void this.apiClient.reportError("SUBSCRIPTION_CHECK_FAILED", "hasActiveSubscription failed");
|
|
3408
|
-
return
|
|
3701
|
+
return resolveClientOfflineFromCache();
|
|
3409
3702
|
}
|
|
3410
3703
|
}
|
|
3411
3704
|
async getSubscription() {
|
|
@@ -3434,6 +3727,7 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3434
3727
|
const wasDebug = this.config?.debug ?? false;
|
|
3435
3728
|
await sessionManager.endSession();
|
|
3436
3729
|
await identityManager.reset();
|
|
3730
|
+
await subscriptionCache.invalidate();
|
|
3437
3731
|
queueProcessor.dispose();
|
|
3438
3732
|
offlineQueue.dispose();
|
|
3439
3733
|
networkMonitor.dispose();
|
|
@@ -3621,7 +3915,7 @@ function usePaywallo() {
|
|
|
3621
3915
|
}
|
|
3622
3916
|
|
|
3623
3917
|
// src/hooks/useProducts.ts
|
|
3624
|
-
import { useCallback as
|
|
3918
|
+
import { useCallback as useCallback6, useEffect as useEffect5, useState as useState6 } from "react";
|
|
3625
3919
|
|
|
3626
3920
|
// src/utils/productMappers.ts
|
|
3627
3921
|
function mapToIAPProduct(product) {
|
|
@@ -3658,11 +3952,11 @@ function mapToFormattedProduct(product) {
|
|
|
3658
3952
|
|
|
3659
3953
|
// src/hooks/useProducts.ts
|
|
3660
3954
|
function useProducts(initialProductIds) {
|
|
3661
|
-
const [products, setProducts] =
|
|
3662
|
-
const [formattedProducts, setFormattedProducts] =
|
|
3663
|
-
const [isLoading, setIsLoading] =
|
|
3664
|
-
const [error, setError] =
|
|
3665
|
-
const loadProducts =
|
|
3955
|
+
const [products, setProducts] = useState6([]);
|
|
3956
|
+
const [formattedProducts, setFormattedProducts] = useState6([]);
|
|
3957
|
+
const [isLoading, setIsLoading] = useState6(false);
|
|
3958
|
+
const [error, setError] = useState6(null);
|
|
3959
|
+
const loadProducts = useCallback6(async (productIds) => {
|
|
3666
3960
|
setIsLoading(true);
|
|
3667
3961
|
setError(null);
|
|
3668
3962
|
try {
|
|
@@ -3681,19 +3975,19 @@ function useProducts(initialProductIds) {
|
|
|
3681
3975
|
void loadProducts(initialProductIds);
|
|
3682
3976
|
}
|
|
3683
3977
|
}, []);
|
|
3684
|
-
const getProduct =
|
|
3978
|
+
const getProduct = useCallback6(
|
|
3685
3979
|
(productId) => {
|
|
3686
3980
|
return products.find((p) => p.productId === productId);
|
|
3687
3981
|
},
|
|
3688
3982
|
[products]
|
|
3689
3983
|
);
|
|
3690
|
-
const getFormattedProduct =
|
|
3984
|
+
const getFormattedProduct = useCallback6(
|
|
3691
3985
|
(productId) => {
|
|
3692
3986
|
return formattedProducts.find((p) => p.productId === productId);
|
|
3693
3987
|
},
|
|
3694
3988
|
[formattedProducts]
|
|
3695
3989
|
);
|
|
3696
|
-
const getPriceInfo =
|
|
3990
|
+
const getPriceInfo = useCallback6(
|
|
3697
3991
|
(productId) => {
|
|
3698
3992
|
const p = formattedProducts.find((fp) => fp.productId === productId);
|
|
3699
3993
|
if (!p) return void 0;
|
|
@@ -3722,11 +4016,11 @@ function useProducts(initialProductIds) {
|
|
|
3722
4016
|
}
|
|
3723
4017
|
|
|
3724
4018
|
// src/hooks/usePurchase.ts
|
|
3725
|
-
import { useCallback as
|
|
4019
|
+
import { useCallback as useCallback7, useState as useState7 } from "react";
|
|
3726
4020
|
function usePurchase() {
|
|
3727
|
-
const [state, setState] =
|
|
3728
|
-
const [error, setError] =
|
|
3729
|
-
const purchase =
|
|
4021
|
+
const [state, setState] = useState7("idle");
|
|
4022
|
+
const [error, setError] = useState7(null);
|
|
4023
|
+
const purchase = useCallback7(async (productId) => {
|
|
3730
4024
|
setState("purchasing");
|
|
3731
4025
|
setError(null);
|
|
3732
4026
|
try {
|
|
@@ -3754,7 +4048,7 @@ function usePurchase() {
|
|
|
3754
4048
|
throw err;
|
|
3755
4049
|
}
|
|
3756
4050
|
}, []);
|
|
3757
|
-
const restore =
|
|
4051
|
+
const restore = useCallback7(async () => {
|
|
3758
4052
|
setState("restoring");
|
|
3759
4053
|
setError(null);
|
|
3760
4054
|
try {
|
|
@@ -3786,90 +4080,10 @@ function usePurchase() {
|
|
|
3786
4080
|
}
|
|
3787
4081
|
|
|
3788
4082
|
// src/hooks/useSubscription.ts
|
|
3789
|
-
import { useCallback as
|
|
4083
|
+
import { useCallback as useCallback8, useEffect as useEffect6, useState as useState8 } from "react";
|
|
3790
4084
|
|
|
3791
4085
|
// src/domains/subscription/SubscriptionManager.ts
|
|
3792
4086
|
import { Platform as Platform8 } from "react-native";
|
|
3793
|
-
|
|
3794
|
-
// src/domains/subscription/SubscriptionCache.ts
|
|
3795
|
-
var CACHE_KEY = "subscription_cache";
|
|
3796
|
-
var DEFAULT_TTL = 5 * 60 * 1e3;
|
|
3797
|
-
var SubscriptionCache = class {
|
|
3798
|
-
constructor(ttl = DEFAULT_TTL) {
|
|
3799
|
-
this.memoryCache = null;
|
|
3800
|
-
this.ttl = ttl;
|
|
3801
|
-
this.storage = new SecureStorage();
|
|
3802
|
-
}
|
|
3803
|
-
async get() {
|
|
3804
|
-
if (this.memoryCache && !this.isExpired(this.memoryCache)) {
|
|
3805
|
-
return this.memoryCache;
|
|
3806
|
-
}
|
|
3807
|
-
try {
|
|
3808
|
-
const stored = await this.storage.get(CACHE_KEY);
|
|
3809
|
-
if (!stored) {
|
|
3810
|
-
return null;
|
|
3811
|
-
}
|
|
3812
|
-
const cached = JSON.parse(stored);
|
|
3813
|
-
if (cached.data.subscription?.expiresAt) {
|
|
3814
|
-
cached.data.subscription.expiresAt = new Date(cached.data.subscription.expiresAt);
|
|
3815
|
-
}
|
|
3816
|
-
if (cached.data.subscription?.originalPurchaseDate) {
|
|
3817
|
-
cached.data.subscription.originalPurchaseDate = new Date(
|
|
3818
|
-
cached.data.subscription.originalPurchaseDate
|
|
3819
|
-
);
|
|
3820
|
-
}
|
|
3821
|
-
if (cached.data.subscription?.latestPurchaseDate) {
|
|
3822
|
-
cached.data.subscription.latestPurchaseDate = new Date(
|
|
3823
|
-
cached.data.subscription.latestPurchaseDate
|
|
3824
|
-
);
|
|
3825
|
-
}
|
|
3826
|
-
if (cached.data.subscription?.cancellationDate) {
|
|
3827
|
-
cached.data.subscription.cancellationDate = new Date(
|
|
3828
|
-
cached.data.subscription.cancellationDate
|
|
3829
|
-
);
|
|
3830
|
-
}
|
|
3831
|
-
if (cached.data.subscription?.gracePeriodExpiresAt) {
|
|
3832
|
-
cached.data.subscription.gracePeriodExpiresAt = new Date(
|
|
3833
|
-
cached.data.subscription.gracePeriodExpiresAt
|
|
3834
|
-
);
|
|
3835
|
-
}
|
|
3836
|
-
cached.isStale = this.isExpired(cached);
|
|
3837
|
-
if (!cached.isStale) {
|
|
3838
|
-
this.memoryCache = cached;
|
|
3839
|
-
}
|
|
3840
|
-
return cached;
|
|
3841
|
-
} catch {
|
|
3842
|
-
return null;
|
|
3843
|
-
}
|
|
3844
|
-
}
|
|
3845
|
-
async set(data) {
|
|
3846
|
-
const cached = {
|
|
3847
|
-
data,
|
|
3848
|
-
cachedAt: Date.now(),
|
|
3849
|
-
isStale: false
|
|
3850
|
-
};
|
|
3851
|
-
this.memoryCache = cached;
|
|
3852
|
-
try {
|
|
3853
|
-
await this.storage.set(CACHE_KEY, JSON.stringify(cached));
|
|
3854
|
-
} catch {
|
|
3855
|
-
}
|
|
3856
|
-
}
|
|
3857
|
-
async invalidate() {
|
|
3858
|
-
this.memoryCache = null;
|
|
3859
|
-
try {
|
|
3860
|
-
await this.storage.remove(CACHE_KEY);
|
|
3861
|
-
} catch {
|
|
3862
|
-
}
|
|
3863
|
-
}
|
|
3864
|
-
isExpired(cached) {
|
|
3865
|
-
return Date.now() - cached.cachedAt > this.ttl;
|
|
3866
|
-
}
|
|
3867
|
-
setTTL(ttl) {
|
|
3868
|
-
this.ttl = ttl;
|
|
3869
|
-
}
|
|
3870
|
-
};
|
|
3871
|
-
|
|
3872
|
-
// src/domains/subscription/SubscriptionManager.ts
|
|
3873
4087
|
var SubscriptionManagerClass = class {
|
|
3874
4088
|
constructor() {
|
|
3875
4089
|
this.config = null;
|
|
@@ -4001,10 +4215,10 @@ var subscriptionManager = new SubscriptionManagerClass();
|
|
|
4001
4215
|
|
|
4002
4216
|
// src/hooks/useSubscription.ts
|
|
4003
4217
|
function useSubscription() {
|
|
4004
|
-
const [status, setStatus] =
|
|
4005
|
-
const [isLoading, setIsLoading] =
|
|
4006
|
-
const [error, setError] =
|
|
4007
|
-
const refresh =
|
|
4218
|
+
const [status, setStatus] = useState8(null);
|
|
4219
|
+
const [isLoading, setIsLoading] = useState8(true);
|
|
4220
|
+
const [error, setError] = useState8(null);
|
|
4221
|
+
const refresh = useCallback8(async () => {
|
|
4008
4222
|
setIsLoading(true);
|
|
4009
4223
|
setError(null);
|
|
4010
4224
|
try {
|
|
@@ -4017,7 +4231,7 @@ function useSubscription() {
|
|
|
4017
4231
|
setIsLoading(false);
|
|
4018
4232
|
}
|
|
4019
4233
|
}, []);
|
|
4020
|
-
const restore =
|
|
4234
|
+
const restore = useCallback8(async () => {
|
|
4021
4235
|
setIsLoading(true);
|
|
4022
4236
|
setError(null);
|
|
4023
4237
|
try {
|
|
@@ -4054,11 +4268,11 @@ function useSubscription() {
|
|
|
4054
4268
|
}
|
|
4055
4269
|
|
|
4056
4270
|
// src/PaywalloProvider.tsx
|
|
4057
|
-
import { useCallback as
|
|
4271
|
+
import { useCallback as useCallback13, useEffect as useEffect7, useMemo as useMemo2, useRef as useRef8, useState as useState12 } from "react";
|
|
4058
4272
|
import { Animated as Animated3, Dimensions as Dimensions4, Easing as Easing2, StyleSheet as StyleSheet3 } from "react-native";
|
|
4059
4273
|
|
|
4060
4274
|
// src/hooks/useCampaignPreload.ts
|
|
4061
|
-
import { useCallback as
|
|
4275
|
+
import { useCallback as useCallback9, useRef as useRef6, useState as useState9 } from "react";
|
|
4062
4276
|
|
|
4063
4277
|
// src/utils/serverProduct.ts
|
|
4064
4278
|
var PERIOD_MAP = {
|
|
@@ -4125,17 +4339,17 @@ function consumeCacheEntry(cache, placement) {
|
|
|
4125
4339
|
|
|
4126
4340
|
// src/hooks/useCampaignPreload.ts
|
|
4127
4341
|
function useCampaignPreload() {
|
|
4128
|
-
const [preloadedWebView, setPreloadedWebView] =
|
|
4342
|
+
const [preloadedWebView, setPreloadedWebView] = useState9(null);
|
|
4129
4343
|
const preloadedCampaignsRef = useRef6(/* @__PURE__ */ new Map());
|
|
4130
|
-
const isPreloadValid =
|
|
4344
|
+
const isPreloadValid = useCallback9(
|
|
4131
4345
|
(placement) => isCacheEntryValid(preloadedCampaignsRef.current.get(placement)),
|
|
4132
4346
|
[]
|
|
4133
4347
|
);
|
|
4134
|
-
const consumePreloadedCampaign =
|
|
4348
|
+
const consumePreloadedCampaign = useCallback9(
|
|
4135
4349
|
(placement) => consumeCacheEntry(preloadedCampaignsRef.current, placement),
|
|
4136
4350
|
[]
|
|
4137
4351
|
);
|
|
4138
|
-
const preloadCampaign =
|
|
4352
|
+
const preloadCampaign = useCallback9(
|
|
4139
4353
|
async (placement, context) => {
|
|
4140
4354
|
try {
|
|
4141
4355
|
const campaign = await PaywalloClient.getCampaign(placement, context);
|
|
@@ -4201,11 +4415,11 @@ function useCampaignPreload() {
|
|
|
4201
4415
|
}
|
|
4202
4416
|
|
|
4203
4417
|
// src/hooks/useProductLoader.ts
|
|
4204
|
-
import { useCallback as
|
|
4418
|
+
import { useCallback as useCallback10, useState as useState10 } from "react";
|
|
4205
4419
|
function useProductLoader() {
|
|
4206
|
-
const [products, setProducts] =
|
|
4207
|
-
const [isLoadingProducts, setIsLoadingProducts] =
|
|
4208
|
-
const refreshProducts =
|
|
4420
|
+
const [products, setProducts] = useState10(/* @__PURE__ */ new Map());
|
|
4421
|
+
const [isLoadingProducts, setIsLoadingProducts] = useState10(false);
|
|
4422
|
+
const refreshProducts = useCallback10(async (productIds) => {
|
|
4209
4423
|
setIsLoadingProducts(true);
|
|
4210
4424
|
try {
|
|
4211
4425
|
const iapService = getIAPService();
|
|
@@ -4223,10 +4437,10 @@ function useProductLoader() {
|
|
|
4223
4437
|
}
|
|
4224
4438
|
|
|
4225
4439
|
// src/hooks/usePurchaseOrchestrator.ts
|
|
4226
|
-
import { useCallback as
|
|
4440
|
+
import { useCallback as useCallback11 } from "react";
|
|
4227
4441
|
function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef) {
|
|
4228
4442
|
const { trackDismissed, trackProductSelected, trackPurchased } = usePaywallTracking();
|
|
4229
|
-
const handlePreloadedPurchase =
|
|
4443
|
+
const handlePreloadedPurchase = useCallback11(
|
|
4230
4444
|
async (productId) => {
|
|
4231
4445
|
try {
|
|
4232
4446
|
if (preloadedWebView) {
|
|
@@ -4261,7 +4475,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
|
|
|
4261
4475
|
},
|
|
4262
4476
|
[preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, trackDismissed, trackProductSelected, trackPurchased]
|
|
4263
4477
|
);
|
|
4264
|
-
const handlePreloadedRestore =
|
|
4478
|
+
const handlePreloadedRestore = useCallback11(async () => {
|
|
4265
4479
|
try {
|
|
4266
4480
|
const transactions = await getIAPService().restore();
|
|
4267
4481
|
if (transactions.length === 0) return;
|
|
@@ -4283,16 +4497,58 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
|
|
|
4283
4497
|
}
|
|
4284
4498
|
|
|
4285
4499
|
// src/hooks/useSubscriptionSync.ts
|
|
4286
|
-
import { useCallback as
|
|
4500
|
+
import { useCallback as useCallback12, useRef as useRef7, useState as useState11 } from "react";
|
|
4287
4501
|
var CACHE_TTL_MS2 = 5 * 60 * 1e3;
|
|
4288
|
-
var ACTIVE_STATUSES = ["active", "
|
|
4502
|
+
var ACTIVE_STATUSES = ["active", "grace_period"];
|
|
4503
|
+
function toDomainSubscriptionInfo(sub) {
|
|
4504
|
+
return {
|
|
4505
|
+
id: "",
|
|
4506
|
+
productId: sub.productId,
|
|
4507
|
+
// The hook's SubscriptionStatus is a subset of the domain's — cast is safe
|
|
4508
|
+
status: sub.status,
|
|
4509
|
+
expiresAt: sub.expiresAt ?? /* @__PURE__ */ new Date(0),
|
|
4510
|
+
originalPurchaseDate: /* @__PURE__ */ new Date(0),
|
|
4511
|
+
latestPurchaseDate: /* @__PURE__ */ new Date(0),
|
|
4512
|
+
willRenew: sub.autoRenewEnabled,
|
|
4513
|
+
isTrial: false,
|
|
4514
|
+
isIntroductoryPeriod: false,
|
|
4515
|
+
platform: sub.platform
|
|
4516
|
+
};
|
|
4517
|
+
}
|
|
4518
|
+
function toDomainResponse(status) {
|
|
4519
|
+
return {
|
|
4520
|
+
hasActiveSubscription: status.hasActiveSubscription,
|
|
4521
|
+
subscription: status.subscription ? toDomainSubscriptionInfo(status.subscription) : null,
|
|
4522
|
+
entitlements: []
|
|
4523
|
+
};
|
|
4524
|
+
}
|
|
4525
|
+
async function resolveOfflineFromCache() {
|
|
4526
|
+
try {
|
|
4527
|
+
const cached = await subscriptionCache.get();
|
|
4528
|
+
if (!cached) return false;
|
|
4529
|
+
const sub = cached.data.subscription;
|
|
4530
|
+
if (!sub) return false;
|
|
4531
|
+
if (!ACTIVE_STATUSES.includes(sub.status)) return false;
|
|
4532
|
+
const hasValidExpiry = sub.expiresAt.getTime() === 0 || sub.expiresAt > /* @__PURE__ */ new Date();
|
|
4533
|
+
return hasValidExpiry;
|
|
4534
|
+
} catch {
|
|
4535
|
+
return false;
|
|
4536
|
+
}
|
|
4537
|
+
}
|
|
4538
|
+
function isActiveCacheEntry(entry) {
|
|
4539
|
+
if (!entry.data) return false;
|
|
4540
|
+
if (!ACTIVE_STATUSES.includes(entry.data.status)) return false;
|
|
4541
|
+
if (entry.data.expiresAt && entry.data.expiresAt < /* @__PURE__ */ new Date()) return false;
|
|
4542
|
+
return true;
|
|
4543
|
+
}
|
|
4289
4544
|
function useSubscriptionSync() {
|
|
4290
|
-
const [subscription, setSubscription] =
|
|
4545
|
+
const [subscription, setSubscription] = useState11(null);
|
|
4291
4546
|
const cacheRef = useRef7(null);
|
|
4292
|
-
const invalidateCache =
|
|
4547
|
+
const invalidateCache = useCallback12(() => {
|
|
4293
4548
|
cacheRef.current = null;
|
|
4549
|
+
void subscriptionCache.invalidate();
|
|
4294
4550
|
}, []);
|
|
4295
|
-
const fetchAndCache =
|
|
4551
|
+
const fetchAndCache = useCallback12(async () => {
|
|
4296
4552
|
const apiClient = PaywalloClient.getApiClient();
|
|
4297
4553
|
const distinctId = PaywalloClient.getDistinctId();
|
|
4298
4554
|
if (!apiClient || !distinctId) return null;
|
|
@@ -4303,18 +4559,37 @@ function useSubscriptionSync() {
|
|
|
4303
4559
|
} : null;
|
|
4304
4560
|
cacheRef.current = { data: sub, expiresAt: Date.now() + CACHE_TTL_MS2 };
|
|
4305
4561
|
setSubscription(sub);
|
|
4562
|
+
void subscriptionCache.set(toDomainResponse(status));
|
|
4306
4563
|
return sub;
|
|
4307
4564
|
}, []);
|
|
4308
|
-
const hasActiveSubscription =
|
|
4565
|
+
const hasActiveSubscription = useCallback12(async () => {
|
|
4309
4566
|
const apiClient = PaywalloClient.getApiClient();
|
|
4310
4567
|
const distinctId = PaywalloClient.getDistinctId();
|
|
4311
4568
|
if (!apiClient || !distinctId) return false;
|
|
4312
4569
|
const cached = cacheRef.current;
|
|
4313
4570
|
if (cached && cached.expiresAt > Date.now()) {
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4571
|
+
return isActiveCacheEntry(cached);
|
|
4572
|
+
}
|
|
4573
|
+
try {
|
|
4574
|
+
const persisted = await subscriptionCache.get();
|
|
4575
|
+
if (persisted) {
|
|
4576
|
+
if (!persisted.isStale) {
|
|
4577
|
+
const sub = persisted.data.subscription ? {
|
|
4578
|
+
productId: persisted.data.subscription.productId,
|
|
4579
|
+
status: persisted.data.subscription.status,
|
|
4580
|
+
expiresAt: persisted.data.subscription.expiresAt ?? null,
|
|
4581
|
+
platform: persisted.data.subscription.platform,
|
|
4582
|
+
autoRenewEnabled: persisted.data.subscription.willRenew,
|
|
4583
|
+
inGracePeriod: persisted.data.subscription.status === "grace_period"
|
|
4584
|
+
} : null;
|
|
4585
|
+
cacheRef.current = { data: sub, expiresAt: Date.now() + CACHE_TTL_MS2 };
|
|
4586
|
+
setSubscription(sub);
|
|
4587
|
+
return persisted.data.hasActiveSubscription;
|
|
4588
|
+
}
|
|
4589
|
+
void fetchAndCache();
|
|
4590
|
+
return persisted.data.hasActiveSubscription;
|
|
4591
|
+
}
|
|
4592
|
+
} catch {
|
|
4318
4593
|
}
|
|
4319
4594
|
try {
|
|
4320
4595
|
const status = await apiClient.getSubscriptionStatus(distinctId);
|
|
@@ -4324,12 +4599,13 @@ function useSubscriptionSync() {
|
|
|
4324
4599
|
} : null;
|
|
4325
4600
|
cacheRef.current = { data: sub, expiresAt: Date.now() + CACHE_TTL_MS2 };
|
|
4326
4601
|
setSubscription(sub);
|
|
4602
|
+
void subscriptionCache.set(toDomainResponse(status));
|
|
4327
4603
|
return status.hasActiveSubscription;
|
|
4328
4604
|
} catch {
|
|
4329
|
-
return
|
|
4605
|
+
return await resolveOfflineFromCache();
|
|
4330
4606
|
}
|
|
4331
|
-
}, []);
|
|
4332
|
-
const getSubscription =
|
|
4607
|
+
}, [fetchAndCache]);
|
|
4608
|
+
const getSubscription = useCallback12(async () => {
|
|
4333
4609
|
const apiClient = PaywalloClient.getApiClient();
|
|
4334
4610
|
const distinctId = PaywalloClient.getDistinctId();
|
|
4335
4611
|
if (!apiClient || !distinctId) return null;
|
|
@@ -4338,7 +4614,22 @@ function useSubscriptionSync() {
|
|
|
4338
4614
|
try {
|
|
4339
4615
|
return await fetchAndCache();
|
|
4340
4616
|
} catch {
|
|
4341
|
-
|
|
4617
|
+
try {
|
|
4618
|
+
const persisted = await subscriptionCache.get();
|
|
4619
|
+
if (!persisted?.data.subscription) return null;
|
|
4620
|
+
const s = persisted.data.subscription;
|
|
4621
|
+
const normalisedStatus = s.status === "grace_period" ? "in_grace_period" : s.status;
|
|
4622
|
+
return {
|
|
4623
|
+
productId: s.productId,
|
|
4624
|
+
status: normalisedStatus,
|
|
4625
|
+
expiresAt: s.expiresAt ?? null,
|
|
4626
|
+
platform: s.platform,
|
|
4627
|
+
autoRenewEnabled: s.willRenew,
|
|
4628
|
+
inGracePeriod: normalisedStatus === "in_grace_period"
|
|
4629
|
+
};
|
|
4630
|
+
} catch {
|
|
4631
|
+
return null;
|
|
4632
|
+
}
|
|
4342
4633
|
}
|
|
4343
4634
|
}, [fetchAndCache]);
|
|
4344
4635
|
return { subscription, hasActiveSubscription, getSubscription, invalidateCache };
|
|
@@ -4347,14 +4638,14 @@ function useSubscriptionSync() {
|
|
|
4347
4638
|
// src/PaywalloProvider.tsx
|
|
4348
4639
|
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
4349
4640
|
function PaywalloProvider({ children, config }) {
|
|
4350
|
-
const [isInitialized, setIsInitialized] =
|
|
4351
|
-
const [initError, setInitError] =
|
|
4352
|
-
const [distinctId, setDistinctId] =
|
|
4353
|
-
const [activePaywall, setActivePaywall] =
|
|
4641
|
+
const [isInitialized, setIsInitialized] = useState12(false);
|
|
4642
|
+
const [initError, setInitError] = useState12(null);
|
|
4643
|
+
const [distinctId, setDistinctId] = useState12(null);
|
|
4644
|
+
const [activePaywall, setActivePaywall] = useState12(null);
|
|
4354
4645
|
const { products, isLoadingProducts, refreshProducts } = useProductLoader();
|
|
4355
4646
|
const { subscription, hasActiveSubscription, getSubscription, invalidateCache } = useSubscriptionSync();
|
|
4356
4647
|
const { preloadedWebView, setPreloadedWebView, preloadCampaign, consumePreloadedCampaign, isPreloadValid } = useCampaignPreload();
|
|
4357
|
-
const clearPreloadedWebView =
|
|
4648
|
+
const clearPreloadedWebView = useCallback13(() => setPreloadedWebView(null), [setPreloadedWebView]);
|
|
4358
4649
|
const { showingPreloadedWebView, presentPreloadedWebView, resolvePreloadedPaywall, handlePreloadedWebViewReady, handlePreloadedClose, setShowingPreloadedWebView, presentedAtRef } = usePaywallPresenter(preloadedWebView, clearPreloadedWebView, setPreloadedWebView);
|
|
4359
4650
|
const { handlePreloadedPurchase: rawPreloadedPurchase, handlePreloadedRestore } = usePurchaseOrchestrator(
|
|
4360
4651
|
preloadedWebView,
|
|
@@ -4363,8 +4654,8 @@ function PaywalloProvider({ children, config }) {
|
|
|
4363
4654
|
setShowingPreloadedWebView,
|
|
4364
4655
|
presentedAtRef
|
|
4365
4656
|
);
|
|
4366
|
-
const [isPreloadPurchasing, setIsPreloadPurchasing] =
|
|
4367
|
-
const handlePreloadedPurchase =
|
|
4657
|
+
const [isPreloadPurchasing, setIsPreloadPurchasing] = useState12(false);
|
|
4658
|
+
const handlePreloadedPurchase = useCallback13(async (productId) => {
|
|
4368
4659
|
setIsPreloadPurchasing(true);
|
|
4369
4660
|
try {
|
|
4370
4661
|
await rawPreloadedPurchase(productId);
|
|
@@ -4381,21 +4672,21 @@ function PaywalloProvider({ children, config }) {
|
|
|
4381
4672
|
setInitError(err instanceof Error ? err : new ClientError(CLIENT_ERROR_CODES.NOT_INITIALIZED, String(err)));
|
|
4382
4673
|
});
|
|
4383
4674
|
}, [config]);
|
|
4384
|
-
const getPaywallConfig =
|
|
4675
|
+
const getPaywallConfig = useCallback13(async (p) => {
|
|
4385
4676
|
const api = PaywalloClient.getApiClient();
|
|
4386
4677
|
return api ? api.getPaywall(p) : null;
|
|
4387
4678
|
}, []);
|
|
4388
|
-
const presentPaywall =
|
|
4679
|
+
const presentPaywall = useCallback13((placement) => {
|
|
4389
4680
|
return new Promise((resolve) => setActivePaywall({ placement, resolver: resolve }));
|
|
4390
4681
|
}, []);
|
|
4391
|
-
const trackCampaignImpression =
|
|
4682
|
+
const trackCampaignImpression = useCallback13((placement, variantKey, campaignId) => {
|
|
4392
4683
|
const api = PaywalloClient.getApiClient();
|
|
4393
4684
|
if (!api) return;
|
|
4394
4685
|
const sessionId = PaywalloClient.getSessionId();
|
|
4395
4686
|
void api.trackEvent("$campaign_impression", PaywalloClient.getDistinctId(), { placement, variantKey, campaignId, ...sessionId && { sessionId } }).catch(() => {
|
|
4396
4687
|
});
|
|
4397
4688
|
}, []);
|
|
4398
|
-
const presentCampaign =
|
|
4689
|
+
const presentCampaign = useCallback13(
|
|
4399
4690
|
async (placement, context) => {
|
|
4400
4691
|
try {
|
|
4401
4692
|
if (preloadedWebView?.placement === placement && preloadedWebView.loaded && isPreloadValid(placement)) {
|
|
@@ -4464,7 +4755,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
4464
4755
|
},
|
|
4465
4756
|
[preloadedWebView, isPreloadValid, consumePreloadedCampaign, presentPreloadedWebView, trackCampaignImpression]
|
|
4466
4757
|
);
|
|
4467
|
-
const restorePurchases =
|
|
4758
|
+
const restorePurchases = useCallback13(async () => {
|
|
4468
4759
|
invalidateCache();
|
|
4469
4760
|
try {
|
|
4470
4761
|
const txs = await getIAPService().restore();
|
|
@@ -4473,7 +4764,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
4473
4764
|
return { success: false, restoredProducts: [], error: error instanceof Error ? error : new PurchaseError(PURCHASE_ERROR_CODES.RESTORE_FAILED, String(error)) };
|
|
4474
4765
|
}
|
|
4475
4766
|
}, [invalidateCache]);
|
|
4476
|
-
const handlePaywallResult =
|
|
4767
|
+
const handlePaywallResult = useCallback13((result) => {
|
|
4477
4768
|
if (activePaywall) {
|
|
4478
4769
|
activePaywall.resolver(result);
|
|
4479
4770
|
setActivePaywall(null);
|
|
@@ -4496,7 +4787,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
4496
4787
|
useEffect7(() => {
|
|
4497
4788
|
if (activePaywall) setPreloadedWebView(null);
|
|
4498
4789
|
}, [activePaywall, setPreloadedWebView]);
|
|
4499
|
-
const noOp =
|
|
4790
|
+
const noOp = useCallback13(() => {
|
|
4500
4791
|
}, []);
|
|
4501
4792
|
const contextValue = useMemo2(() => ({
|
|
4502
4793
|
config: PaywalloClient.getConfig(),
|