import React, { useState } from 'react'; import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator, } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { usePaymentSheet } from '@stripe/stripe-react-native'; import { useBeneficiary } from '@/contexts/BeneficiaryContext'; import { useToast } from '@/components/ui/Toast'; import { AppColors, BorderRadius, FontSizes, FontWeights, Spacing, Shadows } from '@/constants/theme'; import type { Beneficiary, BeneficiarySubscription } from '@/types'; const STRIPE_API_URL = 'https://wellnuo.smartlaunchhub.com/api/stripe'; const SUBSCRIPTION_PRICE = 49; // $49/month interface SubscriptionPaymentProps { beneficiary: Beneficiary; onSuccess?: () => void; compact?: boolean; // For inline use vs full page } export function SubscriptionPayment({ beneficiary, onSuccess, compact = false }: SubscriptionPaymentProps) { const [isProcessing, setIsProcessing] = useState(false); const { initPaymentSheet, presentPaymentSheet } = usePaymentSheet(); const { updateLocalBeneficiary } = useBeneficiary(); const toast = useToast(); const isExpired = beneficiary?.subscription?.status === 'expired'; const handleSubscribe = async () => { setIsProcessing(true); try { // 1. Create subscription payment sheet via new Stripe Subscriptions API const response = await fetch(`${STRIPE_API_URL}/create-subscription-payment-sheet`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ beneficiaryId: beneficiary.id, }), }); const data = await response.json(); // Check if already subscribed if (data.alreadySubscribed) { toast.success( 'Already Subscribed!', `${beneficiary.name} already has an active subscription.` ); // Update local state await updateLocalBeneficiary(beneficiary.id, { subscription: { status: 'active', endDate: data.subscription.currentPeriodEnd, planType: 'monthly', price: SUBSCRIPTION_PRICE, }, }); onSuccess?.(); return; } if (!data.clientSecret) { throw new Error(data.error || 'Failed to create subscription'); } // 2. Initialize the Payment Sheet const { error: initError } = await initPaymentSheet({ merchantDisplayName: 'WellNuo', paymentIntentClientSecret: data.clientSecret, customerId: data.customer, customerEphemeralKeySecret: data.ephemeralKey, returnURL: 'wellnuo://stripe-redirect', applePay: { merchantCountryCode: 'US', }, googlePay: { merchantCountryCode: 'US', testEnv: true, }, }); if (initError) { throw new Error(initError.message); } // 3. Present the Payment Sheet const { error: presentError } = await presentPaymentSheet(); if (presentError) { if (presentError.code === 'Canceled') { setIsProcessing(false); return; } throw new Error(presentError.message); } // 4. Payment successful! Subscription is now active in Stripe // Fetch the current status from Stripe to confirm const statusResponse = await fetch( `${STRIPE_API_URL}/subscription-status/${beneficiary.id}` ); const statusData = await statusResponse.json(); // Update local state with data from Stripe const newSubscription: BeneficiarySubscription = { status: statusData.status === 'active' ? 'active' : 'none', endDate: statusData.currentPeriodEnd, planType: 'monthly', price: SUBSCRIPTION_PRICE, }; await updateLocalBeneficiary(beneficiary.id, { subscription: newSubscription, }); toast.success( 'Subscription Activated!', `Subscription for ${beneficiary.name} is now active.` ); onSuccess?.(); } catch (error) { console.error('Payment error:', error); toast.error( 'Payment Failed', error instanceof Error ? error.message : 'Something went wrong. Please try again.' ); } finally { setIsProcessing(false); } }; const features = [ '24/7 AI wellness monitoring', 'Unlimited Julia AI chat', 'Detailed activity reports', 'Smart alerts & notifications', ]; if (compact) { // Compact version for inline use return ( {isExpired ? 'Subscription Expired' : 'Subscription Required'} ${SUBSCRIPTION_PRICE}/month {isProcessing ? ( ) : ( <> {isExpired ? 'Renew Now' : 'Subscribe Now'} )} ); } // Full version return ( {/* Icon */} {/* Title */} {isExpired ? 'Subscription Expired' : 'Subscription Required'} {isExpired ? `Your subscription for ${beneficiary.name} has expired. Renew now to continue monitoring their wellness.` : `Activate a subscription to view ${beneficiary.name}'s dashboard and wellness data.`} {/* Price Card */} WellNuo Pro Full access to all features ${SUBSCRIPTION_PRICE} /month {features.map((feature, index) => ( {feature} ))} {/* Subscribe Button */} {isProcessing ? ( ) : ( <> {isExpired ? 'Renew Subscription' : 'Subscribe Now'} )} {/* Security Badge */} Secure payment powered by Stripe ); } const styles = StyleSheet.create({ // Full version styles container: { flex: 1, padding: Spacing.xl, alignItems: 'center', justifyContent: 'center', }, iconContainer: { width: 100, height: 100, borderRadius: 50, backgroundColor: AppColors.accentLight, justifyContent: 'center', alignItems: 'center', marginBottom: Spacing.lg, }, title: { fontSize: FontSizes['2xl'], fontWeight: FontWeights.bold, color: AppColors.textPrimary, textAlign: 'center', marginBottom: Spacing.sm, }, subtitle: { fontSize: FontSizes.base, color: AppColors.textSecondary, textAlign: 'center', lineHeight: 24, marginBottom: Spacing.xl, paddingHorizontal: Spacing.md, }, priceCard: { width: '100%', backgroundColor: AppColors.surface, borderRadius: BorderRadius.xl, padding: Spacing.lg, marginBottom: Spacing.xl, ...Shadows.sm, }, priceHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: Spacing.md, }, planName: { fontSize: FontSizes.lg, fontWeight: FontWeights.semibold, color: AppColors.textPrimary, }, planDesc: { fontSize: FontSizes.sm, color: AppColors.textSecondary, marginTop: 2, }, priceBadge: { flexDirection: 'row', alignItems: 'baseline', }, priceAmount: { fontSize: FontSizes['2xl'], fontWeight: FontWeights.bold, color: AppColors.primary, }, priceUnit: { fontSize: FontSizes.sm, color: AppColors.textMuted, marginLeft: 2, }, features: { gap: Spacing.sm, }, featureRow: { flexDirection: 'row', alignItems: 'center', gap: Spacing.sm, }, featureText: { fontSize: FontSizes.sm, color: AppColors.textSecondary, }, subscribeButtonFull: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: Spacing.sm, width: '100%', backgroundColor: AppColors.primary, paddingVertical: Spacing.lg, borderRadius: BorderRadius.lg, ...Shadows.primary, }, subscribeButtonTextFull: { fontSize: FontSizes.lg, fontWeight: FontWeights.semibold, color: AppColors.white, }, securityBadge: { flexDirection: 'row', alignItems: 'center', gap: Spacing.xs, marginTop: Spacing.lg, }, securityText: { fontSize: FontSizes.xs, color: AppColors.success, }, // Compact version styles compactContainer: { backgroundColor: AppColors.surface, borderRadius: BorderRadius.xl, padding: Spacing.lg, ...Shadows.sm, }, compactHeader: { flexDirection: 'row', alignItems: 'center', marginBottom: Spacing.md, }, compactIconContainer: { width: 56, height: 56, borderRadius: 28, backgroundColor: AppColors.accentLight, justifyContent: 'center', alignItems: 'center', marginRight: Spacing.md, }, compactInfo: { flex: 1, }, compactTitle: { fontSize: FontSizes.base, fontWeight: FontWeights.semibold, color: AppColors.textPrimary, }, compactPrice: { fontSize: FontSizes.lg, fontWeight: FontWeights.bold, color: AppColors.primary, marginTop: 2, }, subscribeButton: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: Spacing.sm, backgroundColor: AppColors.primary, paddingVertical: Spacing.md, borderRadius: BorderRadius.lg, }, subscribeButtonText: { fontSize: FontSizes.base, fontWeight: FontWeights.semibold, color: AppColors.white, }, buttonDisabled: { opacity: 0.7, }, });