/** * Member Payment Management Composable * Handles payment setup and subscription creation for pending payment members */ export const useMemberPayment = () => { const { memberData, checkMemberStatus } = useAuth() const { initializeHelcimPay, verifyPayment, cleanup: cleanupHelcimPay } = useHelcimPay() const isProcessingPayment = ref(false) const paymentError = ref(null) const paymentSuccess = ref(false) const customerId = ref('') const customerCode = ref('') /** * Initiate payment setup for a member with pending_payment status * This is the main entry point called from "Complete Payment" buttons */ const initiatePaymentSetup = async () => { isProcessingPayment.value = true paymentError.value = null paymentSuccess.value = false try { // Fast-path: when both Helcim ids are already cached on the member doc // AND a card's on file, we can skip the paid getOrCreateCustomer round // trip entirely and go straight to subscription creation. const hasCachedHelcimIds = Boolean( memberData.value?.helcimCustomerId && memberData.value?.helcimCustomerCode ) let existing = null let probedExistingCard = false let cardToken = null if (hasCachedHelcimIds) { existing = await $fetch('/api/helcim/existing-card').catch((err) => { console.warn('[payment] existing-card lookup failed, falling back to verify flow:', err) return null }) probedExistingCard = true if (existing?.cardToken) { customerId.value = memberData.value.helcimCustomerId customerCode.value = memberData.value.helcimCustomerCode cardToken = existing.cardToken } } if (!cardToken) { // Skip HelcimPay verify if a card's already on file — Helcim refuses // to re-save it, breaking retries after a partial-failed signup. const [, existingFromFull] = await Promise.all([ getOrCreateCustomer(), probedExistingCard ? Promise.resolve(existing) : $fetch('/api/helcim/existing-card').catch((err) => { console.warn('[payment] existing-card lookup failed, falling back to verify flow:', err) return null }), ]) cardToken = existingFromFull?.cardToken || null } if (!cardToken) { await initializeHelcimPay( customerId.value, customerCode.value, 0, ) const paymentResult = await verifyPayment() if (!paymentResult.success) { throw new Error('Payment verification failed') } const verifyResult = await $fetch('/api/helcim/verify-payment', { method: 'POST', body: { cardToken: paymentResult.cardToken, customerId: customerId.value, }, }) if (!verifyResult.success) { throw new Error('Payment verification failed on backend') } cardToken = paymentResult.cardToken } const subscriptionResponse = await $fetch('/api/helcim/subscription', { method: 'POST', body: { customerId: customerId.value, customerCode: customerCode.value, contributionAmount: memberData.value?.contributionAmount ?? 5, cardToken, }, }) if (!subscriptionResponse.success) { throw new Error('Subscription creation failed') } paymentSuccess.value = true await checkMemberStatus() // Clear success message after 3 seconds setTimeout(() => { paymentSuccess.value = false }, 3000) return { success: true, message: 'Payment completed successfully!', } } catch (error) { console.error('Payment setup error:', error) paymentError.value = error.message || 'Payment setup failed. Please try again.' throw error } finally { isProcessingPayment.value = false cleanupHelcimPay() } } /** * Get or create Helcim customer for member */ const getOrCreateCustomer = async () => { try { if (!memberData.value?.helcimCustomerId) { // Create new customer const customerResponse = await $fetch( '/api/helcim/get-or-create-customer', { method: 'POST', }, ) customerId.value = customerResponse.customerId customerCode.value = customerResponse.customerCode console.log( 'Created new Helcim customer:', customerId.value, ) } else { // Get customer code from existing customer const customerResponse = await $fetch( '/api/helcim/customer-code', ) customerId.value = customerResponse.customerId customerCode.value = customerResponse.customerCode console.log( 'Using existing Helcim customer:', customerId.value, ) } } catch (error) { console.error('Failed to get or create customer:', error) throw new Error('Failed to initialize payment. Please try again.') } } /** * Complete payment from status banner * Entry point for clicking "Complete Payment" from any page */ const completePayment = async () => { try { await initiatePaymentSetup() return true } catch (error) { console.error('Payment failed:', error) return false } } const resetPaymentState = () => { isProcessingPayment.value = false paymentError.value = null paymentSuccess.value = false } return { isProcessingPayment: readonly(isProcessingPayment), paymentError: readonly(paymentError), paymentSuccess: readonly(paymentSuccess), initiatePaymentSetup, completePayment, resetPaymentState, } }