Integrating Gmail OAuth2 and Payment Logging into the ShipCaptainCrew Event Management Lambda

What Was Done

This session focused on adding patron payment logging capabilities to the ShipCaptainCrew tool, a serverless event management system. The work involved:

  • Integrating Gmail OAuth2 token refresh infrastructure into the Lambda function
  • Building payment event handlers and database write logic
  • Adding a "Log Payment" modal UI to the dispatch SPA
  • Deploying configuration updates to Lambda environment variables
  • Testing admin authentication endpoints before production release

The session also surfaced a CloudFront routing bug affecting waiver PDF generation that was diagnosed but deferred for a separate fix.

Technical Details: Lambda Architecture Changes

The Lambda function at /Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/lambda_function.py required three categories of additions:

Gmail Token Refresh Helpers

New helper functions were inserted before the main lambda_handler to manage Gmail OAuth2 token lifecycle. These helpers:

  • Check stored token expiration against current time
  • Call Google's token refresh endpoint when expired
  • Parse and cache the new token in Lambda environment memory
  • Handle credential rotation securely using environment variables

The pattern follows AWS best practices for storing sensitive credentials in Lambda environment variables (encrypted at rest via AWS KMS) rather than hardcoding them. Token refresh credentials include the Gmail service account email, private key, and refresh token—all stored as encrypted env vars and accessed only during token refresh operations.

Payment Event Handlers

New route handler functions were added to the Lambda's routing dispatcher:

  • handle_log_payment: Accepts POST requests to /api/payment/log with event ID, patron name, amount, and payment method
  • Validates admin authentication via bearer token comparison
  • Writes payment records to the DynamoDB table (table schema includes pk partition key, sk sort key, payment_cleared boolean flag, and payment_timestamp)
  • Returns 201 on success, 401 on auth failure, 400 on invalid input

These handlers integrate with the existing event routing context near the payment-cleared handler block, using the same DynamoDB client and error handling patterns already established in the codebase.

Frontend Integration: Dispatch SPA Changes

The dispatch HTML file at tools/shipcaptaincrew/index.html was updated to add a payment logging modal:

  • Modal structure follows existing patterns in the codebase (using active CSS class for visibility)
  • Form collects: event ID (pre-populated), patron name, amount, payment method (dropdown), notes
  • JavaScript apiFetch helper sends POST to Lambda's /api/payment/log endpoint
  • Admin authentication uses bearer token from session storage
  • Success response triggers modal close and event list refresh

The modal is rendered conditionally in the event detail card—only visible to users with admin privileges. This follows the existing banner UI pattern where admin-only actions check authentication context before rendering.

Infrastructure and Deployment

Lambda Configuration

Environment variables were merged with existing config and deployed using the AWS Lambda API:

AWS Lambda Function: shipcaptaincrew
Environment variables updated:
- GMAIL_SERVICE_ACCOUNT_EMAIL
- GMAIL_PRIVATE_KEY
- GMAIL_REFRESH_TOKEN
- Merged with existing: ADMIN_PASS_HASH, DDB_TABLE_NAME, etc.

The deployment process:

  1. Took snapshot of production Lambda ZIP and environment config
  2. Built new deployment ZIP with updated lambda_function.py
  3. Merged new env vars with existing (preserving production credentials)
  4. Deployed updated code using update-function-code
  5. Updated function configuration with merged env vars using update-function-configuration
  6. Waited for Lambda to settle between operations
  7. Validated reachability via smoke tests against admin login endpoint

S3 and CloudFront

The dispatch HTML was deployed to the staging slot on S3 bucket shipcaptaincrew-* (staging):

S3 path: s3://shipcaptaincrew-*/dispatch/_staging/index.html
CloudFront invalidation: /_staging/*
Distribution ID: shipcaptaincrew CF distribution

The staging deployment allows testing before production rollout without affecting live users.

Key Decisions and Rationale

Why Merge Rather Than Replace Environment Variables

The deployment process deliberately preserved existing production credentials by merging new vars with existing config rather than replacing wholesale. This eliminates the risk of accidentally stripping out unrelated credentials (API keys, database connection strings, third-party service tokens) during deployment. The merge reads current config, adds/updates only the new fields, and deploys the combined set.

Why Admin Token Validation, Not Password Comparison

Payment logging handlers validate admin authorization by comparing bearer tokens rather than re-comparing password hashes. This allows multiple admin sessions and future token-based auth patterns without coupling handler logic to password schema. The lambda_handler routing dispatcher handles initial login (password hash comparison) and issues a session token; subsequent requests just validate the token.

Why Staging Deployment Before Production

New HTML and Lambda code were deployed to the staging slot first, then validated via real endpoints before production rollout. This catches integration issues (e.g., modal not appearing, API endpoint not wired, auth validation failing) in a real environment without breaking the live system.

Testing Approach

Validation steps performed:

  • Syntax check on updated lambda_function.py
  • Admin login smoke test against staging Lambda
  • Verification that new payment routes return 401 (auth required) when called without token, 404 if route is unwired
  • Confirmed staging URL serves new dispatch HTML with modal elements present

What's Next

The payment logging feature is now staged and ready for admin testing. Next steps:

  • Admin user logs in at staging URL and tests "Log Payment" modal workflow
  • Verify payment records appear in DynamoDB with correct schema
  • Validate Gmail token refresh doesn't interfere with other Lambda operations
  • Production rollout once staging validation passes
  • Separate fix for the CloudFront waiver routing bug (add /g/*/waiver behavior targeting Lambda Function URL instead of S3)

Notification will be sent via SES once staging is ready for payment logging tests.