From 84624e50cfdcae4e949c1800b7d8fcd985ee4025 Mon Sep 17 00:00:00 2001 From: Timo Knuth Date: Mon, 6 Jul 2026 13:47:56 +0200 Subject: [PATCH] fix(app): free-tier billing management view, loading guard, dead paywall code cleanup Co-Authored-By: Claude Fable 5 --- app/profile/billing.tsx | 331 ++++------------------------------------ 1 file changed, 31 insertions(+), 300 deletions(-) diff --git a/app/profile/billing.tsx b/app/profile/billing.tsx index 547ccd1..bdc99e6 100644 --- a/app/profile/billing.tsx +++ b/app/profile/billing.tsx @@ -112,34 +112,11 @@ const getBillingCopy = (language: Language) => { manageSubscription: 'Abo verwalten', subscriptionTitle: 'Abos', subscriptionHint: 'Wähle ein Abo und schalte stärkere KI-Scans sowie mehr Credits frei.', - paywallTitle: 'Vollstaendige Diagnose freischalten', - paywallHint: 'Starte Pro fuer echte GPT-5.4 Scans, deinen 7-Tage-Rettungsplan und 100 Credits fuer AI-Scans und Follow-ups.', startTrial: '7 Tage kostenlos testen', - hardPaywallTitle: 'Pflanze scannen. Richtig pflegen.', - hardPaywallHint: 'GreenLens erkennt Art, Pflegebedarf und mögliche Probleme in Sekunden.', - identifiedChip: 'Monstera erkannt', - benefitUnlimitedScans: 'Unbegrenzte Scans', - benefitCarePlan: 'Pflegeplan + Erinnerungen', - weeklyPlanName: 'Monatlich', - yearlyPlanName: 'Jährlich', - weeklyPriceFallback: '5,99 € / Monat', - yearlyPriceFallback: '39,99 € / Jahr', - bestOffer: 'Bestes Angebot', - noPaymentToday: 'Keine Zahlung heute · jederzeit kündbar', - monthlyFooter: 'Monatlich kündbar · kein Gratis-Test', - monthlyCta: 'Monatlich starten', - yearlyCta: 'Jaehrlich starten', - yearlyTrialBadge: '7 TAGE GRATIS', - monthlyBadge: 'FLEXIBEL', - yearlySubline: 'Danach 39,99 EUR/Jahr. Jederzeit kuendbar.', - monthlySubline: '4,99 EUR/Monat. Ohne Jahresbindung.', - saveLabel: 'Bester Wert', expoGoPurchaseTitle: 'Kauf nur im Dev Build', expoGoPurchaseMessage: 'Expo Go kann keine Apple- oder RevenueCat-Kaufmaske anzeigen. Im Development Build oder TestFlight erscheint hier der echte 7-Tage-Trial. Fuer lokale Tests kannst du Pro simulieren.', expoGoSimulate: 'Pro simulieren', continueWithoutPro: 'Ohne Pro fortfahren', - perYear: '/ Jahr', - perMonth: '/ Monat', freePlanName: 'Free', freePlanPrice: '0 EUR / Monat', proPlanName: 'Pro', @@ -200,34 +177,11 @@ const getBillingCopy = (language: Language) => { manageSubscription: 'Administrar Suscripción', subscriptionTitle: 'Suscripciones', subscriptionHint: 'Elige un plan y desbloquea escaneos con IA más potentes y más créditos.', - paywallTitle: 'Desbloquear diagnostico completo', - paywallHint: 'Inicia Pro para escaneos reales con GPT-5.4, tu plan de rescate de 7 dias y 100 creditos para escaneos IA y seguimientos.', startTrial: 'Probar 7 dias gratis', - hardPaywallTitle: 'Escanea plantas. Cuídalas bien.', - hardPaywallHint: 'GreenLens identifica la especie, los cuidados y posibles problemas en segundos.', - identifiedChip: 'Monstera identificada', - benefitUnlimitedScans: 'Escaneos ilimitados', - benefitCarePlan: 'Plan de cuidado + recordatorios', - weeklyPlanName: 'Mensual', - yearlyPlanName: 'Anual', - weeklyPriceFallback: '5,99 € / mes', - yearlyPriceFallback: '39,99 € / año', - bestOffer: 'Mejor oferta', - noPaymentToday: 'Sin pago hoy · cancela cuando quieras', - monthlyFooter: 'Cancela mensualmente · sin prueba gratis', - monthlyCta: 'Empezar mensual', - yearlyCta: 'Empezar anual', - yearlyTrialBadge: '7 DIAS GRATIS', - monthlyBadge: 'FLEXIBLE', - yearlySubline: 'Despues 39.99 EUR/ano. Cancela cuando quieras.', - monthlySubline: '4.99 EUR/mes. Sin compromiso anual.', - saveLabel: 'Mejor valor', expoGoPurchaseTitle: 'Compra solo en Dev Build', expoGoPurchaseMessage: 'Expo Go no puede mostrar la compra nativa de Apple o RevenueCat. En Development Build o TestFlight aparecera el trial real de 7 dias. Para pruebas locales puedes simular Pro.', expoGoSimulate: 'Simular Pro', continueWithoutPro: 'Continuar sin Pro', - perYear: '/ ano', - perMonth: '/ mes', freePlanName: 'Gratis', freePlanPrice: '0 EUR / Mes', proPlanName: 'Pro', @@ -288,34 +242,11 @@ const getBillingCopy = (language: Language) => { manageSubscription: 'Manage Subscription', subscriptionTitle: 'Subscriptions', subscriptionHint: 'Choose a plan to unlock stronger AI scans and more credits.', - paywallTitle: 'Unlock the full diagnosis', - paywallHint: 'Start Pro for real GPT-5.4 scans, your 7-day rescue plan, and 100 credits for AI scans and follow-ups.', startTrial: 'Start 7-day free trial', - hardPaywallTitle: 'Scan plants. Care for them right.', - hardPaywallHint: 'GreenLens identifies the species, care needs, and possible problems in seconds.', - identifiedChip: 'Monstera identified', - benefitUnlimitedScans: 'Unlimited scans', - benefitCarePlan: 'Care plan + reminders', - weeklyPlanName: 'Monthly', - yearlyPlanName: 'Yearly', - weeklyPriceFallback: '$4.99 / month', - yearlyPriceFallback: '$39.99 / year', - bestOffer: 'Best offer', - noPaymentToday: 'No payment today · cancel anytime', - monthlyFooter: 'Cancel monthly · no free trial', - monthlyCta: 'Start monthly', - yearlyCta: 'Start yearly', - yearlyTrialBadge: '7 DAYS FREE', - monthlyBadge: 'FLEXIBLE', - yearlySubline: 'Then EUR 39.99/year. Cancel anytime.', - monthlySubline: 'EUR 4.99/month. No annual commitment.', - saveLabel: 'Best value', expoGoPurchaseTitle: 'Purchase requires a dev build', expoGoPurchaseMessage: 'Expo Go cannot show the native Apple or RevenueCat purchase sheet. In a Development Build or TestFlight this opens the real 7-day trial. For local testing you can simulate Pro.', expoGoSimulate: 'Simulate Pro', continueWithoutPro: 'Continue without Pro', - perYear: '/ year', - perMonth: '/ month', freePlanName: 'Free', freePlanPrice: '0 EUR / Month', proPlanName: 'Pro', @@ -394,7 +325,7 @@ export default function BillingScreen() { const planId = billingSummary?.entitlement?.plan || 'free'; const credits = isLoadingBilling && !billingSummary ? '...' : (billingSummary?.credits?.available ?? 0); - const showPaywallPlans = (!session || paywallRequested) && planId !== 'pro'; + const showPaywallPlans = (!session || paywallRequested) && (!isLoadingBilling || !session) && planId !== 'pro'; useEffect(() => { let cancelled = false; @@ -802,122 +733,39 @@ export default function BillingScreen() { ) : ( <> - {session && planId === 'pro' && ( + {session && ( {copy.planLabel} {planId === 'pro' ? copy.planPro : copy.planFree} - setSubModalVisible(true)} - > - {copy.manageSubscription} - + {planId === 'pro' && ( + setSubModalVisible(true)} + > + {copy.manageSubscription} + + )} {copy.creditsAvailableLabel} {credits} + + {planId !== 'pro' && ( + router.push('/profile/billing?view=paywall')} + activeOpacity={0.86} + > + + {copy.startTrial} + + )} )} - {showPaywallPlans && ( - - {copy.paywallTitle} - - {copy.paywallHint} - - - - {copy.proBenefits.slice(0, 3).map((benefit, index) => ( - - - {benefit} - - ))} - - - handlePurchase('yearly_pro')} - disabled={isUpdating || !storeReady || Boolean(storeError)} - activeOpacity={0.9} - > - - - GreenLens Pro - {copy.saveLabel} - - - {copy.yearlyTrialBadge} - - - - {yearlyPrice} - {copy.perYear} - - {copy.yearlySubline} - - - {copy.startTrial} - - - - handlePurchase('monthly_pro')} - disabled={isUpdating || !storeReady || Boolean(storeError)} - activeOpacity={0.9} - > - - - Monatlich - {copy.monthlySubline} - - - {copy.monthlyBadge} - - - - {monthlyPrice} - {copy.perMonth} - - - {!storeReady ? ( - - ) : ( - <> - {copy.monthlyCta} - - - )} - - - - - Linking.openURL('https://greenlenspro.com/privacy')}> - Privacy Policy - - · - Linking.openURL('https://greenlenspro.com/terms')}> - Terms of Use - - - - {copy.restorePurchases} - - - )} - - {session && planId === 'pro' && !isExpoGo ? ( + {session && !isExpoGo ? ( {copy.topupTitle} {copy.topupHint} @@ -1264,30 +1112,18 @@ const styles = StyleSheet.create({ fontSize: 32, fontWeight: '700', }, - paywallTitle: { - fontSize: 24, - fontWeight: '800', - lineHeight: 30, - marginBottom: 8, - }, - paywallValueRows: { - gap: 8, - marginBottom: 14, - }, - paywallValueRow: { - minHeight: 42, - borderRadius: 12, - paddingHorizontal: 12, - paddingVertical: 10, + upgradeCta: { + marginTop: 16, + height: 50, + borderRadius: 14, flexDirection: 'row', alignItems: 'center', - gap: 9, + justifyContent: 'center', + gap: 8, }, - paywallValueText: { - flex: 1, - fontSize: 13, - fontWeight: '700', - lineHeight: 18, + upgradeCtaText: { + fontSize: 16, + fontWeight: '800', }, topupBtn: { flexDirection: 'row', @@ -1446,114 +1282,9 @@ const styles = StyleSheet.create({ fontSize: 15, fontWeight: '500', }, - guestPlanCard: { - borderWidth: 2, - borderRadius: 12, - padding: 16, - }, - paywallPlanCardPrimary: { - borderWidth: 2, - borderRadius: 14, - padding: 16, - marginTop: 2, - }, - paywallPlanCardSecondary: { - borderWidth: 1, - borderRadius: 14, - padding: 14, - marginTop: 10, - }, disabledPlanCard: { opacity: 0.72, }, - guestPlanHeader: { - flexDirection: 'row', - alignItems: 'center', - gap: 8, - marginBottom: 4, - }, - planTopRow: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'flex-start', - gap: 10, - marginBottom: 10, - }, - guestPlanName: { - fontSize: 18, - fontWeight: '700', - }, - planSubline: { - fontSize: 12, - fontWeight: '600', - marginTop: 2, - }, - priceRow: { - flexDirection: 'row', - alignItems: 'baseline', - gap: 6, - }, - guestPlanPrice: { - fontSize: 22, - fontWeight: '700', - marginBottom: 2, - }, - planTerm: { - fontSize: 13, - fontWeight: '600', - }, - guestPlanRenew: { - fontSize: 12, - lineHeight: 17, - }, - trialCallout: { - minHeight: 46, - borderRadius: 12, - paddingHorizontal: 12, - paddingVertical: 10, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - gap: 8, - marginTop: 14, - }, - trialCalloutText: { - fontSize: 15, - fontWeight: '800', - }, - secondaryBadge: { - borderWidth: 1, - borderRadius: 999, - paddingHorizontal: 8, - paddingVertical: 3, - flexShrink: 0, - }, - secondaryBadgeText: { - fontSize: 10, - fontWeight: '800', - }, - monthlyCtaText: { - fontSize: 13, - fontWeight: '800', - }, - monthlyCtaButton: { - minHeight: 42, - borderRadius: 12, - borderWidth: 1, - marginTop: 12, - paddingHorizontal: 14, - paddingVertical: 10, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - gap: 8, - }, - guestSubscribeBtn: { - marginTop: 14, - paddingVertical: 12, - borderRadius: 10, - alignItems: 'center', - }, legalLinksRow: { flexDirection: 'row', justifyContent: 'center',