@umituz/react-native-subscription 2.27.66 → 2.27.68

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.
Files changed (125) hide show
  1. package/package.json +1 -1
  2. package/src/domains/credits/application/CreditLimitCalculator.ts +17 -0
  3. package/src/domains/credits/application/CreditsInitializer.ts +85 -0
  4. package/src/domains/credits/application/DeductCreditsCommand.ts +52 -0
  5. package/src/domains/credits/application/PurchaseMetadataGenerator.ts +59 -0
  6. package/src/domains/credits/application/credit-strategies/CreditAllocationContext.ts +35 -0
  7. package/src/domains/credits/application/credit-strategies/ICreditStrategy.ts +18 -0
  8. package/src/domains/credits/application/credit-strategies/StandardPurchaseCreditStrategy.ts +16 -0
  9. package/src/domains/credits/application/credit-strategies/SyncCreditStrategy.ts +15 -0
  10. package/src/domains/credits/application/credit-strategies/TrialCreditStrategy.ts +18 -0
  11. package/src/{infrastructure/mappers → domains/credits/core}/CreditsMapper.ts +4 -4
  12. package/src/domains/credits/infrastructure/CreditsRepository.ts +102 -0
  13. package/src/{presentation/hooks → domains/credits/presentation}/useCredits.ts +21 -4
  14. package/src/domains/subscription/application/SubscriptionAuthListener.ts +26 -0
  15. package/src/domains/subscription/application/SubscriptionInitializer.ts +77 -0
  16. package/src/{infrastructure/services → domains/subscription/application}/SubscriptionInitializerTypes.ts +21 -1
  17. package/src/domains/subscription/application/SubscriptionSyncService.ts +71 -0
  18. package/src/domains/subscription/application/SubscriptionSyncUtils.ts +16 -0
  19. package/src/{revenuecat/domain/value-objects → domains/subscription/core}/RevenueCatConfig.ts +1 -1
  20. package/src/{domain/types → domains/subscription/core}/RevenueCatData.ts +1 -1
  21. package/src/{domain/entities → domains/subscription/core}/SubscriptionStatus.ts +13 -21
  22. package/src/domains/subscription/core/SubscriptionStatusHandlers.ts +51 -0
  23. package/src/domains/subscription/infrastructure/handlers/PackageHandler.ts +67 -0
  24. package/src/domains/subscription/infrastructure/handlers/PurchaseStatusResolver.ts +27 -0
  25. package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useRevenueCat.ts +1 -1
  26. package/src/domains/subscription/infrastructure/managers/SubscriptionInternalState.ts +12 -0
  27. package/src/domains/subscription/infrastructure/managers/SubscriptionManager.ts +110 -0
  28. package/src/{revenuecat → domains/subscription}/infrastructure/services/PurchaseHandler.ts +1 -1
  29. package/src/{revenuecat → domains/subscription}/infrastructure/services/RestoreHandler.ts +1 -1
  30. package/src/{revenuecat → domains/subscription}/infrastructure/services/RevenueCatInitializer.ts +1 -1
  31. package/src/{revenuecat → domains/subscription}/infrastructure/services/RevenueCatService.ts +2 -2
  32. package/src/{presentation/hooks → domains/subscription/presentation}/usePremium.ts +7 -4
  33. package/src/domains/trial/application/TrialEligibilityService.ts +25 -0
  34. package/src/domains/trial/application/TrialService.ts +69 -0
  35. package/src/{infrastructure/services → domains/trial/core}/TrialTypes.ts +1 -1
  36. package/src/domains/trial/infrastructure/DeviceTrialRepository.ts +30 -0
  37. package/src/index.ts +28 -59
  38. package/src/init/createSubscriptionInitModule.ts +1 -1
  39. package/src/presentation/components/details/PremiumStatusBadge.tsx +1 -3
  40. package/src/presentation/hooks/feedback/useFeedbackSubmit.ts +1 -1
  41. package/src/presentation/hooks/index.ts +11 -11
  42. package/src/shared/application/ports/IRevenueCatService.ts +32 -0
  43. package/src/shared/infrastructure/SubscriptionEventBus.ts +51 -0
  44. package/src/application/README.md +0 -50
  45. package/src/domain/README.md +0 -54
  46. package/src/domain/entities/README.md +0 -50
  47. package/src/domain/entities/SubscriptionStatus.test.ts +0 -149
  48. package/src/domain/errors/README.md +0 -53
  49. package/src/domain/value-objects/README.md +0 -50
  50. package/src/infrastructure/README.md +0 -55
  51. package/src/infrastructure/mappers/README.md +0 -21
  52. package/src/infrastructure/models/README.md +0 -26
  53. package/src/infrastructure/repositories/CreditsRepository.ts +0 -132
  54. package/src/infrastructure/repositories/README.md +0 -99
  55. package/src/infrastructure/services/CreditsInitializer.ts +0 -170
  56. package/src/infrastructure/services/README.md +0 -99
  57. package/src/infrastructure/services/SubscriptionInitializer.ts +0 -176
  58. package/src/infrastructure/services/SubscriptionService.ts +0 -133
  59. package/src/infrastructure/services/TrialService.ts +0 -197
  60. package/src/infrastructure/services/app-service-helpers.ts +0 -111
  61. package/src/revenuecat/README.md +0 -104
  62. package/src/revenuecat/application/README.md +0 -43
  63. package/src/revenuecat/application/ports/IRevenueCatService.ts +0 -76
  64. package/src/revenuecat/application/ports/README.md +0 -41
  65. package/src/revenuecat/domain/README.md +0 -48
  66. package/src/revenuecat/domain/constants/README.md +0 -41
  67. package/src/revenuecat/domain/entities/README.md +0 -42
  68. package/src/revenuecat/domain/errors/README.md +0 -53
  69. package/src/revenuecat/domain/types/README.md +0 -41
  70. package/src/revenuecat/domain/value-objects/README.md +0 -41
  71. package/src/revenuecat/index.ts +0 -13
  72. package/src/revenuecat/infrastructure/handlers/PackageHandler.ts +0 -161
  73. package/src/revenuecat/infrastructure/managers/SubscriptionManager.ts +0 -165
  74. package/src/revenuecat/presentation/README.md +0 -42
  75. /package/src/{domain/entities → domains/credits/core}/Credits.ts +0 -0
  76. /package/src/{infrastructure/models → domains/credits/core}/UserCreditsDocument.ts +0 -0
  77. /package/src/{infrastructure/repositories → domains/credits/infrastructure}/CreditsRepositoryProvider.ts +0 -0
  78. /package/src/{presentation/hooks → domains/credits/presentation}/useDeductCredit.ts +0 -0
  79. /package/src/{revenuecat/domain/constants → domains/subscription/core}/RevenueCatConstants.ts +0 -0
  80. /package/src/{revenuecat/domain/errors → domains/subscription/core}/RevenueCatError.ts +0 -0
  81. /package/src/{revenuecat/domain/types → domains/subscription/core}/RevenueCatTypes.ts +0 -0
  82. /package/src/{domain/entities → domains/subscription/core}/SubscriptionConstants.ts +0 -0
  83. /package/src/{revenuecat → domains/subscription}/infrastructure/README.md +0 -0
  84. /package/src/{revenuecat → domains/subscription}/infrastructure/config/README.md +0 -0
  85. /package/src/{revenuecat → domains/subscription}/infrastructure/handlers/README.md +0 -0
  86. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/README.md +0 -0
  87. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/subscriptionQueryKeys.ts +0 -0
  88. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useCustomerInfo.ts +0 -0
  89. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useInitializeSubscription.ts +0 -0
  90. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/usePaywallFlow.ts +0 -0
  91. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/usePurchasePackage.ts +0 -0
  92. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useRestorePurchase.ts +0 -0
  93. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useRevenueCatTrialEligibility.ts +0 -0
  94. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useSubscriptionPackages.ts +0 -0
  95. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useSubscriptionQueries.ts +0 -0
  96. /package/src/{revenuecat → domains/subscription}/infrastructure/managers/README.md +0 -0
  97. /package/src/{revenuecat → domains/subscription}/infrastructure/services/CustomerInfoListenerManager.ts +0 -0
  98. /package/src/{revenuecat → domains/subscription}/infrastructure/services/OfferingsFetcher.ts +0 -0
  99. /package/src/{revenuecat → domains/subscription}/infrastructure/services/README.md +0 -0
  100. /package/src/{revenuecat → domains/subscription}/infrastructure/services/ServiceStateManager.ts +0 -0
  101. /package/src/{revenuecat → domains/subscription}/infrastructure/utils/ApiKeyResolver.ts +0 -0
  102. /package/src/{revenuecat → domains/subscription}/infrastructure/utils/InitializationCache.ts +0 -0
  103. /package/src/{revenuecat → domains/subscription}/infrastructure/utils/PremiumStatusSyncer.ts +0 -0
  104. /package/src/{revenuecat → domains/subscription}/infrastructure/utils/README.md +0 -0
  105. /package/src/{revenuecat → domains/subscription}/infrastructure/utils/RenewalDetector.ts +0 -0
  106. /package/src/{revenuecat → domains/subscription}/infrastructure/utils/UserIdProvider.ts +0 -0
  107. /package/src/{presentation/hooks → domains/subscription/presentation}/useAuthAwarePurchase.ts +0 -0
  108. /package/src/{presentation/hooks → domains/subscription/presentation}/useAuthSubscriptionSync.ts +0 -0
  109. /package/src/{presentation/hooks → domains/subscription/presentation}/useFeatureGate.ts +0 -0
  110. /package/src/{presentation/hooks → domains/subscription/presentation}/usePaywallVisibility.ts +0 -0
  111. /package/src/{presentation/hooks → domains/subscription/presentation}/usePremiumGate.ts +0 -0
  112. /package/src/{presentation/hooks → domains/subscription/presentation}/useSavedPurchaseAutoExecution.ts +0 -0
  113. /package/src/{presentation/hooks → domains/subscription/presentation}/useSubscriptionSettingsConfig.ts +0 -0
  114. /package/src/{presentation/hooks → domains/subscription/presentation}/useSubscriptionSettingsConfig.utils.ts +0 -0
  115. /package/src/{presentation/hooks → domains/subscription/presentation}/useSubscriptionStatus.ts +0 -0
  116. /package/src/{infrastructure/services → shared/application}/ActivationHandler.ts +0 -0
  117. /package/src/{infrastructure/services → shared/application}/FeedbackService.ts +0 -0
  118. /package/src/{application → shared/application}/ports/ISubscriptionRepository.ts +0 -0
  119. /package/src/{application → shared/application}/ports/ISubscriptionService.ts +0 -0
  120. /package/src/{application → shared/application}/ports/README.md +0 -0
  121. /package/src/{domain/errors → shared/utils}/InsufficientCreditsError.ts +0 -0
  122. /package/src/{infrastructure → shared}/utils/Logger.ts +0 -0
  123. /package/src/{domain/value-objects → shared/utils}/Result.ts +0 -0
  124. /package/src/{domain/value-objects → shared/utils}/SubscriptionConfig.ts +0 -0
  125. /package/src/{domain/errors → shared/utils}/SubscriptionError.ts +0 -0
