Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/azfar-imtiaz/PayPulse-Cloud/llms.txt

Use this file to discover all available pages before exploring further.

PayPulse Cloud uses two S3 buckets, both defined in aws-infra-terraform/s3.tf.

rental-invoices-bucket

Stores all invoice files — rental PDFs and retail HTML — uploaded by ingestion Lambda functions.

wallenstam-lambda-bucket

Stores zipped Lambda source code. Uploading a new zip triggers a function update.

rental-invoices-bucket

This bucket holds every invoice file processed by PayPulse. It is configured with versioning, AES-256 server-side encryption, and full public access blocking.
resource "aws_s3_bucket" "rental_invoices" {
  bucket = var.invoices_bucket_name   # default: "rental-invoices-bucket"
}

resource "aws_s3_bucket_versioning" "rental_invoices" {
  bucket = aws_s3_bucket.rental_invoices.id
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "rental_invoices" {
  bucket = aws_s3_bucket.rental_invoices.id
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

resource "aws_s3_bucket_public_access_block" "rental_invoices" {
  bucket                  = aws_s3_bucket.rental_invoices.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

Path structure

All invoices are stored under the invoices/ prefix. The path encodes the user ID, invoice type, and (for retail) the sub-category and vendor. Rental invoices (PDF)
invoices/{user_id}/rental/invoice_0001.pdf
Retail invoices (HTML)
invoices/{user_id}/retail/{sub_type}/{vendor}_{date}_{hash}.html
Where {sub_type} is one of:
Sub-typeDescription
food-deliveryRestaurant and food delivery orders
clothingClothing and fashion purchases
technologyElectronics and tech products
subscriptionsStreaming services, memberships, etc.
groceryGrocery store purchases
utilityElectricity, water, internet bills
miscellaneousOther retail purchases
travelFlights, trains, buses, and other transport
User IDs are UUIDs prefixed with user_, generated at signup (e.g., user_a1b2c3d4-...).

wallenstam-lambda-bucket

This bucket holds the deployment artifacts for all zip-deployed Lambda functions.
resource "aws_s3_bucket" "lambda_bucket" {
  bucket = var.lambda_functions_bucket_name   # default: "wallenstam-lambda-bucket"
}

resource "aws_s3_bucket_versioning" "lambda_bucket_versioning" {
  bucket = aws_s3_bucket.lambda_bucket.id
  versioning_configuration {
    status = "Enabled"
  }
}
A bucket policy grants lambda.amazonaws.com s3:GetObject access so Lambda can pull deployment packages:
resource "aws_s3_bucket_policy" "lambda_bucket_policy" {
  bucket = aws_s3_bucket.lambda_bucket.id
  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [{
      Effect    = "Allow",
      Principal = { Service = "lambda.amazonaws.com" },
      Action    = "s3:GetObject",
      Resource  = "${aws_s3_bucket.lambda_bucket.arn}/*"
    }]
  })
}

Versioning and deployment

Each Lambda function reads its source code from an S3 object. S3 versioning is enabled so every upload creates a new object version. When a zip file is updated and terraform apply is run, Terraform detects the new S3 object version and updates the Lambda function accordingly. Functions deployed this way include: login_user, signup_user, fetch_invoices, fetch_latest_invoice, fetch_retail_invoices, get_rental_invoice, get_rental_invoices, get_user_profile, gmail_store_tokens, delete_user, and send_invoice_notification.
The parse_invoice function is deployed as a Docker image pushed to an ECR repository, not as a zip file in this bucket.

S3 event triggers

Uploads to rental-invoices-bucket trigger Lambda functions automatically via S3 event notifications:
resource "aws_s3_bucket_notification" "invoice_upload_trigger" {
  bucket = aws_s3_bucket.rental_invoices.id

  # PDF upload triggers parse_invoice
  lambda_function {
    lambda_function_arn = module.lambdas.parse_invoice_arn
    events              = ["s3:ObjectCreated:*"]
    filter_prefix       = "invoices/"
    filter_suffix       = ".pdf"
  }

  # HTML upload triggers parse_retail_invoice
  lambda_function {
    lambda_function_arn = module.lambdas.parse_retail_invoice_arn
    events              = ["s3:ObjectCreated:*"]
    filter_prefix       = "invoices/"
    filter_suffix       = ".html"
  }
}
EventFilterLambda triggered
s3:ObjectCreated:*invoices/*.pdfparse_invoice
s3:ObjectCreated:*invoices/*.htmlparse_retail_invoice
When fetch_latest_invoice or fetch_invoices uploads a PDF to invoices/{user_id}/rental/, S3 fires the notification, which invokes parse_invoice. That function parses the PDF and writes the result to DynamoDB, which in turn triggers send_invoice_notification via the DynamoDB stream.

Build docs developers (and LLMs) love