@umituz/react-native-subscription 2.14.99 → 2.14.101
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/README.md +211 -394
- package/package.json +3 -3
- package/src/application/README.md +46 -225
- package/src/application/ports/README.md +42 -97
- package/src/domain/README.md +36 -384
- package/src/domain/constants/README.md +0 -56
- package/src/domain/entities/README.md +43 -169
- package/src/domain/entities/SubscriptionStatus.ts +1 -1
- package/src/domain/errors/README.md +33 -287
- package/src/domain/value-objects/README.md +43 -179
- package/src/domains/README.md +50 -238
- package/src/domains/README.md.bak +274 -0
- package/src/domains/config/README.md +93 -383
- package/src/domains/config/domain/README.md +23 -376
- package/src/domains/config/domain/entities/README.md +34 -343
- package/src/domains/paywall/README.md +99 -369
- package/src/domains/paywall/components/README.md +34 -178
- package/src/domains/paywall/entities/README.md +34 -193
- package/src/domains/paywall/hooks/README.md +34 -122
- package/src/domains/wallet/README.md +34 -275
- package/src/domains/wallet/README.md.bak +209 -0
- package/src/domains/wallet/domain/README.md +34 -101
- package/src/domains/wallet/domain/entities/README.md +34 -115
- package/src/domains/wallet/domain/errors/README.md +34 -151
- package/src/domains/wallet/infrastructure/README.md +34 -89
- package/src/domains/wallet/presentation/components/README.md +34 -224
- package/src/domains/wallet/presentation/components/TransactionItem.tsx +1 -1
- package/src/domains/wallet/presentation/hooks/README.md +34 -248
- package/src/infrastructure/README.md +37 -496
- package/src/infrastructure/mappers/README.md +0 -13
- package/src/infrastructure/repositories/README.md +74 -360
- package/src/infrastructure/services/ActivationHandler.ts +1 -1
- package/src/infrastructure/services/README.md +95 -370
- package/src/infrastructure/services/SubscriptionService.ts +1 -1
- package/src/presentation/README.md +123 -408
- package/src/presentation/README.md.bak +172 -0
- package/src/presentation/components/README.md +151 -179
- package/src/presentation/components/README.md.bak +217 -0
- package/src/presentation/components/details/CreditRow.md +65 -310
- package/src/presentation/components/details/DetailRow.md +63 -255
- package/src/presentation/components/details/PremiumDetailsCard.md +65 -238
- package/src/presentation/components/details/PremiumStatusBadge.md +64 -239
- package/src/presentation/components/details/README.md +97 -447
- package/src/presentation/components/feedback/PaywallFeedbackModal.md +63 -287
- package/src/presentation/components/feedback/README.md +97 -445
- package/src/presentation/components/paywall/PaywallModal.md +66 -416
- package/src/presentation/components/paywall/README.md +50 -186
- package/src/presentation/components/sections/README.md +97 -466
- package/src/presentation/components/sections/SubscriptionSection.md +92 -244
- package/src/presentation/hooks/README.md +154 -741
- package/src/presentation/hooks/useAuthAwarePurchase.md +58 -325
- package/src/presentation/hooks/useAuthGate.md +61 -375
- package/src/presentation/hooks/useAuthSubscriptionSync.md +66 -370
- package/src/presentation/hooks/useCreditChecker.md +73 -378
- package/src/presentation/hooks/useCredits.md +74 -313
- package/src/presentation/hooks/useCredits.md.bak +231 -0
- package/src/presentation/hooks/useCreditsGate.md +66 -318
- package/src/presentation/hooks/useDeductCredit.md +0 -76
- package/src/presentation/hooks/useDeductCredit.ts +1 -1
- package/src/presentation/hooks/useDevTestCallbacks.md +63 -394
- package/src/presentation/hooks/useFeatureGate.md +105 -150
- package/src/presentation/hooks/useFeatureGate.md.bak +284 -0
- package/src/presentation/hooks/useInitializeCredits.md +64 -430
- package/src/presentation/hooks/usePaywall.md +61 -306
- package/src/presentation/hooks/usePaywallOperations.md +64 -458
- package/src/presentation/hooks/usePaywallVisibility.md +67 -316
- package/src/presentation/hooks/usePremium.md +84 -226
- package/src/presentation/hooks/usePremiumGate.md +60 -395
- package/src/presentation/hooks/usePremiumWithCredits.md +64 -401
- package/src/presentation/hooks/useSubscription.md +66 -422
- package/src/presentation/hooks/useSubscriptionDetails.md +65 -410
- package/src/presentation/hooks/useSubscriptionGate.md +80 -164
- package/src/presentation/hooks/useSubscriptionSettingsConfig.md +66 -346
- package/src/presentation/hooks/useSubscriptionStatus.md +66 -396
- package/src/presentation/hooks/useUserTier.md +63 -328
- package/src/presentation/hooks/useUserTierWithRepository.md +64 -424
- package/src/presentation/screens/README.md +48 -190
- package/src/presentation/types/README.md +0 -16
- package/src/presentation/utils/README.md +0 -21
- package/src/presentation/utils/subscriptionDateUtils.ts +1 -1
- package/src/revenuecat/README.md +99 -518
- package/src/revenuecat/application/README.md +35 -150
- package/src/revenuecat/application/ports/README.md +34 -162
- package/src/revenuecat/domain/README.md +42 -141
- package/src/revenuecat/domain/constants/README.md +34 -176
- package/src/revenuecat/domain/entities/README.md +34 -374
- package/src/revenuecat/domain/errors/README.md +47 -191
- package/src/revenuecat/domain/types/README.md +34 -366
- package/src/revenuecat/domain/value-objects/README.md +34 -434
- package/src/revenuecat/infrastructure/README.md +34 -43
- package/src/revenuecat/infrastructure/config/README.md +32 -23
- package/src/revenuecat/infrastructure/handlers/README.md +34 -211
- package/src/revenuecat/infrastructure/managers/README.md +34 -42
- package/src/revenuecat/infrastructure/services/README.md +35 -318
- package/src/revenuecat/infrastructure/utils/README.md +34 -375
- package/src/revenuecat/presentation/README.md +34 -176
- package/src/revenuecat/presentation/hooks/README.md +29 -35
- package/src/utils/README.md +38 -525
|
@@ -2,513 +2,54 @@
|
|
|
2
2
|
|
|
3
3
|
Abonelik sisteminin dış dünya ile iletişimini sağlayan implementations ve repositories içeren katman.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Location
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- **Services**: Dış servis entegrasyonları (Firebase, RevenueCat, vb.)
|
|
9
|
-
- **Managers**: Karmaşık operasyon yöneticileri
|
|
10
|
-
- **Handlers**: Özel durum ve event yöneticileri
|
|
7
|
+
`src/infrastructure/`
|
|
11
8
|
|
|
12
|
-
##
|
|
9
|
+
## Strategy
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
infrastructure/
|
|
16
|
-
├── repositories/
|
|
17
|
-
│ └── CreditsRepositoryProvider.ts # Kredi repository konfigürasyonu
|
|
18
|
-
├── services/
|
|
19
|
-
│ ├── SubscriptionService.ts # Abonelik servisi implementasyonu
|
|
20
|
-
│ └── SubscriptionInitializer.ts # Abonelik başlatıcı
|
|
21
|
-
└── [other implementations]
|
|
22
|
-
```
|
|
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.
|
|
23
12
|
|
|
24
|
-
##
|
|
13
|
+
## Restrictions
|
|
25
14
|
|
|
26
|
-
###
|
|
15
|
+
### REQUIRED
|
|
27
16
|
|
|
28
|
-
|
|
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
|
|
29
23
|
|
|
30
|
-
|
|
31
|
-
import {
|
|
32
|
-
configureCreditsRepository,
|
|
33
|
-
getCreditsRepository,
|
|
34
|
-
getCreditsConfig,
|
|
35
|
-
resetCreditsRepository,
|
|
36
|
-
isCreditsRepositoryConfigured,
|
|
37
|
-
type CreditsConfig,
|
|
38
|
-
} from '@umituz/react-native-subscription';
|
|
24
|
+
### PROHIBITED
|
|
39
25
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
|
43
30
|
|
|
44
|
-
|
|
45
|
-
{
|
|
46
|
-
id: 'credits_small',
|
|
47
|
-
productId: 'com.app.credits.small',
|
|
48
|
-
amount: 100,
|
|
49
|
-
price: 0.99,
|
|
50
|
-
currency: 'USD',
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
id: 'credits_medium',
|
|
54
|
-
productId: 'com.app.credits.medium',
|
|
55
|
-
amount: 500,
|
|
56
|
-
price: 3.99,
|
|
57
|
-
currency: 'USD',
|
|
58
|
-
},
|
|
59
|
-
],
|
|
31
|
+
### CRITICAL
|
|
60
32
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
|
66
38
|
|
|
67
|
-
|
|
68
|
-
enabled: true,
|
|
69
|
-
daysUntilExpiration: 365,
|
|
70
|
-
},
|
|
71
|
-
};
|
|
39
|
+
## AI Agent Guidelines
|
|
72
40
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
|
79
49
|
|
|
80
|
-
|
|
81
|
-
const repository = getCreditsRepository();
|
|
82
|
-
const creditsConfig = getCreditsConfig();
|
|
50
|
+
## Related Documentation
|
|
83
51
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// Reset (test için)
|
|
90
|
-
resetCreditsRepository();
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### CreditsRepository
|
|
94
|
-
|
|
95
|
-
Kendi repository implementasyonunuzu oluşturun:
|
|
96
|
-
|
|
97
|
-
```typescript
|
|
98
|
-
import {
|
|
99
|
-
createCreditsRepository,
|
|
100
|
-
type CreditsRepository,
|
|
101
|
-
type UserCredits,
|
|
102
|
-
type CreditsResult,
|
|
103
|
-
} from '@umituz/react-native-subscription';
|
|
104
|
-
|
|
105
|
-
class MyCreditsRepository implements CreditsRepository {
|
|
106
|
-
async getCredits(userId: string): Promise<UserCredits> {
|
|
107
|
-
// Veritabanından kredi bilgilerini getir
|
|
108
|
-
const doc = await db.collection('credits').doc(userId).get();
|
|
109
|
-
return doc.data();
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
async addCredits(
|
|
113
|
-
userId: string,
|
|
114
|
-
amount: number,
|
|
115
|
-
reason: string
|
|
116
|
-
): Promise<CreditsResult> {
|
|
117
|
-
// Kredi ekle
|
|
118
|
-
await db.collection('credits').doc(userId).update({
|
|
119
|
-
balance: firebase.firestore.FieldValue.increment(amount),
|
|
120
|
-
lastUpdated: new Date().toISOString(),
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
return {
|
|
124
|
-
success: true,
|
|
125
|
-
newBalance: currentBalance + amount,
|
|
126
|
-
transaction: {
|
|
127
|
-
id: generateId(),
|
|
128
|
-
amount,
|
|
129
|
-
reason,
|
|
130
|
-
timestamp: new Date().toISOString(),
|
|
131
|
-
},
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
async deductCredits(
|
|
136
|
-
userId: string,
|
|
137
|
-
amount: number,
|
|
138
|
-
reason: string
|
|
139
|
-
): Promise<CreditsResult> {
|
|
140
|
-
// Bakiye kontrolü
|
|
141
|
-
const current = await this.getCredits(userId);
|
|
142
|
-
if (current.balance < amount) {
|
|
143
|
-
return {
|
|
144
|
-
success: false,
|
|
145
|
-
error: 'Insufficient credits',
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Kredi düş
|
|
150
|
-
await db.collection('credits').doc(userId).update({
|
|
151
|
-
balance: firebase.firestore.FieldValue.increment(-amount),
|
|
152
|
-
lastUpdated: new Date().toISOString(),
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
return {
|
|
156
|
-
success: true,
|
|
157
|
-
newBalance: current.balance - amount,
|
|
158
|
-
transaction: {
|
|
159
|
-
id: generateId(),
|
|
160
|
-
amount: -amount,
|
|
161
|
-
reason,
|
|
162
|
-
timestamp: new Date().toISOString(),
|
|
163
|
-
},
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Repository oluştur ve kullan
|
|
169
|
-
const repository = new MyCreditsRepository();
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
## Services
|
|
173
|
-
|
|
174
|
-
### SubscriptionService
|
|
175
|
-
|
|
176
|
-
Abonelik servisi implementasyonu:
|
|
177
|
-
|
|
178
|
-
```typescript
|
|
179
|
-
import {
|
|
180
|
-
SubscriptionService,
|
|
181
|
-
initializeSubscriptionService,
|
|
182
|
-
type SubscriptionConfig,
|
|
183
|
-
} from '@umituz/react-native-subscription';
|
|
184
|
-
|
|
185
|
-
// Manuel oluşturma
|
|
186
|
-
const service = new SubscriptionService({
|
|
187
|
-
repository: myRepository,
|
|
188
|
-
onStatusChanged: (userId, newStatus) => {
|
|
189
|
-
console.log(`User ${userId}: ${newStatus.type}`);
|
|
190
|
-
},
|
|
191
|
-
onError: (error) => {
|
|
192
|
-
console.error('Error:', error);
|
|
193
|
-
},
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
// Helper ile başlatma
|
|
197
|
-
await initializeSubscriptionService({
|
|
198
|
-
repository: myRepository,
|
|
199
|
-
firebase: firebaseInstance,
|
|
200
|
-
revenueCatApiKey: 'your_api_key',
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
// Kullanım
|
|
204
|
-
const status = await service.getSubscriptionStatus('user-123');
|
|
205
|
-
const isPremium = await service.isPremium('user-123');
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
### SubscriptionInitializer
|
|
209
|
-
|
|
210
|
-
Uygulamanın başında abonlik sistemini başlatmak için:
|
|
211
|
-
|
|
212
|
-
```typescript
|
|
213
|
-
import {
|
|
214
|
-
initializeSubscription,
|
|
215
|
-
type SubscriptionInitConfig,
|
|
216
|
-
type CreditPackageConfig,
|
|
217
|
-
} from '@umituz/react-native-subscription';
|
|
218
|
-
|
|
219
|
-
const config: SubscriptionInitConfig = {
|
|
220
|
-
revenueCatApiKey: process.env.REVENUECAT_API_KEY,
|
|
221
|
-
revenueCatEntitlementId: 'premium',
|
|
222
|
-
|
|
223
|
-
// Opsiyonel kredi konfigürasyonu
|
|
224
|
-
creditPackages: [
|
|
225
|
-
{
|
|
226
|
-
id: 'small',
|
|
227
|
-
productId: 'com.app.credits.100',
|
|
228
|
-
amount: 100,
|
|
229
|
-
price: 0.99,
|
|
230
|
-
},
|
|
231
|
-
{
|
|
232
|
-
id: 'medium',
|
|
233
|
-
productId: 'com.app.credits.500',
|
|
234
|
-
amount: 500,
|
|
235
|
-
price: 3.99,
|
|
236
|
-
},
|
|
237
|
-
],
|
|
238
|
-
|
|
239
|
-
// Callback'ler
|
|
240
|
-
onInitialized: () => {
|
|
241
|
-
console.log('Subscription system initialized');
|
|
242
|
-
},
|
|
243
|
-
onError: (error) => {
|
|
244
|
-
console.error('Initialization error:', error);
|
|
245
|
-
},
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
// Başlat
|
|
249
|
-
await initializeSubscription(config);
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
## Firebase Integration
|
|
253
|
-
|
|
254
|
-
### Firestore Repository
|
|
255
|
-
|
|
256
|
-
Firebase Firestore ile repository implementasyonu:
|
|
257
|
-
|
|
258
|
-
```typescript
|
|
259
|
-
import firestore from '@react-native-firebase/firestore';
|
|
260
|
-
import type { ISubscriptionRepository } from '@umituz/react-native-subscription';
|
|
261
|
-
|
|
262
|
-
class FirestoreSubscriptionRepository implements ISubscriptionRepository {
|
|
263
|
-
private collection = firestore().collection('subscriptions');
|
|
264
|
-
|
|
265
|
-
async getSubscriptionStatus(userId: string) {
|
|
266
|
-
const doc = await this.collection.doc(userId).get();
|
|
267
|
-
if (!doc.exists) return null;
|
|
268
|
-
return doc.data();
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
async saveSubscriptionStatus(userId: string, status: any) {
|
|
272
|
-
await this.collection.doc(userId).set(status, { merge: true });
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
async deleteSubscriptionStatus(userId: string) {
|
|
276
|
-
await this.collection.doc(userId).delete();
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
isSubscriptionValid(status: any): boolean {
|
|
280
|
-
if (!status.isActive) return false;
|
|
281
|
-
if (!status.expirationDate) return true;
|
|
282
|
-
return new Date(status.expirationDate) > new Date();
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// Real-time updates
|
|
286
|
-
subscribeToStatus(
|
|
287
|
-
userId: string,
|
|
288
|
-
callback: (status: any) => void
|
|
289
|
-
): () => void {
|
|
290
|
-
const unsubscribe = this.collection
|
|
291
|
-
.doc(userId)
|
|
292
|
-
.onSnapshot((doc) => {
|
|
293
|
-
if (doc.exists) {
|
|
294
|
-
callback(doc.data());
|
|
295
|
-
}
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
return unsubscribe;
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
## Error Handling
|
|
304
|
-
|
|
305
|
-
Infrastructure hatalarını yönetme:
|
|
306
|
-
|
|
307
|
-
```typescript
|
|
308
|
-
import {
|
|
309
|
-
SubscriptionRepositoryError,
|
|
310
|
-
SubscriptionValidationError,
|
|
311
|
-
} from '@umituz/react-native-subscription';
|
|
312
|
-
|
|
313
|
-
class SafeRepository {
|
|
314
|
-
async getSubscriptionStatus(userId: string) {
|
|
315
|
-
try {
|
|
316
|
-
if (!userId || userId.length === 0) {
|
|
317
|
-
throw new SubscriptionValidationError('Invalid user ID');
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
const data = await this.fetchFromDatabase(userId);
|
|
321
|
-
|
|
322
|
-
if (!data) {
|
|
323
|
-
return null;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
return data;
|
|
327
|
-
} catch (error) {
|
|
328
|
-
if (error instanceof SubscriptionValidationError) {
|
|
329
|
-
// Validation hatası
|
|
330
|
-
console.error('Validation failed:', error.message);
|
|
331
|
-
throw error;
|
|
332
|
-
} else {
|
|
333
|
-
// Beklenmedik hata
|
|
334
|
-
throw new SubscriptionRepositoryError(
|
|
335
|
-
'Failed to get subscription status',
|
|
336
|
-
error
|
|
337
|
-
);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
## Caching
|
|
345
|
-
|
|
346
|
-
Performans için caching implementasyonu:
|
|
347
|
-
|
|
348
|
-
```typescript
|
|
349
|
-
class CachedRepository {
|
|
350
|
-
private cache = new Map<string, any>();
|
|
351
|
-
private cacheTimeout = 5 * 60 * 1000; // 5 dakika
|
|
352
|
-
|
|
353
|
-
async getSubscriptionStatus(userId: string) {
|
|
354
|
-
// Cache kontrolü
|
|
355
|
-
const cached = this.cache.get(userId);
|
|
356
|
-
if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
|
|
357
|
-
return cached.data;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// Veritabanından getir
|
|
361
|
-
const data = await this.fetchFromDatabase(userId);
|
|
362
|
-
|
|
363
|
-
// Cache'e kaydet
|
|
364
|
-
this.cache.set(userId, {
|
|
365
|
-
data,
|
|
366
|
-
timestamp: Date.now(),
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
return data;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
invalidateCache(userId: string) {
|
|
373
|
-
this.cache.delete(userId);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
clearCache() {
|
|
377
|
-
this.cache.clear();
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
## Testing
|
|
383
|
-
|
|
384
|
-
Mock repository ile test:
|
|
385
|
-
|
|
386
|
-
```typescript
|
|
387
|
-
class MockSubscriptionRepository {
|
|
388
|
-
private data = new Map<string, any>();
|
|
389
|
-
|
|
390
|
-
async getSubscriptionStatus(userId: string) {
|
|
391
|
-
return this.data.get(userId) || null;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
async saveSubscriptionStatus(userId: string, status: any) {
|
|
395
|
-
this.data.set(userId, status);
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
async deleteSubscriptionStatus(userId: string) {
|
|
399
|
-
this.data.delete(userId);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
isSubscriptionValid(status: any): boolean {
|
|
403
|
-
return status?.isActive ?? false;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// Test helper
|
|
407
|
-
__setTestData(userId: string, status: any) {
|
|
408
|
-
this.data.set(userId, status);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
__clearData() {
|
|
412
|
-
this.data.clear();
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// Testlerde
|
|
417
|
-
const mockRepo = new MockSubscriptionRepository();
|
|
418
|
-
const service = new SubscriptionService({
|
|
419
|
-
repository: mockRepo,
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
mockRepo.__setTestData('user-123', {
|
|
423
|
-
type: 'premium',
|
|
424
|
-
isActive: true,
|
|
425
|
-
isPremium: true,
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
const status = await service.getSubscriptionStatus('user-123');
|
|
429
|
-
expect(status?.type).toBe('premium');
|
|
430
|
-
```
|
|
431
|
-
|
|
432
|
-
## Best Practices
|
|
433
|
-
|
|
434
|
-
1. **Dependency Injection**: Repository'leri constructor'da alın
|
|
435
|
-
2. **Error Handling**: Tüm hataları yakalayın ve uygun şekilde handle edin
|
|
436
|
-
3. **Caching**: Sık kullanılan verileri cache'leyin
|
|
437
|
-
4. **Validation**: Girdileri validate edin
|
|
438
|
-
5. **Logging**: Önemli operasyonları log'layın
|
|
439
|
-
6. **Testing**: Mock implementasyonlarla test edilebilir yapın
|
|
440
|
-
7. **Retry Logic**: Network hataları için retry logic ekleyin
|
|
441
|
-
|
|
442
|
-
## Örnek: Full Implementation
|
|
443
|
-
|
|
444
|
-
```typescript
|
|
445
|
-
import {
|
|
446
|
-
SubscriptionService,
|
|
447
|
-
type ISubscriptionRepository,
|
|
448
|
-
SubscriptionStatus,
|
|
449
|
-
} from '@umituz/react-native-subscription';
|
|
450
|
-
import firestore from '@react-native-firebase/firestore';
|
|
451
|
-
|
|
452
|
-
class FirestoreRepository implements ISubscriptionRepository {
|
|
453
|
-
private db = firestore();
|
|
454
|
-
|
|
455
|
-
async getSubscriptionStatus(userId: string): Promise<SubscriptionStatus | null> {
|
|
456
|
-
try {
|
|
457
|
-
const doc = await this.db
|
|
458
|
-
.collection('subscriptions')
|
|
459
|
-
.doc(userId)
|
|
460
|
-
.get();
|
|
461
|
-
|
|
462
|
-
if (!doc.exists) return null;
|
|
463
|
-
return doc.data() as SubscriptionStatus;
|
|
464
|
-
} catch (error) {
|
|
465
|
-
console.error('Error fetching subscription:', error);
|
|
466
|
-
throw error;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
async saveSubscriptionStatus(
|
|
471
|
-
userId: string,
|
|
472
|
-
status: SubscriptionStatus
|
|
473
|
-
): Promise<void> {
|
|
474
|
-
try {
|
|
475
|
-
await this.db
|
|
476
|
-
.collection('subscriptions')
|
|
477
|
-
.doc(userId)
|
|
478
|
-
.set(status, { merge: true });
|
|
479
|
-
} catch (error) {
|
|
480
|
-
console.error('Error saving subscription:', error);
|
|
481
|
-
throw error;
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
async deleteSubscriptionStatus(userId: string): Promise<void> {
|
|
486
|
-
try {
|
|
487
|
-
await this.db
|
|
488
|
-
.collection('subscriptions')
|
|
489
|
-
.doc(userId)
|
|
490
|
-
.delete();
|
|
491
|
-
} catch (error) {
|
|
492
|
-
console.error('Error deleting subscription:', error);
|
|
493
|
-
throw error;
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
isSubscriptionValid(status: SubscriptionStatus): boolean {
|
|
498
|
-
if (!status.isActive) return false;
|
|
499
|
-
if (!status.expirationDate) return true;
|
|
500
|
-
return new Date(status.expirationDate) > new Date();
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
// Kullanım
|
|
505
|
-
const repository = new FirestoreRepository();
|
|
506
|
-
const service = new SubscriptionService({
|
|
507
|
-
repository,
|
|
508
|
-
onStatusChanged: (userId, status) => {
|
|
509
|
-
console.log(`Status changed for ${userId}`);
|
|
510
|
-
},
|
|
511
|
-
});
|
|
512
|
-
|
|
513
|
-
await service.activateSubscription('user-123', 'premium_monthly', null);
|
|
514
|
-
```
|
|
52
|
+
- [Application Layer](../application/README.md)
|
|
53
|
+
- [Domain Layer](../domain/README.md)
|
|
54
|
+
- [Repositories](./repositories/README.md)
|
|
55
|
+
- [Services](./services/README.md)
|
|
@@ -15,19 +15,6 @@ This directory contains mapper functions that transform data between different l
|
|
|
15
15
|
|
|
16
16
|
Mappers provide clean separation between layers:
|
|
17
17
|
|
|
18
|
-
```
|
|
19
|
-
External API → Mapper → Domain Entity
|
|
20
|
-
Domain Entity → Mapper → DTO/Model
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Usage
|
|
24
|
-
|
|
25
|
-
```typescript
|
|
26
|
-
import { mapToSubscriptionStatus } from './mappers/subscriptionMapper';
|
|
27
|
-
|
|
28
|
-
const entity = mapToSubscriptionStatus(apiResponse);
|
|
29
|
-
```
|
|
30
|
-
|
|
31
18
|
## Related
|
|
32
19
|
|
|
33
20
|
- [Models](../models/README.md)
|