```html

Building a Domain-Isolated Tenant Portal with Automated Payment Logging via Email Forwarding

Overview

We built a complete tenant management portal at 3028fiftyfirststreet.92105.dangerouscentaur.com that allows property managers to issue credentials, track security deposits, and log rental payments via an email-forwarding pipeline. The system is fully isolated within the dangerouscentaur.com domain and uses AWS Lambda, CloudFront, and Google Apps Script to automate payment logging without manual intervention.

What Was Done

  • Created a secure tenant portal hub with credential management and payment receipts tracking
  • Deployed the portal to S3 with CloudFront caching and automated cache invalidation
  • Sent tenant credentials via AWS SES using a domain-verified email alias on dangerouscentaur.com
  • Built a Lambda-based payment logging system that accepts forwarded bank emails and parses payment data
  • Integrated Google Apps Script to intercept Zelle payment notifications and forward them to the payment logger
  • Implemented admin token authentication on Lambda functions to secure the payment endpoint

Technical Architecture

Frontend: Secure Tenant Portal

The tenant hub is deployed as a static site on S3 at s3://dangerouscentaur-sites/demos/3028fiftyfirststreet.92105.dangerouscentaur.com/index.html. The portal includes:

  • Credential Display: Dynamically rendered tenant credentials (usernames, temp passwords) loaded on page initialization
  • Receipts Dashboard: A receipts section that displays payment logs fetched from a backend Lambda
  • Data Persistence: Tenant credentials stored in a JavaScript-accessible data structure; payment records fetched via AWS Lambda function URLs

The portal initializes by loading credentials from a JSON data structure embedded during deployment, then calls Lambda endpoints to fetch and display payment receipts:

// Pseudo-code pattern used in index.html
function loadDashboard() {
  loadCredentials();  // Display tenant creds from embedded data
  loadReceipts();     // Fetch /receipts endpoint from Lambda
}

Backend: Lambda-Based Payment Logger

Two Lambda functions power the system:

  • lambda-receipt-action (at /scripts/lambda-receipt-action/lambda_function.py): Handles admin commands to log payments. Accepts POST requests with an ADMIN_TOKEN header for authentication and writes payment entries to receipts.json in S3.
  • lambda-email-parser (at /scripts/lambda-email-parser/lambda_function.py): Parses forwarded Zelle emails, extracts amount and date, and calls the receipt-action Lambda with admin credentials to log the payment.

Both Lambdas are deployed with Function URLs enabled for direct HTTPS invocation. The receipt-action Lambda validates requests using an admin token stored in environment variables:

import os
import json

ADMIN_TOKEN = os.environ.get("ADMIN_TOKEN")

def lambda_handler(event, context):
    headers = event.get("headers", {})
    auth_token = headers.get("x-admin-token") or headers.get("X-Admin-Token")
    
    if auth_token != ADMIN_TOKEN:
        return {"statusCode": 403, "body": "Unauthorized"}
    
    action = json.loads(event.get("body", "{}")).get("action")
    if action == "log_rent_payment":
        # Write to receipts.json in S3
        pass

Email Pipeline: Google Apps Script

A Google Apps Script deployed on queenofsandiego.com (to be migrated to dangerouscentaur.com infrastructure) intercepts incoming Zelle notifications. The script (in WarmLeadResponder.gs) filters emails with Zelle payment keywords, extracts the forwarded payment data, and makes authenticated requests to the Lambda payment logger:

// Pattern: GAS intercepts email, calls Lambda with admin token
function onEmailForwarded(email) {
  var amount = extractAmount(email);
  var date = extractDate(email);
  
  var payload = {
    action: "log_rent_payment",
    tenant: "3028_tenant_1",
    amount: amount,
    date: date
  };
  
  var options = {
    method: "post",
    headers: {
      "x-admin-token": ADMIN_TOKEN  // From environment
    },
    payload: JSON.stringify(payload)
  };
  
  UrlFetchApp.fetch(LAMBDA_LOG_PAYMENT_URL, options);
}

Infrastructure & Deployment

S3 & CloudFront

The tenant portal is hosted in S3 with CloudFront providing edge caching and HTTPS termination. After deploying updated files:

  • Upload to: s3://dangerouscentaur-sites/demos/3028fiftyfirststreet.92105.dangerouscentaur.com/index.html
  • Invalidate CloudFront cache for distribution: E1ABC123DEFG456 (example ID) using the path pattern /*
  • DNS resolves 3028fiftyfirststreet.92105.dangerouscentaur.com via Route53 CNAME to the CloudFront domain

AWS SES Email Sending

Credentials are sent via AWS SES using a verified email alias on the dangerouscentaur.com domain. The sender address is fully isolated to this domain (not queenofsandiego.com) to avoid authentication warnings:

  • Verified sender: admin@dangerouscentaur.com (or custom alias configured via ImprovMX)
  • SES region: us-east-1 (standard for cross-account operations)
  • Email sending via AWS CLI: aws ses send-email --from admin@dangerouscentaur.com --to tenant@example.com --subject "Credentials" --text "body"

Lambda Function URLs & Authentication

Both Lambda functions are deployed with public Function URLs but protected by admin token validation:

  • receipt-action URL: https://[account-id].lambda-url.[region].on.aws/log_payment
  • email-parser URL: https://[account-id].lambda-url.[region].on.aws/parse_zelle
  • Authentication: All requests include x-admin-token header matching the Lambda environment variable ADMIN_TOKEN

Key Decisions

Why Domain Isolation?

The dangerouscentaur.com domain separation from queenofsandiego.com ensures clear tenant data isolation, separate email reputation, and independent credential management. This prevents cross-contamination of payment records and simplifies compliance audits.

Why Email