@umituz/react-native-subscription 2.2.33 → 2.2.35

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-subscription",
3
- "version": "2.2.33",
3
+ "version": "2.2.35",
4
4
  "description": "Complete subscription management with RevenueCat, paywall UI, and credits system for React Native apps",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
package/src/index.ts CHANGED
@@ -9,6 +9,11 @@
9
9
  // DOMAIN LAYER - Errors
10
10
  // =============================================================================
11
11
 
12
+ // =============================================================================
13
+ // BROKEN EXPORTS - Files missing
14
+ // =============================================================================
15
+
16
+ /*
12
17
  export {
13
18
  SubscriptionError,
14
19
  SubscriptionRepositoryError,
@@ -16,10 +21,6 @@ export {
16
21
  SubscriptionConfigurationError,
17
22
  } from "./domain/errors/SubscriptionError";
18
23
 
19
- // =============================================================================
20
- // DOMAIN LAYER - Entities
21
- // =============================================================================
22
-
23
24
  export {
24
25
  createDefaultSubscriptionStatus,
25
26
  isSubscriptionValid,
@@ -28,17 +29,9 @@ export type { SubscriptionStatus } from "./domain/entities/SubscriptionStatus";
28
29
 
29
30
  export type { SubscriptionConfig } from "./domain/value-objects/SubscriptionConfig";
30
31
 
31
- // =============================================================================
32
- // APPLICATION LAYER - Ports
33
- // =============================================================================
34
-
35
32
  export type { ISubscriptionRepository } from "./application/ports/ISubscriptionRepository";
36
33
  export type { ISubscriptionService } from "./application/ports/ISubscriptionService";
37
34
 
38
- // =============================================================================
39
- // INFRASTRUCTURE LAYER - Services
40
- // =============================================================================
41
-
42
35
  export {
43
36
  SubscriptionService,
44
37
  initializeSubscriptionService,
@@ -46,14 +39,6 @@ export {
46
39
  resetSubscriptionService,
47
40
  } from "./infrastructure/services/SubscriptionService";
48
41
 
49
- // Feedback
50
- export * from "./presentation/components/feedback/PaywallFeedbackModal";
51
- export * from "./presentation/hooks/feedback/usePaywallFeedback";
52
-
53
- // =============================================================================
54
- // PRESENTATION LAYER - Hooks
55
- // =============================================================================
56
-
57
42
  export { useSubscription } from "./presentation/hooks/useSubscription";
58
43
  export type { UseSubscriptionResult } from "./presentation/hooks/useSubscription";
59
44
 
@@ -86,6 +71,11 @@ export {
86
71
  type UseUserTierWithRepositoryResult,
87
72
  type AuthProvider,
88
73
  } from "./presentation/hooks/useUserTierWithRepository";
74
+ */
75
+
76
+ // Feedback
77
+ export * from "./presentation/components/feedback/PaywallFeedbackModal";
78
+ export * from "./presentation/hooks/feedback/usePaywallFeedback";
89
79
 
90
80
  // =============================================================================
91
81
  // PRESENTATION LAYER - Paywall Components
