@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,52 @@
|
|
|
1
|
+
# Presentation Utils
|
|
2
|
+
|
|
3
|
+
Utility functions and helpers for the presentation layer.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This directory contains utility functions used by presentation components and hooks.
|
|
8
|
+
|
|
9
|
+
## Contents
|
|
10
|
+
|
|
11
|
+
- **subscriptionDateUtils.ts** - Date formatting and manipulation for subscription display
|
|
12
|
+
- **paywallUtils.ts** - Paywall-related utility functions
|
|
13
|
+
|
|
14
|
+
## Date Utilities
|
|
15
|
+
|
|
16
|
+
### formatDate
|
|
17
|
+
|
|
18
|
+
Format ISO date string to localized date string.
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
formatDate('2024-01-15T10:30:00Z'); // "January 15, 2024"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### calculateDaysRemaining
|
|
25
|
+
|
|
26
|
+
Calculate days between now and expiration date.
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
calculateDaysRemaining('2024-02-15T10:30:00Z'); // 31
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### convertPurchasedAt
|
|
33
|
+
|
|
34
|
+
Convert purchasedAt date to ISO string.
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
convertPurchasedAt(new Date()); // "2024-01-15T10:30:00Z"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { formatDate, calculateDaysRemaining } from '../utils/subscriptionDateUtils';
|
|
44
|
+
|
|
45
|
+
const expirationDisplay = formatDate(subscription.expirationDate);
|
|
46
|
+
const daysLeft = calculateDaysRemaining(subscription.expirationDate);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Related
|
|
50
|
+
|
|
51
|
+
- [Hooks](../hooks/README.md)
|
|
52
|
+
- [Types](../types/README.md)
|
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
# RevenueCat Integration
|
|
2
|
+
|
|
3
|
+
RevenueCat ile abonelik yönetimi için kapsamlı entegrasyon ve API wrapper.
|
|
4
|
+
|
|
5
|
+
## Özellikler
|
|
6
|
+
|
|
7
|
+
- **Otomatik Başlatma**: RevenueCat SDK otomatik başlatma ve konfigürasyon
|
|
8
|
+
- **User ID Yönetimi**: Auth sistemleriyle entegre user ID yönetimi
|
|
9
|
+
- **Purchase Flow**: Satın alma işlemleri için yönetilen flow
|
|
10
|
+
- **Restore İşlemi**: Satın alma geri yükleme desteği
|
|
11
|
+
- **Customer Info**: Kullanıcı abonelik bilgilerini takip
|
|
12
|
+
- **Error Handling**: RevenueCat hatalarını yönetme
|
|
13
|
+
|
|
14
|
+
## Kurulum
|
|
15
|
+
|
|
16
|
+
### 1. RevenueCat SDK Kurulumu
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install react-native-purchases
|
|
20
|
+
# veya
|
|
21
|
+
yarn add react-native-purchases
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 2. Başlatma
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import {
|
|
28
|
+
initializeSubscription,
|
|
29
|
+
SubscriptionInitConfig,
|
|
30
|
+
} from '@umituz/react-native-subscription';
|
|
31
|
+
|
|
32
|
+
const config: SubscriptionInitConfig = {
|
|
33
|
+
revenueCatApiKey: 'your_api_key',
|
|
34
|
+
revenueCatEntitlementId: 'premium',
|
|
35
|
+
|
|
36
|
+
// Opsiyonel
|
|
37
|
+
userDefaultsSuiteName: 'app.revenuecat',
|
|
38
|
+
diagnosticsEnabled: __DEV__,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
await initializeSubscription(config);
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 3. Provider ile Kullanım
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { SubscriptionProvider } from '@umituz/react-native-subscription';
|
|
48
|
+
|
|
49
|
+
function App() {
|
|
50
|
+
return (
|
|
51
|
+
<SubscriptionProvider config={config}>
|
|
52
|
+
<YourApp />
|
|
53
|
+
</SubscriptionProvider>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Hooks
|
|
59
|
+
|
|
60
|
+
### useRevenueCat
|
|
61
|
+
|
|
62
|
+
RevenueCat'e erişim ve temel işlemler için:
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { useRevenueCat } from '@umituz/react-native-subscription';
|
|
66
|
+
|
|
67
|
+
function RevenueCatExample() {
|
|
68
|
+
const {
|
|
69
|
+
isReady,
|
|
70
|
+
isInitialized,
|
|
71
|
+
error,
|
|
72
|
+
purchaserInfo,
|
|
73
|
+
offerings,
|
|
74
|
+
} = useRevenueCat();
|
|
75
|
+
|
|
76
|
+
if (!isInitialized) {
|
|
77
|
+
return <ActivityIndicator />;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<View>
|
|
82
|
+
<Text>Status: {isReady ? 'Ready' : 'Loading'}</Text>
|
|
83
|
+
</View>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### useCustomerInfo
|
|
89
|
+
|
|
90
|
+
Kullanıcı abonelik bilgileri için:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import { useCustomerInfo } from '@umituz/react-native-subscription';
|
|
94
|
+
|
|
95
|
+
function SubscriptionStatus() {
|
|
96
|
+
const {
|
|
97
|
+
customerInfo,
|
|
98
|
+
isLoading,
|
|
99
|
+
error,
|
|
100
|
+
refetch,
|
|
101
|
+
} = useCustomerInfo();
|
|
102
|
+
|
|
103
|
+
if (isLoading) return <ActivityIndicator />;
|
|
104
|
+
|
|
105
|
+
const entitlement = customerInfo?.entitlements.active['premium'];
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<View>
|
|
109
|
+
<Text>
|
|
110
|
+
Status: {entitlement ? 'Premium' : 'Free'}
|
|
111
|
+
</Text>
|
|
112
|
+
{entitlement && (
|
|
113
|
+
<Text>
|
|
114
|
+
Expires: {new Date(entitlement.expirationDate).toLocaleDateString()}
|
|
115
|
+
</Text>
|
|
116
|
+
)}
|
|
117
|
+
<Button onPress={refetch} title="Refresh" />
|
|
118
|
+
</View>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### useInitializeSubscription
|
|
124
|
+
|
|
125
|
+
Başlatma durumu kontrolü için:
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
import { useInitializeSubscription } from '@umituz/react-native-subscription';
|
|
129
|
+
|
|
130
|
+
function InitCheck() {
|
|
131
|
+
const {
|
|
132
|
+
isInitialized,
|
|
133
|
+
isInitializing,
|
|
134
|
+
error,
|
|
135
|
+
initialize,
|
|
136
|
+
} = useInitializeSubscription();
|
|
137
|
+
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
if (!isInitialized && !isInitializing) {
|
|
140
|
+
initialize();
|
|
141
|
+
}
|
|
142
|
+
}, []);
|
|
143
|
+
|
|
144
|
+
if (isInitializing) {
|
|
145
|
+
return <Text>Initializing...</Text>;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (error) {
|
|
149
|
+
return <Text>Error: {error.message}</Text>;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return <Text>Ready!</Text>;
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### useSubscriptionPackages
|
|
157
|
+
|
|
158
|
+
Mevcut abonelik paketleri için:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
import { useSubscriptionPackages } from '@umituz/react-native-subscription';
|
|
162
|
+
|
|
163
|
+
function PackageList() {
|
|
164
|
+
const {
|
|
165
|
+
packages,
|
|
166
|
+
offerings,
|
|
167
|
+
isLoading,
|
|
168
|
+
error,
|
|
169
|
+
} = useSubscriptionPackages({
|
|
170
|
+
offeringId: 'default',
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
if (isLoading) return <ActivityIndicator />;
|
|
174
|
+
|
|
175
|
+
return (
|
|
176
|
+
<ScrollView>
|
|
177
|
+
{packages.map((pkg) => (
|
|
178
|
+
<PackageCard
|
|
179
|
+
key={pkg.identifier}
|
|
180
|
+
package={pkg}
|
|
181
|
+
onPress={() => handlePurchase(pkg)}
|
|
182
|
+
/>
|
|
183
|
+
))}
|
|
184
|
+
</ScrollView>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### usePaywallFlow
|
|
190
|
+
|
|
191
|
+
Tam paywall flow'u için:
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
import { usePaywallFlow } from '@umituz/react-native-subscription';
|
|
195
|
+
|
|
196
|
+
function Paywall() {
|
|
197
|
+
const {
|
|
198
|
+
packages,
|
|
199
|
+
selectedPackage,
|
|
200
|
+
isLoading,
|
|
201
|
+
error,
|
|
202
|
+
selectPackage,
|
|
203
|
+
purchaseSelectedPackage,
|
|
204
|
+
restorePurchases,
|
|
205
|
+
} = usePaywallFlow();
|
|
206
|
+
|
|
207
|
+
const handlePurchase = async () => {
|
|
208
|
+
try {
|
|
209
|
+
const result = await purchaseSelectedPackage();
|
|
210
|
+
|
|
211
|
+
if (result.success) {
|
|
212
|
+
Alert.alert('Success', 'You are now a premium user!');
|
|
213
|
+
} else {
|
|
214
|
+
Alert.alert('Error', result.error?.message);
|
|
215
|
+
}
|
|
216
|
+
} catch (err) {
|
|
217
|
+
Alert.alert('Error', 'Purchase failed');
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
return (
|
|
222
|
+
<View>
|
|
223
|
+
{packages.map((pkg) => (
|
|
224
|
+
<TouchableOpacity
|
|
225
|
+
key={pkg.identifier}
|
|
226
|
+
onPress={() => selectPackage(pkg)}
|
|
227
|
+
style={selectedPackage?.identifier === pkg.identifier &&
|
|
228
|
+
styles.selected
|
|
229
|
+
}
|
|
230
|
+
>
|
|
231
|
+
<Text>{pkg.product.title}</Text>
|
|
232
|
+
<Text>{pkg.product.priceString}</Text>
|
|
233
|
+
</TouchableOpacity>
|
|
234
|
+
))}
|
|
235
|
+
|
|
236
|
+
<Button
|
|
237
|
+
onPress={handlePurchase}
|
|
238
|
+
disabled={!selectedPackage || isLoading}
|
|
239
|
+
title="Subscribe"
|
|
240
|
+
/>
|
|
241
|
+
|
|
242
|
+
<Button
|
|
243
|
+
onPress={restorePurchases}
|
|
244
|
+
disabled={isLoading}
|
|
245
|
+
title="Restore"
|
|
246
|
+
/>
|
|
247
|
+
</View>
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### useRestorePurchase
|
|
253
|
+
|
|
254
|
+
Satın alma geri yükleme için:
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
import { useRestorePurchase } from '@umituz/react-native-subscription';
|
|
258
|
+
|
|
259
|
+
function RestoreButton() {
|
|
260
|
+
const { restorePurchase, isLoading, error, isRestored } = useRestorePurchase();
|
|
261
|
+
|
|
262
|
+
const handleRestore = async () => {
|
|
263
|
+
const result = await restorePurchase();
|
|
264
|
+
|
|
265
|
+
if (result.success) {
|
|
266
|
+
Alert.alert('Success', 'Purchase restored!');
|
|
267
|
+
} else {
|
|
268
|
+
Alert.alert('Error', result.error?.message || 'Restore failed');
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
return (
|
|
273
|
+
<Button
|
|
274
|
+
onPress={handleRestore}
|
|
275
|
+
disabled={isLoading}
|
|
276
|
+
title={isLoading ? 'Restoring...' : 'Restore Purchase'}
|
|
277
|
+
/>
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Hata Yönetimi
|
|
283
|
+
|
|
284
|
+
### RevenueCatError
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
import {
|
|
288
|
+
RevenueCatError,
|
|
289
|
+
ErrorCode,
|
|
290
|
+
handleRevenueCatError,
|
|
291
|
+
} from '@umituz/react-native-subscription';
|
|
292
|
+
|
|
293
|
+
try {
|
|
294
|
+
await purchasePackage(packageToPurchase);
|
|
295
|
+
} catch (error) {
|
|
296
|
+
if (error instanceof RevenueCatError) {
|
|
297
|
+
switch (error.code) {
|
|
298
|
+
case ErrorCode.PurchaseCancelledError:
|
|
299
|
+
console.log('User cancelled');
|
|
300
|
+
break;
|
|
301
|
+
case ErrorCode.PurchaseInvalidError:
|
|
302
|
+
console.log('Invalid purchase');
|
|
303
|
+
break;
|
|
304
|
+
case ErrorCode.NetworkError:
|
|
305
|
+
console.log('Network error');
|
|
306
|
+
break;
|
|
307
|
+
default:
|
|
308
|
+
handleRevenueCatError(error);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## User ID Yönetimi
|
|
315
|
+
|
|
316
|
+
### Auth ile Entegrasyon
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
import { useAuthSubscriptionSync } from '@umituz/react-native-subscription';
|
|
320
|
+
|
|
321
|
+
function AuthSync() {
|
|
322
|
+
const { user } = useAuth();
|
|
323
|
+
const { syncUserId, clearUserId } = useAuthSubscriptionSync();
|
|
324
|
+
|
|
325
|
+
useEffect(() => {
|
|
326
|
+
if (user?.uid) {
|
|
327
|
+
// Kullanıcı giriş yaptığında
|
|
328
|
+
syncUserId(user.uid);
|
|
329
|
+
} else {
|
|
330
|
+
// Kullanıcı çıkış yaptığında
|
|
331
|
+
clearUserId();
|
|
332
|
+
}
|
|
333
|
+
}, [user?.uid]);
|
|
334
|
+
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Manuel User ID
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
import { configureUserId } from '@umituz/react-native-subscription';
|
|
343
|
+
|
|
344
|
+
// Kullanıcı giriş yaptığında
|
|
345
|
+
await configureUserId('user-123');
|
|
346
|
+
|
|
347
|
+
// Kullanıcı çıkış yaptığında
|
|
348
|
+
await configureUserId(null); // veya await resetUserId()
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## API Key Çözümleme
|
|
352
|
+
|
|
353
|
+
### Environment Bazlı API Keys
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
import {
|
|
357
|
+
ApiKeyResolver,
|
|
358
|
+
resolveApiKey,
|
|
359
|
+
} from '@umituz/react-native-subscription';
|
|
360
|
+
|
|
361
|
+
const apiKeyResolver = new ApiKeyResolver({
|
|
362
|
+
development: 'dev_api_key',
|
|
363
|
+
production: 'prod_api_key',
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
const apiKey = apiKeyResolver.resolve(__DEV__ ? 'development' : 'production');
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
## SubscriptionManager
|
|
370
|
+
|
|
371
|
+
Gelişmiş işlemler için SubscriptionManager kullanımı:
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
import { SubscriptionManager } from '@umituz/react-native-subscription';
|
|
375
|
+
|
|
376
|
+
const manager = new SubscriptionManager(config);
|
|
377
|
+
|
|
378
|
+
// Başlatma
|
|
379
|
+
await manager.initialize(userId);
|
|
380
|
+
|
|
381
|
+
// Satın alma
|
|
382
|
+
const result = await manager.purchasePackage(packageToPurchase);
|
|
383
|
+
|
|
384
|
+
// Geri yükleme
|
|
385
|
+
const restoreResult = await manager.restorePurchases();
|
|
386
|
+
|
|
387
|
+
// Customer info
|
|
388
|
+
const info = await manager.getCustomerInfo();
|
|
389
|
+
|
|
390
|
+
// Offerings
|
|
391
|
+
const offerings = await manager.getOfferings();
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
## Best Practices
|
|
395
|
+
|
|
396
|
+
1. **User ID Sync**: Auth sistemiyle user ID'leri her zaman senkronize edin
|
|
397
|
+
2. **Error Handling**: TümRevenueCat işlemlerinde hata yönetimi kullanın
|
|
398
|
+
3. **Loading States**: Kullanıcıya uygun loading feedback'i verin
|
|
399
|
+
4. **Restore**: Her zaman "Restore Purchase" seçeneği sunun
|
|
400
|
+
5. **Test Mode**: Geliştirme sırasında test mode kullanın
|
|
401
|
+
6. **Debug Mode**: Production'da debug modu kapalı tutun
|
|
402
|
+
|
|
403
|
+
## Örnek Uygulama
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
import React, { useEffect } from 'react';
|
|
407
|
+
import { View, Text, Button, ActivityIndicator } from 'react-native';
|
|
408
|
+
import {
|
|
409
|
+
usePaywallFlow,
|
|
410
|
+
useCustomerInfo,
|
|
411
|
+
useRestorePurchase,
|
|
412
|
+
} from '@umituz/react-native-subscription';
|
|
413
|
+
|
|
414
|
+
export default function SubscriptionScreen() {
|
|
415
|
+
const {
|
|
416
|
+
packages,
|
|
417
|
+
selectedPackage,
|
|
418
|
+
isLoading,
|
|
419
|
+
selectPackage,
|
|
420
|
+
purchaseSelectedPackage,
|
|
421
|
+
} = usePaywallFlow();
|
|
422
|
+
|
|
423
|
+
const { customerInfo, isLoading: infoLoading } = useCustomerInfo();
|
|
424
|
+
const { restorePurchase } = useRestorePurchase();
|
|
425
|
+
|
|
426
|
+
const entitlement = customerInfo?.entitlements.active['premium'];
|
|
427
|
+
|
|
428
|
+
const handleSubscribe = async () => {
|
|
429
|
+
if (!selectedPackage) return;
|
|
430
|
+
|
|
431
|
+
try {
|
|
432
|
+
const result = await purchaseSelectedPackage();
|
|
433
|
+
|
|
434
|
+
if (result.success) {
|
|
435
|
+
Alert.alert('Success', 'Welcome to Premium!');
|
|
436
|
+
} else {
|
|
437
|
+
Alert.alert('Error', result.error?.message || 'Purchase failed');
|
|
438
|
+
}
|
|
439
|
+
} catch (error) {
|
|
440
|
+
Alert.alert('Error', 'Something went wrong');
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
if (isLoading || infoLoading) {
|
|
445
|
+
return <ActivityIndicator />;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return (
|
|
449
|
+
<View>
|
|
450
|
+
{entitlement ? (
|
|
451
|
+
<>
|
|
452
|
+
<Text>You are Premium!</Text>
|
|
453
|
+
<Text>
|
|
454
|
+
Expires: {new Date(entitlement.expirationDate).toLocaleDateString()}
|
|
455
|
+
</Text>
|
|
456
|
+
</>
|
|
457
|
+
) : (
|
|
458
|
+
<>
|
|
459
|
+
<Text>Choose your plan:</Text>
|
|
460
|
+
{packages.map((pkg) => (
|
|
461
|
+
<TouchableOpacity
|
|
462
|
+
key={pkg.identifier}
|
|
463
|
+
onPress={() => selectPackage(pkg)}
|
|
464
|
+
style={
|
|
465
|
+
selectedPackage?.identifier === pkg.identifier &&
|
|
466
|
+
styles.selected
|
|
467
|
+
}
|
|
468
|
+
>
|
|
469
|
+
<Text>{pkg.product.title}</Text>
|
|
470
|
+
<Text>{pkg.product.priceString}</Text>
|
|
471
|
+
<Text>{pkg.product.description}</Text>
|
|
472
|
+
</TouchableOpacity>
|
|
473
|
+
))}
|
|
474
|
+
|
|
475
|
+
<Button
|
|
476
|
+
onPress={handleSubscribe}
|
|
477
|
+
disabled={!selectedPackage || isLoading}
|
|
478
|
+
title="Subscribe"
|
|
479
|
+
/>
|
|
480
|
+
|
|
481
|
+
<Button onPress={restorePurchase} title="Restore" />
|
|
482
|
+
</>
|
|
483
|
+
)}
|
|
484
|
+
</View>
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
## Tip Tanımlamaları
|
|
490
|
+
|
|
491
|
+
```typescript
|
|
492
|
+
interface RevenueCatConfig {
|
|
493
|
+
apiKey: string;
|
|
494
|
+
entitlements: {
|
|
495
|
+
premium: string;
|
|
496
|
+
};
|
|
497
|
+
userDefaultsSuiteName?: string;
|
|
498
|
+
diagnosticsEnabled?: boolean;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
interface PurchaseResult {
|
|
502
|
+
success: boolean;
|
|
503
|
+
error?: Error;
|
|
504
|
+
customerInfo?: CustomerInfo;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
interface CustomerInfo {
|
|
508
|
+
entitlements: {
|
|
509
|
+
active: Record<string, EntitlementInfo>;
|
|
510
|
+
all: Record<string, EntitlementInfo>;
|
|
511
|
+
};
|
|
512
|
+
activeSubscriptions: string[];
|
|
513
|
+
allPurchasedProductIdentifiers: string[];
|
|
514
|
+
latestExpirationDate: string;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
interface Package {
|
|
518
|
+
identifier: string;
|
|
519
|
+
packageType: PACKAGE_TYPE;
|
|
520
|
+
product: Product;
|
|
521
|
+
offeringIdentifier: string;
|
|
522
|
+
}
|
|
523
|
+
```
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# RevenueCat Domain
|
|
2
|
+
|
|
3
|
+
Domain entities and types for RevenueCat integration.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This directory contains domain-specific entities representing RevenueCat concepts like entitlements, offerings, and packages.
|
|
8
|
+
|
|
9
|
+
## Entities
|
|
10
|
+
|
|
11
|
+
### EntitlementInfo
|
|
12
|
+
Represents a RevenueCat entitlement.
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
interface EntitlementInfo {
|
|
16
|
+
identifier: string;
|
|
17
|
+
isActive: boolean;
|
|
18
|
+
willRenew: boolean;
|
|
19
|
+
expirationDate?: Date;
|
|
20
|
+
productIdentifier?: string;
|
|
21
|
+
period?: PackagePeriod;
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Usage:**
|
|
26
|
+
```typescript
|
|
27
|
+
const entitlement: EntitlementInfo = {
|
|
28
|
+
identifier: 'premium',
|
|
29
|
+
isActive: true,
|
|
30
|
+
willRenew: true,
|
|
31
|
+
expirationDate: new Date('2024-12-31'),
|
|
32
|
+
productIdentifier: 'com.app.premium.annual',
|
|
33
|
+
period: 'annual',
|
|
34
|
+
};
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### OfferingInfo
|
|
38
|
+
Represents a RevenueCat offering containing packages.
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
interface OfferingInfo {
|
|
42
|
+
identifier: string;
|
|
43
|
+
packages: PackageInfo[];
|
|
44
|
+
lifetime?: PackageInfo;
|
|
45
|
+
monthly?: PackageInfo;
|
|
46
|
+
annual?: PackageInfo;
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### PackageInfo
|
|
51
|
+
Represents a purchasable package.
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
interface PackageInfo {
|
|
55
|
+
identifier: string;
|
|
56
|
+
packageType: PackageType;
|
|
57
|
+
product: ProductInfo;
|
|
58
|
+
offeringIdentifier: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface ProductInfo {
|
|
62
|
+
identifier: string;
|
|
63
|
+
title: string;
|
|
64
|
+
description: string;
|
|
65
|
+
price: number;
|
|
66
|
+
pricePerMonth?: number;
|
|
67
|
+
currencyCode: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
type PackageType = 'WEEKLY' | 'MONTHLY' | 'THREE_MONTHLY' | 'SIX_MONTHLY' | 'ANNUAL' | 'LIFETIME';
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Constants
|
|
74
|
+
|
|
75
|
+
### Entitlement Identifiers
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
export const ENTITLEMENT_IDS = {
|
|
79
|
+
PREMIUM: 'premium',
|
|
80
|
+
PRO: 'pro',
|
|
81
|
+
BASIC: 'basic',
|
|
82
|
+
} as const;
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Package Periods
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
export const PACKAGE_PERIODS = {
|
|
89
|
+
WEEKLY: 'weekly',
|
|
90
|
+
MONTHLY: 'monthly',
|
|
91
|
+
ANNUAL: 'annual',
|
|
92
|
+
LIFETIME: 'lifetime',
|
|
93
|
+
} as const;
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Helper Functions
|
|
97
|
+
|
|
98
|
+
### Get Package Period
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
function getPackagePeriod(packageType: PackageType): PackagePeriod {
|
|
102
|
+
const periodMap: Record<PackageType, PackagePeriod> = {
|
|
103
|
+
WEEKLY: 'monthly',
|
|
104
|
+
MONTHLY: 'monthly',
|
|
105
|
+
THREE_MONTHLY: 'monthly',
|
|
106
|
+
SIX_MONTHLY: 'monthly',
|
|
107
|
+
ANNUAL: 'annual',
|
|
108
|
+
LIFETIME: 'lifetime',
|
|
109
|
+
};
|
|
110
|
+
return periodMap[packageType];
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Calculate Price Per Month
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
function calculatePricePerMonth(packageInfo: PackageInfo): number | null {
|
|
118
|
+
if (!packageInfo.product.pricePerMonth) {
|
|
119
|
+
return packageInfo.product.price;
|
|
120
|
+
}
|
|
121
|
+
return packageInfo.product.pricePerMonth;
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Format Price
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
function formatPrice(price: number, currencyCode: string): string {
|
|
129
|
+
return new Intl.NumberFormat('en-US', {
|
|
130
|
+
style: 'currency',
|
|
131
|
+
currency: currencyCode,
|
|
132
|
+
}).format(price);
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Best Practices
|
|
137
|
+
|
|
138
|
+
1. **Type Mapping**: Map RevenueCat types to domain types
|
|
139
|
+
2. **Null Safety**: Handle optional properties safely
|
|
140
|
+
3. **Validation**: Validate RevenueCat data
|
|
141
|
+
4. **Formatting**: Format prices and dates consistently
|
|
142
|
+
5. **Constants**: Use constants for identifiers
|
|
143
|
+
|
|
144
|
+
## Related
|
|
145
|
+
|
|
146
|
+
- [RevenueCat README](../README.md)
|
|
147
|
+
- [Subscription Manager](../infrastructure/managers/README.md)
|