```html

Building a Self-Contained Payment Logging System for Multi-Tenant Property Management

We recently built a complete payment tracking system for a property management application that needed to live entirely within the dangerouscentaur.com domain—completely isolated from the primary business domain. The system needed to do three things: provision tenant credentials securely, allow payment logging via forwarded bank emails, and maintain an audit trail without manual intervention.

The Architecture Challenge

The tenant portal at https://3028fiftyfirststreet.92105.dangerouscentaur.com/ needed a payment verification workflow. Tenants would pay via Zelle (a peer-to-peer payment service), and we needed a way to:

  • Log incoming payments without manual data entry
  • Keep all communication within the dangerouscentaur.com domain
  • Maintain a tamper-evident audit trail in S3
  • Avoid any cross-domain dependencies

The core issue: email coming from queenofsandiego.com to tenants was a domain leakage problem. We needed a clean, self-contained email infrastructure.

Phase 1: Tenant Credential Generation and Distribution

We modified the tenant hub index file at /Users/cb/Documents/repos/sites/dangerouscentaur/demos/3028fiftyfirststreet.92105.dangerouscentaur.com/index.html to include a credentials table with freshly generated temporary passwords. Each password was hashed using bcrypt and stored in the hub's initialization data.

The deployment pipeline:


# Build updated index.html with credential table
# Upload to S3 bucket: dangerouscentaur-property-demos
aws s3 cp index.html s3://dangerouscentaur-property-demos/3028fiftyfirststreet.92105/index.html

# Invalidate CloudFront distribution
aws cloudfront create-invalidation \
  --distribution-id [DIST_ID] \
  --paths "/*"

For email delivery, we configured SES (Simple Email Service) to send from a proper @dangerouscentaur.com alias. Instead of using a human inbox, we set up ImprovMX to forward mail destined for payments@dangerouscentaur.com to a Lambda function endpoint.

Phase 2: Email-to-Lambda Pipeline for Payment Receipts

We created a new Lambda function at /Users/cb/Documents/repos/sites/dangerouscentaur/demos/3028fiftyfirststreet.92105.dangerouscentaur.com/scripts/lambda-email-parser/lambda_function.py. This function:

  • Receives forwarded Zelle payment confirmations via SES
  • Parses the email body to extract payment amount, timestamp, and tenant reference
  • Calls the receipt-action Lambda with an admin token to log the payment
  • Stores the email body as evidence in S3 for audit purposes

The email-parser Lambda doesn't store data directly—it acts as a translator between the email format and the internal payment logging API.

Phase 3: Receipt Action Lambda with Admin Token Protection

We extended the existing receipt-action Lambda at /Users/cb/Documents/repos/sites/dangerouscentaur/demos/3028fiftyfirststreet.92105.dangerouscentaur.com/scripts/lambda-receipt-action/lambda_function.py with a new log_rent_payment action.

The key architectural decision: all payment logging requires an ADMIN_TOKEN environment variable. This token is:

  • Set during Lambda deployment, never stored in code
  • Required in the Authorization header for any payment mutation
  • Different from tenant-facing API tokens (which are read-only)
  • Rotatable without redeploying the Lambda

The payment logging function signature:


def log_rent_payment(tenant_id, amount, payment_method, receipt_email_key):
    """
    Log a rent payment to the receipts.json audit trail.
    
    Args:
        tenant_id: Property identifier (e.g., "3028_51st")
        amount: Payment amount in cents
        payment_method: "zelle" | "check" | "ach"
        receipt_email_key: S3 key to the forwarded email for evidence
    
    Returns:
        Updated receipts.json entry with timestamp and signature
    """

The receipts are stored in S3 at s3://dangerouscentaur-property-demos/3028fiftyfirststreet.92105/receipts.json as an append-only log with cryptographic signatures.

Phase 4: Google Apps Script Integration for Email Forwarding

We modified /Users/cb/Documents/repos/sites/queenofsandiego.com/WarmLeadResponder.gs to add a command handler for payment logging. However—and this is critical—the actual payment logging calls the dangerouscentaur.com Lambda endpoint, not any queenofsandiego.com infrastructure.

The GAS script now:

  • Listens for emails forwarded to a @dangerouscentaur.com address
  • Parses Zelle confirmation language
  • Extracts tenant ID from the email metadata
  • Calls the receipt-action Lambda with the ADMIN_TOKEN
  • Logs the action in a local audit sheet within the dangerouscentaur.com domain

Why This Architecture?

Domain isolation: The tenant never receives mail from or connects to queenofsandiego.com. All communication is @dangerouscentaur.com. This matters for deliverability, compliance, and operational separation.

Email forwarding instead of manual entry: Rather than asking the property manager to type payment details into a form, they can forward their bank notification directly. The system parses it automatically, reducing data-entry errors.

Append-only receipts log: Instead of updating a mutable payments database, we append timestamped entries to receipts.json`. This creates an immutable audit trail that's easy to export and reconcile with bank statements.

Admin token separation: The Lambda has two token classes: tenant tokens (for reading their own data) and admin tokens (for recording payments). This prevents tenant-facing endpoints from accidentally logging payments.

Infrastructure Resources

  • S3 bucket: dangerouscentaur-property-demos (stores HTML, receipts.json, email archives)
  • CloudFront distribution: Fronts the property demo site for TLS termination and caching
  • Lambda functions:
    • lambda-receipt-action (logs payments, returns receipt)
    • lambda-email-parser (translates SES events to receipt-action calls)
  • SES configuration: Domain verified for