payments-dashboard/README.md

73 lines
4.2 KiB
Markdown
Raw Normal View History

# Payments Dashboard
AWS SAM application that ingests payment CSVs, syncs check data with Bank of America CashPro APIs, processes Gusto payroll confirmation emails into Slack notifications, and surfaces an outstanding-payments dashboard in Slack.
## Architecture
- **ProcessPayrollEmail** — Lambda triggered by S3 (inbound email) and SQS (batch timer). SES receives Gusto payroll emails at `payroll@int.seahaven.com`, stores them to S3, and this Lambda parses the email body, extracts financial data, and posts a combined Slack notification (employee payroll + contractor payments) after a 10-minute batching window. Runs outside VPC.
- **ProcessPaymentCsv** — Lambda triggered by S3 CSV upload. Parses Stampli payment exports, upserts to DynamoDB, and submits new/cancelled checks to the CashPro Check Management API.
- **FetchBoaTransactions** — Scheduled Lambda (weekdays 9am ET). Calls the CashPro Previous Day Transaction Inquiry API and matches cleared/returned checks back to DynamoDB records.
- **SlackAppHome** — Lambda behind API Gateway. Renders the payments dashboard on the Slack App Home tab with outstanding aging buckets and drill-down modals.
ProcessPaymentCsv, FetchBoaTransactions, and SlackAppHome run inside a VPC with a NAT Gateway for a static outbound IP (required by BoA IP whitelisting). ProcessPayrollEmail runs outside the VPC.
## Payroll Email Pipeline
Gusto sends payroll confirmation emails when payroll is run. A Gmail filter on adam@seahavenind.com auto-forwards emails from `automated@gusto.com` and `gustonoreply@gusto.com` to `payroll@int.seahaven.com`.
**Flow:** Gmail forward → SES receipt rule → S3 bucket → Lambda parses email → DynamoDB (pending) → SQS delay queue (10 min) → Lambda batches all pending items for that date → single Slack message → DynamoDB (notified)
**Deduplication:** Each email is deduplicated by DynamoDB key (`PAYROLL_EMAIL#employee#<date>` or `PAYROLL_EMAIL#contractor#<date>#<bank-suffix>`). The batch post is deduplicated by `PAYROLL_BATCH#<date>`. All items have a 90-day TTL.
## BoA CashPro API Integration
Two separate CashPro APIs are used, each with its own OAuth credentials:
| API | Purpose | Endpoint |
|-----|---------|----------|
| Check Management | Issue and cancel checks | `/cashpro/checkmanagement/v1/check-issues` |
| Reporting (Transaction Inquiry) | Fetch previous-day transactions | `/cashpro/reporting/v1/transaction-inquiries/previous-day` |
**Authentication flow:**
1. POST to `/authn/v1/client-authentication` with `applicationID`, `client_id`, and `client_secret`
2. Receive a Bearer `access_token` (valid 1 hour)
3. Pass the token in the `Authorization` header for subsequent API calls
**Base URLs:**
- Production: `https://api.bofa.com`
- Sandbox: `https://api-sb.bofa.com`
## SSM Parameters
All BoA credentials and config are stored in AWS SSM Parameter Store (SecureString):
| Parameter | Description |
|-----------|-------------|
| `/payments-dashboard/boa-check-mgmt-app-id` | Check Management application ID |
| `/payments-dashboard/boa-check-mgmt-client-id` | Check Management client ID |
| `/payments-dashboard/boa-check-mgmt-token` | Check Management client secret |
| `/payments-dashboard/boa-reporting-app-id` | Reporting application ID |
| `/payments-dashboard/boa-account-info-client-id` | Reporting client ID |
| `/payments-dashboard/boa-account-info-token` | Reporting client secret |
| `/payments-dashboard/boa-account-number` | BoA account number |
| `/payments-dashboard/boa-company-id` | CashPro company ID (check management) |
| `/payments-dashboard/boa-bank-id` | BoA routing number |
| `/payments-dashboard/slack-bot-token` | Slack Bot OAuth token |
## Scripts
| Script | Purpose |
|--------|---------|
| `scripts/test-boa-sandbox.js` | One-off sandbox connectivity test for both CashPro APIs |
| `scripts/seed-from-csv.js` | Seed DynamoDB from a local CSV file |
| `scripts/seed-bank-status.js` | Seed bank clear status data into DynamoDB |
## Deployment
```bash
sam build
sam deploy --guided
```
The `BOA_BASE_URL` environment variable in `template.yaml` controls whether Lambdas hit production (`https://api.bofa.com`) or sandbox (`https://api-sb.bofa.com`). All other BoA config is read from SSM at runtime.