786 lines
26 KiB
Vue
786 lines
26 KiB
Vue
<template>
|
|
<div>
|
|
<!-- Page Header - Context aware -->
|
|
<PageHeader
|
|
v-if="!isAuthenticated"
|
|
title="Join Ghost Guild"
|
|
subtitle="Become a member of our community and start building a more worker-centric future for games."
|
|
theme="gray"
|
|
size="large"
|
|
/>
|
|
<PageHeader
|
|
v-else
|
|
title="You're Already a Member!"
|
|
:subtitle="`Welcome back, ${memberData?.name || 'member'}. You're already part of Ghost Guild in the ${memberData?.circle || 'community'} circle.`"
|
|
theme="gray"
|
|
size="large"
|
|
/>
|
|
|
|
<!-- Membership Sign Up Form -->
|
|
<section v-if="!isAuthenticated" class="py-20 bg-[--ui-bg]">
|
|
<UContainer class="max-w-4xl">
|
|
<div class="text-center mb-12">
|
|
<h2 class="text-3xl font-bold text-[--ui-text] mb-4">
|
|
Membership Sign Up
|
|
</h2>
|
|
<p class="text-lg text-[--ui-text]">
|
|
Choose your circle to connect with others at your stage. Choose your
|
|
contribution based on what you can afford. Everyone gets full
|
|
access.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Step Indicators -->
|
|
<div class="flex justify-center mb-8">
|
|
<div class="flex items-center space-x-4">
|
|
<div class="flex items-center">
|
|
<div
|
|
:class="[
|
|
'w-10 h-10 rounded-full flex items-center justify-center font-semibold',
|
|
currentStep >= 1
|
|
? 'bg-neutral-900 text-neutral-50'
|
|
: 'bg-neutral-200 text-neutral-500',
|
|
]"
|
|
>
|
|
1
|
|
</div>
|
|
<span
|
|
class="ml-2 font-medium"
|
|
:class="
|
|
currentStep === 1 ? 'text-[--ui-text]' : 'text-neutral-500'
|
|
"
|
|
>
|
|
Information
|
|
</span>
|
|
</div>
|
|
|
|
<div v-if="needsPayment" class="w-16 h-1 bg-neutral-200">
|
|
<div
|
|
class="h-full bg-neutral-900 transition-all"
|
|
:style="{ width: currentStep >= 2 ? '100%' : '0%' }"
|
|
/>
|
|
</div>
|
|
|
|
<div v-if="needsPayment" class="flex items-center">
|
|
<div
|
|
:class="[
|
|
'w-10 h-10 rounded-full flex items-center justify-center font-semibold',
|
|
currentStep >= 2
|
|
? 'bg-neutral-900 text-neutral-50'
|
|
: 'bg-neutral-200 text-neutral-500',
|
|
]"
|
|
>
|
|
2
|
|
</div>
|
|
<span
|
|
class="ml-2 font-medium"
|
|
:class="
|
|
currentStep === 2 ? 'text-[--ui-text]' : 'text-neutral-500'
|
|
"
|
|
>
|
|
Payment
|
|
</span>
|
|
</div>
|
|
|
|
<div class="w-16 h-1 bg-neutral-200">
|
|
<div
|
|
class="h-full bg-neutral-900 transition-all"
|
|
:style="{ width: currentStep >= 3 ? '100%' : '0%' }"
|
|
/>
|
|
</div>
|
|
|
|
<div class="flex items-center">
|
|
<div
|
|
:class="[
|
|
'w-10 h-10 rounded-full flex items-center justify-center font-semibold',
|
|
currentStep >= 3
|
|
? 'bg-neutral-900 text-neutral-50'
|
|
: 'bg-neutral-200 text-neutral-500',
|
|
]"
|
|
>
|
|
<span v-if="needsPayment">3</span>
|
|
<span v-else>2</span>
|
|
</div>
|
|
<span
|
|
class="ml-2 font-medium"
|
|
:class="
|
|
currentStep === 3 ? 'text-[--ui-text]' : 'text-neutral-500'
|
|
"
|
|
>
|
|
Confirmation
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Error Message -->
|
|
<div v-if="errorMessage" class="mb-6">
|
|
<UAlert color="red" :title="errorMessage" />
|
|
</div>
|
|
|
|
<!-- Step 1: Information -->
|
|
<div v-if="currentStep === 1" class="bg-[--ui-bg-elevated]">
|
|
<UForm :state="form" class="space-y-8" @submit="handleSubmit">
|
|
<!-- Personal Information -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<UFormField label="Full Name" name="name" required>
|
|
<UInput
|
|
v-model="form.name"
|
|
placeholder="Enter your full name"
|
|
size="xl"
|
|
class="w-full"
|
|
/>
|
|
</UFormField>
|
|
|
|
<UFormField label="Email Address" name="email" required>
|
|
<UInput
|
|
v-model="form.email"
|
|
type="email"
|
|
size="xl"
|
|
class="w-full"
|
|
placeholder="Enter your email address"
|
|
/>
|
|
</UFormField>
|
|
</div>
|
|
|
|
<!-- Circle Selection -->
|
|
<div>
|
|
<h3 class="text-lg font-semibold mb-4">Choose Your Circle</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
<label
|
|
v-for="option in circleOptions"
|
|
:key="option.value"
|
|
class="flex flex-col p-6 rounded-lg border-2 cursor-pointer transition-all hover:shadow-md"
|
|
:class="
|
|
form.circle === option.value
|
|
? 'border-primary bg-primary/5'
|
|
: 'border-neutral-200 hover:border-neutral-400'
|
|
"
|
|
>
|
|
<input
|
|
v-model="form.circle"
|
|
type="radio"
|
|
:value="option.value"
|
|
name="circle"
|
|
class="sr-only"
|
|
/>
|
|
<div class="font-medium text-lg mb-2">{{ option.label }}</div>
|
|
<div class="text-sm text-[--ui-text-muted]">
|
|
{{ option.description }}
|
|
</div>
|
|
</label>
|
|
</div>
|
|
<p class="text-sm text-[--ui-text-muted] mt-3 italic">
|
|
Not sure? Start with Community - you can always move.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Contribution Selection -->
|
|
<div>
|
|
<UFormField
|
|
label="Choose Your Monthly Contribution"
|
|
name="contributionTier"
|
|
required
|
|
>
|
|
<USelect
|
|
v-model="form.contributionTier"
|
|
:items="contributionOptions"
|
|
value-key="value"
|
|
size="xl"
|
|
class="w-full"
|
|
/>
|
|
</UFormField>
|
|
</div>
|
|
|
|
<!-- Submit Button -->
|
|
<div class="flex justify-center pt-6">
|
|
<UButton
|
|
type="submit"
|
|
:loading="isSubmitting"
|
|
:disabled="!isFormValid"
|
|
size="xl"
|
|
class="px-12"
|
|
>
|
|
{{
|
|
needsPayment ? "Continue to Payment" : "Complete Registration"
|
|
}}
|
|
</UButton>
|
|
</div>
|
|
</UForm>
|
|
</div>
|
|
|
|
<!-- Step 2: Payment -->
|
|
<div
|
|
v-if="currentStep === 2"
|
|
class="bg-[--ui-bg-elevated] rounded-xl p-8"
|
|
>
|
|
<div class="mb-6">
|
|
<h3 class="text-2xl font-bold text-[--ui-text] mb-2">
|
|
Payment Information
|
|
</h3>
|
|
<p class="text-[--ui-text-muted]">
|
|
You're signing up for the {{ selectedTier.label }} plan
|
|
</p>
|
|
<p class="text-lg font-semibold text-[--ui-text] mt-2">
|
|
${{ selectedTier.amount }} CAD / month
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Payment Instructions -->
|
|
<div class="bg-[--ui-bg] rounded-lg p-6 mb-6">
|
|
<p class="text-[--ui-text]">
|
|
Click "Complete Payment" below to open the secure payment modal
|
|
and verify your payment method.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Action Buttons -->
|
|
<div class="flex justify-between pt-6">
|
|
<UButton
|
|
variant="outline"
|
|
size="lg"
|
|
@click="goBack"
|
|
:disabled="isSubmitting"
|
|
>
|
|
Back
|
|
</UButton>
|
|
<UButton size="lg" :loading="isSubmitting" @click="processPayment">
|
|
Complete Payment
|
|
</UButton>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 3: Confirmation -->
|
|
<div
|
|
v-if="currentStep === 3"
|
|
class="bg-[--ui-bg-elevated] rounded-xl p-8"
|
|
>
|
|
<div class="text-center">
|
|
<div
|
|
class="w-20 h-20 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-6"
|
|
>
|
|
<svg
|
|
class="w-10 h-10 text-green-500"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M5 13l4 4L19 7"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
|
|
<h3 class="text-2xl font-bold text-[--ui-text] mb-4">
|
|
Welcome to Ghost Guild!
|
|
</h3>
|
|
|
|
<div v-if="successMessage" class="mb-6">
|
|
<UAlert color="green" :title="successMessage" />
|
|
</div>
|
|
|
|
<div class="bg-[--ui-bg] rounded-lg p-6 mb-6 text-left">
|
|
<h4 class="font-semibold mb-3">Membership Details:</h4>
|
|
<dl class="space-y-2">
|
|
<div class="flex justify-between">
|
|
<dt class="text-[--ui-text-muted]">Name:</dt>
|
|
<dd class="font-medium">{{ form.name }}</dd>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<dt class="text-[--ui-text-muted]">Email:</dt>
|
|
<dd class="font-medium">{{ form.email }}</dd>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<dt class="text-[--ui-text-muted]">Circle:</dt>
|
|
<dd class="font-medium capitalize">{{ form.circle }}</dd>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<dt class="text-[--ui-text-muted]">Contribution:</dt>
|
|
<dd class="font-medium">{{ selectedTier.label }}</dd>
|
|
</div>
|
|
<div v-if="customerCode" class="flex justify-between">
|
|
<dt class="text-[--ui-text-muted]">Member ID:</dt>
|
|
<dd class="font-medium">{{ customerCode }}</dd>
|
|
</div>
|
|
</dl>
|
|
</div>
|
|
|
|
<p class="text-[--ui-text-muted] mb-4">
|
|
We've sent a confirmation email to {{ form.email }} with your
|
|
membership details.
|
|
</p>
|
|
|
|
<div class="bg-[--ui-bg] rounded-lg p-4 mb-8">
|
|
<p class="text-[--ui-text] text-center">
|
|
You will be automatically redirected to your dashboard in a few
|
|
seconds...
|
|
</p>
|
|
</div>
|
|
|
|
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
|
<UButton to="/member/dashboard" size="lg" class="px-8">
|
|
Go to Dashboard Now
|
|
</UButton>
|
|
<UButton variant="outline" size="lg" @click="resetForm">
|
|
Register Another Member
|
|
</UButton>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</UContainer>
|
|
</section>
|
|
|
|
<!-- Member Info Section - Shows for logged-in members -->
|
|
<section v-if="isAuthenticated" class="py-20 bg-[--ui-bg]">
|
|
<UContainer class="max-w-4xl">
|
|
<div class="bg-[--ui-bg-elevated] rounded-xl p-8 mb-8">
|
|
<h2 class="text-2xl font-bold text-[--ui-text] mb-6">
|
|
Your Membership
|
|
</h2>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
|
|
<div class="bg-[--ui-bg] rounded-lg p-6">
|
|
<h3 class="text-sm font-medium text-[--ui-text-muted] mb-2">
|
|
Circle
|
|
</h3>
|
|
<p class="text-xl font-semibold text-[--ui-text] capitalize">
|
|
{{ memberData?.circle || "Community" }}
|
|
</p>
|
|
</div>
|
|
|
|
<div class="bg-[--ui-bg] rounded-lg p-6">
|
|
<h3 class="text-sm font-medium text-[--ui-text-muted] mb-2">
|
|
Contribution
|
|
</h3>
|
|
<p class="text-xl font-semibold text-[--ui-text]">
|
|
${{ memberData?.contributionTier || "0" }} CAD/month
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex flex-col sm:flex-row gap-4">
|
|
<UButton to="/member/dashboard" size="lg">
|
|
Go to Dashboard
|
|
</UButton>
|
|
<UButton to="/member/profile" variant="outline" size="lg">
|
|
Edit Profile
|
|
</UButton>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-6">
|
|
<h3 class="text-lg font-semibold text-[--ui-text] mb-3">
|
|
Want to change your circle or contribution?
|
|
</h3>
|
|
<p class="text-[--ui-text] mb-4">
|
|
You can update your circle and adjust your monthly contribution at
|
|
any time from your profile settings.
|
|
</p>
|
|
<UButton to="/member/profile" variant="soft" color="primary">
|
|
Update Membership Settings
|
|
</UButton>
|
|
</div>
|
|
</UContainer>
|
|
</section>
|
|
|
|
<!-- How Ghost Guild Works -->
|
|
<section class="py-20 bg-[--ui-bg-elevated]">
|
|
<UContainer>
|
|
<div class="max-w-4xl mx-auto">
|
|
<div class="text-center mb-12">
|
|
<h2 class="text-3xl font-bold text-[--ui-text] mb-4">
|
|
How Ghost Guild Works
|
|
</h2>
|
|
<p class="text-lg text-[--ui-text]">
|
|
Every member gets everything. Your circle helps you find relevant
|
|
content and peers. Your contribution helps sustain our solidarity
|
|
economy.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
|
|
<!-- Full Access -->
|
|
<div class="bg-[--ui-bg] rounded-xl p-6">
|
|
<h3 class="text-xl font-semibold mb-4 text-[--ui-text]">
|
|
Full Access
|
|
</h3>
|
|
<ul class="text-[--ui-text] space-y-2">
|
|
<li>Complete resource library</li>
|
|
<li>All workshops and events</li>
|
|
<li>Slack community</li>
|
|
<li>Voting rights</li>
|
|
<li>Peer support opportunities</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<!-- Circle-Specific Guidance -->
|
|
<div class="bg-[--ui-bg] rounded-xl p-6">
|
|
<h3 class="text-xl font-semibold mb-4 text-[--ui-text]">
|
|
Circle-Specific Guidance
|
|
</h3>
|
|
<ul class="text-[--ui-text] space-y-2">
|
|
<li>Resources for your stage</li>
|
|
<li>Connection with peers</li>
|
|
<li>Workshop recommendations</li>
|
|
<li>Support for your challenges</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</UContainer>
|
|
</section>
|
|
|
|
<!-- How to Join -->
|
|
<section class="py-20 bg-[--ui-bg]">
|
|
<UContainer>
|
|
<div class="max-w-4xl mx-auto">
|
|
<div class="space-y-8">
|
|
<div class="flex items-start gap-6">
|
|
<div class="flex-shrink-0">
|
|
<div
|
|
class="w-12 h-12 bg-neutral-900 rounded-full flex items-center justify-center text-neutral-50 font-bold text-xl"
|
|
>
|
|
1
|
|
</div>
|
|
</div>
|
|
<div class="flex-1">
|
|
<h3 class="text-xl font-semibold mb-2 text-[--ui-text]">
|
|
Pick your circle
|
|
</h3>
|
|
<p class="text-[--ui-text]">
|
|
Where are you in your co-op journey? Select based on where you
|
|
are in your cooperative journey - exploring, building, or
|
|
practicing. Not sure? Start with Community.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-start gap-6">
|
|
<div class="flex-shrink-0">
|
|
<div
|
|
class="w-12 h-12 bg-neutral-900 rounded-full flex items-center justify-center text-neutral-50 font-bold text-xl"
|
|
>
|
|
2
|
|
</div>
|
|
</div>
|
|
<div class="flex-1">
|
|
<h3 class="text-xl font-semibold mb-2 text-[--ui-text]">
|
|
Choose your contribution
|
|
</h3>
|
|
<p class="text-[--ui-text]">
|
|
What can you afford? ($0-50+/month) Choose based on your
|
|
financial capacity. From $0 for those who need support to $50+
|
|
for those who can sponsor others. You can adjust anytime.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-start gap-6">
|
|
<div class="flex-shrink-0">
|
|
<div
|
|
class="w-12 h-12 bg-neutral-900 rounded-full flex items-center justify-center text-neutral-50 font-bold text-xl"
|
|
>
|
|
3
|
|
</div>
|
|
</div>
|
|
<div class="flex-1">
|
|
<h3 class="text-xl font-semibold mb-2 text-[--ui-text]">
|
|
Join the community
|
|
</h3>
|
|
<p class="text-[--ui-text]">
|
|
Get access to everything. Fill out your profile, agree to our
|
|
community guidelines, and complete payment (if applicable).
|
|
You'll get access to our community as soon as we review your
|
|
application.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</UContainer>
|
|
</section>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { reactive, ref, computed, onMounted, onUnmounted } from "vue";
|
|
import { getCircleOptions } from "~/config/circles";
|
|
import {
|
|
getContributionOptions,
|
|
requiresPayment,
|
|
getContributionTierByValue,
|
|
} from "~/config/contributions";
|
|
|
|
// Auth state
|
|
const { isAuthenticated, memberData, checkMemberStatus } = useAuth();
|
|
|
|
// Check authentication status on mount
|
|
onMounted(async () => {
|
|
await checkMemberStatus();
|
|
});
|
|
|
|
// Form state
|
|
const form = reactive({
|
|
email: "",
|
|
name: "",
|
|
circle: "community",
|
|
contributionTier: "15",
|
|
billingAddress: {
|
|
street: "",
|
|
city: "",
|
|
province: "",
|
|
postalCode: "",
|
|
country: "CA",
|
|
},
|
|
});
|
|
|
|
// UI state
|
|
const isSubmitting = ref(false);
|
|
const currentStep = ref(1); // 1: Info, 2: Billing (paid only), 3: Payment, 4: Confirmation
|
|
const errorMessage = ref("");
|
|
const successMessage = ref("");
|
|
|
|
// Helcim state
|
|
const customerId = ref(null);
|
|
const customerCode = ref(null);
|
|
const subscriptionData = ref(null);
|
|
const paymentToken = ref(null);
|
|
|
|
// Circle options from central config
|
|
const circleOptions = getCircleOptions();
|
|
|
|
// Contribution options from central config
|
|
const contributionOptions = getContributionOptions();
|
|
|
|
// Initialize composables
|
|
const {
|
|
initializeHelcimPay,
|
|
verifyPayment,
|
|
cleanup: cleanupHelcimPay,
|
|
} = useHelcimPay();
|
|
|
|
// Form validation
|
|
const isFormValid = computed(() => {
|
|
return form.name && form.email && form.circle && form.contributionTier;
|
|
});
|
|
|
|
// Check if payment is required
|
|
const needsPayment = computed(() => {
|
|
return requiresPayment(form.contributionTier);
|
|
});
|
|
|
|
// Get selected tier info
|
|
const selectedTier = computed(() => {
|
|
return getContributionTierByValue(form.contributionTier);
|
|
});
|
|
|
|
// Step 1: Create customer
|
|
const handleSubmit = async () => {
|
|
if (isSubmitting.value || !isFormValid.value) return;
|
|
|
|
isSubmitting.value = true;
|
|
errorMessage.value = "";
|
|
|
|
try {
|
|
// Create customer in Helcim
|
|
const response = await $fetch("/api/helcim/customer", {
|
|
method: "POST",
|
|
body: {
|
|
name: form.name,
|
|
email: form.email,
|
|
circle: form.circle,
|
|
contributionTier: form.contributionTier,
|
|
billingAddress: form.billingAddress,
|
|
},
|
|
});
|
|
|
|
if (response.success) {
|
|
console.log("Customer response:", response);
|
|
customerId.value = response.customerId;
|
|
customerCode.value = response.customerCode;
|
|
|
|
// Token is now set as httpOnly cookie by the server
|
|
// No need to manually set cookie on client side
|
|
|
|
// Move to next step
|
|
if (needsPayment.value) {
|
|
currentStep.value = 2;
|
|
// Debug log
|
|
console.log(
|
|
"Customer ID:",
|
|
customerId.value,
|
|
"Customer Code:",
|
|
customerCode.value,
|
|
);
|
|
// Initialize HelcimPay.js session for card verification
|
|
await initializeHelcimPay(customerId.value, customerCode.value, 0);
|
|
} else {
|
|
// For free tier, create subscription directly
|
|
await createSubscription();
|
|
// Check member status to ensure user is properly authenticated
|
|
await checkMemberStatus();
|
|
|
|
// Automatically redirect to dashboard after a short delay
|
|
setTimeout(() => {
|
|
navigateTo("/member/dashboard");
|
|
}, 3000); // 3 second delay to show success message
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("Error creating customer:", error);
|
|
errorMessage.value =
|
|
error.data?.message || "Failed to create account. Please try again.";
|
|
} finally {
|
|
isSubmitting.value = false;
|
|
}
|
|
};
|
|
|
|
// Step 2: Process payment
|
|
const processPayment = async () => {
|
|
if (isSubmitting.value) return;
|
|
|
|
console.log("Starting payment process...");
|
|
isSubmitting.value = true;
|
|
errorMessage.value = "";
|
|
|
|
try {
|
|
console.log("Calling verifyPayment()...");
|
|
// Verify payment through HelcimPay.js
|
|
const paymentResult = await verifyPayment();
|
|
console.log("Payment result from HelcimPay:", paymentResult);
|
|
|
|
if (paymentResult.success) {
|
|
paymentToken.value = paymentResult.cardToken;
|
|
console.log("Payment successful, cardToken:", paymentResult.cardToken);
|
|
|
|
console.log("Calling verify-payment endpoint...");
|
|
// Verify payment on server
|
|
const verifyResult = await $fetch("/api/helcim/verify-payment", {
|
|
method: "POST",
|
|
body: {
|
|
cardToken: paymentResult.cardToken,
|
|
customerId: customerId.value,
|
|
},
|
|
});
|
|
console.log("Payment verification result:", verifyResult);
|
|
|
|
console.log("Calling createSubscription...");
|
|
// Create subscription (don't let subscription errors prevent form progression)
|
|
const subscriptionResult = await createSubscription(
|
|
paymentResult.cardToken,
|
|
);
|
|
|
|
if (!subscriptionResult || !subscriptionResult.success) {
|
|
console.warn(
|
|
"Subscription creation failed but payment succeeded:",
|
|
subscriptionResult?.error,
|
|
);
|
|
// Still progress to success page since payment worked
|
|
currentStep.value = 3;
|
|
successMessage.value =
|
|
"Payment successful! Subscription setup may need manual completion.";
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("Payment process error:", error);
|
|
console.error("Error details:", {
|
|
message: error.message,
|
|
statusCode: error.statusCode,
|
|
statusMessage: error.statusMessage,
|
|
data: error.data,
|
|
});
|
|
errorMessage.value =
|
|
error.message || "Payment verification failed. Please try again.";
|
|
} finally {
|
|
isSubmitting.value = false;
|
|
}
|
|
};
|
|
|
|
// Create subscription
|
|
const createSubscription = async (cardToken = null) => {
|
|
try {
|
|
console.log("Creating subscription with:", {
|
|
customerId: customerId.value,
|
|
contributionTier: form.contributionTier,
|
|
cardToken: cardToken ? "present" : "null",
|
|
});
|
|
|
|
const response = await $fetch("/api/helcim/subscription", {
|
|
method: "POST",
|
|
body: {
|
|
customerId: customerId.value,
|
|
customerCode: customerCode.value,
|
|
contributionTier: form.contributionTier,
|
|
cardToken: cardToken,
|
|
},
|
|
});
|
|
|
|
console.log("Subscription creation response:", response);
|
|
|
|
if (response.success) {
|
|
subscriptionData.value = response.subscription;
|
|
console.log("Moving to step 3 - success!");
|
|
currentStep.value = 3;
|
|
successMessage.value = "Your membership has been activated successfully!";
|
|
|
|
// Check member status to ensure user is properly authenticated
|
|
await checkMemberStatus();
|
|
|
|
// Automatically redirect to dashboard after a short delay
|
|
setTimeout(() => {
|
|
navigateTo("/member/dashboard");
|
|
}, 3000); // 3 second delay to show success message
|
|
} else {
|
|
throw new Error("Subscription creation failed - response not successful");
|
|
}
|
|
} catch (error) {
|
|
console.error("Subscription creation error:", error);
|
|
console.error("Error details:", {
|
|
message: error.message,
|
|
statusCode: error.statusCode,
|
|
statusMessage: error.statusMessage,
|
|
data: error.data,
|
|
});
|
|
console.error(
|
|
"Subscription creation completely failed, but payment was successful",
|
|
);
|
|
// Don't throw error - let the calling function handle progression
|
|
return {
|
|
success: false,
|
|
error:
|
|
error.data?.message || error.message || "Failed to create subscription",
|
|
};
|
|
}
|
|
};
|
|
|
|
// Go back to previous step
|
|
const goBack = () => {
|
|
if (currentStep.value > 1) {
|
|
currentStep.value--;
|
|
errorMessage.value = "";
|
|
}
|
|
};
|
|
|
|
// Reset form
|
|
const resetForm = () => {
|
|
currentStep.value = 1;
|
|
customerId.value = null;
|
|
customerCode.value = null;
|
|
subscriptionData.value = null;
|
|
paymentToken.value = null;
|
|
errorMessage.value = "";
|
|
successMessage.value = "";
|
|
form.email = "";
|
|
form.name = "";
|
|
form.circle = "community";
|
|
form.contributionTier = "15";
|
|
};
|
|
|
|
// Cleanup on unmount
|
|
onUnmounted(() => {
|
|
cleanupHelcimPay();
|
|
});
|
|
</script>
|