In-App Purchases & Subscriptions
Relevant source files
The following files were used as context for generating this wiki page:
Introduction
The Happy project implements a multi-platform subscription management system primarily powered by the RevenueCat SDK. This system enables the application to offer premium features, manage entitlements, and synchronize purchase states across macOS, iOS, Android, and Web platforms. By utilizing RevenueCat, the project abstracts the complexities of StoreKit 2 (iOS/macOS) and Google Play Billing (Android) into a unified interface.
The system's primary goal is to manage the "Pro" entitlement, which grants users expanded access to session capabilities and premium features. The architecture is designed to be resilient, offering graceful fallbacks and conditional compilation for environments where the SDK may not be available.
Sources: apps/macos/CLAUDE.md:213-220, apps/web/vue/src/composables/usePurchases.ts:1-20
Multi-Platform Architecture
The subscription logic is distributed across several client applications, each tailored to its native environment while maintaining a shared logical flow for checking entitlements and presenting paywalls.
Platform Implementations
| Platform | Technology | Primary Components |
|---|---|---|
| macOS | Swift / RevenueCat SDK | PurchaseService.swift, PurchaseViewModel.swift, PaywallView.swift |
| iOS | Swift / RevenueCat SDK | PurchaseService.swift, StoreKit 2 integration |
| Android | Kotlin / Google Play Billing | Google Play Billing, subscription status UI |
| Web (React) | TypeScript / RevenueCat JS | revenueCat.ts, types.ts |
| Web (Vue) | TypeScript / Pinia | usePurchases.ts, purchases store |
Sources: apps/macos/CLAUDE.md:215-220, apps/mobile/android/CLAUDE.md:154, apps/mobile/ios/CLAUDE.md:46, apps/web/react/sources/sync/revenueCat/revenueCat.ts:1-20
Logic Flow for Entitlement Verification
The following diagram illustrates the general sequence of events when a user attempts to access a restricted feature requiring the "Pro" entitlement.
RevenueCat Integration (Web & React Native)
The project utilizes a standardized RevenueCatInterface to ensure consistency between React Native (native mobile) and Web implementations.
Data Structures
The system defines several core interfaces to handle subscription data:
- CustomerInfo: Contains active subscriptions, entitlements, and original user ID.
- Product: Represents the SKU with price, currency, and description.
- Offering: A collection of packages (e.g., Monthly, Annual) presented to the user.
Sources: apps/web/react/sources/sync/revenueCat/types.ts:3-40
Core SDK Functions
The RevenueCatNative class implements the standard interface for mobile platforms:
async purchaseStoreProduct(product: Product): Promise<PurchaseResult> {
const nativeProducts = await Purchases.getProducts([product.identifier]);
if (nativeProducts.length === 0) {
throw new AppError(ErrorCodes.PRODUCT_NOT_FOUND, `Product ${product.identifier} not found`);
}
const result = await Purchases.purchaseStoreProduct(nativeProducts[0]);
return { customerInfo: this.transformCustomerInfo(result.customerInfo) };
}
Sources: apps/web/react/sources/sync/revenueCat/revenueCat.ts:46-57
Native macOS & iOS Implementation
In the native Apple applications, the implementation is built using SwiftUI and Swift Concurrency (async/await).
Key Components
- PurchaseService: A singleton actor or class that wraps the RevenueCat SDK. It handles configuration with the
REVENUECAT_MACOS_KEYenvironment variable. - Entitlement Gating: Premium features are guarded by checking
customerInfo.entitlements.active. - Graceful Fallback: The code uses
#if canImport(RevenueCat)to allow the app to compile and run even in development environments where the SDK is not linked.
Sources: apps/macos/CLAUDE.md:223-238, apps/mobile/ios/CLAUDE.md:188-195
Vue.js Purchase Composable
The Vue implementation uses a specialized composable usePurchases.ts which integrates with a Pinia store to manage global purchase state.
State Management
The composable exposes reactive references for:
isPro: A boolean derived from the active "pro" entitlement.availablePackages: A list of products fetched from the current offering.isLoading: Tracks the status of network requests to RevenueCat.
Sources: apps/web/vue/src/composables/usePurchases.ts:55-80
Analytics Tracking
The purchase flow is instrumented with specific analytics events:
PURCHASE_STARTEDPURCHASE_COMPLETEDPURCHASE_CANCELLEDRESTORE_STARTED
Sources: apps/web/vue/src/composables/usePurchases.ts:165-175, apps/web/vue/src/composables/usePurchases.ts:220-225
Subscription Configuration
| Parameter | Type | Description |
|---|---|---|
apiKey | String | RevenueCat Public API Key (Platform specific) |
appUserID | String | Unique identifier for the user (often the account ID) |
useAmazon | Boolean | Whether to use Amazon Appstore (Android specific) |
requiredEntitlementIdentifier | String | Typically defaults to "pro" |
Sources: apps/web/react/sources/sync/revenueCat/types.ts:43-47, apps/web/react/sources/sync/revenueCat/revenueCat.ts:24-30
Conclusion
The In-App Purchases and Subscriptions system provides a robust, cross-platform framework for monetizing the Happy application. By centralizing entitlement logic through RevenueCat and providing platform-specific UI components like the PaywallView, the architecture ensures a seamless user experience. The system is highly resilient, featuring comprehensive error handling for product fetching and built-in analytics to monitor the health of the subscription funnel.
Sources: apps/macos/CLAUDE.md:213-238, apps/web/vue/src/composables/usePurchases.ts:335-350