```html

Building a Payment Logger for Event Crew: Lambda Handlers, CloudFront Routing, and Modal-Driven UX

What Was Done

This session added a patron payment logging system to the Ship Captain Crew tool, enabling event crew to record customer payments without leaving the dispatch dashboard. The work involved three layers:

  • Backend: New Lambda handler handle_payment_log that persists payment records to DynamoDB
  • Frontend: Modal UI in the dispatch HTML for capturing payment details (amount, method, notes)
  • Infrastructure: CloudFront routing fix for waiver endpoints and environment variable management for Gmail integration

This followed a critical discovery: the local development files were significantly stale (976 lines vs. 2463 lines in S3), requiring a full pull before any modifications per the Rule D1 protocol.

Technical Details: Lambda Architecture

The payment logger integrates into the existing event-driven Lambda structure at /Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/lambda_function.py.

New Handler: Payment Logging

The handle_payment_log function was inserted before the main lambda_handler routing block. It accepts POST requests to /api/events/{event_id}/payment with a JSON body:

{
  "amount": 150.00,
  "method": "card",
  "patron_name": "John Doe",
  "notes": "Deposit for charter"
}

The handler validates the authenticated user is crew-level or admin, then writes a DynamoDB item to the shipcaptaincrew table with:

  • PK: Event ID (existing partition key pattern)
  • SK: PAYMENT#{timestamp}#{uuid} (new sort key prefix for payment records)
  • logged_by: Crew member's email
  • logged_at: ISO 8601 timestamp
  • payment_amount, payment_method, patron_name, notes: User-supplied fields

This design isolates payment logs from event items while maintaining query efficiency—crew can fetch all payments for an event with a single DynamoDB query on the Event ID + PAYMENT prefix.

Authentication & Authorization

The handler reuses existing auth helpers already present in Lambda (verify_jwt_token, get_user_role). Payment logging is restricted to crew and admins; customers cannot log payments.

Frontend: Dispatch HTML Modal

The dispatch HTML at /Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/index.html received a new modal component. Rather than creating custom styling, the modal follows the existing pattern: a hidden <div id="payment-log-modal"> with display: none by default, toggled to display: block via JavaScript when crew clicks "Log Payment" on an event card.

The modal includes:

  • Amount input (type="number", step="0.01", required)
  • Payment method dropdown (card, cash, check, other)
  • Patron name field (optional)
  • Notes textarea for context
  • Submit button that calls the logPayment function

JavaScript Handler: logPayment()

The logPayment() function gathers form values, calls apiFetch to POST to /api/events/{eventId}/payment, handles errors by displaying a banner message, and on success dismisses the modal and refreshes the event list.

The function integrates with existing infrastructure: it reuses the apiFetch helper (which injects auth tokens) and the banner system for user feedback.

Infrastructure: CloudFront & Routing Corrections

During exploration, a routing bug was discovered and fixed: waiver requests for /g/{event_id}/waiver were being routed to S3 instead of the Lambda Function URL, causing the SPA to misparse the path. This required adding a new CloudFront behavior:

  • Path Pattern: /g/*/waiver
  • Origin: Lambda Function URL (not S3)
  • Cache Policy: Managed-CachingDisabled (no caching for dynamic Lambda responses)
  • Distribution ID: shipcaptaincrew CloudFront distribution

This was deployed via the AWS Console and invalidated with aws cloudfront create-invalidation --distribution-id {ID} --paths "/*" to clear edge caches.

Environment Variables & Secrets Management

The Lambda function environment variables were audited and updated to ensure Gmail OAuth credentials (for potential email notifications) were preserved during deployment. The process involved:

  1. Taking a snapshot of the production Lambda zip and env vars
  2. Building a new zip from local source
  3. Merging the new code with the existing prod env vars using aws lambda update-function-configuration
  4. Deploying the updated code with aws lambda update-function-code
  5. Allowing 30 seconds for AWS to finalize config changes before code deployment (CloudFormation eventual consistency)

This prevented accidental loss of Gmail tokens and other secrets not tracked in version control.

Testing & Staging Deployment

The updated dispatch HTML was deployed to the staging slot (/_staging/* path in S3) and served via CloudFront. Smoke tests verified:

  • Admin login remained functional (existing handle_admin_login)
  • Event list API (/api/events) returned properly
  • Modal rendered without JavaScript errors
  • Routing to Lambda for API calls worked correctly

CloudFront invalidation was issued for /_staging/* to ensure fresh content delivery.

Key Decisions & Rationale

Sort Key Prefix Pattern (PAYMENT#): This allows efficient queries for payments on an event while keeping payment records separate from the event item itself, avoiding schema creep and maintaining DynamoDB partition health.

Timestamp + UUID in SK: Guarantees uniqueness even if two crew members log payments simultaneously, and enables natural sorting by time.

Modal Over Page Navigation: Crew remain on the event list, maintaining context and workflow. This reduces context-switching friction for high-volume payment logging during busy charter days.

Reuse of Existing Auth & API Patterns: Rather than inventing new helpers, the payment handler plugs into the existing apiFetch, JWT verification, and banner notification system. This reduces code duplication and cognitive load on future maintainers.

What's Next

The system is staged and ready for crew testing.