Major additions: - Territory manager system with application workflow - Custom pricing and page builder with Craft.js - Enhanced Stripe Connect onboarding - CodeReadr QR scanning integration - Kiosk mode for venue sales - Super admin dashboard and analytics - MCP integration for AI-powered operations Infrastructure improvements: - Centralized API client and routing system - Enhanced authentication with organization context - Comprehensive theme management system - Advanced event management with custom tabs - Performance monitoring and accessibility features Database schema updates: - Territory management tables - Custom pages and pricing structures - Kiosk PIN system - Enhanced organization profiles - CodeReadr integration tables 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
216 lines
7.2 KiB
TypeScript
216 lines
7.2 KiB
TypeScript
export const prerender = false;
|
|
|
|
import type { APIRoute } from 'astro';
|
|
import { stripe } from '../../../lib/stripe';
|
|
import { supabase, supabaseAdmin } from '../../../lib/supabase';
|
|
import { verifyAuth } from '../../../lib/auth';
|
|
import { logUserActivity } from '../../../lib/logger';
|
|
|
|
export const GET: APIRoute = async ({ request }) => {
|
|
try {
|
|
|
|
// Verify authentication
|
|
const authContext = await verifyAuth(request);
|
|
if (!authContext) {
|
|
console.error('Account status check failed: Unauthorized request');
|
|
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
|
|
status: 401,
|
|
headers: { 'Content-Type': 'application/json' }
|
|
});
|
|
}
|
|
|
|
const { user } = authContext;
|
|
|
|
// Get user's organization ID first (using admin client to bypass RLS)
|
|
const { data: userData, error: userError } = await (supabaseAdmin || supabase)
|
|
.from('users')
|
|
.select('organization_id')
|
|
.eq('id', user.id)
|
|
.single();
|
|
|
|
if (userError || !userData?.organization_id) {
|
|
console.error('Account status check failed: Organization not found', {
|
|
userId: user.id,
|
|
userEmail: user.email,
|
|
error: userError
|
|
});
|
|
return new Response(JSON.stringify({
|
|
error: 'Organization not found',
|
|
debug: {
|
|
userId: user.id,
|
|
userEmail: user.email,
|
|
userData,
|
|
userError
|
|
}
|
|
}), {
|
|
status: 404,
|
|
headers: { 'Content-Type': 'application/json' }
|
|
});
|
|
}
|
|
|
|
// Now get the organization details
|
|
const { data: organization, error: orgError } = await (supabaseAdmin || supabase)
|
|
.from('organizations')
|
|
.select(`
|
|
id,
|
|
name,
|
|
stripe_account_id,
|
|
account_status,
|
|
stripe_onboarding_status,
|
|
stripe_details_submitted,
|
|
stripe_charges_enabled,
|
|
stripe_payouts_enabled,
|
|
onboarding_completed_at
|
|
`)
|
|
.eq('id', userData.organization_id)
|
|
.single();
|
|
|
|
if (orgError || !organization) {
|
|
console.error('Account status check failed: Organization details not found', {
|
|
userId: user.id,
|
|
organizationId: userData.organization_id,
|
|
error: orgError
|
|
});
|
|
return new Response(JSON.stringify({
|
|
error: 'Organization details not found',
|
|
debug: {
|
|
userId: user.id,
|
|
userEmail: user.email,
|
|
organizationId: userData.organization_id,
|
|
orgError
|
|
}
|
|
}), {
|
|
status: 404,
|
|
headers: { 'Content-Type': 'application/json' }
|
|
});
|
|
}
|
|
|
|
// If no Stripe account exists, return basic status
|
|
if (!organization.stripe_account_id) {
|
|
console.log('No Stripe account found for organization', {
|
|
userId: user.id,
|
|
organizationId: organization.id,
|
|
organizationName: organization.name,
|
|
accountStatus: organization.account_status
|
|
});
|
|
return new Response(JSON.stringify({
|
|
account_status: organization.account_status,
|
|
stripe_onboarding_status: 'not_started',
|
|
can_start_onboarding: organization.account_status === 'approved',
|
|
details_submitted: false,
|
|
charges_enabled: false,
|
|
payouts_enabled: false,
|
|
requirements: {
|
|
currently_due: [],
|
|
eventually_due: [],
|
|
past_due: []
|
|
}
|
|
}), {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' }
|
|
});
|
|
}
|
|
|
|
// Get detailed status from Stripe
|
|
console.log('Retrieving Stripe account status', {
|
|
userId: user.id,
|
|
organizationId: organization.id,
|
|
stripeAccountId: organization.stripe_account_id
|
|
});
|
|
|
|
const account = await stripe.accounts.retrieve(organization.stripe_account_id);
|
|
|
|
// Determine overall onboarding status
|
|
let onboarding_status = 'in_progress';
|
|
if (account.details_submitted && account.charges_enabled) {
|
|
onboarding_status = 'completed';
|
|
} else if (account.details_submitted) {
|
|
onboarding_status = 'pending_review';
|
|
}
|
|
|
|
console.log('Stripe account status retrieved', {
|
|
stripeAccountId: organization.stripe_account_id,
|
|
detailsSubmitted: account.details_submitted,
|
|
chargesEnabled: account.charges_enabled,
|
|
payoutsEnabled: account.payouts_enabled,
|
|
onboardingStatus: onboarding_status,
|
|
requirementsCount: {
|
|
currentlyDue: account.requirements?.currently_due?.length || 0,
|
|
eventuallyDue: account.requirements?.eventually_due?.length || 0,
|
|
pastDue: account.requirements?.past_due?.length || 0
|
|
}
|
|
});
|
|
|
|
// Update organization with latest Stripe status
|
|
const { error: updateError } = await (supabaseAdmin || supabase)
|
|
.from('organizations')
|
|
.update({
|
|
stripe_onboarding_status: onboarding_status,
|
|
stripe_details_submitted: account.details_submitted,
|
|
stripe_charges_enabled: account.charges_enabled,
|
|
stripe_payouts_enabled: account.payouts_enabled,
|
|
...(onboarding_status === 'completed' && !organization.onboarding_completed_at && {
|
|
onboarding_completed_at: new Date().toISOString(),
|
|
account_status: 'active'
|
|
})
|
|
})
|
|
.eq('id', organization.id);
|
|
|
|
if (updateError) {
|
|
console.error('Failed to update organization with latest Stripe status', {
|
|
organizationId: organization.id,
|
|
error: updateError
|
|
});
|
|
}
|
|
|
|
// Log status check if onboarding was completed
|
|
if (onboarding_status === 'completed' && organization.stripe_onboarding_status !== 'completed') {
|
|
await logUserActivity({
|
|
userId: user.id,
|
|
action: 'stripe_onboarding_completed',
|
|
resourceType: 'organization',
|
|
resourceId: organization.id,
|
|
details: {
|
|
stripe_account_id: organization.stripe_account_id,
|
|
charges_enabled: account.charges_enabled,
|
|
payouts_enabled: account.payouts_enabled
|
|
}
|
|
});
|
|
}
|
|
|
|
return new Response(JSON.stringify({
|
|
account_status: onboarding_status === 'completed' ? 'active' : organization.account_status,
|
|
stripe_onboarding_status: onboarding_status,
|
|
can_start_onboarding: organization.account_status === 'approved',
|
|
details_submitted: account.details_submitted,
|
|
charges_enabled: account.charges_enabled,
|
|
payouts_enabled: account.payouts_enabled,
|
|
requirements: {
|
|
currently_due: account.requirements?.currently_due || [],
|
|
eventually_due: account.requirements?.eventually_due || [],
|
|
past_due: account.requirements?.past_due || []
|
|
},
|
|
stripe_account_id: organization.stripe_account_id,
|
|
business_type: account.business_type,
|
|
country: account.country,
|
|
default_currency: account.default_currency,
|
|
created: account.created
|
|
}), {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' }
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Account status check failed with unexpected error', {
|
|
error: error instanceof Error ? error.message : error,
|
|
stack: error instanceof Error ? error.stack : undefined
|
|
});
|
|
return new Response(JSON.stringify({
|
|
error: 'Failed to check account status',
|
|
details: error instanceof Error ? error.message : 'Unknown error'
|
|
}), {
|
|
status: 500,
|
|
headers: { 'Content-Type': 'application/json' }
|
|
});
|
|
}
|
|
}; |