@umituz/react-native-subscription 2.14.96 → 2.14.98
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/LICENSE +21 -0
- package/README.md +462 -0
- package/package.json +3 -3
- package/src/application/README.md +229 -0
- package/src/application/ports/README.md +103 -0
- package/src/domain/README.md +402 -0
- package/src/domain/constants/README.md +80 -0
- package/src/domain/entities/README.md +176 -0
- package/src/domain/errors/README.md +307 -0
- package/src/domain/value-objects/README.md +186 -0
- package/src/domains/config/README.md +390 -0
- package/src/domains/paywall/README.md +371 -0
- package/src/domains/paywall/components/PaywallHeader.tsx +8 -11
- package/src/domains/paywall/components/README.md +185 -0
- package/src/domains/paywall/entities/README.md +199 -0
- package/src/domains/paywall/hooks/README.md +129 -0
- package/src/domains/wallet/README.md +292 -0
- package/src/domains/wallet/domain/README.md +108 -0
- package/src/domains/wallet/domain/entities/README.md +122 -0
- package/src/domains/wallet/domain/errors/README.md +157 -0
- package/src/domains/wallet/infrastructure/README.md +96 -0
- package/src/domains/wallet/presentation/components/BalanceCard.tsx +6 -12
- package/src/domains/wallet/presentation/components/README.md +231 -0
- package/src/domains/wallet/presentation/hooks/README.md +255 -0
- package/src/infrastructure/README.md +514 -0
- package/src/infrastructure/mappers/README.md +34 -0
- package/src/infrastructure/models/README.md +26 -0
- package/src/infrastructure/repositories/README.md +385 -0
- package/src/infrastructure/services/README.md +374 -0
- package/src/presentation/README.md +410 -0
- package/src/presentation/components/README.md +183 -0
- package/src/presentation/components/details/CreditRow.md +337 -0
- package/src/presentation/components/details/DetailRow.md +283 -0
- package/src/presentation/components/details/PremiumDetailsCard.md +266 -0
- package/src/presentation/components/details/PremiumStatusBadge.md +266 -0
- package/src/presentation/components/details/README.md +449 -0
- package/src/presentation/components/feedback/PaywallFeedbackModal.md +314 -0
- package/src/presentation/components/feedback/README.md +447 -0
- package/src/presentation/components/paywall/PaywallModal.md +444 -0
- package/src/presentation/components/paywall/README.md +190 -0
- package/src/presentation/components/sections/README.md +468 -0
- package/src/presentation/components/sections/SubscriptionSection.md +246 -0
- package/src/presentation/hooks/README.md +743 -0
- package/src/presentation/hooks/useAuthAwarePurchase.md +359 -0
- package/src/presentation/hooks/useAuthGate.md +403 -0
- package/src/presentation/hooks/useAuthSubscriptionSync.md +398 -0
- package/src/presentation/hooks/useCreditChecker.md +407 -0
- package/src/presentation/hooks/useCredits.md +342 -0
- package/src/presentation/hooks/useCreditsGate.md +346 -0
- package/src/presentation/hooks/useDeductCredit.md +160 -0
- package/src/presentation/hooks/useDevTestCallbacks.md +422 -0
- package/src/presentation/hooks/useFeatureGate.md +157 -0
- package/src/presentation/hooks/useInitializeCredits.md +458 -0
- package/src/presentation/hooks/usePaywall.md +334 -0
- package/src/presentation/hooks/usePaywallOperations.md +486 -0
- package/src/presentation/hooks/usePaywallVisibility.md +344 -0
- package/src/presentation/hooks/usePremium.md +230 -0
- package/src/presentation/hooks/usePremiumGate.md +423 -0
- package/src/presentation/hooks/usePremiumWithCredits.md +429 -0
- package/src/presentation/hooks/useSubscription.md +450 -0
- package/src/presentation/hooks/useSubscriptionDetails.md +438 -0
- package/src/presentation/hooks/useSubscriptionGate.md +168 -0
- package/src/presentation/hooks/useSubscriptionSettingsConfig.md +374 -0
- package/src/presentation/hooks/useSubscriptionStatus.md +424 -0
- package/src/presentation/hooks/useUserTier.md +356 -0
- package/src/presentation/hooks/useUserTierWithRepository.md +452 -0
- package/src/presentation/screens/README.md +194 -0
- package/src/presentation/types/README.md +38 -0
- package/src/presentation/utils/README.md +52 -0
- package/src/revenuecat/README.md +523 -0
- package/src/revenuecat/domain/README.md +147 -0
- package/src/revenuecat/domain/errors/README.md +197 -0
- package/src/revenuecat/infrastructure/config/README.md +40 -0
- package/src/revenuecat/infrastructure/managers/README.md +49 -0
- package/src/revenuecat/presentation/hooks/README.md +56 -0
- package/src/revenuecat/presentation/hooks/usePurchasePackage.ts +1 -1
- package/src/utils/README.md +529 -0
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
# Infrastructure Layer
|
|
2
|
+
|
|
3
|
+
Abonelik sisteminin dış dünya ile iletişimini sağlayan implementations ve repositories içeren katman.
|
|
4
|
+
|
|
5
|
+
## Sorumluluklar
|
|
6
|
+
|
|
7
|
+
- **Repositories**: Veri erişim implementasyonları
|
|
8
|
+
- **Services**: Dış servis entegrasyonları (Firebase, RevenueCat, vb.)
|
|
9
|
+
- **Managers**: Karmaşık operasyon yöneticileri
|
|
10
|
+
- **Handlers**: Özel durum ve event yöneticileri
|
|
11
|
+
|
|
12
|
+
## Yapı
|
|
13
|
+
|
|
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
|
+
```
|
|
23
|
+
|
|
24
|
+
## Repositories
|
|
25
|
+
|
|
26
|
+
### CreditsRepositoryProvider
|
|
27
|
+
|
|
28
|
+
Kredi repository'sini konfigüre etmek ve yönetmek için:
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import {
|
|
32
|
+
configureCreditsRepository,
|
|
33
|
+
getCreditsRepository,
|
|
34
|
+
getCreditsConfig,
|
|
35
|
+
resetCreditsRepository,
|
|
36
|
+
isCreditsRepositoryConfigured,
|
|
37
|
+
type CreditsConfig,
|
|
38
|
+
} from '@umituz/react-native-subscription';
|
|
39
|
+
|
|
40
|
+
// Repository konfigürasyonu
|
|
41
|
+
const config: CreditsConfig = {
|
|
42
|
+
initialCredits: 100,
|
|
43
|
+
|
|
44
|
+
creditPackages: [
|
|
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
|
+
],
|
|
60
|
+
|
|
61
|
+
creditCosts: {
|
|
62
|
+
ai_generation: 1,
|
|
63
|
+
ai_analysis: 2,
|
|
64
|
+
premium_feature: 5,
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
expiration: {
|
|
68
|
+
enabled: true,
|
|
69
|
+
daysUntilExpiration: 365,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Repository'i konfigüre et
|
|
74
|
+
configureCreditsRepository({
|
|
75
|
+
firebase: firebaseInstance,
|
|
76
|
+
storage: storageInstance,
|
|
77
|
+
config,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Repository'e eriş
|
|
81
|
+
const repository = getCreditsRepository();
|
|
82
|
+
const creditsConfig = getCreditsConfig();
|
|
83
|
+
|
|
84
|
+
// Konfigürasyon kontrolü
|
|
85
|
+
if (isCreditsRepositoryConfigured()) {
|
|
86
|
+
// Repository kullanıma hazır
|
|
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
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
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
|
+
```
|
|
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
|
+
## Related
|
|
32
|
+
|
|
33
|
+
- [Models](../models/README.md)
|
|
34
|
+
- [Domain](../../domain/README.md)
|
|
@@ -0,0 +1,26 @@
|
|
|
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)
|