```html

Building a Payment-to-Portal Pipeline: Decoupling Tenant Communications from Marketing Infrastructure

What Was Done

We implemented a complete payment logging and credential distribution system for the tenant hub at 3028fiftyfirststreet.92105.dangerouscentaur.com, with a critical architectural requirement: complete isolation from the queenofsandiego.com marketing domain. This involved three major components:

  • Regenerating tenant credentials and redeploying the tenant portal
  • Establishing a dedicated dangerouscentaur.com SES sender identity for tenant communications
  • Building a Zelle payment forwarding system that auto-logs deposits to the tenant hub without manual intervention

The Credential Regeneration Problem

Credentials had been stored in plaintext in the tenant hub's index.html file, at /Users/cb/Documents/repos/sites/dangerouscentaur/demos/3028fiftyfirststreet.92105.dangerouscentaur.com/index.html. While this is acceptable for a development demo (credentials are ephemeral and regeneratable), the deployment approach needed hardening:

  • Generated fresh temporary passwords with bcrypt hashing
  • Updated the hub's user credentials table with new hashes
  • Deployed updated HTML to S3 at bucket s3://dangerouscentaur-sites/
  • Invalidated the CloudFront distribution cache (distribution ID: determined via AWS CLI) to force edge servers to pull fresh assets

This approach trades some convenience (plain credentials) for auditability—password changes are now tracked in git history and deployments are timestamped through S3 object versions.

SES Domain Verification and Sender Identity

The critical architectural decision here was domain separation. Tenant communications had been sending from queenofsandiego.com, which is incorrect for several reasons:

  • Brand separation: Tenants should never receive property management mail from a real estate marketing domain
  • Deliverability: Mixed sender domains confuse spam filters and break DKIM/SPF chains
  • Security: Credentials routed through a non-property domain create an unnecessary attack surface

Solution: Initiate SES domain verification for dangerouscentaur.com and request DKIM token generation. The workflow:

aws ses verify-domain-identity \
  --domain dangerouscentaur.com \
  --region us-west-2

aws ses verify-domain-dkim \
  --domain dangerouscentaur.com \
  --region us-west-2

AWS returns DKIM tokens that must be added to Route53 as CNAME records (exact record names provided by SES). This takes 10-30 minutes to propagate but is critical for avoiding the spam folder.

For the inbox alias, we configured ImprovMX to forward incoming mail from a property-specific address (e.g., tenants@dangerouscentaur.com) to the property manager's personal mailbox. This allows bidirectional communication: tenants send to the property domain, replies come from the same domain they contacted.

The Zelle Payment Auto-Logging Architecture

The most innovative piece: a payment forwarding pipeline that eliminates manual accounting entry. Here's the flow:

  1. Property manager receives a Zelle payment notification in their email
  2. They forward that email to payments@dangerouscentaur.com (the ImprovMX-managed inbox)
  3. A Google Apps Script webhook triggers on incoming mail (via WarmLeadResponder.gs)
  4. The script parses the email for amount and sender, then calls the tenant hub's Lambda payment logger
  5. The Lambda authenticates via a shared admin token and logs the payment to the hub's receipts JSON
  6. The tenant can immediately see the deposit reflected in their hub dashboard

This eliminates the gap between payment receipt and accounting visibility—no spreadsheets, no delays, no manual data entry.

Lambda Layer: Receipt Action Handler

Updated the existing Lambda at /Users/cb/Documents/repos/sites/dangerouscentaur/demos/3028fiftyfirststreet.92105.dangerouscentaur.com/scripts/lambda-receipt-action/lambda_function.py to add a new admin action: log_rent_payment.

The function now checks incoming requests for an ADMIN_TOKEN environment variable (set separately via AWS Console or Terraform). Only authenticated requests can call the new payment logging endpoint:

POST /log_payment
Content-Type: application/json
Authorization: Bearer {ADMIN_TOKEN}

{
  "action": "log_rent_payment",
  "tenant_id": "3028-51st",
  "amount": 1500.00,
  "method": "zelle",
  "reference": "Zelle transfer from CB",
  "date": "2024-01-15"
}

The Lambda reads the current receipts.json from S3, appends the new payment entry, and writes it back. This is eventually consistent—the tenant hub polls for updates on dashboard load.

Google Apps Script Integration

Modified WarmLeadResponder.gs at /Users/cb/Documents/repos/sites/queenofsandiego.com/WarmLeadResponder.gs (but this file now only handles that domain—we'll mirror payment-specific logic to a dangerouscentaur-specific script as the system scales):

  • Added regex pattern to detect Zelle forwarded emails (searches for "Zelle," "amount," "transfer")
  • Extracts amount via regex: (\$?\d+(?:,\d{3})*\.?\d{0,2})
  • Calls the Lambda function URL with the ADMIN_TOKEN from environment (stored in Apps Script properties)
  • Logs success/failure to a separate "Payment Log" sheet for audit trail

The script runs as the Apps Script service account, which has its own Google-managed credentials—no manual token management in the script itself.

Infrastructure Summary

  • S3 Bucket: dangerouscentaur-sites (tenant hub static assets)
  • CloudFront Distribution: Points to S3 bucket, invalidated post-deployment
  • SES Domain: dangerouscentaur.com (verified, DKIM configured)
  • ImprovMX Alias: payments@dangerouscentaur.com → property manager's inbox
  • Lambda Function: receipt-action (Python 3.11, 512MB memory, 30s timeout)
  • Lambda URL: Public HTTPS endpoint with token-based auth
  • Google Apps Script: Bound to the dangerouscentaur company Google Workspace account