import React, { useState } from 'react'; import { View, Text, StyleSheet, TouchableOpacity, ScrollView, ActivityIndicator, Alert, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { router, useLocalSearchParams } from 'expo-router'; import { Ionicons } from '@expo/vector-icons'; import { usePaymentSheet } from '@stripe/stripe-react-native'; import { AppColors, Spacing, BorderRadius, FontSizes, FontWeights, Shadows } from '@/constants/theme'; import { useAuth } from '@/contexts/AuthContext'; import { useBeneficiary } from '@/contexts/BeneficiaryContext'; import { api } from '@/services/api'; const STRIPE_API_URL = 'https://wellnuo.smartlaunchhub.com/api/stripe'; const STARTER_KIT = { name: 'WellNuo Starter Kit', price: '$249', priceValue: 249, description: 'Everything you need to start monitoring your loved ones', features: [ 'Motion sensor (PIR)', 'Door/window sensor', 'Temperature & humidity sensor', 'WellNuo Hub', 'Mobile app access', '1 year subscription included', ], }; export default function PurchaseScreen() { // Get lovedOneName from add-loved-one flow const params = useLocalSearchParams<{ lovedOneName?: string; beneficiaryId?: string }>(); const lovedOneName = params.lovedOneName || ''; const beneficiaryId = params.beneficiaryId; const [isProcessing, setIsProcessing] = useState(false); const [step, setStep] = useState<'purchase' | 'order_placed'>('purchase'); const { user } = useAuth(); const { addLocalBeneficiary, updateLocalBeneficiary } = useBeneficiary(); const { initPaymentSheet, presentPaymentSheet } = usePaymentSheet(); const handlePurchase = async () => { setIsProcessing(true); try { // 1. Create Payment Sheet on our server const response = await fetch(`${STRIPE_API_URL}/create-payment-sheet`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email: user?.email || 'guest@wellnuo.com', amount: STARTER_KIT.priceValue * 100, // Convert to cents ($249.00) metadata: { userId: user?.user_id || 'guest', beneficiaryName: lovedOneName || 'To be configured', }, }), }); const data = await response.json(); if (!data.paymentIntent) { throw new Error(data.error || 'Failed to create payment sheet'); } // 2. Initialize the Payment Sheet const { error: initError } = await initPaymentSheet({ merchantDisplayName: 'WellNuo', paymentIntentClientSecret: data.paymentIntent, customerId: data.customer, customerEphemeralKeySecret: data.ephemeralKey, defaultBillingDetails: { email: user?.email || '', }, 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') { // User cancelled - do nothing setIsProcessing(false); return; } throw new Error(presentError.message); } // 4. Payment successful! Create or update beneficiary with 'ordered' status if (beneficiaryId) { // Update existing beneficiary await updateLocalBeneficiary(beneficiaryId, { equipmentStatus: 'ordered', }); } else if (lovedOneName) { // Create new beneficiary with ordered status await addLocalBeneficiary({ name: lovedOneName, equipmentStatus: 'ordered', }); } // Mark onboarding as completed await api.setOnboardingCompleted(true); // Show Order Placed screen setStep('order_placed'); } catch (error) { console.error('Payment error:', error); Alert.alert( 'Payment Failed', error instanceof Error ? error.message : 'Something went wrong. Please try again.' ); } setIsProcessing(false); }; const handleSkip = () => { router.replace({ pathname: '/(auth)/activate', params: { lovedOneName }, }); }; const handleGoToHome = () => { router.replace('/(tabs)'); }; // Order Placed Screen if (step === 'order_placed') { return ( {/* Success Icon */} Order Placed! Thank you for your purchase {/* Order Info */} Item {STARTER_KIT.name} For {lovedOneName || 'Your loved one'} Total {STARTER_KIT.price} {/* What's Next */} What's Next? 1 Order Processing We'll prepare your kit for shipping 2 Shipping Notification You'll receive an email with tracking info 3 Activate Your Kit Enter the serial number when delivered {/* Actions */} Go to My Loved Ones ); } return ( {/* Header */} router.canGoBack() ? router.back() : router.replace('/(tabs)')} > Get Started {/* Product Card */} {STARTER_KIT.name} {STARTER_KIT.price} {STARTER_KIT.description} {/* Features */} {STARTER_KIT.features.map((feature, index) => ( {feature} ))} {/* Security Badge */} Secure payment powered by Stripe {/* Bottom Actions */} {isProcessing ? ( ) : ( <> Buy Now - {STARTER_KIT.price} )} I already have a kit ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: AppColors.background, }, content: { padding: Spacing.lg, }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: Spacing.xl, }, backButton: { padding: Spacing.sm, marginLeft: -Spacing.sm, }, title: { fontSize: FontSizes.xl, fontWeight: FontWeights.bold, color: AppColors.textPrimary, }, placeholder: { width: 40, }, productCard: { backgroundColor: AppColors.white, borderRadius: BorderRadius.xl, padding: Spacing.xl, alignItems: 'center', borderWidth: 2, borderColor: AppColors.primary, shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.1, shadowRadius: 12, elevation: 5, }, productIcon: { width: 80, height: 80, borderRadius: 40, backgroundColor: `${AppColors.primary}15`, alignItems: 'center', justifyContent: 'center', marginBottom: Spacing.lg, }, productName: { fontSize: FontSizes['2xl'], fontWeight: FontWeights.bold, color: AppColors.textPrimary, textAlign: 'center', marginBottom: Spacing.sm, }, productPrice: { fontSize: FontSizes['3xl'], fontWeight: FontWeights.bold, color: AppColors.primary, marginBottom: Spacing.sm, }, productDescription: { fontSize: FontSizes.base, color: AppColors.textSecondary, textAlign: 'center', marginBottom: Spacing.xl, }, features: { width: '100%', gap: Spacing.md, }, featureRow: { flexDirection: 'row', alignItems: 'center', gap: Spacing.sm, }, featureText: { fontSize: FontSizes.base, color: AppColors.textPrimary, flex: 1, }, securityBadge: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: Spacing.sm, marginTop: Spacing.xl, paddingVertical: Spacing.md, backgroundColor: `${AppColors.success}10`, borderRadius: BorderRadius.lg, }, securityText: { fontSize: FontSizes.sm, color: AppColors.success, }, bottomActions: { padding: Spacing.lg, paddingBottom: Spacing.xl, gap: Spacing.md, }, purchaseButton: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: Spacing.sm, backgroundColor: AppColors.primary, paddingVertical: Spacing.lg, borderRadius: BorderRadius.lg, }, buttonDisabled: { opacity: 0.7, }, purchaseButtonText: { fontSize: FontSizes.lg, fontWeight: FontWeights.semibold, color: AppColors.white, }, skipButton: { alignItems: 'center', paddingVertical: Spacing.md, }, skipButtonText: { fontSize: FontSizes.base, color: AppColors.textSecondary, textDecorationLine: 'underline', }, // Order Placed Screen Styles orderPlacedContainer: { flex: 1, padding: Spacing.lg, alignItems: 'center', justifyContent: 'center', }, successIcon: { width: 100, height: 100, borderRadius: 50, backgroundColor: AppColors.success, alignItems: 'center', justifyContent: 'center', marginBottom: Spacing.xl, ...Shadows.lg, }, orderPlacedTitle: { fontSize: FontSizes['2xl'], fontWeight: FontWeights.bold, color: AppColors.textPrimary, marginBottom: Spacing.xs, }, orderPlacedSubtitle: { fontSize: FontSizes.base, color: AppColors.textSecondary, marginBottom: Spacing.xl, }, orderInfoCard: { width: '100%', backgroundColor: AppColors.surface, borderRadius: BorderRadius.lg, padding: Spacing.lg, marginBottom: Spacing.lg, ...Shadows.sm, }, orderInfoRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: Spacing.sm, borderBottomWidth: 1, borderBottomColor: AppColors.border, }, orderInfoLabel: { fontSize: FontSizes.sm, color: AppColors.textSecondary, }, orderInfoValue: { fontSize: FontSizes.base, fontWeight: FontWeights.medium, color: AppColors.textPrimary, }, orderInfoPrice: { color: AppColors.primary, fontWeight: FontWeights.bold, }, whatsNextCard: { width: '100%', backgroundColor: AppColors.surface, borderRadius: BorderRadius.lg, padding: Spacing.lg, marginBottom: Spacing.xl, ...Shadows.sm, }, whatsNextTitle: { fontSize: FontSizes.lg, fontWeight: FontWeights.semibold, color: AppColors.textPrimary, marginBottom: Spacing.lg, }, stepItem: { flexDirection: 'row', marginBottom: Spacing.md, }, stepNumber: { width: 28, height: 28, borderRadius: 14, backgroundColor: AppColors.primary, alignItems: 'center', justifyContent: 'center', marginRight: Spacing.md, }, stepNumberLast: { backgroundColor: AppColors.textMuted, }, stepNumberText: { fontSize: FontSizes.sm, fontWeight: FontWeights.bold, color: AppColors.white, }, stepContent: { flex: 1, }, stepTitle: { fontSize: FontSizes.base, fontWeight: FontWeights.medium, color: AppColors.textPrimary, }, stepDescription: { fontSize: FontSizes.sm, color: AppColors.textSecondary, marginTop: 2, }, orderPlacedActions: { width: '100%', }, primaryButton: { backgroundColor: AppColors.primary, paddingVertical: Spacing.lg, borderRadius: BorderRadius.lg, alignItems: 'center', ...Shadows.primary, }, primaryButtonText: { fontSize: FontSizes.base, fontWeight: FontWeights.semibold, color: AppColors.white, }, });