@@ -1,50 +0,0 @@
1
- # Domain Value Objects
2
-
3
- Value objects for the subscription domain.
4
-
5
- ## Location
6
-
7
- `src/domain/value-objects/`
8
-
9
- ## Strategy
10
-
11
- Value objects are immutable objects that represent concepts by their attributes rather than identity. They ensure validity and prevent primitive obsession by providing type-safe, validated representations.
12
-
13
- ## Restrictions
14
-
15
- ### REQUIRED
16
-
17
- - MUST be immutable (all properties readonly)
18
- - MUST validate on creation (fail fast)
19
- - MUST override equality (compare by value, not reference)
20
- - MUST use for complex attributes (not simple primitives)
21
- - MUST be small and focused
22
-
23
- ### PROHIBITED
24
-
25
- - MUST NOT allow mutation after creation
26
- - MUST NOT use reference equality for comparison
27
- - MUST NOT contain invalid states
28
- - MUST NOT have identity-based equality
29
-
30
- ### CRITICAL
31
-
32
- - Always validate on creation
33
- - Implement value-based equality comparison
34
- - Keep value objects small and focused
35
- - Use only for complex attributes, not simple primitives
36
-
37
- ## AI Agent Guidelines
38
-
39
- When working with value objects:
40
- 1. Make immutable - all properties readonly
41
- 2. Validate on creation - fail fast
42
- 3. Override equality - compare by value, not reference
43
- 4. Use for complex attributes - don't use for simple primitives
44
- 5. Keep small - value objects should be focused
45
-
46
- ## Related Documentation
47
-
48
- - [Domain Entities](../entities/README.md)
49
- - [Domain Errors](../errors/README.md)
50
- - [Domain Layer](../README.md)
@@ -1,55 +0,0 @@
1
- # Infrastructure Layer
2
-
3
- Abonelik sisteminin dış dünya ile iletişimini sağlayan implementations ve repositories içeren katman.
4
-
5
- ## Location
6
-
7
- `src/infrastructure/`
8
-
9
- ## Strategy
10
-
11
- Dış servis entegrasyonlarını (Firebase, RevenueCat), veri erişim implementasyonlarını ve karmaşık operasyon yöneticilerini içeren katman. Dependency injection ve test edilebilirlik sağlar.
12
-
13
- ## Restrictions
14
-
15
- ### REQUIRED
16
-
17
- - MUST implement all port interfaces from application layer
18
- - MUST handle all errors gracefully and propagate appropriately
19
- - MUST validate all inputs before processing
20
- - MUST implement caching for frequently accessed data
21
- - MUST support logging for debugging and monitoring
22
- - MUST be testable with mock implementations
23
-
24
- ### PROHIBITED
25
-
26
- - MUST NOT contain business logic (belongs in domain/application)
27
- - MUST NOT bypass error handling
28
- - MUST NOT expose implementation details to other layers
29
- - MUST NOT create tight coupling with external services
30
-
31
- ### CRITICAL
32
-
33
- - Always validate inputs before processing
34
- - Implement proper error handling for all external calls
35
- - Use dependency injection for all external dependencies
36
- - Ensure all implementations are mockable for testing
37
- - Implement retry logic for network operations
38
-
39
- ## AI Agent Guidelines
40
-
41
- When working with infrastructure layer:
42
- 1. Dependency Injection - repository'leri constructor'da alın
43
- 2. Error Handling - tüm hataları yakalayın ve uygun şekilde handle edin
44
- 3. Caching - sık kullanılan verileri cache'leyin
45
- 4. Validation - girdileri validate edin
46
- 5. Logging - önemli operasyonları log'layın
47
- 6. Testing - mock implementasyonlarla test edilebilir yapın
48
- 7. Retry Logic - network hataları için retry logic ekleyin
49
-
50
- ## Related Documentation
51
-
52
- - [Application Layer](../application/README.md)
53
- - [Domain Layer](../domain/README.md)
54
- - [Repositories](./repositories/README.md)
55
- - [Services](./services/README.md)
@@ -1,21 +0,0 @@
1
- # Infrastructure Mappers
2
-
3
- Data transformation mappers between layers.
4
-
5
- ## Overview
6
-
7
- This directory contains mapper functions that transform data between different layers (e.g., domain entities to DTOs, external API responses to domain models).
8
-
9
- ## Contents
10
-
11
- - **subscriptionMapper.ts** - Maps between subscription entities and external formats
12
- - **creditsMapper.ts** - Maps between credit entities and Firestore documents
13
-
14
- ## Purpose
15
-
16
- Mappers provide clean separation between layers:
17
-
18
- ## Related
19
-
20
- - [Models](../models/README.md)
21
- - [Domain](../../domain/README.md)
@@ -1,26 +0,0 @@
1
- # Infrastructure Models
2
-
3
- Data models and schemas used by the infrastructure layer.
4
-
5
- ## Overview
6
-
7
- This directory contains data models, schemas, and interfaces used by infrastructure implementations.
8
-
9
- ## Contents
10
-
11
- - **SubscriptionModel.ts** - Subscription data model for persistence/transport
12
- - **CreditsModel.ts** - Credits data model for Firestore
13
-
14
- ## Purpose
15
-
16
- Models provide structure for data storage and API communication:
17
-
18
- - **Validation**: Ensure data integrity
19
- - **Serialization**: Convert to/from storage formats
20
- - **Type Safety**: Provide TypeScript interfaces
21
-
22
- ## Related
23
-
24
- - [Repositories](../repositories/README.md)
25
- - [Services](../services/README.md)
26
- - [Domain Entities](../../domain/entities/README.md)
@@ -1,132 +0,0 @@
1
- /**
2
- * Credits Repository
3
- */
4
-
5
- import { doc, getDoc, runTransaction, serverTimestamp, type Firestore, type Transaction } from "firebase/firestore";
6
- import { BaseRepository, getFirestore } from "@umituz/react-native-firebase";
7
- import type { CreditsConfig, CreditsResult, DeductCreditsResult } from "../../domain/entities/Credits";
8
- import type { UserCreditsDocumentRead, PurchaseSource } from "../models/UserCreditsDocument";
9
- import { initializeCreditsTransaction, type InitializeCreditsMetadata } from "../services/CreditsInitializer";
10
- import { detectPackageType } from "../../utils/packageTypeDetector";
11
- import { getCreditAllocation } from "../../utils/creditMapper";
12
- import { CreditsMapper } from "../mappers/CreditsMapper";
13
- import type { RevenueCatData } from "../../domain/types/RevenueCatData";
14
-
15
- export type { RevenueCatData } from "../../domain/types/RevenueCatData";
16
-
17
- export class CreditsRepository extends BaseRepository {
18
- constructor(private config: CreditsConfig) { super(); }
19
-
20
- private getRef(db: Firestore, userId: string) {
21
- return this.config.useUserSubcollection
22
- ? doc(db, "users", userId, "credits", "balance")
23
- : doc(db, this.config.collectionName, userId);
24
- }
25
-
26
- async getCredits(userId: string): Promise<CreditsResult> {
27
- const db = getFirestore();
28
- if (!db) {
29
- if (__DEV__) console.log("[CreditsRepository] No Firestore instance");
30
- return { success: false, error: { message: "No DB", code: "DB_ERR" } };
31
- }
32
- try {
33
- const ref = this.getRef(db, userId);
34
- if (__DEV__) console.log("[CreditsRepository] Fetching credits:", { userId: userId.slice(0, 8), path: ref.path });
35
- const snap = await getDoc(ref);
36
- if (!snap.exists()) {
37
- if (__DEV__) console.log("[CreditsRepository] No credits document found");
38
- return { success: true, data: undefined };
39
- }
40
- const d = snap.data() as UserCreditsDocumentRead;
41
- const entity = CreditsMapper.toEntity(d);
42
- if (__DEV__) console.log("[CreditsRepository] Credits fetched:", { credits: entity.credits, limit: entity.creditLimit });
43
- return { success: true, data: entity };
44
- } catch (e: unknown) {
45
- const message = e instanceof Error ? e.message : String(e);
46
- if (__DEV__) console.error("[CreditsRepository] Fetch error:", message);
47
- return { success: false, error: { message, code: "FETCH_ERR" } };
48
- }
49
- }
50
-
51
- async initializeCredits(
52
- userId: string, purchaseId?: string, productId?: string,
53
- source?: PurchaseSource, revenueCatData?: RevenueCatData
54
- ): Promise<CreditsResult> {
55
- const db = getFirestore();
56
- if (!db) return { success: false, error: { message: "No DB", code: "INIT_ERR" } };
57
- try {
58
- let cfg = { ...this.config };
59
- if (productId) {
60
- const amt = this.config.creditPackageAmounts?.[productId];
61
- if (amt) cfg = { ...cfg, creditLimit: amt };
62
- else {
63
- const packageType = detectPackageType(productId);
64
- const dynamicLimit = getCreditAllocation(packageType, this.config.packageAllocations);
65
- if (dynamicLimit !== null) cfg = { ...cfg, creditLimit: dynamicLimit };
66
- }
67
- }
68
-
69
- const metadata: InitializeCreditsMetadata = {
70
- productId, source,
71
- expirationDate: revenueCatData?.expirationDate,
72
- willRenew: revenueCatData?.willRenew,
73
- originalTransactionId: revenueCatData?.originalTransactionId,
74
- isPremium: revenueCatData?.isPremium,
75
- periodType: revenueCatData?.periodType,
76
- };
77
-
78
- await initializeCreditsTransaction(db, this.getRef(db, userId), cfg, purchaseId, metadata);
79
- // Re-fetch from Firestore to get the actual stored data with all fields
80
- const snap = await getDoc(this.getRef(db, userId));
81
- const fullData = snap.exists() ? snap.data() as UserCreditsDocumentRead : undefined;
82
- return {
83
- success: true,
84
- data: fullData ? CreditsMapper.toEntity(fullData) : undefined,
85
- };
86
- } catch (e: unknown) {
87
- const message = e instanceof Error ? e.message : String(e);
88
- return { success: false, error: { message, code: "INIT_ERR" } };
89
- }
90
- }
91
-
92
- async deductCredit(userId: string, cost: number = 1): Promise<DeductCreditsResult> {
93
- const db = getFirestore();
94
- if (!db) return { success: false, error: { message: "No DB", code: "ERR" } };
95
- try {
96
- const remaining = await runTransaction(db, async (tx: Transaction) => {
97
- const docSnap = await tx.get(this.getRef(db, userId));
98
- if (!docSnap.exists()) throw new Error("NO_CREDITS");
99
- const current = docSnap.data().credits as number;
100
- if (current < cost) throw new Error("CREDITS_EXHAUSTED");
101
- const updated = current - cost;
102
- tx.update(this.getRef(db, userId), { credits: updated, lastUpdatedAt: serverTimestamp() });
103
- return updated;
104
- });
105
- return { success: true, remainingCredits: remaining };
106
- } catch (e: unknown) {
107
- const message = e instanceof Error ? e.message : String(e);
108
- const code = message === "NO_CREDITS" || message === "CREDITS_EXHAUSTED" ? message : "DEDUCT_ERR";
109
- return { success: false, error: { message, code } };
110
- }
111
- }
112
-
113
- async hasCredits(userId: string, cost: number = 1): Promise<boolean> {
114
- const res = await this.getCredits(userId);
115
- return !!(res.success && res.data && res.data.credits >= cost);
116
- }
117
-
118
- async syncExpiredStatus(userId: string): Promise<void> {
119
- const db = getFirestore();
120
- if (!db) return;
121
- try {
122
- const ref = this.getRef(db, userId);
123
- const { updateDoc } = await import("firebase/firestore");
124
- await updateDoc(ref, { isPremium: false, status: "expired", lastUpdatedAt: serverTimestamp() });
125
- if (__DEV__) console.log("[CreditsRepository] Synced expired status:", userId.slice(0, 8));
126
- } catch (e) {
127
- if (__DEV__) console.error("[CreditsRepository] Sync expired failed:", e);
128
- }
129
- }
130
- }
131
-
132
- export const createCreditsRepository = (c: CreditsConfig) => new CreditsRepository(c);
@@ -1,99 +0,0 @@
1
- # Infrastructure Repositories
2
-
3
- Repository implementations for data persistence.
4
-
5
- ## Location
6
-
7
- - **Base Path**: `/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-subscription/src/infrastructure/repositories/`
8
- - **Repositories**: `src/infrastructure/repositories/`
9
-
10
- ## Strategy
11
-
12
- ### Repository Pattern
13
-
14
- Data access abstraction layer.
15
-
16
- - **CreditsRepository**: Credits data management
17
- - **TransactionRepository**: Transaction history
18
- - **SubscriptionRepository**: Subscription status management
19
- - **Custom Repositories**: Extensible repository architecture
20
-
21
- ### Data Access Patterns
22
-
23
- Structured data access implementations.
24
-
25
- - **Active Record**: Entity-based data management
26
- - **Data Mapper**: Separation of entities and data
27
- - **Repository with Caching**: Performance optimization
28
- - **Transaction Support**: Atomic operations
29
-
30
- ### Storage Implementations
31
-
32
- Multiple storage backend support.
33
-
34
- - **Firestore**: Cloud Firestore implementation
35
- - **In-Memory**: Testing and development
36
- - **HTTP**: REST API integration
37
- - **Redis**: Redis cache implementation
38
-
39
- ### Advanced Features
40
-
41
- Enterprise-level repository capabilities.
42
-
43
- - **Real-Time Updates**: Firestore real-time listeners
44
- - **Transaction Support**: Multi-document transactions
45
- - **Caching**: Performance optimization
46
- - **Mocking**: Testing support
47
-
48
- ## Restrictions
49
-
50
- ### REQUIRED
51
-
52
- - **Interface Implementation**: Implement required interfaces
53
- - **Error Handling**: Transform storage errors to domain errors
54
- - **Validation**: Validate data before saving
55
- - **Logging**: Log repository operations
56
-
57
- ### PROHIBITED
58
-
59
- - **Business Logic**: Keep business logic in services
60
- - **Direct Exposure**: Never expose storage-specific types
61
- - **Missing Validation**: Never save invalid data
62
- - **Swallowed Errors**: Always handle or propagate errors
63
-
64
- ### CRITICAL
65
-
66
- - **Error Transformation**: Transform storage errors appropriately
67
- - **Data Integrity**: Maintain data integrity
68
- - **Performance**: Optimize with caching and batching
69
- - **Testing**: Support mock implementations
70
-
71
- ## AI Agent Guidelines
72
-
73
- ### When Modifying Repositories
74
-
75
- 1. **Interface Compliance**: Maintain interface contracts
76
- 2. **Error Handling**: Transform errors appropriately
77
- 3. **Validation**: Validate all data
78
- 4. **Logging**: Log operations for debugging
79
-
80
- ### When Creating Custom Repositories
81
-
82
- 1. **Interface Implementation**: Implement required interfaces
83
- 2. **Error Transformation**: Transform storage errors
84
- 3. **Validation**: Add data validation
85
- 4. **Testing**: Create mock implementations
86
-
87
- ### When Fixing Repository Bugs
88
-
89
- 1. **Data Access**: Check data access logic
90
- 2. **Error Handling**: Verify error transformation
91
- 3. **Validation**: Check validation rules
92
- 4. **Edge Cases**: Test with null/undefined values
93
-
94
- ## Related Documentation
95
-
96
- - [Infrastructure Services](/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-subscription/src/infrastructure/services/README.md)
97
- - [Infrastructure Layer](/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-subscription/src/infrastructure/README.md)
98
- - [Application Ports](/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-subscription/src/application/ports/README.md)
99
- - [Domain Layer](/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-subscription/src/domain/README.md)
@@ -1,170 +0,0 @@
1
- /**
2
- * Credits Initializer
3
- * Handles subscription credit initialization for premium users
4
- */
5
-
6
- import { Platform } from "react-native";
7
- import Constants from "expo-constants";
8
- import {
9
- runTransaction,
10
- serverTimestamp,
11
- Timestamp,
12
- type Firestore,
13
- type FieldValue,
14
- type Transaction,
15
- type DocumentReference,
16
- } from "firebase/firestore";
17
- import type { CreditsConfig } from "../../domain/entities/Credits";
18
- import type {
19
- UserCreditsDocumentRead,
20
- PurchaseSource,
21
- PurchaseType,
22
- PurchaseMetadata,
23
- } from "../models/UserCreditsDocument";
24
- import { SUBSCRIPTION_STATUS, resolveSubscriptionStatus, type PeriodType } from "../../domain/entities/SubscriptionStatus";
25
- import { TRIAL_CONFIG } from "./TrialService";
26
- import { detectPackageType } from "../../utils/packageTypeDetector";
27
- import { getCreditAllocation } from "../../utils/creditMapper";
28
-
29
- interface InitializationResult {
30
- credits: number;
31
- alreadyProcessed?: boolean;
32
- }
33
-
34
- export interface InitializeCreditsMetadata {
35
- productId?: string;
36
- source?: PurchaseSource;
37
- type?: PurchaseType;
38
- expirationDate?: string | null;
39
- willRenew?: boolean;
40
- originalTransactionId?: string;
41
- isPremium?: boolean;
42
- periodType?: PeriodType;
43
- }
44
-
45
- export async function initializeCreditsTransaction(
46
- db: Firestore,
47
- creditsRef: DocumentReference,
48
- config: CreditsConfig,
49
- purchaseId?: string,
50
- metadata?: InitializeCreditsMetadata
51
- ): Promise<InitializationResult> {
52
- return runTransaction(db, async (transaction: Transaction) => {
53
- const creditsDoc = await transaction.get(creditsRef);
54
- const now = serverTimestamp();
55
- const existingData = creditsDoc.exists() ? creditsDoc.data() as UserCreditsDocumentRead : null;
56
-
57
- let purchasedAt: FieldValue = now;
58
- let processedPurchases: string[] = existingData?.processedPurchases || [];
59
-
60
- if (existingData && purchaseId && processedPurchases.includes(purchaseId)) {
61
- return { credits: existingData.credits, alreadyProcessed: true };
62
- }
63
-
64
- if (existingData?.purchasedAt) {
65
- purchasedAt = existingData.purchasedAt as unknown as FieldValue;
66
- }
67
-
68
- if (purchaseId) {
69
- processedPurchases = [...processedPurchases, purchaseId].slice(-10);
70
- }
71
-
72
- const productId = metadata?.productId;
73
- const packageType = productId ? detectPackageType(productId) : undefined;
74
- const allocation = packageType && packageType !== "unknown"
75
- ? getCreditAllocation(packageType, config.packageAllocations)
76
- : null;
77
- const creditLimit = allocation ?? config.creditLimit;
78
-
79
- const platform = Platform.OS as "ios" | "android";
80
- const appVersion = Constants.expoConfig?.version;
81
-
82
- let purchaseType: PurchaseType = metadata?.type ?? "initial";
83
- if (existingData?.packageType && packageType !== "unknown") {
84
- const oldLimit = existingData.creditLimit || 0;
85
- if (creditLimit > oldLimit) purchaseType = "upgrade";
86
- else if (creditLimit < oldLimit) purchaseType = "downgrade";
87
- else if (purchaseId?.startsWith("renewal_")) purchaseType = "renewal";
88
- }
89
-
90
- const purchaseMetadata: PurchaseMetadata | undefined =
91
- productId && metadata?.source && packageType && packageType !== "unknown" ? {
92
- productId,
93
- packageType,
94
- creditLimit,
95
- source: metadata.source,
96
- type: purchaseType,
97
- platform,
98
- appVersion,
99
- timestamp: Timestamp.fromDate(new Date()) as unknown as PurchaseMetadata["timestamp"],
100
- } : undefined;
101
-
102
- const purchaseHistory = purchaseMetadata
103
- ? [...(existingData?.purchaseHistory || []), purchaseMetadata].slice(-10)
104
- : existingData?.purchaseHistory;
105
-
106
- const isPremium = metadata?.isPremium ?? true;
107
- const willRenew = metadata?.willRenew;
108
- const periodType = metadata?.periodType;
109
-
110
- const expirationDateStr = metadata?.expirationDate;
111
- const isExpired = expirationDateStr ? new Date(expirationDateStr).getTime() < Date.now() : false;
112
- const status = resolveSubscriptionStatus({ isPremium, willRenew, isExpired, periodType });
113
-
114
- // Determine if this is a status sync (not a new purchase or renewal)
115
- // Status sync should preserve existing credits, only update metadata
116
- const isStatusSync = purchaseId?.startsWith("status_sync_") ?? false;
117
- const isNewPurchaseOrRenewal = purchaseId?.startsWith("purchase_") || purchaseId?.startsWith("renewal_");
118
-
119
- let newCredits = creditLimit;
120
- if (status === SUBSCRIPTION_STATUS.TRIAL) {
121
- newCredits = TRIAL_CONFIG.CREDITS;
122
- } else if (status === SUBSCRIPTION_STATUS.TRIAL_CANCELED) {
123
- newCredits = 0;
124
- } else if (isStatusSync && existingData?.credits !== undefined && existingData.isPremium) {
125
- // Status sync for existing premium user: preserve current credits
126
- newCredits = existingData.credits;
127
- }
128
-
129
- const creditsData: Record<string, unknown> = {
130
- isPremium,
131
- status,
132
- credits: newCredits,
133
- creditLimit,
134
- purchasedAt,
135
- lastUpdatedAt: now,
136
- lastPurchaseAt: isNewPurchaseOrRenewal ? now : (existingData?.lastPurchaseAt ?? now),
137
- processedPurchases,
138
- };
139
-
140
- if (metadata?.expirationDate) creditsData.expirationDate = Timestamp.fromDate(new Date(metadata.expirationDate));
141
- if (metadata?.willRenew !== undefined) creditsData.willRenew = metadata.willRenew;
142
- if (metadata?.originalTransactionId) creditsData.originalTransactionId = metadata.originalTransactionId;
143
- if (packageType && packageType !== "unknown") creditsData.packageType = packageType;
144
- if (productId) {
145
- creditsData.productId = productId;
146
- creditsData.platform = platform;
147
- creditsData.appVersion = appVersion;
148
- }
149
-
150
- const isTrialing = status === SUBSCRIPTION_STATUS.TRIAL || status === SUBSCRIPTION_STATUS.TRIAL_CANCELED;
151
- if (periodType) creditsData.periodType = periodType;
152
- if (isTrialing) {
153
- creditsData.isTrialing = status === SUBSCRIPTION_STATUS.TRIAL;
154
- creditsData.trialCredits = TRIAL_CONFIG.CREDITS;
155
- if (!existingData?.trialStartDate) creditsData.trialStartDate = now;
156
- if (metadata?.expirationDate) creditsData.trialEndDate = Timestamp.fromDate(new Date(metadata.expirationDate));
157
- } else if (existingData?.isTrialing && isPremium) {
158
- creditsData.isTrialing = false;
159
- creditsData.convertedFromTrial = true;
160
- }
161
-
162
- if (metadata?.source) creditsData.purchaseSource = metadata.source;
163
- if (metadata?.type) creditsData.purchaseType = purchaseType;
164
- if (purchaseHistory?.length) creditsData.purchaseHistory = purchaseHistory;
165
-
166
- transaction.set(creditsRef, creditsData, { merge: true });
167
-
168
- return { credits: newCredits };
169
- });
170
- }
@@ -1,99 +0,0 @@
1
- # Infrastructure Services
2
-
3
- Service implementations in the infrastructure layer.
4
-
5
- ## Location
6
-
7
- - **Base Path**: `/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-subscription/src/infrastructure/services/`
8
- - **Services**: `src/infrastructure/services/`
9
-
10
- ## Strategy
11
-
12
- ### Service Architecture
13
-
14
- Business logic implementation for subscription operations.
15
-
16
- - **SubscriptionService**: Main subscription management service
17
- - **SubscriptionInitializer**: System initialization
18
- - **Status Sync**: Automatic status synchronization
19
- - **Error Handling**: Comprehensive error management
20
-
21
- ### Service Features
22
-
23
- Core service capabilities.
24
-
25
- - **Automatic Status Sync**: Real-time status updates
26
- - **Error Handling**: Robust error management
27
- - **Caching**: Performance optimization through caching
28
- - **Event Handling**: Status change event callbacks
29
-
30
- ### Advanced Patterns
31
-
32
- Enterprise-grade service patterns.
33
-
34
- - **Retry Logic**: Automatic retry on failures
35
- - **Circuit Breaker**: Fault tolerance patterns
36
- - **Observability**: Event emission and tracking
37
- - **Transaction Support**: Atomic operations
38
-
39
- ### Custom Services
40
-
41
- Extensible service architecture.
42
-
43
- - **Interface Implementation**: Custom service implementations
44
- - **Dependency Injection**: Flexible dependency management
45
- - **Testing Support**: Test-friendly design
46
- - **Configuration**: Service-level configuration
47
-
48
- ## Restrictions
49
-
50
- ### REQUIRED
51
-
52
- - **Dependency Injection**: Use constructor injection
53
- - **Error Handling**: Handle all errors appropriately
54
- - **Logging**: Log important operations
55
- - **Validation**: Validate all inputs
56
-
57
- ### PROHIBITED
58
-
59
- - **Direct Storage Access**: Access storage only through repositories
60
- - **Missing Error Handling**: Never swallow errors
61
- - **Hardcoded Values**: Use configuration
62
- - **Circular Dependencies**: Avoid circular dependencies
63
-
64
- ### CRITICAL
65
-
66
- - **Error Transformation**: Transform storage errors to domain errors
67
- - **Performance**: Optimize with caching
68
- - **Reliability**: Implement retry logic
69
- - **Observability**: Track all operations
70
-
71
- ## AI Agent Guidelines
72
-
73
- ### When Modifying Services
74
-
75
- 1. **Interface Compliance**: Maintain interface contracts
76
- 2. **Error Handling**: Comprehensive error handling
77
- 3. **Logging**: Add logging for debugging
78
- 4. **Testing**: Write comprehensive tests
79
-
80
- ### When Creating Custom Services
81
-
82
- 1. **Interface Implementation**: Implement required interfaces
83
- 2. **Dependency Injection**: Inject dependencies via constructor
84
- 3. **Error Handling**: Transform errors appropriately
85
- 4. **Documentation**: Document behavior and contracts
86
-
87
- ### When Fixing Service Bugs
88
-
89
- 1. **Repository Integration**: Check repository calls
90
- 2. **Error Handling**: Verify error transformation
91
- 3. **Business Logic**: Check business rules
92
- 4. **Edge Cases**: Test boundary conditions
93
-
94
- ## Related Documentation
95
-
96
- - [Infrastructure Repositories](/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-subscription/src/infrastructure/repositories/README.md)
97
- - [Application Layer](/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-subscription/src/application/README.md)
98
- - [Application Ports](/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-subscription/src/application/ports/README.md)
99
- - [Infrastructure Layer](/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-subscription/src/infrastructure/README.md)