@umituz/react-native-subscription 2.14.97 → 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 +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/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/utils/README.md +529 -0
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
# usePaywallOperations Hook
|
|
2
|
+
|
|
3
|
+
Complete paywall purchase operations with authentication handling.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { usePaywallOperations } from '@umituz/react-native-subscription';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Signature
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
function usePaywallOperations(params: {
|
|
15
|
+
userId: string | undefined;
|
|
16
|
+
isAnonymous: boolean;
|
|
17
|
+
onPaywallClose?: () => void;
|
|
18
|
+
onPurchaseSuccess?: () => void;
|
|
19
|
+
onAuthRequired?: () => void;
|
|
20
|
+
}): {
|
|
21
|
+
pendingPackage: PurchasesPackage | null;
|
|
22
|
+
handlePurchase: (pkg: PurchasesPackage) => Promise<boolean>;
|
|
23
|
+
handleRestore: () => Promise<boolean>;
|
|
24
|
+
handleInAppPurchase: (pkg: PurchasesPackage) => Promise<boolean>;
|
|
25
|
+
handleInAppRestore: () => Promise<boolean>;
|
|
26
|
+
completePendingPurchase: () => Promise<boolean>;
|
|
27
|
+
clearPendingPackage: () => void;
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Parameters
|
|
32
|
+
|
|
33
|
+
| Parameter | Type | Default | Description |
|
|
34
|
+
|-----------|------|---------|-------------|
|
|
35
|
+
| `userId` | `string \| undefined` | **Required** | Current user ID |
|
|
36
|
+
| `isAnonymous` | `boolean` | **Required** | Whether user is anonymous/guest |
|
|
37
|
+
| `onPaywallClose` | `() => void` | `undefined` | Callback when paywall closes |
|
|
38
|
+
| `onPurchaseSuccess` | `() => void` | `undefined` | Callback after successful purchase |
|
|
39
|
+
| `onAuthRequired` | `() => void` | `undefined` | Callback when auth is required |
|
|
40
|
+
|
|
41
|
+
## Returns
|
|
42
|
+
|
|
43
|
+
| Property | Type | Description |
|
|
44
|
+
|----------|------|-------------|
|
|
45
|
+
| `pendingPackage` | `PurchasesPackage \| null` | Package waiting for auth |
|
|
46
|
+
| `handlePurchase` | `(pkg) => Promise<boolean>` | Handle purchase (post-auth) |
|
|
47
|
+
| `handleRestore` | `() => Promise<boolean>` | Handle restore (post-auth) |
|
|
48
|
+
| `handleInAppPurchase` | `(pkg) => Promise<boolean>` | Handle purchase (in-app) |
|
|
49
|
+
| `handleInAppRestore` | `() => Promise<boolean>` | Handle restore (in-app) |
|
|
50
|
+
| `completePendingPurchase` | `() => Promise<boolean>` | Complete purchase after auth |
|
|
51
|
+
| `clearPendingPackage` | `() => void` | Clear pending package |
|
|
52
|
+
|
|
53
|
+
## Basic Usage
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
function PaywallScreen() {
|
|
57
|
+
const { user } = useAuth();
|
|
58
|
+
const { isAnonymous } = useAuth();
|
|
59
|
+
|
|
60
|
+
const {
|
|
61
|
+
handlePurchase,
|
|
62
|
+
handleRestore,
|
|
63
|
+
} = usePaywallOperations({
|
|
64
|
+
userId: user?.uid,
|
|
65
|
+
isAnonymous,
|
|
66
|
+
onPurchaseSuccess: () => {
|
|
67
|
+
navigation.goBack();
|
|
68
|
+
},
|
|
69
|
+
onAuthRequired: () => {
|
|
70
|
+
showAuthModal();
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const onPackagePress = async (pkg: PurchasesPackage) => {
|
|
75
|
+
const success = await handlePurchase(pkg);
|
|
76
|
+
if (success) {
|
|
77
|
+
console.log('Purchase successful');
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const onRestorePress = async () => {
|
|
82
|
+
const success = await handleRestore();
|
|
83
|
+
if (success) {
|
|
84
|
+
console.log('Restore successful');
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<View>
|
|
90
|
+
<PackageList onSelectPackage={onPackagePress} />
|
|
91
|
+
<Button onPress={onRestorePress} title="Restore Purchases" />
|
|
92
|
+
</View>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Advanced Usage
|
|
98
|
+
|
|
99
|
+
### With Pending Purchase Flow
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
function PaywallWithPendingFlow() {
|
|
103
|
+
const { user } = useAuth();
|
|
104
|
+
const { isAnonymous } = useAuth();
|
|
105
|
+
|
|
106
|
+
const {
|
|
107
|
+
pendingPackage,
|
|
108
|
+
handlePurchase,
|
|
109
|
+
completePendingPurchase,
|
|
110
|
+
clearPendingPackage,
|
|
111
|
+
} = usePaywallOperations({
|
|
112
|
+
userId: user?.uid,
|
|
113
|
+
isAnonymous,
|
|
114
|
+
onAuthRequired: () => {
|
|
115
|
+
navigation.navigate('AuthModal', {
|
|
116
|
+
purpose: 'purchase',
|
|
117
|
+
onAuthSuccess: () => {
|
|
118
|
+
// Complete purchase after auth
|
|
119
|
+
completePendingPurchase();
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Show pending package indicator
|
|
126
|
+
useEffect(() => {
|
|
127
|
+
if (pendingPackage && !user) {
|
|
128
|
+
console.log('Package pending authentication:', pendingPackage.identifier);
|
|
129
|
+
}
|
|
130
|
+
}, [pendingPackage, user]);
|
|
131
|
+
|
|
132
|
+
const handleCancelAuth = () => {
|
|
133
|
+
clearPendingPackage();
|
|
134
|
+
navigation.goBack();
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<View>
|
|
139
|
+
{pendingPackage && !user && (
|
|
140
|
+
<Banner>
|
|
141
|
+
<Text>Complete your purchase after signing in</Text>
|
|
142
|
+
<Button onPress={handleCancelAuth} title="Cancel" />
|
|
143
|
+
</Banner>
|
|
144
|
+
)}
|
|
145
|
+
|
|
146
|
+
<PackageList onSelectPackage={handlePurchase} />
|
|
147
|
+
</View>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### With In-App Purchases
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
function InAppPaywall() {
|
|
156
|
+
const { user } = useAuth();
|
|
157
|
+
const { isAnonymous } = useAuth();
|
|
158
|
+
|
|
159
|
+
const {
|
|
160
|
+
handleInAppPurchase,
|
|
161
|
+
handleInAppRestore,
|
|
162
|
+
} = usePaywallOperations({
|
|
163
|
+
userId: user?.uid,
|
|
164
|
+
isAnonymous,
|
|
165
|
+
onPaywallClose: () => {
|
|
166
|
+
navigation.goBack();
|
|
167
|
+
},
|
|
168
|
+
onPurchaseSuccess: () => {
|
|
169
|
+
navigation.goBack();
|
|
170
|
+
},
|
|
171
|
+
onAuthRequired: () => {
|
|
172
|
+
showAuthModal();
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<Modal>
|
|
178
|
+
<PaywallContent>
|
|
179
|
+
<PackageList onSelectPackage={handleInAppPurchase} />
|
|
180
|
+
<RestoreButton onPress={handleInAppRestore} />
|
|
181
|
+
<CloseButton onPress={() => navigation.goBack()} />
|
|
182
|
+
</PaywallContent>
|
|
183
|
+
</Modal>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### With Purchase Analytics
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
function TrackedPaywall() {
|
|
192
|
+
const { user } = useAuth();
|
|
193
|
+
const { isAnonymous } = useAuth();
|
|
194
|
+
|
|
195
|
+
const {
|
|
196
|
+
handlePurchase,
|
|
197
|
+
handleRestore,
|
|
198
|
+
pendingPackage,
|
|
199
|
+
} = usePaywallOperations({
|
|
200
|
+
userId: user?.uid,
|
|
201
|
+
isAnonymous,
|
|
202
|
+
onPurchaseSuccess: () => {
|
|
203
|
+
analytics.track('purchase_completed', {
|
|
204
|
+
userId: user?.uid,
|
|
205
|
+
packageIdentifier: pendingPackage?.identifier,
|
|
206
|
+
});
|
|
207
|
+
},
|
|
208
|
+
onAuthRequired: () => {
|
|
209
|
+
analytics.track('auth_required_for_purchase');
|
|
210
|
+
showAuthModal();
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const onPackageSelect = async (pkg: PurchasesPackage) => {
|
|
215
|
+
analytics.track('purchase_attempted', {
|
|
216
|
+
packageIdentifier: pkg.identifier,
|
|
217
|
+
price: pkg.product.price,
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
const success = await handlePurchase(pkg);
|
|
221
|
+
|
|
222
|
+
if (!success) {
|
|
223
|
+
analytics.track('purchase_failed', {
|
|
224
|
+
packageIdentifier: pkg.identifier,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
return <PackageList onSelectPackage={onPackageSelect} />;
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### With Error Handling
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
function RobustPaywall() {
|
|
237
|
+
const { user } = useAuth();
|
|
238
|
+
const { isAnonymous } = useAuth();
|
|
239
|
+
|
|
240
|
+
const {
|
|
241
|
+
handlePurchase,
|
|
242
|
+
handleRestore,
|
|
243
|
+
completePendingPurchase,
|
|
244
|
+
clearPendingPackage,
|
|
245
|
+
} = usePaywallOperations({
|
|
246
|
+
userId: user?.uid,
|
|
247
|
+
isAnonymous,
|
|
248
|
+
onAuthRequired: () => {
|
|
249
|
+
showAuthModal({
|
|
250
|
+
onAuthSuccess: async () => {
|
|
251
|
+
const success = await completePendingPurchase();
|
|
252
|
+
if (!success) {
|
|
253
|
+
Alert.alert('Error', 'Could not complete purchase');
|
|
254
|
+
clearPendingPackage();
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
onAuthCancelled: () => {
|
|
258
|
+
clearPendingPackage();
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
const handlePackagePress = async (pkg: PurchasesPackage) => {
|
|
265
|
+
try {
|
|
266
|
+
const success = await handlePurchase(pkg);
|
|
267
|
+
|
|
268
|
+
if (!success) {
|
|
269
|
+
if (!user) {
|
|
270
|
+
// Auth required - handled by onAuthRequired
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
// Other failure
|
|
274
|
+
Alert.alert('Purchase Failed', 'Could not complete purchase. Please try again.');
|
|
275
|
+
}
|
|
276
|
+
} catch (error) {
|
|
277
|
+
console.error('Purchase error:', error);
|
|
278
|
+
Alert.alert('Error', 'An unexpected error occurred');
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
return <PackageList onSelectPackage={handlePackagePress} />;
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Examples
|
|
287
|
+
|
|
288
|
+
### Complete Paywall Implementation
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
function PaywallScreen() {
|
|
292
|
+
const { user } = useAuth();
|
|
293
|
+
const { isAnonymous } = useAuth();
|
|
294
|
+
const navigation = useNavigation();
|
|
295
|
+
|
|
296
|
+
const {
|
|
297
|
+
pendingPackage,
|
|
298
|
+
handlePurchase,
|
|
299
|
+
handleRestore,
|
|
300
|
+
completePendingPurchase,
|
|
301
|
+
clearPendingPackage,
|
|
302
|
+
} = usePaywallOperations({
|
|
303
|
+
userId: user?.uid,
|
|
304
|
+
isAnonymous,
|
|
305
|
+
onPaywallClose: () => {
|
|
306
|
+
navigation.goBack();
|
|
307
|
+
},
|
|
308
|
+
onPurchaseSuccess: () => {
|
|
309
|
+
// Refresh user data
|
|
310
|
+
queryClient.invalidateQueries(['subscription']);
|
|
311
|
+
navigation.goBack();
|
|
312
|
+
},
|
|
313
|
+
onAuthRequired: () => {
|
|
314
|
+
// Show auth modal with pending purchase context
|
|
315
|
+
navigation.push('AuthModal', {
|
|
316
|
+
purpose: 'purchase',
|
|
317
|
+
message: 'Sign in to complete your purchase',
|
|
318
|
+
onAuthSuccess: async () => {
|
|
319
|
+
const success = await completePendingPurchase();
|
|
320
|
+
if (success) {
|
|
321
|
+
navigation.goBack();
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
onAuthCancelled: () => {
|
|
325
|
+
clearPendingPackage();
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
},
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
const [selectedPackage, setSelectedPackage] = useState<PurchasesPackage | null>(null);
|
|
332
|
+
|
|
333
|
+
const onPackagePress = async (pkg: PurchasesPackage) => {
|
|
334
|
+
setSelectedPackage(pkg);
|
|
335
|
+
const success = await handlePurchase(pkg);
|
|
336
|
+
setSelectedPackage(null);
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
const onRestorePress = async () => {
|
|
340
|
+
const success = await handleRestore();
|
|
341
|
+
Alert.alert(
|
|
342
|
+
success ? 'Success' : 'No Purchases Found',
|
|
343
|
+
success
|
|
344
|
+
? 'Your purchases have been restored'
|
|
345
|
+
: 'No previous purchases found for this account'
|
|
346
|
+
);
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
return (
|
|
350
|
+
<ScrollView style={styles.container}>
|
|
351
|
+
<PaywallHeader />
|
|
352
|
+
|
|
353
|
+
{pendingPackage && !user && (
|
|
354
|
+
<Banner style={styles.pendingBanner}>
|
|
355
|
+
<Text>Complete your purchase after signing in</Text>
|
|
356
|
+
</Banner>
|
|
357
|
+
)}
|
|
358
|
+
|
|
359
|
+
<PackageList
|
|
360
|
+
packages={packages}
|
|
361
|
+
selectedPackage={selectedPackage}
|
|
362
|
+
onSelectPackage={onPackagePress}
|
|
363
|
+
/>
|
|
364
|
+
|
|
365
|
+
<View style={styles.restoreSection}>
|
|
366
|
+
<Button
|
|
367
|
+
onPress={onRestorePress}
|
|
368
|
+
variant="outline"
|
|
369
|
+
title="Restore Purchases"
|
|
370
|
+
/>
|
|
371
|
+
</View>
|
|
372
|
+
|
|
373
|
+
<TermsAndConditions />
|
|
374
|
+
</ScrollView>
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Post-Onboarding Purchase
|
|
380
|
+
|
|
381
|
+
```typescript
|
|
382
|
+
function PostOnboardingPaywall() {
|
|
383
|
+
const { user } = useAuth();
|
|
384
|
+
const { isAnonymous } = useAuth();
|
|
385
|
+
|
|
386
|
+
const {
|
|
387
|
+
handlePurchase,
|
|
388
|
+
completePendingPurchase,
|
|
389
|
+
} = usePaywallOperations({
|
|
390
|
+
userId: user?.uid,
|
|
391
|
+
isAnonymous,
|
|
392
|
+
onPurchaseSuccess: () => {
|
|
393
|
+
// Navigate to onboarding completion
|
|
394
|
+
navigation.reset({
|
|
395
|
+
index: 0,
|
|
396
|
+
routes: [{ name: 'Home' }],
|
|
397
|
+
});
|
|
398
|
+
},
|
|
399
|
+
onAuthRequired: () => {
|
|
400
|
+
navigation.navigate('Auth', {
|
|
401
|
+
screen: 'SignUp',
|
|
402
|
+
params: {
|
|
403
|
+
onAuthSuccess: async () => {
|
|
404
|
+
await completePendingPurchase();
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
});
|
|
408
|
+
},
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
return (
|
|
412
|
+
<View>
|
|
413
|
+
<OnboardingPaywallContent />
|
|
414
|
+
<PackageList onSelectPackage={handlePurchase} />
|
|
415
|
+
</View>
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
## Purchase Flow
|
|
421
|
+
|
|
422
|
+
### Authenticated User
|
|
423
|
+
|
|
424
|
+
```typescript
|
|
425
|
+
user = { uid: 'user-123' }
|
|
426
|
+
isAnonymous = false
|
|
427
|
+
|
|
428
|
+
handlePurchase(package)
|
|
429
|
+
→ Purchase proceeds immediately ✅
|
|
430
|
+
→ onPurchaseSuccess() called ✅
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Anonymous User
|
|
434
|
+
|
|
435
|
+
```typescript
|
|
436
|
+
user = null
|
|
437
|
+
isAnonymous = true
|
|
438
|
+
|
|
439
|
+
handlePurchase(package)
|
|
440
|
+
→ Purchase blocked 🚫
|
|
441
|
+
→ pendingPackage = package 📦
|
|
442
|
+
→ onAuthRequired() called 🔐
|
|
443
|
+
→ User completes auth...
|
|
444
|
+
→ completePendingPurchase()
|
|
445
|
+
→ Purchase proceeds ✅
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Pending Package Management
|
|
449
|
+
|
|
450
|
+
```typescript
|
|
451
|
+
// User selects package while not authenticated
|
|
452
|
+
handlePurchase(monthlyPackage)
|
|
453
|
+
→ pendingPackage = monthlyPackage
|
|
454
|
+
→ onAuthRequired() triggered
|
|
455
|
+
|
|
456
|
+
// User cancels auth
|
|
457
|
+
clearPendingPackage()
|
|
458
|
+
→ pendingPackage = null
|
|
459
|
+
|
|
460
|
+
// Or user completes auth
|
|
461
|
+
completePendingPurchase()
|
|
462
|
+
→ Purchases monthlyPackage
|
|
463
|
+
→ pendingPackage = null
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
## Best Practices
|
|
467
|
+
|
|
468
|
+
1. **Handle auth flow** - Implement onAuthRequired callback
|
|
469
|
+
2. **Show pending state** - Display indicator when package is pending
|
|
470
|
+
3. **Clear pending** - Reset pending when user cancels auth
|
|
471
|
+
4. **Track events** - Monitor purchase attempts and completions
|
|
472
|
+
5. **Handle errors** - Show user-friendly error messages
|
|
473
|
+
6. **Test both flows** - Authenticated and anonymous users
|
|
474
|
+
7. **Provide restore** - Always offer restore purchases option
|
|
475
|
+
|
|
476
|
+
## Related Hooks
|
|
477
|
+
|
|
478
|
+
- **usePremium** - For purchase and restore operations
|
|
479
|
+
- **useAuthAwarePurchase** - For auth-gated purchases
|
|
480
|
+
- **usePaywallVisibility** - For paywall visibility control
|
|
481
|
+
|
|
482
|
+
## See Also
|
|
483
|
+
|
|
484
|
+
- [Paywall Screen](../screens/README.md)
|
|
485
|
+
- [Purchase Flow](../../../docs/PURCHASE_FLOW.md)
|
|
486
|
+
- [Auth Integration](./useAuthSubscriptionSync.md)
|