@umituz/react-native-subscription 2.14.99 → 2.14.101
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +211 -394
- package/package.json +3 -3
- package/src/application/README.md +46 -225
- package/src/application/ports/README.md +42 -97
- package/src/domain/README.md +36 -384
- package/src/domain/constants/README.md +0 -56
- package/src/domain/entities/README.md +43 -169
- package/src/domain/entities/SubscriptionStatus.ts +1 -1
- package/src/domain/errors/README.md +33 -287
- package/src/domain/value-objects/README.md +43 -179
- package/src/domains/README.md +50 -238
- package/src/domains/README.md.bak +274 -0
- package/src/domains/config/README.md +93 -383
- package/src/domains/config/domain/README.md +23 -376
- package/src/domains/config/domain/entities/README.md +34 -343
- package/src/domains/paywall/README.md +99 -369
- package/src/domains/paywall/components/README.md +34 -178
- package/src/domains/paywall/entities/README.md +34 -193
- package/src/domains/paywall/hooks/README.md +34 -122
- package/src/domains/wallet/README.md +34 -275
- package/src/domains/wallet/README.md.bak +209 -0
- package/src/domains/wallet/domain/README.md +34 -101
- package/src/domains/wallet/domain/entities/README.md +34 -115
- package/src/domains/wallet/domain/errors/README.md +34 -151
- package/src/domains/wallet/infrastructure/README.md +34 -89
- package/src/domains/wallet/presentation/components/README.md +34 -224
- package/src/domains/wallet/presentation/components/TransactionItem.tsx +1 -1
- package/src/domains/wallet/presentation/hooks/README.md +34 -248
- package/src/infrastructure/README.md +37 -496
- package/src/infrastructure/mappers/README.md +0 -13
- package/src/infrastructure/repositories/README.md +74 -360
- package/src/infrastructure/services/ActivationHandler.ts +1 -1
- package/src/infrastructure/services/README.md +95 -370
- package/src/infrastructure/services/SubscriptionService.ts +1 -1
- package/src/presentation/README.md +123 -408
- package/src/presentation/README.md.bak +172 -0
- package/src/presentation/components/README.md +151 -179
- package/src/presentation/components/README.md.bak +217 -0
- package/src/presentation/components/details/CreditRow.md +65 -310
- package/src/presentation/components/details/DetailRow.md +63 -255
- package/src/presentation/components/details/PremiumDetailsCard.md +65 -238
- package/src/presentation/components/details/PremiumStatusBadge.md +64 -239
- package/src/presentation/components/details/README.md +97 -447
- package/src/presentation/components/feedback/PaywallFeedbackModal.md +63 -287
- package/src/presentation/components/feedback/README.md +97 -445
- package/src/presentation/components/paywall/PaywallModal.md +66 -416
- package/src/presentation/components/paywall/README.md +50 -186
- package/src/presentation/components/sections/README.md +97 -466
- package/src/presentation/components/sections/SubscriptionSection.md +92 -244
- package/src/presentation/hooks/README.md +154 -741
- package/src/presentation/hooks/useAuthAwarePurchase.md +58 -325
- package/src/presentation/hooks/useAuthGate.md +61 -375
- package/src/presentation/hooks/useAuthSubscriptionSync.md +66 -370
- package/src/presentation/hooks/useCreditChecker.md +73 -378
- package/src/presentation/hooks/useCredits.md +74 -313
- package/src/presentation/hooks/useCredits.md.bak +231 -0
- package/src/presentation/hooks/useCreditsGate.md +66 -318
- package/src/presentation/hooks/useDeductCredit.md +0 -76
- package/src/presentation/hooks/useDeductCredit.ts +1 -1
- package/src/presentation/hooks/useDevTestCallbacks.md +63 -394
- package/src/presentation/hooks/useFeatureGate.md +105 -150
- package/src/presentation/hooks/useFeatureGate.md.bak +284 -0
- package/src/presentation/hooks/useInitializeCredits.md +64 -430
- package/src/presentation/hooks/usePaywall.md +61 -306
- package/src/presentation/hooks/usePaywallOperations.md +64 -458
- package/src/presentation/hooks/usePaywallVisibility.md +67 -316
- package/src/presentation/hooks/usePremium.md +84 -226
- package/src/presentation/hooks/usePremiumGate.md +60 -395
- package/src/presentation/hooks/usePremiumWithCredits.md +64 -401
- package/src/presentation/hooks/useSubscription.md +66 -422
- package/src/presentation/hooks/useSubscriptionDetails.md +65 -410
- package/src/presentation/hooks/useSubscriptionGate.md +80 -164
- package/src/presentation/hooks/useSubscriptionSettingsConfig.md +66 -346
- package/src/presentation/hooks/useSubscriptionStatus.md +66 -396
- package/src/presentation/hooks/useUserTier.md +63 -328
- package/src/presentation/hooks/useUserTierWithRepository.md +64 -424
- package/src/presentation/screens/README.md +48 -190
- package/src/presentation/types/README.md +0 -16
- package/src/presentation/utils/README.md +0 -21
- package/src/presentation/utils/subscriptionDateUtils.ts +1 -1
- package/src/revenuecat/README.md +99 -518
- package/src/revenuecat/application/README.md +35 -150
- package/src/revenuecat/application/ports/README.md +34 -162
- package/src/revenuecat/domain/README.md +42 -141
- package/src/revenuecat/domain/constants/README.md +34 -176
- package/src/revenuecat/domain/entities/README.md +34 -374
- package/src/revenuecat/domain/errors/README.md +47 -191
- package/src/revenuecat/domain/types/README.md +34 -366
- package/src/revenuecat/domain/value-objects/README.md +34 -434
- package/src/revenuecat/infrastructure/README.md +34 -43
- package/src/revenuecat/infrastructure/config/README.md +32 -23
- package/src/revenuecat/infrastructure/handlers/README.md +34 -211
- package/src/revenuecat/infrastructure/managers/README.md +34 -42
- package/src/revenuecat/infrastructure/services/README.md +35 -318
- package/src/revenuecat/infrastructure/utils/README.md +34 -375
- package/src/revenuecat/presentation/README.md +34 -176
- package/src/revenuecat/presentation/hooks/README.md +29 -35
- package/src/utils/README.md +38 -525
|
@@ -2,485 +2,91 @@
|
|
|
2
2
|
|
|
3
3
|
Complete paywall purchase operations with authentication handling.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Location
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
import { usePaywallOperations } from '@umituz/react-native-subscription';
|
|
9
|
-
```
|
|
7
|
+
**Import Path**: `@umituz/react-native-subscription`
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
**File**: `src/presentation/hooks/usePaywallOperations.ts`
|
|
12
10
|
|
|
13
|
-
|
|
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
|
-
```
|
|
11
|
+
**Type**: Hook
|
|
30
12
|
|
|
31
|
-
##
|
|
13
|
+
## Strategy
|
|
32
14
|
|
|
33
|
-
|
|
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 |
|
|
15
|
+
### Paywall Purchase Flow with Auth
|
|
40
16
|
|
|
41
|
-
|
|
17
|
+
1. **Auth Check**: Verify if user is authenticated before purchase
|
|
18
|
+
2. **Pending Package Management**: Store package when auth is required
|
|
19
|
+
3. **Auth Flow Trigger**: Show auth modal for unauthenticated users
|
|
20
|
+
4. **Purchase Execution**: Complete purchase after authentication
|
|
21
|
+
5. **Restore Support**: Handle purchase restoration with auth check
|
|
22
|
+
6. **Callback Handling**: Execute appropriate callbacks at each stage
|
|
42
23
|
|
|
43
|
-
|
|
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 |
|
|
24
|
+
### Integration Points
|
|
52
25
|
|
|
53
|
-
|
|
26
|
+
- **usePremium**: For purchase and restore operations
|
|
27
|
+
- **Auth Context**: User authentication state
|
|
28
|
+
- **Paywall Domain**: For paywall display and management
|
|
29
|
+
- **Auth UI**: For authentication modal
|
|
30
|
+
- **RevenueCat**: For purchase transactions
|
|
54
31
|
|
|
55
|
-
|
|
56
|
-
function PaywallScreen() {
|
|
57
|
-
const { user } = useAuth();
|
|
58
|
-
const { isAnonymous } = useAuth();
|
|
32
|
+
## Restrictions
|
|
59
33
|
|
|
60
|
-
|
|
61
|
-
handlePurchase,
|
|
62
|
-
handleRestore,
|
|
63
|
-
} = usePaywallOperations({
|
|
64
|
-
userId: user?.uid,
|
|
65
|
-
isAnonymous,
|
|
66
|
-
onPurchaseSuccess: () => {
|
|
67
|
-
navigation.goBack();
|
|
68
|
-
},
|
|
69
|
-
onAuthRequired: () => {
|
|
70
|
-
showAuthModal();
|
|
71
|
-
},
|
|
72
|
-
});
|
|
34
|
+
### REQUIRED
|
|
73
35
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
};
|
|
36
|
+
- **User ID**: MUST provide valid userId parameter
|
|
37
|
+
- **Anonymous Check**: MUST provide isAnonymous parameter
|
|
38
|
+
- **Auth Required Callback**: MUST implement onAuthRequired callback
|
|
39
|
+
- **Error Handling**: MUST handle purchase failures
|
|
80
40
|
|
|
81
|
-
|
|
82
|
-
const success = await handleRestore();
|
|
83
|
-
if (success) {
|
|
84
|
-
console.log('Restore successful');
|
|
85
|
-
}
|
|
86
|
-
};
|
|
41
|
+
### PROHIBITED
|
|
87
42
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
</View>
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
```
|
|
43
|
+
- **NEVER** call without valid userId
|
|
44
|
+
- **NEVER** call without isAnonymous parameter
|
|
45
|
+
- **DO NOT** proceed with purchase without auth check
|
|
46
|
+
- **DO NOT** ignore pending package state
|
|
96
47
|
|
|
97
|
-
|
|
48
|
+
### CRITICAL SAFETY
|
|
98
49
|
|
|
99
|
-
|
|
50
|
+
- **ALWAYS** check authentication status before purchase
|
|
51
|
+
- **MUST** handle pending package after auth
|
|
52
|
+
- **ALWAYS** provide clear auth flow for users
|
|
53
|
+
- **NEVER** allow anonymous purchases without auth
|
|
100
54
|
|
|
101
|
-
|
|
102
|
-
function PaywallWithPendingFlow() {
|
|
103
|
-
const { user } = useAuth();
|
|
104
|
-
const { isAnonymous } = useAuth();
|
|
55
|
+
## AI Agent Guidelines
|
|
105
56
|
|
|
106
|
-
|
|
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
|
-
});
|
|
57
|
+
### When Implementing Paywall Operations
|
|
124
58
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}, [pendingPackage, user]);
|
|
59
|
+
1. **Always** provide valid userId
|
|
60
|
+
2. **Always** provide isAnonymous status
|
|
61
|
+
3. **Always** implement onAuthRequired with pending package handling
|
|
62
|
+
4. **Always** implement onPurchaseSuccess callback
|
|
63
|
+
5. **Always** handle purchase and restore failures
|
|
131
64
|
|
|
132
|
-
|
|
133
|
-
clearPendingPackage();
|
|
134
|
-
navigation.goBack();
|
|
135
|
-
};
|
|
65
|
+
### Integration Checklist
|
|
136
66
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
67
|
+
- [ ] Import from correct path: `@umituz/react-native-subscription`
|
|
68
|
+
- [ ] Provide valid userId
|
|
69
|
+
- [ ] Provide isAnonymous status
|
|
70
|
+
- [ ] Implement onAuthRequired callback
|
|
71
|
+
- [ ] Implement onPurchaseSuccess callback
|
|
72
|
+
- [ ] Implement onPaywallClose callback
|
|
73
|
+
- [ ] Handle pending package state
|
|
74
|
+
- [ ] Test purchase with authenticated user
|
|
75
|
+
- [ ] Test purchase with anonymous user
|
|
76
|
+
- [ ] Test purchase flow after authentication
|
|
145
77
|
|
|
146
|
-
|
|
147
|
-
</View>
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
```
|
|
78
|
+
### Common Patterns
|
|
151
79
|
|
|
152
|
-
|
|
80
|
+
1. **Pending Purchase Flow**: Store package, show auth, complete purchase
|
|
81
|
+
2. **In-App Purchases**: Use handleInAppPurchase for modal paywalls
|
|
82
|
+
3. **Purchase Analytics**: Track purchase attempts and completions
|
|
83
|
+
4. **Error Recovery**: Handle failures and provide retry options
|
|
84
|
+
5. **Post-Onboarding**: Handle purchases after onboarding flow
|
|
153
85
|
|
|
154
|
-
|
|
155
|
-
function InAppPaywall() {
|
|
156
|
-
const { user } = useAuth();
|
|
157
|
-
const { isAnonymous } = useAuth();
|
|
86
|
+
## Related Documentation
|
|
158
87
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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)
|
|
88
|
+
- **usePremium**: For purchase and restore operations
|
|
89
|
+
- **useAuthAwarePurchase**: For auth-gated purchases
|
|
90
|
+
- **usePaywallVisibility**: For paywall visibility control
|
|
91
|
+
- **Paywall Screen**: `src/presentation/screens/README.md`
|
|
92
|
+
- **Purchase Flow**: `src/docs/PURCHASE_FLOW.md`
|