```html

Building a Self-Contained Payment Logging System: Divorcing Tenant Infrastructure from Marketing Domain

What Was Done

We built a complete payment verification and logging system for a rental property tenant portal, with a critical requirement: complete separation from the marketing domain (queenofsandiego.com). This involved:

  • Generating fresh tenant credentials and redeploying the tenant hub portal at 3028fiftyfirststreet.92105.dangerouscentaur.com
  • Setting up SES email infrastructure on the dangerouscentaur.com domain (completely separate from the marketing domain)
  • Building a Lambda-based payment logging pipeline that accepts forwarded bank emails and automatically logs Zelle payments
  • Creating a Google Apps Script webhook receiver to parse payment notifications and trigger Lambda functions
  • Establishing an admin token authentication scheme to secure the payment logging endpoint

Technical Architecture

Domain Separation Pattern

The core architectural decision was strict domain isolation. All tenant-facing infrastructure runs under dangerouscentaur.com, while queenofsandiego.com remains purely for marketing. This separation provides:

  • Security boundary: Tenant data and accounting operations are completely isolated from public-facing marketing infrastructure
  • Email reputation: Tenant communications use dedicated SES-verified domain credentials, preventing marketing email issues from affecting critical tenant communications
  • Operational clarity: Each domain has its own CloudFront distributions, S3 buckets, and Lambda functions with no cross-domain dependencies

Tenant Hub Portal Structure

The tenant portal lives in:

/Users/cb/Documents/repos/sites/dangerouscentaur/demos/
3028fiftyfirststreet.92105.dangerouscentaur.com/

This directory contains:

  • index.html — Main portal interface with embedded tenant credentials (hashed passwords)
  • scripts/lambda-receipt-action/lambda_function.py — AWS Lambda that logs payment events
  • scripts/lambda-email-parser/lambda_function.py — AWS Lambda that parses forwarded bank emails

The portal is served via S3 with CloudFront CDN. We identified the CloudFront distribution using the AWS CLI and invalidated the full cache after each deployment to ensure fresh credentials reached tenants immediately.

Password Generation and Deployment

Fresh temporary passwords were generated and embedded in index.html with bcrypt hashing (not plaintext). The updated portal was deployed using:

aws s3 cp index.html s3://[bucket-name]/index.html
aws cloudfront create-invalidation --distribution-id [DIST-ID] --paths "/*"

SES Email Infrastructure

AWS SES was configured to send emails from the dangerouscentaur.com domain. We:

  1. Verified the domain with SES (DKIM tokens added to DNS)
  2. Created ImprovMX aliases to receive payment notifications at a dedicated inbox
  3. Sent credential emails via SES SDK with proper sender configuration (no more queenofsandiego.com domain in tenant emails)

Email was sent using AWS SES directly from Python/Node.js code, ensuring proper domain authentication headers and deliverability.

Payment Logging Pipeline

Lambda: Receipt Action Handler

File: scripts/lambda-receipt-action/lambda_function.py

This Lambda function is the core of the payment system. It:

  • Exposes an HTTP endpoint via AWS Lambda Function URL
  • Validates incoming requests using an ADMIN_TOKEN environment variable
  • Accepts JSON payloads with payment details (tenant ID, amount, payment method, date)
  • Appends payment records to receipts.json stored in S3
  • Returns structured JSON responses for logging and auditing

The function URL was configured with QUALIFIER=LIVE and resource-based policy to accept only authenticated requests:

def handler(event, context):
    # Extract admin token from request headers
    token = event.get('headers', {}).get('Authorization', '')
    
    if token != os.environ.get('ADMIN_TOKEN'):
        return {
            'statusCode': 401,
            'body': json.dumps({'error': 'Unauthorized'})
        }
    
    body = json.loads(event.get('body', '{}'))
    
    # Log payment to receipts.json in S3
    payment_record = {
        'tenant_id': body.get('tenant_id'),
        'amount': body.get('amount'),
        'method': body.get('method'),  # 'zelle', 'check', etc.
        'date': datetime.now().isoformat()
    }
    
    # Read current receipts, append, write back
    # (Includes proper S3 retry logic)
    
    return {
        'statusCode': 200,
        'body': json.dumps({'success': True, 'record_id': ...})
    }

Lambda: Email Parser

File: scripts/lambda-email-parser/lambda_function.py

This Lambda parses forwarded Zelle confirmation emails and extracts payment details (amount, date, reference number). It calls the receipt-action Lambda with proper authentication headers.

Google Apps Script: Webhook Receiver

File: WarmLeadResponder.gs (modified)

The GAS script was enhanced to:

  • Receive forwarded emails from the owner's bank inbox
  • Detect payment-related subject lines and body content (Zelle confirmations)
  • Extract amount, date, and sender details using regex patterns
  • Call the Lambda receipt-action endpoint with the ADMIN_TOKEN header
  • Log results to a Google Sheet for owner visibility

The GAS command handler was wired to intercept emails matching payment patterns:

function doPost(e) {
    var data = JSON.parse(e.postData.contents);
    
    if (isProbablyPayment(data.subject, data.body)) {
        var paymentInfo = parseZelleEmail(data.body);
        
        var response = callLambda(
            'log_rent_payment',
            {
                tenant_id: extractTenantId(data),
                amount: paymentInfo.amount,
                method: 'zelle',
                date: paymentInfo.date
            }
        );
        
        logToSheet('Payments', response);
    }
}

Infrastructure Components

  • S3 Bucket: Stores tenant portal HTML, assets, and receipts.json
  • CloudFront Distribution: Serves the tenant portal with edge caching
  • Lambda Functions: Two separate functions with unique URLs for receipt logging and email parsing
  • SES