import { Platform } from "react-native"; import Purchases, { LOG_LEVEL, PURCHASES_ERROR_CODE, type CustomerInfo, type PurchasesError, type PurchasesOffering, type PurchasesPackage, } from "react-native-purchases"; const revenueCatAppleApiKey = process.env.EXPO_PUBLIC_REVENUECAT_APPLE_API_KEY?.trim() ?? ""; let configuredAppUserId: string | null = null; export type BillingPackagePlan = "solo" | "annual"; export type BillingPackageOption = { description: string | null; id: string; package: PurchasesPackage; plan: BillingPackagePlan; price: string; title: string; }; const isIosPurchasePlatform = () => Platform.OS === "ios"; const inferBillingPackagePlan = (input: string): BillingPackagePlan | null => { const normalized = input.toLowerCase(); if (normalized.includes("annual") || normalized.includes("founder")) { return "annual"; } if (normalized.includes("solo")) { return "solo"; } return null; }; export const canUseInAppPurchases = () => isIosPurchasePlatform() && revenueCatAppleApiKey.length > 0; export const ensurePurchasesConfigured = async (appUserID: string) => { if (!canUseInAppPurchases()) { return false; } if (__DEV__) { await Purchases.setLogLevel(LOG_LEVEL.DEBUG); } const isConfigured = await Purchases.isConfigured(); if (!isConfigured) { Purchases.configure({ apiKey: revenueCatAppleApiKey, appUserID, }); configuredAppUserId = appUserID; return true; } if (configuredAppUserId !== appUserID) { await Purchases.logIn(appUserID); configuredAppUserId = appUserID; } return true; }; const toBillingPackageOption = (pkg: PurchasesPackage): BillingPackageOption | null => { const plan = inferBillingPackagePlan(pkg.product.identifier) ?? inferBillingPackagePlan(pkg.identifier); if (!plan) { return null; } return { description: pkg.product.description ?? null, id: pkg.product.identifier, package: pkg, plan, price: pkg.product.priceString, title: pkg.product.title, }; }; export const getCurrentOffering = async (appUserID: string): Promise => { const isConfigured = await ensurePurchasesConfigured(appUserID); if (!isConfigured) { return null; } const offerings = await Purchases.getOfferings(); return offerings.current; }; export const getBillingPackageOptions = async (appUserID: string) => { const offering = await getCurrentOffering(appUserID); if (!offering) { return []; } return offering.availablePackages .map(toBillingPackageOption) .filter((pkg): pkg is BillingPackageOption => Boolean(pkg)); }; export const purchaseBillingPackage = async (appUserID: string, pkg: PurchasesPackage) => { await ensurePurchasesConfigured(appUserID); return Purchases.purchasePackage(pkg); }; export const restoreBillingPurchases = async (appUserID: string): Promise => { await ensurePurchasesConfigured(appUserID); return Purchases.restorePurchases(); }; export const isPurchaseCancelledError = (error: unknown) => (error as PurchasesError | undefined)?.code === PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR;