payments-dashboard/template.yaml
Adam Moussa ffd46c4bda Fix BoA transaction matching, add status progression protection, enable daily schedule
- Fix transaction code mapping (475=Cleared, 255=Returned) in fetchBoaTransactions
- Match on customerReference instead of bankReference for check number matching
- Add bank-confirmed protection: CSV cannot override status once bank confirms Cleared
- Add status progression guard: CSV cannot regress status backward in lifecycle
- Cleared status is permanent — cannot be voided, cancelled, or changed
- Enable daily fetchBoaTransactions schedule (9am ET weekdays)
- Add production test script and dry run simulation script
2026-04-14 17:43:13 -04:00

273 lines
8.2 KiB
YAML

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Payments Dashboard - S3 CSV ingestion to Slack App Home
Globals:
Function:
Runtime: nodejs20.x
Timeout: 30
MemorySize: 256
Environment:
Variables:
TABLE_NAME: !Ref DashboardTable
Resources:
# VPC with private subnet + NAT Gateway for static outbound IP
Vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.20.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: payments-dashboard-vpc
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: 10.20.1.0/24
AvailabilityZone: !Select [0, !GetAZs ""]
Tags:
- Key: Name
Value: payments-dashboard-private
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: 10.20.2.0/24
AvailabilityZone: !Select [0, !GetAZs ""]
Tags:
- Key: Name
Value: payments-dashboard-public
InternetGateway:
Type: AWS::EC2::InternetGateway
VpcGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref Vpc
InternetGatewayId: !Ref InternetGateway
NatEip:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
NatGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatEip.AllocationId
SubnetId: !Ref PublicSubnet
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref Vpc
PublicRoute:
Type: AWS::EC2::Route
DependsOn: VpcGatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet
RouteTableId: !Ref PublicRouteTable
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref Vpc
PrivateRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway
PrivateSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet
RouteTableId: !Ref PrivateRouteTable
LambdaSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Payments Dashboard Lambda outbound access
VpcId: !Ref Vpc
SecurityGroupEgress:
- IpProtocol: "-1"
CidrIp: 0.0.0.0/0
PaymentsCsvBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub seahaven-payments-csv-${AWS::AccountId}
DashboardTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: PaymentsDashboard
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: pk
AttributeType: S
KeySchema:
- AttributeName: pk
KeyType: HASH
ProcessPaymentCsvFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: payments-processPaymentCsv
Handler: src/processPaymentCsv.handler
Timeout: 120
Environment:
Variables:
BOA_BASE_URL: https://api.bofa.com
BOA_CHECK_MGMT_APP_ID_PARAM: /payments-dashboard/boa-check-mgmt-app-id
BOA_CHECK_MGMT_CLIENT_ID_PARAM: /payments-dashboard/boa-check-mgmt-client-id
BOA_CHECK_MGMT_SECRET_PARAM: /payments-dashboard/boa-check-mgmt-token
BOA_ACCOUNT_NUMBER_PARAM: /payments-dashboard/boa-account-number
BOA_COMPANY_ID_PARAM: /payments-dashboard/boa-company-id
VpcConfig:
SubnetIds:
- !Ref PrivateSubnet
SecurityGroupIds:
- !Ref LambdaSecurityGroup
Events:
CsvUpload:
Type: S3
Properties:
Bucket: !Ref PaymentsCsvBucket
Events: s3:ObjectCreated:*
Filter:
S3Key:
Rules:
- Name: suffix
Value: .csv
Policies:
- S3ReadPolicy:
BucketName: !Sub seahaven-payments-csv-${AWS::AccountId}
- DynamoDBCrudPolicy:
TableName: !Ref DashboardTable
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-check-mgmt-app-id
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-check-mgmt-client-id
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-check-mgmt-token
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-account-number
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-company-id
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- ec2:CreateNetworkInterface
- ec2:DescribeNetworkInterfaces
- ec2:DeleteNetworkInterface
Resource: "*"
SlackAppHomeFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: payments-slackAppHome
Handler: src/slackAppHome.handler
VpcConfig:
SubnetIds:
- !Ref PrivateSubnet
SecurityGroupIds:
- !Ref LambdaSecurityGroup
Environment:
Variables:
SLACK_BOT_TOKEN_PARAM: /payments-dashboard/slack-bot-token
Events:
SlackEvent:
Type: HttpApi
Properties:
Path: /slack/events
Method: POST
Policies:
- DynamoDBReadPolicy:
TableName: !Ref DashboardTable
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/slack-bot-token
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- ec2:CreateNetworkInterface
- ec2:DescribeNetworkInterfaces
- ec2:DeleteNetworkInterface
Resource: "*"
FetchBoaTransactionsFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: payments-fetchBoaTransactions
Handler: src/fetchBoaTransactions.handler
Timeout: 60
VpcConfig:
SubnetIds:
- !Ref PrivateSubnet
SecurityGroupIds:
- !Ref LambdaSecurityGroup
Environment:
Variables:
BOA_BASE_URL: https://api.bofa.com
BOA_REPORTING_APP_ID_PARAM: /payments-dashboard/boa-reporting-app-id
BOA_REPORTING_CLIENT_ID_PARAM: /payments-dashboard/boa-account-info-client-id
BOA_REPORTING_SECRET_PARAM: /payments-dashboard/boa-account-info-token
BOA_ACCOUNT_NUMBER_PARAM: /payments-dashboard/boa-account-number
BOA_BANK_ID_PARAM: /payments-dashboard/boa-bank-id
Events:
DailySchedule:
Type: Schedule
Properties:
Schedule: cron(0 13 ? * MON-FRI *)
Description: Fetch BoA previous day transactions at 9am ET (13:00 UTC)
Enabled: true
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref DashboardTable
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-reporting-app-id
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-account-info-client-id
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-account-info-token
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-account-number
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-bank-id
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- ec2:CreateNetworkInterface
- ec2:DescribeNetworkInterfaces
- ec2:DeleteNetworkInterface
Resource: "*"
Outputs:
SlackEventUrl:
Description: URL to set as the Slack app Request URL
Value: !Sub https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com/slack/events
CsvBucket:
Description: S3 bucket for CSV uploads
Value: !Ref PaymentsCsvBucket
StaticOutboundIp:
Description: Static IP for BoA API whitelist
Value: !Ref NatEip