Most cloud portfolio projects look like this: spin up an EC2 instance, deploy a web app, take a screenshot. Done.
That tells a hiring manager you can follow a tutorial.
This is not that.
I built a real-time transaction screening pipeline modeled after how financial institutions actually handle suspicious activity routing.
The entire processing backbone costs $2.48/month to run at 500 000 transactions.
Here is how it works, why every decision was made, and what it actually costs to secure it properly.
The Problem This Solves
Payment processors and banks screen every transaction before it clears. A transaction above a defined threshold, or matching a suspicious pattern, needs to be flagged, stored, and routed to an analyst in near real-time. Latency here is not a UX problem. It is a compliance and fraud exposure problem.
The architecture needs to handle three things without failing:
Ingest transactions reliably, even under burst load
Evaluate and persist every transaction regardless of outcome
Alert immediately when a transaction crosses the threshold, with zero alert loss
A traditional EC2-based approach handles this with always-on servers. You pay for compute whether transactions are flowing or not. You manage patching, availability zones, and process monitoring yourself.
The serverless approach inverts this entirely.
Architecture
Every transaction enters through SQS. Lambda processes each message, writes the full record to DynamoDB regardless of whether it is flagged, and fires an SNS alert only if the amount exceeds the threshold. If Lambda fails to process a message three times, SQS routes it to a Dead Letter Queue for manual investigation. Nothing is silently dropped.
Lambda was deployed inside a VPC across two availability zones. All connections to SQS, DynamoDB, SNS, and CloudWatch run through VPC interface and gateway endpoints. No traffic touches the public internet at any point in the pipeline.
Lambda Function
import json
import boto3
import os
from decimal import Decimal
dyn = boto3.resource('dynamodb')
sns = boto3.client('sns')
TABLE = os.environ.get("TABLE", "fraud-detections")
SNS_ARN = os.environ.get("SNS_ARN", "arn:aws:sns:us-east-1:461840362463:fraud-alerts")
# Threshold represents configurable screening rule for the institution
THRESHOLD = 500000
def lambda_handler(event, context):
for record in event["Records"]:
body = json.loads(record["body"])
txn_id = body["transaction_id"]
amount = Decimal(str(body["amount"]))
merchant = body["merchant"]
flagged = amount > THRESHOLD
dyn.Table(TABLE).put_item(
Item={
"transaction_id": txn_id,
"amount": amount,
"merchant": merchant,
"flagged": flagged
}
)
if flagged:
sns.publish(
TopicArn=SNS_ARN,
Message=f"Suspicious transfer Flagged: {txn_id} — NGN {amount} — {merchant}",
Subject="Fraud Alert"
)
return {'statusCode': 200}
Every transaction is written to DynamoDB first, before the flag check. This matters. In a real system you need a complete audit trail. A transaction that does not trigger an alert is not necessarily clean. It needs to exist in the record. Regulators do not accept gaps.
The threshold is a variable, not a hardcoded business rule. In production this would be pulled from a configuration store or Parameter Store, allowing compliance teams to adjust screening rules without a code deployment.
Decimal is used instead of float for the amount. DynamoDB does not accept Python floats. Using float here causes silent precision errors on large transaction amounts. This is a real production bug that shows up in systems built by engineers who have not actually handled financial data before.
Verified Results
Two transactions were sent through the pipeline.
TXN-001 crossed the NGN 500,000 threshold. The SNS alert fired immediately to email with the subject line "Fraud Alert" and the full transaction details in the message body.
TXN-002 was written to DynamoDB with flagged: false. No alert sent. Complete audit record preserved.
Both records confirmed live in DynamoDB
Email alert received from SNS Topic.
What It Actually Costs
| Service | Monthly Cost |
|---|---|
| AWS Lambda (500k requests) | $1.14 |
| Amazon SQS (1.5M requests) | $0.60 |
| Amazon DynamoDB (1GB) | $0.56 |
| Amazon SNS (10k alerts) | $0.18 |
| Pipeline Total | $2.48 |
| VPC Interface Endpoints (3x) | $43.81 |
| Secured Total | $46.29/month |
The pipeline itself costs $2.48/month to process 500,000 transactions.
The VPC endpoints cost $43.81/month.
That is 94.6% of the total bill for a security control, not compute.
This is an intentional tradeoff. Without VPC endpoints, Lambda communicates with SQS, DynamoDB, SNS, and CloudWatch over the public internet. In a financial context that is not a configuration choice. It is an audit finding. VPC endpoints keep all traffic private within the AWS network, satisfy network isolation requirements common in PCI-DSS and SOC 2 environments, and eliminate the data exfiltration risk that comes with public endpoint exposure.
If this were a cost-only conversation, you would skip the endpoints and save $43.81/month. In a production fintech environment, that decision gets flagged in the first security review.
What This Is Not
This pipeline has a single threshold rule. Production fraud detection at scale uses ML-based anomaly scoring, velocity checks across merchant categories, device fingerprinting, and graph-based relationship analysis. Those are layered on top of an event-driven backbone exactly like this one.
This is the infrastructure layer. It is the part that has to work before any model or rule engine gets plugged in. Getting this layer wrong means no amount of ML sophistication above it recovers cleanly.
Why Serverless for This
Three concrete reasons:
Burst handling without pre-provisioning. Payment transaction volume is not linear. End-of-month salary runs, Black Friday, public holiday spikes. Lambda scales to concurrency automatically. An EC2-based system requires capacity planning or auto-scaling lag measured in minutes.
No idle cost. An EC2 t3.micro running 24/7 costs approximately $7.59/month before you add storage, monitoring, or patching overhead. Lambda at 500,000 transactions costs $1.14/month. At zero transactions it costs nothing.
Operational surface reduced to code. There is no OS to patch, no SSH access to harden, no process monitor to configure. The attack surface is the IAM role and the function code. Both are auditable and version-controlled.
What Carries Into Production
The patterns here are not demo-specific:
SQS as a durable ingest buffer decouples transaction producers from the processing layer. A downstream Lambda failure does not lose the transaction.
DLQ after three failures means no silent message loss. Every failed transaction is recoverable.
VPC-private connectivity means the pipeline satisfies network isolation requirements out of the box.
IAM roles scoped to least privilege mean Lambda cannot touch anything outside its defined resource set.
These are the same architectural decisions you find in production payment infrastructure. The scale is different. The patterns are not.
Victor Ojeje is a Cloud and Infrastructure Engineer based in Lagos. He builds production-grade AWS infrastructure with a focus on security, automation, and cost-conscious design.
LinkedIn: linkedin.com/in/victorojeje | GitHub: github.com/escanut


Top comments (0)