@umituz/react-native-subscription 2.27.65 → 2.27.67
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 +3 -1
- package/src/domains/credits/application/CreditLimitCalculator.ts +17 -0
- package/src/domains/credits/application/CreditsInitializer.ts +85 -0
- package/src/domains/credits/application/DeductCreditsCommand.ts +52 -0
- package/src/domains/credits/application/PurchaseMetadataGenerator.ts +59 -0
- package/src/domains/credits/application/credit-strategies/CreditAllocationContext.ts +35 -0
- package/src/domains/credits/application/credit-strategies/ICreditStrategy.ts +18 -0
- package/src/domains/credits/application/credit-strategies/StandardPurchaseCreditStrategy.ts +16 -0
- package/src/domains/credits/application/credit-strategies/SyncCreditStrategy.ts +15 -0
- package/src/domains/credits/application/credit-strategies/TrialCreditStrategy.ts +18 -0
- package/src/{infrastructure/mappers → domains/credits/core}/CreditsMapper.ts +4 -4
- package/src/domains/credits/infrastructure/CreditsRepository.ts +102 -0
- package/src/{presentation/hooks → domains/credits/presentation}/useCredits.ts +21 -4
- package/src/domains/subscription/application/SubscriptionAuthListener.ts +26 -0
- package/src/domains/subscription/application/SubscriptionInitializer.ts +77 -0
- package/src/{infrastructure/services → domains/subscription/application}/SubscriptionInitializerTypes.ts +21 -1
- package/src/domains/subscription/application/SubscriptionSyncService.ts +71 -0
- package/src/domains/subscription/application/SubscriptionSyncUtils.ts +16 -0
- package/src/{revenuecat/domain/value-objects → domains/subscription/core}/RevenueCatConfig.ts +1 -1
- package/src/{domain/types → domains/subscription/core}/RevenueCatData.ts +1 -1
- package/src/{domain/entities → domains/subscription/core}/SubscriptionStatus.ts +13 -21
- package/src/domains/subscription/core/SubscriptionStatusHandlers.ts +51 -0
- package/src/domains/subscription/infrastructure/handlers/PackageHandler.ts +67 -0
- package/src/domains/subscription/infrastructure/handlers/PurchaseStatusResolver.ts +27 -0
- package/src/domains/subscription/infrastructure/managers/SubscriptionInternalState.ts +12 -0
- package/src/domains/subscription/infrastructure/managers/SubscriptionManager.ts +110 -0
- package/src/{presentation/hooks → domains/subscription/presentation}/usePremium.ts +7 -4
- package/src/domains/trial/application/TrialEligibilityService.ts +25 -0
- package/src/domains/trial/application/TrialService.ts +68 -0
- package/src/{infrastructure/services → domains/trial/core}/TrialTypes.ts +1 -1
- package/src/domains/trial/infrastructure/DeviceTrialRepository.ts +30 -0
- package/src/presentation/components/details/PremiumStatusBadge.tsx +2 -2
- package/src/presentation/hooks/index.ts +11 -11
- package/src/shared/infrastructure/SubscriptionEventBus.ts +51 -0
- package/src/utils/packageTypeDetector.ts +13 -18
- package/src/application/README.md +0 -50
- package/src/domain/entities/README.md +0 -50
- package/src/domain/entities/SubscriptionStatus.test.ts +0 -105
- package/src/domain/errors/README.md +0 -53
- package/src/domain/value-objects/README.md +0 -50
- package/src/infrastructure/README.md +0 -55
- package/src/infrastructure/mappers/README.md +0 -21
- package/src/infrastructure/models/README.md +0 -26
- package/src/infrastructure/repositories/CreditsRepository.ts +0 -132
- package/src/infrastructure/repositories/README.md +0 -99
- package/src/infrastructure/services/CreditsInitializer.ts +0 -170
- package/src/infrastructure/services/README.md +0 -99
- package/src/infrastructure/services/SubscriptionInitializer.ts +0 -176
- package/src/infrastructure/services/SubscriptionService.ts +0 -133
- package/src/infrastructure/services/TrialService.ts +0 -197
- package/src/infrastructure/services/app-service-helpers.ts +0 -111
- package/src/revenuecat/README.md +0 -104
- package/src/revenuecat/application/README.md +0 -43
- package/src/revenuecat/application/ports/IRevenueCatService.ts +0 -76
- package/src/revenuecat/application/ports/README.md +0 -41
- package/src/revenuecat/domain/README.md +0 -48
- package/src/revenuecat/domain/constants/README.md +0 -41
- package/src/revenuecat/domain/entities/README.md +0 -42
- package/src/revenuecat/domain/errors/README.md +0 -53
- package/src/revenuecat/domain/types/README.md +0 -41
- package/src/revenuecat/domain/value-objects/README.md +0 -41
- package/src/revenuecat/index.ts +0 -13
- package/src/revenuecat/infrastructure/handlers/PackageHandler.ts +0 -161
- package/src/revenuecat/infrastructure/managers/SubscriptionManager.ts +0 -165
- package/src/revenuecat/presentation/README.md +0 -42
- /package/src/{domain/entities → domains/credits/core}/Credits.ts +0 -0
- /package/src/{infrastructure/models → domains/credits/core}/UserCreditsDocument.ts +0 -0
- /package/src/{infrastructure/repositories → domains/credits/infrastructure}/CreditsRepositoryProvider.ts +0 -0
- /package/src/{presentation/hooks → domains/credits/presentation}/useDeductCredit.ts +0 -0
- /package/src/{revenuecat/domain/constants → domains/subscription/core}/RevenueCatConstants.ts +0 -0
- /package/src/{revenuecat/domain/errors → domains/subscription/core}/RevenueCatError.ts +0 -0
- /package/src/{revenuecat/domain/types → domains/subscription/core}/RevenueCatTypes.ts +0 -0
- /package/src/{domain/entities → domains/subscription/core}/SubscriptionConstants.ts +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/README.md +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/config/README.md +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/handlers/README.md +0 -0
- /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/README.md +0 -0
- /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/subscriptionQueryKeys.ts +0 -0
- /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useCustomerInfo.ts +0 -0
- /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useInitializeSubscription.ts +0 -0
- /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/usePaywallFlow.ts +0 -0
- /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/usePurchasePackage.ts +0 -0
- /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useRestorePurchase.ts +0 -0
- /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useRevenueCat.ts +0 -0
- /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useRevenueCatTrialEligibility.ts +0 -0
- /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useSubscriptionPackages.ts +0 -0
- /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useSubscriptionQueries.ts +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/managers/README.md +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/services/CustomerInfoListenerManager.ts +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/services/OfferingsFetcher.ts +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/services/PurchaseHandler.ts +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/services/README.md +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/services/RestoreHandler.ts +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/services/RevenueCatInitializer.ts +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/services/RevenueCatService.ts +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/services/ServiceStateManager.ts +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/utils/ApiKeyResolver.ts +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/utils/InitializationCache.ts +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/utils/PremiumStatusSyncer.ts +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/utils/README.md +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/utils/RenewalDetector.ts +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/utils/UserIdProvider.ts +0 -0
- /package/src/{presentation/hooks → domains/subscription/presentation}/useAuthAwarePurchase.ts +0 -0
- /package/src/{presentation/hooks → domains/subscription/presentation}/useAuthSubscriptionSync.ts +0 -0
- /package/src/{presentation/hooks → domains/subscription/presentation}/useFeatureGate.ts +0 -0
- /package/src/{presentation/hooks → domains/subscription/presentation}/usePaywallVisibility.ts +0 -0
- /package/src/{presentation/hooks → domains/subscription/presentation}/usePremiumGate.ts +0 -0
- /package/src/{presentation/hooks → domains/subscription/presentation}/useSavedPurchaseAutoExecution.ts +0 -0
- /package/src/{presentation/hooks → domains/subscription/presentation}/useSubscriptionSettingsConfig.ts +0 -0
- /package/src/{presentation/hooks → domains/subscription/presentation}/useSubscriptionSettingsConfig.utils.ts +0 -0
- /package/src/{presentation/hooks → domains/subscription/presentation}/useSubscriptionStatus.ts +0 -0
- /package/src/{infrastructure/services → shared/application}/ActivationHandler.ts +0 -0
- /package/src/{infrastructure/services → shared/application}/FeedbackService.ts +0 -0
- /package/src/{application → shared/application}/ports/ISubscriptionRepository.ts +0 -0
- /package/src/{application → shared/application}/ports/ISubscriptionService.ts +0 -0
- /package/src/{application → shared/application}/ports/README.md +0 -0
- /package/src/{domain/errors → shared/utils}/InsufficientCreditsError.ts +0 -0
- /package/src/{infrastructure → shared}/utils/Logger.ts +0 -0
- /package/src/{domain/value-objects → shared/utils}/Result.ts +0 -0
- /package/src/{domain/value-objects → shared/utils}/SubscriptionConfig.ts +0 -0
- /package/src/{domain/errors → shared/utils}/SubscriptionError.ts +0 -0
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# RevenueCat Domain Entities
|
|
2
|
-
|
|
3
|
-
## Location
|
|
4
|
-
Core domain entities for RevenueCat integration.
|
|
5
|
-
|
|
6
|
-
## Strategy
|
|
7
|
-
This directory contains TypeScript type definitions and entities representing RevenueCat concepts like customer info, entitlements, offerings, and packages with proper type safety.
|
|
8
|
-
|
|
9
|
-
## Restrictions
|
|
10
|
-
|
|
11
|
-
### REQUIRED
|
|
12
|
-
- Must use TypeScript types for all entities
|
|
13
|
-
- Must maintain type safety
|
|
14
|
-
- Must validate entity data
|
|
15
|
-
- Must provide clear type definitions
|
|
16
|
-
|
|
17
|
-
### PROHIBITED
|
|
18
|
-
- DO NOT use `any` type for entities
|
|
19
|
-
- DO NOT bypass type checking
|
|
20
|
-
- DO NOT create invalid entity states
|
|
21
|
-
- DO NOT use loose type definitions
|
|
22
|
-
|
|
23
|
-
### CRITICAL SAFETY
|
|
24
|
-
- All entities MUST be type-safe
|
|
25
|
-
- Entity data MUST be validated
|
|
26
|
-
- Type definitions MUST be clear and specific
|
|
27
|
-
- Entity states MUST be valid
|
|
28
|
-
|
|
29
|
-
## AI Agent Guidelines
|
|
30
|
-
1. Define clear TypeScript types for all RevenueCat concepts
|
|
31
|
-
2. Maintain strict type safety for all entities
|
|
32
|
-
3. Validate entity data before use
|
|
33
|
-
4. Provide comprehensive type definitions
|
|
34
|
-
5. Document entity properties and relationships
|
|
35
|
-
6. Use type guards for runtime validation
|
|
36
|
-
7. Test entity creation and validation thoroughly
|
|
37
|
-
|
|
38
|
-
## Related Documentation
|
|
39
|
-
- [RevenueCat Domain](../README.md)
|
|
40
|
-
- [RevenueCat Value Objects](../value-objects/README.md)
|
|
41
|
-
- [RevenueCat Errors](../errors/README.md)
|
|
42
|
-
- [RevenueCat Types](../types/README.md)
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
# RevenueCat Domain Errors
|
|
2
|
-
|
|
3
|
-
Domain-specific errors for RevenueCat operations.
|
|
4
|
-
|
|
5
|
-
## Location
|
|
6
|
-
|
|
7
|
-
`src/revenuecat/domain/errors/`
|
|
8
|
-
|
|
9
|
-
## Strategy
|
|
10
|
-
|
|
11
|
-
Custom error classes for RevenueCat-specific error conditions, providing typed error handling for purchase, entitlement, configuration, and offering failures.
|
|
12
|
-
|
|
13
|
-
## Restrictions
|
|
14
|
-
|
|
15
|
-
### REQUIRED
|
|
16
|
-
|
|
17
|
-
- MUST use specific error types (not generic errors)
|
|
18
|
-
- MUST include machine-readable error codes
|
|
19
|
-
- MUST include relevant contextual data
|
|
20
|
-
- MUST implement recovery strategies
|
|
21
|
-
- MUST convert errors to user-friendly messages
|
|
22
|
-
- MUST log errors for debugging
|
|
23
|
-
- MUST track errors for analytics
|
|
24
|
-
|
|
25
|
-
### PROHIBITED
|
|
26
|
-
|
|
27
|
-
- MUST NOT expose sensitive RevenueCat API keys or tokens
|
|
28
|
-
- MUST NOT leak raw RevenueCat errors to users
|
|
29
|
-
- MUST NOT ignore error conditions
|
|
30
|
-
|
|
31
|
-
### CRITICAL
|
|
32
|
-
|
|
33
|
-
- Always implement specific error types
|
|
34
|
-
- Include machine-readable codes for programmatic handling
|
|
35
|
-
- Provide recovery strategies where applicable
|
|
36
|
-
- Convert technical errors to user-friendly messages
|
|
37
|
-
- Log all errors for debugging and monitoring
|
|
38
|
-
|
|
39
|
-
## AI Agent Guidelines
|
|
40
|
-
|
|
41
|
-
When working with RevenueCat errors:
|
|
42
|
-
1. Specific Errors - use specific error types
|
|
43
|
-
2. Error Codes - include machine-readable codes
|
|
44
|
-
3. Context - include relevant data in error
|
|
45
|
-
4. Recovery - implement recovery strategies
|
|
46
|
-
5. User Feedback - convert errors to user-friendly messages
|
|
47
|
-
6. Logging - log errors for debugging
|
|
48
|
-
7. Analytics - track errors for monitoring
|
|
49
|
-
|
|
50
|
-
## Related Documentation
|
|
51
|
-
|
|
52
|
-
- [RevenueCat Domain](../README.md)
|
|
53
|
-
- [RevenueCat Services](../../infrastructure/services/README.md)
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# RevenueCat Domain Types
|
|
2
|
-
|
|
3
|
-
## Location
|
|
4
|
-
Type definitions and type utilities for RevenueCat integration.
|
|
5
|
-
|
|
6
|
-
## Strategy
|
|
7
|
-
This directory contains TypeScript type definitions, type guards, and utility types used throughout the RevenueCat integration for type safety and compile-time checks.
|
|
8
|
-
|
|
9
|
-
## Restrictions
|
|
10
|
-
|
|
11
|
-
### REQUIRED
|
|
12
|
-
- Must use type guards for runtime checks
|
|
13
|
-
- Must provide utility types for common operations
|
|
14
|
-
- Must maintain type safety throughout
|
|
15
|
-
- Must use strict TypeScript types
|
|
16
|
-
|
|
17
|
-
### PROHIBITED
|
|
18
|
-
- DO NOT use `any` type
|
|
19
|
-
- DO NOT bypass type guards
|
|
20
|
-
- DO NOT ignore type safety
|
|
21
|
-
- DO NOT use loose type definitions
|
|
22
|
-
|
|
23
|
-
### CRITICAL SAFETY
|
|
24
|
-
- All type checks MUST use type guards
|
|
25
|
-
- All types MUST be strictly defined
|
|
26
|
-
- Type assertions MUST be safe
|
|
27
|
-
- Utility types MUST be used appropriately
|
|
28
|
-
|
|
29
|
-
## AI Agent Guidelines
|
|
30
|
-
1. Use type guards for runtime type checking
|
|
31
|
-
2. Define utility types for common operations
|
|
32
|
-
3. Maintain strict type safety throughout codebase
|
|
33
|
-
4. Provide clear type definitions for all RevenueCat concepts
|
|
34
|
-
5. Use type assertions only when safe
|
|
35
|
-
6. Test type guards thoroughly
|
|
36
|
-
7. Document complex types with comments
|
|
37
|
-
|
|
38
|
-
## Related Documentation
|
|
39
|
-
- [RevenueCat Domain](../README.md)
|
|
40
|
-
- [RevenueCat Entities](../entities/README.md)
|
|
41
|
-
- [RevenueCat Value Objects](../value-objects/README.md)
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# RevenueCat Domain Value Objects
|
|
2
|
-
|
|
3
|
-
## Location
|
|
4
|
-
Value objects for RevenueCat integration.
|
|
5
|
-
|
|
6
|
-
## Strategy
|
|
7
|
-
This directory contains value objects - immutable types that represent concepts in the RevenueCat domain with no identity, defined by their attributes rather than identity.
|
|
8
|
-
|
|
9
|
-
## Restrictions
|
|
10
|
-
|
|
11
|
-
### REQUIRED
|
|
12
|
-
- Must validate in constructor
|
|
13
|
-
- Must keep value objects immutable
|
|
14
|
-
- Must implement proper equality based on values
|
|
15
|
-
- Must provide locale-aware formatting
|
|
16
|
-
|
|
17
|
-
### PROHIBITED
|
|
18
|
-
- DO NOT modify value objects after creation
|
|
19
|
-
- DO NOT bypass validation in constructor
|
|
20
|
-
- DO NOT use identity-based equality
|
|
21
|
-
- DO NOT ignore locale in formatting
|
|
22
|
-
|
|
23
|
-
### CRITICAL SAFETY
|
|
24
|
-
- All value objects MUST be immutable
|
|
25
|
-
- Validation MUST happen in constructor
|
|
26
|
-
- Equality MUST be based on values, not identity
|
|
27
|
-
- Formatting MUST be locale-aware
|
|
28
|
-
|
|
29
|
-
## AI Agent Guidelines
|
|
30
|
-
1. Never modify value objects after creation
|
|
31
|
-
2. Validate all inputs in constructor with fail-fast
|
|
32
|
-
3. Implement proper equality based on values
|
|
33
|
-
4. Provide locale-aware formatting methods
|
|
34
|
-
5. Return new instances for all operations
|
|
35
|
-
6. Use TypeScript for compile-time type safety
|
|
36
|
-
7. Provide toString/JSON methods if needed for serialization
|
|
37
|
-
|
|
38
|
-
## Related Documentation
|
|
39
|
-
- [RevenueCat Domain](../README.md)
|
|
40
|
-
- [RevenueCat Entities](../entities/README.md)
|
|
41
|
-
- [RevenueCat Types](../types/README.md)
|
package/src/revenuecat/index.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export * from "./domain/errors/RevenueCatError";
|
|
2
|
-
export * from "./domain/value-objects/RevenueCatConfig";
|
|
3
|
-
export * from "./domain/types/RevenueCatTypes";
|
|
4
|
-
export * from "./domain/constants/RevenueCatConstants";
|
|
5
|
-
export * from "./application/ports/IRevenueCatService";
|
|
6
|
-
export * from "./infrastructure/services/RevenueCatService";
|
|
7
|
-
export * from "./infrastructure/managers/SubscriptionManager";
|
|
8
|
-
export * from "./infrastructure/handlers/PackageHandler";
|
|
9
|
-
export * from "./presentation/hooks/useRevenueCat";
|
|
10
|
-
export * from "./presentation/hooks/useCustomerInfo";
|
|
11
|
-
export * from "./presentation/hooks/usePaywallFlow";
|
|
12
|
-
export * from "./presentation/hooks/useSubscriptionQueries";
|
|
13
|
-
export * from "./presentation/hooks/useRevenueCatTrialEligibility";
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Package Handler
|
|
3
|
-
* Handles package operations (fetch, purchase, restore, premium status)
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { PurchasesPackage, CustomerInfo } from "react-native-purchases";
|
|
7
|
-
import type { IRevenueCatService } from "../../application/ports/IRevenueCatService";
|
|
8
|
-
import { getPremiumEntitlement } from "../../domain/types/RevenueCatTypes";
|
|
9
|
-
|
|
10
|
-
export interface PremiumStatus {
|
|
11
|
-
isPremium: boolean;
|
|
12
|
-
expirationDate: Date | null;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface RestoreResultInfo {
|
|
16
|
-
success: boolean;
|
|
17
|
-
productId: string | null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export class PackageHandler {
|
|
21
|
-
constructor(
|
|
22
|
-
private service: IRevenueCatService | null,
|
|
23
|
-
private entitlementId: string
|
|
24
|
-
) { }
|
|
25
|
-
|
|
26
|
-
setService(service: IRevenueCatService | null): void {
|
|
27
|
-
this.service = service;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async fetchPackages(): Promise<PurchasesPackage[]> {
|
|
31
|
-
if (__DEV__) {
|
|
32
|
-
console.log('[DEBUG PackageHandler] fetchPackages called', {
|
|
33
|
-
hasService: !!this.service,
|
|
34
|
-
isInitialized: this.service?.isInitialized(),
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
if (!this.service?.isInitialized()) {
|
|
38
|
-
if (__DEV__) {
|
|
39
|
-
console.log('[DEBUG PackageHandler] Service not initialized, returning empty');
|
|
40
|
-
}
|
|
41
|
-
return [];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
try {
|
|
45
|
-
const offering = await this.service.fetchOfferings();
|
|
46
|
-
|
|
47
|
-
if (__DEV__) {
|
|
48
|
-
console.log('[DEBUG PackageHandler] fetchOfferings result:', {
|
|
49
|
-
hasOffering: !!offering,
|
|
50
|
-
identifier: offering?.identifier,
|
|
51
|
-
packagesCount: offering?.availablePackages?.length ?? 0,
|
|
52
|
-
packages: offering?.availablePackages?.map(p => ({
|
|
53
|
-
identifier: p.identifier,
|
|
54
|
-
productIdentifier: p.product.identifier,
|
|
55
|
-
offeringIdentifier: p.offeringIdentifier
|
|
56
|
-
}))
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return offering?.availablePackages ?? [];
|
|
61
|
-
} catch (error) {
|
|
62
|
-
if (__DEV__) {
|
|
63
|
-
console.error('[DEBUG PackageHandler] fetchOfferings failed:', error);
|
|
64
|
-
}
|
|
65
|
-
return [];
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
async purchase(pkg: PurchasesPackage, userId: string): Promise<boolean> {
|
|
70
|
-
if (__DEV__) {
|
|
71
|
-
console.log('[DEBUG PackageHandler] purchase() called', {
|
|
72
|
-
productId: pkg.product.identifier,
|
|
73
|
-
userId,
|
|
74
|
-
serviceExists: !!this.service,
|
|
75
|
-
isInitialized: this.service?.isInitialized(),
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (!this.service?.isInitialized()) {
|
|
80
|
-
if (__DEV__) {
|
|
81
|
-
console.log('[DEBUG PackageHandler] Service not initialized', {
|
|
82
|
-
productId: pkg.product.identifier,
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
return false;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
if (__DEV__) {
|
|
90
|
-
console.log('[DEBUG PackageHandler] Calling service.purchasePackage...');
|
|
91
|
-
}
|
|
92
|
-
const result = await this.service.purchasePackage(pkg, userId);
|
|
93
|
-
if (__DEV__) {
|
|
94
|
-
console.log('[DEBUG PackageHandler] Purchase result:', {
|
|
95
|
-
success: result.success,
|
|
96
|
-
productId: pkg.product.identifier,
|
|
97
|
-
isPremium: result.isPremium,
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
return result.success;
|
|
101
|
-
} catch (error) {
|
|
102
|
-
if (__DEV__) {
|
|
103
|
-
console.error('[DEBUG PackageHandler] Purchase failed:', {
|
|
104
|
-
error,
|
|
105
|
-
productId: pkg.product.identifier,
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
async restore(userId: string): Promise<RestoreResultInfo> {
|
|
113
|
-
if (!this.service?.isInitialized()) {
|
|
114
|
-
return { success: false, productId: null };
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
try {
|
|
118
|
-
const result = await this.service.restorePurchases(userId);
|
|
119
|
-
|
|
120
|
-
// Extract product ID from active entitlement
|
|
121
|
-
let productId: string | null = null;
|
|
122
|
-
if (result.success && result.customerInfo) {
|
|
123
|
-
const entitlement = getPremiumEntitlement(
|
|
124
|
-
result.customerInfo,
|
|
125
|
-
this.entitlementId
|
|
126
|
-
);
|
|
127
|
-
if (entitlement) {
|
|
128
|
-
productId = entitlement.productIdentifier;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return { success: result.success, productId };
|
|
133
|
-
} catch (error) {
|
|
134
|
-
if (__DEV__) {
|
|
135
|
-
console.error('[DEBUG PackageHandler] Restore failed:', {
|
|
136
|
-
error,
|
|
137
|
-
userId,
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
return { success: false, productId: null };
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
checkPremiumStatusFromInfo(customerInfo: CustomerInfo): PremiumStatus {
|
|
145
|
-
const entitlement = getPremiumEntitlement(customerInfo, this.entitlementId);
|
|
146
|
-
|
|
147
|
-
if (entitlement) {
|
|
148
|
-
return {
|
|
149
|
-
isPremium: true,
|
|
150
|
-
expirationDate: entitlement.expirationDate
|
|
151
|
-
? new Date(entitlement.expirationDate)
|
|
152
|
-
: null,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return {
|
|
157
|
-
isPremium: false,
|
|
158
|
-
expirationDate: null,
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
}
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Subscription Manager
|
|
3
|
-
* Facade for subscription operations
|
|
4
|
-
* Coordinates UserIdProvider, InitializationCache, and PackageHandler
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { PurchasesPackage } from "react-native-purchases";
|
|
8
|
-
import type { RevenueCatConfig } from "../../domain/value-objects/RevenueCatConfig";
|
|
9
|
-
import type { IRevenueCatService } from "../../application/ports/IRevenueCatService";
|
|
10
|
-
import { initializeRevenueCatService, getRevenueCatService } from "../services/RevenueCatService";
|
|
11
|
-
import { UserIdProvider } from "../utils/UserIdProvider";
|
|
12
|
-
import { InitializationCache } from "../utils/InitializationCache";
|
|
13
|
-
import { PackageHandler } from "../handlers/PackageHandler";
|
|
14
|
-
import type { PremiumStatus, RestoreResultInfo } from "../handlers/PackageHandler";
|
|
15
|
-
|
|
16
|
-
export interface SubscriptionManagerConfig {
|
|
17
|
-
config: RevenueCatConfig;
|
|
18
|
-
apiKey: string;
|
|
19
|
-
getAnonymousUserId?: () => Promise<string>;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
class SubscriptionManagerImpl {
|
|
23
|
-
private managerConfig: SubscriptionManagerConfig | null = null;
|
|
24
|
-
private serviceInstance: IRevenueCatService | null = null;
|
|
25
|
-
private userIdProvider = new UserIdProvider();
|
|
26
|
-
private initCache = new InitializationCache();
|
|
27
|
-
private packageHandler: PackageHandler | null = null;
|
|
28
|
-
|
|
29
|
-
configure(config: SubscriptionManagerConfig): void {
|
|
30
|
-
this.managerConfig = config;
|
|
31
|
-
this.packageHandler = new PackageHandler(null, config.config.entitlementIdentifier);
|
|
32
|
-
|
|
33
|
-
if (config.getAnonymousUserId) {
|
|
34
|
-
this.userIdProvider.configure(config.getAnonymousUserId);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
private ensureConfigured(): void {
|
|
39
|
-
if (!this.managerConfig || !this.packageHandler) {
|
|
40
|
-
throw new Error("SubscriptionManager not configured");
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
private async performInitialization(userId: string): Promise<boolean> {
|
|
45
|
-
this.ensureConfigured();
|
|
46
|
-
|
|
47
|
-
await initializeRevenueCatService(this.managerConfig!.config);
|
|
48
|
-
this.serviceInstance = getRevenueCatService();
|
|
49
|
-
|
|
50
|
-
if (!this.serviceInstance) {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
this.packageHandler!.setService(this.serviceInstance);
|
|
55
|
-
|
|
56
|
-
const result = await this.serviceInstance.initialize(userId);
|
|
57
|
-
return result.success;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async initialize(userId?: string): Promise<boolean> {
|
|
61
|
-
this.ensureConfigured();
|
|
62
|
-
|
|
63
|
-
const effectiveUserId = userId || (await this.userIdProvider.getOrCreateAnonymousUserId());
|
|
64
|
-
|
|
65
|
-
// Atomic check-and-acquire to prevent race conditions
|
|
66
|
-
const { shouldInit, existingPromise } = this.initCache.tryAcquireInitialization(effectiveUserId);
|
|
67
|
-
|
|
68
|
-
if (!shouldInit && existingPromise) {
|
|
69
|
-
return existingPromise;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const promise = this.performInitialization(effectiveUserId);
|
|
73
|
-
this.initCache.setPromise(promise, effectiveUserId);
|
|
74
|
-
|
|
75
|
-
return promise;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
isConfigured(): boolean {
|
|
79
|
-
return this.managerConfig !== null && this.packageHandler !== null;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
isInitialized(): boolean {
|
|
83
|
-
return this.serviceInstance?.isInitialized() ?? false;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
isInitializedForUser(userId: string): boolean {
|
|
87
|
-
return this.serviceInstance?.isInitialized() === true &&
|
|
88
|
-
this.initCache.getCurrentUserId() === userId;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
getEntitlementId(): string | null {
|
|
92
|
-
return this.managerConfig?.config.entitlementIdentifier || null;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
async getPackages(): Promise<PurchasesPackage[]> {
|
|
96
|
-
if (__DEV__) {
|
|
97
|
-
console.log('[DEBUG SubscriptionManager] getPackages called', {
|
|
98
|
-
isConfigured: this.isConfigured(),
|
|
99
|
-
isInitialized: this.isInitialized(),
|
|
100
|
-
hasServiceInstance: !!this.serviceInstance,
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
this.ensureConfigured();
|
|
104
|
-
if (!this.serviceInstance) {
|
|
105
|
-
this.serviceInstance = getRevenueCatService();
|
|
106
|
-
this.packageHandler!.setService(this.serviceInstance);
|
|
107
|
-
}
|
|
108
|
-
return this.packageHandler!.fetchPackages();
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
async purchasePackage(pkg: PurchasesPackage): Promise<boolean> {
|
|
112
|
-
if (__DEV__) {
|
|
113
|
-
console.log('[DEBUG SubscriptionManager] purchasePackage called', {
|
|
114
|
-
productId: pkg.product.identifier,
|
|
115
|
-
isConfigured: this.isConfigured(),
|
|
116
|
-
isInitialized: this.isInitialized(),
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
this.ensureConfigured();
|
|
120
|
-
const userId = this.initCache.getCurrentUserId();
|
|
121
|
-
if (__DEV__) {
|
|
122
|
-
console.log('[DEBUG SubscriptionManager] userId from cache:', userId);
|
|
123
|
-
}
|
|
124
|
-
if (!userId) {
|
|
125
|
-
if (__DEV__) {
|
|
126
|
-
console.log('[DEBUG SubscriptionManager] No userId, returning false');
|
|
127
|
-
}
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
return this.packageHandler!.purchase(pkg, userId);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
async restore(): Promise<RestoreResultInfo> {
|
|
134
|
-
this.ensureConfigured();
|
|
135
|
-
const userId = this.initCache.getCurrentUserId();
|
|
136
|
-
if (!userId) return { success: false, productId: null };
|
|
137
|
-
return this.packageHandler!.restore(userId);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
async checkPremiumStatus(): Promise<PremiumStatus> {
|
|
141
|
-
this.ensureConfigured();
|
|
142
|
-
const userId = this.initCache.getCurrentUserId();
|
|
143
|
-
if (!userId) return { isPremium: false, expirationDate: null };
|
|
144
|
-
|
|
145
|
-
const customerInfo = await this.serviceInstance?.getCustomerInfo();
|
|
146
|
-
if (customerInfo) {
|
|
147
|
-
return this.packageHandler!.checkPremiumStatusFromInfo(customerInfo);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return { isPremium: false, expirationDate: null };
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
async reset(): Promise<void> {
|
|
154
|
-
if (this.serviceInstance) {
|
|
155
|
-
await this.serviceInstance.reset();
|
|
156
|
-
}
|
|
157
|
-
this.initCache.reset();
|
|
158
|
-
this.userIdProvider.reset();
|
|
159
|
-
this.serviceInstance = null;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export const SubscriptionManager = new SubscriptionManagerImpl();
|
|
164
|
-
|
|
165
|
-
export type { PremiumStatus };
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# RevenueCat Presentation
|
|
2
|
-
|
|
3
|
-
## Location
|
|
4
|
-
Presentation layer for RevenueCat integration.
|
|
5
|
-
|
|
6
|
-
## Strategy
|
|
7
|
-
This directory contains React hooks, components, and utilities for integrating RevenueCat functionality into the UI layer with proper loading states, error handling, and caching.
|
|
8
|
-
|
|
9
|
-
## Restrictions
|
|
10
|
-
|
|
11
|
-
### REQUIRED
|
|
12
|
-
- Must show loading states during async operations
|
|
13
|
-
- Must handle and display errors appropriately
|
|
14
|
-
- Must cache customer info and offerings
|
|
15
|
-
- Must re-render on entitlement changes
|
|
16
|
-
|
|
17
|
-
### PROHIBITED
|
|
18
|
-
- DO NOT skip loading states
|
|
19
|
-
- DO NOT show technical errors to users
|
|
20
|
-
- DO NOT ignore cache invalidation
|
|
21
|
-
- DO NOT prevent re-renders on state changes
|
|
22
|
-
|
|
23
|
-
### CRITICAL SAFETY
|
|
24
|
-
- All async operations MUST show loading states
|
|
25
|
-
- All errors MUST be handled and displayed appropriately
|
|
26
|
-
- Data MUST be cached appropriately
|
|
27
|
-
- UI MUST re-render on entitlement changes
|
|
28
|
-
|
|
29
|
-
## AI Agent Guidelines
|
|
30
|
-
1. Always show loading states during async operations
|
|
31
|
-
2. Handle and display errors appropriately to users
|
|
32
|
-
3. Update UI optimistically where possible
|
|
33
|
-
4. Cache customer info and offerings appropriately
|
|
34
|
-
5. Re-render on entitlement changes
|
|
35
|
-
6. Provide clear feedback during purchases
|
|
36
|
-
7. Validate data before displaying to users
|
|
37
|
-
|
|
38
|
-
## Related Documentation
|
|
39
|
-
- [RevenueCat Integration](../README.md)
|
|
40
|
-
- [RevenueCat Application](../application/README.md)
|
|
41
|
-
- [RevenueCat Infrastructure](../infrastructure/README.md)
|
|
42
|
-
- [RevenueCat Domain](../domain/README.md)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/src/{revenuecat/domain/constants → domains/subscription/core}/RevenueCatConstants.ts
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/usePaywallFlow.ts
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useRevenueCat.ts
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/src/{revenuecat → domains/subscription}/infrastructure/services/OfferingsFetcher.ts
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/src/{revenuecat → domains/subscription}/infrastructure/services/RevenueCatInitializer.ts
RENAMED
|
File without changes
|
/package/src/{revenuecat → domains/subscription}/infrastructure/services/RevenueCatService.ts
RENAMED
|
File without changes
|
/package/src/{revenuecat → domains/subscription}/infrastructure/services/ServiceStateManager.ts
RENAMED
|
File without changes
|
|
File without changes
|
/package/src/{revenuecat → domains/subscription}/infrastructure/utils/InitializationCache.ts
RENAMED
|
File without changes
|
/package/src/{revenuecat → domains/subscription}/infrastructure/utils/PremiumStatusSyncer.ts
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/src/{presentation/hooks → domains/subscription/presentation}/useAuthAwarePurchase.ts
RENAMED
|
File without changes
|
/package/src/{presentation/hooks → domains/subscription/presentation}/useAuthSubscriptionSync.ts
RENAMED
|
File without changes
|
|
File without changes
|
/package/src/{presentation/hooks → domains/subscription/presentation}/usePaywallVisibility.ts
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|