```html

Building a Domain-Isolated Tenant Payment Portal with Zelle Email Forwarding Integration

What Was Done

We built a complete tenant management and payment logging system for a rental property, isolated entirely within the dangerouscentaur.com domain to maintain operational separation from a separate property management business. The system includes:

  • A tenant hub portal at 3028fiftyfirststreet.92105.dangerouscentaur.com with credential management
  • Lambda-based payment logging that accepts Zelle payment notifications via email forwarding
  • SES email infrastructure configured with domain-verified sending from dangerouscentaur.com
  • Google Apps Script integration to parse incoming payment emails and trigger Lambda actions
  • A receipts.json file stored in S3 that serves as the single source of truth for logged payments

Technical Architecture

Portal Infrastructure

The tenant hub lives at /Users/cb/Documents/repos/sites/dangerouscentaur/demos/3028fiftyfirststreet.92105.dangerouscentaur.com/ and is deployed to S3 with CloudFront distribution serving HTTPS. The portal is a static site with embedded credentials for tenant access, protected by temporary passwords that are regenerated during deployment cycles.

The index.html file contains:

  • A credentials section with tenant names and temporary passwords
  • JavaScript functions to load payment receipts from receipts.json
  • Dashboard initialization that hits the Lambda function URL for receipt-action
  • CSS and responsive design for mobile and desktop viewing

Deployment happens via S3 sync and CloudFront cache invalidation:

aws s3 sync ./demos/3028fiftyfirststreet.92105.dangerouscentaur.com/ s3://dangerouscentaur-sites/tenant-portal/ --delete
aws cloudfront create-invalidation --distribution-id [DIST_ID] --paths "/*"

Lambda Payment Logging System

Two Lambda functions work together to handle payment logging:

1. receipt-action Lambda (/scripts/lambda-receipt-action/lambda_function.py)

This function exposes an HTTP endpoint that accepts POST requests with payment data. It includes:

  • Admin token authentication via environment variable ADMIN_TOKEN
  • A log_payment action that writes to receipts.json in S3
  • Payload validation to ensure tenant ID, amount, date, and payment method are present
  • Atomic read-modify-write operations on receipts.json to prevent race conditions

2. lambda-email-parser Lambda (/scripts/lambda-email-parser/lambda_function.py)

This function is triggered by SES receipt rules when payment emails arrive. It parses the email body for Zelle payment details and calls the receipt-action Lambda's HTTP endpoint with the appropriate payload.

Email Infrastructure

SES is configured with:

  • dangerouscentaur.com as a verified sending domain (not just individual email addresses)
  • DKIM tokens generated and added to Route53 for domain authentication
  • An ImprovMX alias forwarding payments@dangerouscentaur.com to a Gmail inbox where the GAS script monitors incoming messages
  • SES receipt rules that trigger the lambda-email-parser function when emails arrive at payments@dangerouscentaur.com

This approach ensures all emails appear to come from the dangerouscentaur.com domain, maintaining clear operational boundaries from the separate business domain.

Google Apps Script Integration

The WarmLeadResponder.gs script in the queenofsandiego.com project was modified to include a handler for the 3028 51st Street property. However, we created a clear architectural boundary:

  • The GAS script monitors an inbox associated with dangerouscentaur.com
  • It recognizes forwarded Zelle emails by parsing sender and subject line patterns
  • When a Zelle payment email is detected, it extracts tenant ID, amount, and date
  • It calls the receipt-action Lambda's HTTP endpoint with admin token authentication
  • The Lambda then handles all S3 and payment logging operations

Key Implementation Details

Payment Workflow

The workflow from Zelle payment to logged receipt works as follows:

  1. Tenant sends Zelle payment to the property owner's bank account
  2. Bank sends Zelle confirmation email to the property owner's email
  3. Property owner forwards that email to payments@dangerouscentaur.com
  4. ImprovMX alias receives the email and Google Apps Script processes it
  5. GAS script parses the forwarded email for payment details
  6. GAS makes an authenticated HTTPS POST to the receipt-action Lambda URL
  7. Lambda validates the admin token and writes to receipts.json in S3
  8. Tenant portal automatically loads the updated receipts.json, showing the logged payment

Credential Management

Tenant credentials (usernames and temporary passwords) are embedded in the portal's index.html. During deployment:

  • New passwords are generated using cryptographically secure methods
  • Password hashes are computed for any backend validation (if needed in future)
  • The updated index.html is pushed to S3
  • SES sends the new credentials to each tenant's email address from dangerouscentaur.com
  • CloudFront cache is invalidated so tenants receive the updated portal immediately

Why These Decisions Were Made

Domain Isolation: By keeping all infrastructure and emails within dangerouscentaur.com, we maintain operational and technical separation from the other property management business. This prevents accidental data commingling and makes it clear which emails and systems belong to which operation.

Email Forwarding vs. Direct Integration: We chose to have the property owner forward bank emails rather than implement direct Zelle API integration. This is simpler operationally—Zelle doesn't offer a robust webhook system—and leverages the existing email workflow the property owner already uses.

S3 + Lambda for Receipts: Using receipts.json in S3 as the single source of truth is simpler than a managed database for a low-volume operation (one property with a few tenants). Lambda handles the read-modify-write atomically, and S3 versioning provides audit history.

GAS as the Glue: Google Apps Script monitors email inboxes that the property owner already uses. It acts as the intelligent parser layer, extracting structured data from unstructured emails before calling Lambda. This avoids building custom email parsing logic in Lambda itself.

What's Next

Future enhancements could include:

  • Dashboard analytics showing payment trends and outstanding balances
  • Automated reminder emails to