- Add comprehensive analytics components with export functionality - Implement territory management with manager performance tracking - Add seatmap components for venue layout management - Create customer management features with modal interface - Add advanced hooks for dashboard flags and territory data - Implement seat selection and venue management utilities - Add type definitions for ticketing and seatmap systems 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
34 KiB
Stripe Connect Checkout + Ticket Minting Guide
This guide covers the complete implementation of Stripe Connect checkout sessions with automatic ticket generation.
Overview
The system now supports:
- ✅ Checkout Creation: Using connected Stripe accounts with platform fees
- ✅ Payment Processing: Secure Stripe Checkout with proper fee splitting
- ✅ Webhook Handling: Automatic ticket minting on payment completion
- ✅ Inventory Management: Automatic inventory updates and sold count tracking
- ✅ Order Records: Complete purchase history with customer details
Backend Implementation
New Cloud Functions
1. createCheckout
Endpoint: POST /api/checkout/create
Creates a Stripe Checkout session using the organization's connected account with full idempotency and inventory safety.
Request Body:
{
orgId: string;
eventId: string;
ticketTypeId: string;
qty: number;
purchaserEmail?: string;
successUrl: string;
cancelUrl: string;
}
Response:
{
url: string; // Stripe Checkout URL
sessionId: string; // Session ID for tracking
}
Key Features:
- Validates organization has connected Stripe account and charges enabled
- Calculates platform fees using
PLATFORM_FEE_BPSenvironment variable (default 3%) - Creates checkout with connected account using
stripeAccountparameter - Includes comprehensive metadata for webhook processing
- Creates placeholder order for UI polling
- Full inventory validation before checkout creation
2. stripeWebhookConnect
Endpoint: POST /api/stripe/webhook/connect
Handles webhooks from connected accounts with full idempotency protection.
Supported Events:
checkout.session.completed- Triggers ticket mintingpayment_intent.succeeded- Additional payment tracking
Ticket Minting Process:
- Idempotency Check: Creates
processedSessions/{sessionId}document to prevent duplicates - Inventory Transaction: Atomically validates and decrements inventory
- Ticket Generation: Creates individual tickets with UUID QR codes
- Order Update: Marks order as 'paid' with payment details
- Email Delivery: Sends confirmation email with QR code links
- Error Handling: Graceful failure handling with comprehensive logging
All operations use Firestore transactions for atomic consistency.
3. verifyTicket
Endpoint: POST /api/tickets/verify or GET /api/tickets/verify/:qr
Verifies ticket QR codes and marks them as scanned.
Request Body (POST):
{
qr: string;
}
Response:
{
valid: boolean;
ticket?: {
id: string;
eventId: string;
ticketTypeId: string;
eventName?: string;
ticketTypeName?: string;
status: string;
purchaserEmail?: string;
};
reason?: string; // 'already_scanned', 'ticket_voided', 'Ticket not found'
scannedAt?: string;
}
Key Features:
- Atomic scan status updates to prevent double-scanning
- Comprehensive ticket information in response
- Support for both POST with body and GET with path parameter
- Detailed error reasons for failed verifications
4. getOrder
Endpoint: POST /api/orders/get
Retrieves order details by session ID for frontend polling.
Request Body:
{
sessionId: string;
}
Response:
{
id: string;
status: 'pending' | 'paid' | 'failed_sold_out';
qty: number;
totalCents: number;
purchaserEmail?: string;
eventName?: string;
ticketTypeName?: string;
// ... additional order details
}
Environment Configuration
Required environment variables for Firebase Functions:
# Stripe Configuration
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET_CONNECT=whsec_... # For connected account events
# Email Configuration (optional - logs in dev if not set)
EMAIL_API_KEY=re_... # Resend API key
# Application Configuration
APP_URL=https://staging.blackcanyontickets.com # For QR code links
PLATFORM_FEE_BPS=300 # 3% platform fee (default if not set)
Firebase Functions config (legacy method):
firebase functions:config:set stripe.secret_key="sk_test_..."
firebase functions:config:set stripe.connect_webhook_secret="whsec_..."
firebase functions:config:set email.api_key="re_..."
firebase functions:config:set app.url="https://staging.blackcanyontickets.com"
firebase functions:config:set platform.fee_bps="300"
Webhook Setup
Platform Webhook (Existing)
- URL:
https://us-central1-PROJECT_ID.cloudfunctions.net/stripeWebhook - Events:
account.updated - Purpose: Sync account connection status
Connect Webhook (New)
- URL:
https://us-central1-PROJECT_ID.cloudfunctions.net/stripeWebhookConnect - Events:
checkout.session.completed - Purpose: Handle successful payments and mint tickets with idempotency protection
Note: Connect webhooks receive a Stripe-Account header identifying which connected account triggered the event.
Frontend Implementation
useCheckout Hook
import { useCheckout } from '../hooks/useCheckout';
const { createCheckout, isLoading, error } = useCheckout();
// Create checkout session
await createCheckout({
orgId: user.organization.id,
eventId: 'event_123',
ticketTypeId: 'ticket_type_456',
quantity: 2,
customerEmail: 'customer@example.com',
successUrl: '/checkout/success',
cancelUrl: '/checkout/cancel',
});
TicketPurchase Component
Complete checkout component with:
- Quantity selector with inventory validation
- Price breakdown including platform fees
- Customer email input
- Secure checkout button
- Error handling and loading states
import { TicketPurchase } from '../components/checkout/TicketPurchase';
<TicketPurchase
eventId="event_123"
ticketTypeId="ticket_type_456"
ticketTypeName="General Admission"
priceInCents={5000} // $50.00
maxQuantity={10}
inventory={50}
/>
Success/Cancel Pages
/checkout/success- Displays purchase confirmation with ticket details/checkout/cancel- Handles cancelled purchases with retry options
Data Schema
Tickets Collection
// /tickets/{ticketId}
{
orgId: string;
eventId: string;
ticketTypeId: string;
orderId: string; // Links to order (sessionId)
purchaserEmail: string;
qr: string; // UUID for scanning
status: 'issued' | 'scanned' | 'void';
createdAt: Timestamp;
scannedAt?: Timestamp;
updatedAt?: Timestamp;
}
Orders Collection
// /orders/{sessionId}
{
orgId: string;
eventId: string;
ticketTypeId: string;
qty: number;
sessionId: string;
status: 'pending' | 'paid' | 'failed_sold_out';
totalCents: number;
createdAt: Timestamp;
purchaserEmail?: string;
paymentIntentId?: string;
stripeAccountId: string;
updatedAt?: Timestamp;
failureReason?: string; // For failed orders
}
Updated Ticket Types
// /ticket_types/{ticketTypeId}
{
orgId: string;
eventId: string;
name: string;
priceCents: number;
currency: 'USD';
inventory: number; // Total available
sold: number; // Number sold (incremented atomically)
createdAt: Timestamp;
updatedAt?: Timestamp;
}
Processed Sessions Collection (Idempotency)
// /processedSessions/{sessionId}
{
sessionId: string;
processedAt: Timestamp;
orgId: string;
eventId: string;
ticketTypeId: string;
qty: number;
paymentIntentId: string;
stripeAccountId: string;
}
Testing Flow
End-to-End Test Scenario
- Setup: Ensure organization has connected Stripe account
- Create Event: With published status and active ticket types
- Purchase Flow:
# User clicks "Purchase Tickets" # → createStripeCheckout called # → Redirects to Stripe Checkout # → User completes payment # → Stripe sends webhook to stripeConnectWebhook # → Tickets automatically minted # → User redirected to success page
Validation Points
✅ Payment Processing:
- Platform fee calculated correctly (2.9% + $0.30)
- Connected account receives net amount
- Platform receives application fee
✅ Ticket Generation:
- Unique ticket IDs generated
- QR codes created for scanning
- Customer information captured
✅ Inventory Management:
- Available inventory decremented
- Sold count incremented
- Overselling prevented
✅ Data Consistency:
- All database operations atomic
- Order and ticket records linked
- Audit trail maintained
Error Handling
Common Scenarios
- Payment Fails: User sees error, can retry
- Webhook Delay: Tickets may take 1-2 minutes to appear
- Inventory Conflict: Prevented by atomic operations
- Email Issues: Tickets stored in database regardless
Monitoring
Add logging to track:
- Checkout session creation success/failure rates
- Webhook processing times
- Ticket minting success rates
- Payment vs. ticket generation correlation
Security Considerations
Payment Security
- ✅ Card data never touches your servers
- ✅ PCI compliance handled by Stripe
- ✅ Webhook signatures verified
- ✅ Connected account isolation
Data Protection
- ✅ Customer data encrypted in transit and at rest
- ✅ Minimal customer data stored (email, name)
- ✅ Ticket IDs non-enumerable (timestamp + random)
- ✅ Organization data isolation maintained
Performance Optimizations
Database Efficiency
- Batch operations for ticket creation
- Indexed queries for ticket lookup
- Efficient inventory updates
- Proper error rollback
Frontend Optimization
- Immediate redirect to Stripe (no waiting)
- Optimistic UI updates where appropriate
- Error state handling
- Loading state management
Next Steps
Advanced Features
- Email Notifications: Send ticket confirmations via Resend/SendGrid
- PDF Generation: Create downloadable ticket PDFs with QR codes
- Calendar Integration: Add event to customer calendars
- ✅ Refund Handling: Secure refund processing with organization validation
- Bulk Purchases: Handle group bookings and corporate sales
- Waitlist Management: Handle sold-out scenarios with waitlist functionality
- Dynamic Pricing: Time-based and demand-based pricing strategies
- Multi-Currency Support: International payment processing
Performance Optimizations
- Webhook Batching: Process multiple events in single transaction
- Inventory Caching: Redis caching for high-traffic events
- Database Sharding: Partition tickets by event for scale
- Queue Processing: Async ticket generation for very large orders
- CDN Integration: Cache static checkout pages
Analytics Integration
- Sales Tracking: Revenue and conversion analytics with refund adjustments
- Customer Insights: Purchase behavior analysis and fraud detection
- Event Performance: Attendance and sales metrics with real-time updates
- Platform Metrics: Fee collection and growth tracking with configurable rates
- Operational Analytics: Webhook processing times, error rates, and idempotency metrics
- Security Analytics: Failed authentication attempts and suspicious refund patterns
Refunds, Voids & Disputes System
Overview
The platform now supports comprehensive refund management, dispute handling, and financial reconciliation with enterprise-grade safety features:
- ✅ Full/Partial/Per-Ticket Refunds: Flexible refund options with proper ledger tracking
- ✅ Automatic Dispute Handling: Tickets locked on dispute creation, outcomes processed automatically
- ✅ Financial Reconciliation: Complete ledger system with CSV export capabilities
- ✅ Permission-Based Access: Only admins/super-admins can process refunds
- ✅ Idempotent Operations: Prevents duplicate refunds and maintains data consistency
Refund Management
Creating Refunds
Endpoint: POST /api/refunds/create
Request Body:
{
orderId: string; // Required: Order to refund
ticketId?: string; // Optional: Specific ticket to refund
amountCents?: number; // Optional: Custom amount (defaults to full/ticket price)
reason?: string; // Optional: Reason for refund
}
Refund Types:
-
Full Order Refund: Refunds entire order amount
{ orderId: "order_123" } -
Single Ticket Refund: Refunds specific ticket at ticket price
{ orderId: "order_123", ticketId: "ticket_456" } -
Partial Amount Refund: Custom refund amount
{ orderId: "order_123", amountCents: 2500 } // $25.00 -
Multiple Tickets: Multiple tickets via custom amount
{ orderId: "order_123", amountCents: 5000 } // 2 x $25 tickets
Response:
{
refundId: string;
stripeRefundId: string;
amountCents: number;
status: "succeeded" | "failed";
}
Key Features:
- Permission Validation: Only org admins/super-admins can create refunds
- Idempotency Protection: Duplicate refund requests return existing refund
- Automatic Fee Handling: Platform fees and Stripe fees refunded proportionally
- Ticket Status Updates: Refunded tickets marked as 'refunded' status
- Comprehensive Logging: Full audit trail for all refund operations
Refund Validation Rules
- Order must be in 'paid' status
- Refund amount cannot exceed order total
- Tickets must be 'issued' or 'scanned' (not already refunded/void/disputed)
- Only organization members with admin privileges can create refunds
- Disputed orders cannot be refunded (must resolve dispute first)
Dispute Handling
Automatic Dispute Processing
The system automatically handles Stripe dispute webhooks:
charge.dispute.created:
- Finds order by payment intent/charge ID
- Locks all related tickets (status: 'locked_dispute')
- Creates dispute fee ledger entries if applicable
- Updates order with dispute information
charge.dispute.closed:
- If Won: Restores tickets to previous status ('issued'/'scanned')
- If Lost: Voids tickets and creates negative ledger entries (loss accounting)
Dispute Status Tracking:
// Order dispute field
{
dispute?: {
disputeId: string;
status: string;
reason: string;
outcome?: "won" | "lost";
createdAt: Timestamp;
closedAt?: Timestamp;
}
}
Ticket Status Flow:
Normal Flow: issued → scanned
Dispute Flow: issued → locked_dispute → (won: issued) | (lost: void)
Getting Dispute Information
Endpoint: POST /api/disputes/get
Request Body:
{ orderId: string; }
Response:
{
orderId: string;
dispute: {
disputeId: string;
status: string;
reason: string;
outcome?: string;
} | null;
}
Financial Ledger System
Ledger Entry Types
All financial transactions are recorded in the ledger collection:
interface LedgerEntry {
orgId: string;
eventId: string;
orderId: string;
type: "sale" | "refund" | "fee" | "platform_fee" | "dispute_fee";
amountCents: number; // Positive for revenue, negative for costs
currency: "USD";
stripe: {
balanceTxnId?: string;
chargeId?: string;
refundId?: string;
disputeId?: string;
accountId: string;
};
createdAt: Timestamp;
meta?: Record<string, any>;
}
Ledger Entry Creation
For Sales (in checkout.session.completed):
// Sale entry (+$150.00)
{ type: "sale", amountCents: 15000 }
// Platform fee (+$4.50 for platform)
{ type: "platform_fee", amountCents: 450 }
// Stripe processing fee (-$4.65 for organizer)
{ type: "fee", amountCents: -465 }
For Refunds (in createRefund and refund.created webhook):
// Refund entry (-$75.00)
{ type: "refund", amountCents: -7500 }
// Platform fee refund (-$2.25 for platform)
{ type: "platform_fee", amountCents: -225 }
// Stripe refund fee (varies by policy)
{ type: "fee", amountCents: -0 }
For Disputes (in charge.dispute.created):
// Dispute fee (-$15.00 typically)
{ type: "dispute_fee", amountCents: -1500 }
Reconciliation & Reporting
Reconciliation API
Endpoint: POST /api/reconciliation/data
Request Body:
{
orgId: string;
eventId?: string; // Optional: specific event or "all"
startDate: string; // ISO date string
endDate: string; // ISO date string
format?: "json" | "csv"; // Default: json
}
Response (JSON):
{
summary: {
grossSales: number; // Total sales amount
refunds: number; // Total refunds (positive)
stripeFees: number; // Total Stripe fees (positive)
platformFees: number; // Total platform fees (positive)
disputeFees: number; // Total dispute fees (positive)
netToOrganizer: number; // Final amount to organizer
totalTransactions: number; // Unique order count
period: { start: string; end: string; }
};
entries: LedgerEntry[]; // Detailed transactions
total: number; // Entry count
}
Calculation Formula:
Net to Organizer = Gross Sales - Refunds - Stripe Fees - Platform Fees - Dispute Fees
CSV Export
Request with format: "csv" returns downloadable CSV with:
- Summary Section: Period, totals, and key metrics
- Transaction Details: Complete ledger entries
- Headers: Date, Type, Amount, Order ID, Stripe Transaction ID, Account ID, Notes
CSV Structure:
SUMMARY
2024-01-01,Period Start,,,,,
2024-12-31,Period End,,,,,
,Gross Sales,150.00,,,,
,Net to Organizer,140.85,,,,
TRANSACTIONS
Date,Type,Amount,Order ID,Stripe Transaction ID,Account ID,Notes
2024-08-01T10:30:00Z,sale,150.00,order-123,txn_abc123,acct_def456,
2024-08-01T10:30:00Z,platform_fee,4.50,order-123,txn_abc123,acct_def456,
Frontend Components
RefundModal Component
Location: src/features/orders/RefundModal.tsx
Props:
interface RefundModalProps {
isOpen: boolean;
onClose: () => void;
order: {
id: string;
totalCents: number;
tickets: Array<{
id: string;
status: string;
priceCents: number;
ticketTypeName: string;
}>;
};
onRefundCreated?: (refundId: string) => void;
}
Features:
- Refund Type Selection: Full, partial, or specific tickets
- Dynamic Amount Calculation: Updates based on selection
- Validation: Prevents invalid amounts and states
- Real-time Feedback: Success/error states with detailed messages
- Permission Awareness: Disabled for users without refund permissions
OrdersTable Component
Location: src/features/orders/OrdersTable.tsx
Features:
- Order Display: Status, amounts, customer info, ticket details
- Dispute Alerts: Visual indicators for disputed orders
- Refund History: Shows existing refunds with amounts and dates
- Action Buttons: Context-aware refund and view options
- Real-time Updates: Refreshes data after refund operations
Reconciliation Component
Location: src/features/reports/Reconciliation.tsx
Features:
- Date Range Filtering: Custom period selection
- Event Filtering: All events or specific event
- Summary Cards: Key financial metrics with visual indicators
- Detailed Breakdown: Sortable transaction table
- CSV Export: Client-side download generation
- Real-time Calculations: Updates as filters change
Security & Permissions
Refund Permissions
Only users with these roles can create refunds:
- Super Admin: Global platform admin
- Organization Admin: Admin of the specific organization
- Territory Manager: Manager of organization's territory (future feature)
Permission Validation
// Server-side validation in all refund endpoints
async function checkRefundPermissions(uid: string, orgId: string): Promise<boolean> {
const userDoc = await db.collection("users").doc(uid).get();
const userData = userDoc.data();
// Super admin access
if (userData?.role === "super_admin") return true;
// Org admin access
if (userData?.organization?.id === orgId && userData?.role === "admin") return true;
return false;
}
API Security
- Authentication: All refund APIs require valid JWT token
- Authorization: Organization-based access control
- Input Validation: Comprehensive validation of all parameters
- Rate Limiting: Protection against abuse (configured at infrastructure level)
Error Handling & Monitoring
Comprehensive Error Handling
// Refund creation errors
{
"orderId is required" |
"Order not found" |
"Can only refund paid orders" |
"Invalid refund amount: {amount}. Must be between 1 and {max}" |
"Cannot refund ticket with status: {status}" |
"Insufficient permissions" |
"Refund failed" // With Stripe error details
}
Logging & Monitoring
Structured Logging:
console.log(`[create_refund] Starting refund creation`, {
orderId, ticketId, amountCents, uid, orgId,
timestamp: new Date().toISOString()
});
console.log(`[create_refund] Refund completed successfully`, {
refundId, stripeRefundId, amountCents,
processingTime: Date.now() - startTime
});
Key Metrics to Monitor:
- Refund success/failure rates
- Average refund processing time
- Dispute resolution outcomes
- Ledger entry consistency
- Permission denial attempts
Testing & Validation
Comprehensive Test Coverage
File: tests/refunds-disputes.spec.ts
Test Scenarios:
- ✅ Full order refunds with success validation
- ✅ Partial amount refunds with input validation
- ✅ Single and multiple ticket refunds
- ✅ Refund amount validation and error handling
- ✅ Permission-based access control
- ✅ Idempotency for duplicate refund requests
- ✅ Dispute status display and ticket locking
- ✅ Reconciliation calculations and CSV export
- ✅ Ledger entry creation and integrity
Manual Testing Checklist
- Admin can create all types of refunds
- Non-admin users get permission denied
- Refunded tickets show correct status
- Ledger entries balance with Stripe dashboard
- Disputed orders block refund attempts
- CSV export contains accurate data
- Duplicate refund requests handled gracefully
Production Deployment
Environment Variables
# Required for refunds and disputes
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET_CONNECT=whsec_...
EMAIL_API_KEY=re_...
APP_URL=https://portal.blackcanyontickets.com
PLATFORM_FEE_BPS=300 # 3% platform fee
Webhook Configuration
Connect Webhook Events (required for dispute handling):
charge.dispute.created
charge.dispute.closed
refund.created
checkout.session.completed
Database Indexes
Recommended Firestore indexes:
// ledger collection
{ orgId: "asc", createdAt: "desc" }
{ orgId: "asc", eventId: "asc", createdAt: "desc" }
{ orderId: "asc", type: "asc" }
// refunds collection
{ orgId: "asc", createdAt: "desc" }
{ orderId: "asc", createdAt: "desc" }
// orders collection
{ paymentIntentId: "asc" }
Troubleshooting
Common Issues
Refund Fails with Stripe Error:
- Check if payment intent supports refunds
- Verify connected account has sufficient balance
- Ensure webhook secrets are correctly configured
Ledger Entries Don't Match Stripe:
- Verify all webhook events are being processed
- Check for missing balance transaction data
- Validate fee calculations against Stripe dashboard
Disputes Not Processing:
- Confirm webhook endpoint is receiving events
- Verify
charge.dispute.*events are configured - Check account identification in webhook headers
CSV Export Issues:
- Verify
csv-writerdependency is installed - Check file system permissions for temporary files
- Validate date range parameters
Support Escalation
For production issues:
- Check Firebase Functions logs for detailed error traces
- Verify Stripe webhook delivery logs
- Validate ledger entry consistency with database queries
- Contact platform support with specific error messages and timestamps
Summary of Complete Implementation
✅ Security Enhancements
- Idempotency Protection: Prevents duplicate operations across all functions
- Transactional Safety: Atomic operations for inventory, refunds, and ledger entries
- Permission-Based Access: Role-based refund authorization with org isolation
- Input Validation: Comprehensive validation of all API inputs and business rules
- Account Isolation: Proper Stripe Connect account handling and data separation
✅ Financial Integrity
- Complete Ledger System: Every transaction recorded with full audit trail
- Automated Fee Tracking: Stripe processing fees and platform fees captured automatically
- Dispute Accounting: Proper handling of dispute outcomes with financial adjustments
- Reconciliation Tools: Real-time financial reporting with CSV export capabilities
- Multi-Currency Support: Foundation for international payment processing (USD only currently)
✅ Operational Excellence
- Comprehensive Error Handling: Graceful handling of all failure scenarios
- Structured Logging: Consistent audit trail optimized for monitoring and alerting
- Performance Monitoring: Processing times and success rates tracked across all operations
- Automated Testing: Complete test coverage for critical financial operations
- Production Readiness: Enterprise-grade configuration and deployment guidelines
✅ User Experience
- Intuitive Refund Interface: Easy-to-use modal with multiple refund options
- Real-time Status Updates: Live feedback on refund processing and dispute status
- Comprehensive Reporting: Detailed financial reconciliation with export capabilities
- Permission-Aware UI: Interface adapts based on user roles and permissions
- Mobile-Responsive Design: Full functionality across all device types
The refunds, disputes, and reconciliation system is now production-ready with enterprise-grade financial controls, comprehensive audit trails, and robust error handling! 💰🔒📊
Connecting Stripe for Organizers
Accessing Payment Settings
Organizers need to connect their Stripe account before they can publish events and accept payments.
Navigation Path:
- From any event detail page → "Connect Stripe Account" button in payment banner
- From publish modal → "Connect Payments" action button (with
data-testid="paymentCheck") - Direct URL:
/org/{orgId}/payments
React Integration Components
The frontend integration includes several key components with proper TypeScript support and testing:
PaymentSettings Component
Location: src/features/org/PaymentSettings.tsx
Features:
- Design Token Based: Uses semantic color and spacing tokens (no hardcoded values)
- Data Test IDs: All interactive elements have
data-testidattributes for testing - Zustand Integration: Connects to
useCurrentOrgStorefor real-time payment status - Mock API Integration: Uses
api.stripeConnectservice for consistent data fetching
Key Elements:
data-testid="connectBtn"- Connect/Continue Setup buttondata-testid="refreshBtn"- Refresh Status buttondata-testid="disconnectBtn"- Disconnect button- UX Callout: Shows warning when payment not connected for publishing
PublishEventModal Component
Location: src/features/events/PublishEventModal.tsx
Enhanced Features:
- Payment Checklist: Payment requirement with
data-testid="paymentCheck" - Publish Button:
data-testid="publishBtn"for E2E testing - Smart Actions: Direct link to payment settings when payment not connected
- Requirement Validation: Blocks publishing until all requirements met including payment
Stripe Connect Hooks
Location: src/hooks/useStripeConnect.ts
Provides:
useStripeConnect(orgId)- Main hook for all Stripe Connect operationsuseStripeConnectStatus(orgId)- Auto-fetching payment statususeStripeConnectStart(orgId)- Onboarding flow initiationuseStripeConnectRefresh(orgId)- Manual status refresh
State Management:
- Integrates with Zustand org store
- Mock API integration for demo/testing
- Proper loading and error states
- TypeScript interfaces for all data structures
API Integration
Mock API Endpoints (for frontend demo):
POST ${VITE_API_BASE}/stripe/connect/start- Start onboardingGET ${VITE_API_BASE}/stripe/connect/status- Get connection status
Service Layer:
import { api } from '../services/api';
// Start onboarding
const result = await api.stripeConnect.startOnboarding(orgId);
// Check status
const status = await api.stripeConnect.getConnectStatus(orgId);
Payment Settings Interface
The Payment Settings page shows:
Connection Status
- Provider: Always shows "Stripe" (only supported provider)
- Connected: ✅ (Green check) or ❌ (Warning badge)
- Business Name: Displayed when account is fully set up
- Account Details: Shows detailsSubmitted and chargesEnabled status
Action Buttons
- Connect Stripe: Starts initial onboarding flow
- Continue Setup: Resumes incomplete onboarding
- Refresh Status: Manually checks current Stripe account status
- Disconnect: Removes Stripe connection (stub implementation)
What "Connected" Means
An account is considered fully connected when:
detailsSubmitted: true- Business details provided to StripechargesEnabled: true- Stripe has approved the account for processing- Both conditions must be true for
connected: true
Onboarding Process
- Click Connect/Continue: Redirects to Stripe Connect onboarding
- Complete Forms: Provide business details, banking info, identity verification
- Submit for Review: Stripe reviews account (1-2 business days typical)
- Account Approved:
chargesEnabledbecomes true, account fully connected - Return to Platform: Automatic redirect back to Payment Settings
Status Messages
- Setup Required: Account created but details not submitted
- Under Review: Details submitted, waiting for Stripe approval
- Connected: Fully operational, can accept payments
Publish Flow Integration
Events cannot be published until:
- ✅ At least 1 active ticket type exists
- ✅ Valid event dates (start < end)
- ✅ Stripe account connected (
connected: true)
The publish modal enforces these requirements and provides direct links to resolve each issue.
Troubleshooting
Common Issues:
- Interrupted Onboarding: Use "Continue Setup" button to resume
- Long Review Times: Stripe may request additional documentation
- Account Restricted: Check Stripe dashboard for specific requirements
- Status Not Updating: Use "Refresh Status" button to sync latest data
Support:
- Platform issues → Contact BCT support
- Stripe account issues → Contact Stripe support directly
Testing & Quality Assurance
Comprehensive Test Suite
PaymentSettings Component Tests (src/features/org/__tests__/PaymentSettings.test.tsx):
- ✅ Renders payment settings page with correct elements
- ✅ Shows disconnected state when no payment info
- ✅ Displays connected status with account details
- ✅ Handles partially connected state with setup required
- ✅ Calls correct API endpoints with proper parameters
- ✅ Updates Zustand store on successful operations
- ✅ Displays loading states during API calls
- ✅ Shows error messages when API calls fail
- ✅ Handles URL parameters for post-onboarding flows
- ✅ Shows development info in DEV mode
PublishEventModal Component Tests (src/features/events/__tests__/PublishEventModal.test.tsx):
- ✅ Renders modal with requirements checklist
- ✅ Shows loading state while checking requirements
- ✅ Displays all requirements with proper pass/fail states
- ✅ Blocks publishing when payment not connected
- ✅ Shows payment checklist with correct data-testid
- ✅ Enables publish button when all requirements met
- ✅ Handles successful publishing flow with auto-close
- ✅ Provides action buttons for fixing failed requirements
- ✅ Maintains proper accessibility attributes
- ✅ Handles error states gracefully
Testing Patterns
Data Test IDs:
data-testid="connectBtn"- Connect/Continue Setup buttondata-testid="refreshBtn"- Refresh Status buttondata-testid="disconnectBtn"- Disconnect buttondata-testid="paymentCheck"- Payment requirements checklist itemdata-testid="publishBtn"- Publish Event button
Mock API Integration:
// Tests use consistent mocking patterns
global.fetch = vi.fn();
mockFetch.mockResolvedValue({
ok: true,
json: async () => ({ payment: mockPaymentData }),
});
Zustand Store Testing:
// Store integration is mocked and validated
(useCurrentOrgStore as any).mockReturnValue({
org: mockOrg,
updatePaymentStatus: mockUpdatePaymentStatus,
});
expect(mockUpdatePaymentStatus).toHaveBeenCalledWith(expectedPaymentData);
End-to-End Testing
Critical User Flows:
-
Payment Connection Flow:
- Navigate to payment settings
- Click connect button
- Redirect to Stripe (mocked)
- Return with success status
- Verify payment status updated
-
Publishing with Payment Check:
- Open publish modal
- Verify payment requirement shows connected status
- Publish button enabled when all requirements met
- Successful publishing updates event status
-
Error Handling Paths:
- API failures show appropriate error messages
- Loading states provide user feedback
- Permission errors prevent unauthorized actions
Performance & Accessibility
Key Metrics:
- ✅ WCAG AA Compliance: Proper ARIA labels and keyboard navigation
- ✅ Mobile Responsive: Works on all device sizes
- ✅ Loading States: Immediate user feedback during API calls
- ✅ Error Recovery: Clear error messages with retry options
- ✅ Design Token Usage: No hardcoded colors or spacing values
Browser Support:
- Modern browsers with ES2020+ support
- Mobile Safari and Chrome
- Desktop Firefox, Chrome, Edge, Safari
Production Readiness Checklist
- All components use design tokens (no hardcoded styles)
- Comprehensive data-testid coverage for E2E testing
- TypeScript interfaces for all props and state
- Error boundaries and graceful failure handling
- Loading states for all async operations
- Accessibility compliance with proper ARIA attributes
- Mock API integration for frontend-only testing
- Comprehensive test coverage with React Testing Library
- Integration with existing Zustand state management
- Mobile-first responsive design implementation