@umituz/react-native-subscription 2.27.113 → 2.27.114
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 +1 -1
- package/src/domains/credits/application/CreditsInitializer.ts +27 -116
- package/src/domains/credits/application/credit-strategies/CreditAllocationOrchestrator.ts +1 -6
- package/src/domains/credits/application/creditDocumentHelpers.ts +58 -0
- package/src/domains/credits/application/creditOperationUtils.ts +154 -0
- package/src/domains/credits/presentation/useCredits.ts +1 -2
- package/src/domains/paywall/hooks/usePaywallActions.ts +0 -3
- package/src/domains/subscription/application/SubscriptionSyncService.ts +16 -20
- package/src/domains/subscription/core/RevenueCatError.ts +40 -31
- package/src/domains/subscription/infrastructure/handlers/PackageHandler.ts +0 -1
- package/src/domains/subscription/infrastructure/hooks/useRevenueCatTrialEligibility.ts +19 -85
- package/src/domains/subscription/infrastructure/managers/SubscriptionManager.ts +33 -75
- package/src/domains/subscription/infrastructure/managers/subscriptionManagerUtils.ts +57 -0
- package/src/domains/subscription/infrastructure/services/CustomerInfoListenerManager.ts +3 -12
- package/src/domains/subscription/infrastructure/services/PurchaseHandler.ts +0 -2
- package/src/domains/subscription/infrastructure/services/RevenueCatInitializer.ts +2 -4
- package/src/domains/subscription/infrastructure/services/RevenueCatService.ts +1 -5
- package/src/domains/subscription/infrastructure/utils/PremiumStatusSyncer.ts +3 -12
- package/src/domains/subscription/infrastructure/utils/authPurchaseState.ts +69 -0
- package/src/domains/subscription/infrastructure/utils/trialEligibilityUtils.ts +77 -0
- package/src/domains/subscription/presentation/components/feedback/FeedbackOption.tsx +139 -0
- package/src/domains/subscription/presentation/components/feedback/PaywallFeedbackModal.tsx +15 -70
- package/src/domains/subscription/presentation/components/feedback/paywallFeedbackStyles.ts +0 -92
- package/src/domains/subscription/presentation/stores/purchaseLoadingStore.ts +1 -18
- package/src/domains/subscription/presentation/useAuthAwarePurchase.ts +19 -69
- package/src/domains/subscription/presentation/usePremium.ts +2 -11
- package/src/domains/subscription/presentation/useSubscriptionStatus.ts +1 -6
- package/src/domains/trial/application/TrialService.ts +4 -8
- package/src/domains/wallet/infrastructure/repositories/TransactionRepository.ts +1 -1
- package/src/domains/wallet/infrastructure/services/ProductMetadataService.ts +0 -13
- package/src/domains/wallet/presentation/hooks/useProductMetadata.ts +0 -10
- package/src/domains/wallet/presentation/hooks/useTransactionHistory.ts +0 -8
- package/src/init/createSubscriptionInitModule.ts +1 -4
- package/src/presentation/hooks/feedback/useFeedbackSubmit.ts +0 -14
- package/src/shared/application/FeedbackService.ts +0 -21
- package/src/shared/infrastructure/SubscriptionEventBus.ts +1 -2
- package/src/shared/types/CommonTypes.ts +65 -0
- package/src/shared/utils/BaseError.ts +26 -0
- package/src/shared/utils/Logger.ts +14 -45
- package/src/shared/utils/Result.ts +16 -0
- package/src/shared/utils/SubscriptionError.ts +20 -30
- package/src/utils/packageTypeDetector.ts +0 -4
|
@@ -18,15 +18,12 @@ export function createSubscriptionInitModule(config: SubscriptionInitModuleConfi
|
|
|
18
18
|
try {
|
|
19
19
|
const apiKey = getApiKey();
|
|
20
20
|
if (!apiKey) {
|
|
21
|
-
if (__DEV__) console.log('[SubscriptionInit] No API key - skipping');
|
|
22
21
|
return true;
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
await initializeSubscription({ apiKey, ...subscriptionConfig });
|
|
26
|
-
if (__DEV__) console.log('[SubscriptionInit] Initialized');
|
|
27
25
|
return true;
|
|
28
|
-
} catch
|
|
29
|
-
if (__DEV__) console.error('[SubscriptionInit] Error:', error);
|
|
26
|
+
} catch {
|
|
30
27
|
return false;
|
|
31
28
|
}
|
|
32
29
|
},
|
|
@@ -27,13 +27,6 @@ export function usePaywallFeedbackSubmit(
|
|
|
27
27
|
|
|
28
28
|
const submit = useCallback(
|
|
29
29
|
async (reason: string) => {
|
|
30
|
-
if (__DEV__) {
|
|
31
|
-
console.log("[usePaywallFeedbackSubmit] Submitting:", {
|
|
32
|
-
userId: user?.uid?.slice(0, 8),
|
|
33
|
-
reason: reason.slice(0, 20),
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
|
|
37
30
|
try {
|
|
38
31
|
const result = await submitPaywallFeedback(
|
|
39
32
|
user?.uid ?? null,
|
|
@@ -81,13 +74,6 @@ export function useSettingsFeedbackSubmit(
|
|
|
81
74
|
|
|
82
75
|
const submit = useCallback(
|
|
83
76
|
async (data: SettingsFeedbackData) => {
|
|
84
|
-
if (__DEV__) {
|
|
85
|
-
console.log("[useSettingsFeedbackSubmit] Submitting:", {
|
|
86
|
-
userId: user?.uid?.slice(0, 8),
|
|
87
|
-
type: data.type,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
77
|
try {
|
|
92
78
|
const result = await submitSettingsFeedback(
|
|
93
79
|
user?.uid ?? null,
|
|
@@ -32,30 +32,16 @@ export async function submitFeedback(
|
|
|
32
32
|
const db = getFirestore();
|
|
33
33
|
|
|
34
34
|
if (!db) {
|
|
35
|
-
if (__DEV__) {
|
|
36
|
-
console.warn("[FeedbackService] Firestore not available");
|
|
37
|
-
}
|
|
38
35
|
return { success: false, error: new Error("Firestore not available") };
|
|
39
36
|
}
|
|
40
37
|
|
|
41
38
|
if (!data.userId) {
|
|
42
|
-
if (__DEV__) {
|
|
43
|
-
console.warn("[FeedbackService] User ID is required for feedback");
|
|
44
|
-
}
|
|
45
39
|
return { success: false, error: new Error("User ID is required") };
|
|
46
40
|
}
|
|
47
41
|
|
|
48
42
|
try {
|
|
49
|
-
if (__DEV__) {
|
|
50
|
-
console.log("[FeedbackService] Submitting feedback:", {
|
|
51
|
-
type: data.type,
|
|
52
|
-
userId: data.userId?.slice(0, 8),
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
43
|
const now = new Date().toISOString();
|
|
57
44
|
|
|
58
|
-
// Store under users/{userId}/feedback
|
|
59
45
|
const userDocRef = doc(db, "users", data.userId);
|
|
60
46
|
const feedbackCollectionRef = collection(userDocRef, "feedback");
|
|
61
47
|
|
|
@@ -70,15 +56,8 @@ export async function submitFeedback(
|
|
|
70
56
|
updatedAt: now,
|
|
71
57
|
});
|
|
72
58
|
|
|
73
|
-
if (__DEV__) {
|
|
74
|
-
console.log("[FeedbackService] Feedback submitted successfully");
|
|
75
|
-
}
|
|
76
|
-
|
|
77
59
|
return { success: true };
|
|
78
60
|
} catch (error) {
|
|
79
|
-
if (__DEV__) {
|
|
80
|
-
console.error("[FeedbackService] Submit error:", error);
|
|
81
|
-
}
|
|
82
61
|
return {
|
|
83
62
|
success: false,
|
|
84
63
|
error: error instanceof Error ? error : new Error("Unknown error"),
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common Type Definitions
|
|
3
|
+
* Shared types used across multiple domains
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Purchase result from any purchase operation
|
|
8
|
+
*/
|
|
9
|
+
export interface PurchaseResult {
|
|
10
|
+
success: boolean;
|
|
11
|
+
productId?: string;
|
|
12
|
+
error?: Error;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Transaction result for repository operations
|
|
17
|
+
*/
|
|
18
|
+
export interface TransactionResult<T = unknown> {
|
|
19
|
+
success: boolean;
|
|
20
|
+
data?: T;
|
|
21
|
+
error?: Error;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Subscription status information
|
|
26
|
+
*/
|
|
27
|
+
export interface SubscriptionStatusInfo {
|
|
28
|
+
isActive: boolean;
|
|
29
|
+
isExpired: boolean;
|
|
30
|
+
isTrial: boolean;
|
|
31
|
+
willRenew: boolean;
|
|
32
|
+
expirationDate?: Date;
|
|
33
|
+
productId?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Credits information
|
|
38
|
+
*/
|
|
39
|
+
export interface CreditsInfo {
|
|
40
|
+
credits: number;
|
|
41
|
+
creditLimit: number;
|
|
42
|
+
isPremium: boolean;
|
|
43
|
+
status: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Transaction metadata for any transaction type
|
|
48
|
+
*/
|
|
49
|
+
export interface TransactionMetadata {
|
|
50
|
+
productId: string;
|
|
51
|
+
amount: number;
|
|
52
|
+
currency?: string;
|
|
53
|
+
timestamp: Date;
|
|
54
|
+
type: 'purchase' | 'renewal' | 'restore' | 'credit_purchase';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Platform information
|
|
59
|
+
*/
|
|
60
|
+
export type Platform = 'ios' | 'android';
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Purchase source tracking
|
|
64
|
+
*/
|
|
65
|
+
export type PurchaseSource = 'settings' | 'paywall' | 'upgrade_prompt' | 'auto-execution' | 'manual';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Error Class
|
|
3
|
+
* Common base error for all domain errors
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export abstract class BaseError extends Error {
|
|
7
|
+
public readonly code: string;
|
|
8
|
+
public readonly cause?: Error;
|
|
9
|
+
|
|
10
|
+
constructor(message: string, code: string, cause?: Error) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = this.constructor.name;
|
|
13
|
+
this.code = code;
|
|
14
|
+
this.cause = cause;
|
|
15
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
toJSON() {
|
|
19
|
+
return {
|
|
20
|
+
name: this.name,
|
|
21
|
+
code: this.code,
|
|
22
|
+
message: this.message,
|
|
23
|
+
cause: this.cause?.message,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Subscription Logger
|
|
3
3
|
* Centralized logging utility for the subscription package.
|
|
4
|
-
* All logs are
|
|
4
|
+
* All logs are disabled - no logging.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
export type LogLevel = "debug" | "info" | "warn" | "error";
|
|
@@ -25,86 +25,55 @@ export type LogCategory = (typeof LOG_CATEGORY)[keyof typeof LOG_CATEGORY];
|
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Development-only logger that automatically respects __DEV__ flag.
|
|
28
|
-
* All methods are no-ops
|
|
28
|
+
* All methods are no-ops - logging is disabled.
|
|
29
29
|
*/
|
|
30
30
|
class SubscriptionLogger {
|
|
31
|
-
private enabled: boolean = true;
|
|
32
|
-
private categories: Set<LogCategory> = new Set();
|
|
33
|
-
|
|
34
31
|
/** Enable or disable all logging */
|
|
35
|
-
setEnabled(
|
|
36
|
-
this.enabled = enabled;
|
|
32
|
+
setEnabled(_enabled: boolean): void {
|
|
37
33
|
}
|
|
38
34
|
|
|
39
35
|
/** Enable logging for specific categories only */
|
|
40
|
-
setCategories(
|
|
41
|
-
this.categories = new Set(categories);
|
|
36
|
+
setCategories(_categories: LogCategory[]): void {
|
|
42
37
|
}
|
|
43
38
|
|
|
44
39
|
/** Clear category filter (log all categories) */
|
|
45
40
|
clearCategories(): void {
|
|
46
|
-
this.categories.clear();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
private shouldLog(category?: LogCategory): boolean {
|
|
50
|
-
if (typeof __DEV__ === "undefined" || !__DEV__) return false;
|
|
51
|
-
if (!this.enabled) return false;
|
|
52
|
-
if (this.categories.size > 0 && category && !this.categories.has(category)) return false;
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
private formatMessage(category: LogCategory | string, message: string): string {
|
|
57
|
-
return `[${category}] ${message}`;
|
|
58
41
|
}
|
|
59
42
|
|
|
60
|
-
debug(
|
|
61
|
-
if (!this.shouldLog(category)) return;
|
|
62
|
-
console.log(this.formatMessage(category, message), metadata ?? "");
|
|
43
|
+
debug(_category: LogCategory, _message: string, _metadata?: LogMetadata): void {
|
|
63
44
|
}
|
|
64
45
|
|
|
65
|
-
info(
|
|
66
|
-
if (!this.shouldLog(category)) return;
|
|
67
|
-
console.log(this.formatMessage(category, message), metadata ?? "");
|
|
46
|
+
info(_category: LogCategory, _message: string, _metadata?: LogMetadata): void {
|
|
68
47
|
}
|
|
69
48
|
|
|
70
|
-
warn(
|
|
71
|
-
if (!this.shouldLog(category)) return;
|
|
72
|
-
console.warn(this.formatMessage(category, message), metadata ?? "");
|
|
49
|
+
warn(_category: LogCategory, _message: string, _metadata?: LogMetadata): void {
|
|
73
50
|
}
|
|
74
51
|
|
|
75
|
-
error(
|
|
76
|
-
if (!this.shouldLog(category)) return;
|
|
77
|
-
console.error(this.formatMessage(category, message), { error, ...metadata });
|
|
52
|
+
error(_category: LogCategory, _message: string, _error?: unknown, _metadata?: LogMetadata): void {
|
|
78
53
|
}
|
|
79
54
|
|
|
80
55
|
/** Log purchase flow events */
|
|
81
|
-
purchase(
|
|
82
|
-
this.debug(LOG_CATEGORY.PURCHASE, message, metadata);
|
|
56
|
+
purchase(_message: string, _metadata?: LogMetadata): void {
|
|
83
57
|
}
|
|
84
58
|
|
|
85
59
|
/** Log credits-related events */
|
|
86
|
-
credits(
|
|
87
|
-
this.debug(LOG_CATEGORY.CREDITS, message, metadata);
|
|
60
|
+
credits(_message: string, _metadata?: LogMetadata): void {
|
|
88
61
|
}
|
|
89
62
|
|
|
90
63
|
/** Log trial-related events */
|
|
91
|
-
trial(
|
|
92
|
-
this.debug(LOG_CATEGORY.TRIAL, message, metadata);
|
|
64
|
+
trial(_message: string, _metadata?: LogMetadata): void {
|
|
93
65
|
}
|
|
94
66
|
|
|
95
67
|
/** Log RevenueCat SDK events */
|
|
96
|
-
revenueCat(
|
|
97
|
-
this.debug(LOG_CATEGORY.REVENUECAT, message, metadata);
|
|
68
|
+
revenueCat(_message: string, _metadata?: LogMetadata): void {
|
|
98
69
|
}
|
|
99
70
|
|
|
100
71
|
/** Log feature gate events */
|
|
101
|
-
featureGate(
|
|
102
|
-
this.debug(LOG_CATEGORY.FEATURE_GATE, message, metadata);
|
|
72
|
+
featureGate(_message: string, _metadata?: LogMetadata): void {
|
|
103
73
|
}
|
|
104
74
|
|
|
105
75
|
/** Log sync operations */
|
|
106
|
-
sync(
|
|
107
|
-
this.debug(LOG_CATEGORY.SYNC, message, metadata);
|
|
76
|
+
sync(_message: string, _metadata?: LogMetadata): void {
|
|
108
77
|
}
|
|
109
78
|
}
|
|
110
79
|
|
|
@@ -114,6 +114,22 @@ export function tryCatchSync<T>(
|
|
|
114
114
|
return failure(mappedError);
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
|
+
export function tryCatchSync<T>(
|
|
118
|
+
fn: () => T,
|
|
119
|
+
errorMapper?: (error: unknown) => Error
|
|
120
|
+
): Result<T, Error> {
|
|
121
|
+
try {
|
|
122
|
+
const data = fn();
|
|
123
|
+
return success(data);
|
|
124
|
+
} catch {
|
|
125
|
+
const mappedError = errorMapper
|
|
126
|
+
? errorMapper(error)
|
|
127
|
+
: error instanceof Error
|
|
128
|
+
? error
|
|
129
|
+
: new Error(String(error));
|
|
130
|
+
return failure(mappedError);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
117
133
|
|
|
118
134
|
/** Combine multiple results into one */
|
|
119
135
|
export function combine<T, E>(results: Result<T, E>[]): Result<T[], E> {
|
|
@@ -3,55 +3,45 @@
|
|
|
3
3
|
* Custom error class for subscription-related errors
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
public readonly code: string;
|
|
6
|
+
import { BaseError } from "./BaseError";
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
export class SubscriptionError extends BaseError {
|
|
9
|
+
constructor(message: string, code: string = 'SUBSCRIPTION_ERROR', cause?: Error) {
|
|
10
|
+
super(message, code, cause);
|
|
11
11
|
this.name = 'SubscriptionError';
|
|
12
|
-
this.code = code;
|
|
13
|
-
Object.setPrototypeOf(this, SubscriptionError.prototype);
|
|
14
12
|
}
|
|
15
13
|
|
|
16
|
-
static notFound(message: string = 'Subscription not found'): SubscriptionError {
|
|
17
|
-
return new SubscriptionError(message, 'SUBSCRIPTION_NOT_FOUND');
|
|
14
|
+
static notFound(message: string = 'Subscription not found', cause?: Error): SubscriptionError {
|
|
15
|
+
return new SubscriptionError(message, 'SUBSCRIPTION_NOT_FOUND', cause);
|
|
18
16
|
}
|
|
19
17
|
|
|
20
|
-
static expired(message: string = 'Subscription has expired'): SubscriptionError {
|
|
21
|
-
return new SubscriptionError(message, 'SUBSCRIPTION_EXPIRED');
|
|
18
|
+
static expired(message: string = 'Subscription has expired', cause?: Error): SubscriptionError {
|
|
19
|
+
return new SubscriptionError(message, 'SUBSCRIPTION_EXPIRED', cause);
|
|
22
20
|
}
|
|
23
21
|
|
|
24
|
-
static purchaseFailed(message: string = 'Purchase failed'): SubscriptionError {
|
|
25
|
-
return new SubscriptionError(message, 'PURCHASE_FAILED');
|
|
22
|
+
static purchaseFailed(message: string = 'Purchase failed', cause?: Error): SubscriptionError {
|
|
23
|
+
return new SubscriptionError(message, 'PURCHASE_FAILED', cause);
|
|
26
24
|
}
|
|
27
25
|
|
|
28
|
-
static restoreFailed(message: string = 'Restore failed'): SubscriptionError {
|
|
29
|
-
return new SubscriptionError(message, 'RESTORE_FAILED');
|
|
26
|
+
static restoreFailed(message: string = 'Restore failed', cause?: Error): SubscriptionError {
|
|
27
|
+
return new SubscriptionError(message, 'RESTORE_FAILED', cause);
|
|
30
28
|
}
|
|
31
29
|
|
|
32
|
-
static networkError(message: string = 'Network error'): SubscriptionError {
|
|
33
|
-
return new SubscriptionError(message, 'NETWORK_ERROR');
|
|
30
|
+
static networkError(message: string = 'Network error', cause?: Error): SubscriptionError {
|
|
31
|
+
return new SubscriptionError(message, 'NETWORK_ERROR', cause);
|
|
34
32
|
}
|
|
35
33
|
}
|
|
36
34
|
|
|
37
|
-
export class SubscriptionRepositoryError extends
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
constructor(message: string, code: string = 'REPOSITORY_ERROR') {
|
|
41
|
-
super(message);
|
|
35
|
+
export class SubscriptionRepositoryError extends BaseError {
|
|
36
|
+
constructor(message: string, code: string = 'REPOSITORY_ERROR', cause?: Error) {
|
|
37
|
+
super(message, code, cause);
|
|
42
38
|
this.name = 'SubscriptionRepositoryError';
|
|
43
|
-
this.code = code;
|
|
44
|
-
Object.setPrototypeOf(this, SubscriptionRepositoryError.prototype);
|
|
45
39
|
}
|
|
46
40
|
}
|
|
47
41
|
|
|
48
|
-
export class SubscriptionValidationError extends
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
constructor(message: string, code: string = 'VALIDATION_ERROR') {
|
|
52
|
-
super(message);
|
|
42
|
+
export class SubscriptionValidationError extends BaseError {
|
|
43
|
+
constructor(message: string, code: string = 'VALIDATION_ERROR', cause?: Error) {
|
|
44
|
+
super(message, code, cause);
|
|
53
45
|
this.name = 'SubscriptionValidationError';
|
|
54
|
-
this.code = code;
|
|
55
|
-
Object.setPrototypeOf(this, SubscriptionValidationError.prototype);
|
|
56
46
|
}
|
|
57
47
|
}
|
|
@@ -54,9 +54,5 @@ export function detectPackageType(productIdentifier: string): SubscriptionPackag
|
|
|
54
54
|
return PACKAGE_TYPE.LIFETIME;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
if (__DEV__ && productIdentifier !== 'no_subscription') {
|
|
58
|
-
console.warn("[PackageTypeDetector] Unknown package type for:", productIdentifier);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
57
|
return PACKAGE_TYPE.UNKNOWN;
|
|
62
58
|
}
|