@virex-tech/paywallo-sdk 1.1.3 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PaywalloSdk.podspec +1 -0
- package/README.md +28 -53
- package/android/build.gradle +1 -0
- package/android/src/main/java/com/paywallo/sdk/PaywalloFBBridgeModule.kt +84 -0
- package/android/src/main/java/com/paywallo/sdk/PaywalloSdkPackage.kt +4 -1
- package/dist/index.d.mts +84 -53
- package/dist/index.d.ts +84 -53
- package/dist/index.js +1069 -723
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1046 -695
- package/dist/index.mjs.map +1 -1
- package/ios/PaywalloFBBridge.m +23 -0
- package/ios/PaywalloFBBridge.swift +77 -0
- package/ios/PaywalloStoreKit.swift +0 -4
- package/ios/RCTBridgeTypes.swift +6 -0
- package/package.json +5 -19
package/dist/index.js
CHANGED
|
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
8
11
|
var __export = (target, all) => {
|
|
9
12
|
for (var name in all)
|
|
10
13
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -27,6 +30,25 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
30
|
));
|
|
28
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
32
|
|
|
33
|
+
// src/utils/uuid.ts
|
|
34
|
+
var uuid_exports = {};
|
|
35
|
+
__export(uuid_exports, {
|
|
36
|
+
generateUUID: () => generateUUID
|
|
37
|
+
});
|
|
38
|
+
function generateUUID() {
|
|
39
|
+
const template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
|
|
40
|
+
return template.replace(/[xy]/g, (c) => {
|
|
41
|
+
const r = Math.random() * 16 | 0;
|
|
42
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
43
|
+
return v.toString(16);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
var init_uuid = __esm({
|
|
47
|
+
"src/utils/uuid.ts"() {
|
|
48
|
+
"use strict";
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
30
52
|
// src/index.ts
|
|
31
53
|
var index_exports = {};
|
|
32
54
|
__export(index_exports, {
|
|
@@ -44,6 +66,7 @@ __export(index_exports, {
|
|
|
44
66
|
IDENTITY_ERROR_CODES: () => IDENTITY_ERROR_CODES,
|
|
45
67
|
IdentityError: () => IdentityError,
|
|
46
68
|
IdentityManager: () => IdentityManager,
|
|
69
|
+
MetaBridge: () => MetaBridge,
|
|
47
70
|
PAYWALL_ERROR_CODES: () => PAYWALL_ERROR_CODES,
|
|
48
71
|
PURCHASE_ERROR_CODES: () => PURCHASE_ERROR_CODES,
|
|
49
72
|
PaywallContext: () => PaywallContext,
|
|
@@ -71,6 +94,7 @@ __export(index_exports, {
|
|
|
71
94
|
hasVariables: () => hasVariables,
|
|
72
95
|
identityManager: () => identityManager,
|
|
73
96
|
initLocalization: () => initLocalization,
|
|
97
|
+
metaBridge: () => metaBridge,
|
|
74
98
|
nativeStoreKit: () => nativeStoreKit,
|
|
75
99
|
networkMonitor: () => networkMonitor,
|
|
76
100
|
offlineQueue: () => offlineQueue,
|
|
@@ -130,7 +154,7 @@ var CLIENT_ERROR_CODES = {
|
|
|
130
154
|
|
|
131
155
|
// src/domains/paywall/PaywallModal.tsx
|
|
132
156
|
var import_react5 = require("react");
|
|
133
|
-
var
|
|
157
|
+
var import_react_native6 = require("react-native");
|
|
134
158
|
|
|
135
159
|
// src/domains/paywall/usePaywallActions.ts
|
|
136
160
|
var import_react2 = require("react");
|
|
@@ -184,6 +208,9 @@ function createPurchaseError(code, message) {
|
|
|
184
208
|
// src/domains/iap/NativeStoreKit.ts
|
|
185
209
|
var import_react_native = require("react-native");
|
|
186
210
|
|
|
211
|
+
// src/domains/identity/IdentityManager.ts
|
|
212
|
+
var import_react_native_device_info = __toESM(require("react-native-device-info"));
|
|
213
|
+
|
|
187
214
|
// src/domains/identity/IdentityError.ts
|
|
188
215
|
var IdentityError = class extends PaywalloError {
|
|
189
216
|
constructor(code, message) {
|
|
@@ -199,37 +226,17 @@ var IDENTITY_ERROR_CODES = {
|
|
|
199
226
|
IDENTIFY_FAILED: "IDENTITY_IDENTIFY_FAILED"
|
|
200
227
|
};
|
|
201
228
|
|
|
202
|
-
// src/
|
|
203
|
-
|
|
204
|
-
const template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
|
|
205
|
-
return template.replace(/[xy]/g, (c) => {
|
|
206
|
-
const r = Math.random() * 16 | 0;
|
|
207
|
-
const v = c === "x" ? r : r & 3 | 8;
|
|
208
|
-
return v.toString(16);
|
|
209
|
-
});
|
|
210
|
-
}
|
|
229
|
+
// src/domains/identity/IdentityManager.ts
|
|
230
|
+
init_uuid();
|
|
211
231
|
|
|
212
232
|
// src/core/storage/SecureStorage.ts
|
|
213
233
|
var import_async_storage = __toESM(require("@react-native-async-storage/async-storage"));
|
|
214
|
-
var
|
|
215
|
-
async function loadKeychain() {
|
|
216
|
-
if (_keychain !== void 0) return _keychain;
|
|
217
|
-
try {
|
|
218
|
-
const mod = await import("react-native-keychain");
|
|
219
|
-
_keychain = mod.default;
|
|
220
|
-
} catch {
|
|
221
|
-
_keychain = null;
|
|
222
|
-
}
|
|
223
|
-
return _keychain;
|
|
224
|
-
}
|
|
234
|
+
var import_react_native_keychain = __toESM(require("react-native-keychain"));
|
|
225
235
|
var KEYCHAIN_SERVICE_PREFIX = "com.paywallo.sdk.";
|
|
226
236
|
var SecureStorage = class {
|
|
227
237
|
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
|
|
228
238
|
constructor(_debug = false) {
|
|
229
239
|
}
|
|
230
|
-
/**
|
|
231
|
-
* Get a value, trying Keychain first (survives reinstalls on iOS), then AsyncStorage.
|
|
232
|
-
*/
|
|
233
240
|
async get(key) {
|
|
234
241
|
const keychainValue = await this.getFromKeychain(key);
|
|
235
242
|
if (keychainValue) {
|
|
@@ -245,9 +252,6 @@ var SecureStorage = class {
|
|
|
245
252
|
return null;
|
|
246
253
|
}
|
|
247
254
|
}
|
|
248
|
-
/**
|
|
249
|
-
* Set a value in both Keychain (if available) and AsyncStorage.
|
|
250
|
-
*/
|
|
251
255
|
async set(key, value) {
|
|
252
256
|
try {
|
|
253
257
|
await Promise.all([
|
|
@@ -271,13 +275,11 @@ var SecureStorage = class {
|
|
|
271
275
|
}
|
|
272
276
|
}
|
|
273
277
|
hasKeychainSupport() {
|
|
274
|
-
return
|
|
278
|
+
return true;
|
|
275
279
|
}
|
|
276
280
|
async getFromKeychain(key) {
|
|
277
|
-
const keychain = await loadKeychain();
|
|
278
|
-
if (!keychain) return null;
|
|
279
281
|
try {
|
|
280
|
-
const result = await
|
|
282
|
+
const result = await import_react_native_keychain.default.getGenericPassword({ service: `${KEYCHAIN_SERVICE_PREFIX}${key}` });
|
|
281
283
|
if (result && result.password) {
|
|
282
284
|
return result.password;
|
|
283
285
|
}
|
|
@@ -287,20 +289,16 @@ var SecureStorage = class {
|
|
|
287
289
|
}
|
|
288
290
|
}
|
|
289
291
|
async setToKeychain(key, value) {
|
|
290
|
-
const keychain = await loadKeychain();
|
|
291
|
-
if (!keychain) return false;
|
|
292
292
|
try {
|
|
293
|
-
await
|
|
293
|
+
await import_react_native_keychain.default.setGenericPassword("panel", value, { service: `${KEYCHAIN_SERVICE_PREFIX}${key}` });
|
|
294
294
|
return true;
|
|
295
295
|
} catch {
|
|
296
296
|
return false;
|
|
297
297
|
}
|
|
298
298
|
}
|
|
299
299
|
async removeFromKeychain(key) {
|
|
300
|
-
const keychain = await loadKeychain();
|
|
301
|
-
if (!keychain) return false;
|
|
302
300
|
try {
|
|
303
|
-
await
|
|
301
|
+
await import_react_native_keychain.default.resetGenericPassword({ service: `${KEYCHAIN_SERVICE_PREFIX}${key}` });
|
|
304
302
|
return true;
|
|
305
303
|
} catch {
|
|
306
304
|
return false;
|
|
@@ -311,11 +309,13 @@ var secureStorage = new SecureStorage();
|
|
|
311
309
|
|
|
312
310
|
// src/domains/identity/IdentityManager.ts
|
|
313
311
|
var DEVICE_ID_KEY = "device_id";
|
|
312
|
+
var ANON_ID_KEY = "anon_id";
|
|
314
313
|
var USER_EMAIL_KEY = "user_email";
|
|
315
314
|
var USER_PROPERTIES_KEY = "user_properties";
|
|
316
315
|
var IdentityManager = class {
|
|
317
316
|
constructor() {
|
|
318
317
|
this.deviceId = null;
|
|
318
|
+
this.anonId = null;
|
|
319
319
|
this.email = null;
|
|
320
320
|
this.properties = {};
|
|
321
321
|
this.apiClient = null;
|
|
@@ -332,33 +332,22 @@ var IdentityManager = class {
|
|
|
332
332
|
this.secureStorage = new SecureStorage(debug);
|
|
333
333
|
await this.loadPersistedState();
|
|
334
334
|
if (!this.deviceId) {
|
|
335
|
-
let DeviceInfo = null;
|
|
336
|
-
try {
|
|
337
|
-
const mod = await import("react-native-device-info");
|
|
338
|
-
DeviceInfo = mod.default;
|
|
339
|
-
} catch {
|
|
340
|
-
DeviceInfo = null;
|
|
341
|
-
}
|
|
342
335
|
try {
|
|
343
|
-
|
|
344
|
-
this.deviceId = await DeviceInfo.getUniqueId();
|
|
345
|
-
this.log("Got device ID from DeviceInfo:", this.deviceId);
|
|
346
|
-
} else {
|
|
347
|
-
this.deviceId = generateUUID();
|
|
348
|
-
this.log("DeviceInfo not installed, using generated UUID:", this.deviceId);
|
|
349
|
-
}
|
|
336
|
+
this.deviceId = await import_react_native_device_info.default.getUniqueId();
|
|
350
337
|
} catch {
|
|
351
338
|
this.deviceId = generateUUID();
|
|
352
|
-
this.log("DeviceInfo unavailable, using generated UUID:", this.deviceId);
|
|
353
339
|
}
|
|
354
340
|
const stored = await this.secureStorage.set(DEVICE_ID_KEY, this.deviceId);
|
|
355
341
|
if (!stored) {
|
|
356
|
-
this.log("Failed to persist device ID to secure storage, using fallback");
|
|
357
342
|
await this.secureStorage.set("device_id_fallback", this.deviceId).catch(() => {
|
|
358
|
-
this.log("Fallback also failed \u2014 device ID only in memory");
|
|
359
343
|
});
|
|
360
344
|
}
|
|
361
345
|
}
|
|
346
|
+
if (!this.anonId) {
|
|
347
|
+
this.anonId = "$paywallo_anon:" + generateUUID();
|
|
348
|
+
await this.secureStorage.set(ANON_ID_KEY, this.anonId).catch(() => {
|
|
349
|
+
});
|
|
350
|
+
}
|
|
362
351
|
this.initialized = true;
|
|
363
352
|
return this.deviceId ?? "";
|
|
364
353
|
}
|
|
@@ -382,21 +371,18 @@ var IdentityManager = class {
|
|
|
382
371
|
this.properties = { ...this.properties, ...properties };
|
|
383
372
|
await this.secureStorage.set(USER_PROPERTIES_KEY, JSON.stringify(this.properties));
|
|
384
373
|
}
|
|
385
|
-
if (this.apiClient && this.
|
|
374
|
+
if (this.apiClient && this.anonId) {
|
|
386
375
|
try {
|
|
387
|
-
await this.apiClient.identify(this.
|
|
388
|
-
|
|
389
|
-
} catch (error) {
|
|
390
|
-
this.log("Failed to update identity on server:", error);
|
|
376
|
+
await this.apiClient.identify(this.anonId, properties, email, this.deviceId ?? void 0);
|
|
377
|
+
} catch {
|
|
391
378
|
}
|
|
392
379
|
}
|
|
393
380
|
}
|
|
394
381
|
getDistinctId() {
|
|
395
|
-
if (!this.initialized || !this.
|
|
396
|
-
if (this.debug) console.warn("[Paywallo Identity] getDistinctId called before initialization");
|
|
382
|
+
if (!this.initialized || !this.anonId) {
|
|
397
383
|
return "";
|
|
398
384
|
}
|
|
399
|
-
return this.
|
|
385
|
+
return this.anonId;
|
|
400
386
|
}
|
|
401
387
|
getDeviceId() {
|
|
402
388
|
return this.deviceId;
|
|
@@ -411,24 +397,22 @@ var IdentityManager = class {
|
|
|
411
397
|
this.ensureInitialized();
|
|
412
398
|
this.properties = { ...this.properties, ...properties };
|
|
413
399
|
await this.secureStorage.set(USER_PROPERTIES_KEY, JSON.stringify(this.properties));
|
|
414
|
-
if (this.apiClient && this.
|
|
400
|
+
if (this.apiClient && this.anonId) {
|
|
415
401
|
try {
|
|
416
|
-
await this.apiClient.identify(this.
|
|
417
|
-
} catch
|
|
418
|
-
this.log("Failed to update properties on server:", error);
|
|
402
|
+
await this.apiClient.identify(this.anonId, this.properties, this.email ?? void 0, this.deviceId ?? void 0);
|
|
403
|
+
} catch {
|
|
419
404
|
}
|
|
420
405
|
}
|
|
421
406
|
}
|
|
422
407
|
async reset() {
|
|
408
|
+
const newAnonId = "$paywallo_anon:" + generateUUID();
|
|
409
|
+
this.anonId = newAnonId;
|
|
423
410
|
this.email = null;
|
|
424
411
|
this.properties = {};
|
|
425
|
-
this.initialized = false;
|
|
426
|
-
this.apiClient = null;
|
|
427
|
-
this.deviceId = null;
|
|
428
412
|
await Promise.all([
|
|
413
|
+
this.secureStorage?.set(ANON_ID_KEY, newAnonId) ?? Promise.resolve(false),
|
|
429
414
|
this.secureStorage?.remove(USER_EMAIL_KEY) ?? Promise.resolve(false),
|
|
430
|
-
this.secureStorage?.remove(USER_PROPERTIES_KEY) ?? Promise.resolve(false)
|
|
431
|
-
this.secureStorage?.remove("device_id_fallback") ?? Promise.resolve(false)
|
|
415
|
+
this.secureStorage?.remove(USER_PROPERTIES_KEY) ?? Promise.resolve(false)
|
|
432
416
|
]);
|
|
433
417
|
}
|
|
434
418
|
getState() {
|
|
@@ -445,21 +429,23 @@ var IdentityManager = class {
|
|
|
445
429
|
if (!this.secureStorage) {
|
|
446
430
|
return;
|
|
447
431
|
}
|
|
448
|
-
const [storedDeviceId, storedEmail, storedPropertiesJson] = await Promise.all([
|
|
432
|
+
const [storedDeviceId, storedAnonId, storedEmail, storedPropertiesJson] = await Promise.all([
|
|
449
433
|
this.secureStorage.get(DEVICE_ID_KEY),
|
|
434
|
+
this.secureStorage.get(ANON_ID_KEY),
|
|
450
435
|
this.secureStorage.get(USER_EMAIL_KEY),
|
|
451
436
|
this.secureStorage.get(USER_PROPERTIES_KEY)
|
|
452
437
|
]);
|
|
453
438
|
if (storedDeviceId) {
|
|
454
439
|
this.deviceId = storedDeviceId;
|
|
455
|
-
this.log("Loaded device ID from storage:", this.deviceId);
|
|
456
440
|
} else {
|
|
457
441
|
const fallbackDeviceId = await this.secureStorage.get("device_id_fallback");
|
|
458
442
|
if (fallbackDeviceId) {
|
|
459
443
|
this.deviceId = fallbackDeviceId;
|
|
460
|
-
this.log("Loaded device ID from fallback storage:", this.deviceId);
|
|
461
444
|
}
|
|
462
445
|
}
|
|
446
|
+
if (storedAnonId) {
|
|
447
|
+
this.anonId = storedAnonId;
|
|
448
|
+
}
|
|
463
449
|
if (storedEmail) {
|
|
464
450
|
this.email = storedEmail;
|
|
465
451
|
}
|
|
@@ -479,11 +465,6 @@ var IdentityManager = class {
|
|
|
479
465
|
);
|
|
480
466
|
}
|
|
481
467
|
}
|
|
482
|
-
log(...args) {
|
|
483
|
-
if (this.debug) {
|
|
484
|
-
console.warn("[Paywallo Identity]", ...args);
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
468
|
};
|
|
488
469
|
var identityManager = new IdentityManager();
|
|
489
470
|
|
|
@@ -568,7 +549,6 @@ var nativeStoreKit = {
|
|
|
568
549
|
isAvailable,
|
|
569
550
|
async getProducts(productIds) {
|
|
570
551
|
if (!PaywalloStoreKitNative) {
|
|
571
|
-
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] StoreKit native module not available");
|
|
572
552
|
return [];
|
|
573
553
|
}
|
|
574
554
|
const nativeProducts = await PaywalloStoreKitNative.getProducts(productIds);
|
|
@@ -582,20 +562,41 @@ var nativeStoreKit = {
|
|
|
582
562
|
);
|
|
583
563
|
}
|
|
584
564
|
const distinctId = identityManager.getDistinctId();
|
|
585
|
-
const
|
|
586
|
-
if (!
|
|
587
|
-
|
|
565
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
566
|
+
if (distinctId && !uuidRegex.test(distinctId) && PaywalloClient.getConfig()?.debug) {
|
|
567
|
+
console.warn("[Paywallo] distinctId is not a valid UUID for appAccountToken:", distinctId);
|
|
568
|
+
}
|
|
569
|
+
try {
|
|
570
|
+
const result = await PaywalloStoreKitNative.purchase(productId, distinctId ?? null);
|
|
571
|
+
if (!result || result.pending) {
|
|
572
|
+
return null;
|
|
573
|
+
}
|
|
574
|
+
return mapNativePurchase(result);
|
|
575
|
+
} catch (err) {
|
|
576
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
577
|
+
if (message.toLowerCase().includes("cancel")) {
|
|
578
|
+
throw new PurchaseError(PURCHASE_ERROR_CODES.USER_CANCELLED, message, true);
|
|
579
|
+
}
|
|
580
|
+
throw new PurchaseError(PURCHASE_ERROR_CODES.PURCHASE_FAILED, message);
|
|
588
581
|
}
|
|
589
|
-
return mapNativePurchase(result);
|
|
590
582
|
},
|
|
591
583
|
async finishTransaction(transactionId) {
|
|
592
584
|
if (!PaywalloStoreKitNative) return;
|
|
593
|
-
|
|
585
|
+
try {
|
|
586
|
+
await PaywalloStoreKitNative.finishTransaction(transactionId);
|
|
587
|
+
} catch (err) {
|
|
588
|
+
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] finishTransaction native error", err);
|
|
589
|
+
}
|
|
594
590
|
},
|
|
595
591
|
async getActiveTransactions() {
|
|
596
592
|
if (!PaywalloStoreKitNative) return [];
|
|
597
|
-
|
|
598
|
-
|
|
593
|
+
try {
|
|
594
|
+
const results = await PaywalloStoreKitNative.getActiveTransactions();
|
|
595
|
+
return results.map(mapNativePurchase);
|
|
596
|
+
} catch (err) {
|
|
597
|
+
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] getActiveTransactions failed", err);
|
|
598
|
+
return [];
|
|
599
|
+
}
|
|
599
600
|
}
|
|
600
601
|
};
|
|
601
602
|
|
|
@@ -638,8 +639,8 @@ var IAPService = class {
|
|
|
638
639
|
this.productsCache.set(product.productId, product);
|
|
639
640
|
}
|
|
640
641
|
return products;
|
|
641
|
-
} catch (
|
|
642
|
-
if (
|
|
642
|
+
} catch (err) {
|
|
643
|
+
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] loadProducts failed", err);
|
|
643
644
|
return [];
|
|
644
645
|
}
|
|
645
646
|
}
|
|
@@ -649,35 +650,47 @@ var IAPService = class {
|
|
|
649
650
|
getCachedProducts() {
|
|
650
651
|
return new Map(this.productsCache);
|
|
651
652
|
}
|
|
653
|
+
async validateWithRetry(attempt, maxRetries = 2, delayMs = 3e3) {
|
|
654
|
+
let lastError;
|
|
655
|
+
for (let i = 0; i <= maxRetries; i++) {
|
|
656
|
+
try {
|
|
657
|
+
return await attempt();
|
|
658
|
+
} catch (error) {
|
|
659
|
+
lastError = error;
|
|
660
|
+
if (i < maxRetries) await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
throw lastError;
|
|
664
|
+
}
|
|
652
665
|
async validateWithServer(purchase, productId, options) {
|
|
653
666
|
const apiClient = this.getApiClient();
|
|
654
667
|
const product = this.productsCache.get(productId);
|
|
655
668
|
const platform = import_react_native2.Platform.OS;
|
|
656
669
|
const distinctId = PaywalloClient.getDistinctId();
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
670
|
+
return this.validateWithRetry(
|
|
671
|
+
() => apiClient.validatePurchase(
|
|
672
|
+
platform,
|
|
673
|
+
purchase.receipt,
|
|
674
|
+
purchase.productId,
|
|
675
|
+
purchase.transactionId,
|
|
676
|
+
product?.priceValue ?? 0,
|
|
677
|
+
product?.currency ?? "USD",
|
|
678
|
+
this.getDeviceCountry(),
|
|
679
|
+
distinctId,
|
|
680
|
+
options?.paywallPlacement,
|
|
681
|
+
options?.variantKey
|
|
682
|
+
)
|
|
668
683
|
);
|
|
669
|
-
return validation;
|
|
670
684
|
}
|
|
671
685
|
async finishTransaction(transactionId) {
|
|
672
686
|
try {
|
|
673
687
|
await nativeStoreKit.finishTransaction(transactionId);
|
|
674
|
-
} catch (
|
|
675
|
-
if (
|
|
688
|
+
} catch (err) {
|
|
689
|
+
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] finishTransaction failed", err);
|
|
676
690
|
}
|
|
677
691
|
}
|
|
678
692
|
async purchase(productId, options) {
|
|
679
693
|
if (!nativeStoreKit.isAvailable()) {
|
|
680
|
-
if (this.debug) console.warn("[Paywallo][Purchase] StoreKit not available");
|
|
681
694
|
return {
|
|
682
695
|
success: false,
|
|
683
696
|
error: new PurchaseError(PURCHASE_ERROR_CODES.STORE_NOT_AVAILABLE, "In-app purchases are not available on this device")
|
|
@@ -688,27 +701,13 @@ var IAPService = class {
|
|
|
688
701
|
if (!purchase) {
|
|
689
702
|
return { success: false };
|
|
690
703
|
}
|
|
691
|
-
let validation = null;
|
|
692
|
-
try {
|
|
693
|
-
validation = await this.validateWithServer(purchase, productId, options);
|
|
694
|
-
} catch (validationError) {
|
|
695
|
-
if (this.debug) console.warn("[Paywallo][Purchase] Server validation FAILED:", validationError instanceof Error ? validationError.message : validationError);
|
|
696
|
-
return {
|
|
697
|
-
success: false,
|
|
698
|
-
error: new PurchaseError(PURCHASE_ERROR_CODES.VALIDATION_FAILED, validationError instanceof Error ? validationError.message : "Server validation failed")
|
|
699
|
-
};
|
|
700
|
-
}
|
|
701
|
-
if (!validation?.success) {
|
|
702
|
-
return {
|
|
703
|
-
success: false,
|
|
704
|
-
error: new PurchaseError(PURCHASE_ERROR_CODES.VALIDATION_FAILED, validation?.error ? `Server validation: ${validation.error}` : "Server validation failed")
|
|
705
|
-
};
|
|
706
|
-
}
|
|
707
704
|
await this.finishTransaction(purchase.transactionId);
|
|
705
|
+
this.validateWithServer(purchase, productId, options).catch((err) => {
|
|
706
|
+
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] background validation failed", err);
|
|
707
|
+
});
|
|
708
708
|
return { success: true, purchase };
|
|
709
709
|
} catch (error) {
|
|
710
710
|
const e = error instanceof PurchaseError ? error : new PurchaseError(PURCHASE_ERROR_CODES.PURCHASE_FAILED, error instanceof Error ? error.message : String(error));
|
|
711
|
-
if (this.debug) console.warn("[Paywallo][Purchase] FAILED:", e.message);
|
|
712
711
|
return { success: false, error: e };
|
|
713
712
|
}
|
|
714
713
|
}
|
|
@@ -743,15 +742,15 @@ var IAPService = class {
|
|
|
743
742
|
if (r.status === "fulfilled" && r.value.success) {
|
|
744
743
|
try {
|
|
745
744
|
await nativeStoreKit.finishTransaction(tx.transactionId);
|
|
746
|
-
} catch (
|
|
747
|
-
if (
|
|
745
|
+
} catch (err) {
|
|
746
|
+
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] finishTransaction failed", err);
|
|
748
747
|
}
|
|
749
748
|
validated.push(tx);
|
|
750
749
|
}
|
|
751
750
|
}
|
|
752
751
|
return validated;
|
|
753
|
-
} catch (
|
|
754
|
-
if (
|
|
752
|
+
} catch (err) {
|
|
753
|
+
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] restore failed", err);
|
|
755
754
|
return [];
|
|
756
755
|
}
|
|
757
756
|
}
|
|
@@ -781,7 +780,8 @@ function usePaywallTracking() {
|
|
|
781
780
|
...opts.campaignId && { campaignId: opts.campaignId },
|
|
782
781
|
...sessionId && { sessionId }
|
|
783
782
|
});
|
|
784
|
-
} catch {
|
|
783
|
+
} catch (err) {
|
|
784
|
+
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] trackDismissed failed", err);
|
|
785
785
|
}
|
|
786
786
|
}, []);
|
|
787
787
|
const trackProductSelected = (0, import_react.useCallback)((opts) => {
|
|
@@ -795,7 +795,8 @@ function usePaywallTracking() {
|
|
|
795
795
|
...opts.variantKey && { variantKey: opts.variantKey },
|
|
796
796
|
...opts.campaignId && { campaignId: opts.campaignId },
|
|
797
797
|
...sessionId && { sessionId }
|
|
798
|
-
}).catch(() => {
|
|
798
|
+
}).catch((err) => {
|
|
799
|
+
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] trackProductSelected failed", err);
|
|
799
800
|
});
|
|
800
801
|
}, []);
|
|
801
802
|
const trackPurchased = (0, import_react.useCallback)(async (opts) => {
|
|
@@ -811,7 +812,8 @@ function usePaywallTracking() {
|
|
|
811
812
|
...opts.campaignId && { campaignId: opts.campaignId },
|
|
812
813
|
...sessionId && { sessionId }
|
|
813
814
|
});
|
|
814
|
-
} catch {
|
|
815
|
+
} catch (err) {
|
|
816
|
+
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] trackPurchased failed", err);
|
|
815
817
|
}
|
|
816
818
|
}, []);
|
|
817
819
|
return { trackDismissed, trackProductSelected, trackPurchased };
|
|
@@ -819,7 +821,7 @@ function usePaywallTracking() {
|
|
|
819
821
|
|
|
820
822
|
// src/domains/paywall/usePaywallActions.ts
|
|
821
823
|
var SCREEN_HEIGHT = import_react_native3.Dimensions.get("window").height;
|
|
822
|
-
function usePaywallActions(placement, paywallConfig, onResult, variantKey, campaignId) {
|
|
824
|
+
function usePaywallActions(placement, paywallConfig, onResult, variantKey, campaignId, openAnim) {
|
|
823
825
|
const [isPurchasing, setIsPurchasing] = (0, import_react2.useState)(false);
|
|
824
826
|
const [isClosing, setIsClosing] = (0, import_react2.useState)(false);
|
|
825
827
|
const slideAnim = (0, import_react2.useRef)(new import_react_native3.Animated.Value(0)).current;
|
|
@@ -828,7 +830,8 @@ function usePaywallActions(placement, paywallConfig, onResult, variantKey, campa
|
|
|
828
830
|
const handleClose = (0, import_react2.useCallback)(() => {
|
|
829
831
|
if (isClosing) return;
|
|
830
832
|
setIsClosing(true);
|
|
831
|
-
|
|
833
|
+
const animTarget = openAnim ?? slideAnim;
|
|
834
|
+
import_react_native3.Animated.timing(animTarget, { toValue: SCREEN_HEIGHT, duration: 350, useNativeDriver: true }).start(() => {
|
|
832
835
|
if (paywallConfig) {
|
|
833
836
|
const durationSeconds = Math.round((Date.now() - presentedAtRef.current) / 1e3);
|
|
834
837
|
void trackDismissed({ placement, paywallId: paywallConfig.id, durationSeconds, action: "close", variantKey, campaignId });
|
|
@@ -837,7 +840,7 @@ function usePaywallActions(placement, paywallConfig, onResult, variantKey, campa
|
|
|
837
840
|
setIsClosing(false);
|
|
838
841
|
onResult({ presented: true, purchased: false, cancelled: true, restored: false });
|
|
839
842
|
});
|
|
840
|
-
}, [isClosing, onResult, paywallConfig, placement, slideAnim, trackDismissed, variantKey, campaignId]);
|
|
843
|
+
}, [isClosing, openAnim, onResult, paywallConfig, placement, slideAnim, trackDismissed, variantKey, campaignId]);
|
|
841
844
|
const handlePurchase = (0, import_react2.useCallback)(async (productId) => {
|
|
842
845
|
if (isPurchasing) return;
|
|
843
846
|
setIsPurchasing(true);
|
|
@@ -855,7 +858,6 @@ function usePaywallActions(placement, paywallConfig, onResult, variantKey, campa
|
|
|
855
858
|
onResult({ presented: true, purchased: false, cancelled: false, restored: false });
|
|
856
859
|
}
|
|
857
860
|
} catch (error) {
|
|
858
|
-
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] Purchase error:", error);
|
|
859
861
|
onResult({ presented: true, purchased: false, cancelled: false, restored: false });
|
|
860
862
|
} finally {
|
|
861
863
|
setIsPurchasing(false);
|
|
@@ -876,7 +878,6 @@ function usePaywallActions(placement, paywallConfig, onResult, variantKey, campa
|
|
|
876
878
|
onResult({ presented: true, purchased: false, cancelled: false, restored: false });
|
|
877
879
|
}
|
|
878
880
|
} catch (error) {
|
|
879
|
-
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] Restore error:", error);
|
|
880
881
|
onResult({ presented: true, purchased: false, cancelled: false, restored: false });
|
|
881
882
|
} finally {
|
|
882
883
|
setIsPurchasing(false);
|
|
@@ -952,7 +953,8 @@ function usePaywallLoader(placement, visible, preloadedConfig, preloadedProducts
|
|
|
952
953
|
const map = /* @__PURE__ */ new Map();
|
|
953
954
|
for (const p of storeProducts) map.set(p.productId, p);
|
|
954
955
|
setProducts(map);
|
|
955
|
-
} catch {
|
|
956
|
+
} catch (err) {
|
|
957
|
+
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] loadProducts in paywall failed", err);
|
|
956
958
|
setProducts(/* @__PURE__ */ new Map());
|
|
957
959
|
}
|
|
958
960
|
} else {
|
|
@@ -971,7 +973,6 @@ function usePaywallLoader(placement, visible, preloadedConfig, preloadedProducts
|
|
|
971
973
|
}
|
|
972
974
|
} catch (err) {
|
|
973
975
|
const e = err instanceof Error ? err : new PaywallError(PAYWALL_ERROR_CODES.LOAD_FAILED, String(err));
|
|
974
|
-
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] Error loading paywall:", e.message);
|
|
975
976
|
setError(e);
|
|
976
977
|
}
|
|
977
978
|
};
|
|
@@ -980,11 +981,96 @@ function usePaywallLoader(placement, visible, preloadedConfig, preloadedProducts
|
|
|
980
981
|
return { paywallConfig, products, error };
|
|
981
982
|
}
|
|
982
983
|
|
|
984
|
+
// src/utils/localization.ts
|
|
985
|
+
var import_react_native4 = require("react-native");
|
|
986
|
+
var currentLanguage = "pt-BR";
|
|
987
|
+
var defaultLanguage = "pt-BR";
|
|
988
|
+
function setCurrentLanguage(language) {
|
|
989
|
+
currentLanguage = language;
|
|
990
|
+
}
|
|
991
|
+
function setDefaultLanguage(language) {
|
|
992
|
+
defaultLanguage = language;
|
|
993
|
+
}
|
|
994
|
+
function getCurrentLanguage() {
|
|
995
|
+
return currentLanguage;
|
|
996
|
+
}
|
|
997
|
+
function getDefaultLanguage() {
|
|
998
|
+
return defaultLanguage;
|
|
999
|
+
}
|
|
1000
|
+
function getLocalizedString(text) {
|
|
1001
|
+
if (!text) return "";
|
|
1002
|
+
if (typeof text === "string") return text;
|
|
1003
|
+
return text[currentLanguage] ?? text[defaultLanguage] ?? Object.values(text)[0] ?? "";
|
|
1004
|
+
}
|
|
1005
|
+
function detectDeviceLanguage() {
|
|
1006
|
+
try {
|
|
1007
|
+
if (import_react_native4.Platform.OS === "ios") {
|
|
1008
|
+
const settings = import_react_native4.NativeModules.SettingsManager?.settings;
|
|
1009
|
+
return settings?.AppleLocale ?? settings?.AppleLanguages?.[0] ?? "en-US";
|
|
1010
|
+
}
|
|
1011
|
+
return import_react_native4.NativeModules.I18nManager?.localeIdentifier ?? "en-US";
|
|
1012
|
+
} catch {
|
|
1013
|
+
return "en-US";
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
var SDK_STRINGS = {
|
|
1017
|
+
paywallErrorTitle: {
|
|
1018
|
+
"pt-BR": "Algo deu errado",
|
|
1019
|
+
"pt": "Algo deu errado",
|
|
1020
|
+
"en": "Something went wrong",
|
|
1021
|
+
"en-US": "Something went wrong",
|
|
1022
|
+
"en-GB": "Something went wrong",
|
|
1023
|
+
"es": "Algo sali\xF3 mal",
|
|
1024
|
+
"es-419": "Algo sali\xF3 mal"
|
|
1025
|
+
},
|
|
1026
|
+
paywallRetryButton: {
|
|
1027
|
+
"pt-BR": "Tentar novamente",
|
|
1028
|
+
"pt": "Tentar novamente",
|
|
1029
|
+
"en": "Try again",
|
|
1030
|
+
"en-US": "Try again",
|
|
1031
|
+
"en-GB": "Try again",
|
|
1032
|
+
"es": "Reintentar",
|
|
1033
|
+
"es-419": "Reintentar"
|
|
1034
|
+
},
|
|
1035
|
+
paywallCloseButton: {
|
|
1036
|
+
"pt-BR": "Fechar",
|
|
1037
|
+
"pt": "Fechar",
|
|
1038
|
+
"en": "Close",
|
|
1039
|
+
"en-US": "Close",
|
|
1040
|
+
"en-GB": "Close",
|
|
1041
|
+
"es": "Cerrar",
|
|
1042
|
+
"es-419": "Cerrar"
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
function getSdkString(key) {
|
|
1046
|
+
const map = SDK_STRINGS[key];
|
|
1047
|
+
const lang = currentLanguage;
|
|
1048
|
+
if (lang in map) return map[lang];
|
|
1049
|
+
const base = lang.split("-")[0];
|
|
1050
|
+
if (base && base in map) return map[base];
|
|
1051
|
+
if (defaultLanguage in map) return map[defaultLanguage];
|
|
1052
|
+
return Object.values(map)[0];
|
|
1053
|
+
}
|
|
1054
|
+
function initLocalization(options) {
|
|
1055
|
+
if (options?.defaultLanguage) {
|
|
1056
|
+
defaultLanguage = options.defaultLanguage;
|
|
1057
|
+
}
|
|
1058
|
+
if (options?.detectDevice !== false) {
|
|
1059
|
+
const deviceLanguage = detectDeviceLanguage();
|
|
1060
|
+
currentLanguage = deviceLanguage;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
|
|
983
1064
|
// src/domains/paywall/PaywallWebView.tsx
|
|
984
1065
|
var import_react4 = require("react");
|
|
985
|
-
var
|
|
1066
|
+
var import_react_native5 = require("react-native");
|
|
986
1067
|
|
|
987
1068
|
// src/domains/paywall/parseWebViewMessage.ts
|
|
1069
|
+
function deriveMessageId(message) {
|
|
1070
|
+
if (message.id) return message.id;
|
|
1071
|
+
const payloadKey = message.payload?.productId ?? message.payload?.url ?? (message.payload?.timestamp != null ? String(message.payload.timestamp) : "");
|
|
1072
|
+
return `${message.type}:${payloadKey}`;
|
|
1073
|
+
}
|
|
988
1074
|
function parseWebViewMessage(raw) {
|
|
989
1075
|
try {
|
|
990
1076
|
const parsed = JSON.parse(raw);
|
|
@@ -1008,7 +1094,7 @@ function buildPaywallInjectionScript(data) {
|
|
|
1008
1094
|
secondaryProductId: data.secondaryProductId
|
|
1009
1095
|
}
|
|
1010
1096
|
};
|
|
1011
|
-
return `(function(){var d=${JSON.stringify(paywallData)},a=0,m=50;function t(){a++;if(window.receiveNativeMessage){window.receiveNativeMessage(d);return;}if(a<m){setTimeout(t,100);}
|
|
1097
|
+
return `(function(){var d=${JSON.stringify(paywallData)},a=0,m=50;function t(){a++;if(window.receiveNativeMessage){window.receiveNativeMessage(d);return;}if(a<m){setTimeout(t,100);}}t();})();true;`;
|
|
1012
1098
|
}
|
|
1013
1099
|
function buildPurchaseStateScript(isPurchasing) {
|
|
1014
1100
|
return `if(window.receiveNativeMessage){window.receiveNativeMessage({type:"purchaseState",data:{isPurchasing:${isPurchasing}}});}true;`;
|
|
@@ -1019,14 +1105,14 @@ var import_jsx_runtime = require("react/jsx-runtime");
|
|
|
1019
1105
|
var NativeWebViewComponent = null;
|
|
1020
1106
|
function getNativeWebView() {
|
|
1021
1107
|
if (!NativeWebViewComponent) {
|
|
1022
|
-
NativeWebViewComponent = (0,
|
|
1108
|
+
NativeWebViewComponent = (0, import_react_native5.requireNativeComponent)("PaywalloWebView");
|
|
1023
1109
|
}
|
|
1024
1110
|
return NativeWebViewComponent;
|
|
1025
1111
|
}
|
|
1026
1112
|
var webViewEmitter = null;
|
|
1027
1113
|
function getWebViewEmitter() {
|
|
1028
1114
|
if (!webViewEmitter) {
|
|
1029
|
-
webViewEmitter = new
|
|
1115
|
+
webViewEmitter = new import_react_native5.NativeEventEmitter(import_react_native5.NativeModules.PaywalloWebViewModule);
|
|
1030
1116
|
}
|
|
1031
1117
|
return webViewEmitter;
|
|
1032
1118
|
}
|
|
@@ -1044,6 +1130,7 @@ function PaywallWebView({
|
|
|
1044
1130
|
primaryProductId,
|
|
1045
1131
|
secondaryProductId,
|
|
1046
1132
|
isPurchasing,
|
|
1133
|
+
webUrl: webUrlProp,
|
|
1047
1134
|
onPurchase,
|
|
1048
1135
|
onClose,
|
|
1049
1136
|
onRestore,
|
|
@@ -1055,8 +1142,39 @@ function PaywallWebView({
|
|
|
1055
1142
|
const [scriptToInject, setScriptToInject] = (0, import_react4.useState)(void 0);
|
|
1056
1143
|
const onErrorRef = (0, import_react4.useRef)(onError);
|
|
1057
1144
|
onErrorRef.current = onError;
|
|
1145
|
+
const onReadyRef = (0, import_react4.useRef)(onReady);
|
|
1146
|
+
onReadyRef.current = onReady;
|
|
1058
1147
|
const NativeWebView = (0, import_react4.useMemo)(() => getNativeWebView(), []);
|
|
1059
|
-
const
|
|
1148
|
+
const [resolvedWebUrl, setResolvedWebUrl] = (0, import_react4.useState)(
|
|
1149
|
+
() => webUrlProp ?? PaywalloClient.getWebUrl()
|
|
1150
|
+
);
|
|
1151
|
+
(0, import_react4.useEffect)(() => {
|
|
1152
|
+
if (webUrlProp !== void 0) {
|
|
1153
|
+
setResolvedWebUrl(webUrlProp ?? null);
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
const url = PaywalloClient.getWebUrl();
|
|
1157
|
+
if (url) {
|
|
1158
|
+
setResolvedWebUrl(url);
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
const interval = setInterval(() => {
|
|
1162
|
+
const u = PaywalloClient.getWebUrl();
|
|
1163
|
+
if (u) {
|
|
1164
|
+
setResolvedWebUrl(u);
|
|
1165
|
+
clearInterval(interval);
|
|
1166
|
+
}
|
|
1167
|
+
}, 200);
|
|
1168
|
+
return () => clearInterval(interval);
|
|
1169
|
+
}, [webUrlProp]);
|
|
1170
|
+
(0, import_react4.useEffect)(() => {
|
|
1171
|
+
if (!resolvedWebUrl) {
|
|
1172
|
+
onErrorRef.current?.({
|
|
1173
|
+
code: -1,
|
|
1174
|
+
description: "Paywall URL not configured. SDK may not be initialized."
|
|
1175
|
+
});
|
|
1176
|
+
}
|
|
1177
|
+
}, []);
|
|
1060
1178
|
const buildAndSetInjectionScript = (0, import_react4.useCallback)(() => {
|
|
1061
1179
|
if (!craftData || paywallDataSent.current) return;
|
|
1062
1180
|
paywallDataSent.current = true;
|
|
@@ -1067,19 +1185,21 @@ function PaywallWebView({
|
|
|
1067
1185
|
secondaryProductId
|
|
1068
1186
|
});
|
|
1069
1187
|
setScriptToInject(script);
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
const lastProcessedRef = (0, import_react4.useRef)({ type: "", time: 0 });
|
|
1188
|
+
}, [craftData, products, primaryProductId, secondaryProductId]);
|
|
1189
|
+
const processedIdsRef = (0, import_react4.useRef)(/* @__PURE__ */ new Set());
|
|
1073
1190
|
const handleParsedMessage = (0, import_react4.useCallback)(
|
|
1074
1191
|
(message) => {
|
|
1075
|
-
const
|
|
1076
|
-
if (
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
lastProcessedRef.current = { type: message.type, time: now };
|
|
1192
|
+
const msgId = deriveMessageId(message);
|
|
1193
|
+
if (processedIdsRef.current.has(msgId)) return;
|
|
1194
|
+
processedIdsRef.current.add(msgId);
|
|
1195
|
+
setTimeout(() => processedIdsRef.current.delete(msgId), 5e3);
|
|
1080
1196
|
switch (message.type) {
|
|
1081
1197
|
case "ready":
|
|
1082
|
-
if (!paywallDataSent.current)
|
|
1198
|
+
if (!paywallDataSent.current) {
|
|
1199
|
+
buildAndSetInjectionScript();
|
|
1200
|
+
} else {
|
|
1201
|
+
onReadyRef.current?.();
|
|
1202
|
+
}
|
|
1083
1203
|
break;
|
|
1084
1204
|
case "purchase":
|
|
1085
1205
|
if (message.payload?.productId) onPurchase(message.payload.productId);
|
|
@@ -1094,7 +1214,7 @@ function PaywallWebView({
|
|
|
1094
1214
|
if (message.payload?.url) {
|
|
1095
1215
|
const url = message.payload.url;
|
|
1096
1216
|
if (/^https?:\/\//i.test(url) || /^mailto:/i.test(url)) {
|
|
1097
|
-
void
|
|
1217
|
+
void import_react_native5.Linking.openURL(url);
|
|
1098
1218
|
}
|
|
1099
1219
|
}
|
|
1100
1220
|
break;
|
|
@@ -1170,8 +1290,9 @@ function PaywallWebView({
|
|
|
1170
1290
|
if (!paywallDataSent.current) return;
|
|
1171
1291
|
setScriptToInject(buildPurchaseStateScript(isPurchasing));
|
|
1172
1292
|
}, [isPurchasing]);
|
|
1173
|
-
|
|
1174
|
-
|
|
1293
|
+
if (!resolvedWebUrl) return null;
|
|
1294
|
+
const paywallUrl = `${resolvedWebUrl}/paywall/${paywallId}`;
|
|
1295
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native5.View, { style: styles.container, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1175
1296
|
NativeWebView,
|
|
1176
1297
|
{
|
|
1177
1298
|
sourceUrl: paywallUrl,
|
|
@@ -1183,14 +1304,26 @@ function PaywallWebView({
|
|
|
1183
1304
|
}
|
|
1184
1305
|
) });
|
|
1185
1306
|
}
|
|
1186
|
-
var styles =
|
|
1307
|
+
var styles = import_react_native5.StyleSheet.create({
|
|
1187
1308
|
container: { flex: 1 },
|
|
1188
1309
|
webview: { flex: 1, backgroundColor: "transparent" }
|
|
1189
1310
|
});
|
|
1190
1311
|
|
|
1191
1312
|
// src/domains/paywall/PaywallModal.tsx
|
|
1192
1313
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1193
|
-
var SCREEN_HEIGHT2 =
|
|
1314
|
+
var SCREEN_HEIGHT2 = import_react_native6.Dimensions.get("window").height;
|
|
1315
|
+
function ErrorFallback({ description, onRetry, onClose }) {
|
|
1316
|
+
const overrides = PaywalloClient.getConfig()?.errorStrings;
|
|
1317
|
+
const title = overrides?.title ?? getSdkString("paywallErrorTitle");
|
|
1318
|
+
const retryLabel = overrides?.retry ?? getSdkString("paywallRetryButton");
|
|
1319
|
+
const closeLabel = overrides?.close ?? getSdkString("paywallCloseButton");
|
|
1320
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native6.View, { style: styles2.fallbackContainer, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native6.View, { style: styles2.fallbackCard, children: [
|
|
1321
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native6.Text, { style: styles2.fallbackTitle, children: title }),
|
|
1322
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native6.Text, { style: styles2.fallbackDescription, children: description }),
|
|
1323
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native6.Pressable, { style: styles2.retryButton, onPress: onRetry, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native6.Text, { style: styles2.retryButtonText, children: retryLabel }) }),
|
|
1324
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native6.Pressable, { style: styles2.closeButton, onPress: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native6.Text, { style: styles2.closeButtonText, children: closeLabel }) })
|
|
1325
|
+
] }) });
|
|
1326
|
+
}
|
|
1194
1327
|
function PaywallModal({
|
|
1195
1328
|
placement,
|
|
1196
1329
|
visible,
|
|
@@ -1208,7 +1341,13 @@ function PaywallModal({
|
|
|
1208
1341
|
variantKey,
|
|
1209
1342
|
campaignId
|
|
1210
1343
|
);
|
|
1211
|
-
const
|
|
1344
|
+
const openAnim = (0, import_react5.useRef)(new import_react_native6.Animated.Value(SCREEN_HEIGHT2)).current;
|
|
1345
|
+
const [modalVisible, setModalVisible] = (0, import_react5.useState)(false);
|
|
1346
|
+
const handleResult = (0, import_react5.useCallback)((result) => {
|
|
1347
|
+
setModalVisible(false);
|
|
1348
|
+
onResult(result);
|
|
1349
|
+
}, [onResult]);
|
|
1350
|
+
const { isPurchasing, slideAnim, handleClose, handlePurchase, handleRestore } = usePaywallActions(placement, paywallConfig, handleResult, variantKey, campaignId, openAnim);
|
|
1212
1351
|
const [webViewError, setWebViewError] = (0, import_react5.useState)(null);
|
|
1213
1352
|
const [retryCount, setRetryCount] = (0, import_react5.useState)(0);
|
|
1214
1353
|
const retryCountRef = (0, import_react5.useRef)(0);
|
|
@@ -1228,36 +1367,38 @@ function PaywallModal({
|
|
|
1228
1367
|
setWebViewError(null);
|
|
1229
1368
|
setRetryCount(0);
|
|
1230
1369
|
}, []);
|
|
1231
|
-
const openAnim = (0, import_react5.useRef)(new import_react_native5.Animated.Value(SCREEN_HEIGHT2)).current;
|
|
1232
1370
|
(0, import_react5.useEffect)(() => {
|
|
1233
1371
|
if (visible) {
|
|
1372
|
+
setModalVisible(true);
|
|
1234
1373
|
openAnim.setValue(SCREEN_HEIGHT2);
|
|
1235
|
-
|
|
1374
|
+
import_react_native6.Animated.timing(openAnim, {
|
|
1236
1375
|
toValue: 0,
|
|
1237
1376
|
duration: 550,
|
|
1238
|
-
easing:
|
|
1377
|
+
easing: import_react_native6.Easing.out(import_react_native6.Easing.cubic),
|
|
1239
1378
|
useNativeDriver: true
|
|
1240
1379
|
}).start();
|
|
1241
1380
|
}
|
|
1242
1381
|
}, [visible, openAnim]);
|
|
1243
|
-
if (!visible) return null;
|
|
1382
|
+
if (!modalVisible && !visible) return null;
|
|
1244
1383
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1245
|
-
|
|
1384
|
+
import_react_native6.Modal,
|
|
1246
1385
|
{
|
|
1247
|
-
visible,
|
|
1386
|
+
visible: modalVisible,
|
|
1248
1387
|
animationType: "none",
|
|
1249
1388
|
presentationStyle: "overFullScreen",
|
|
1250
1389
|
onRequestClose: handleClose,
|
|
1251
1390
|
transparent: true,
|
|
1252
1391
|
statusBarTranslucent: true,
|
|
1253
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1254
|
-
error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1255
|
-
!error && paywallConfig && "craftData" in paywallConfig.config && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: webViewError ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1392
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native6.View, { style: styles2.overlay, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native6.Animated.View, { style: [styles2.animatedContainer, { transform: [{ translateY: openAnim }] }], children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native6.View, { style: styles2.container, children: [
|
|
1393
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native6.View, { style: styles2.errorContainer, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native6.Text, { style: styles2.errorText, children: error.message }) }),
|
|
1394
|
+
!error && paywallConfig && "craftData" in paywallConfig.config && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: webViewError ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1395
|
+
ErrorFallback,
|
|
1396
|
+
{
|
|
1397
|
+
description: webViewError.description,
|
|
1398
|
+
onRetry: handleRetry,
|
|
1399
|
+
onClose: handleClose
|
|
1400
|
+
}
|
|
1401
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1261
1402
|
PaywallWebView,
|
|
1262
1403
|
{
|
|
1263
1404
|
paywallId: paywallConfig.id,
|
|
@@ -1277,7 +1418,7 @@ function PaywallModal({
|
|
|
1277
1418
|
}
|
|
1278
1419
|
);
|
|
1279
1420
|
}
|
|
1280
|
-
var styles2 =
|
|
1421
|
+
var styles2 = import_react_native6.StyleSheet.create({
|
|
1281
1422
|
overlay: { flex: 1, backgroundColor: "rgba(0,0,0,0.3)" },
|
|
1282
1423
|
animatedContainer: { flex: 1, backgroundColor: "#fff" },
|
|
1283
1424
|
container: { flex: 1, backgroundColor: "#fff" },
|
|
@@ -1342,7 +1483,11 @@ var PaywallErrorBoundary = class extends import_react6.Component {
|
|
|
1342
1483
|
return { hasError: true };
|
|
1343
1484
|
}
|
|
1344
1485
|
componentDidCatch(error) {
|
|
1345
|
-
if (PaywalloClient.getConfig()?.debug) console.
|
|
1486
|
+
if (PaywalloClient.getConfig()?.debug) console.error("[Paywallo] Paywall render error:", error);
|
|
1487
|
+
try {
|
|
1488
|
+
PaywalloClient.getApiClient()?.reportError("PAYWALL_RENDER_ERROR", error.message);
|
|
1489
|
+
} catch {
|
|
1490
|
+
}
|
|
1346
1491
|
}
|
|
1347
1492
|
render() {
|
|
1348
1493
|
return this.state.hasError ? null : this.props.children;
|
|
@@ -1365,7 +1510,7 @@ function usePaywallContext() {
|
|
|
1365
1510
|
|
|
1366
1511
|
// src/domains/paywall/usePaywallPresenter.ts
|
|
1367
1512
|
var import_react8 = require("react");
|
|
1368
|
-
function usePaywallPresenter(preloadedWebView,
|
|
1513
|
+
function usePaywallPresenter(preloadedWebView, setPreloadedWebView) {
|
|
1369
1514
|
const [showingPreloadedWebView, setShowingPreloadedWebView] = (0, import_react8.useState)(false);
|
|
1370
1515
|
const resolverRef = (0, import_react8.useRef)(null);
|
|
1371
1516
|
const presentedAtRef = (0, import_react8.useRef)(Date.now());
|
|
@@ -1414,9 +1559,8 @@ function usePaywallPresenter(preloadedWebView, clearPreloadedWebView, setPreload
|
|
|
1414
1559
|
};
|
|
1415
1560
|
void track();
|
|
1416
1561
|
setShowingPreloadedWebView(false);
|
|
1417
|
-
clearPreloadedWebView();
|
|
1418
1562
|
resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: true, restored: false });
|
|
1419
|
-
}, [preloadedWebView,
|
|
1563
|
+
}, [preloadedWebView, resolvePreloadedPaywall]);
|
|
1420
1564
|
return { showingPreloadedWebView, presentPreloadedWebView, resolvePreloadedPaywall, handlePreloadedWebViewReady, handlePreloadedClose, setShowingPreloadedWebView, presentedAtRef };
|
|
1421
1565
|
}
|
|
1422
1566
|
|
|
@@ -1624,9 +1768,7 @@ var AffiliateManager = class {
|
|
|
1624
1768
|
}
|
|
1625
1769
|
}
|
|
1626
1770
|
log(...args) {
|
|
1627
|
-
if (this.debug)
|
|
1628
|
-
console.warn("[AffiliateManager]", ...args);
|
|
1629
|
-
}
|
|
1771
|
+
if (this.debug) console.log("[Paywallo:Affiliate]", ...args);
|
|
1630
1772
|
}
|
|
1631
1773
|
};
|
|
1632
1774
|
var affiliateManager = new AffiliateManager();
|
|
@@ -1644,26 +1786,42 @@ var PaywalloAffiliate = {
|
|
|
1644
1786
|
};
|
|
1645
1787
|
|
|
1646
1788
|
// src/domains/subscription/SubscriptionCache.ts
|
|
1647
|
-
var
|
|
1789
|
+
var LEGACY_CACHE_KEY = "subscription_cache";
|
|
1790
|
+
var CACHE_KEY_PREFIX = "subscription_cache:";
|
|
1648
1791
|
var DEFAULT_TTL = 24 * 60 * 60 * 1e3;
|
|
1792
|
+
function keyFor(distinctId) {
|
|
1793
|
+
return `${CACHE_KEY_PREFIX}${distinctId}`;
|
|
1794
|
+
}
|
|
1649
1795
|
var SubscriptionCache = class {
|
|
1650
1796
|
constructor(ttl = DEFAULT_TTL) {
|
|
1651
|
-
this.memoryCache =
|
|
1797
|
+
this.memoryCache = /* @__PURE__ */ new Map();
|
|
1798
|
+
this.legacyCleanupDone = false;
|
|
1652
1799
|
this.ttl = ttl;
|
|
1653
1800
|
this.storage = new SecureStorage();
|
|
1654
1801
|
}
|
|
1655
|
-
async
|
|
1656
|
-
if (this.
|
|
1657
|
-
|
|
1802
|
+
async cleanupLegacyCache() {
|
|
1803
|
+
if (this.legacyCleanupDone) return;
|
|
1804
|
+
this.legacyCleanupDone = true;
|
|
1805
|
+
try {
|
|
1806
|
+
await this.storage.remove(LEGACY_CACHE_KEY);
|
|
1807
|
+
} catch {
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
async get(distinctId) {
|
|
1811
|
+
await this.cleanupLegacyCache();
|
|
1812
|
+
if (!distinctId) return null;
|
|
1813
|
+
const memEntry = this.memoryCache.get(distinctId);
|
|
1814
|
+
if (memEntry && !this.isExpired(memEntry)) {
|
|
1815
|
+
return memEntry;
|
|
1658
1816
|
}
|
|
1659
1817
|
try {
|
|
1660
|
-
const stored = await this.storage.get(
|
|
1818
|
+
const stored = await this.storage.get(keyFor(distinctId));
|
|
1661
1819
|
if (!stored) {
|
|
1662
1820
|
return null;
|
|
1663
1821
|
}
|
|
1664
1822
|
const parsed = JSON.parse(stored);
|
|
1665
1823
|
if (typeof parsed !== "object" || parsed === null || !("data" in parsed) || typeof parsed.data !== "object" || parsed.data === null || !("cachedAt" in parsed) || typeof parsed.cachedAt !== "number") {
|
|
1666
|
-
await this.storage.remove(
|
|
1824
|
+
await this.storage.remove(keyFor(distinctId));
|
|
1667
1825
|
return null;
|
|
1668
1826
|
}
|
|
1669
1827
|
const cached = parsed;
|
|
@@ -1692,32 +1850,39 @@ var SubscriptionCache = class {
|
|
|
1692
1850
|
}
|
|
1693
1851
|
cached.isStale = this.isExpired(cached);
|
|
1694
1852
|
if (!cached.isStale) {
|
|
1695
|
-
this.memoryCache
|
|
1853
|
+
this.memoryCache.set(distinctId, cached);
|
|
1696
1854
|
}
|
|
1697
1855
|
return cached;
|
|
1698
1856
|
} catch {
|
|
1699
1857
|
return null;
|
|
1700
1858
|
}
|
|
1701
1859
|
}
|
|
1702
|
-
async set(data) {
|
|
1860
|
+
async set(distinctId, data) {
|
|
1861
|
+
if (!distinctId) return;
|
|
1703
1862
|
const cached = {
|
|
1704
1863
|
data,
|
|
1705
1864
|
cachedAt: Date.now(),
|
|
1706
1865
|
isStale: false
|
|
1707
1866
|
};
|
|
1708
|
-
this.memoryCache
|
|
1867
|
+
this.memoryCache.set(distinctId, cached);
|
|
1709
1868
|
try {
|
|
1710
|
-
await this.storage.set(
|
|
1711
|
-
} catch
|
|
1712
|
-
if (__DEV__) console.warn("[SubscriptionCache] set() failed:", error instanceof Error ? error.message : String(error));
|
|
1869
|
+
await this.storage.set(keyFor(distinctId), JSON.stringify(cached));
|
|
1870
|
+
} catch {
|
|
1713
1871
|
}
|
|
1714
1872
|
}
|
|
1715
|
-
async invalidate() {
|
|
1716
|
-
|
|
1873
|
+
async invalidate(distinctId) {
|
|
1874
|
+
if (!distinctId) return;
|
|
1875
|
+
this.memoryCache.delete(distinctId);
|
|
1717
1876
|
try {
|
|
1718
|
-
await this.storage.remove(
|
|
1719
|
-
} catch
|
|
1720
|
-
|
|
1877
|
+
await this.storage.remove(keyFor(distinctId));
|
|
1878
|
+
} catch {
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
async invalidateAll() {
|
|
1882
|
+
this.memoryCache.clear();
|
|
1883
|
+
try {
|
|
1884
|
+
await this.storage.remove(LEGACY_CACHE_KEY);
|
|
1885
|
+
} catch {
|
|
1721
1886
|
}
|
|
1722
1887
|
}
|
|
1723
1888
|
isExpired(cached) {
|
|
@@ -1729,60 +1894,214 @@ var SubscriptionCache = class {
|
|
|
1729
1894
|
};
|
|
1730
1895
|
var subscriptionCache = new SubscriptionCache();
|
|
1731
1896
|
|
|
1732
|
-
// src/
|
|
1897
|
+
// src/domains/subscription/SubscriptionManager.ts
|
|
1733
1898
|
var import_react_native7 = require("react-native");
|
|
1734
1899
|
|
|
1735
|
-
// src/
|
|
1736
|
-
var
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1900
|
+
// src/domains/session/SessionError.ts
|
|
1901
|
+
var SessionError = class extends PaywalloError {
|
|
1902
|
+
constructor(code, message) {
|
|
1903
|
+
super("session", code, message);
|
|
1904
|
+
this.name = "SessionError";
|
|
1905
|
+
}
|
|
1906
|
+
};
|
|
1907
|
+
var SESSION_ERROR_CODES = {
|
|
1908
|
+
NOT_INITIALIZED: "SESSION_NOT_INITIALIZED",
|
|
1909
|
+
START_FAILED: "SESSION_START_FAILED",
|
|
1910
|
+
END_FAILED: "SESSION_END_FAILED",
|
|
1911
|
+
RESTORE_FAILED: "SESSION_RESTORE_FAILED"
|
|
1912
|
+
};
|
|
1913
|
+
|
|
1914
|
+
// src/domains/subscription/SubscriptionManager.ts
|
|
1915
|
+
var SubscriptionManagerClass = class {
|
|
1916
|
+
constructor() {
|
|
1917
|
+
this.config = null;
|
|
1918
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
1919
|
+
this.userId = null;
|
|
1920
|
+
this.cache = new SubscriptionCache();
|
|
1921
|
+
}
|
|
1922
|
+
cacheKey() {
|
|
1923
|
+
return this.userId ?? "__anonymous__";
|
|
1924
|
+
}
|
|
1925
|
+
init(config) {
|
|
1926
|
+
this.config = config;
|
|
1927
|
+
if (config.cacheTTL) {
|
|
1928
|
+
this.cache.setTTL(config.cacheTTL);
|
|
1743
1929
|
}
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1930
|
+
}
|
|
1931
|
+
setUserId(userId) {
|
|
1932
|
+
if (this.userId !== userId) {
|
|
1933
|
+
this.userId = userId;
|
|
1934
|
+
void this.cache.invalidateAll();
|
|
1747
1935
|
}
|
|
1748
|
-
return DEFAULT_WEB_URL;
|
|
1749
|
-
} catch {
|
|
1750
|
-
return DEFAULT_WEB_URL;
|
|
1751
1936
|
}
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1937
|
+
async hasActiveSubscription(forceRefresh = false) {
|
|
1938
|
+
const status = await this.getSubscriptionStatus(forceRefresh);
|
|
1939
|
+
return status.hasActiveSubscription;
|
|
1940
|
+
}
|
|
1941
|
+
async getSubscription(forceRefresh = false) {
|
|
1942
|
+
const status = await this.getSubscriptionStatus(forceRefresh);
|
|
1943
|
+
return status.subscription;
|
|
1944
|
+
}
|
|
1945
|
+
async getEntitlements(forceRefresh = false) {
|
|
1946
|
+
const status = await this.getSubscriptionStatus(forceRefresh);
|
|
1947
|
+
return status.entitlements;
|
|
1948
|
+
}
|
|
1949
|
+
async getSubscriptionStatus(forceRefresh = false) {
|
|
1950
|
+
if (!this.config) {
|
|
1951
|
+
return this.getEmptyStatus();
|
|
1952
|
+
}
|
|
1953
|
+
if (!forceRefresh) {
|
|
1954
|
+
const cached = await this.cache.get(this.cacheKey());
|
|
1955
|
+
if (cached && !cached.isStale) {
|
|
1956
|
+
return cached.data;
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
try {
|
|
1960
|
+
const status = await this.fetchSubscriptionStatus();
|
|
1961
|
+
await this.cache.set(this.cacheKey(), status);
|
|
1962
|
+
this.notifyListeners(status);
|
|
1963
|
+
return status;
|
|
1964
|
+
} catch (error) {
|
|
1965
|
+
this.log("Failed to fetch subscription status", error);
|
|
1966
|
+
const cached = await this.cache.get(this.cacheKey());
|
|
1967
|
+
if (cached) {
|
|
1968
|
+
return cached.data;
|
|
1969
|
+
}
|
|
1970
|
+
return this.getEmptyStatus();
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
async restorePurchases() {
|
|
1974
|
+
if (!this.config) {
|
|
1975
|
+
throw new SessionError(
|
|
1976
|
+
SESSION_ERROR_CODES.NOT_INITIALIZED,
|
|
1977
|
+
"SubscriptionManager not initialized"
|
|
1978
|
+
);
|
|
1979
|
+
}
|
|
1980
|
+
await this.cache.invalidate(this.cacheKey());
|
|
1981
|
+
return this.getSubscriptionStatus(true);
|
|
1982
|
+
}
|
|
1983
|
+
async fetchSubscriptionStatus() {
|
|
1984
|
+
if (!this.config) {
|
|
1985
|
+
return this.getEmptyStatus();
|
|
1986
|
+
}
|
|
1987
|
+
const url = new URL(`${this.config.serverUrl}/subscriptions/status`);
|
|
1988
|
+
if (this.userId) {
|
|
1989
|
+
url.searchParams.set("userId", this.userId);
|
|
1990
|
+
}
|
|
1991
|
+
url.searchParams.set("platform", import_react_native7.Platform.OS);
|
|
1992
|
+
const response = await fetch(url.toString(), {
|
|
1993
|
+
method: "GET",
|
|
1994
|
+
headers: {
|
|
1995
|
+
"Content-Type": "application/json",
|
|
1996
|
+
"X-App-Key": this.config.appKey
|
|
1997
|
+
}
|
|
1998
|
+
});
|
|
1999
|
+
if (!response.ok) {
|
|
2000
|
+
throw new SessionError(
|
|
2001
|
+
SESSION_ERROR_CODES.RESTORE_FAILED,
|
|
2002
|
+
`Failed to fetch subscription status: ${response.status}`
|
|
2003
|
+
);
|
|
2004
|
+
}
|
|
2005
|
+
const data = await response.json();
|
|
2006
|
+
if (data.subscription) {
|
|
2007
|
+
data.subscription.expiresAt = new Date(data.subscription.expiresAt);
|
|
2008
|
+
data.subscription.originalPurchaseDate = new Date(data.subscription.originalPurchaseDate);
|
|
2009
|
+
data.subscription.latestPurchaseDate = new Date(data.subscription.latestPurchaseDate);
|
|
2010
|
+
if (data.subscription.cancellationDate) {
|
|
2011
|
+
data.subscription.cancellationDate = new Date(data.subscription.cancellationDate);
|
|
2012
|
+
}
|
|
2013
|
+
if (data.subscription.gracePeriodExpiresAt) {
|
|
2014
|
+
data.subscription.gracePeriodExpiresAt = new Date(data.subscription.gracePeriodExpiresAt);
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
return data;
|
|
2018
|
+
}
|
|
2019
|
+
getEmptyStatus() {
|
|
2020
|
+
return {
|
|
2021
|
+
hasActiveSubscription: false,
|
|
2022
|
+
subscription: null,
|
|
2023
|
+
entitlements: []
|
|
2024
|
+
};
|
|
2025
|
+
}
|
|
2026
|
+
addListener(listener) {
|
|
2027
|
+
this.listeners.add(listener);
|
|
2028
|
+
return () => this.listeners.delete(listener);
|
|
2029
|
+
}
|
|
2030
|
+
notifyListeners(status) {
|
|
2031
|
+
for (const listener of this.listeners) {
|
|
2032
|
+
listener(status);
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
async onPurchaseComplete() {
|
|
2036
|
+
await this.cache.invalidate(this.cacheKey());
|
|
2037
|
+
await this.getSubscriptionStatus(true);
|
|
2038
|
+
}
|
|
2039
|
+
log(...args) {
|
|
2040
|
+
if (this.config?.debug) console.log("[Paywallo:Subscription]", ...args);
|
|
2041
|
+
}
|
|
2042
|
+
};
|
|
2043
|
+
var subscriptionManager = new SubscriptionManagerClass();
|
|
2044
|
+
|
|
2045
|
+
// src/core/ApiClient.ts
|
|
2046
|
+
var import_react_native9 = require("react-native");
|
|
2047
|
+
|
|
2048
|
+
// src/core/apiUrlUtils.ts
|
|
2049
|
+
var DEFAULT_WEB_URL = "https://paywallo.com.br";
|
|
2050
|
+
function deriveWebUrl(baseUrl) {
|
|
2051
|
+
try {
|
|
2052
|
+
const url = new URL(baseUrl);
|
|
2053
|
+
if (url.hostname.startsWith("192.168") || url.hostname === "localhost") {
|
|
2054
|
+
url.port = "3000";
|
|
2055
|
+
return url.toString().replace(/\/$/, "");
|
|
2056
|
+
}
|
|
2057
|
+
if (url.hostname.startsWith("api.")) {
|
|
2058
|
+
url.hostname = url.hostname.replace("api.", "app.");
|
|
2059
|
+
return url.toString().replace(/\/$/, "");
|
|
2060
|
+
}
|
|
2061
|
+
return DEFAULT_WEB_URL;
|
|
2062
|
+
} catch {
|
|
2063
|
+
return DEFAULT_WEB_URL;
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
function buildConditionalFlagUrl(baseUrl, flagKey, context) {
|
|
2067
|
+
const url = new URL(`${baseUrl}/api/v1/conditional-flags/${flagKey}`);
|
|
2068
|
+
if (context.platform) url.searchParams.set("platform", context.platform);
|
|
2069
|
+
if (context.appVersion) url.searchParams.set("appVersion", context.appVersion);
|
|
2070
|
+
if (context.country) url.searchParams.set("country", context.country);
|
|
2071
|
+
if (context.distinctId) url.searchParams.set("distinctId", context.distinctId);
|
|
2072
|
+
return url.toString();
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
// src/domains/iap/apiPurchaseMethods.ts
|
|
2076
|
+
async function validatePurchase(client, appKey, platform, receipt, productId, transactionId, priceLocal, currency, country, distinctId, paywallPlacement, variantKey) {
|
|
2077
|
+
const response = await client.post(
|
|
2078
|
+
`/purchases/validate/${appKey}`,
|
|
2079
|
+
{
|
|
2080
|
+
platform,
|
|
2081
|
+
receipt,
|
|
2082
|
+
productId,
|
|
2083
|
+
transactionId,
|
|
2084
|
+
priceLocal,
|
|
2085
|
+
currency,
|
|
2086
|
+
country,
|
|
2087
|
+
distinctId,
|
|
2088
|
+
...paywallPlacement !== void 0 && { paywallPlacement },
|
|
2089
|
+
...variantKey !== void 0 && { variantKey }
|
|
2090
|
+
},
|
|
2091
|
+
false
|
|
2092
|
+
);
|
|
2093
|
+
if (!response.ok) {
|
|
2094
|
+
throw new Error(`Server validation failed: HTTP ${response.status}`);
|
|
2095
|
+
}
|
|
2096
|
+
return response.data;
|
|
2097
|
+
}
|
|
2098
|
+
async function getSubscriptionStatus(client, baseUrl, appKey, distinctId) {
|
|
1783
2099
|
const url = new URL(`${baseUrl}/purchases/status/${appKey}`);
|
|
1784
2100
|
url.searchParams.set("distinctId", distinctId);
|
|
1785
2101
|
const response = await client.get(url.toString());
|
|
2102
|
+
if (!response.ok) {
|
|
2103
|
+
throw new Error(`Subscription status check failed: HTTP ${response.status}`);
|
|
2104
|
+
}
|
|
1786
2105
|
return response.data;
|
|
1787
2106
|
}
|
|
1788
2107
|
async function getEmergencyPaywall(client) {
|
|
@@ -1962,10 +2281,8 @@ var HttpClient = class {
|
|
|
1962
2281
|
sleep(ms) {
|
|
1963
2282
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1964
2283
|
}
|
|
1965
|
-
log(
|
|
1966
|
-
if (this.config.debug)
|
|
1967
|
-
console.warn("[HttpClient]", message, data ?? "");
|
|
1968
|
-
}
|
|
2284
|
+
log(...args) {
|
|
2285
|
+
if (this.config.debug) console.log("[Paywallo:Http]", ...args);
|
|
1969
2286
|
}
|
|
1970
2287
|
setDebug(debug) {
|
|
1971
2288
|
this.config.debug = debug;
|
|
@@ -1976,7 +2293,7 @@ var HttpClient = class {
|
|
|
1976
2293
|
};
|
|
1977
2294
|
|
|
1978
2295
|
// src/core/network/NetworkMonitor.ts
|
|
1979
|
-
var
|
|
2296
|
+
var import_react_native8 = require("react-native");
|
|
1980
2297
|
|
|
1981
2298
|
// src/core/network/types.ts
|
|
1982
2299
|
var DEFAULT_NETWORK_CONFIG = {
|
|
@@ -1991,8 +2308,6 @@ var NetworkMonitorClass = class {
|
|
|
1991
2308
|
this.config = DEFAULT_NETWORK_CONFIG;
|
|
1992
2309
|
this.listeners = /* @__PURE__ */ new Set();
|
|
1993
2310
|
this.currentState = "unknown";
|
|
1994
|
-
this.netInfoModule = null;
|
|
1995
|
-
this.netInfoSubscription = null;
|
|
1996
2311
|
this.appStateSubscription = null;
|
|
1997
2312
|
this.initialized = false;
|
|
1998
2313
|
this.checkIntervalId = null;
|
|
@@ -2008,13 +2323,6 @@ var NetworkMonitorClass = class {
|
|
|
2008
2323
|
void this.setupFallbackDetection();
|
|
2009
2324
|
this.setupAppStateListener();
|
|
2010
2325
|
}
|
|
2011
|
-
setupNetInfoListener() {
|
|
2012
|
-
if (!this.netInfoModule) return;
|
|
2013
|
-
this.netInfoSubscription = this.netInfoModule.addEventListener((state) => {
|
|
2014
|
-
const newState = this.netInfoStateToNetworkState(state);
|
|
2015
|
-
this.updateState(newState);
|
|
2016
|
-
});
|
|
2017
|
-
}
|
|
2018
2326
|
async setupFallbackDetection() {
|
|
2019
2327
|
await this.checkConnectivity();
|
|
2020
2328
|
this.checkIntervalId = setInterval(() => {
|
|
@@ -2022,7 +2330,7 @@ var NetworkMonitorClass = class {
|
|
|
2022
2330
|
}, this.config.checkInterval);
|
|
2023
2331
|
}
|
|
2024
2332
|
setupAppStateListener() {
|
|
2025
|
-
this.appStateSubscription =
|
|
2333
|
+
this.appStateSubscription = import_react_native8.AppState.addEventListener("change", (state) => {
|
|
2026
2334
|
if (state === "active") {
|
|
2027
2335
|
void this.checkConnectivity();
|
|
2028
2336
|
}
|
|
@@ -2042,15 +2350,6 @@ var NetworkMonitorClass = class {
|
|
|
2042
2350
|
this.updateState("offline");
|
|
2043
2351
|
}
|
|
2044
2352
|
}
|
|
2045
|
-
netInfoStateToNetworkState(state) {
|
|
2046
|
-
if (state.isConnected === null) {
|
|
2047
|
-
return "unknown";
|
|
2048
|
-
}
|
|
2049
|
-
if (!state.isConnected) {
|
|
2050
|
-
return "offline";
|
|
2051
|
-
}
|
|
2052
|
-
return "online";
|
|
2053
|
-
}
|
|
2054
2353
|
updateState(newState) {
|
|
2055
2354
|
const previousState = this.currentState;
|
|
2056
2355
|
if (previousState === newState) {
|
|
@@ -2093,10 +2392,6 @@ var NetworkMonitorClass = class {
|
|
|
2093
2392
|
return this.currentState;
|
|
2094
2393
|
}
|
|
2095
2394
|
dispose() {
|
|
2096
|
-
if (this.netInfoSubscription) {
|
|
2097
|
-
this.netInfoSubscription();
|
|
2098
|
-
this.netInfoSubscription = null;
|
|
2099
|
-
}
|
|
2100
2395
|
if (this.appStateSubscription) {
|
|
2101
2396
|
this.appStateSubscription.remove();
|
|
2102
2397
|
this.appStateSubscription = null;
|
|
@@ -2109,10 +2404,8 @@ var NetworkMonitorClass = class {
|
|
|
2109
2404
|
this.initialized = false;
|
|
2110
2405
|
this.currentState = "unknown";
|
|
2111
2406
|
}
|
|
2112
|
-
log(
|
|
2113
|
-
if (this.config.debug)
|
|
2114
|
-
console.warn("[NetworkMonitor]", message, data ?? "");
|
|
2115
|
-
}
|
|
2407
|
+
log(...args) {
|
|
2408
|
+
if (this.config.debug) console.log("[Paywallo:Network]", ...args);
|
|
2116
2409
|
}
|
|
2117
2410
|
setDebug(debug) {
|
|
2118
2411
|
this.config.debug = debug;
|
|
@@ -2122,6 +2415,7 @@ var networkMonitor = new NetworkMonitorClass();
|
|
|
2122
2415
|
|
|
2123
2416
|
// src/core/queue/OfflineQueue.ts
|
|
2124
2417
|
var import_async_storage2 = __toESM(require("@react-native-async-storage/async-storage"));
|
|
2418
|
+
init_uuid();
|
|
2125
2419
|
|
|
2126
2420
|
// src/core/queue/deduplication.ts
|
|
2127
2421
|
function normalizeForComparison(obj) {
|
|
@@ -2142,6 +2436,8 @@ function normalizeForComparison(obj) {
|
|
|
2142
2436
|
return "{" + pairs.join(",") + "}";
|
|
2143
2437
|
}
|
|
2144
2438
|
function findDuplicateIndex(queue, item) {
|
|
2439
|
+
const byId = queue.findIndex((existing) => existing.id === item.id);
|
|
2440
|
+
if (byId !== -1) return byId;
|
|
2145
2441
|
const normalizedBody = normalizeForComparison(item.body);
|
|
2146
2442
|
return queue.findIndex(
|
|
2147
2443
|
(existing) => existing.url === item.url && normalizeForComparison(existing.body) === normalizedBody
|
|
@@ -2210,9 +2506,9 @@ var OfflineQueueClass = class {
|
|
|
2210
2506
|
void this.saveToStorage();
|
|
2211
2507
|
}
|
|
2212
2508
|
}
|
|
2213
|
-
async enqueue(method, url, body, headers) {
|
|
2509
|
+
async enqueue(method, url, body, headers, id) {
|
|
2214
2510
|
const item = {
|
|
2215
|
-
id: generateUUID(),
|
|
2511
|
+
id: id ?? generateUUID(),
|
|
2216
2512
|
method,
|
|
2217
2513
|
url,
|
|
2218
2514
|
body,
|
|
@@ -2296,7 +2592,6 @@ var OfflineQueueClass = class {
|
|
|
2296
2592
|
this.emit({ type: "processing:end", queueSize: this.queue.length });
|
|
2297
2593
|
return { processed, failed };
|
|
2298
2594
|
}
|
|
2299
|
-
/** Returns true if the item was permanently removed (counted as failed). */
|
|
2300
2595
|
async handleItemFailure(item, error, permanent = false) {
|
|
2301
2596
|
if (permanent || item.attempts >= item.maxAttempts) {
|
|
2302
2597
|
await this.removeItem(item.id);
|
|
@@ -2358,10 +2653,8 @@ var OfflineQueueClass = class {
|
|
|
2358
2653
|
}
|
|
2359
2654
|
}
|
|
2360
2655
|
}
|
|
2361
|
-
log(
|
|
2362
|
-
if (this.config.debug)
|
|
2363
|
-
console.warn("[OfflineQueue]", message, data ?? "");
|
|
2364
|
-
}
|
|
2656
|
+
log(...args) {
|
|
2657
|
+
if (this.config.debug) console.log("[Paywallo:Queue]", ...args);
|
|
2365
2658
|
}
|
|
2366
2659
|
setDebug(debug) {
|
|
2367
2660
|
this.config.debug = debug;
|
|
@@ -2503,10 +2796,8 @@ var QueueProcessorClass = class {
|
|
|
2503
2796
|
this.httpClient = null;
|
|
2504
2797
|
this.initialized = false;
|
|
2505
2798
|
}
|
|
2506
|
-
log(
|
|
2507
|
-
if (this.config.debug)
|
|
2508
|
-
console.warn("[QueueProcessor]", message, data ?? "");
|
|
2509
|
-
}
|
|
2799
|
+
log(...args) {
|
|
2800
|
+
if (this.config.debug) console.log("[Paywallo:QueueProcessor]", ...args);
|
|
2510
2801
|
}
|
|
2511
2802
|
setDebug(debug) {
|
|
2512
2803
|
this.config.debug = debug;
|
|
@@ -2518,10 +2809,9 @@ var queueProcessor = new QueueProcessorClass();
|
|
|
2518
2809
|
var ApiCache = class {
|
|
2519
2810
|
constructor() {
|
|
2520
2811
|
this.CACHE_TTL = 3e5;
|
|
2521
|
-
// 5 minutes
|
|
2522
2812
|
this.CACHE_NULL_TTL = 3e4;
|
|
2523
|
-
// 30 seconds — for null/404 variants
|
|
2524
2813
|
this.MAX_SIZE = 200;
|
|
2814
|
+
this.STALE_WINDOW_MS = 864e5;
|
|
2525
2815
|
this.variantCache = /* @__PURE__ */ new Map();
|
|
2526
2816
|
this.paywallCache = /* @__PURE__ */ new Map();
|
|
2527
2817
|
this.campaignCache = /* @__PURE__ */ new Map();
|
|
@@ -2559,7 +2849,7 @@ var ApiCache = class {
|
|
|
2559
2849
|
}
|
|
2560
2850
|
getStaleVariant(key) {
|
|
2561
2851
|
const entry = this.variantCache.get(key);
|
|
2562
|
-
if (entry && Date.now() - entry.ts <
|
|
2852
|
+
if (entry && Date.now() - entry.ts < this.STALE_WINDOW_MS) return entry.value;
|
|
2563
2853
|
return void 0;
|
|
2564
2854
|
}
|
|
2565
2855
|
getPaywall(key) {
|
|
@@ -2571,42 +2861,42 @@ var ApiCache = class {
|
|
|
2571
2861
|
}
|
|
2572
2862
|
getStalePaywall(key) {
|
|
2573
2863
|
const entry = this.paywallCache.get(key);
|
|
2574
|
-
if (entry && Date.now() - entry.ts <
|
|
2864
|
+
if (entry && Date.now() - entry.ts < this.STALE_WINDOW_MS) return entry.value;
|
|
2575
2865
|
return void 0;
|
|
2576
2866
|
}
|
|
2577
2867
|
getCampaign(key) {
|
|
2578
2868
|
return this.getCached(this.campaignCache, key);
|
|
2579
2869
|
}
|
|
2580
|
-
setCampaign(key, value) {
|
|
2581
|
-
this.setCached(this.campaignCache, key, value);
|
|
2870
|
+
setCampaign(key, value, ttlOverride) {
|
|
2871
|
+
this.setCached(this.campaignCache, key, value, ttlOverride);
|
|
2582
2872
|
this.evictExpiredEntries(this.campaignCache);
|
|
2583
2873
|
}
|
|
2584
2874
|
getStaleCampaign(key) {
|
|
2585
2875
|
const entry = this.campaignCache.get(key);
|
|
2586
|
-
if (entry && Date.now() - entry.ts <
|
|
2876
|
+
if (entry && Date.now() - entry.ts < this.STALE_WINDOW_MS) return entry.value;
|
|
2587
2877
|
return void 0;
|
|
2588
2878
|
}
|
|
2589
2879
|
};
|
|
2590
2880
|
|
|
2591
2881
|
// src/core/ApiClient.ts
|
|
2592
|
-
var SDK_VERSION = "1.
|
|
2593
|
-
var DEFAULT_BASE_URL = "https://panel.lucasqueiroga.shop";
|
|
2882
|
+
var SDK_VERSION = "1.4.0";
|
|
2594
2883
|
var _ApiClient = class _ApiClient {
|
|
2595
|
-
constructor(appKey, debug = false, environment = "Production", offlineQueueEnabled = true, timeout = 3e4) {
|
|
2884
|
+
constructor(appKey, apiUrl, debug = false, environment = "Production", offlineQueueEnabled = true, timeout = 3e4) {
|
|
2596
2885
|
this.cache = new ApiCache();
|
|
2597
2886
|
this.appKey = appKey;
|
|
2598
|
-
this.baseUrl =
|
|
2887
|
+
this.baseUrl = apiUrl;
|
|
2599
2888
|
this.webUrl = deriveWebUrl(this.baseUrl);
|
|
2600
2889
|
this.debug = debug;
|
|
2601
2890
|
this.environment = environment;
|
|
2602
2891
|
this.offlineQueueEnabled = offlineQueueEnabled;
|
|
2603
2892
|
this.httpClient = new HttpClient(this.baseUrl, { timeout, debug });
|
|
2604
2893
|
}
|
|
2605
|
-
// ─── Infrastructure ──────────────────────────────────────────────────────────
|
|
2606
|
-
/** Exposed only for QueueProcessor initialization. Prefer get()/post() for requests. */
|
|
2607
2894
|
getHttpClient() {
|
|
2608
2895
|
return this.httpClient;
|
|
2609
2896
|
}
|
|
2897
|
+
getBaseUrl() {
|
|
2898
|
+
return this.baseUrl;
|
|
2899
|
+
}
|
|
2610
2900
|
getWebUrl() {
|
|
2611
2901
|
return this.webUrl;
|
|
2612
2902
|
}
|
|
@@ -2632,7 +2922,6 @@ var _ApiClient = class _ApiClient {
|
|
|
2632
2922
|
async clearQueue() {
|
|
2633
2923
|
await offlineQueue.clear();
|
|
2634
2924
|
}
|
|
2635
|
-
// ─── Public HTTP facade (headers injected automatically) ─────────────────────
|
|
2636
2925
|
async get(path) {
|
|
2637
2926
|
return this.httpClient.get(path, { headers: { "X-App-Key": this.appKey } });
|
|
2638
2927
|
}
|
|
@@ -2646,29 +2935,29 @@ var _ApiClient = class _ApiClient {
|
|
|
2646
2935
|
message,
|
|
2647
2936
|
context,
|
|
2648
2937
|
sdkVersion: SDK_VERSION,
|
|
2649
|
-
platform:
|
|
2938
|
+
platform: import_react_native9.Platform.OS
|
|
2650
2939
|
}, true);
|
|
2651
|
-
} catch {
|
|
2940
|
+
} catch (err) {
|
|
2941
|
+
if (this.debug) console.warn("[Paywallo:ApiClient] reportError failed", err);
|
|
2652
2942
|
}
|
|
2653
2943
|
}
|
|
2654
|
-
// ─── Business methods ────────────────────────────────────────────────────────
|
|
2655
2944
|
async trackEvent(eventName, distinctId, properties, timestamp) {
|
|
2656
2945
|
await this.postWithQueue("/api/v1/events", {
|
|
2657
2946
|
eventName,
|
|
2658
2947
|
distinctId,
|
|
2659
|
-
properties: { ...properties, platform:
|
|
2948
|
+
properties: { ...properties, platform: import_react_native9.Platform.OS },
|
|
2660
2949
|
timestamp: timestamp ?? Date.now(),
|
|
2661
2950
|
environment: this.environment.toLowerCase()
|
|
2662
2951
|
}, `event:${eventName}`);
|
|
2663
2952
|
}
|
|
2664
|
-
async identify(distinctId, properties, email) {
|
|
2665
|
-
await this.postWithQueue("/api/v1/identify", { distinctId, properties: properties ?? {}, email, platform:
|
|
2953
|
+
async identify(distinctId, properties, email, deviceId) {
|
|
2954
|
+
await this.postWithQueue("/api/v1/identify", { distinctId, properties: properties ?? {}, email, platform: import_react_native9.Platform.OS, ...deviceId && { deviceId } }, "identify");
|
|
2666
2955
|
}
|
|
2667
2956
|
async startSession(distinctId, sessionId, platform) {
|
|
2668
2957
|
await this.postWithQueue("/api/v1/sessions/start", {
|
|
2669
2958
|
distinctId,
|
|
2670
2959
|
sessionId,
|
|
2671
|
-
platform: platform ??
|
|
2960
|
+
platform: platform ?? import_react_native9.Platform.OS,
|
|
2672
2961
|
environment: this.environment.toLowerCase()
|
|
2673
2962
|
}, `session.start:${sessionId}`);
|
|
2674
2963
|
return { success: true };
|
|
@@ -2746,7 +3035,7 @@ var _ApiClient = class _ApiClient {
|
|
|
2746
3035
|
const result = await getCampaignData(this, this.baseUrl, placement, distinctId, context);
|
|
2747
3036
|
if (!result) {
|
|
2748
3037
|
this.log("No campaign found for placement:", placement);
|
|
2749
|
-
this.cache.setCampaign(key, null);
|
|
3038
|
+
this.cache.setCampaign(key, null, this.cache.nullTTL);
|
|
2750
3039
|
return null;
|
|
2751
3040
|
}
|
|
2752
3041
|
this.log("Got campaign:", placement, result.variantKey);
|
|
@@ -2759,6 +3048,32 @@ var _ApiClient = class _ApiClient {
|
|
|
2759
3048
|
return null;
|
|
2760
3049
|
}
|
|
2761
3050
|
}
|
|
3051
|
+
async getPrimaryCampaign(distinctId) {
|
|
3052
|
+
const key = `primary:${distinctId}`;
|
|
3053
|
+
const hit = this.cache.getCampaign(key);
|
|
3054
|
+
if (hit !== void 0) return hit;
|
|
3055
|
+
try {
|
|
3056
|
+
const res = await this.get(`/campaigns/public/primary?distinctId=${encodeURIComponent(distinctId)}`);
|
|
3057
|
+
if (res.status === 404) {
|
|
3058
|
+
this.cache.setCampaign(key, null, this.cache.nullTTL);
|
|
3059
|
+
return null;
|
|
3060
|
+
}
|
|
3061
|
+
if (!res.ok) {
|
|
3062
|
+
this.log("Non-OK response for primary campaign:", res.status);
|
|
3063
|
+
const stale = this.cache.getStaleCampaign(key);
|
|
3064
|
+
if (stale !== void 0) return stale;
|
|
3065
|
+
return null;
|
|
3066
|
+
}
|
|
3067
|
+
this.log("Got primary campaign:", res.data?.campaignId);
|
|
3068
|
+
this.cache.setCampaign(key, res.data);
|
|
3069
|
+
return res.data;
|
|
3070
|
+
} catch (error) {
|
|
3071
|
+
this.log("Failed to get primary campaign:", error);
|
|
3072
|
+
const stale = this.cache.getStaleCampaign(key);
|
|
3073
|
+
if (stale !== void 0) return stale;
|
|
3074
|
+
return null;
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
2762
3077
|
async validatePurchase(platform, receipt, productId, transactionId, priceLocal, currency, country, distinctId, paywallPlacement, variantKey) {
|
|
2763
3078
|
const result = await validatePurchase(
|
|
2764
3079
|
this,
|
|
@@ -2827,12 +3142,6 @@ var _ApiClient = class _ApiClient {
|
|
|
2827
3142
|
return { value: false, flagKey };
|
|
2828
3143
|
}
|
|
2829
3144
|
}
|
|
2830
|
-
// ─── Cache-only reads ────────────────────────────────────────────────────────
|
|
2831
|
-
/**
|
|
2832
|
-
* Reads a flag variant from cache layers only — no network call.
|
|
2833
|
-
* Checks in-memory cache first, then SecureStorage.
|
|
2834
|
-
* Returns null when both layers miss.
|
|
2835
|
-
*/
|
|
2836
3145
|
async getVariantFromCache(flagKey, distinctId) {
|
|
2837
3146
|
const key = `${flagKey}:${distinctId}`;
|
|
2838
3147
|
const hit = this.cache.getVariant(key);
|
|
@@ -2861,13 +3170,10 @@ var _ApiClient = class _ApiClient {
|
|
|
2861
3170
|
try {
|
|
2862
3171
|
const value = JSON.stringify({ variant: flag.variant, payload: flag.payload, cachedAt: Date.now() });
|
|
2863
3172
|
await secureStorage.set(`flag:${flagKey}:${distinctId}`, value);
|
|
2864
|
-
} catch {
|
|
3173
|
+
} catch (err) {
|
|
3174
|
+
this.log("writeFlagToStorage failed", err);
|
|
2865
3175
|
}
|
|
2866
3176
|
}
|
|
2867
|
-
/**
|
|
2868
|
-
* POST with offline queue fallback. Enqueues when offline or on network failure.
|
|
2869
|
-
* Throws only when offlineQueueEnabled is false and the request fails.
|
|
2870
|
-
*/
|
|
2871
3177
|
async postWithQueue(url, payload, label) {
|
|
2872
3178
|
if (this.offlineQueueEnabled && !networkMonitor.isOnline()) {
|
|
2873
3179
|
await offlineQueue.enqueue("POST", url, payload, { "X-App-Key": this.appKey });
|
|
@@ -2887,10 +3193,9 @@ var _ApiClient = class _ApiClient {
|
|
|
2887
3193
|
}
|
|
2888
3194
|
}
|
|
2889
3195
|
log(...args) {
|
|
2890
|
-
if (this.debug) console.
|
|
3196
|
+
if (this.debug) console.log("[Paywallo:ApiClient]", ...args);
|
|
2891
3197
|
}
|
|
2892
3198
|
};
|
|
2893
|
-
// ─── Private helpers ─────────────────────────────────────────────────────────
|
|
2894
3199
|
_ApiClient.FLAG_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
2895
3200
|
var ApiClient = _ApiClient;
|
|
2896
3201
|
|
|
@@ -2911,42 +3216,72 @@ var CAMPAIGN_ERROR_CODES = {
|
|
|
2911
3216
|
|
|
2912
3217
|
// src/domains/campaign/CampaignGateService.ts
|
|
2913
3218
|
var PRELOAD_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
3219
|
+
var PRELOAD_WAIT_MAX_MS = 2e3;
|
|
3220
|
+
var PRELOAD_WAIT_INTERVAL_MS = 100;
|
|
2914
3221
|
var CampaignGateService = class {
|
|
2915
3222
|
constructor() {
|
|
2916
3223
|
this.preloadedCampaigns = /* @__PURE__ */ new Map();
|
|
3224
|
+
this.pendingPreloads = /* @__PURE__ */ new Set();
|
|
2917
3225
|
}
|
|
2918
3226
|
async getCampaign(apiClient, placement, context) {
|
|
2919
3227
|
const distinctId = identityManager.getDistinctId();
|
|
2920
3228
|
if (!distinctId) return null;
|
|
2921
3229
|
return apiClient.getCampaign(placement, distinctId, context);
|
|
2922
3230
|
}
|
|
3231
|
+
async waitForPreload(placement) {
|
|
3232
|
+
if (!this.pendingPreloads.has(placement)) return;
|
|
3233
|
+
const deadline = Date.now() + PRELOAD_WAIT_MAX_MS;
|
|
3234
|
+
await new Promise((resolve) => {
|
|
3235
|
+
const check = () => {
|
|
3236
|
+
if (!this.pendingPreloads.has(placement) || Date.now() >= deadline) {
|
|
3237
|
+
resolve();
|
|
3238
|
+
return;
|
|
3239
|
+
}
|
|
3240
|
+
setTimeout(check, PRELOAD_WAIT_INTERVAL_MS);
|
|
3241
|
+
};
|
|
3242
|
+
check();
|
|
3243
|
+
});
|
|
3244
|
+
}
|
|
2923
3245
|
async presentCampaign(apiClient, placement, hasActive, paywallPresenter, campaignPresenter, context) {
|
|
2924
|
-
|
|
3246
|
+
await this.waitForPreload(placement);
|
|
3247
|
+
const preloaded = this.getPreloadedCampaign(placement);
|
|
3248
|
+
const campaign = preloaded ?? await this.getCampaign(apiClient, placement, context);
|
|
2925
3249
|
if (!campaign) {
|
|
2926
3250
|
return { presented: false, purchased: false, cancelled: false, restored: false, error: new CampaignError(CAMPAIGN_ERROR_CODES.NOT_FOUND, "No campaign found") };
|
|
2927
3251
|
}
|
|
2928
|
-
if (!context?.forceShow
|
|
2929
|
-
|
|
3252
|
+
if (!context?.forceShow) {
|
|
3253
|
+
const isActive = await hasActive();
|
|
3254
|
+
if (isActive) {
|
|
3255
|
+
return { presented: false, purchased: false, cancelled: false, restored: false, skippedReason: "subscriber" };
|
|
3256
|
+
}
|
|
2930
3257
|
}
|
|
2931
3258
|
if (campaignPresenter) return campaignPresenter(campaign);
|
|
2932
3259
|
if (!paywallPresenter) throw new PaywallError(PAYWALL_ERROR_CODES.NOT_INITIALIZED, "PaywalloProvider not mounted. Wrap your app with <PaywalloProvider>");
|
|
2933
|
-
return { ...await paywallPresenter(
|
|
3260
|
+
return { ...await paywallPresenter(placement), campaignId: campaign.campaignId, variantKey: campaign.variantKey };
|
|
2934
3261
|
}
|
|
2935
3262
|
async requireSubscriptionWithCampaign(apiClient, placement, hasActive, paywallPresenter, campaignPresenter, context) {
|
|
2936
|
-
|
|
3263
|
+
const isActive = await hasActive();
|
|
3264
|
+
if (PaywalloClient.getConfig()?.debug) console.info(`[Paywallo] requireSubscription \u2014 hasActive: ${isActive}`);
|
|
3265
|
+
if (isActive) return true;
|
|
2937
3266
|
const r = await this.presentCampaign(apiClient, placement, hasActive, paywallPresenter, campaignPresenter, context);
|
|
2938
3267
|
return r.purchased || r.restored;
|
|
2939
3268
|
}
|
|
2940
3269
|
async preloadCampaign(apiClient, placement, context) {
|
|
3270
|
+
this.pendingPreloads.add(placement);
|
|
2941
3271
|
try {
|
|
2942
3272
|
const campaign = await this.getCampaign(apiClient, placement, context);
|
|
2943
3273
|
if (!campaign?.paywall) {
|
|
2944
3274
|
return { success: false, error: new CampaignError(CAMPAIGN_ERROR_CODES.NOT_FOUND, `Campaign or paywall not found: ${placement}`) };
|
|
2945
3275
|
}
|
|
2946
3276
|
this.preloadedCampaigns.set(placement, { campaign, preloadedAt: Date.now() });
|
|
3277
|
+
if (PaywalloClient.getConfig()?.debug) {
|
|
3278
|
+
console.info(`[Paywallo PRELOAD] data cached \u2014 placement: ${placement}`);
|
|
3279
|
+
}
|
|
2947
3280
|
return { success: true };
|
|
2948
3281
|
} catch (error) {
|
|
2949
3282
|
return { success: false, error: error instanceof Error ? error : new PaywalloError("panel", "PANEL_UNKNOWN", String(error)) };
|
|
3283
|
+
} finally {
|
|
3284
|
+
this.pendingPreloads.delete(placement);
|
|
2950
3285
|
}
|
|
2951
3286
|
}
|
|
2952
3287
|
getPreloadedCampaign(placement) {
|
|
@@ -2960,12 +3295,13 @@ var CampaignGateService = class {
|
|
|
2960
3295
|
}
|
|
2961
3296
|
clearPreloaded() {
|
|
2962
3297
|
this.preloadedCampaigns.clear();
|
|
3298
|
+
this.pendingPreloads.clear();
|
|
2963
3299
|
}
|
|
2964
3300
|
};
|
|
2965
3301
|
var campaignGateService = new CampaignGateService();
|
|
2966
3302
|
|
|
2967
3303
|
// src/domains/identity/AdvertisingIdManager.ts
|
|
2968
|
-
var
|
|
3304
|
+
var import_react_native10 = require("react-native");
|
|
2969
3305
|
var ZERO_IDFA = "00000000-0000-0000-0000-000000000000";
|
|
2970
3306
|
var AdvertisingIdManager = class {
|
|
2971
3307
|
constructor() {
|
|
@@ -2976,7 +3312,7 @@ var AdvertisingIdManager = class {
|
|
|
2976
3312
|
const fallback = { idfv: null, idfa: null, gaid: null, attStatus: "unavailable" };
|
|
2977
3313
|
try {
|
|
2978
3314
|
const tracking = await import("expo-tracking-transparency");
|
|
2979
|
-
if (
|
|
3315
|
+
if (import_react_native10.Platform.OS === "ios") {
|
|
2980
3316
|
if (!tracking.isAvailable()) {
|
|
2981
3317
|
const idfv2 = await this.collectIdfv();
|
|
2982
3318
|
this.cached = { ...fallback, idfv: idfv2 };
|
|
@@ -2995,7 +3331,7 @@ var AdvertisingIdManager = class {
|
|
|
2995
3331
|
this.cached = { idfv, idfa, gaid: null, attStatus: status };
|
|
2996
3332
|
return this.cached;
|
|
2997
3333
|
}
|
|
2998
|
-
if (
|
|
3334
|
+
if (import_react_native10.Platform.OS === "android") {
|
|
2999
3335
|
const androidStatus = await tracking.getTrackingPermissionsAsync();
|
|
3000
3336
|
const optedIn = androidStatus.granted;
|
|
3001
3337
|
const rawGaid = optedIn ? tracking.getAdvertisingId() : null;
|
|
@@ -3025,43 +3361,67 @@ var AdvertisingIdManager = class {
|
|
|
3025
3361
|
}
|
|
3026
3362
|
};
|
|
3027
3363
|
|
|
3028
|
-
// src/domains/identity/
|
|
3364
|
+
// src/domains/identity/InstallTracker.ts
|
|
3365
|
+
var import_react_native14 = require("react-native");
|
|
3366
|
+
var import_async_storage3 = __toESM(require("@react-native-async-storage/async-storage"));
|
|
3367
|
+
var import_react_native_device_info2 = __toESM(require("react-native-device-info"));
|
|
3368
|
+
|
|
3369
|
+
// src/domains/identity/MetaBridge.ts
|
|
3370
|
+
var import_react_native11 = require("react-native");
|
|
3029
3371
|
var TIMEOUT_MS = 2e3;
|
|
3030
|
-
var
|
|
3031
|
-
|
|
3372
|
+
var NativeBridge = import_react_native11.NativeModules.PaywalloFBBridge ?? null;
|
|
3373
|
+
var MetaBridge = class {
|
|
3374
|
+
async getAnonymousID() {
|
|
3375
|
+
if (!NativeBridge) return null;
|
|
3032
3376
|
try {
|
|
3033
3377
|
let timerId;
|
|
3034
|
-
const
|
|
3378
|
+
const timeout = new Promise((resolve) => {
|
|
3035
3379
|
timerId = setTimeout(() => resolve(null), TIMEOUT_MS);
|
|
3036
3380
|
});
|
|
3037
|
-
const result = await Promise.race([
|
|
3381
|
+
const result = await Promise.race([NativeBridge.getAnonymousID(), timeout]);
|
|
3038
3382
|
clearTimeout(timerId);
|
|
3039
3383
|
return result;
|
|
3040
3384
|
} catch {
|
|
3041
3385
|
return null;
|
|
3042
3386
|
}
|
|
3043
3387
|
}
|
|
3044
|
-
async
|
|
3388
|
+
async logEvent(name, params = {}) {
|
|
3389
|
+
if (!NativeBridge) return;
|
|
3045
3390
|
try {
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3391
|
+
await NativeBridge.logEvent(name, params);
|
|
3392
|
+
} catch {
|
|
3393
|
+
}
|
|
3394
|
+
}
|
|
3395
|
+
async logPurchase(amount, currency, params = {}) {
|
|
3396
|
+
if (!NativeBridge) return;
|
|
3397
|
+
try {
|
|
3398
|
+
await NativeBridge.logPurchase(amount, currency, params);
|
|
3399
|
+
} catch {
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
setUserID(userId) {
|
|
3403
|
+
if (!NativeBridge) return;
|
|
3404
|
+
try {
|
|
3405
|
+
NativeBridge.setUserID(userId);
|
|
3406
|
+
} catch {
|
|
3407
|
+
}
|
|
3408
|
+
}
|
|
3409
|
+
flush() {
|
|
3410
|
+
if (!NativeBridge) return;
|
|
3411
|
+
try {
|
|
3412
|
+
NativeBridge.flush();
|
|
3049
3413
|
} catch {
|
|
3050
|
-
return null;
|
|
3051
3414
|
}
|
|
3052
3415
|
}
|
|
3053
3416
|
};
|
|
3054
|
-
var
|
|
3055
|
-
|
|
3056
|
-
// src/domains/identity/InstallTracker.ts
|
|
3057
|
-
var import_react_native11 = require("react-native");
|
|
3417
|
+
var metaBridge = new MetaBridge();
|
|
3058
3418
|
|
|
3059
3419
|
// src/domains/identity/InstallReferrerManager.ts
|
|
3060
|
-
var
|
|
3420
|
+
var import_react_native12 = require("react-native");
|
|
3061
3421
|
var REFERRER_TIMEOUT_MS = 5e3;
|
|
3062
3422
|
var InstallReferrerManager = class {
|
|
3063
3423
|
async getReferrer() {
|
|
3064
|
-
if (
|
|
3424
|
+
if (import_react_native12.Platform.OS !== "android") {
|
|
3065
3425
|
return this.emptyResult();
|
|
3066
3426
|
}
|
|
3067
3427
|
let timerId;
|
|
@@ -3121,21 +3481,8 @@ var InstallReferrerManager = class {
|
|
|
3121
3481
|
var installReferrerManager = new InstallReferrerManager();
|
|
3122
3482
|
|
|
3123
3483
|
// src/domains/session/SessionManager.ts
|
|
3124
|
-
var
|
|
3125
|
-
|
|
3126
|
-
// src/domains/session/SessionError.ts
|
|
3127
|
-
var SessionError = class extends PaywalloError {
|
|
3128
|
-
constructor(code, message) {
|
|
3129
|
-
super("session", code, message);
|
|
3130
|
-
this.name = "SessionError";
|
|
3131
|
-
}
|
|
3132
|
-
};
|
|
3133
|
-
var SESSION_ERROR_CODES = {
|
|
3134
|
-
NOT_INITIALIZED: "SESSION_NOT_INITIALIZED",
|
|
3135
|
-
START_FAILED: "SESSION_START_FAILED",
|
|
3136
|
-
END_FAILED: "SESSION_END_FAILED",
|
|
3137
|
-
RESTORE_FAILED: "SESSION_RESTORE_FAILED"
|
|
3138
|
-
};
|
|
3484
|
+
var import_react_native13 = require("react-native");
|
|
3485
|
+
init_uuid();
|
|
3139
3486
|
|
|
3140
3487
|
// src/domains/session/sessionTracking.ts
|
|
3141
3488
|
async function trackSessionStart(apiClient, sessionId, sessionStartedAt) {
|
|
@@ -3303,7 +3650,7 @@ var SessionManager = class {
|
|
|
3303
3650
|
if (this.appStateSubscription) {
|
|
3304
3651
|
this.appStateSubscription.remove();
|
|
3305
3652
|
}
|
|
3306
|
-
this.appStateSubscription =
|
|
3653
|
+
this.appStateSubscription = import_react_native13.AppState.addEventListener("change", this.handleAppStateChange);
|
|
3307
3654
|
}
|
|
3308
3655
|
async handleForeground() {
|
|
3309
3656
|
if (this.backgroundTimestamp) {
|
|
@@ -3365,9 +3712,7 @@ var SessionManager = class {
|
|
|
3365
3712
|
}
|
|
3366
3713
|
}
|
|
3367
3714
|
log(...args) {
|
|
3368
|
-
if (this.debug)
|
|
3369
|
-
console.warn("[Paywallo Session]", ...args);
|
|
3370
|
-
}
|
|
3715
|
+
if (this.debug) console.log("[Paywallo:Session]", ...args);
|
|
3371
3716
|
}
|
|
3372
3717
|
};
|
|
3373
3718
|
var sessionManager = new SessionManager();
|
|
@@ -3380,12 +3725,11 @@ var InstallTracker = class {
|
|
|
3380
3725
|
}
|
|
3381
3726
|
async trackIfNeeded(apiClient, requestATT = false) {
|
|
3382
3727
|
const storage = new SecureStorage(this.debug);
|
|
3383
|
-
const alreadyTracked = await storage.get("install_tracked");
|
|
3728
|
+
const alreadyTracked = await storage.get("@panel:install_tracked");
|
|
3384
3729
|
if (alreadyTracked) return;
|
|
3385
3730
|
let fallbackTracked = null;
|
|
3386
3731
|
try {
|
|
3387
|
-
|
|
3388
|
-
fallbackTracked = await AsyncStorage3.getItem("@paywallo:install_tracked");
|
|
3732
|
+
fallbackTracked = await import_async_storage3.default.getItem("@paywallo:install_tracked");
|
|
3389
3733
|
} catch {
|
|
3390
3734
|
}
|
|
3391
3735
|
if (fallbackTracked) return;
|
|
@@ -3393,16 +3737,10 @@ var InstallTracker = class {
|
|
|
3393
3737
|
const sessionId = sessionManager.getSessionId();
|
|
3394
3738
|
const installedAt = Date.now();
|
|
3395
3739
|
if (!distinctId) return;
|
|
3396
|
-
|
|
3397
|
-
try {
|
|
3398
|
-
const mod = await import("react-native-device-info");
|
|
3399
|
-
deviceInfo = mod.default;
|
|
3400
|
-
} catch {
|
|
3401
|
-
deviceInfo = null;
|
|
3402
|
-
}
|
|
3740
|
+
const deviceInfo = import_react_native_device_info2.default;
|
|
3403
3741
|
let deviceData = {};
|
|
3404
3742
|
try {
|
|
3405
|
-
const screen =
|
|
3743
|
+
const screen = import_react_native14.Dimensions.get("screen");
|
|
3406
3744
|
const screenWidth = Math.round(screen.width ?? 0);
|
|
3407
3745
|
const screenHeight = Math.round(screen.height ?? 0);
|
|
3408
3746
|
const locale = Intl.DateTimeFormat().resolvedOptions().locale ?? "unknown";
|
|
@@ -3411,33 +3749,80 @@ var InstallTracker = class {
|
|
|
3411
3749
|
const osVersion = deviceInfo?.getSystemVersion() || "unknown";
|
|
3412
3750
|
const appVersion = deviceInfo?.getVersion() || "unknown";
|
|
3413
3751
|
const buildNumber = deviceInfo?.getBuildNumber() || "0";
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
buildNumber,
|
|
3419
|
-
...screenWidth > 0 && { screenWidth },
|
|
3420
|
-
...screenHeight > 0 && { screenHeight },
|
|
3421
|
-
timezone,
|
|
3422
|
-
locale
|
|
3423
|
-
};
|
|
3424
|
-
} catch {
|
|
3425
|
-
if (this.debug) {
|
|
3426
|
-
console.warn("[Paywallo] Failed to collect device data for install event");
|
|
3752
|
+
let carrier = "unknown";
|
|
3753
|
+
try {
|
|
3754
|
+
carrier = await deviceInfo?.getCarrier?.() ?? "unknown";
|
|
3755
|
+
} catch {
|
|
3427
3756
|
}
|
|
3757
|
+
let totalDisk = 0;
|
|
3758
|
+
try {
|
|
3759
|
+
totalDisk = Number(await deviceInfo?.getTotalDiskCapacity?.()) || 0;
|
|
3760
|
+
} catch {
|
|
3761
|
+
}
|
|
3762
|
+
let freeDisk = 0;
|
|
3763
|
+
try {
|
|
3764
|
+
freeDisk = Number(await deviceInfo?.getFreeDiskStorage?.()) || 0;
|
|
3765
|
+
} catch {
|
|
3766
|
+
}
|
|
3767
|
+
let totalRam = 0;
|
|
3768
|
+
try {
|
|
3769
|
+
totalRam = Number(await deviceInfo?.getTotalMemory?.()) || 0;
|
|
3770
|
+
} catch {
|
|
3771
|
+
}
|
|
3772
|
+
let brand = "unknown";
|
|
3773
|
+
try {
|
|
3774
|
+
brand = deviceInfo?.getBrand?.() ?? "unknown";
|
|
3775
|
+
} catch {
|
|
3776
|
+
}
|
|
3777
|
+
let installerPackage = null;
|
|
3778
|
+
try {
|
|
3779
|
+
if (import_react_native14.Platform.OS === "android") {
|
|
3780
|
+
installerPackage = await deviceInfo?.getInstallerPackageName?.() ?? null;
|
|
3781
|
+
}
|
|
3782
|
+
} catch {
|
|
3783
|
+
}
|
|
3784
|
+
deviceData = {
|
|
3785
|
+
deviceModel,
|
|
3786
|
+
osVersion,
|
|
3787
|
+
appVersion,
|
|
3788
|
+
buildNumber,
|
|
3789
|
+
...screenWidth > 0 && { screenWidth },
|
|
3790
|
+
...screenHeight > 0 && { screenHeight },
|
|
3791
|
+
timezone,
|
|
3792
|
+
locale,
|
|
3793
|
+
...carrier !== "unknown" && { carrier },
|
|
3794
|
+
...totalDisk > 0 && { totalDisk },
|
|
3795
|
+
...freeDisk > 0 && { freeDisk },
|
|
3796
|
+
...totalRam > 0 && { totalRam },
|
|
3797
|
+
...brand !== "unknown" && { brand },
|
|
3798
|
+
...installerPackage && { installerPackage }
|
|
3799
|
+
};
|
|
3800
|
+
} catch {
|
|
3428
3801
|
}
|
|
3429
3802
|
const [fbAnonId, referrer, adIds] = await Promise.all([
|
|
3430
|
-
|
|
3803
|
+
metaBridge.getAnonymousID(),
|
|
3431
3804
|
installReferrerManager.getReferrer(),
|
|
3432
3805
|
this.advertisingIdManager.collect(requestATT)
|
|
3433
3806
|
]);
|
|
3807
|
+
let effectiveAnonId = fbAnonId;
|
|
3808
|
+
if (!effectiveAnonId) {
|
|
3809
|
+
const stored = await storage.get("@panel:anon_id");
|
|
3810
|
+
if (stored) {
|
|
3811
|
+
effectiveAnonId = stored;
|
|
3812
|
+
} else {
|
|
3813
|
+
const { generateUUID: generateUUID2 } = await Promise.resolve().then(() => (init_uuid(), uuid_exports));
|
|
3814
|
+
effectiveAnonId = `PW_${generateUUID2()}`;
|
|
3815
|
+
await storage.set("@panel:anon_id", effectiveAnonId).catch(() => {
|
|
3816
|
+
});
|
|
3817
|
+
}
|
|
3818
|
+
}
|
|
3434
3819
|
try {
|
|
3435
3820
|
await apiClient.trackEvent("$app_installed", distinctId, {
|
|
3436
3821
|
installedAt,
|
|
3437
|
-
platform:
|
|
3822
|
+
platform: import_react_native14.Platform.OS,
|
|
3438
3823
|
...sessionId && { sessionId },
|
|
3439
3824
|
...deviceData,
|
|
3440
|
-
...
|
|
3825
|
+
...effectiveAnonId && { fbAnonId: effectiveAnonId },
|
|
3441
3826
|
...referrer.rawReferrer && { installReferrer: referrer.rawReferrer },
|
|
3442
3827
|
...referrer.trackingId && { referrerTrackingId: referrer.trackingId },
|
|
3443
3828
|
...referrer.fbclid && { referrerFbclid: referrer.fbclid },
|
|
@@ -3449,19 +3834,47 @@ var InstallTracker = class {
|
|
|
3449
3834
|
...adIds.gaid && { gaid: adIds.gaid },
|
|
3450
3835
|
attStatus: adIds.attStatus
|
|
3451
3836
|
});
|
|
3452
|
-
|
|
3837
|
+
if (import_react_native14.Platform.OS === "ios") {
|
|
3838
|
+
void this.callDeferredMatch(apiClient, deviceData, adIds, effectiveAnonId).catch(() => {
|
|
3839
|
+
});
|
|
3840
|
+
}
|
|
3841
|
+
await storage.set("@panel:install_tracked", String(installedAt));
|
|
3453
3842
|
try {
|
|
3454
|
-
|
|
3455
|
-
await AsyncStorage3.setItem("@paywallo:install_tracked", String(installedAt));
|
|
3843
|
+
await import_async_storage3.default.setItem("@paywallo:install_tracked", String(installedAt));
|
|
3456
3844
|
} catch {
|
|
3457
3845
|
}
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3846
|
+
} catch {
|
|
3847
|
+
}
|
|
3848
|
+
}
|
|
3849
|
+
async callDeferredMatch(apiClient, deviceData, adIds, anonId) {
|
|
3850
|
+
const storage = new SecureStorage(this.debug);
|
|
3851
|
+
const alreadyMatched = await storage.get("@panel:deferred_match_done");
|
|
3852
|
+
if (alreadyMatched) return;
|
|
3853
|
+
const controller = new AbortController();
|
|
3854
|
+
const timer = setTimeout(() => controller.abort(), 5e3);
|
|
3855
|
+
try {
|
|
3856
|
+
const baseUrl = apiClient.getBaseUrl();
|
|
3857
|
+
const appKey = apiClient.appKey;
|
|
3858
|
+
await fetch(`${baseUrl}/api/v1/attribution/deferred-match`, {
|
|
3859
|
+
method: "POST",
|
|
3860
|
+
headers: { "Content-Type": "application/json", "X-App-Key": appKey },
|
|
3861
|
+
body: JSON.stringify({
|
|
3862
|
+
idfv: adIds.idfv,
|
|
3863
|
+
deviceModel: deviceData.deviceModel,
|
|
3864
|
+
osVersion: deviceData.osVersion,
|
|
3865
|
+
screenWidth: deviceData.screenWidth,
|
|
3866
|
+
screenHeight: deviceData.screenHeight,
|
|
3867
|
+
timezone: deviceData.timezone,
|
|
3868
|
+
locale: deviceData.locale,
|
|
3869
|
+
fbAnonId: anonId
|
|
3870
|
+
}),
|
|
3871
|
+
signal: controller.signal
|
|
3872
|
+
});
|
|
3873
|
+
await storage.set("@panel:deferred_match_done", "1").catch(() => {
|
|
3874
|
+
});
|
|
3875
|
+
} catch {
|
|
3876
|
+
} finally {
|
|
3877
|
+
clearTimeout(timer);
|
|
3465
3878
|
}
|
|
3466
3879
|
}
|
|
3467
3880
|
};
|
|
@@ -3471,25 +3884,13 @@ function isValidEventName(name) {
|
|
|
3471
3884
|
if (name.startsWith("$")) return /^\$[a-z][a-z0-9]*(_[a-z0-9]+)*$/.test(name);
|
|
3472
3885
|
return /^[a-z][a-z0-9]*(_[a-z0-9]+)*$/.test(name);
|
|
3473
3886
|
}
|
|
3474
|
-
var OFFLINE_ACTIVE_STATUSES = ["active", "grace_period"];
|
|
3475
|
-
async function resolveClientOfflineFromCache() {
|
|
3476
|
-
try {
|
|
3477
|
-
const cached = await subscriptionCache.get();
|
|
3478
|
-
if (!cached) return false;
|
|
3479
|
-
const sub = cached.data.subscription;
|
|
3480
|
-
if (!sub) return false;
|
|
3481
|
-
if (!OFFLINE_ACTIVE_STATUSES.includes(sub.status)) return false;
|
|
3482
|
-
if (!sub.expiresAt) return true;
|
|
3483
|
-
return sub.expiresAt > /* @__PURE__ */ new Date();
|
|
3484
|
-
} catch {
|
|
3485
|
-
return false;
|
|
3486
|
-
}
|
|
3487
|
-
}
|
|
3488
3887
|
var _PaywalloClientClass = class _PaywalloClientClass {
|
|
3489
3888
|
constructor() {
|
|
3490
3889
|
this.config = null;
|
|
3491
3890
|
this.apiClient = null;
|
|
3492
3891
|
this.initPromise = null;
|
|
3892
|
+
this.readyPromise = null;
|
|
3893
|
+
this.resolveReady = null;
|
|
3493
3894
|
this.paywallPresenter = null;
|
|
3494
3895
|
this.campaignPresenter = null;
|
|
3495
3896
|
this.subscriptionGetter = null;
|
|
@@ -3499,29 +3900,30 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3499
3900
|
this.pendingConfig = null;
|
|
3500
3901
|
this.networkCleanup = null;
|
|
3501
3902
|
this.initAttempts = 0;
|
|
3903
|
+
this.autoPreloadedPlacement = null;
|
|
3904
|
+
this.autoPreloadPlacementPromise = null;
|
|
3905
|
+
this.sessionFlagsMap = /* @__PURE__ */ new Map();
|
|
3502
3906
|
}
|
|
3503
3907
|
async init(config) {
|
|
3504
|
-
if (
|
|
3908
|
+
if (!this.readyPromise) {
|
|
3909
|
+
this.readyPromise = new Promise((resolve) => {
|
|
3910
|
+
this.resolveReady = resolve;
|
|
3911
|
+
});
|
|
3912
|
+
}
|
|
3505
3913
|
if (this.config && this.apiClient) {
|
|
3506
|
-
if (config.debug) console.warn("[Paywallo INIT]", "Already initialized \u2014 skipping");
|
|
3507
3914
|
return;
|
|
3508
3915
|
}
|
|
3509
3916
|
if (this.initPromise) {
|
|
3510
|
-
if (config.debug) console.warn("[Paywallo INIT]", "Init already in progress \u2014 awaiting...");
|
|
3511
3917
|
await this.initPromise;
|
|
3512
|
-
if (config.debug) console.warn("[Paywallo INIT]", "Awaited existing init \u2014 done");
|
|
3513
3918
|
return;
|
|
3514
3919
|
}
|
|
3515
3920
|
if (!config.appKey) throw new ClientError(CLIENT_ERROR_CODES.MISSING_APP_KEY, "appKey is required");
|
|
3516
|
-
if (config.debug) console.warn("[Paywallo INIT]", "Starting fresh init...");
|
|
3517
3921
|
this.pendingConfig = config;
|
|
3518
3922
|
this.initAttempts = 0;
|
|
3519
3923
|
this.initPromise = this.initWithRetry(config);
|
|
3520
3924
|
try {
|
|
3521
3925
|
await this.initPromise;
|
|
3522
|
-
if (config.debug) console.warn("[Paywallo INIT]", "init() SUCCESS");
|
|
3523
3926
|
} catch (error) {
|
|
3524
|
-
if (config.debug) console.warn("[Paywallo INIT]", "init() FAILED:", error instanceof Error ? error.message : String(error));
|
|
3525
3927
|
this.initPromise = null;
|
|
3526
3928
|
this.config = null;
|
|
3527
3929
|
this.apiClient = null;
|
|
@@ -3530,6 +3932,18 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3530
3932
|
throw error;
|
|
3531
3933
|
}
|
|
3532
3934
|
}
|
|
3935
|
+
isReady() {
|
|
3936
|
+
return this.config !== null && this.apiClient !== null;
|
|
3937
|
+
}
|
|
3938
|
+
waitUntilReady() {
|
|
3939
|
+
if (this.isReady()) return Promise.resolve();
|
|
3940
|
+
if (!this.readyPromise) {
|
|
3941
|
+
this.readyPromise = new Promise((resolve) => {
|
|
3942
|
+
this.resolveReady = resolve;
|
|
3943
|
+
});
|
|
3944
|
+
}
|
|
3945
|
+
return this.readyPromise;
|
|
3946
|
+
}
|
|
3533
3947
|
async initWithRetry(config) {
|
|
3534
3948
|
while (this.initAttempts <= _PaywalloClientClass.MAX_INIT_RETRIES) {
|
|
3535
3949
|
try {
|
|
@@ -3544,7 +3958,6 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3544
3958
|
this.initAttempts++;
|
|
3545
3959
|
if (this.initAttempts > _PaywalloClientClass.MAX_INIT_RETRIES) throw error;
|
|
3546
3960
|
const delay = _PaywalloClientClass.RETRY_DELAYS[this.initAttempts - 1] ?? 1e4;
|
|
3547
|
-
if (config.debug) console.warn("[Paywallo]", `Init attempt ${this.initAttempts} failed, retrying in ${delay}ms`, error);
|
|
3548
3961
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
3549
3962
|
}
|
|
3550
3963
|
}
|
|
@@ -3555,12 +3968,12 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3555
3968
|
this.networkCleanup = networkMonitor.addListener((state) => {
|
|
3556
3969
|
if (state !== "online") return;
|
|
3557
3970
|
if (this.config && this.apiClient) return;
|
|
3558
|
-
if (
|
|
3971
|
+
if (this.initPromise) return;
|
|
3559
3972
|
this.initPromise = this.initWithRetry(config);
|
|
3560
|
-
this.initPromise.
|
|
3561
|
-
if (config.debug) console.warn("[Paywallo]", "Init succeeded after network recovery");
|
|
3562
|
-
}).catch(() => {
|
|
3973
|
+
this.initPromise.catch(() => {
|
|
3563
3974
|
this.reportInitFailure(config, null, "NETWORK_RECOVERY_FAILED");
|
|
3975
|
+
}).finally(() => {
|
|
3976
|
+
if (!this.isReady()) this.initPromise = null;
|
|
3564
3977
|
});
|
|
3565
3978
|
});
|
|
3566
3979
|
}
|
|
@@ -3572,12 +3985,13 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3572
3985
|
}
|
|
3573
3986
|
reportInitFailure(config, error, reason) {
|
|
3574
3987
|
try {
|
|
3575
|
-
const tempClient = new ApiClient(config.appKey, config.debug);
|
|
3988
|
+
const tempClient = new ApiClient(config.appKey, config.apiUrl ?? "https://paywallo.com.br", config.debug);
|
|
3576
3989
|
void tempClient.reportError("INIT_FAILED", reason, {
|
|
3577
3990
|
attempts: this.initAttempts,
|
|
3578
3991
|
error: error instanceof Error ? error.message : String(error)
|
|
3579
3992
|
});
|
|
3580
|
-
} catch {
|
|
3993
|
+
} catch (err) {
|
|
3994
|
+
if (config.debug) console.warn("[Paywallo] reportInitFailure error", err);
|
|
3581
3995
|
}
|
|
3582
3996
|
}
|
|
3583
3997
|
reportInitSuccess(config, attempts) {
|
|
@@ -3585,41 +3999,83 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3585
3999
|
if (this.apiClient) {
|
|
3586
4000
|
void this.apiClient.reportError("INIT_RECOVERED", `Succeeded after ${attempts} retries`, { attempts });
|
|
3587
4001
|
}
|
|
3588
|
-
} catch {
|
|
4002
|
+
} catch (err) {
|
|
4003
|
+
if (config.debug) console.warn("[Paywallo] reportInitSuccess error", err);
|
|
4004
|
+
}
|
|
4005
|
+
}
|
|
4006
|
+
async resolveSessionFlags(keys, apiClient, distinctId) {
|
|
4007
|
+
try {
|
|
4008
|
+
const results = await apiClient.evaluateFlags(keys, distinctId);
|
|
4009
|
+
this.sessionFlagsMap.clear();
|
|
4010
|
+
for (const key of keys) {
|
|
4011
|
+
this.sessionFlagsMap.set(key, key in results ? results[key] : null);
|
|
4012
|
+
}
|
|
4013
|
+
} catch (error) {
|
|
4014
|
+
for (const key of keys) {
|
|
4015
|
+
this.sessionFlagsMap.set(key, null);
|
|
4016
|
+
}
|
|
3589
4017
|
}
|
|
3590
4018
|
}
|
|
3591
4019
|
async doInit(config) {
|
|
3592
4020
|
const environment = config.environment ?? (typeof __DEV__ !== "undefined" && __DEV__ ? "Sandbox" : "Production");
|
|
3593
4021
|
const offlineQueueEnabled = config.offlineQueueEnabled !== false;
|
|
3594
|
-
if (config.debug) console.warn("[Paywallo INIT]", "Step 1: networkMonitor + offlineQueue...");
|
|
3595
4022
|
await Promise.all([
|
|
3596
4023
|
networkMonitor.initialize({ debug: config.debug }),
|
|
3597
4024
|
offlineQueue.initialize({ debug: config.debug }).then(
|
|
3598
4025
|
() => offlineQueue.clearItemsWithInvalidAppKey(config.appKey)
|
|
3599
4026
|
)
|
|
3600
4027
|
]);
|
|
3601
|
-
|
|
3602
|
-
if (config.debug) console.warn("[Paywallo INIT]", "Step 2: ApiClient + queueProcessor + affiliate...");
|
|
3603
|
-
this.apiClient = new ApiClient(config.appKey, config.debug, environment, offlineQueueEnabled, config.timeout);
|
|
4028
|
+
this.apiClient = new ApiClient(config.appKey, config.apiUrl ?? "https://paywallo.com.br", config.debug, environment, offlineQueueEnabled, config.timeout);
|
|
3604
4029
|
queueProcessor.initialize(this.apiClient.getHttpClient(), { debug: config.debug });
|
|
3605
4030
|
affiliateManager.initialize(this.apiClient, config.debug);
|
|
3606
|
-
|
|
3607
|
-
|
|
4031
|
+
subscriptionManager.init({
|
|
4032
|
+
serverUrl: this.apiClient.getBaseUrl(),
|
|
4033
|
+
appKey: config.appKey,
|
|
4034
|
+
cacheTTL: config.subscriptionCacheTTL,
|
|
4035
|
+
debug: config.debug
|
|
4036
|
+
});
|
|
3608
4037
|
await Promise.all([
|
|
3609
4038
|
identityManager.initialize(this.apiClient, config.debug),
|
|
3610
4039
|
sessionManager.initialize(this.apiClient, config.sessionConfig, config.debug)
|
|
3611
4040
|
]);
|
|
3612
4041
|
const distinctId = identityManager.getDistinctId();
|
|
3613
|
-
if (config.debug) console.warn("[Paywallo INIT]", "Step 3 DONE \u2014 distinctId:", distinctId ? distinctId.substring(0, 8) + "..." : "EMPTY");
|
|
3614
4042
|
if (config.autoStartSession !== false) {
|
|
3615
4043
|
sessionManager.startSession().catch(() => {
|
|
3616
|
-
if (config.debug) console.warn("[Paywallo]", "Session start failed during init \u2014 continuing");
|
|
3617
4044
|
});
|
|
3618
4045
|
}
|
|
3619
4046
|
new InstallTracker(config.debug).trackIfNeeded(this.apiClient, config.requestATT === true).catch(() => {
|
|
3620
4047
|
});
|
|
4048
|
+
if (config.sessionFlags && config.sessionFlags.length > 0) {
|
|
4049
|
+
await this.resolveSessionFlags(config.sessionFlags, this.apiClient, distinctId);
|
|
4050
|
+
}
|
|
3621
4051
|
this.config = config;
|
|
3622
|
-
if (
|
|
4052
|
+
if (this.resolveReady) {
|
|
4053
|
+
this.resolveReady();
|
|
4054
|
+
this.resolveReady = null;
|
|
4055
|
+
}
|
|
4056
|
+
if (config.debug) console.info("[Paywallo INIT] complete");
|
|
4057
|
+
if (this.apiClient) {
|
|
4058
|
+
const apiClientRef = this.apiClient;
|
|
4059
|
+
if (config.autoPreloadCampaign) {
|
|
4060
|
+
this.autoPreloadedPlacement = config.autoPreloadCampaign;
|
|
4061
|
+
this.autoPreloadPlacementPromise = Promise.resolve(config.autoPreloadCampaign);
|
|
4062
|
+
if (config.debug) console.info(`[Paywallo PRELOAD] started \u2014 placement: ${config.autoPreloadCampaign}`);
|
|
4063
|
+
void campaignGateService.preloadCampaign(apiClientRef, config.autoPreloadCampaign).catch(() => {
|
|
4064
|
+
});
|
|
4065
|
+
} else {
|
|
4066
|
+
this.autoPreloadPlacementPromise = apiClientRef.getPrimaryCampaign(distinctId).then((primary) => {
|
|
4067
|
+
const placement = primary?.placement ?? primary?.paywall?.placement;
|
|
4068
|
+
if (placement) {
|
|
4069
|
+
this.autoPreloadedPlacement = placement;
|
|
4070
|
+
if (config.debug) console.info(`[Paywallo PRELOAD] started \u2014 placement: ${placement}`);
|
|
4071
|
+
void campaignGateService.preloadCampaign(apiClientRef, placement).catch(() => {
|
|
4072
|
+
});
|
|
4073
|
+
return placement;
|
|
4074
|
+
}
|
|
4075
|
+
return null;
|
|
4076
|
+
}).catch(() => null);
|
|
4077
|
+
}
|
|
4078
|
+
}
|
|
3623
4079
|
}
|
|
3624
4080
|
async identify(options) {
|
|
3625
4081
|
this.ensureInitialized();
|
|
@@ -3629,7 +4085,6 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3629
4085
|
const apiClient = this.getApiClientOrThrow();
|
|
3630
4086
|
const distinctId = identityManager.getDistinctId();
|
|
3631
4087
|
if (!distinctId) {
|
|
3632
|
-
if (this.config?.debug) console.warn("[Paywallo TRACK]", "SKIPPED \u2014 no distinctId", eventName);
|
|
3633
4088
|
return;
|
|
3634
4089
|
}
|
|
3635
4090
|
if (!isValidEventName(eventName)) {
|
|
@@ -3637,25 +4092,6 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3637
4092
|
throw new ClientError(CLIENT_ERROR_CODES.INVALID_EVENT_NAME, msg);
|
|
3638
4093
|
}
|
|
3639
4094
|
const sessionId = sessionManager.getSessionId();
|
|
3640
|
-
if (eventName === "$purchase_completed") {
|
|
3641
|
-
if (this.config?.debug) console.warn("[Paywallo PURCHASE]", "tracking purchase:", JSON.stringify({
|
|
3642
|
-
distinctId: distinctId.substring(0, 8) + "...",
|
|
3643
|
-
productId: options?.properties?.productId,
|
|
3644
|
-
priceUsd: options?.properties?.priceUsd,
|
|
3645
|
-
price: options?.properties?.price,
|
|
3646
|
-
currency: options?.properties?.currency,
|
|
3647
|
-
placement: options?.properties?.placement
|
|
3648
|
-
}));
|
|
3649
|
-
} else if (eventName === "$paywall_viewed" || eventName === "$paywall_closed") {
|
|
3650
|
-
if (this.config?.debug) console.warn("[Paywallo PAYWALL]", eventName, JSON.stringify({
|
|
3651
|
-
distinctId: distinctId.substring(0, 8) + "...",
|
|
3652
|
-
placement: options?.properties?.placement,
|
|
3653
|
-
variantKey: options?.properties?.variantKey,
|
|
3654
|
-
timeOnPaywall: options?.properties?.timeOnPaywall
|
|
3655
|
-
}));
|
|
3656
|
-
} else {
|
|
3657
|
-
if (this.config?.debug) console.warn("[Paywallo TRACK]", eventName, "distinctId:", distinctId.substring(0, 8) + "...");
|
|
3658
|
-
}
|
|
3659
4095
|
await apiClient.trackEvent(eventName, distinctId, {
|
|
3660
4096
|
...identityManager.getProperties(),
|
|
3661
4097
|
...options?.properties,
|
|
@@ -3666,7 +4102,6 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3666
4102
|
const apiClient = this.getApiClientOrThrow();
|
|
3667
4103
|
const distinctId = identityManager.getDistinctId();
|
|
3668
4104
|
if (!distinctId) {
|
|
3669
|
-
if (this.config?.debug) console.warn("[Paywallo] Skipping event \u2014 no distinctId available");
|
|
3670
4105
|
return;
|
|
3671
4106
|
}
|
|
3672
4107
|
if (!Number.isInteger(index) || index < 0) {
|
|
@@ -3690,7 +4125,6 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3690
4125
|
const apiClient = this.getApiClientOrThrow();
|
|
3691
4126
|
const distinctId = identityManager.getDistinctId();
|
|
3692
4127
|
if (!distinctId) {
|
|
3693
|
-
if (this.config?.debug) console.warn("[Paywallo] Skipping event \u2014 no distinctId available");
|
|
3694
4128
|
return;
|
|
3695
4129
|
}
|
|
3696
4130
|
if (!actionName || typeof actionName !== "string") {
|
|
@@ -3719,14 +4153,10 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3719
4153
|
try {
|
|
3720
4154
|
const distinctId = identityManager.getDistinctId();
|
|
3721
4155
|
if (!distinctId) {
|
|
3722
|
-
if (this.config?.debug) console.warn("[Paywallo AB]", "getVariant SKIPPED \u2014 no distinctId", flagKey);
|
|
3723
4156
|
return { variant: null };
|
|
3724
4157
|
}
|
|
3725
|
-
|
|
3726
|
-
if (this.config?.debug) console.warn("[Paywallo AB]", `getVariant("${flagKey}") =`, result.variant);
|
|
3727
|
-
return result;
|
|
4158
|
+
return await this.getApiClientOrThrow().getVariant(flagKey, distinctId);
|
|
3728
4159
|
} catch (error) {
|
|
3729
|
-
if (this.config?.debug) console.warn("[Paywallo]", "getVariant failed \u2014 returning null", error);
|
|
3730
4160
|
if (this.apiClient) void this.apiClient.reportError("GET_VARIANT_FAILED", error instanceof Error ? error.message : String(error));
|
|
3731
4161
|
return { variant: null };
|
|
3732
4162
|
}
|
|
@@ -3742,7 +4172,6 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3742
4172
|
const result = await this.getApiClientOrThrow().getConditionalFlag(flagKey, { distinctId, ...context });
|
|
3743
4173
|
return result.value;
|
|
3744
4174
|
} catch (error) {
|
|
3745
|
-
if (this.config?.debug) console.warn("[Paywallo]", "getConditionalFlag failed \u2014 returning false", error);
|
|
3746
4175
|
if (this.apiClient) void this.apiClient.reportError("GET_CONDITIONAL_FLAG_FAILED", error instanceof Error ? error.message : String(error));
|
|
3747
4176
|
return false;
|
|
3748
4177
|
}
|
|
@@ -3765,15 +4194,18 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3765
4194
|
async hasActiveSubscription() {
|
|
3766
4195
|
this.ensureInitialized();
|
|
3767
4196
|
try {
|
|
3768
|
-
if (this.activeChecker)
|
|
4197
|
+
if (this.activeChecker) {
|
|
4198
|
+
return await this.activeChecker();
|
|
4199
|
+
}
|
|
3769
4200
|
const distinctId = identityManager.getDistinctId();
|
|
3770
|
-
if (!distinctId || !this.apiClient)
|
|
4201
|
+
if (!distinctId || !this.apiClient) {
|
|
4202
|
+
return false;
|
|
4203
|
+
}
|
|
3771
4204
|
const status = await this.apiClient.getSubscriptionStatus(distinctId);
|
|
3772
4205
|
return status.hasActiveSubscription;
|
|
3773
|
-
} catch {
|
|
3774
|
-
if (this.
|
|
3775
|
-
|
|
3776
|
-
return resolveClientOfflineFromCache();
|
|
4206
|
+
} catch (error) {
|
|
4207
|
+
if (this.apiClient) void this.apiClient.reportError("SUBSCRIPTION_CHECK_FAILED", error instanceof Error ? error.message : "hasActiveSubscription failed");
|
|
4208
|
+
return false;
|
|
3777
4209
|
}
|
|
3778
4210
|
}
|
|
3779
4211
|
async getSubscription() {
|
|
@@ -3786,7 +4218,6 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3786
4218
|
if (!status.subscription) return null;
|
|
3787
4219
|
return { ...status.subscription, expiresAt: status.subscription.expiresAt ? new Date(status.subscription.expiresAt) : null };
|
|
3788
4220
|
} catch {
|
|
3789
|
-
if (this.config?.debug) console.warn("[Paywallo]", "getSubscription failed \u2014 returning null");
|
|
3790
4221
|
return null;
|
|
3791
4222
|
}
|
|
3792
4223
|
}
|
|
@@ -3797,20 +4228,24 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3797
4228
|
}
|
|
3798
4229
|
async reset() {
|
|
3799
4230
|
await identityManager.reset();
|
|
4231
|
+
await subscriptionCache.invalidateAll();
|
|
3800
4232
|
}
|
|
3801
4233
|
async fullReset() {
|
|
3802
4234
|
const wasDebug = this.config?.debug ?? false;
|
|
3803
4235
|
await sessionManager.endSession();
|
|
3804
4236
|
await identityManager.reset();
|
|
3805
|
-
await subscriptionCache.
|
|
4237
|
+
await subscriptionCache.invalidateAll();
|
|
3806
4238
|
queueProcessor.dispose();
|
|
3807
4239
|
offlineQueue.dispose();
|
|
3808
4240
|
networkMonitor.dispose();
|
|
3809
4241
|
campaignGateService.clearPreloaded();
|
|
3810
4242
|
this.cleanupNetworkRecovery();
|
|
4243
|
+
this.sessionFlagsMap.clear();
|
|
3811
4244
|
this.apiClient = null;
|
|
3812
4245
|
this.config = null;
|
|
3813
4246
|
this.initPromise = null;
|
|
4247
|
+
this.readyPromise = null;
|
|
4248
|
+
this.resolveReady = null;
|
|
3814
4249
|
this.pendingConfig = null;
|
|
3815
4250
|
this.initAttempts = 0;
|
|
3816
4251
|
this.paywallPresenter = null;
|
|
@@ -3819,7 +4254,13 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3819
4254
|
this.activeChecker = null;
|
|
3820
4255
|
this.restoreHandler = null;
|
|
3821
4256
|
this.lastOnboardingStepTimestamp = null;
|
|
3822
|
-
|
|
4257
|
+
this.autoPreloadedPlacement = null;
|
|
4258
|
+
this.autoPreloadPlacementPromise = null;
|
|
4259
|
+
}
|
|
4260
|
+
getSessionFlag(key) {
|
|
4261
|
+
if (!this.config || !this.apiClient) return null;
|
|
4262
|
+
const value = this.sessionFlagsMap.get(key);
|
|
4263
|
+
return value !== void 0 ? value : null;
|
|
3823
4264
|
}
|
|
3824
4265
|
getConfig() {
|
|
3825
4266
|
return this.config;
|
|
@@ -3908,6 +4349,15 @@ var _PaywalloClientClass = class _PaywalloClientClass {
|
|
|
3908
4349
|
getPreloadedCampaign(placement) {
|
|
3909
4350
|
return campaignGateService.getPreloadedCampaign(placement);
|
|
3910
4351
|
}
|
|
4352
|
+
isPreloaded(placement) {
|
|
4353
|
+
return campaignGateService.getPreloadedCampaign(placement) !== null;
|
|
4354
|
+
}
|
|
4355
|
+
getAutoPreloadedPlacement() {
|
|
4356
|
+
return this.autoPreloadedPlacement;
|
|
4357
|
+
}
|
|
4358
|
+
waitForAutoPreloadedPlacement() {
|
|
4359
|
+
return this.autoPreloadPlacementPromise ?? Promise.resolve(null);
|
|
4360
|
+
}
|
|
3911
4361
|
isOnline() {
|
|
3912
4362
|
return networkMonitor.isOnline();
|
|
3913
4363
|
}
|
|
@@ -3937,48 +4387,6 @@ var PaywalloClient = new PaywalloClientClass();
|
|
|
3937
4387
|
var import_react9 = require("react");
|
|
3938
4388
|
var PaywalloContext = (0, import_react9.createContext)(null);
|
|
3939
4389
|
|
|
3940
|
-
// src/utils/localization.ts
|
|
3941
|
-
var import_react_native12 = require("react-native");
|
|
3942
|
-
var currentLanguage = "pt-BR";
|
|
3943
|
-
var defaultLanguage = "pt-BR";
|
|
3944
|
-
function setCurrentLanguage(language) {
|
|
3945
|
-
currentLanguage = language;
|
|
3946
|
-
}
|
|
3947
|
-
function setDefaultLanguage(language) {
|
|
3948
|
-
defaultLanguage = language;
|
|
3949
|
-
}
|
|
3950
|
-
function getCurrentLanguage() {
|
|
3951
|
-
return currentLanguage;
|
|
3952
|
-
}
|
|
3953
|
-
function getDefaultLanguage() {
|
|
3954
|
-
return defaultLanguage;
|
|
3955
|
-
}
|
|
3956
|
-
function getLocalizedString(text) {
|
|
3957
|
-
if (!text) return "";
|
|
3958
|
-
if (typeof text === "string") return text;
|
|
3959
|
-
return text[currentLanguage] ?? text[defaultLanguage] ?? Object.values(text)[0] ?? "";
|
|
3960
|
-
}
|
|
3961
|
-
function detectDeviceLanguage() {
|
|
3962
|
-
try {
|
|
3963
|
-
if (import_react_native12.Platform.OS === "ios") {
|
|
3964
|
-
const settings = import_react_native12.NativeModules.SettingsManager?.settings;
|
|
3965
|
-
return settings?.AppleLocale ?? settings?.AppleLanguages?.[0] ?? "en-US";
|
|
3966
|
-
}
|
|
3967
|
-
return import_react_native12.NativeModules.I18nManager?.localeIdentifier ?? "en-US";
|
|
3968
|
-
} catch {
|
|
3969
|
-
return "en-US";
|
|
3970
|
-
}
|
|
3971
|
-
}
|
|
3972
|
-
function initLocalization(options) {
|
|
3973
|
-
if (options?.defaultLanguage) {
|
|
3974
|
-
defaultLanguage = options.defaultLanguage;
|
|
3975
|
-
}
|
|
3976
|
-
if (options?.detectDevice !== false) {
|
|
3977
|
-
const deviceLanguage = detectDeviceLanguage();
|
|
3978
|
-
currentLanguage = deviceLanguage;
|
|
3979
|
-
}
|
|
3980
|
-
}
|
|
3981
|
-
|
|
3982
4390
|
// src/hooks/usePaywallo.ts
|
|
3983
4391
|
var import_react10 = require("react");
|
|
3984
4392
|
function usePaywallo() {
|
|
@@ -4004,7 +4412,8 @@ function mapToIAPProduct(product) {
|
|
|
4004
4412
|
type: product.type === "subscription" ? "subs" : "inapp",
|
|
4005
4413
|
subscriptionPeriodAndroid: product.subscriptionPeriod,
|
|
4006
4414
|
introductoryPrice: product.introductoryPrice,
|
|
4007
|
-
freeTrialPeriodAndroid: product.freeTrialPeriod
|
|
4415
|
+
freeTrialPeriodAndroid: product.freeTrialPeriod,
|
|
4416
|
+
trialDays: product.trialDays ?? 0
|
|
4008
4417
|
};
|
|
4009
4418
|
}
|
|
4010
4419
|
function mapToFormattedProduct(product) {
|
|
@@ -4019,6 +4428,7 @@ function mapToFormattedProduct(product) {
|
|
|
4019
4428
|
subscriptionPeriod: product.subscriptionPeriod,
|
|
4020
4429
|
introductoryPrice: product.introductoryPrice,
|
|
4021
4430
|
freeTrialPeriod: product.freeTrialPeriod,
|
|
4431
|
+
trialDays: product.trialDays ?? 0,
|
|
4022
4432
|
pricePerMonth: product.pricePerMonth,
|
|
4023
4433
|
pricePerWeek: product.pricePerWeek,
|
|
4024
4434
|
pricePerDay: product.pricePerDay
|
|
@@ -4104,23 +4514,31 @@ function usePurchase() {
|
|
|
4104
4514
|
if (result.success && result.purchase) {
|
|
4105
4515
|
setState("idle");
|
|
4106
4516
|
return {
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4517
|
+
status: "success",
|
|
4518
|
+
purchase: {
|
|
4519
|
+
productId: result.purchase.productId,
|
|
4520
|
+
transactionId: result.purchase.transactionId,
|
|
4521
|
+
transactionDate: result.purchase.transactionDate,
|
|
4522
|
+
transactionReceipt: result.purchase.receipt
|
|
4523
|
+
}
|
|
4111
4524
|
};
|
|
4112
4525
|
}
|
|
4113
4526
|
if (result.error) {
|
|
4114
|
-
|
|
4115
|
-
setError(purchaseError);
|
|
4527
|
+
setError(result.error);
|
|
4116
4528
|
setState("error");
|
|
4117
|
-
|
|
4529
|
+
return { status: "failed", error: result.error };
|
|
4118
4530
|
}
|
|
4119
4531
|
setState("idle");
|
|
4120
|
-
|
|
4532
|
+
return { status: "cancelled" };
|
|
4121
4533
|
} catch (err) {
|
|
4534
|
+
const purchaseError = err instanceof PurchaseError ? err : createPurchaseError("PURCHASE_FAILED", err instanceof Error ? err.message : String(err));
|
|
4535
|
+
if (purchaseError.userCancelled) {
|
|
4536
|
+
setState("idle");
|
|
4537
|
+
return { status: "cancelled" };
|
|
4538
|
+
}
|
|
4539
|
+
setError(purchaseError);
|
|
4122
4540
|
setState("error");
|
|
4123
|
-
|
|
4541
|
+
return { status: "failed", error: purchaseError };
|
|
4124
4542
|
}
|
|
4125
4543
|
}, []);
|
|
4126
4544
|
const restore = (0, import_react12.useCallback)(async () => {
|
|
@@ -4148,7 +4566,6 @@ function usePurchase() {
|
|
|
4148
4566
|
purchase,
|
|
4149
4567
|
restore,
|
|
4150
4568
|
error,
|
|
4151
|
-
isLoading: state === "loading",
|
|
4152
4569
|
isPurchasing: state === "purchasing",
|
|
4153
4570
|
isRestoring: state === "restoring"
|
|
4154
4571
|
};
|
|
@@ -4156,139 +4573,6 @@ function usePurchase() {
|
|
|
4156
4573
|
|
|
4157
4574
|
// src/hooks/useSubscription.ts
|
|
4158
4575
|
var import_react13 = require("react");
|
|
4159
|
-
|
|
4160
|
-
// src/domains/subscription/SubscriptionManager.ts
|
|
4161
|
-
var import_react_native13 = require("react-native");
|
|
4162
|
-
var SubscriptionManagerClass = class {
|
|
4163
|
-
constructor() {
|
|
4164
|
-
this.config = null;
|
|
4165
|
-
this.listeners = /* @__PURE__ */ new Set();
|
|
4166
|
-
this.userId = null;
|
|
4167
|
-
this.cache = new SubscriptionCache();
|
|
4168
|
-
}
|
|
4169
|
-
init(config) {
|
|
4170
|
-
this.config = config;
|
|
4171
|
-
if (config.cacheTTL) {
|
|
4172
|
-
this.cache.setTTL(config.cacheTTL);
|
|
4173
|
-
}
|
|
4174
|
-
}
|
|
4175
|
-
setUserId(userId) {
|
|
4176
|
-
if (this.userId !== userId) {
|
|
4177
|
-
this.userId = userId;
|
|
4178
|
-
void this.cache.invalidate();
|
|
4179
|
-
}
|
|
4180
|
-
}
|
|
4181
|
-
async hasActiveSubscription(forceRefresh = false) {
|
|
4182
|
-
const status = await this.getSubscriptionStatus(forceRefresh);
|
|
4183
|
-
return status.hasActiveSubscription;
|
|
4184
|
-
}
|
|
4185
|
-
async getSubscription(forceRefresh = false) {
|
|
4186
|
-
const status = await this.getSubscriptionStatus(forceRefresh);
|
|
4187
|
-
return status.subscription;
|
|
4188
|
-
}
|
|
4189
|
-
async getEntitlements(forceRefresh = false) {
|
|
4190
|
-
const status = await this.getSubscriptionStatus(forceRefresh);
|
|
4191
|
-
return status.entitlements;
|
|
4192
|
-
}
|
|
4193
|
-
async getSubscriptionStatus(forceRefresh = false) {
|
|
4194
|
-
if (!this.config) {
|
|
4195
|
-
return this.getEmptyStatus();
|
|
4196
|
-
}
|
|
4197
|
-
if (!forceRefresh) {
|
|
4198
|
-
const cached = await this.cache.get();
|
|
4199
|
-
if (cached && !cached.isStale) {
|
|
4200
|
-
return cached.data;
|
|
4201
|
-
}
|
|
4202
|
-
}
|
|
4203
|
-
try {
|
|
4204
|
-
const status = await this.fetchSubscriptionStatus();
|
|
4205
|
-
await this.cache.set(status);
|
|
4206
|
-
this.notifyListeners(status);
|
|
4207
|
-
return status;
|
|
4208
|
-
} catch (error) {
|
|
4209
|
-
this.log("Failed to fetch subscription status", error);
|
|
4210
|
-
const cached = await this.cache.get();
|
|
4211
|
-
if (cached) {
|
|
4212
|
-
return cached.data;
|
|
4213
|
-
}
|
|
4214
|
-
return this.getEmptyStatus();
|
|
4215
|
-
}
|
|
4216
|
-
}
|
|
4217
|
-
async restorePurchases() {
|
|
4218
|
-
if (!this.config) {
|
|
4219
|
-
throw new SessionError(
|
|
4220
|
-
SESSION_ERROR_CODES.NOT_INITIALIZED,
|
|
4221
|
-
"SubscriptionManager not initialized"
|
|
4222
|
-
);
|
|
4223
|
-
}
|
|
4224
|
-
await this.cache.invalidate();
|
|
4225
|
-
return this.getSubscriptionStatus(true);
|
|
4226
|
-
}
|
|
4227
|
-
async fetchSubscriptionStatus() {
|
|
4228
|
-
if (!this.config) {
|
|
4229
|
-
return this.getEmptyStatus();
|
|
4230
|
-
}
|
|
4231
|
-
const url = new URL(`${this.config.serverUrl}/subscriptions/status`);
|
|
4232
|
-
if (this.userId) {
|
|
4233
|
-
url.searchParams.set("userId", this.userId);
|
|
4234
|
-
}
|
|
4235
|
-
url.searchParams.set("platform", import_react_native13.Platform.OS);
|
|
4236
|
-
const response = await fetch(url.toString(), {
|
|
4237
|
-
method: "GET",
|
|
4238
|
-
headers: {
|
|
4239
|
-
"Content-Type": "application/json",
|
|
4240
|
-
"X-App-Key": this.config.appKey
|
|
4241
|
-
}
|
|
4242
|
-
});
|
|
4243
|
-
if (!response.ok) {
|
|
4244
|
-
throw new SessionError(
|
|
4245
|
-
SESSION_ERROR_CODES.RESTORE_FAILED,
|
|
4246
|
-
`Failed to fetch subscription status: ${response.status}`
|
|
4247
|
-
);
|
|
4248
|
-
}
|
|
4249
|
-
const data = await response.json();
|
|
4250
|
-
if (data.subscription) {
|
|
4251
|
-
data.subscription.expiresAt = new Date(data.subscription.expiresAt);
|
|
4252
|
-
data.subscription.originalPurchaseDate = new Date(data.subscription.originalPurchaseDate);
|
|
4253
|
-
data.subscription.latestPurchaseDate = new Date(data.subscription.latestPurchaseDate);
|
|
4254
|
-
if (data.subscription.cancellationDate) {
|
|
4255
|
-
data.subscription.cancellationDate = new Date(data.subscription.cancellationDate);
|
|
4256
|
-
}
|
|
4257
|
-
if (data.subscription.gracePeriodExpiresAt) {
|
|
4258
|
-
data.subscription.gracePeriodExpiresAt = new Date(data.subscription.gracePeriodExpiresAt);
|
|
4259
|
-
}
|
|
4260
|
-
}
|
|
4261
|
-
return data;
|
|
4262
|
-
}
|
|
4263
|
-
getEmptyStatus() {
|
|
4264
|
-
return {
|
|
4265
|
-
hasActiveSubscription: false,
|
|
4266
|
-
subscription: null,
|
|
4267
|
-
entitlements: []
|
|
4268
|
-
};
|
|
4269
|
-
}
|
|
4270
|
-
addListener(listener) {
|
|
4271
|
-
this.listeners.add(listener);
|
|
4272
|
-
return () => this.listeners.delete(listener);
|
|
4273
|
-
}
|
|
4274
|
-
notifyListeners(status) {
|
|
4275
|
-
for (const listener of this.listeners) {
|
|
4276
|
-
listener(status);
|
|
4277
|
-
}
|
|
4278
|
-
}
|
|
4279
|
-
async onPurchaseComplete() {
|
|
4280
|
-
await this.cache.invalidate();
|
|
4281
|
-
await this.getSubscriptionStatus(true);
|
|
4282
|
-
}
|
|
4283
|
-
log(message, data) {
|
|
4284
|
-
if (this.config?.debug) {
|
|
4285
|
-
console.warn(`[SubscriptionManager] ${message}`, data ?? "");
|
|
4286
|
-
}
|
|
4287
|
-
}
|
|
4288
|
-
};
|
|
4289
|
-
var subscriptionManager = new SubscriptionManagerClass();
|
|
4290
|
-
|
|
4291
|
-
// src/hooks/useSubscription.ts
|
|
4292
4576
|
function useSubscription() {
|
|
4293
4577
|
const [status, setStatus] = (0, import_react13.useState)(null);
|
|
4294
4578
|
const [isLoading, setIsLoading] = (0, import_react13.useState)(true);
|
|
@@ -4323,6 +4607,9 @@ function useSubscription() {
|
|
|
4323
4607
|
void subscriptionManager.getSubscriptionStatus().then((s) => {
|
|
4324
4608
|
setStatus(s);
|
|
4325
4609
|
setIsLoading(false);
|
|
4610
|
+
}).catch((err) => {
|
|
4611
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
4612
|
+
setIsLoading(false);
|
|
4326
4613
|
});
|
|
4327
4614
|
const removeListener = subscriptionManager.addListener((newStatus) => {
|
|
4328
4615
|
setStatus(newStatus);
|
|
@@ -4344,7 +4631,7 @@ function useSubscription() {
|
|
|
4344
4631
|
|
|
4345
4632
|
// src/PaywalloProvider.tsx
|
|
4346
4633
|
var import_react18 = require("react");
|
|
4347
|
-
var
|
|
4634
|
+
var import_react_native15 = require("react-native");
|
|
4348
4635
|
|
|
4349
4636
|
// src/hooks/useCampaignPreload.ts
|
|
4350
4637
|
var import_react14 = require("react");
|
|
@@ -4405,10 +4692,9 @@ var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
|
4405
4692
|
function isCacheEntryValid(entry) {
|
|
4406
4693
|
return !!entry && Date.now() - entry.preloadedAt < CACHE_TTL_MS;
|
|
4407
4694
|
}
|
|
4408
|
-
function
|
|
4695
|
+
function getCacheEntry(cache, placement) {
|
|
4409
4696
|
const entry = cache.get(placement);
|
|
4410
4697
|
if (!isCacheEntryValid(entry)) return null;
|
|
4411
|
-
cache.delete(placement);
|
|
4412
4698
|
return entry ?? null;
|
|
4413
4699
|
}
|
|
4414
4700
|
|
|
@@ -4421,7 +4707,7 @@ function useCampaignPreload() {
|
|
|
4421
4707
|
[]
|
|
4422
4708
|
);
|
|
4423
4709
|
const consumePreloadedCampaign = (0, import_react14.useCallback)(
|
|
4424
|
-
(placement) =>
|
|
4710
|
+
(placement) => getCacheEntry(preloadedCampaignsRef.current, placement),
|
|
4425
4711
|
[]
|
|
4426
4712
|
);
|
|
4427
4713
|
const preloadCampaign = (0, import_react14.useCallback)(
|
|
@@ -4469,6 +4755,7 @@ function useCampaignPreload() {
|
|
|
4469
4755
|
campaignId: campaign.campaignId,
|
|
4470
4756
|
variantKey: campaign.variantKey
|
|
4471
4757
|
});
|
|
4758
|
+
if (PaywalloClient.getConfig()?.debug) console.info(`[Paywallo WEBVIEW] mounting hidden \u2014 placement: ${placement}`);
|
|
4472
4759
|
return { success: true };
|
|
4473
4760
|
} catch (error) {
|
|
4474
4761
|
return {
|
|
@@ -4502,8 +4789,8 @@ function useProductLoader() {
|
|
|
4502
4789
|
const map = /* @__PURE__ */ new Map();
|
|
4503
4790
|
for (const p of loaded) map.set(p.productId, p);
|
|
4504
4791
|
setProducts(map);
|
|
4505
|
-
} catch (
|
|
4506
|
-
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo]
|
|
4792
|
+
} catch (err) {
|
|
4793
|
+
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] refreshProducts failed", err);
|
|
4507
4794
|
} finally {
|
|
4508
4795
|
setIsLoadingProducts(false);
|
|
4509
4796
|
}
|
|
@@ -4513,7 +4800,7 @@ function useProductLoader() {
|
|
|
4513
4800
|
|
|
4514
4801
|
// src/hooks/usePurchaseOrchestrator.ts
|
|
4515
4802
|
var import_react16 = require("react");
|
|
4516
|
-
function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef) {
|
|
4803
|
+
function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, invalidateSubscriptionCache) {
|
|
4517
4804
|
const { trackDismissed, trackProductSelected, trackPurchased } = usePaywallTracking();
|
|
4518
4805
|
const handlePreloadedPurchase = (0, import_react16.useCallback)(
|
|
4519
4806
|
async (productId) => {
|
|
@@ -4538,17 +4825,18 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
|
|
|
4538
4825
|
await trackDismissed({ placement: preloadedWebView.placement, paywallId: preloadedWebView.paywallId, durationSeconds, action: "purchase", variantKey: preloadedWebView.variantKey, campaignId: preloadedWebView.campaignId });
|
|
4539
4826
|
await trackPurchased({ placement: preloadedWebView.placement, paywallId: preloadedWebView.paywallId, productId, variantKey: preloadedWebView.variantKey, campaignId: preloadedWebView.campaignId });
|
|
4540
4827
|
}
|
|
4828
|
+
invalidateSubscriptionCache();
|
|
4541
4829
|
setShowingPreloadedWebView(false);
|
|
4542
4830
|
clearPreloadedWebView();
|
|
4543
4831
|
resolvePreloadedPaywall({ presented: true, purchased: true, cancelled: false, restored: false });
|
|
4544
4832
|
} catch (error) {
|
|
4545
|
-
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo]
|
|
4833
|
+
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] preloaded purchase failed", error);
|
|
4546
4834
|
setShowingPreloadedWebView(false);
|
|
4547
4835
|
clearPreloadedWebView();
|
|
4548
4836
|
resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: false });
|
|
4549
4837
|
}
|
|
4550
4838
|
},
|
|
4551
|
-
[preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, trackDismissed, trackProductSelected, trackPurchased]
|
|
4839
|
+
[preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, trackDismissed, trackProductSelected, trackPurchased, invalidateSubscriptionCache]
|
|
4552
4840
|
);
|
|
4553
4841
|
const handlePreloadedRestore = (0, import_react16.useCallback)(async () => {
|
|
4554
4842
|
try {
|
|
@@ -4558,16 +4846,17 @@ function usePurchaseOrchestrator(preloadedWebView, resolvePreloadedPaywall, clea
|
|
|
4558
4846
|
const durationSeconds = Math.round((Date.now() - presentedAtRef.current) / 1e3);
|
|
4559
4847
|
await trackDismissed({ placement: preloadedWebView.placement, paywallId: preloadedWebView.paywallId, durationSeconds, action: "restore", variantKey: preloadedWebView.variantKey, campaignId: preloadedWebView.campaignId });
|
|
4560
4848
|
}
|
|
4849
|
+
invalidateSubscriptionCache();
|
|
4561
4850
|
setShowingPreloadedWebView(false);
|
|
4562
4851
|
clearPreloadedWebView();
|
|
4563
4852
|
resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: true });
|
|
4564
4853
|
} catch (error) {
|
|
4565
|
-
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo]
|
|
4854
|
+
if (PaywalloClient.getConfig()?.debug) console.warn("[Paywallo] preloaded restore failed", error);
|
|
4566
4855
|
setShowingPreloadedWebView(false);
|
|
4567
4856
|
clearPreloadedWebView();
|
|
4568
4857
|
resolvePreloadedPaywall({ presented: true, purchased: false, cancelled: false, restored: false });
|
|
4569
4858
|
}
|
|
4570
|
-
}, [preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, trackDismissed]);
|
|
4859
|
+
}, [preloadedWebView, resolvePreloadedPaywall, clearPreloadedWebView, setShowingPreloadedWebView, presentedAtRef, trackDismissed, invalidateSubscriptionCache]);
|
|
4571
4860
|
return { handlePreloadedPurchase, handlePreloadedRestore };
|
|
4572
4861
|
}
|
|
4573
4862
|
|
|
@@ -4579,7 +4868,6 @@ function toDomainSubscriptionInfo(sub) {
|
|
|
4579
4868
|
return {
|
|
4580
4869
|
id: "",
|
|
4581
4870
|
productId: sub.productId,
|
|
4582
|
-
// The hook's SubscriptionStatus is a subset of the domain's — cast is safe
|
|
4583
4871
|
status: sub.status,
|
|
4584
4872
|
expiresAt: sub.expiresAt ?? /* @__PURE__ */ new Date(0),
|
|
4585
4873
|
originalPurchaseDate: /* @__PURE__ */ new Date(0),
|
|
@@ -4597,31 +4885,32 @@ function toDomainResponse(status) {
|
|
|
4597
4885
|
entitlements: []
|
|
4598
4886
|
};
|
|
4599
4887
|
}
|
|
4600
|
-
|
|
4888
|
+
function isSubscriptionStillActive(sub) {
|
|
4889
|
+
if (!sub) return false;
|
|
4890
|
+
if (!ACTIVE_STATUSES.includes(sub.status)) return false;
|
|
4891
|
+
if (sub.expiresAt && sub.expiresAt.getTime() !== 0 && sub.expiresAt < /* @__PURE__ */ new Date()) return false;
|
|
4892
|
+
return true;
|
|
4893
|
+
}
|
|
4894
|
+
async function resolveOfflineFromCache(distinctId) {
|
|
4601
4895
|
try {
|
|
4602
|
-
const cached = await subscriptionCache.get();
|
|
4896
|
+
const cached = await subscriptionCache.get(distinctId);
|
|
4603
4897
|
if (!cached) return false;
|
|
4604
4898
|
const sub = cached.data.subscription;
|
|
4605
|
-
|
|
4606
|
-
if (!ACTIVE_STATUSES.includes(sub.status)) return false;
|
|
4607
|
-
const hasValidExpiry = sub.expiresAt.getTime() === 0 || sub.expiresAt > /* @__PURE__ */ new Date();
|
|
4608
|
-
return hasValidExpiry;
|
|
4899
|
+
return isSubscriptionStillActive(sub ? { status: sub.status, expiresAt: sub.expiresAt ?? null } : null);
|
|
4609
4900
|
} catch {
|
|
4610
4901
|
return false;
|
|
4611
4902
|
}
|
|
4612
4903
|
}
|
|
4613
4904
|
function isActiveCacheEntry(entry) {
|
|
4614
|
-
|
|
4615
|
-
if (!ACTIVE_STATUSES.includes(entry.data.status)) return false;
|
|
4616
|
-
if (entry.data.expiresAt && entry.data.expiresAt < /* @__PURE__ */ new Date()) return false;
|
|
4617
|
-
return true;
|
|
4905
|
+
return isSubscriptionStillActive(entry.data);
|
|
4618
4906
|
}
|
|
4619
4907
|
function useSubscriptionSync() {
|
|
4620
4908
|
const [subscription, setSubscription] = (0, import_react17.useState)(null);
|
|
4621
4909
|
const cacheRef = (0, import_react17.useRef)(null);
|
|
4622
4910
|
const invalidateCache = (0, import_react17.useCallback)(() => {
|
|
4623
4911
|
cacheRef.current = null;
|
|
4624
|
-
|
|
4912
|
+
const distinctId = PaywalloClient.getDistinctId();
|
|
4913
|
+
if (distinctId) void subscriptionCache.invalidate(distinctId);
|
|
4625
4914
|
}, []);
|
|
4626
4915
|
const fetchAndCache = (0, import_react17.useCallback)(async () => {
|
|
4627
4916
|
const apiClient = PaywalloClient.getApiClient();
|
|
@@ -4634,7 +4923,7 @@ function useSubscriptionSync() {
|
|
|
4634
4923
|
} : null;
|
|
4635
4924
|
cacheRef.current = { data: sub, expiresAt: Date.now() + CACHE_TTL_MS2 };
|
|
4636
4925
|
setSubscription(sub);
|
|
4637
|
-
void subscriptionCache.set(toDomainResponse(status));
|
|
4926
|
+
void subscriptionCache.set(distinctId, toDomainResponse(status));
|
|
4638
4927
|
return sub;
|
|
4639
4928
|
}, []);
|
|
4640
4929
|
const hasActiveSubscription = (0, import_react17.useCallback)(async () => {
|
|
@@ -4646,23 +4935,23 @@ function useSubscriptionSync() {
|
|
|
4646
4935
|
return isActiveCacheEntry(cached);
|
|
4647
4936
|
}
|
|
4648
4937
|
try {
|
|
4649
|
-
const persisted = await subscriptionCache.get();
|
|
4650
|
-
if (persisted) {
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4938
|
+
const persisted = await subscriptionCache.get(distinctId);
|
|
4939
|
+
if (persisted && !persisted.isStale) {
|
|
4940
|
+
const persistedSub = persisted.data.subscription;
|
|
4941
|
+
const sub = persistedSub ? {
|
|
4942
|
+
productId: persistedSub.productId,
|
|
4943
|
+
status: persistedSub.status,
|
|
4944
|
+
expiresAt: persistedSub.expiresAt ?? null,
|
|
4945
|
+
platform: persistedSub.platform,
|
|
4946
|
+
autoRenewEnabled: persistedSub.willRenew,
|
|
4947
|
+
inGracePeriod: persistedSub.status === "grace_period"
|
|
4948
|
+
} : null;
|
|
4949
|
+
const stillActive = isSubscriptionStillActive(sub);
|
|
4950
|
+
if (stillActive) {
|
|
4660
4951
|
cacheRef.current = { data: sub, expiresAt: Date.now() + CACHE_TTL_MS2 };
|
|
4661
4952
|
setSubscription(sub);
|
|
4662
|
-
return
|
|
4953
|
+
return true;
|
|
4663
4954
|
}
|
|
4664
|
-
void fetchAndCache();
|
|
4665
|
-
return persisted.data.hasActiveSubscription;
|
|
4666
4955
|
}
|
|
4667
4956
|
} catch {
|
|
4668
4957
|
}
|
|
@@ -4674,12 +4963,12 @@ function useSubscriptionSync() {
|
|
|
4674
4963
|
} : null;
|
|
4675
4964
|
cacheRef.current = { data: sub, expiresAt: Date.now() + CACHE_TTL_MS2 };
|
|
4676
4965
|
setSubscription(sub);
|
|
4677
|
-
void subscriptionCache.set(toDomainResponse(status));
|
|
4966
|
+
void subscriptionCache.set(distinctId, toDomainResponse(status));
|
|
4678
4967
|
return status.hasActiveSubscription;
|
|
4679
4968
|
} catch {
|
|
4680
|
-
return await resolveOfflineFromCache();
|
|
4969
|
+
return await resolveOfflineFromCache(distinctId);
|
|
4681
4970
|
}
|
|
4682
|
-
}, [
|
|
4971
|
+
}, []);
|
|
4683
4972
|
const getSubscription = (0, import_react17.useCallback)(async () => {
|
|
4684
4973
|
const apiClient = PaywalloClient.getApiClient();
|
|
4685
4974
|
const distinctId = PaywalloClient.getDistinctId();
|
|
@@ -4690,7 +4979,7 @@ function useSubscriptionSync() {
|
|
|
4690
4979
|
return await fetchAndCache();
|
|
4691
4980
|
} catch {
|
|
4692
4981
|
try {
|
|
4693
|
-
const persisted = await subscriptionCache.get();
|
|
4982
|
+
const persisted = await subscriptionCache.get(distinctId);
|
|
4694
4983
|
if (!persisted?.data.subscription) return null;
|
|
4695
4984
|
const s = persisted.data.subscription;
|
|
4696
4985
|
const normalisedStatus = s.status === "grace_period" ? "in_grace_period" : s.status;
|
|
@@ -4721,13 +5010,38 @@ function PaywalloProvider({ children, config }) {
|
|
|
4721
5010
|
const { subscription, hasActiveSubscription, getSubscription, invalidateCache } = useSubscriptionSync();
|
|
4722
5011
|
const { preloadedWebView, setPreloadedWebView, preloadCampaign, consumePreloadedCampaign, isPreloadValid } = useCampaignPreload();
|
|
4723
5012
|
const clearPreloadedWebView = (0, import_react18.useCallback)(() => setPreloadedWebView(null), [setPreloadedWebView]);
|
|
4724
|
-
const
|
|
5013
|
+
const autoPreloadTriggeredRef = (0, import_react18.useRef)(false);
|
|
5014
|
+
const preloadedWebViewRef = (0, import_react18.useRef)(preloadedWebView);
|
|
5015
|
+
(0, import_react18.useEffect)(() => {
|
|
5016
|
+
preloadedWebViewRef.current = preloadedWebView;
|
|
5017
|
+
}, [preloadedWebView]);
|
|
5018
|
+
const triggerRepreload = (0, import_react18.useCallback)((placement) => {
|
|
5019
|
+
void PaywalloClient.preloadCampaign(placement).catch(() => {
|
|
5020
|
+
});
|
|
5021
|
+
}, []);
|
|
5022
|
+
const { showingPreloadedWebView, presentPreloadedWebView, resolvePreloadedPaywall, handlePreloadedWebViewReady: baseHandlePreloadedWebViewReady, handlePreloadedClose: baseHandlePreloadedClose, setShowingPreloadedWebView, presentedAtRef } = usePaywallPresenter(preloadedWebView, setPreloadedWebView);
|
|
5023
|
+
const SCREEN_HEIGHT3 = (0, import_react18.useMemo)(() => import_react_native15.Dimensions.get("window").height, []);
|
|
5024
|
+
const slideAnim = (0, import_react18.useRef)(new import_react_native15.Animated.Value(SCREEN_HEIGHT3)).current;
|
|
5025
|
+
const handlePreloadedWebViewReady = (0, import_react18.useCallback)(() => {
|
|
5026
|
+
if (PaywalloClient.getConfig()?.debug) {
|
|
5027
|
+
console.info(`[Paywallo WEBVIEW] loaded \u2014 placement: ${preloadedWebView?.placement}`);
|
|
5028
|
+
}
|
|
5029
|
+
baseHandlePreloadedWebViewReady();
|
|
5030
|
+
}, [config?.debug, preloadedWebView?.placement, baseHandlePreloadedWebViewReady]);
|
|
5031
|
+
const handlePreloadedClose = (0, import_react18.useCallback)(() => {
|
|
5032
|
+
const placement = preloadedWebView?.placement;
|
|
5033
|
+
import_react_native15.Animated.timing(slideAnim, { toValue: SCREEN_HEIGHT3, duration: 200, easing: import_react_native15.Easing.in(import_react_native15.Easing.cubic), useNativeDriver: true }).start(() => {
|
|
5034
|
+
baseHandlePreloadedClose();
|
|
5035
|
+
if (placement) triggerRepreload(placement);
|
|
5036
|
+
});
|
|
5037
|
+
}, [baseHandlePreloadedClose, preloadedWebView, triggerRepreload, slideAnim, SCREEN_HEIGHT3]);
|
|
4725
5038
|
const { handlePreloadedPurchase: rawPreloadedPurchase, handlePreloadedRestore } = usePurchaseOrchestrator(
|
|
4726
5039
|
preloadedWebView,
|
|
4727
5040
|
resolvePreloadedPaywall,
|
|
4728
5041
|
clearPreloadedWebView,
|
|
4729
5042
|
setShowingPreloadedWebView,
|
|
4730
|
-
presentedAtRef
|
|
5043
|
+
presentedAtRef,
|
|
5044
|
+
invalidateCache
|
|
4731
5045
|
);
|
|
4732
5046
|
const [isPreloadPurchasing, setIsPreloadPurchasing] = (0, import_react18.useState)(false);
|
|
4733
5047
|
const handlePreloadedPurchase = (0, import_react18.useCallback)(async (productId) => {
|
|
@@ -4740,13 +5054,19 @@ function PaywalloProvider({ children, config }) {
|
|
|
4740
5054
|
}, [rawPreloadedPurchase]);
|
|
4741
5055
|
(0, import_react18.useEffect)(() => {
|
|
4742
5056
|
initLocalization({ detectDevice: true });
|
|
4743
|
-
PaywalloClient.init(config).then(() => {
|
|
5057
|
+
PaywalloClient.init(config).then(async () => {
|
|
4744
5058
|
setDistinctId(PaywalloClient.getDistinctId());
|
|
4745
5059
|
setIsInitialized(true);
|
|
5060
|
+
if (!autoPreloadTriggeredRef.current) {
|
|
5061
|
+
autoPreloadTriggeredRef.current = true;
|
|
5062
|
+
const placement = await PaywalloClient.waitForAutoPreloadedPlacement();
|
|
5063
|
+
if (placement) void preloadCampaign(placement).catch(() => {
|
|
5064
|
+
});
|
|
5065
|
+
}
|
|
4746
5066
|
}).catch((err) => {
|
|
4747
5067
|
setInitError(err instanceof Error ? err : new ClientError(CLIENT_ERROR_CODES.NOT_INITIALIZED, String(err)));
|
|
4748
5068
|
});
|
|
4749
|
-
}, [config]);
|
|
5069
|
+
}, [config, preloadCampaign]);
|
|
4750
5070
|
const getPaywallConfig = (0, import_react18.useCallback)(async (p) => {
|
|
4751
5071
|
const api = PaywalloClient.getApiClient();
|
|
4752
5072
|
return api ? api.getPaywall(p) : null;
|
|
@@ -4845,28 +5165,54 @@ function PaywalloProvider({ children, config }) {
|
|
|
4845
5165
|
setActivePaywall(null);
|
|
4846
5166
|
}
|
|
4847
5167
|
}, [activePaywall]);
|
|
5168
|
+
const bridgePaywallPresenter = (0, import_react18.useCallback)((placement) => {
|
|
5169
|
+
if (PaywalloClient.getConfig()?.debug) {
|
|
5170
|
+
console.info(`[Paywallo PRESENT] called \u2014 preloaded: ${preloadedWebViewRef.current?.loaded === true} placement: ${placement}`);
|
|
5171
|
+
}
|
|
5172
|
+
if (preloadedWebViewRef.current?.placement === placement) {
|
|
5173
|
+
if (preloadedWebViewRef.current.loaded) {
|
|
5174
|
+
return presentPreloadedWebView();
|
|
5175
|
+
}
|
|
5176
|
+
return new Promise((resolve) => {
|
|
5177
|
+
let elapsed = 0;
|
|
5178
|
+
const poll = () => {
|
|
5179
|
+
if (preloadedWebViewRef.current?.loaded) {
|
|
5180
|
+
resolve(presentPreloadedWebView());
|
|
5181
|
+
return;
|
|
5182
|
+
}
|
|
5183
|
+
elapsed += 100;
|
|
5184
|
+
if (elapsed >= 500) {
|
|
5185
|
+
resolve(presentCampaign(placement));
|
|
5186
|
+
return;
|
|
5187
|
+
}
|
|
5188
|
+
setTimeout(poll, 100);
|
|
5189
|
+
};
|
|
5190
|
+
setTimeout(poll, 100);
|
|
5191
|
+
});
|
|
5192
|
+
}
|
|
5193
|
+
return presentCampaign(placement);
|
|
5194
|
+
}, [presentPreloadedWebView, presentCampaign]);
|
|
4848
5195
|
(0, import_react18.useEffect)(() => {
|
|
4849
5196
|
if (!isInitialized) return;
|
|
4850
5197
|
const onEmergency = async (id) => {
|
|
4851
5198
|
try {
|
|
4852
|
-
await
|
|
5199
|
+
await bridgePaywallPresenter(id);
|
|
4853
5200
|
} catch {
|
|
4854
5201
|
}
|
|
4855
5202
|
};
|
|
4856
|
-
PaywalloClient.registerPaywallPresenter(
|
|
5203
|
+
PaywalloClient.registerPaywallPresenter(bridgePaywallPresenter);
|
|
4857
5204
|
PaywalloClient.registerSubscriptionGetter(getSubscription);
|
|
4858
5205
|
PaywalloClient.registerActiveChecker(hasActiveSubscription);
|
|
4859
5206
|
PaywalloClient.registerRestoreHandler(restorePurchases);
|
|
4860
5207
|
PaywalloClient.registerEmergencyPaywallHandler(onEmergency);
|
|
4861
|
-
}, [isInitialized,
|
|
4862
|
-
|
|
4863
|
-
if (activePaywall) setPreloadedWebView(null);
|
|
4864
|
-
}, [activePaywall, setPreloadedWebView]);
|
|
5208
|
+
}, [isInitialized, bridgePaywallPresenter, getSubscription, hasActiveSubscription, restorePurchases]);
|
|
5209
|
+
const isReady = isInitialized && !isLoadingProducts;
|
|
4865
5210
|
const noOp = (0, import_react18.useCallback)(() => {
|
|
4866
5211
|
}, []);
|
|
4867
5212
|
const contextValue = (0, import_react18.useMemo)(() => ({
|
|
4868
5213
|
config: PaywalloClient.getConfig(),
|
|
4869
5214
|
isInitialized,
|
|
5215
|
+
isReady,
|
|
4870
5216
|
initError,
|
|
4871
5217
|
distinctId,
|
|
4872
5218
|
subscription,
|
|
@@ -4882,6 +5228,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
4882
5228
|
refreshProducts
|
|
4883
5229
|
}), [
|
|
4884
5230
|
isInitialized,
|
|
5231
|
+
isReady,
|
|
4885
5232
|
initError,
|
|
4886
5233
|
distinctId,
|
|
4887
5234
|
subscription,
|
|
@@ -4896,24 +5243,20 @@ function PaywalloProvider({ children, config }) {
|
|
|
4896
5243
|
getPaywallConfig,
|
|
4897
5244
|
refreshProducts
|
|
4898
5245
|
]);
|
|
4899
|
-
|
|
4900
|
-
const slideAnim = (0, import_react18.useRef)(new import_react_native14.Animated.Value(SCREEN_HEIGHT3)).current;
|
|
4901
|
-
(0, import_react18.useEffect)(() => {
|
|
5246
|
+
(0, import_react18.useLayoutEffect)(() => {
|
|
4902
5247
|
if (showingPreloadedWebView) {
|
|
4903
5248
|
slideAnim.setValue(SCREEN_HEIGHT3);
|
|
4904
|
-
|
|
4905
|
-
} else {
|
|
4906
|
-
slideAnim.setValue(SCREEN_HEIGHT3);
|
|
5249
|
+
import_react_native15.Animated.timing(slideAnim, { toValue: 0, duration: 200, easing: import_react_native15.Easing.out(import_react_native15.Easing.cubic), useNativeDriver: true }).start();
|
|
4907
5250
|
}
|
|
4908
5251
|
}, [showingPreloadedWebView, slideAnim, SCREEN_HEIGHT3]);
|
|
4909
5252
|
const preloadStyle = [
|
|
4910
5253
|
styles3.preloadOverlay,
|
|
4911
5254
|
showingPreloadedWebView ? styles3.visible : styles3.hidden,
|
|
4912
|
-
{ transform: [{ translateY:
|
|
5255
|
+
showingPreloadedWebView ? { transform: [{ translateY: slideAnim }] } : void 0
|
|
4913
5256
|
];
|
|
4914
5257
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(PaywalloContext.Provider, { value: contextValue, children: [
|
|
4915
5258
|
children,
|
|
4916
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PaywallErrorBoundary, { children: !!preloadedWebView?.craftData && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
5259
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PaywallErrorBoundary, { children: !!preloadedWebView?.craftData && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native15.Animated.View, { style: preloadStyle, pointerEvents: showingPreloadedWebView ? "auto" : "none", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
4917
5260
|
PaywallWebView,
|
|
4918
5261
|
{
|
|
4919
5262
|
paywallId: preloadedWebView.paywallId,
|
|
@@ -4922,6 +5265,7 @@ function PaywalloProvider({ children, config }) {
|
|
|
4922
5265
|
primaryProductId: preloadedWebView.primaryProductId,
|
|
4923
5266
|
secondaryProductId: preloadedWebView.secondaryProductId,
|
|
4924
5267
|
isPurchasing: isPreloadPurchasing,
|
|
5268
|
+
webUrl: isInitialized ? PaywalloClient.getWebUrl() : null,
|
|
4925
5269
|
onReady: handlePreloadedWebViewReady,
|
|
4926
5270
|
onClose: showingPreloadedWebView ? handlePreloadedClose : noOp,
|
|
4927
5271
|
onPurchase: showingPreloadedWebView ? handlePreloadedPurchase : noOp,
|
|
@@ -4942,10 +5286,10 @@ function PaywalloProvider({ children, config }) {
|
|
|
4942
5286
|
) })
|
|
4943
5287
|
] });
|
|
4944
5288
|
}
|
|
4945
|
-
var styles3 =
|
|
5289
|
+
var styles3 = import_react_native15.StyleSheet.create({
|
|
4946
5290
|
preloadOverlay: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, backgroundColor: "#ffffff" },
|
|
4947
|
-
hidden: { zIndex: -1 },
|
|
4948
|
-
visible: { zIndex: 9999 }
|
|
5291
|
+
hidden: { zIndex: -1, opacity: 0, width: 1, height: 1, overflow: "hidden" },
|
|
5292
|
+
visible: { zIndex: 9999, opacity: 1 }
|
|
4949
5293
|
});
|
|
4950
5294
|
|
|
4951
5295
|
// src/utils/productVariableResolver.ts
|
|
@@ -5085,6 +5429,7 @@ var index_default = Paywallo;
|
|
|
5085
5429
|
IDENTITY_ERROR_CODES,
|
|
5086
5430
|
IdentityError,
|
|
5087
5431
|
IdentityManager,
|
|
5432
|
+
MetaBridge,
|
|
5088
5433
|
PAYWALL_ERROR_CODES,
|
|
5089
5434
|
PURCHASE_ERROR_CODES,
|
|
5090
5435
|
PaywallContext,
|
|
@@ -5111,6 +5456,7 @@ var index_default = Paywallo;
|
|
|
5111
5456
|
hasVariables,
|
|
5112
5457
|
identityManager,
|
|
5113
5458
|
initLocalization,
|
|
5459
|
+
metaBridge,
|
|
5114
5460
|
nativeStoreKit,
|
|
5115
5461
|
networkMonitor,
|
|
5116
5462
|
offlineQueue,
|