@umituz/react-native-subscription 2.27.11 → 2.27.13

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.27.11",
3
+ "version": "2.27.13",
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
@@ -96,3 +96,18 @@ export * from "./utils";
96
96
 
97
97
  // RevenueCat
98
98
  export * from "./revenuecat";
99
+
100
+ // App Service Helpers (for configureAppServices)
101
+ export {
102
+ createCreditService,
103
+ createPaywallService,
104
+ type CreditServiceConfig,
105
+ type ICreditService,
106
+ type IPaywallService,
107
+ } from "./infrastructure/services/app-service-helpers";
108
+
109
+ // Init Module Factory
110
+ export {
111
+ createSubscriptionInitModule,
112
+ type SubscriptionInitModuleConfig,
113
+ } from './init';
@@ -0,0 +1,115 @@
1
+ /**
2
+ * App Service Helpers
3
+ * Creates ready-to-use service implementations for configureAppServices
4
+ */
5
+
6
+ import {
7
+ getCreditsRepository,
8
+ getRevenueCatService,
9
+ getPremiumEntitlement,
10
+ } from "./SubscriptionInitializer";
11
+ import { creditsQueryKeys } from "../../presentation/hooks/useCredits";
12
+ import { paywallControl } from "../../domains/paywall/hooks/usePaywall";
13
+ import {
14
+ getGlobalQueryClient,
15
+ hasGlobalQueryClient,
16
+ } from "@umituz/react-native-design-system";
17
+ import {
18
+ useAuthStore,
19
+ selectUserId,
20
+ } from "@umituz/react-native-auth";
21
+
22
+ declare const __DEV__: boolean;
23
+
24
+ export interface CreditServiceConfig {
25
+ entitlementId: string;
26
+ }
27
+
28
+ export interface ICreditService {
29
+ checkCredits: (cost: number) => Promise<boolean>;
30
+ deductCredits: (cost: number) => Promise<void>;
31
+ refundCredits: (amount: number, error?: unknown) => Promise<void>;
32
+ calculateCost: (capability: string, metadata?: Record<string, unknown>) => number;
33
+ }
34
+
35
+ export interface IPaywallService {
36
+ showPaywall: (requiredCredits: number) => void;
37
+ }
38
+
39
+ const checkPremiumStatus = async (entitlementId: string): Promise<boolean> => {
40
+ try {
41
+ const rcService = getRevenueCatService();
42
+ if (!rcService) return false;
43
+
44
+ const customerInfo = await rcService.getCustomerInfo();
45
+ if (!customerInfo) return false;
46
+
47
+ return !!getPremiumEntitlement(customerInfo, entitlementId);
48
+ } catch {
49
+ return false;
50
+ }
51
+ };
52
+
53
+ /**
54
+ * Creates a credit service implementation
55
+ */
56
+ export function createCreditService(config: CreditServiceConfig): ICreditService {
57
+ const { entitlementId } = config;
58
+
59
+ return {
60
+ checkCredits: async (cost: number): Promise<boolean> => {
61
+ const userId = selectUserId(useAuthStore.getState());
62
+ if (!userId) return false;
63
+
64
+ // Premium users bypass credit check
65
+ if (await checkPremiumStatus(entitlementId)) return true;
66
+
67
+ try {
68
+ const repository = getCreditsRepository();
69
+ const result = await repository.getCredits(userId);
70
+ if (!result.success || !result.data) return false;
71
+ return (result.data.credits ?? 0) >= cost;
72
+ } catch {
73
+ return false;
74
+ }
75
+ },
76
+
77
+ deductCredits: async (cost: number): Promise<void> => {
78
+ const userId = selectUserId(useAuthStore.getState());
79
+ if (!userId) return;
80
+
81
+ // Premium users don't consume credits
82
+ if (await checkPremiumStatus(entitlementId)) return;
83
+
84
+ try {
85
+ const repository = getCreditsRepository();
86
+ await repository.deductCredit(userId, cost);
87
+
88
+ if (hasGlobalQueryClient()) {
89
+ getGlobalQueryClient().invalidateQueries({
90
+ queryKey: creditsQueryKeys.user(userId),
91
+ });
92
+ }
93
+ } catch (error) {
94
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
95
+ console.error("[CreditService] Deduct error:", error);
96
+ }
97
+ }
98
+ },
99
+
100
+ refundCredits: async (): Promise<void> => {
101
+ // No-op for now
102
+ },
103
+
104
+ calculateCost: (): number => 1,
105
+ };
106
+ }
107
+
108
+ /**
109
+ * Creates a paywall service implementation
110
+ */
111
+ export function createPaywallService(): IPaywallService {
112
+ return {
113
+ showPaywall: () => paywallControl.open(),
114
+ };
115
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Subscription Init Module Factory
3
+ * Creates a ready-to-use InitModule for app initialization
4
+ */
5
+
6
+ import { initializeSubscription, type SubscriptionInitConfig } from '../infrastructure/services/SubscriptionInitializer';
7
+
8
+ declare const __DEV__: boolean;
9
+
10
+ /**
11
+ * InitModule interface (from @umituz/react-native-design-system)
12
+ */
13
+ export interface InitModule {
14
+ name: string;
15
+ init: () => Promise<boolean>;
16
+ critical?: boolean;
17
+ dependsOn?: string[];
18
+ }
19
+
20
+ export interface SubscriptionInitModuleConfig extends Omit<SubscriptionInitConfig, 'apiKey'> {
21
+ /**
22
+ * RevenueCat API key getter function
23
+ * Returns the API key or undefined if not available
24
+ */
25
+ getApiKey: () => string | undefined;
26
+
27
+ /**
28
+ * Optional RevenueCat test store key getter
29
+ */
30
+ getTestStoreKey?: () => string | undefined;
31
+
32
+ /**
33
+ * Whether this module is critical for app startup
34
+ * @default true
35
+ */
36
+ critical?: boolean;
37
+
38
+ /**
39
+ * Module dependencies
40
+ * @default ["auth"]
41
+ */
42
+ dependsOn?: string[];
43
+ }
44
+
45
+ /**
46
+ * Creates a Subscription initialization module for use with createAppInitializer
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * import { createAppInitializer } from "@umituz/react-native-design-system";
51
+ * import { createFirebaseInitModule } from "@umituz/react-native-firebase";
52
+ * import { createAuthInitModule } from "@umituz/react-native-auth";
53
+ * import { createSubscriptionInitModule } from "@umituz/react-native-subscription";
54
+ *
55
+ * export const initializeApp = createAppInitializer({
56
+ * modules: [
57
+ * createFirebaseInitModule(),
58
+ * createAuthInitModule({ userCollection: "users" }),
59
+ * createSubscriptionInitModule({
60
+ * getApiKey: () => getRevenueCatApiKey(),
61
+ * entitlementId: "premium",
62
+ * credits: {
63
+ * collectionName: "credits",
64
+ * creditLimit: 500,
65
+ * enableFreeCredits: true,
66
+ * freeCredits: 1,
67
+ * },
68
+ * }),
69
+ * ],
70
+ * });
71
+ * ```
72
+ */
73
+ export function createSubscriptionInitModule(
74
+ config: SubscriptionInitModuleConfig
75
+ ): InitModule {
76
+ const {
77
+ getApiKey,
78
+ getTestStoreKey,
79
+ critical = true,
80
+ dependsOn = ['auth'],
81
+ ...subscriptionConfig
82
+ } = config;
83
+
84
+ return {
85
+ name: 'subscription',
86
+ critical,
87
+ dependsOn,
88
+ init: async () => {
89
+ try {
90
+ const apiKey = getApiKey();
91
+
92
+ if (!apiKey) {
93
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
94
+ console.log('[createSubscriptionInitModule] No API key - skipping');
95
+ }
96
+ return true; // Not an error, just skip
97
+ }
98
+
99
+ const testStoreKey = getTestStoreKey?.();
100
+
101
+ await initializeSubscription({
102
+ apiKey,
103
+ testStoreKey,
104
+ ...subscriptionConfig,
105
+ });
106
+
107
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
108
+ console.log('[createSubscriptionInitModule] Subscription initialized');
109
+ }
110
+
111
+ return true;
112
+ } catch (error) {
113
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
114
+ console.error('[createSubscriptionInitModule] Error:', error);
115
+ }
116
+ // Continue on error - subscription is not critical for app launch
117
+ return true;
118
+ }
119
+ },
120
+ };
121
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Subscription Init Module
3
+ * Provides factory for creating app initialization modules
4
+ */
5
+
6
+ export {
7
+ createSubscriptionInitModule,
8
+ type SubscriptionInitModuleConfig,
9
+ type InitModule,
10
+ } from './createSubscriptionInitModule';