@virex-tech/paywallo-sdk 1.1.0 → 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 -140
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +498 -202
- 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";
|
|
@@ -946,6 +955,13 @@ function getWebViewEmitter() {
|
|
|
946
955
|
}
|
|
947
956
|
return webViewEmitter;
|
|
948
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
|
+
}
|
|
949
965
|
function PaywallWebView({
|
|
950
966
|
paywallId,
|
|
951
967
|
craftData,
|
|
@@ -956,11 +972,14 @@ function PaywallWebView({
|
|
|
956
972
|
onPurchase,
|
|
957
973
|
onClose,
|
|
958
974
|
onRestore,
|
|
959
|
-
onReady
|
|
975
|
+
onReady,
|
|
976
|
+
onError
|
|
960
977
|
}) {
|
|
961
978
|
const paywallDataSent = useRef3(false);
|
|
962
979
|
const loadEndReceived = useRef3(false);
|
|
963
980
|
const [scriptToInject, setScriptToInject] = useState3(void 0);
|
|
981
|
+
const onErrorRef = useRef3(onError);
|
|
982
|
+
onErrorRef.current = onError;
|
|
964
983
|
const NativeWebView = useMemo(() => getNativeWebView(), []);
|
|
965
984
|
const webUrl = useMemo(() => PaywalloClient.getWebUrl() ?? "http://localhost:3000", []);
|
|
966
985
|
const buildAndSetInjectionScript = useCallback3(() => {
|
|
@@ -1020,17 +1039,51 @@ function PaywallWebView({
|
|
|
1020
1039
|
const message = parseWebViewMessage(event.data);
|
|
1021
1040
|
if (message) handleParsedMessage(message);
|
|
1022
1041
|
});
|
|
1042
|
+
const errorSub = emitter.addListener(
|
|
1043
|
+
"PaywalloWebViewError",
|
|
1044
|
+
(event) => {
|
|
1045
|
+
onErrorRef.current?.(parseErrorEvent(event));
|
|
1046
|
+
}
|
|
1047
|
+
);
|
|
1023
1048
|
return () => {
|
|
1024
1049
|
loadEndSub.remove();
|
|
1025
1050
|
messageSub.remove();
|
|
1051
|
+
errorSub.remove();
|
|
1026
1052
|
};
|
|
1027
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
|
+
}, []);
|
|
1028
1065
|
const handleLoadEnd = useCallback3(() => {
|
|
1029
1066
|
if (!loadEndReceived.current) {
|
|
1030
1067
|
loadEndReceived.current = true;
|
|
1031
1068
|
buildAndSetInjectionScript();
|
|
1032
1069
|
}
|
|
1033
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
|
+
}, []);
|
|
1034
1087
|
const handleMessage = useCallback3(
|
|
1035
1088
|
(event) => {
|
|
1036
1089
|
const message = parseWebViewMessage(event.nativeEvent.data);
|
|
@@ -1050,6 +1103,7 @@ function PaywallWebView({
|
|
|
1050
1103
|
injectedJavaScript: scriptToInject,
|
|
1051
1104
|
onMessage: handleMessage,
|
|
1052
1105
|
onLoadEnd: handleLoadEnd,
|
|
1106
|
+
onError: handleNativeError,
|
|
1053
1107
|
style: styles.webview
|
|
1054
1108
|
}
|
|
1055
1109
|
) });
|
|
@@ -1060,7 +1114,7 @@ var styles = StyleSheet.create({
|
|
|
1060
1114
|
});
|
|
1061
1115
|
|
|
1062
1116
|
// src/domains/paywall/PaywallModal.tsx
|
|
1063
|
-
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
1117
|
+
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
1064
1118
|
var SCREEN_HEIGHT2 = Dimensions2.get("window").height;
|
|
1065
1119
|
function PaywallModal({
|
|
1066
1120
|
placement,
|
|
@@ -1080,6 +1134,25 @@ function PaywallModal({
|
|
|
1080
1134
|
campaignId
|
|
1081
1135
|
);
|
|
1082
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
|
+
}, []);
|
|
1083
1156
|
const openAnim = useRef4(new Animated2.Value(SCREEN_HEIGHT2)).current;
|
|
1084
1157
|
useEffect3(() => {
|
|
1085
1158
|
if (visible) {
|
|
@@ -1104,7 +1177,12 @@ function PaywallModal({
|
|
|
1104
1177
|
statusBarTranslucent: true,
|
|
1105
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: [
|
|
1106
1179
|
error && /* @__PURE__ */ jsx2(View2, { style: styles2.errorContainer, children: /* @__PURE__ */ jsx2(Text, { style: styles2.errorText, children: error.message }) }),
|
|
1107
|
-
!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(
|
|
1108
1186
|
PaywallWebView,
|
|
1109
1187
|
{
|
|
1110
1188
|
paywallId: paywallConfig.id,
|
|
@@ -1115,39 +1193,67 @@ function PaywallModal({
|
|
|
1115
1193
|
isPurchasing,
|
|
1116
1194
|
onClose: handleClose,
|
|
1117
1195
|
onPurchase: handlePurchase,
|
|
1118
|
-
onRestore: handleRestore
|
|
1119
|
-
|
|
1120
|
-
|
|
1196
|
+
onRestore: handleRestore,
|
|
1197
|
+
onError: handleWebViewError
|
|
1198
|
+
},
|
|
1199
|
+
retryCount
|
|
1200
|
+
) })
|
|
1121
1201
|
] }) }) })
|
|
1122
1202
|
}
|
|
1123
1203
|
);
|
|
1124
1204
|
}
|
|
1125
1205
|
var styles2 = StyleSheet2.create({
|
|
1126
|
-
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: {
|
|
1127
1212
|
flex: 1,
|
|
1128
|
-
|
|
1213
|
+
justifyContent: "center",
|
|
1214
|
+
alignItems: "center",
|
|
1215
|
+
backgroundColor: "#111010",
|
|
1216
|
+
padding: 24
|
|
1129
1217
|
},
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
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
|
|
1135
1225
|
},
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1226
|
+
fallbackTitle: {
|
|
1227
|
+
fontSize: 18,
|
|
1228
|
+
fontWeight: "600",
|
|
1229
|
+
color: "#f5f5f5",
|
|
1230
|
+
marginBottom: 10,
|
|
1231
|
+
textAlign: "center"
|
|
1139
1232
|
},
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
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,
|
|
1143
1245
|
alignItems: "center",
|
|
1144
|
-
|
|
1246
|
+
marginBottom: 12
|
|
1145
1247
|
},
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
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)" }
|
|
1151
1257
|
});
|
|
1152
1258
|
|
|
1153
1259
|
// src/domains/paywall/PaywallErrorBoundary.tsx
|
|
@@ -1183,9 +1289,9 @@ function usePaywallContext() {
|
|
|
1183
1289
|
}
|
|
1184
1290
|
|
|
1185
1291
|
// src/domains/paywall/usePaywallPresenter.ts
|
|
1186
|
-
import { useCallback as
|
|
1292
|
+
import { useCallback as useCallback5, useEffect as useEffect4, useRef as useRef5, useState as useState5 } from "react";
|
|
1187
1293
|
function usePaywallPresenter(preloadedWebView, clearPreloadedWebView, setPreloadedWebView) {
|
|
1188
|
-
const [showingPreloadedWebView, setShowingPreloadedWebView] =
|
|
1294
|
+
const [showingPreloadedWebView, setShowingPreloadedWebView] = useState5(false);
|
|
1189
1295
|
const resolverRef = useRef5(null);
|
|
1190
1296
|
const presentedAtRef = useRef5(Date.now());
|
|
1191
1297
|
useEffect4(() => {
|
|
@@ -1196,23 +1302,23 @@ function usePaywallPresenter(preloadedWebView, clearPreloadedWebView, setPreload
|
|
|
1196
1302
|
}
|
|
1197
1303
|
};
|
|
1198
1304
|
}, []);
|
|
1199
|
-
const presentPreloadedWebView =
|
|
1305
|
+
const presentPreloadedWebView = useCallback5(() => {
|
|
1200
1306
|
return new Promise((resolve) => {
|
|
1201
1307
|
resolverRef.current = resolve;
|
|
1202
1308
|
presentedAtRef.current = Date.now();
|
|
1203
1309
|
setShowingPreloadedWebView(true);
|
|
1204
1310
|
});
|
|
1205
1311
|
}, []);
|
|
1206
|
-
const resolvePreloadedPaywall =
|
|
1312
|
+
const resolvePreloadedPaywall = useCallback5((result) => {
|
|
1207
1313
|
if (resolverRef.current) {
|
|
1208
1314
|
resolverRef.current(result);
|
|
1209
1315
|
resolverRef.current = null;
|
|
1210
1316
|
}
|
|
1211
1317
|
}, []);
|
|
1212
|
-
const handlePreloadedWebViewReady =
|
|
1318
|
+
const handlePreloadedWebViewReady = useCallback5(() => {
|
|
1213
1319
|
setPreloadedWebView((prev) => prev ? { ...prev, loaded: true } : null);
|
|
1214
1320
|
}, [setPreloadedWebView]);
|
|
1215
|
-
const handlePreloadedClose =
|
|
1321
|
+
const handlePreloadedClose = useCallback5(() => {
|
|
1216
1322
|
const track = async () => {
|
|
1217
1323
|
try {
|
|
1218
1324
|
const apiClient = PaywalloClient.getApiClient();
|
|
@@ -1462,6 +1568,92 @@ var PaywalloAffiliate = {
|
|
|
1462
1568
|
getWithdrawals: () => affiliateManager.getWithdrawals()
|
|
1463
1569
|
};
|
|
1464
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
|
+
|
|
1465
1657
|
// src/core/ApiClient.ts
|
|
1466
1658
|
import { Platform as Platform3 } from "react-native";
|
|
1467
1659
|
|
|
@@ -2324,7 +2516,7 @@ var ApiCache = class {
|
|
|
2324
2516
|
// src/core/ApiClient.ts
|
|
2325
2517
|
var SDK_VERSION = "1.0.0";
|
|
2326
2518
|
var DEFAULT_BASE_URL = "https://panel.lucasqueiroga.shop";
|
|
2327
|
-
var
|
|
2519
|
+
var _ApiClient = class _ApiClient {
|
|
2328
2520
|
constructor(appKey, debug = false, environment = "Production", offlineQueueEnabled = true, timeout = 3e4) {
|
|
2329
2521
|
this.cache = new ApiCache();
|
|
2330
2522
|
this.appKey = appKey;
|
|
@@ -2414,6 +2606,11 @@ var ApiClient = class {
|
|
|
2414
2606
|
const key = `${flagKey}:${distinctId}`;
|
|
2415
2607
|
const hit = this.cache.getVariant(key);
|
|
2416
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
|
+
}
|
|
2417
2614
|
try {
|
|
2418
2615
|
const res = await this.get(`/api/v1/flags/${flagKey}?distinctId=${encodeURIComponent(distinctId)}`);
|
|
2419
2616
|
if (res.status === 404) {
|
|
@@ -2428,6 +2625,7 @@ var ApiClient = class {
|
|
|
2428
2625
|
return { variant: null };
|
|
2429
2626
|
}
|
|
2430
2627
|
this.cache.setVariant(key, res.data);
|
|
2628
|
+
await this.writeFlagToStorage(flagKey, distinctId, res.data);
|
|
2431
2629
|
return res.data;
|
|
2432
2630
|
} catch (error) {
|
|
2433
2631
|
this.log("Failed to get variant:", flagKey, error);
|
|
@@ -2509,6 +2707,35 @@ var ApiClient = class {
|
|
|
2509
2707
|
this.log("Subscription status:", result.hasActiveSubscription);
|
|
2510
2708
|
return result;
|
|
2511
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
|
+
}
|
|
2512
2739
|
async getConditionalFlag(flagKey, context) {
|
|
2513
2740
|
try {
|
|
2514
2741
|
const url = buildConditionalFlagUrl(this.baseUrl, flagKey, context);
|
|
@@ -2525,7 +2752,43 @@ var ApiClient = class {
|
|
|
2525
2752
|
return { value: false, flagKey };
|
|
2526
2753
|
}
|
|
2527
2754
|
}
|
|
2528
|
-
// ───
|
|
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
|
+
}
|
|
2529
2792
|
/**
|
|
2530
2793
|
* POST with offline queue fallback. Enqueues when offline or on network failure.
|
|
2531
2794
|
* Throws only when offlineQueueEnabled is false and the request fails.
|
|
@@ -2552,6 +2815,9 @@ var ApiClient = class {
|
|
|
2552
2815
|
if (this.debug) console.warn("[Paywallo API]", ...args);
|
|
2553
2816
|
}
|
|
2554
2817
|
};
|
|
2818
|
+
// ─── Private helpers ─────────────────────────────────────────────────────────
|
|
2819
|
+
_ApiClient.FLAG_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
2820
|
+
var ApiClient = _ApiClient;
|
|
2555
2821
|
|
|
2556
2822
|
// src/domains/campaign/CampaignError.ts
|
|
2557
2823
|
var CampaignError = class extends PaywalloError {
|
|
@@ -2579,18 +2845,21 @@ var CampaignGateService = class {
|
|
|
2579
2845
|
if (!distinctId) return null;
|
|
2580
2846
|
return apiClient.getCampaign(placement, distinctId, context);
|
|
2581
2847
|
}
|
|
2582
|
-
async presentCampaign(apiClient, placement, paywallPresenter, campaignPresenter, context) {
|
|
2848
|
+
async presentCampaign(apiClient, placement, hasActive, paywallPresenter, campaignPresenter, context) {
|
|
2583
2849
|
const campaign = await this.getCampaign(apiClient, placement, context);
|
|
2584
2850
|
if (!campaign) {
|
|
2585
2851
|
return { presented: false, purchased: false, cancelled: false, restored: false, error: new CampaignError(CAMPAIGN_ERROR_CODES.NOT_FOUND, "No campaign found") };
|
|
2586
2852
|
}
|
|
2853
|
+
if (!context?.forceShow && await hasActive()) {
|
|
2854
|
+
return { presented: false, purchased: false, cancelled: false, restored: false, skippedReason: "subscriber" };
|
|
2855
|
+
}
|
|
2587
2856
|
if (campaignPresenter) return campaignPresenter(campaign);
|
|
2588
2857
|
if (!paywallPresenter) throw new PaywallError(PAYWALL_ERROR_CODES.NOT_INITIALIZED, "PaywalloProvider not mounted. Wrap your app with <PaywalloProvider>");
|
|
2589
2858
|
return { ...await paywallPresenter(campaign.paywall.placement), campaignId: campaign.campaignId, variantKey: campaign.variantKey };
|
|
2590
2859
|
}
|
|
2591
2860
|
async requireSubscriptionWithCampaign(apiClient, placement, hasActive, paywallPresenter, campaignPresenter, context) {
|
|
2592
2861
|
if (await hasActive()) return true;
|
|
2593
|
-
const r = await this.presentCampaign(apiClient, placement, paywallPresenter, campaignPresenter, context);
|
|
2862
|
+
const r = await this.presentCampaign(apiClient, placement, hasActive, paywallPresenter, campaignPresenter, context);
|
|
2594
2863
|
return r.purchased || r.restored;
|
|
2595
2864
|
}
|
|
2596
2865
|
async preloadCampaign(apiClient, placement, context) {
|
|
@@ -3127,6 +3396,20 @@ function isValidEventName(name) {
|
|
|
3127
3396
|
if (name.startsWith("$")) return /^\$[a-z][a-z0-9]*(_[a-z0-9]+)*$/.test(name);
|
|
3128
3397
|
return /^[a-z][a-z0-9]*(_[a-z0-9]+)*$/.test(name);
|
|
3129
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
|
+
}
|
|
3130
3413
|
var _PaywalloClientClass = class _PaywalloClientClass {
|
|
3131
3414
|
constructor() {
|
|
3132
3415
|
this.config = null;
|
|
@@ -3143,27 +3426,27 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3143
3426
|
this.initAttempts = 0;
|
|
3144
3427
|
}
|
|
3145
3428
|
async init(config) {
|
|
3146
|
-
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");
|
|
3147
3430
|
if (this.config && this.apiClient) {
|
|
3148
|
-
console.warn("[Paywallo INIT]", "Already initialized \u2014 skipping");
|
|
3431
|
+
if (config.debug) console.warn("[Paywallo INIT]", "Already initialized \u2014 skipping");
|
|
3149
3432
|
return;
|
|
3150
3433
|
}
|
|
3151
3434
|
if (this.initPromise) {
|
|
3152
|
-
console.warn("[Paywallo INIT]", "Init already in progress \u2014 awaiting...");
|
|
3435
|
+
if (config.debug) console.warn("[Paywallo INIT]", "Init already in progress \u2014 awaiting...");
|
|
3153
3436
|
await this.initPromise;
|
|
3154
|
-
console.warn("[Paywallo INIT]", "Awaited existing init \u2014 done");
|
|
3437
|
+
if (config.debug) console.warn("[Paywallo INIT]", "Awaited existing init \u2014 done");
|
|
3155
3438
|
return;
|
|
3156
3439
|
}
|
|
3157
3440
|
if (!config.appKey) throw new ClientError(CLIENT_ERROR_CODES.MISSING_APP_KEY, "appKey is required");
|
|
3158
|
-
console.warn("[Paywallo INIT]", "Starting fresh init...");
|
|
3441
|
+
if (config.debug) console.warn("[Paywallo INIT]", "Starting fresh init...");
|
|
3159
3442
|
this.pendingConfig = config;
|
|
3160
3443
|
this.initAttempts = 0;
|
|
3161
3444
|
this.initPromise = this.initWithRetry(config);
|
|
3162
3445
|
try {
|
|
3163
3446
|
await this.initPromise;
|
|
3164
|
-
console.warn("[Paywallo INIT]", "init() SUCCESS");
|
|
3447
|
+
if (config.debug) console.warn("[Paywallo INIT]", "init() SUCCESS");
|
|
3165
3448
|
} catch (error) {
|
|
3166
|
-
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));
|
|
3167
3450
|
this.initPromise = null;
|
|
3168
3451
|
this.config = null;
|
|
3169
3452
|
this.apiClient = null;
|
|
@@ -3233,26 +3516,26 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3233
3516
|
async doInit(config) {
|
|
3234
3517
|
const environment = config.environment ?? (typeof __DEV__ !== "undefined" && __DEV__ ? "Sandbox" : "Production");
|
|
3235
3518
|
const offlineQueueEnabled = config.offlineQueueEnabled !== false;
|
|
3236
|
-
console.warn("[Paywallo INIT]", "Step 1: networkMonitor + offlineQueue...");
|
|
3519
|
+
if (config.debug) console.warn("[Paywallo INIT]", "Step 1: networkMonitor + offlineQueue...");
|
|
3237
3520
|
await Promise.all([
|
|
3238
3521
|
networkMonitor.initialize({ debug: config.debug }),
|
|
3239
3522
|
offlineQueue.initialize({ debug: config.debug }).then(
|
|
3240
3523
|
() => offlineQueue.clearItemsWithInvalidAppKey(config.appKey)
|
|
3241
3524
|
)
|
|
3242
3525
|
]);
|
|
3243
|
-
console.warn("[Paywallo INIT]", "Step 1 DONE");
|
|
3244
|
-
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...");
|
|
3245
3528
|
this.apiClient = new ApiClient(config.appKey, config.debug, environment, offlineQueueEnabled, config.timeout);
|
|
3246
3529
|
queueProcessor.initialize(this.apiClient.getHttpClient(), { debug: config.debug });
|
|
3247
3530
|
affiliateManager.initialize(this.apiClient, config.debug);
|
|
3248
|
-
console.warn("[Paywallo INIT]", "Step 2 DONE");
|
|
3249
|
-
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...");
|
|
3250
3533
|
await Promise.all([
|
|
3251
3534
|
identityManager.initialize(this.apiClient, config.debug),
|
|
3252
3535
|
sessionManager.initialize(this.apiClient, config.sessionConfig, config.debug)
|
|
3253
3536
|
]);
|
|
3254
3537
|
const distinctId = identityManager.getDistinctId();
|
|
3255
|
-
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");
|
|
3256
3539
|
if (config.autoStartSession !== false) {
|
|
3257
3540
|
sessionManager.startSession().catch(() => {
|
|
3258
3541
|
if (config.debug) console.warn("[Paywallo]", "Session start failed during init \u2014 continuing");
|
|
@@ -3261,7 +3544,7 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3261
3544
|
new InstallTracker(config.debug).trackIfNeeded(this.apiClient, config.requestATT === true).catch(() => {
|
|
3262
3545
|
});
|
|
3263
3546
|
this.config = config;
|
|
3264
|
-
console.warn("[Paywallo INIT]", "COMPLETE \u2014 SDK ready, environment:", environment);
|
|
3547
|
+
if (config.debug) console.warn("[Paywallo INIT]", "COMPLETE \u2014 SDK ready, environment:", environment);
|
|
3265
3548
|
}
|
|
3266
3549
|
async identify(options) {
|
|
3267
3550
|
this.ensureInitialized();
|
|
@@ -3271,7 +3554,7 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3271
3554
|
const apiClient = this.getApiClientOrThrow();
|
|
3272
3555
|
const distinctId = identityManager.getDistinctId();
|
|
3273
3556
|
if (!distinctId) {
|
|
3274
|
-
console.warn("[Paywallo TRACK]", "SKIPPED \u2014 no distinctId", eventName);
|
|
3557
|
+
if (this.config?.debug) console.warn("[Paywallo TRACK]", "SKIPPED \u2014 no distinctId", eventName);
|
|
3275
3558
|
return;
|
|
3276
3559
|
}
|
|
3277
3560
|
if (!isValidEventName(eventName)) {
|
|
@@ -3280,7 +3563,7 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3280
3563
|
}
|
|
3281
3564
|
const sessionId = sessionManager.getSessionId();
|
|
3282
3565
|
if (eventName === "$purchase_completed") {
|
|
3283
|
-
console.warn("[Paywallo PURCHASE]", "tracking purchase:", JSON.stringify({
|
|
3566
|
+
if (this.config?.debug) console.warn("[Paywallo PURCHASE]", "tracking purchase:", JSON.stringify({
|
|
3284
3567
|
distinctId: distinctId.substring(0, 8) + "...",
|
|
3285
3568
|
productId: options?.properties?.productId,
|
|
3286
3569
|
priceUsd: options?.properties?.priceUsd,
|
|
@@ -3289,14 +3572,14 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3289
3572
|
placement: options?.properties?.placement
|
|
3290
3573
|
}));
|
|
3291
3574
|
} else if (eventName === "$paywall_viewed" || eventName === "$paywall_closed") {
|
|
3292
|
-
console.warn("[Paywallo PAYWALL]", eventName, JSON.stringify({
|
|
3575
|
+
if (this.config?.debug) console.warn("[Paywallo PAYWALL]", eventName, JSON.stringify({
|
|
3293
3576
|
distinctId: distinctId.substring(0, 8) + "...",
|
|
3294
3577
|
placement: options?.properties?.placement,
|
|
3295
3578
|
variantKey: options?.properties?.variantKey,
|
|
3296
3579
|
timeOnPaywall: options?.properties?.timeOnPaywall
|
|
3297
3580
|
}));
|
|
3298
3581
|
} else {
|
|
3299
|
-
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) + "...");
|
|
3300
3583
|
}
|
|
3301
3584
|
await apiClient.trackEvent(eventName, distinctId, {
|
|
3302
3585
|
...identityManager.getProperties(),
|
|
@@ -3347,15 +3630,25 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3347
3630
|
...sessionId && { sessionId }
|
|
3348
3631
|
});
|
|
3349
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
|
+
}
|
|
3350
3643
|
async getVariant(flagKey) {
|
|
3351
3644
|
try {
|
|
3352
3645
|
const distinctId = identityManager.getDistinctId();
|
|
3353
3646
|
if (!distinctId) {
|
|
3354
|
-
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);
|
|
3355
3648
|
return { variant: null };
|
|
3356
3649
|
}
|
|
3357
3650
|
const result = await this.getApiClientOrThrow().getVariant(flagKey, distinctId);
|
|
3358
|
-
console.warn("[Paywallo AB]", `getVariant("${flagKey}") =`, result.variant);
|
|
3651
|
+
if (this.config?.debug) console.warn("[Paywallo AB]", `getVariant("${flagKey}") =`, result.variant);
|
|
3359
3652
|
return result;
|
|
3360
3653
|
} catch (error) {
|
|
3361
3654
|
if (this.config?.debug) console.warn("[Paywallo]", "getVariant failed \u2014 returning null", error);
|
|
@@ -3363,6 +3656,10 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3363
3656
|
return { variant: null };
|
|
3364
3657
|
}
|
|
3365
3658
|
}
|
|
3659
|
+
async evaluateFlags(keys) {
|
|
3660
|
+
this.ensureInitialized();
|
|
3661
|
+
return this.getApiClientOrThrow().evaluateFlags(keys, identityManager.getDistinctId());
|
|
3662
|
+
}
|
|
3366
3663
|
async getConditionalFlag(flagKey, context) {
|
|
3367
3664
|
try {
|
|
3368
3665
|
const distinctId = identityManager.getDistinctId();
|
|
@@ -3388,7 +3685,7 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3388
3685
|
}
|
|
3389
3686
|
async presentCampaign(placement, context) {
|
|
3390
3687
|
this.ensureInitialized();
|
|
3391
|
-
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);
|
|
3392
3689
|
}
|
|
3393
3690
|
async hasActiveSubscription() {
|
|
3394
3691
|
this.ensureInitialized();
|
|
@@ -3396,11 +3693,12 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3396
3693
|
if (this.activeChecker) return await this.activeChecker();
|
|
3397
3694
|
const distinctId = identityManager.getDistinctId();
|
|
3398
3695
|
if (!distinctId || !this.apiClient) return false;
|
|
3399
|
-
|
|
3696
|
+
const status = await this.apiClient.getSubscriptionStatus(distinctId);
|
|
3697
|
+
return status.hasActiveSubscription;
|
|
3400
3698
|
} catch {
|
|
3401
|
-
if (this.config?.debug) console.warn("[Paywallo]", "hasActiveSubscription failed \u2014
|
|
3699
|
+
if (this.config?.debug) console.warn("[Paywallo]", "hasActiveSubscription failed \u2014 using persistent cache");
|
|
3402
3700
|
if (this.apiClient) void this.apiClient.reportError("SUBSCRIPTION_CHECK_FAILED", "hasActiveSubscription failed");
|
|
3403
|
-
return
|
|
3701
|
+
return resolveClientOfflineFromCache();
|
|
3404
3702
|
}
|
|
3405
3703
|
}
|
|
3406
3704
|
async getSubscription() {
|
|
@@ -3429,6 +3727,7 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3429
3727
|
const wasDebug = this.config?.debug ?? false;
|
|
3430
3728
|
await sessionManager.endSession();
|
|
3431
3729
|
await identityManager.reset();
|
|
3730
|
+
await subscriptionCache.invalidate();
|
|
3432
3731
|
queueProcessor.dispose();
|
|
3433
3732
|
offlineQueue.dispose();
|
|
3434
3733
|
networkMonitor.dispose();
|
|
@@ -3616,7 +3915,7 @@ function usePaywallo() {
|
|
|
3616
3915
|
}
|
|
3617
3916
|
|
|
3618
3917
|
// src/hooks/useProducts.ts
|
|
3619
|
-
import { useCallback as
|
|
3918
|
+
import { useCallback as useCallback6, useEffect as useEffect5, useState as useState6 } from "react";
|
|
3620
3919
|
|
|
3621
3920
|
// src/utils/productMappers.ts
|
|
3622
3921
|
function mapToIAPProduct(product) {
|
|
@@ -3653,11 +3952,11 @@ function mapToFormattedProduct(product) {
|
|
|
3653
3952
|
|
|
3654
3953
|
// src/hooks/useProducts.ts
|
|
3655
3954
|
function useProducts(initialProductIds) {
|
|
3656
|
-
const [products, setProducts] =
|
|
3657
|
-
const [formattedProducts, setFormattedProducts] =
|
|
3658
|
-
const [isLoading, setIsLoading] =
|
|
3659
|
-
const [error, setError] =
|
|
3660
|
-
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) => {
|
|
3661
3960
|
setIsLoading(true);
|
|
3662
3961
|
setError(null);
|
|
3663
3962
|
try {
|
|
@@ -3676,19 +3975,19 @@ function useProducts(initialProductIds) {
|
|
|
3676
3975
|
void loadProducts(initialProductIds);
|
|
3677
3976
|
}
|
|
3678
3977
|
}, []);
|
|
3679
|
-
const getProduct =
|
|
3978
|
+
const getProduct = useCallback6(
|
|
3680
3979
|
(productId) => {
|
|
3681
3980
|
return products.find((p) => p.productId === productId);
|
|
3682
3981
|
},
|
|
3683
3982
|
[products]
|
|
3684
3983
|
);
|
|
3685
|
-
const getFormattedProduct =
|
|
3984
|
+
const getFormattedProduct = useCallback6(
|
|
3686
3985
|
(productId) => {
|
|
3687
3986
|
return formattedProducts.find((p) => p.productId === productId);
|
|
3688
3987
|
},
|
|
3689
3988
|
[formattedProducts]
|
|
3690
3989
|
);
|
|
3691
|
-
const getPriceInfo =
|
|
3990
|
+
const getPriceInfo = useCallback6(
|
|
3692
3991
|
(productId) => {
|
|
3693
3992
|
const p = formattedProducts.find((fp) => fp.productId === productId);
|
|
3694
3993
|
if (!p) return void 0;
|
|
@@ -3717,11 +4016,11 @@ function useProducts(initialProductIds) {
|
|
|
3717
4016
|
}
|
|
3718
4017
|
|
|
3719
4018
|
// src/hooks/usePurchase.ts
|
|
3720
|
-
import { useCallback as
|
|
4019
|
+
import { useCallback as useCallback7, useState as useState7 } from "react";
|
|
3721
4020
|
function usePurchase() {
|
|
3722
|
-
const [state, setState] =
|
|
3723
|
-
const [error, setError] =
|
|
3724
|
-
const purchase =
|
|
4021
|
+
const [state, setState] = useState7("idle");
|
|
4022
|
+
const [error, setError] = useState7(null);
|
|
4023
|
+
const purchase = useCallback7(async (productId) => {
|
|
3725
4024
|
setState("purchasing");
|
|
3726
4025
|
setError(null);
|
|
3727
4026
|
try {
|
|
@@ -3749,7 +4048,7 @@ function usePurchase() {
|
|
|
3749
4048
|
throw err;
|
|
3750
4049
|
}
|
|
3751
4050
|
}, []);
|
|
3752
|
-
const restore =
|
|
4051
|
+
const restore = useCallback7(async () => {
|
|
3753
4052
|
setState("restoring");
|
|
3754
4053
|
setError(null);
|
|
3755
4054
|
try {
|
|
@@ -3781,90 +4080,10 @@ function usePurchase() {
|
|
|
3781
4080
|
}
|
|
3782
4081
|
|
|
3783
4082
|
// src/hooks/useSubscription.ts
|
|
3784
|
-
import { useCallback as
|
|
4083
|
+
import { useCallback as useCallback8, useEffect as useEffect6, useState as useState8 } from "react";
|
|
3785
4084
|
|
|
3786
4085
|
// src/domains/subscription/SubscriptionManager.ts
|
|
3787
4086
|
import { Platform as Platform8 } from "react-native";
|
|
3788
|
-
|
|
3789
|
-
// src/domains/subscription/SubscriptionCache.ts
|
|
3790
|
-
var CACHE_KEY = "subscription_cache";
|
|
3791
|
-
var DEFAULT_TTL = 5 * 60 * 1e3;
|
|
3792
|
-
var SubscriptionCache = class {
|
|
3793
|
-
constructor(ttl = DEFAULT_TTL) {
|
|
3794
|
-
this.memoryCache = null;
|
|
3795
|
-
this.ttl = ttl;
|
|
3796
|
-
this.storage = new SecureStorage();
|
|
3797
|
-
}
|
|
3798
|
-
async get() {
|
|
3799
|
-
if (this.memoryCache && !this.isExpired(this.memoryCache)) {
|
|
3800
|
-
return this.memoryCache;
|
|
3801
|
-
}
|
|
3802
|
-
try {
|
|
3803
|
-
const stored = await this.storage.get(CACHE_KEY);
|
|
3804
|
-
if (!stored) {
|
|
3805
|
-
return null;
|
|
3806
|
-
}
|
|
3807
|
-
const cached = JSON.parse(stored);
|
|
3808
|
-
if (cached.data.subscription?.expiresAt) {
|
|
3809
|
-
cached.data.subscription.expiresAt = new Date(cached.data.subscription.expiresAt);
|
|
3810
|
-
}
|
|
3811
|
-
if (cached.data.subscription?.originalPurchaseDate) {
|
|
3812
|
-
cached.data.subscription.originalPurchaseDate = new Date(
|
|
3813
|
-
cached.data.subscription.originalPurchaseDate
|
|
3814
|
-
);
|
|
3815
|
-
}
|
|
3816
|
-
if (cached.data.subscription?.latestPurchaseDate) {
|
|
3817
|
-
cached.data.subscription.latestPurchaseDate = new Date(
|
|
3818
|
-
cached.data.subscription.latestPurchaseDate
|
|
3819
|
-
);
|
|
3820
|
-
}
|
|
3821
|
-
if (cached.data.subscription?.cancellationDate) {
|
|
3822
|
-
cached.data.subscription.cancellationDate = new Date(
|
|
3823
|
-
cached.data.subscription.cancellationDate
|
|
3824
|
-
);
|
|
3825
|
-
}
|
|
3826
|
-
if (cached.data.subscription?.gracePeriodExpiresAt) {
|
|
3827
|
-
cached.data.subscription.gracePeriodExpiresAt = new Date(
|
|
3828
|
-
cached.data.subscription.gracePeriodExpiresAt
|
|
3829
|
-
);
|
|
3830
|
-
}
|
|
3831
|
-
cached.isStale = this.isExpired(cached);
|
|
3832
|
-
if (!cached.isStale) {
|
|
3833
|
-
this.memoryCache = cached;
|
|
3834
|
-
}
|
|
3835
|
-
return cached;
|
|
3836
|
-
} catch {
|
|
3837
|
-
return null;
|
|
3838
|
-
}
|
|
3839
|
-
}
|
|
3840
|
-
async set(data) {
|
|
3841
|
-
const cached = {
|
|
3842
|
-
data,
|
|
3843
|
-
cachedAt: Date.now(),
|
|
3844
|
-
isStale: false
|
|
3845
|
-
};
|
|
3846
|
-
this.memoryCache = cached;
|
|
3847
|
-
try {
|
|
3848
|
-
await this.storage.set(CACHE_KEY, JSON.stringify(cached));
|
|
3849
|
-
} catch {
|
|
3850
|
-
}
|
|
3851
|
-
}
|
|
3852
|
-
async invalidate() {
|
|
3853
|
-
this.memoryCache = null;
|
|
3854
|
-
try {
|
|
3855
|
-
await this.storage.remove(CACHE_KEY);
|
|
3856
|
-
} catch {
|
|
3857
|
-
}
|
|
3858
|
-
}
|
|
3859
|
-
isExpired(cached) {
|
|
3860
|
-
return Date.now() - cached.cachedAt > this.ttl;
|
|
3861
|
-
}
|
|
3862
|
-
setTTL(ttl) {
|
|
3863
|
-
this.ttl = ttl;
|
|
3864
|
-
}
|
|
3865
|
-
};
|
|
3866
|
-
|
|
3867
|
-
// src/domains/subscription/SubscriptionManager.ts
|
|
3868
4087
|
var SubscriptionManagerClass = class {
|
|
3869
4088
|
constructor() {
|
|
3870
4089
|
this.config = null;
|
|
@@ -3996,10 +4215,10 @@ var subscriptionManager = new SubscriptionManagerClass();
|
|
|
3996
4215
|
|
|
3997
4216
|
// src/hooks/useSubscription.ts
|
|
3998
4217
|
function useSubscription() {
|
|
3999
|
-
const [status, setStatus] =
|
|
4000
|
-
const [isLoading, setIsLoading] =
|
|
4001
|
-
const [error, setError] =
|
|
4002
|
-
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 () => {
|
|
4003
4222
|
setIsLoading(true);
|
|
4004
4223
|
setError(null);
|
|
4005
4224
|
try {
|
|
@@ -4012,7 +4231,7 @@ function useSubscription() {
|
|
|
4012
4231
|
setIsLoading(false);
|
|
4013
4232
|
}
|
|
4014
4233
|
}, []);
|
|
4015
|
-
const restore =
|
|
4234
|
+
const restore = useCallback8(async () => {
|
|
4016
4235
|
setIsLoading(true);
|
|
4017
4236
|
setError(null);
|
|
4018
4237
|
try {
|
|
@@ -4049,11 +4268,11 @@ function useSubscription() {
|
|
|
4049
4268
|
}
|
|
4050
4269
|
|
|
4051
4270
|
// src/PaywalloProvider.tsx
|
|
4052
|
-
import { useCallback as
|
|
4271
|
+
import { useCallback as useCallback13, useEffect as useEffect7, useMemo as useMemo2, useRef as useRef8, useState as useState12 } from "react";
|
|
4053
4272
|
import { Animated as Animated3, Dimensions as Dimensions4, Easing as Easing2, StyleSheet as StyleSheet3 } from "react-native";
|
|
4054
4273
|
|
|
4055
4274
|
// src/hooks/useCampaignPreload.ts
|
|
4056
|
-
import { useCallback as
|
|
4275
|
+
import { useCallback as useCallback9, useRef as useRef6, useState as useState9 } from "react";
|
|
4057
4276
|
|
|
4058
4277
|
// src/utils/serverProduct.ts
|
|
4059
4278
|
var PERIOD_MAP = {
|
|
@@ -4120,17 +4339,17 @@ function consumeCacheEntry(cache, placement) {
|
|
|
4120
4339
|
|
|
4121
4340
|
// src/hooks/useCampaignPreload.ts
|
|
4122
4341
|
function useCampaignPreload() {
|
|
4123
|
-
const [preloadedWebView, setPreloadedWebView] =
|
|
4342
|
+
const [preloadedWebView, setPreloadedWebView] = useState9(null);
|
|
4124
4343
|
const preloadedCampaignsRef = useRef6(/* @__PURE__ */ new Map());
|
|
4125
|
-
const isPreloadValid =
|
|
4344
|
+
const isPreloadValid = useCallback9(
|
|
4126
4345
|
(placement) => isCacheEntryValid(preloadedCampaignsRef.current.get(placement)),
|
|
4127
4346
|
[]
|
|
4128
4347
|
);
|
|
4129
|
-
const consumePreloadedCampaign =
|
|
4348
|
+
const consumePreloadedCampaign = useCallback9(
|
|
4130
4349
|
(placement) => consumeCacheEntry(preloadedCampaignsRef.current, placement),
|
|
4131
4350
|
[]
|
|
4132
4351
|
);
|
|
4133
|
-
const preloadCampaign =
|
|
4352
|
+
const preloadCampaign = useCallback9(
|
|
4134
4353
|
async (placement, context) => {
|
|
4135
4354
|
try {
|
|
4136
4355
|
const campaign = await PaywalloClient.getCampaign(placement, context);
|
|
@@ -4196,11 +4415,11 @@ function useCampaignPreload() {
|
|
|
4196
4415
|
}
|
|
4197
4416
|
|
|
4198
4417
|
// src/hooks/useProductLoader.ts
|
|
4199
|
-
import { useCallback as
|
|
4418
|
+
import { useCallback as useCallback10, useState as useState10 } from "react";
|
|
4200
4419
|
function useProductLoader() {
|
|
4201
|
-
const [products, setProducts] =
|
|
4202
|
-
const [isLoadingProducts, setIsLoadingProducts] =
|
|
4203
|
-
const refreshProducts =
|
|
4420
|
+
const [products, setProducts] = useState10(/* @__PURE__ */ new Map());
|
|
4421
|
+
const [isLoadingProducts, setIsLoadingProducts] = useState10(false);
|
|
4422
|
+
const refreshProducts = useCallback10(async (productIds) => {
|
|
4204
4423
|
setIsLoadingProducts(true);
|
|
4205
4424
|
try {
|
|
4206
4425
|
const iapService = getIAPService();
|
|
@@ -4218,10 +4437,10 @@ function useProductLoader() {
|
|
|
4218
4437
|
}
|
|
4219
4438
|
|
|
4220
4439
|
// src/hooks/usePurchaseOrchestrator.ts
|
|
4221
|
-
import { useCallback as
|
|
4440
|
+
import { useCallback as useCallback11 } from "react";
|
|
4222
4441
|
function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef) {
|
|
4223
4442
|
const { trackDismissed, trackProductSelected, trackPurchased } = usePaywallTracking();
|
|
4224
|
-
const handlePreloadedPurchase =
|
|
4443
|
+
const handlePreloadedPurchase = useCallback11(
|
|
4225
4444
|
async (productId) => {
|
|
4226
4445
|
try {
|
|
4227
4446
|
if (preloadedWebView) {
|
|
@@ -4256,7 +4475,7 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
|
|
|
4256
4475
|
},
|
|
4257
4476
|
[preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, trackDismissed, trackProductSelected, trackPurchased]
|
|
4258
4477
|
);
|
|
4259
|
-
const handlePreloadedRestore =
|
|
4478
|
+
const handlePreloadedRestore = useCallback11(async () => {
|
|
4260
4479
|
try {
|
|
4261
4480
|
const transactions = await getIAPService().restore();
|
|
4262
4481
|
if (transactions.length === 0) return;
|
|
@@ -4278,16 +4497,58 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
|
|
|
4278
4497
|
}
|
|
4279
4498
|
|
|
4280
4499
|
// src/hooks/useSubscriptionSync.ts
|
|
4281
|
-
import { useCallback as
|
|
4500
|
+
import { useCallback as useCallback12, useRef as useRef7, useState as useState11 } from "react";
|
|
4282
4501
|
var CACHE_TTL_MS2 = 5 * 60 * 1e3;
|
|
4283
|
-
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
|
+
}
|
|
4284
4544
|
function useSubscriptionSync() {
|
|
4285
|
-
const [subscription, setSubscription] =
|
|
4545
|
+
const [subscription, setSubscription] = useState11(null);
|
|
4286
4546
|
const cacheRef = useRef7(null);
|
|
4287
|
-
const invalidateCache =
|
|
4547
|
+
const invalidateCache = useCallback12(() => {
|
|
4288
4548
|
cacheRef.current = null;
|
|
4549
|
+
void subscriptionCache.invalidate();
|
|
4289
4550
|
}, []);
|
|
4290
|
-
const fetchAndCache =
|
|
4551
|
+
const fetchAndCache = useCallback12(async () => {
|
|
4291
4552
|
const apiClient = PaywalloClient.getApiClient();
|
|
4292
4553
|
const distinctId = PaywalloClient.getDistinctId();
|
|
4293
4554
|
if (!apiClient || !distinctId) return null;
|
|
@@ -4298,18 +4559,37 @@ function useSubscriptionSync() {
|
|
|
4298
4559
|
} : null;
|
|
4299
4560
|
cacheRef.current = { data: sub, expiresAt: Date.now() + CACHE_TTL_MS2 };
|
|
4300
4561
|
setSubscription(sub);
|
|
4562
|
+
void subscriptionCache.set(toDomainResponse(status));
|
|
4301
4563
|
return sub;
|
|
4302
4564
|
}, []);
|
|
4303
|
-
const hasActiveSubscription =
|
|
4565
|
+
const hasActiveSubscription = useCallback12(async () => {
|
|
4304
4566
|
const apiClient = PaywalloClient.getApiClient();
|
|
4305
4567
|
const distinctId = PaywalloClient.getDistinctId();
|
|
4306
4568
|
if (!apiClient || !distinctId) return false;
|
|
4307
4569
|
const cached = cacheRef.current;
|
|
4308
4570
|
if (cached && cached.expiresAt > Date.now()) {
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
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 {
|
|
4313
4593
|
}
|
|
4314
4594
|
try {
|
|
4315
4595
|
const status = await apiClient.getSubscriptionStatus(distinctId);
|
|
@@ -4319,12 +4599,13 @@ function useSubscriptionSync() {
|
|
|
4319
4599
|
} : null;
|
|
4320
4600
|
cacheRef.current = { data: sub, expiresAt: Date.now() + CACHE_TTL_MS2 };
|
|
4321
4601
|
setSubscription(sub);
|
|
4602
|
+
void subscriptionCache.set(toDomainResponse(status));
|
|
4322
4603
|
return status.hasActiveSubscription;
|
|
4323
4604
|
} catch {
|
|
4324
|
-
return
|
|
4605
|
+
return await resolveOfflineFromCache();
|
|
4325
4606
|
}
|
|
4326
|
-
}, []);
|
|
4327
|
-
const getSubscription =
|
|
4607
|
+
}, [fetchAndCache]);
|
|
4608
|
+
const getSubscription = useCallback12(async () => {
|
|
4328
4609
|
const apiClient = PaywalloClient.getApiClient();
|
|
4329
4610
|
const distinctId = PaywalloClient.getDistinctId();
|
|
4330
4611
|
if (!apiClient || !distinctId) return null;
|
|
@@ -4333,7 +4614,22 @@ function useSubscriptionSync() {
|
|
|
4333
4614
|
try {
|
|
4334
4615
|
return await fetchAndCache();
|
|
4335
4616
|
} catch {
|
|
4336
|
-
|
|
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
|
+
}
|
|
4337
4633
|
}
|
|
4338
4634
|
}, [fetchAndCache]);
|
|
4339
4635
|
return { subscription, hasActiveSubscription, getSubscription, invalidateCache };
|
|
@@ -4342,14 +4638,14 @@ function useSubscriptionSync() {
|
|
|
4342
4638
|
// src/PaywalloProvider.tsx
|
|
4343
4639
|
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
4344
4640
|
function PaywalloProvider({ children, config }) {
|
|
4345
|
-
const [isInitialized, setIsInitialized] =
|
|
4346
|
-
const [initError, setInitError] =
|
|
4347
|
-
const [distinctId, setDistinctId] =
|
|
4348
|
-
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);
|
|
4349
4645
|
const { products, isLoadingProducts, refreshProducts } = useProductLoader();
|
|
4350
4646
|
const { subscription, hasActiveSubscription, getSubscription, invalidateCache } = useSubscriptionSync();
|
|
4351
4647
|
const { preloadedWebView, setPreloadedWebView, preloadCampaign, consumePreloadedCampaign, isPreloadValid } = useCampaignPreload();
|
|
4352
|
-
const clearPreloadedWebView =
|
|
4648
|
+
const clearPreloadedWebView = useCallback13(() => setPreloadedWebView(null), [setPreloadedWebView]);
|
|
4353
4649
|
const { showingPreloadedWebView, presentPreloadedWebView, resolvePreloadedPaywall, handlePreloadedWebViewReady, handlePreloadedClose, setShowingPreloadedWebView, presentedAtRef } = usePaywallPresenter(preloadedWebView, clearPreloadedWebView, setPreloadedWebView);
|
|
4354
4650
|
const { handlePreloadedPurchase: rawPreloadedPurchase, handlePreloadedRestore } = usePurchaseOrchestrator(
|
|
4355
4651
|
preloadedWebView,
|
|
@@ -4358,8 +4654,8 @@ function PaywalloProvider({ children, config }) {
|
|
|
4358
4654
|
setShowingPreloadedWebView,
|
|
4359
4655
|
presentedAtRef
|
|
4360
4656
|
);
|
|
4361
|
-
const [isPreloadPurchasing, setIsPreloadPurchasing] =
|
|
4362
|
-
const handlePreloadedPurchase =
|
|
4657
|
+
const [isPreloadPurchasing, setIsPreloadPurchasing] = useState12(false);
|
|
4658
|
+
const handlePreloadedPurchase = useCallback13(async (productId) => {
|
|
4363
4659
|
setIsPreloadPurchasing(true);
|
|
4364
4660
|
try {
|
|
4365
4661
|
await rawPreloadedPurchase(productId);
|
|
@@ -4376,21 +4672,21 @@ function PaywalloProvider({ children, config }) {
|
|
|
4376
4672
|
setInitError(err instanceof Error ? err : new ClientError(CLIENT_ERROR_CODES.NOT_INITIALIZED, String(err)));
|
|
4377
4673
|
});
|
|
4378
4674
|
}, [config]);
|
|
4379
|
-
const getPaywallConfig =
|
|
4675
|
+
const getPaywallConfig = useCallback13(async (p) => {
|
|
4380
4676
|
const api = PaywalloClient.getApiClient();
|
|
4381
4677
|
return api ? api.getPaywall(p) : null;
|
|
4382
4678
|
}, []);
|
|
4383
|
-
const presentPaywall =
|
|
4679
|
+
const presentPaywall = useCallback13((placement) => {
|
|
4384
4680
|
return new Promise((resolve) => setActivePaywall({ placement, resolver: resolve }));
|
|
4385
4681
|
}, []);
|
|
4386
|
-
const trackCampaignImpression =
|
|
4682
|
+
const trackCampaignImpression = useCallback13((placement, variantKey, campaignId) => {
|
|
4387
4683
|
const api = PaywalloClient.getApiClient();
|
|
4388
4684
|
if (!api) return;
|
|
4389
4685
|
const sessionId = PaywalloClient.getSessionId();
|
|
4390
4686
|
void api.trackEvent("$campaign_impression", PaywalloClient.getDistinctId(), { placement, variantKey, campaignId, ...sessionId && { sessionId } }).catch(() => {
|
|
4391
4687
|
});
|
|
4392
4688
|
}, []);
|
|
4393
|
-
const presentCampaign =
|
|
4689
|
+
const presentCampaign = useCallback13(
|
|
4394
4690
|
async (placement, context) => {
|
|
4395
4691
|
try {
|
|
4396
4692
|
if (preloadedWebView?.placement === placement && preloadedWebView.loaded && isPreloadValid(placement)) {
|
|
@@ -4459,7 +4755,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
4459
4755
|
},
|
|
4460
4756
|
[preloadedWebView, isPreloadValid, consumePreloadedCampaign, presentPreloadedWebView, trackCampaignImpression]
|
|
4461
4757
|
);
|
|
4462
|
-
const restorePurchases =
|
|
4758
|
+
const restorePurchases = useCallback13(async () => {
|
|
4463
4759
|
invalidateCache();
|
|
4464
4760
|
try {
|
|
4465
4761
|
const txs = await getIAPService().restore();
|
|
@@ -4468,7 +4764,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
4468
4764
|
return { success: false, restoredProducts: [], error: error instanceof Error ? error : new PurchaseError(PURCHASE_ERROR_CODES.RESTORE_FAILED, String(error)) };
|
|
4469
4765
|
}
|
|
4470
4766
|
}, [invalidateCache]);
|
|
4471
|
-
const handlePaywallResult =
|
|
4767
|
+
const handlePaywallResult = useCallback13((result) => {
|
|
4472
4768
|
if (activePaywall) {
|
|
4473
4769
|
activePaywall.resolver(result);
|
|
4474
4770
|
setActivePaywall(null);
|
|
@@ -4491,7 +4787,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
4491
4787
|
useEffect7(() => {
|
|
4492
4788
|
if (activePaywall) setPreloadedWebView(null);
|
|
4493
4789
|
}, [activePaywall, setPreloadedWebView]);
|
|
4494
|
-
const noOp =
|
|
4790
|
+
const noOp = useCallback13(() => {
|
|
4495
4791
|
}, []);
|
|
4496
4792
|
const contextValue = useMemo2(() => ({
|
|
4497
4793
|
config: PaywalloClient.getConfig(),
|