@@ -157,10 +147,12 @@ export {
157
147
  // UTILS - Date & Price
158
148
  // =============================================================================
159
149
 
150
+ /*
160
151
  export {
161
152
  isSubscriptionExpired,
162
153
  getDaysUntilExpiration,
163
154
  } from "./utils/dateValidationUtils";
155
+ */
164
156
 
165
157
  export { formatPrice } from "./utils/priceUtils";
166
158
 
@@ -7,7 +7,7 @@ import Purchases from "react-native-purchases";
7
7
  import type { InitializeResult } from "../../application/ports/IRevenueCatService";
8
8
  import type { RevenueCatConfig } from "../../domain/value-objects/RevenueCatConfig";
9
9
  import { getErrorMessage } from "../../domain/types/RevenueCatTypes";
10
- import { isExpoGo } from "../utils/ExpoGoDetector";
10
+
11
11
  import { resolveApiKey } from "../utils/ApiKeyResolver";
12
12
 
13
13
  export interface InitializerDeps {
@@ -63,12 +63,7 @@ export async function initializeSDK(
63
63
  }
64
64
  }
65
65
 
66
- if (isExpoGo() && !deps.isUsingTestStore()) {
67
- if (__DEV__) {
68
- console.log("[RevenueCat] Skipping - ExpoGo without test store");
69
- }
70
- return { success: false, offering: null, hasPremium: false };
71
- }
66
+
72
67
 
73
68
  const key = apiKey || resolveApiKey(deps.config);
74
69
  if (!key) {
package/LICENSE DELETED
@@ -1,32 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2024 Ümit UZ
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
package/README.md DELETED
@@ -1,216 +0,0 @@
1
- # @umituz/react-native-subscription
2
-
3
- Subscription management system for React Native apps - Database-first approach with secure validation.
4
-
5
- Built with **SOLID**, **DRY**, and **KISS** principles.
6
-
7
- ## Installation
8
-
9
- ```bash
10
- npm install @umituz/react-native-subscription
11
- ```
12
-
13
- ## Peer Dependencies
14
-
15
- - `react` >= 18.2.0
16
- - `react-native` >= 0.74.0
17
-
18
- ## Features
19
-
20
- - ✅ Domain-Driven Design (DDD) architecture
21
- - ✅ SOLID principles (Single Responsibility, Open/Closed, etc.)
22
- - ✅ DRY (Don't Repeat Yourself)
23
- - ✅ KISS (Keep It Simple, Stupid)
24
- - ✅ **Security**: Database-first approach - Always validate server-side
25
- - ✅ Type-safe operations
26
- - ✅ React hooks for easy integration
27
- - ✅ Works with any database (Firebase, Supabase, etc.)
28
-
29
- ## Important: Database-First Approach
30
-
31
- **This package follows a database-first approach:**
32
-
33
- - Subscription status is ALWAYS checked from your database
34
- - This ensures 10-50x faster subscription checks
35
- - Works offline (database cache)
36
- - More reliable than SDK-dependent checks
37
- - **SECURITY**: Server-side validation always enforced
38
-
39
- ## Usage
40
-
41
- ### 1. Implement Repository Interface
42
-
43
- First, implement the `ISubscriptionRepository` interface with your database:
44
-
45
- ```typescript
46
- import type { ISubscriptionRepository } from '@umituz/react-native-subscription';
47
- import type { SubscriptionStatus } from '@umituz/react-native-subscription';
48
-
49
- class MySubscriptionRepository implements ISubscriptionRepository {
50
- async getSubscriptionStatus(userId: string): Promise<SubscriptionStatus | null> {
51
- // Fetch from your database (Firebase, Supabase, etc.)
52
- const doc = await db.collection('users').doc(userId).get();
53
- return doc.data()?.subscription || null;
54
- }
55
-
56
- async updateSubscriptionStatus(
57
- userId: string,
58
- status: Partial<SubscriptionStatus>,
59
- ): Promise<SubscriptionStatus> {
60
- // Update in your database
61
- await db.collection('users').doc(userId).update({
62
- subscription: status,
63
- updatedAt: new Date(),
64
- });
65
- return await this.getSubscriptionStatus(userId) || createDefaultSubscriptionStatus();
66
- }
67
-
68
- isSubscriptionValid(status: SubscriptionStatus): boolean {
69
- if (!status.isPremium) return false;
70
- if (!status.expiresAt) return true; // Lifetime subscription
71
- return new Date(status.expiresAt) > new Date();
72
- }
73
- }
74
- ```
75
-
76
- ### 2. Initialize Subscription Service
77
-
78
- Initialize the service early in your app (e.g., in `App.tsx`):
79
-
80
- ```typescript
81
- import { initializeSubscriptionService } from '@umituz/react-native-subscription';
82
- import { MySubscriptionRepository } from './repositories/MySubscriptionRepository';
83
-
84
- // Initialize Subscription service
85
- initializeSubscriptionService({
86
- repository: new MySubscriptionRepository(),
87
- onStatusChanged: async (userId, status) => {
88
- // Optional: Sync to analytics, send notifications, etc.
89
- await analytics.logEvent('subscription_changed', {
90
- userId,
91
- isPremium: status.isPremium,
92
- });
93
- },
94
- onError: async (error, context) => {
95
- // Optional: Log errors to crash reporting
96
- await crashlytics.logError(error, context);
97
- },
98
- });
99
- ```
100
-
101
- ### 3. Use Subscription Hook in Components
102
-
103
- ```typescript
104
- import { useSubscription } from '@umituz/react-native-subscription';
105
- import { useAuth } from '@umituz/react-native-auth';
106
-
107
- function PremiumFeature() {
108
- const { user } = useAuth();
109
- const { status, isPremium, loading, loadStatus } = useSubscription();
110
-
111
- useEffect(() => {
112
- if (user?.uid) {
113
- loadStatus(user.uid);
114
- }
115
- }, [user?.uid, loadStatus]);
116
-
117
- if (loading) {
118
- return <LoadingSpinner />;
119
- }
120
-
121
- if (!isPremium) {
122
- return <UpgradePrompt />;
123
- }
124
-
125
- return <PremiumContent />;
126
- }
127
- ```
128
-
129
- ### 4. Activate/Deactivate Subscription
130
-
131
- ```typescript
132
- import { getSubscriptionService } from '@umituz/react-native-subscription';
133
-
134
- const service = getSubscriptionService();
135
-
136
- // Activate subscription (e.g., after purchase)
137
- await service.activateSubscription(
138
- userId,
139
- 'premium_monthly',
140
- '2024-12-31T23:59:59Z', // or null for lifetime
141
- );
142
-
143
- // Deactivate subscription
144
- await service.deactivateSubscription(userId);
145
- ```
146
-
147
- ## API
148
-
149
- ### Functions
150
-
151
- - `initializeSubscriptionService(config)`: Initialize Subscription service with configuration
152
- - `getSubscriptionService()`: Get Subscription service instance (throws if not initialized)
153
- - `resetSubscriptionService()`: Reset service instance (useful for testing)
154
-
155
- ### Hook
156
-
157
- - `useSubscription()`: React hook for subscription operations
158
-
159
- ### Types
160
-
161
- - `SubscriptionStatus`: Subscription status entity
162
- - `SubscriptionConfig`: Configuration interface
163
- - `ISubscriptionRepository`: Repository interface (must be implemented)
164
- - `UseSubscriptionResult`: Hook return type
165
-
166
- ### Errors
167
-
168
- - `SubscriptionError`: Base error class
169
- - `SubscriptionRepositoryError`: Repository errors
170
- - `SubscriptionValidationError`: Validation errors
171
- - `SubscriptionConfigurationError`: Configuration errors
172
-
173
- ## Security Best Practices
174
-
175
- 1. **Database-First**: Always check subscription status from your database, not SDK
176
- 2. **Server-Side Validation**: Always validate subscription expiration server-side
177
- 3. **Error Handling**: Always handle errors gracefully
178
- 4. **Repository Pattern**: Implement repository interface with your database
179
- 5. **Callbacks**: Use callbacks to sync subscription changes to analytics/notifications
180
-
181
- ## Integration with RevenueCat
182
-
183
- This package works seamlessly with `@umituz/react-native-revenuecat`:
184
-
185
- ```typescript
186
- import { initializeRevenueCatService } from '@umituz/react-native-revenuecat';
187
- import { getSubscriptionService } from '@umituz/react-native-subscription';
188
-
189
- initializeRevenueCatService({
190
- onPremiumStatusChanged: async (userId, isPremium, productId, expiresAt) => {
191
- const subscriptionService = getSubscriptionService();
192
- if (subscriptionService) {
193
- if (isPremium && productId) {
194
- await subscriptionService.activateSubscription(userId, productId, expiresAt || null);
195
- } else {
196
- await subscriptionService.deactivateSubscription(userId);
197
- }
198
- }
199
- },
200
- });
201
- ```
202
-
203
- ## License
204
-
205
- MIT
206
-
207
-
208
-
209
-
210
-
211
-
212
-
213
-
214
-
215
-
216
-
@@ -1,42 +0,0 @@
1
- /**
2
- * Subscription Repository Interface
3
- * Port for database operations
4
- *
5
- * SECURITY: Apps must implement this interface with their database.
6
- * Never expose database credentials or allow direct database access.
7
- */
8
-
9
- import type { SubscriptionStatus } from '../../domain/entities/SubscriptionStatus';
10
-
11
- export interface ISubscriptionRepository {
12
- /**
13
- * Get subscription status for a user
14
- * Returns null if user not found
15
- */
16
- getSubscriptionStatus(userId: string): Promise<SubscriptionStatus | null>;
17
-
18
- /**
19
- * Update subscription status for a user
20
- */
21
- updateSubscriptionStatus(
22
- userId: string,
23
- status: Partial<SubscriptionStatus>,
24
- ): Promise<SubscriptionStatus>;
25
-
26
- /**
27
- * Check if subscription is valid (not expired)
28
- * SECURITY: Always validate expiration server-side
29
- */
30
- isSubscriptionValid(status: SubscriptionStatus): boolean;
31
- }
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
@@ -1,51 +0,0 @@
1
- /**
2
- * Subscription Service Interface
3
- * Port for subscription operations
4
- */
5
-
6
- import type { SubscriptionStatus } from '../../domain/entities/SubscriptionStatus';
7
-
8
- export interface ISubscriptionService {
9
- /**
10
- * Get subscription status for a user
11
- */
12
- getSubscriptionStatus(userId: string): Promise<SubscriptionStatus>;
13
-
14
- /**
15
- * Check if user has active subscription
16
- */
17
- isPremium(userId: string): Promise<boolean>;
18
-
19
- /**
20
- * Activate subscription
21
- */
22
- activateSubscription(
23
- userId: string,
24
- productId: string,
25
- expiresAt: string | null,
26
- ): Promise<SubscriptionStatus>;
27
-
28
- /**
29
- * Deactivate subscription
30
- */
31
- deactivateSubscription(userId: string): Promise<SubscriptionStatus>;
32
-
33
- /**
34
- * Update subscription status
35
- */
36
- updateSubscriptionStatus(
37
- userId: string,
38
- updates: Partial<SubscriptionStatus>,
39
- ): Promise<SubscriptionStatus>;
40
- }
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
@@ -1,74 +0,0 @@
1
- /**
2
- * Subscription Status Entity
3
- * Represents subscription status for a user
4
- *
5
- * SECURITY: This is a read-only entity from database.
6
- * Never trust client-side subscription status - always validate server-side.
7
- */
8
-
9
- export interface SubscriptionStatus {
10
- /** Whether user has active subscription */
11
- isPremium: boolean;
12
-
13
- /** Subscription expiration date (ISO string) */
14
- expiresAt: string | null;
15
-
16
- /** Product ID of the subscription */
17
- productId: string | null;
18
-
19
- /** When subscription was purchased (ISO string) */
20
- purchasedAt: string | null;
21
-
22
- /** External service customer ID (e.g., RevenueCat customer ID) */
23
- customerId: string | null;
24
-
25
- /** Last sync time with external service (ISO string) */
26
- syncedAt: string | null;
27
- }
28
-
29
- /**
30
- * Create default subscription status (free user)
31
- */
32
- export function createDefaultSubscriptionStatus(): SubscriptionStatus {
33
- return {
34
- isPremium: false,
35
- expiresAt: null,
36
- productId: null,
37
- purchasedAt: null,
38
- customerId: null,
39
- syncedAt: null,
40
- };
41
- }
42
-
43
- /**
44
- * Check if subscription status is valid (not expired)
45
- * SECURITY: Always validate expiration server-side
46
- */
47
- export function isSubscriptionValid(status: SubscriptionStatus | null): boolean {
48
- if (!status || !status.isPremium) {
49
- return false;
50
- }
51
-
52
- if (!status.expiresAt) {
53
- // Lifetime subscription (no expiration)
54
- return true;
55
- }
56
-
57
- const expirationDate = new Date(status.expiresAt);
58
- const now = new Date();
59
-
60
- // Add 1 day buffer for clock skew and timezone issues
61
- const bufferMs = 24 * 60 * 60 * 1000;
62
- return expirationDate.getTime() > now.getTime() - bufferMs;
63
- }
64
-
65
-
66
-
67
-
68
-
69
-
70
-
71
-
72
-
73
-
74
-
@@ -1,43 +0,0 @@
1
- /**
2
- * Subscription Errors
3
- * Domain-specific errors for subscription operations
4
- */
5
-
6
- export class SubscriptionError extends Error {
7
- constructor(message: string, public readonly code: string) {
8
- super(message);
9
- this.name = 'SubscriptionError';
10
- }
11
- }
12
-
13
- export class SubscriptionRepositoryError extends SubscriptionError {
14
- constructor(message: string) {
15
- super(message, 'REPOSITORY_ERROR');
16
- this.name = 'SubscriptionRepositoryError';
17
- }
18
- }
19
-
20
- export class SubscriptionValidationError extends SubscriptionError {
21
- constructor(message: string) {
22
- super(message, 'VALIDATION_ERROR');
23
- this.name = 'SubscriptionValidationError';
24
- }
25
- }
26
-
27
- export class SubscriptionConfigurationError extends SubscriptionError {
28
- constructor(message: string) {
29
- super(message, 'CONFIGURATION_ERROR');
30
- this.name = 'SubscriptionConfigurationError';
31
- }
32
- }
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
@@ -1,22 +0,0 @@
1
- /**
2
- * Subscription Configuration Value Object
3
- * Configuration for subscription service
4
- */
5
-
6
- import type { SubscriptionStatus } from '../entities/SubscriptionStatus';
7
- import type { ISubscriptionRepository } from '../../application/ports/ISubscriptionRepository';
8
-
9
- export interface SubscriptionConfig {
10
- /** Repository implementation for database operations */
11
- repository: ISubscriptionRepository;
12
-
13
- /** Optional callback when subscription status changes */
14
- onStatusChanged?: (
15
- userId: string,
16
- status: SubscriptionStatus,
17
- ) => Promise<void> | void;
18
-
19
- /** Optional callback for error logging */
20
- onError?: (error: Error, context: string) => Promise<void> | void;
21
- }
22
-