Files
blackcanyontickets/reactrebuild0825/STRIPE_CHECKOUT_GUIDE.md
dzinesco aa81eb5adb feat: add advanced analytics and territory management system
- 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>
2025-08-26 09:25:10 -06:00

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_BPS environment variable (default 3%)
  • Creates checkout with connected account using stripeAccount parameter
  • 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 minting
  • payment_intent.succeeded - Additional payment tracking

Ticket Minting Process:

  1. Idempotency Check: Creates processedSessions/{sessionId} document to prevent duplicates
  2. Inventory Transaction: Atomically validates and decrements inventory
  3. Ticket Generation: Creates individual tickets with UUID QR codes
  4. Order Update: Marks order as 'paid' with payment details
  5. Email Delivery: Sends confirmation email with QR code links
  6. 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

  1. Setup: Ensure organization has connected Stripe account
  2. Create Event: With published status and active ticket types
  3. 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

  1. Payment Fails: User sees error, can retry
  2. Webhook Delay: Tickets may take 1-2 minutes to appear
  3. Inventory Conflict: Prevented by atomic operations
  4. 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

  1. Email Notifications: Send ticket confirmations via Resend/SendGrid
  2. PDF Generation: Create downloadable ticket PDFs with QR codes
  3. Calendar Integration: Add event to customer calendars
  4. Refund Handling: Secure refund processing with organization validation
  5. Bulk Purchases: Handle group bookings and corporate sales
  6. Waitlist Management: Handle sold-out scenarios with waitlist functionality
  7. Dynamic Pricing: Time-based and demand-based pricing strategies
  8. Multi-Currency Support: International payment processing

Performance Optimizations

  1. Webhook Batching: Process multiple events in single transaction
  2. Inventory Caching: Redis caching for high-traffic events
  3. Database Sharding: Partition tickets by event for scale
  4. Queue Processing: Async ticket generation for very large orders
  5. CDN Integration: Cache static checkout pages

Analytics Integration

  1. Sales Tracking: Revenue and conversion analytics with refund adjustments
  2. Customer Insights: Purchase behavior analysis and fraud detection
  3. Event Performance: Attendance and sales metrics with real-time updates
  4. Platform Metrics: Fee collection and growth tracking with configurable rates
  5. Operational Analytics: Webhook processing times, error rates, and idempotency metrics
  6. 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:

  1. Full Order Refund: Refunds entire order amount

    { orderId: "order_123" }
    
  2. Single Ticket Refund: Refunds specific ticket at ticket price

    { orderId: "order_123", ticketId: "ticket_456" }
    
  3. Partial Amount Refund: Custom refund amount

    { orderId: "order_123", amountCents: 2500 } // $25.00
    
  4. 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:

  1. Finds order by payment intent/charge ID
  2. Locks all related tickets (status: 'locked_dispute')
  3. Creates dispute fee ledger entries if applicable
  4. Updates order with dispute information

charge.dispute.closed:

  1. If Won: Restores tickets to previous status ('issued'/'scanned')
  2. 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-writer dependency is installed
  • Check file system permissions for temporary files
  • Validate date range parameters

Support Escalation

For production issues:

  1. Check Firebase Functions logs for detailed error traces
  2. Verify Stripe webhook delivery logs
  3. Validate ledger entry consistency with database queries
  4. 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-testid attributes for testing
  • Zustand Integration: Connects to useCurrentOrgStore for real-time payment status
  • Mock API Integration: Uses api.stripeConnect service for consistent data fetching

Key Elements:

  • data-testid="connectBtn" - Connect/Continue Setup button
  • data-testid="refreshBtn" - Refresh Status button
  • data-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 operations
  • useStripeConnectStatus(orgId) - Auto-fetching payment status
  • useStripeConnectStart(orgId) - Onboarding flow initiation
  • useStripeConnectRefresh(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 onboarding
  • GET ${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:

  1. detailsSubmitted: true - Business details provided to Stripe
  2. chargesEnabled: true - Stripe has approved the account for processing
  3. Both conditions must be true for connected: true

Onboarding Process

  1. Click Connect/Continue: Redirects to Stripe Connect onboarding
  2. Complete Forms: Provide business details, banking info, identity verification
  3. Submit for Review: Stripe reviews account (1-2 business days typical)
  4. Account Approved: chargesEnabled becomes true, account fully connected
  5. 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 button
  • data-testid="refreshBtn" - Refresh Status button
  • data-testid="disconnectBtn" - Disconnect button
  • data-testid="paymentCheck" - Payment requirements checklist item
  • data-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:

  1. Payment Connection Flow:

    • Navigate to payment settings
    • Click connect button
    • Redirect to Stripe (mocked)
    • Return with success status
    • Verify payment status updated
  2. 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
  3. 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