@umituz/react-native-subscription 3.1.34 → 3.1.36
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/package.json +1 -1
- package/src/domains/credits/application/CreditsInitializer.ts +34 -39
- package/src/domains/credits/application/DeductCreditsCommand.ts +13 -12
- package/src/domains/credits/infrastructure/operations/CreditsWriter.ts +1 -1
- package/src/domains/credits/presentation/deduct-credit/useDeductCredit.ts +1 -1
- package/src/domains/credits/presentation/useCredits.types.ts +1 -1
- package/src/domains/paywall/components/PaywallScreen.tsx +12 -11
- package/src/domains/paywall/hooks/usePaywallActions.ts +4 -3
- package/src/domains/paywall/hooks/usePaywallActions.utils.ts +14 -19
- package/src/domains/paywall/hooks/usePaywallPurchase.ts +10 -17
- package/src/domains/paywall/hooks/usePaywallRestore.ts +8 -15
- package/src/domains/revenuecat/core/errors/RevenueCatError.ts +1 -1
- package/src/domains/revenuecat/infrastructure/services/ConfigurationStateManager.ts +1 -1
- package/src/domains/revenuecat/infrastructure/services/RevenueCatInitializer.ts +7 -6
- package/src/domains/revenuecat/infrastructure/services/UserSwitchMutex.ts +8 -10
- package/src/domains/revenuecat/infrastructure/services/initializerConstants.ts +1 -1
- package/src/domains/revenuecat/infrastructure/services/userSwitchCore.ts +17 -34
- package/src/domains/revenuecat/infrastructure/services/userSwitchHelpers.ts +4 -5
- package/src/domains/revenuecat/infrastructure/services/userSwitchInitializer.ts +19 -29
- package/src/domains/subscription/application/initializer/BackgroundInitializer.ts +17 -29
- package/src/domains/subscription/application/sync/CreditDocumentOperations.ts +16 -17
- package/src/domains/subscription/application/sync/PurchaseSyncHandler.ts +20 -23
- package/src/domains/subscription/application/sync/RenewalSyncHandler.ts +8 -7
- package/src/domains/subscription/application/sync/StatusChangeSyncHandler.ts +4 -3
- package/src/domains/subscription/application/sync/SyncProcessorLogger.ts +40 -65
- package/src/domains/subscription/application/sync/UserIdResolver.ts +5 -1
- package/src/domains/subscription/infrastructure/handlers/PackageHandler.ts +1 -1
- package/src/domains/subscription/infrastructure/handlers/PurchaseStatusResolver.ts +1 -1
- package/src/domains/subscription/infrastructure/handlers/package-operations/PackageFetcher.ts +7 -6
- package/src/domains/subscription/infrastructure/handlers/package-operations/PackagePurchaser.ts +8 -7
- package/src/domains/subscription/infrastructure/hooks/usePurchasePackage.ts +4 -3
- package/src/domains/subscription/infrastructure/managers/SubscriptionManager.ts +21 -28
- package/src/domains/subscription/infrastructure/managers/initializationHandler.ts +1 -1
- package/src/domains/subscription/infrastructure/managers/managerOperations.ts +1 -1
- package/src/domains/subscription/infrastructure/managers/packageHandlerFactory.ts +1 -1
- package/src/domains/subscription/infrastructure/managers/premiumStatusChecker.ts +1 -1
- package/src/domains/subscription/infrastructure/managers/subscriptionManagerUtils.ts +1 -1
- package/src/domains/subscription/infrastructure/services/CustomerInfoListenerManager.ts +10 -9
- package/src/domains/subscription/infrastructure/services/OfferingsFetcher.ts +14 -21
- package/src/domains/subscription/infrastructure/services/PurchaseHandler.ts +9 -8
- package/src/domains/subscription/infrastructure/services/RestoreHandler.ts +1 -1
- package/src/domains/subscription/infrastructure/services/RevenueCatService.types.ts +5 -4
- package/src/domains/subscription/infrastructure/services/listeners/CustomerInfoHandler.ts +15 -29
- package/src/domains/subscription/infrastructure/services/purchase/PurchaseErrorHandler.ts +4 -2
- package/src/domains/subscription/infrastructure/services/purchase/PurchaseExecutor.ts +27 -33
- package/src/domains/subscription/infrastructure/utils/InitializationCache.ts +5 -1
- package/src/domains/subscription/infrastructure/utils/PremiumStatusSyncer.ts +11 -17
- package/src/domains/subscription/presentation/providers/SubscriptionFlowProvider.tsx +11 -12
- package/src/domains/subscription/presentation/screens/SubscriptionDetailScreen.tsx +1 -1
- package/src/domains/subscription/presentation/useSyncStatusListener.ts +10 -9
- package/src/domains/wallet/presentation/components/TransactionList.tsx +1 -1
- package/src/domains/wallet/presentation/hooks/useTransactionHistory.ts +2 -2
- package/src/domains/wallet/presentation/screens/WalletScreen.tsx +1 -1
- package/src/init/createSubscriptionInitModule.ts +4 -1
- package/src/shared/infrastructure/SubscriptionEventBus.ts +4 -1
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
* Centralized logging for subscription sync operations
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { subscriptionEventBus, SUBSCRIPTION_EVENTS } from "
|
|
6
|
+
import { subscriptionEventBus, SUBSCRIPTION_EVENTS } from "../../../shared/infrastructure/SubscriptionEventBus";
|
|
7
7
|
import type { PurchaseCompletedEvent, RenewalDetectedEvent, PremiumStatusChangedEvent } from "../../core/SubscriptionEvents";
|
|
8
|
+
import { createLogger } from "../../../shared/utils/logger";
|
|
9
|
+
|
|
10
|
+
const logger = createLogger("SubscriptionSyncProcessor");
|
|
8
11
|
|
|
9
12
|
export type SyncPhase = 'purchase' | 'renewal' | 'status_change';
|
|
10
13
|
|
|
@@ -24,97 +27,69 @@ export class SyncProcessorLogger {
|
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
logPurchaseStart(event: PurchaseCompletedEvent) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
});
|
|
35
|
-
}
|
|
30
|
+
logger.debug("PURCHASE START", {
|
|
31
|
+
userId: event.userId,
|
|
32
|
+
productId: event.productId,
|
|
33
|
+
source: event.source,
|
|
34
|
+
packageType: event.packageType,
|
|
35
|
+
timestamp: new Date().toISOString(),
|
|
36
|
+
});
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
logPurchaseSuccess(userId: string, productId: string) {
|
|
39
|
-
|
|
40
|
-
console.log('[SubscriptionSyncProcessor] 🟢 PURCHASE SUCCESS', {
|
|
41
|
-
userId,
|
|
42
|
-
productId,
|
|
43
|
-
timestamp: new Date().toISOString(),
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
logPurchaseError(userId: string, productId: string, error: string) {
|
|
49
|
-
console.error('[SubscriptionSyncProcessor] 🔴 PURCHASE FAILED', {
|
|
40
|
+
logger.debug("PURCHASE SUCCESS", {
|
|
50
41
|
userId,
|
|
51
42
|
productId,
|
|
52
|
-
error,
|
|
53
43
|
timestamp: new Date().toISOString(),
|
|
54
44
|
});
|
|
55
45
|
}
|
|
56
46
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
console.log('[SubscriptionSyncProcessor] 🔵 RENEWAL START', {
|
|
60
|
-
userId: event.userId,
|
|
61
|
-
productId: event.productId,
|
|
62
|
-
newExpirationDate: event.newExpirationDate,
|
|
63
|
-
timestamp: new Date().toISOString(),
|
|
64
|
-
});
|
|
65
|
-
}
|
|
47
|
+
logPurchaseError(userId: string, productId: string, error: string) {
|
|
48
|
+
logger.error("PURCHASE FAILED", error, { userId, productId, timestamp: new Date().toISOString() });
|
|
66
49
|
}
|
|
67
50
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
51
|
+
logRenewalStart(event: RenewalDetectedEvent) {
|
|
52
|
+
logger.debug("RENEWAL START", {
|
|
53
|
+
userId: event.userId,
|
|
54
|
+
productId: event.productId,
|
|
55
|
+
newExpirationDate: event.newExpirationDate,
|
|
56
|
+
timestamp: new Date().toISOString(),
|
|
57
|
+
});
|
|
76
58
|
}
|
|
77
59
|
|
|
78
|
-
|
|
79
|
-
|
|
60
|
+
logRenewalSuccess(userId: string, productId: string) {
|
|
61
|
+
logger.debug("RENEWAL SUCCESS", {
|
|
80
62
|
userId,
|
|
81
63
|
productId,
|
|
82
|
-
error,
|
|
83
64
|
timestamp: new Date().toISOString(),
|
|
84
65
|
});
|
|
85
66
|
}
|
|
86
67
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
console.log('[SubscriptionSyncProcessor] 🔵 STATUS CHANGE START', {
|
|
90
|
-
userId: event.userId,
|
|
91
|
-
isPremium: event.isPremium,
|
|
92
|
-
productId: event.productId,
|
|
93
|
-
willRenew: event.willRenew,
|
|
94
|
-
expirationDate: event.expirationDate,
|
|
95
|
-
timestamp: new Date().toISOString(),
|
|
96
|
-
});
|
|
97
|
-
}
|
|
68
|
+
logRenewalError(userId: string, productId: string, error: string) {
|
|
69
|
+
logger.error("RENEWAL FAILED", error, { userId, productId, timestamp: new Date().toISOString() });
|
|
98
70
|
}
|
|
99
71
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
72
|
+
logStatusChangeStart(event: PremiumStatusChangedEvent) {
|
|
73
|
+
logger.debug("STATUS CHANGE START", {
|
|
74
|
+
userId: event.userId,
|
|
75
|
+
isPremium: event.isPremium,
|
|
76
|
+
productId: event.productId,
|
|
77
|
+
willRenew: event.willRenew,
|
|
78
|
+
expirationDate: event.expirationDate,
|
|
79
|
+
timestamp: new Date().toISOString(),
|
|
80
|
+
});
|
|
109
81
|
}
|
|
110
82
|
|
|
111
|
-
|
|
112
|
-
|
|
83
|
+
logStatusChangeSuccess(userId: string, isPremium: boolean, productId?: string) {
|
|
84
|
+
logger.debug("STATUS CHANGE SUCCESS", {
|
|
113
85
|
userId,
|
|
114
86
|
isPremium,
|
|
115
87
|
productId,
|
|
116
|
-
error,
|
|
117
88
|
timestamp: new Date().toISOString(),
|
|
118
89
|
});
|
|
119
90
|
}
|
|
91
|
+
|
|
92
|
+
logStatusChangeError(userId: string, isPremium: boolean, productId: string | undefined, error: string) {
|
|
93
|
+
logger.error("STATUS CHANGE FAILED", error, { userId, isPremium, productId, timestamp: new Date().toISOString() });
|
|
94
|
+
}
|
|
120
95
|
}
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
* Handles resolution of RevenueCat user ID to credits user ID
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { createLogger } from "../../../shared/utils/logger";
|
|
7
|
+
|
|
8
|
+
const logger = createLogger("UserIdResolver");
|
|
9
|
+
|
|
6
10
|
export class UserIdResolver {
|
|
7
11
|
constructor(private getAnonymousUserId: () => Promise<string>) {}
|
|
8
12
|
|
|
@@ -14,7 +18,7 @@ export class UserIdResolver {
|
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
// Fallback to anonymous user ID
|
|
17
|
-
|
|
21
|
+
logger.warn("revenueCatUserId is empty/null, falling back to anonymousUserId");
|
|
18
22
|
const anonymousId = await this.getAnonymousUserId();
|
|
19
23
|
const trimmedAnonymous = anonymousId?.trim();
|
|
20
24
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PurchasesPackage, CustomerInfo } from "react-native-purchases";
|
|
2
|
-
import type { IRevenueCatService } from "
|
|
2
|
+
import type { IRevenueCatService } from "../../../shared/application/ports/IRevenueCatService";
|
|
3
3
|
import { PurchaseStatusResolver, type PremiumStatus } from "./PurchaseStatusResolver";
|
|
4
4
|
import { fetchPackages } from "./package-operations/PackageFetcher";
|
|
5
5
|
import { executePurchase } from "./package-operations/PackagePurchaser";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CustomerInfo } from "react-native-purchases";
|
|
2
2
|
import { getPremiumEntitlement } from "../../../revenuecat/core/types/RevenueCatTypes";
|
|
3
|
-
import { toDate } from "
|
|
3
|
+
import { toDate } from "../../../shared/utils/dateConverter";
|
|
4
4
|
import { detectPackageType } from "../../../../utils/packageTypeDetector";
|
|
5
5
|
import type { PremiumStatus } from "../../core/types/PremiumStatus";
|
|
6
6
|
|
package/src/domains/subscription/infrastructure/handlers/package-operations/PackageFetcher.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { PurchasesPackage } from "react-native-purchases";
|
|
2
2
|
import type { IRevenueCatService } from "../../../../../shared/application/ports/IRevenueCatService";
|
|
3
|
+
import { createLogger } from "../../../../../shared/utils/logger";
|
|
4
|
+
|
|
5
|
+
const logger = createLogger("PackageFetcher");
|
|
3
6
|
|
|
4
7
|
export async function fetchPackages(
|
|
5
8
|
service: IRevenueCatService
|
|
@@ -20,12 +23,10 @@ export async function fetchPackages(
|
|
|
20
23
|
return [];
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
});
|
|
28
|
-
}
|
|
26
|
+
logger.debug("Returning packages", {
|
|
27
|
+
count: packages.length,
|
|
28
|
+
packageIds: packages.map(p => p.product.identifier),
|
|
29
|
+
});
|
|
29
30
|
|
|
30
31
|
return packages;
|
|
31
32
|
} catch (error) {
|
package/src/domains/subscription/infrastructure/handlers/package-operations/PackagePurchaser.ts
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import type { PurchasesPackage } from "react-native-purchases";
|
|
2
2
|
import type { IRevenueCatService } from "../../../../../shared/application/ports/IRevenueCatService";
|
|
3
|
+
import { createLogger } from "../../../../../shared/utils/logger";
|
|
4
|
+
|
|
5
|
+
const logger = createLogger("PackagePurchaser");
|
|
3
6
|
|
|
4
7
|
export async function executePurchase(
|
|
5
8
|
service: IRevenueCatService,
|
|
6
9
|
pkg: PurchasesPackage,
|
|
7
10
|
userId: string
|
|
8
11
|
): Promise<boolean> {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
});
|
|
15
|
-
}
|
|
12
|
+
logger.debug("executePurchase called", {
|
|
13
|
+
productId: pkg.product.identifier,
|
|
14
|
+
userId,
|
|
15
|
+
serviceInitialized: service.isInitialized(),
|
|
16
|
+
});
|
|
16
17
|
|
|
17
18
|
if (!service.isInitialized()) {
|
|
18
19
|
throw new Error("Service not initialized");
|
|
@@ -7,6 +7,9 @@ import {
|
|
|
7
7
|
} from "@umituz/react-native-auth";
|
|
8
8
|
import { SubscriptionManager } from "../../infrastructure/managers/SubscriptionManager";
|
|
9
9
|
import { getErrorMessage } from "../../../revenuecat/core/errors/RevenueCatErrorHandler";
|
|
10
|
+
import { createLogger } from "../../../shared/utils/logger";
|
|
11
|
+
|
|
12
|
+
const logger = createLogger("usePurchasePackage");
|
|
10
13
|
|
|
11
14
|
interface PurchaseMutationResult {
|
|
12
15
|
success: boolean;
|
|
@@ -24,9 +27,7 @@ export const usePurchasePackage = () => {
|
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
const productId = pkg.product.identifier;
|
|
27
|
-
|
|
28
|
-
console.log(`[Purchase] Initializing and purchasing. User: ${userId}`);
|
|
29
|
-
}
|
|
30
|
+
logger.debug("Initializing and purchasing", { userId, productId });
|
|
30
31
|
|
|
31
32
|
const success = await SubscriptionManager.purchasePackage(pkg, userId);
|
|
32
33
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PurchasesPackage } from "react-native-purchases";
|
|
2
|
-
import type { IRevenueCatService } from "
|
|
2
|
+
import type { IRevenueCatService } from "../../../shared/application/ports/IRevenueCatService";
|
|
3
3
|
import type { PackageHandler } from "../handlers/PackageHandler";
|
|
4
4
|
import { InitializationCache } from "../utils/InitializationCache";
|
|
5
5
|
import { ensureServiceAvailable, getCurrentUserIdOrThrow } from "./subscriptionManagerUtils";
|
|
@@ -10,6 +10,9 @@ import { getPackagesOperation, purchasePackageOperation, restoreOperation } from
|
|
|
10
10
|
import { performServiceInitialization } from "./initializationHandler";
|
|
11
11
|
import { initializationState } from "../state/initializationState";
|
|
12
12
|
import { ANONYMOUS_CACHE_KEY } from "../../core/SubscriptionConstants";
|
|
13
|
+
import { createLogger } from "../../../shared/utils/logger";
|
|
14
|
+
|
|
15
|
+
const logger = createLogger("SubscriptionManager");
|
|
13
16
|
|
|
14
17
|
class SubscriptionManagerImpl {
|
|
15
18
|
private managerConfig: SubscriptionManagerConfig | null = null;
|
|
@@ -23,14 +26,14 @@ class SubscriptionManagerImpl {
|
|
|
23
26
|
|
|
24
27
|
private ensureConfigured(): void {
|
|
25
28
|
if (!this.managerConfig) {
|
|
26
|
-
throw new Error('
|
|
29
|
+
throw new Error('SubscriptionManager: Not configured. Call configure() first.');
|
|
27
30
|
}
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
private ensurePackageHandlerInitialized(): void {
|
|
31
34
|
if (this.packageHandler) return;
|
|
32
35
|
if (!this.serviceInstance || !this.managerConfig) {
|
|
33
|
-
throw new Error('
|
|
36
|
+
throw new Error('SubscriptionManager: Cannot create package handler without service and config');
|
|
34
37
|
}
|
|
35
38
|
this.packageHandler = createPackageHandler(this.serviceInstance, this.managerConfig);
|
|
36
39
|
}
|
|
@@ -40,20 +43,16 @@ class SubscriptionManagerImpl {
|
|
|
40
43
|
|
|
41
44
|
const actualUserId: string = (userId && userId.length > 0) ? userId : '';
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
});
|
|
48
|
-
}
|
|
46
|
+
logger.debug("initialize called", {
|
|
47
|
+
providedUserId: userId,
|
|
48
|
+
actualUserId: actualUserId || '(empty - RevenueCat will generate anonymous ID)',
|
|
49
|
+
});
|
|
49
50
|
|
|
50
51
|
const cacheKey = actualUserId || ANONYMOUS_CACHE_KEY;
|
|
51
52
|
const { shouldInit, existingPromise } = this.initCache.tryAcquireInitialization(cacheKey);
|
|
52
53
|
|
|
53
54
|
if (!shouldInit && existingPromise) {
|
|
54
|
-
|
|
55
|
-
console.log('[SubscriptionManager] Using cached initialization for:', cacheKey);
|
|
56
|
-
}
|
|
55
|
+
logger.debug("Using cached initialization for", cacheKey);
|
|
57
56
|
return existingPromise;
|
|
58
57
|
}
|
|
59
58
|
|
|
@@ -67,11 +66,9 @@ class SubscriptionManagerImpl {
|
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
private async performInitialization(userId: string): Promise<boolean> {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
});
|
|
74
|
-
}
|
|
69
|
+
logger.debug("performInitialization", {
|
|
70
|
+
userId: userId || '(empty - anonymous)',
|
|
71
|
+
});
|
|
75
72
|
|
|
76
73
|
const config = this.managerConfig!;
|
|
77
74
|
const { service, success } = await performServiceInitialization(config.config, userId);
|
|
@@ -83,9 +80,7 @@ class SubscriptionManagerImpl {
|
|
|
83
80
|
initializationState.markInitialized(notifyUserId);
|
|
84
81
|
}
|
|
85
82
|
|
|
86
|
-
|
|
87
|
-
console.log('[SubscriptionManager] Initialization completed:', { success });
|
|
88
|
-
}
|
|
83
|
+
logger.debug("Initialization completed", { success });
|
|
89
84
|
|
|
90
85
|
return success;
|
|
91
86
|
}
|
|
@@ -107,14 +102,12 @@ class SubscriptionManagerImpl {
|
|
|
107
102
|
if (explicitUserId) {
|
|
108
103
|
await this.initialize(explicitUserId);
|
|
109
104
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
});
|
|
117
|
-
}
|
|
105
|
+
logger.debug("purchasePackage: init complete, starting purchase", {
|
|
106
|
+
productId: pkg.product.identifier,
|
|
107
|
+
hasPackageHandler: !!this.packageHandler,
|
|
108
|
+
hasService: !!this.serviceInstance,
|
|
109
|
+
serviceInitialized: this.serviceInstance?.isInitialized() ?? false,
|
|
110
|
+
});
|
|
118
111
|
this.ensurePackageHandlerInitialized();
|
|
119
112
|
const resolvedUserId = explicitUserId || getCurrentUserIdOrThrow(this.initCache);
|
|
120
113
|
const handler = this.packageHandler!;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IRevenueCatService } from "
|
|
1
|
+
import type { IRevenueCatService } from "../../../shared/application/ports/IRevenueCatService";
|
|
2
2
|
import { initializeRevenueCatService, getRevenueCatService } from "../services/revenueCatServiceInstance";
|
|
3
3
|
import { ensureServiceAvailable } from "./subscriptionManagerUtils";
|
|
4
4
|
import type { RevenueCatConfig } from "../../../revenuecat/core/types/RevenueCatConfig";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PurchasesPackage } from "react-native-purchases";
|
|
2
|
-
import type { IRevenueCatService } from "
|
|
2
|
+
import type { IRevenueCatService } from "../../../shared/application/ports/IRevenueCatService";
|
|
3
3
|
import type { PackageHandler } from "../handlers/PackageHandler";
|
|
4
4
|
import type { RestoreResultInfo } from "./SubscriptionManager.types";
|
|
5
5
|
import { ensureConfigured, getOrCreateService } from "./subscriptionManagerUtils";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IRevenueCatService } from "
|
|
1
|
+
import type { IRevenueCatService } from "../../../shared/application/ports/IRevenueCatService";
|
|
2
2
|
import { PackageHandler } from "../handlers/PackageHandler";
|
|
3
3
|
import { ensureServiceAvailable, ensureConfigured } from "./subscriptionManagerUtils";
|
|
4
4
|
import type { SubscriptionManagerConfig } from "./SubscriptionManager.types";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IRevenueCatService } from "
|
|
1
|
+
import type { IRevenueCatService } from "../../../shared/application/ports/IRevenueCatService";
|
|
2
2
|
import type { PackageHandler } from "../handlers/PackageHandler";
|
|
3
3
|
import type { PremiumStatus } from "./SubscriptionManager.types";
|
|
4
4
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SubscriptionManagerConfig } from "./SubscriptionManager.types";
|
|
2
|
-
import type { IRevenueCatService } from "
|
|
2
|
+
import type { IRevenueCatService } from "../../../shared/application/ports/IRevenueCatService";
|
|
3
3
|
import { getRevenueCatService } from "../services/revenueCatServiceInstance";
|
|
4
4
|
import type { InitializationCache } from "../utils/InitializationCache";
|
|
5
5
|
|
|
@@ -2,6 +2,9 @@ import Purchases, { type CustomerInfo } from "react-native-purchases";
|
|
|
2
2
|
import type { RevenueCatConfig } from "../../../revenuecat/core/types/RevenueCatConfig";
|
|
3
3
|
import { ListenerState } from "./listeners/ListenerState";
|
|
4
4
|
import { processCustomerInfo } from "./listeners/CustomerInfoHandler";
|
|
5
|
+
import { createLogger } from "../../../shared/utils/logger";
|
|
6
|
+
|
|
7
|
+
const logger = createLogger("CustomerInfoListenerManager");
|
|
5
8
|
|
|
6
9
|
export class CustomerInfoListenerManager {
|
|
7
10
|
private state = new ListenerState();
|
|
@@ -33,7 +36,7 @@ export class CustomerInfoListenerManager {
|
|
|
33
36
|
this._createAndAttachListener(config);
|
|
34
37
|
return true;
|
|
35
38
|
} catch (error) {
|
|
36
|
-
|
|
39
|
+
logger.error("Failed to setup listener", error);
|
|
37
40
|
this.state.currentUserId = null;
|
|
38
41
|
return false;
|
|
39
42
|
}
|
|
@@ -41,13 +44,11 @@ export class CustomerInfoListenerManager {
|
|
|
41
44
|
|
|
42
45
|
private _createAndAttachListener(config: RevenueCatConfig): void {
|
|
43
46
|
this.state.listener = async (customerInfo: CustomerInfo) => {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
});
|
|
50
|
-
}
|
|
47
|
+
logger.debug("LISTENER TRIGGERED", {
|
|
48
|
+
userId: this.state.currentUserId,
|
|
49
|
+
activeEntitlements: Object.keys(customerInfo.entitlements.active),
|
|
50
|
+
entitlementsCount: Object.keys(customerInfo.entitlements.all).length,
|
|
51
|
+
});
|
|
51
52
|
|
|
52
53
|
const capturedUserId = this.state.currentUserId;
|
|
53
54
|
if (!capturedUserId) {
|
|
@@ -67,7 +68,7 @@ export class CustomerInfoListenerManager {
|
|
|
67
68
|
}
|
|
68
69
|
// else: User switched during async operation, discard stale renewal state
|
|
69
70
|
} catch (error) {
|
|
70
|
-
|
|
71
|
+
logger.error("processCustomerInfo failed", error);
|
|
71
72
|
}
|
|
72
73
|
};
|
|
73
74
|
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import Purchases, { type PurchasesOffering } from "react-native-purchases";
|
|
2
|
+
import { createLogger } from "../../../shared/utils/logger";
|
|
3
|
+
|
|
4
|
+
const logger = createLogger("OfferingsFetcher");
|
|
2
5
|
|
|
3
6
|
interface OfferingsFetcherDeps {
|
|
4
7
|
isInitialized: () => boolean;
|
|
@@ -14,15 +17,13 @@ export async function fetchOfferings(deps: OfferingsFetcherDeps): Promise<Purcha
|
|
|
14
17
|
try {
|
|
15
18
|
const offerings = await Purchases.getOfferings();
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
});
|
|
25
|
-
}
|
|
20
|
+
logger.debug("Offerings received", {
|
|
21
|
+
attempt,
|
|
22
|
+
hasCurrent: !!offerings.current,
|
|
23
|
+
currentId: offerings.current?.identifier,
|
|
24
|
+
allOfferingsCount: Object.keys(offerings.all).length,
|
|
25
|
+
allOfferingIds: Object.keys(offerings.all),
|
|
26
|
+
});
|
|
26
27
|
|
|
27
28
|
if (offerings.current) {
|
|
28
29
|
return offerings.current;
|
|
@@ -35,28 +36,20 @@ export async function fetchOfferings(deps: OfferingsFetcherDeps): Promise<Purcha
|
|
|
35
36
|
|
|
36
37
|
// No offerings found - retry after delay (RevenueCat may still be syncing)
|
|
37
38
|
if (attempt < MAX_FETCH_RETRIES) {
|
|
38
|
-
|
|
39
|
-
console.log('[OfferingsFetcher] No offerings found, retrying...', { attempt: attempt + 1 });
|
|
40
|
-
}
|
|
39
|
+
logger.debug("No offerings found, retrying...", { attempt: attempt + 1 });
|
|
41
40
|
await new Promise<void>(resolve => setTimeout(resolve, FETCH_RETRY_DELAY_MS));
|
|
42
41
|
continue;
|
|
43
42
|
}
|
|
44
43
|
|
|
45
|
-
|
|
46
|
-
console.warn('[OfferingsFetcher] No offerings found after all retries');
|
|
47
|
-
}
|
|
44
|
+
logger.warn("No offerings found after all retries");
|
|
48
45
|
return null;
|
|
49
46
|
} catch (error) {
|
|
50
47
|
if (attempt < MAX_FETCH_RETRIES) {
|
|
51
|
-
|
|
52
|
-
console.warn('[OfferingsFetcher] Fetch failed, retrying...', { attempt: attempt + 1, error });
|
|
53
|
-
}
|
|
48
|
+
logger.warn("Fetch failed, retrying...", error, { attempt: attempt + 1 });
|
|
54
49
|
await new Promise<void>(resolve => setTimeout(resolve, FETCH_RETRY_DELAY_MS));
|
|
55
50
|
continue;
|
|
56
51
|
}
|
|
57
|
-
|
|
58
|
-
console.warn('[OfferingsFetcher] Failed to fetch offerings after all retries:', error);
|
|
59
|
-
}
|
|
52
|
+
logger.warn("Failed to fetch offerings after all retries", error);
|
|
60
53
|
return null;
|
|
61
54
|
}
|
|
62
55
|
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { PurchasesPackage } from "react-native-purchases";
|
|
2
|
-
import type { PurchaseResult } from "
|
|
2
|
+
import type { PurchaseResult } from "../../../shared/application/ports/IRevenueCatService";
|
|
3
3
|
import type { RevenueCatConfig } from "../../../revenuecat/core/types/RevenueCatConfig";
|
|
4
4
|
import { isUserCancelledError, isAlreadyPurchasedError } from "../../../revenuecat/core/types/RevenueCatTypes";
|
|
5
5
|
import { validatePurchaseReady, isConsumableProduct } from "./purchase/PurchaseValidator";
|
|
6
6
|
import { executePurchase } from "./purchase/PurchaseExecutor";
|
|
7
7
|
import { handleAlreadyPurchasedError, handlePurchaseError } from "./purchase/PurchaseErrorHandler";
|
|
8
|
+
import { createLogger } from "../../../shared/utils/logger";
|
|
9
|
+
|
|
10
|
+
const logger = createLogger("PurchaseHandler");
|
|
8
11
|
|
|
9
12
|
export interface PurchaseHandlerDeps {
|
|
10
13
|
config: RevenueCatConfig;
|
|
@@ -21,13 +24,11 @@ export async function handlePurchase(
|
|
|
21
24
|
const consumableIds = deps.config.consumableProductIdentifiers || [];
|
|
22
25
|
const isConsumable = isConsumableProduct(pkg, consumableIds);
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
});
|
|
30
|
-
}
|
|
27
|
+
logger.debug("handlePurchase", {
|
|
28
|
+
productId: pkg.product.identifier,
|
|
29
|
+
userId,
|
|
30
|
+
isConsumable,
|
|
31
|
+
});
|
|
31
32
|
|
|
32
33
|
try {
|
|
33
34
|
const result = await executePurchase(deps.config, userId, pkg, isConsumable);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Purchases from "react-native-purchases";
|
|
2
|
-
import type { RestoreResult } from "
|
|
2
|
+
import type { RestoreResult } from "../../../shared/application/ports/IRevenueCatService";
|
|
3
3
|
import {
|
|
4
4
|
RevenueCatRestoreError,
|
|
5
5
|
RevenueCatInitializationError,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Purchases from "react-native-purchases";
|
|
2
2
|
import type { PurchasesOffering, PurchasesPackage, CustomerInfo } from "react-native-purchases";
|
|
3
|
-
import type { IRevenueCatService, InitializeResult, PurchaseResult, RestoreResult } from "
|
|
3
|
+
import type { IRevenueCatService, InitializeResult, PurchaseResult, RestoreResult } from "../../../shared/application/ports/IRevenueCatService";
|
|
4
4
|
import type { RevenueCatConfig } from "../../../revenuecat/core/types/RevenueCatConfig";
|
|
5
5
|
import { initializeSDK } from "../../../revenuecat/infrastructure/services/RevenueCatInitializer";
|
|
6
6
|
import { fetchOfferings } from "./OfferingsFetcher";
|
|
@@ -8,6 +8,9 @@ import { handlePurchase } from "./PurchaseHandler";
|
|
|
8
8
|
import { handleRestore } from "./RestoreHandler";
|
|
9
9
|
import { CustomerInfoListenerManager } from "./CustomerInfoListenerManager";
|
|
10
10
|
import { ServiceStateManager } from "./ServiceStateManager";
|
|
11
|
+
import { createLogger } from "../../../shared/utils/logger";
|
|
12
|
+
|
|
13
|
+
const logger = createLogger("RevenueCatService");
|
|
11
14
|
|
|
12
15
|
export class RevenueCatService implements IRevenueCatService {
|
|
13
16
|
private stateManager: ServiceStateManager;
|
|
@@ -83,9 +86,7 @@ export class RevenueCatService implements IRevenueCatService {
|
|
|
83
86
|
try {
|
|
84
87
|
await Purchases.logOut();
|
|
85
88
|
} catch (error) {
|
|
86
|
-
|
|
87
|
-
console.warn('[RevenueCatService] logOut failed during reset', { error });
|
|
88
|
-
}
|
|
89
|
+
logger.warn("logOut failed during reset", error);
|
|
89
90
|
}
|
|
90
91
|
}
|
|
91
92
|
|