@umituz/react-native-subscription 2.14.97 → 2.14.99
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 +461 -0
- package/package.json +1 -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/README.md +240 -0
- package/src/domains/config/README.md +390 -0
- package/src/domains/config/domain/README.md +390 -0
- package/src/domains/config/domain/entities/README.md +350 -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 +176 -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/application/README.md +158 -0
- package/src/revenuecat/application/ports/README.md +169 -0
- package/src/revenuecat/domain/README.md +147 -0
- package/src/revenuecat/domain/constants/README.md +183 -0
- package/src/revenuecat/domain/entities/README.md +382 -0
- package/src/revenuecat/domain/errors/README.md +197 -0
- package/src/revenuecat/domain/types/README.md +373 -0
- package/src/revenuecat/domain/value-objects/README.md +441 -0
- package/src/revenuecat/infrastructure/README.md +50 -0
- package/src/revenuecat/infrastructure/config/README.md +40 -0
- package/src/revenuecat/infrastructure/handlers/README.md +218 -0
- package/src/revenuecat/infrastructure/managers/README.md +49 -0
- package/src/revenuecat/infrastructure/services/README.md +325 -0
- package/src/revenuecat/infrastructure/utils/README.md +382 -0
- package/src/revenuecat/presentation/README.md +184 -0
- package/src/revenuecat/presentation/hooks/README.md +56 -0
- package/src/utils/README.md +529 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# Wallet Domain
|
|
2
|
+
|
|
3
|
+
Kredi bakiyesi yönetimi, işlem geçmişi takibi ve ürün metadata yönetimi için kapsamlı çözüm.
|
|
4
|
+
|
|
5
|
+
## Özellikler
|
|
6
|
+
|
|
7
|
+
- **Kredi Bakiyesi Yönetimi**: Kullanıcıların kredi bakiyelerini takip edin ve yönetin
|
|
8
|
+
- **İşlem Geçmişi**: Tüm kredi işlemlerini geçmişte takip edin
|
|
9
|
+
- **Ürün Metadata**: Satın alınan ürünler için metadata yönetimi
|
|
10
|
+
- **Tür Desteği**: Farklı kredi türlerini (AI, premium, vb.) destekleyin
|
|
11
|
+
|
|
12
|
+
## Kurulum
|
|
13
|
+
|
|
14
|
+
### 1. Service Konfigürasyonu
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import { configureProductMetadataService } from '@umituz/react-native-subscription';
|
|
18
|
+
|
|
19
|
+
// Service'i konfigüre edin
|
|
20
|
+
configureProductMetadataService({
|
|
21
|
+
firebase: firebaseInstance,
|
|
22
|
+
storage: storageInstance,
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### 2. Repository Oluşturma
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { createTransactionRepository } from '@umituz/react-native-subscription';
|
|
30
|
+
|
|
31
|
+
const transactionRepository = createTransactionRepository({
|
|
32
|
+
firebase: firebaseInstance,
|
|
33
|
+
userId: 'user-123',
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Kullanım
|
|
38
|
+
|
|
39
|
+
### useWallet Hook
|
|
40
|
+
|
|
41
|
+
Kredi bakiyesi ve temel cüzdan işlemleri için:
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { useWallet } from '@umituz/react-native-subscription';
|
|
45
|
+
|
|
46
|
+
function MyComponent() {
|
|
47
|
+
const { balance, loading, error, refresh } = useWallet({
|
|
48
|
+
userId: 'user-123',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (loading) return <ActivityIndicator />;
|
|
52
|
+
if (error) return <Text>Error: {error.message}</Text>;
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<View>
|
|
56
|
+
<Text>Balance: {balance.credits}</Text>
|
|
57
|
+
<Button onPress={refresh} title="Refresh" />
|
|
58
|
+
</View>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### useTransactionHistory Hook
|
|
64
|
+
|
|
65
|
+
İşlem geçmişini görüntülemek için:
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { useTransactionHistory } from '@umituz/react-native-subscription';
|
|
69
|
+
|
|
70
|
+
function TransactionHistory() {
|
|
71
|
+
const { transactions, loading, hasMore, loadMore } = useTransactionHistory({
|
|
72
|
+
userId: 'user-123',
|
|
73
|
+
limit: 20,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<FlatList
|
|
78
|
+
data={transactions}
|
|
79
|
+
keyExtractor={(item) => item.id}
|
|
80
|
+
onEndReached={hasMore ? loadMore : undefined}
|
|
81
|
+
renderItem={({ item }) => (
|
|
82
|
+
<View>
|
|
83
|
+
<Text>{item.reason}</Text>
|
|
84
|
+
<Text>{item.amount} credits</Text>
|
|
85
|
+
<Text>{new Date(item.timestamp).toLocaleString()}</Text>
|
|
86
|
+
</View>
|
|
87
|
+
)}
|
|
88
|
+
/>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### useProductMetadata Hook
|
|
94
|
+
|
|
95
|
+
Ürün metadata yönetimi için:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { useProductMetadata } from '@umituz/react-native-subscription';
|
|
99
|
+
|
|
100
|
+
function ProductInfo() {
|
|
101
|
+
const { metadata, loading, updateMetadata } = useProductMetadata({
|
|
102
|
+
userId: 'user-123',
|
|
103
|
+
productId: 'premium_monthly',
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const handleUpdate = async () => {
|
|
107
|
+
await updateMetadata({
|
|
108
|
+
lastUsed: new Date().toISOString(),
|
|
109
|
+
usageCount: (metadata?.usageCount || 0) + 1,
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
<View>
|
|
115
|
+
<Text>Product: {metadata?.productId}</Text>
|
|
116
|
+
<Text>Usage: {metadata?.usageCount} times</Text>
|
|
117
|
+
<Button onPress={handleUpdate} title="Update Usage" />
|
|
118
|
+
</View>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Bileşenler
|
|
124
|
+
|
|
125
|
+
### BalanceCard
|
|
126
|
+
|
|
127
|
+
Kredi bakiyesini gösteren kart bileşeni:
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { BalanceCard } from '@umituz/react-native-subscription';
|
|
131
|
+
|
|
132
|
+
<BalanceCard
|
|
133
|
+
balance={150}
|
|
134
|
+
currency="USD"
|
|
135
|
+
translations={{
|
|
136
|
+
title: "Your Balance",
|
|
137
|
+
subtitle: "Credits available",
|
|
138
|
+
}}
|
|
139
|
+
/>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### TransactionItem
|
|
143
|
+
|
|
144
|
+
Tek işlem öğesi:
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import { TransactionItem } from '@umituz/react-native-subscription';
|
|
148
|
+
|
|
149
|
+
<TransactionItem
|
|
150
|
+
transaction={{
|
|
151
|
+
id: 'tx-123',
|
|
152
|
+
amount: -50,
|
|
153
|
+
reason: 'purchase',
|
|
154
|
+
timestamp: '2024-01-01T00:00:00Z',
|
|
155
|
+
}}
|
|
156
|
+
translations={{
|
|
157
|
+
purchase: 'Purchase',
|
|
158
|
+
refund: 'Refund',
|
|
159
|
+
}}
|
|
160
|
+
/>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### TransactionList
|
|
164
|
+
|
|
165
|
+
İşlem listesi bileşeni:
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import { TransactionList } from '@umituz/react-native-subscription';
|
|
169
|
+
|
|
170
|
+
<TransactionList
|
|
171
|
+
transactions={transactions}
|
|
172
|
+
loading={loading}
|
|
173
|
+
onEndReached={loadMore}
|
|
174
|
+
translations={{
|
|
175
|
+
title: 'Transaction History',
|
|
176
|
+
empty: 'No transactions yet',
|
|
177
|
+
}}
|
|
178
|
+
/>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### WalletScreen
|
|
182
|
+
|
|
183
|
+
Tam cüzdan ekranı:
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
import { WalletScreen } from '@umituz/react-native-subscription';
|
|
187
|
+
|
|
188
|
+
<WalletScreen
|
|
189
|
+
userId="user-123"
|
|
190
|
+
config={{
|
|
191
|
+
showBalance: true,
|
|
192
|
+
showHistory: true,
|
|
193
|
+
enableRefresh: true,
|
|
194
|
+
}}
|
|
195
|
+
translations={{
|
|
196
|
+
title: 'My Wallet',
|
|
197
|
+
balance: 'Balance',
|
|
198
|
+
history: 'History',
|
|
199
|
+
}}
|
|
200
|
+
/>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## API Referansı
|
|
204
|
+
|
|
205
|
+
### Tip Tanımlamaları
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
interface CreditBalance {
|
|
209
|
+
credits: number;
|
|
210
|
+
lastUpdated: string;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
interface TransactionLog {
|
|
214
|
+
id: string;
|
|
215
|
+
amount: number;
|
|
216
|
+
reason: TransactionReason;
|
|
217
|
+
timestamp: string;
|
|
218
|
+
metadata?: Record<string, any>;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
type TransactionReason =
|
|
222
|
+
| 'purchase'
|
|
223
|
+
| 'refund'
|
|
224
|
+
| 'usage'
|
|
225
|
+
| 'bonus'
|
|
226
|
+
| 'expiration';
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Yardımcı Fonksiyonlar
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
// Kredi maliyeti hesaplama
|
|
233
|
+
import { getCreditCost, creditsToDollars } from '@umituz/react-native-subscription';
|
|
234
|
+
|
|
235
|
+
const cost = getCreditCost('ai_generation'); // AI işlem maliyeti
|
|
236
|
+
const dollars = creditsToDollars(150, 0.01); // 150 kredi = $1.50
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Hata Yönetimi
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
import {
|
|
243
|
+
WalletError,
|
|
244
|
+
CreditLimitError,
|
|
245
|
+
handleWalletError,
|
|
246
|
+
} from '@umituz/react-native-subscription';
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
// İşlem
|
|
250
|
+
} catch (error) {
|
|
251
|
+
if (error instanceof CreditLimitError) {
|
|
252
|
+
console.log('Yetersiz bakiye');
|
|
253
|
+
}
|
|
254
|
+
handleWalletError(error);
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Örnek Uygulama
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
import React from 'react';
|
|
262
|
+
import { View, Text, Button } from 'react-native';
|
|
263
|
+
import {
|
|
264
|
+
useWallet,
|
|
265
|
+
useTransactionHistory,
|
|
266
|
+
BalanceCard,
|
|
267
|
+
TransactionList,
|
|
268
|
+
} from '@umituz/react-native-subscription';
|
|
269
|
+
|
|
270
|
+
export default function WalletExample() {
|
|
271
|
+
const { balance, refresh } = useWallet({ userId: 'user-123' });
|
|
272
|
+
const { transactions, loading } = useTransactionHistory({
|
|
273
|
+
userId: 'user-123',
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
return (
|
|
277
|
+
<View>
|
|
278
|
+
<BalanceCard balance={balance} />
|
|
279
|
+
<Button title="Refresh" onPress={refresh} />
|
|
280
|
+
<TransactionList transactions={transactions} loading={loading} />
|
|
281
|
+
</View>
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Best Practices
|
|
287
|
+
|
|
288
|
+
1. **Kredi Türleri**: Farklı kredi türleri için farklı cost config'leri kullanın
|
|
289
|
+
2. **Hata Yönetimi**: Tüm işlemleri try-catch blokları içinde sarın
|
|
290
|
+
3. **Loading State**: Yüklenme durumlarını her zaman gösterin
|
|
291
|
+
4. **Refresh**: Kullanıcıya manuel refresh imkanı verin
|
|
292
|
+
5. **Transaction Log**: Tüm işlemleri loglayın ve audit trail tutun
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Wallet Domain
|
|
2
|
+
|
|
3
|
+
Wallet domain containing credits and transaction management logic.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The wallet domain manages user credits, transactions, and credit allocation strategies. It handles credit initialization after purchase, credit deduction, and transaction history.
|
|
8
|
+
|
|
9
|
+
## Structure
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
wallet/
|
|
13
|
+
├── domain/
|
|
14
|
+
│ ├── entities/ # Credit and transaction entities
|
|
15
|
+
│ ├── errors/ # Wallet-specific errors
|
|
16
|
+
│ ├── types/ # Wallet type definitions
|
|
17
|
+
│ └── mappers/ # Data transformation mappers
|
|
18
|
+
├── infrastructure/
|
|
19
|
+
│ ├── repositories/ # Credits persistence
|
|
20
|
+
│ └── services/ # Credit operations
|
|
21
|
+
├── presentation/
|
|
22
|
+
│ ├── hooks/ # React hooks for credits
|
|
23
|
+
│ ├── components/ # Credit UI components
|
|
24
|
+
│ └── screens/ # Credit management screens
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Core Concepts
|
|
28
|
+
|
|
29
|
+
### Credits
|
|
30
|
+
User credits balance that can be consumed for premium features.
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
interface UserCredits {
|
|
34
|
+
credits: number;
|
|
35
|
+
purchasedAt: Date;
|
|
36
|
+
lastUpdatedAt: Date;
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Transactions
|
|
41
|
+
Record of credit transactions (additions and deductions).
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
interface Transaction {
|
|
45
|
+
id: string;
|
|
46
|
+
amount: number;
|
|
47
|
+
reason: string;
|
|
48
|
+
timestamp: Date;
|
|
49
|
+
type: 'purchase' | 'deduction' | 'bonus' | 'renewal';
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Allocation Modes
|
|
54
|
+
|
|
55
|
+
**ACCUMULATE**: Add credits to existing balance (renewals)
|
|
56
|
+
```typescript
|
|
57
|
+
// Renewal: 100 + 100 = 200 credits
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**REPLACE**: Replace existing credits (new purchase)
|
|
61
|
+
```typescript
|
|
62
|
+
// New purchase: Old balance replaced with new amount
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Key Features
|
|
66
|
+
|
|
67
|
+
- **Duplicate Protection**: Prevent duplicate credit allocations
|
|
68
|
+
- **Transactional Operations**: Atomic credit updates
|
|
69
|
+
- **Optimistic Updates**: Immediate UI with rollback
|
|
70
|
+
- **Transaction History**: Complete audit trail
|
|
71
|
+
- **Monthly Reset**: Credits reset on subscription renewal
|
|
72
|
+
|
|
73
|
+
## Usage
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
// In hooks or components
|
|
77
|
+
import { useCredits } from './presentation/hooks/useCredits';
|
|
78
|
+
import { useDeductCredit } from './presentation/hooks/useDeductCredit';
|
|
79
|
+
|
|
80
|
+
function Feature() {
|
|
81
|
+
const { credits } = useCredits({ userId: user?.uid });
|
|
82
|
+
const { deductCredit } = useDeductCredit({
|
|
83
|
+
userId: user?.uid,
|
|
84
|
+
onCreditsExhausted: () => showPaywall(),
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const handleUseFeature = async () => {
|
|
88
|
+
const success = await deductCredit(5);
|
|
89
|
+
if (success) {
|
|
90
|
+
await executeFeature();
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Best Practices
|
|
97
|
+
|
|
98
|
+
1. **Check Balance First**: Verify credits before operations
|
|
99
|
+
2. **Handle Exhaustion**: Provide upgrade path when credits low
|
|
100
|
+
3. **Track Usage**: Log all credit transactions
|
|
101
|
+
4. **Reset Credits**: Clear credits on subscription renewal
|
|
102
|
+
5. **Test Edge Cases**: Zero credits, max credits, duplicates
|
|
103
|
+
|
|
104
|
+
## Related
|
|
105
|
+
|
|
106
|
+
- [Credits README](./README.md)
|
|
107
|
+
- [Credits Entity](./domain/entities/Credits.md)
|
|
108
|
+
- [useCredits Hook](../../presentation/hooks/useCredits.md)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Wallet Domain Entities
|
|
2
|
+
|
|
3
|
+
Core entities for credits and transaction management.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This directory contains domain entities representing credits, transactions, and wallet-related business concepts.
|
|
8
|
+
|
|
9
|
+
## Entities
|
|
10
|
+
|
|
11
|
+
### UserCredits
|
|
12
|
+
Represents user's credit balance and metadata.
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
interface UserCredits {
|
|
16
|
+
credits: number; // Current credit balance
|
|
17
|
+
purchasedAt: Date; // When credits were purchased
|
|
18
|
+
lastUpdatedAt: Date; // Last update timestamp
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface CreditsResult {
|
|
22
|
+
success: boolean;
|
|
23
|
+
data?: UserCredits;
|
|
24
|
+
error?: {
|
|
25
|
+
code: string;
|
|
26
|
+
message: string;
|
|
27
|
+
};
|
|
28
|
+
duplicate?: boolean; // If this was a duplicate operation
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Usage:**
|
|
33
|
+
```typescript
|
|
34
|
+
const credits: UserCredits = {
|
|
35
|
+
credits: 100,
|
|
36
|
+
purchasedAt: new Date('2024-01-01'),
|
|
37
|
+
lastUpdatedAt: new Date('2024-01-15'),
|
|
38
|
+
};
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Transaction
|
|
42
|
+
Represents a credit transaction record.
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
interface Transaction {
|
|
46
|
+
id: string;
|
|
47
|
+
amount: number; // Positive for additions, negative for deductions
|
|
48
|
+
reason: string; // Description of transaction
|
|
49
|
+
timestamp: Date; // When transaction occurred
|
|
50
|
+
type: TransactionType; // Transaction category
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type TransactionType =
|
|
54
|
+
| 'purchase' // Initial purchase
|
|
55
|
+
| 'deduction' // Feature usage
|
|
56
|
+
| 'bonus' // Bonus credits
|
|
57
|
+
| 'renewal' // Subscription renewal
|
|
58
|
+
| 'adjustment'; // Manual adjustment
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Usage:**
|
|
62
|
+
```typescript
|
|
63
|
+
const transaction: Transaction = {
|
|
64
|
+
id: 'tx_123',
|
|
65
|
+
amount: -5,
|
|
66
|
+
reason: 'ai_generation',
|
|
67
|
+
timestamp: new Date(),
|
|
68
|
+
type: 'deduction',
|
|
69
|
+
};
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### CreditPackage
|
|
73
|
+
Represents a credit package for purchase.
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
interface CreditPackage {
|
|
77
|
+
id: string;
|
|
78
|
+
amount: number; // Number of credits
|
|
79
|
+
price: number; // Price in currency units
|
|
80
|
+
currency: string; // Currency code (USD, EUR, etc.)
|
|
81
|
+
description?: string;
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Key Operations
|
|
86
|
+
|
|
87
|
+
### Check Balance
|
|
88
|
+
```typescript
|
|
89
|
+
function hasEnoughCredits(credits: UserCredits, required: number): boolean {
|
|
90
|
+
return credits.credits >= required;
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Calculate Remaining
|
|
95
|
+
```typescript
|
|
96
|
+
function calculateRemaining(credits: UserCredits, spent: number): number {
|
|
97
|
+
return Math.max(0, credits.credits - spent);
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Validate Credits
|
|
102
|
+
```typescript
|
|
103
|
+
function validateCredits(amount: number): void {
|
|
104
|
+
if (amount < 0) {
|
|
105
|
+
throw new Error('Credits cannot be negative');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Best Practices
|
|
111
|
+
|
|
112
|
+
1. **Immutability**: Treat entities as immutable values
|
|
113
|
+
2. **Validation**: Validate in entity methods
|
|
114
|
+
3. **Type Safety**: Use strict TypeScript types
|
|
115
|
+
4. **Business Rules**: Keep business logic in entities
|
|
116
|
+
5. **Serialization**: Handle date serialization properly
|
|
117
|
+
|
|
118
|
+
## Related
|
|
119
|
+
|
|
120
|
+
- [Wallet Domain](../README.md)
|
|
121
|
+
- [Credits Repository](../../infrastructure/repositories/README.md)
|
|
122
|
+
- [useCredits Hook](../../../../presentation/hooks/useCredits.md)
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Wallet Domain Errors
|
|
2
|
+
|
|
3
|
+
Domain-specific errors for wallet and credit operations.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This directory contains custom error classes representing wallet-related error conditions.
|
|
8
|
+
|
|
9
|
+
## Error Types
|
|
10
|
+
|
|
11
|
+
### CreditsExhaustedError
|
|
12
|
+
Thrown when user doesn't have enough credits for an operation.
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
class CreditsExhaustedError extends Error {
|
|
16
|
+
constructor(
|
|
17
|
+
public currentBalance: number,
|
|
18
|
+
public required: number
|
|
19
|
+
) {
|
|
20
|
+
super(
|
|
21
|
+
`Insufficient credits: ${currentBalance}/${required} required`,
|
|
22
|
+
'CREDITS_EXHAUSTED'
|
|
23
|
+
);
|
|
24
|
+
this.name = 'CreditsExhaustedError';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Usage:**
|
|
30
|
+
```typescript
|
|
31
|
+
import { CreditsExhaustedError } from './errors/CreditsExhaustedError';
|
|
32
|
+
|
|
33
|
+
function deductCredits(credits: UserCredits, amount: number) {
|
|
34
|
+
if (credits.credits < amount) {
|
|
35
|
+
throw new CreditsExhaustedError(credits.credits, amount);
|
|
36
|
+
}
|
|
37
|
+
// Proceed with deduction
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### DuplicatePurchaseError
|
|
42
|
+
Thrown when attempting to add credits for a duplicate purchase ID.
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
class DuplicatePurchaseError extends Error {
|
|
46
|
+
constructor(
|
|
47
|
+
public purchaseId: string,
|
|
48
|
+
public existingCredits: UserCredits
|
|
49
|
+
) {
|
|
50
|
+
super(
|
|
51
|
+
`Duplicate purchase ID: ${purchaseId}`,
|
|
52
|
+
'DUPLICATE_PURCHASE'
|
|
53
|
+
);
|
|
54
|
+
this.name = 'DuplicatePurchaseError';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Usage:**
|
|
60
|
+
```typescript
|
|
61
|
+
try {
|
|
62
|
+
await repository.initializeCredits(userId, purchaseId);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
if (error instanceof DuplicatePurchaseError) {
|
|
65
|
+
console.log('Credits already added for this purchase');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### CreditsValidationError
|
|
71
|
+
Thrown when credit validation fails.
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
class CreditsValidationError extends Error {
|
|
75
|
+
constructor(
|
|
76
|
+
message: string,
|
|
77
|
+
public code: string
|
|
78
|
+
) {
|
|
79
|
+
super(message, code);
|
|
80
|
+
this.name = 'CreditsValidationError';
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Usage:**
|
|
86
|
+
```typescript
|
|
87
|
+
function validateCreditAmount(amount: number) {
|
|
88
|
+
if (amount < 0) {
|
|
89
|
+
throw new CreditsValidationError(
|
|
90
|
+
'Credit amount cannot be negative',
|
|
91
|
+
'INVALID_AMOUNT'
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
if (amount > 10000) {
|
|
95
|
+
throw new CreditsValidationError(
|
|
96
|
+
'Credit amount exceeds maximum',
|
|
97
|
+
'EXCEEDS_MAXIMUM'
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Error Handling Pattern
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import {
|
|
107
|
+
CreditsExhaustedError,
|
|
108
|
+
DuplicatePurchaseError,
|
|
109
|
+
CreditsValidationError
|
|
110
|
+
} from './errors';
|
|
111
|
+
|
|
112
|
+
async function handleCreditOperation(userId: string, cost: number) {
|
|
113
|
+
try {
|
|
114
|
+
const result = await repository.deductCredit(userId, cost);
|
|
115
|
+
return result;
|
|
116
|
+
} catch (error) {
|
|
117
|
+
if (error instanceof CreditsExhaustedError) {
|
|
118
|
+
// Show paywall or upgrade prompt
|
|
119
|
+
showPaywall({ required: cost, current: error.currentBalance });
|
|
120
|
+
} else if (error instanceof DuplicatePurchaseError) {
|
|
121
|
+
// Log and continue (not a critical error)
|
|
122
|
+
console.warn('Duplicate purchase detected');
|
|
123
|
+
} else if (error instanceof CreditsValidationError) {
|
|
124
|
+
// Log validation error
|
|
125
|
+
console.error('Validation failed:', error.message);
|
|
126
|
+
} else {
|
|
127
|
+
// Unexpected error
|
|
128
|
+
console.error('Unexpected error:', error);
|
|
129
|
+
}
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Error Codes Reference
|
|
136
|
+
|
|
137
|
+
| Code | Description | Recovery |
|
|
138
|
+
|------|-------------|----------|
|
|
139
|
+
| `CREDITS_EXHAUSTED` | Not enough credits | Show paywall/upgrade |
|
|
140
|
+
| `DUPLICATE_PURCHASE` | Duplicate purchase ID | Ignore (idempotent) |
|
|
141
|
+
| `INVALID_AMOUNT` | Invalid credit amount | Validate input |
|
|
142
|
+
| `EXCEEDS_MAXIMUM` | Amount too large | Cap at maximum |
|
|
143
|
+
| `USER_NOT_FOUND` | User doesn't exist | Create user record |
|
|
144
|
+
| `INITIALIZATION_FAILED` | Credit init failed | Retry operation |
|
|
145
|
+
|
|
146
|
+
## Best Practices
|
|
147
|
+
|
|
148
|
+
1. **Specific Errors**: Use specific error types for different scenarios
|
|
149
|
+
2. **Error Context**: Include relevant data in error properties
|
|
150
|
+
3. **Graceful Handling**: Handle errors appropriately at boundaries
|
|
151
|
+
4. **Logging**: Log errors for debugging
|
|
152
|
+
5. **User Feedback**: Convert errors to user-friendly messages
|
|
153
|
+
|
|
154
|
+
## Related
|
|
155
|
+
|
|
156
|
+
- [Wallet Entities](../entities/README.md)
|
|
157
|
+
- [Credits Repository](../../infrastructure/repositories/README.md)
|