import type { CustomerInfo, PurchasesOfferings, PurchasesStoreProduct, } from 'react-native-purchases'; export type MembershipPlanType = 'weekly' | 'quarterly' | 'lifetime'; export interface MembershipPlanMeta { id: string; fallbackTitle: string; subtitle: string; type: MembershipPlanType; recommended?: boolean; tag?: string; originalPrice?: string; } export const MEMBERSHIP_PLAN_META: MembershipPlanMeta[] = [ { id: 'com.anonymous.digitalpilates.membership.lifetime', fallbackTitle: '终身会员', subtitle: '一次投入,终身健康陪伴', type: 'lifetime', recommended: true, tag: '限时特价', originalPrice: '¥898', }, { id: 'com.anonymous.digitalpilates.membership.quarter', fallbackTitle: '季度会员', subtitle: '3个月蜕变计划,见证身材变化', type: 'quarterly', originalPrice: '¥598', }, { id: 'com.anonymous.digitalpilates.membership.weekly', fallbackTitle: '周会员', subtitle: '7天体验,开启健康第一步', type: 'weekly', originalPrice: '¥128', }, ]; const MEMBERSHIP_PLAN_META_MAP = new Map( MEMBERSHIP_PLAN_META.map((plan) => [plan.id, plan]), ); export interface MembershipPlanSummary { id: string; title: string; description: string; price: number; priceString: string; type: MembershipPlanType | 'unknown'; fallbackTitle?: string; subtitle?: string; originalPrice?: string; tag?: string; recommended?: boolean; } export const getPlanMetaById = (id: string): MembershipPlanMeta | undefined => MEMBERSHIP_PLAN_META_MAP.get(id); export const resolvePlanDisplayName = ( product: PurchasesStoreProduct | null | undefined, fallbackPlan?: MembershipPlanMeta, defaultName = 'VIP 会员', ): string => { const productTitle = product?.title?.trim(); if (productTitle) { return productTitle; } if (fallbackPlan?.fallbackTitle) { return fallbackPlan.fallbackTitle; } return defaultName; }; export const extractMembershipProductsFromOfferings = ( offerings: PurchasesOfferings, ): PurchasesStoreProduct[] => { const packages = offerings.current?.availablePackages ?? []; const matchedProducts = packages .map((pkg) => pkg.product) .filter((product) => MEMBERSHIP_PLAN_META_MAP.has(product.identifier)); if (matchedProducts.length > 0) { return MEMBERSHIP_PLAN_META .map((plan) => matchedProducts.find((product) => product.identifier === plan.id), ) .filter((product): product is PurchasesStoreProduct => Boolean(product)); } return packages.map((pkg) => pkg.product); }; export const summarizeProducts = ( products: PurchasesStoreProduct[], ): MembershipPlanSummary[] => products.map((product) => { const meta = getPlanMetaById(product.identifier); return { id: product.identifier, title: product.title, description: product.description, price: product.price, priceString: product.priceString, type: meta?.type ?? 'unknown', fallbackTitle: meta?.fallbackTitle, subtitle: meta?.subtitle, originalPrice: meta?.originalPrice, tag: meta?.tag, recommended: meta?.recommended ?? false, }; }); export const getActiveSubscriptionIds = ( customerInfo: CustomerInfo, ): string[] => { const activeSubscriptions = customerInfo.activeSubscriptions as unknown; if (Array.isArray(activeSubscriptions)) { return activeSubscriptions; } if (activeSubscriptions && typeof activeSubscriptions === 'object') { return Object.values(activeSubscriptions as Record).filter( (value): value is string => typeof value === 'string', ); } return []; }; export const collectActiveProductIdentifiers = ( customerInfo: CustomerInfo, ): string[] => { const collected = new Set(); Object.values(customerInfo.entitlements.active).forEach((entitlement) => { if (entitlement?.productIdentifier) { collected.add(entitlement.productIdentifier); } }); customerInfo.nonSubscriptionTransactions.forEach((transaction) => { if (transaction?.productIdentifier) { collected.add(transaction.productIdentifier); } }); getActiveSubscriptionIds(customerInfo).forEach((identifier) => { if (identifier) { collected.add(identifier); } }); return Array.from(collected); }; export const hasActiveMembership = (customerInfo: CustomerInfo): boolean => { if (Object.keys(customerInfo.entitlements.active).length > 0) { return true; } if (customerInfo.nonSubscriptionTransactions.length > 0) { return true; } return getActiveSubscriptionIds(customerInfo).length > 0; }; export const pickActiveProductId = ( customerInfo: CustomerInfo, availableProducts: PurchasesStoreProduct[], ): { activeProductId: string | null; activeProductIds: string[]; } => { const activeProductIds = collectActiveProductIdentifiers(customerInfo); if (activeProductIds.length === 0) { return { activeProductId: null, activeProductIds }; } const availableIds = new Set( availableProducts.map((product) => product.identifier), ); for (const plan of MEMBERSHIP_PLAN_META) { if ( activeProductIds.includes(plan.id) && availableIds.has(plan.id) ) { return { activeProductId: plan.id, activeProductIds }; } } for (const identifier of activeProductIds) { if (availableIds.has(identifier)) { return { activeProductId: identifier, activeProductIds }; } } return { activeProductId: null, activeProductIds }; };