Implementing Payment Logging for Event Crew: Lambda Handlers, DynamoDB Schema Integration, and CloudFront Routing Fixes

Overview

This session focused on implementing a payment logging feature for the Ship Captain Crew event management platform, enabling crew members to record patron payments directly from the dispatch dashboard. The work involved extending the Lambda function with new payment handlers, integrating DynamoDB operations, adding a modal UI component to the dispatch SPA, and addressing a CloudFront routing misconfiguration that was causing waiver page loads to fail.

What Was Done

  • Extended /Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/lambda_function.py with Gmail credential helpers and payment event handlers
  • Updated dispatch HTML (index.html) with a "Log Payment" modal component and corresponding UI controls
  • Validated Lambda environment variables for Gmail integration (token retention)
  • Deployed code and configuration to the staging environment via S3 and CloudFront invalidation
  • Diagnosed waiver routing issue: /g/{event_id}/waiver requests were being routed to S3 instead of Lambda, causing the SPA to misparse the URL and fail to load event data

Technical Details: Lambda Handler Architecture

The payment logging feature required adding two new handler functions to the Lambda routing layer. The handlers follow the existing pattern in lambda_function.py, which uses a request dispatcher that examines the HTTP method and path to route to appropriate handlers.

Handler Integration Points:

  • handle_log_payment(event) — POST endpoint that accepts crew payment submissions. This handler validates the request context (must be authenticated crew), extracts payment details from the request body, writes to DynamoDB, and returns a success or error response.
  • handle_get_event(event) (existing) — Already returns event details; verified it includes the fields needed for payment UI binding (event_id, total_price, payments array)
  • Payment handler routing entry inserted near line 1380 in the lambda_handler dispatcher, following existing conditional structure (method + path matching)

Gmail Helper Functions:

Added utility functions to construct and send notification emails via SES when a payment is logged. Environment variables GMAIL_TOKEN, GMAIL_SENDER, and GMAIL_RECIPIENT were verified present in Lambda configuration. The helpers construct email bodies with event details and payment confirmation information, allowing crew to track payment submissions.

DynamoDB Integration:

Payment records are appended to the event item's payments array attribute. The schema was verified via DynamoDB DescribeTable; the events table uses event_id (partition key) and supports nested list operations. The payment handler uses boto3's UpdateExpression with SET payments = list_append(payments, :new_payment) to atomically append payment records, avoiding race conditions when multiple crew members log payments simultaneously.

Dispatch HTML Modal Component

The "Log Payment" modal was added to index.html following the existing modal pattern (hidden by default, displayed via active class toggle). Key implementation details:

  • Modal HTML inserted after the existing event-detail modal, maintaining consistent DOM structure
  • Form inputs capture: amount, payment method (dropdown: cash, card, check), note/reference
  • Submit button calls submitPayment(), a new JavaScript function that:
    • Validates form inputs client-side
    • Calls apiFetch('/api/events/{eventId}/payment', {method: 'POST', ...})
    • Handles success/error responses with appropriate UI feedback
    • Clears form and closes modal on success
  • Button to toggle modal visibility inserted in the event card render function, visible only when user has crew role

Infrastructure & Deployment

S3 Bucket: shipcaptaincrew (S3 root for dispatch assets)

CloudFront Distribution: shipcaptaincrew CF distribution ID was used for cache invalidation via /*/_staging/* pattern to ensure staging environment received fresh HTML.

Lambda Function: shipcaptaincrew Lambda function URL was re-deployed after code changes. Environment variables (including Gmail credentials retained from prior session) were merged and pushed to the function before code deployment to avoid timing issues.

Deployment Order (Critical):

  1. Snapshot current prod Lambda zip and prod HTML (for rollback safety)
  2. Validate Gmail token fields present in current env vars
  3. Update Lambda environment variables with merged config (new vars + existing creds)
  4. Wait for Lambda config update to settle (~5-10 seconds)
  5. Deploy updated Lambda code zip
  6. Wait for code update to settle
  7. Deploy HTML to staging slot on S3 (key: _staging/index.html)
  8. Invalidate CloudFront /_staging/*

This ordering prevents code-to-config skew: if code deploys before env vars are live, the function may not find credentials.

Waiver Route Diagnosis (Not Yet Fixed)

Investigation revealed a CloudFront routing misconfiguration: requests to /g/2026-05-23/waiver are currently routed to S3 (which doesn't have that object), falling through to the dispatch SPA. The SPA misparses 2026-05-23/waiver as an event_id, attempts to fetch /api/g/2026-05-23/waiver, receives HTML from the Lambda waiver handler, and fails with "Could not load event" when it tries to parse HTML as JSON.

Root Cause: CloudFront lacks a behavior routing /g/*/waiver requests to the Lambda Function URL origin. Currently, only /api/* and /admin/* routes point to Lambda; waiver routes fall through to the default S3 origin.

Fix Required: Add a CloudFront behavior with path pattern /g/*/waiver, origin Lambda Function URL, origin request policy allowing authorization headers.

Bonus Issue: Event slug 2026-05-23 violates the established naming convention (should be YYYY-MM-DD-firstname-[period], e.g., 2026-05-23-alice-whale). This should be enforced at event creation time or corrected in the table.

Key Decisions

  • List Append over Item Replace: Used DynamoDB list_append UpdateExpression instead of replacing the entire event item, minimizing write contention and preserving payment history atomically.
  • Client-Side Validation First: Form validation happens in the browser before API calls, reducing Lambda invocations and providing immediate user feedback.
  • SES for Notifications: Email confirmations are sent synchronously within the payment handler, ensuring crew receives feedback; alternatively, could queue async via SNS if payment throughput becomes high.
  • Staging