package banking import ( "fmt" "time" ) // Account types. const ( AcctChecking = "CHECKING" AcctSavings = "SAVINGS" AcctMoneyMarket = "MONEY MARKET" AcctCertDeposit = "CERT OF DEPOSIT" ) // Account represents a fake bank account. type Account struct { Number string Type string Balance int64 // cents } // Transaction represents a fake bank transaction. type Transaction struct { Date string Description string Amount int64 // cents (negative for debits) Balance int64 // running balance in cents } // SecureMessage represents a fake internal message. type SecureMessage struct { ID int Date string From string Subj string Body string Unread bool } // WireTransfer captures data entered during the wire transfer wizard. type WireTransfer struct { RoutingNumber string DestAccount string Beneficiary string BankName string Amount string Memo string AuthCode string } // bankState holds all fake data for a session. type bankState struct { Accounts []Account Transactions map[string][]Transaction // keyed by account number Messages []SecureMessage Transfers []WireTransfer } func newBankState() *bankState { now := time.Now() accounts := []Account{ {Number: "****4821", Type: AcctChecking, Balance: 4738291}, {Number: "****7203", Type: AcctSavings, Balance: 18254100}, {Number: "****9915", Type: AcctMoneyMarket, Balance: 52387450}, {Number: "****1102", Type: AcctCertDeposit, Balance: 25000000}, } transactions := make(map[string][]Transaction) transactions["****4821"] = generateCheckingTxns(now, accounts[0].Balance) transactions["****7203"] = generateSavingsTxns(now, accounts[1].Balance) transactions["****9915"] = generateMoneyMarketTxns(now, accounts[2].Balance) transactions["****1102"] = generateCDTxns(now, accounts[3].Balance) messages := []SecureMessage{ { ID: 1, Date: now.Add(-2 * 24 * time.Hour).Format("01/02/2006"), From: "SYSTEM ADMINISTRATOR", Subj: "SCHEDULED MAINTENANCE WINDOW", Unread: true, Body: fmt.Sprintf(`FROM: SYSTEM ADMINISTRATOR DATE: %s RE: SCHEDULED MAINTENANCE WINDOW ALL TERMINALS WILL BE OFFLINE FOR MAINTENANCE: DATE: %s TIME: 02:00 - 04:00 EST AFFECTED: ALL REGIONS DURING THIS WINDOW, THE FOLLOWING SYSTEMS WILL BE UNAVAILABLE: - WIRE TRANSFER PROCESSING (10.48.2.100:8443) - ACCOUNT MANAGEMENT (10.48.2.101:8443) - ACH BATCH PROCESSOR (10.48.2.105:9090) PLEASE ENSURE ALL PENDING TRANSACTIONS ARE SUBMITTED BEFORE 01:30 EST. CONTACT: HELPDESK EXT 4400 OR ops-support@internal.securebank.local`, now.Add(-2*24*time.Hour).Format("01/02/2006 15:04"), now.Add(5*24*time.Hour).Format("01/02/2006")), }, { ID: 2, Date: now.Add(-5 * 24 * time.Hour).Format("01/02/2006"), From: "COMPLIANCE DEPT", Subj: "QUARTERLY AUDIT REMINDER", Unread: true, Body: `FROM: COMPLIANCE DEPT RE: QUARTERLY AUDIT REMINDER ALL BRANCH MANAGERS: THE Q4 COMPLIANCE AUDIT IS SCHEDULED FOR NEXT WEEK. PLEASE ENSURE THE FOLLOWING ARE CURRENT: 1. TRANSACTION LOGS EXPORTED TO \\FILESERV01\AUDIT\Q4 2. VAULT ACCESS CODES ROTATED (LAST ROTATION: SEE VAULT-MGMT PORTAL) 3. EMPLOYEE ACCESS REVIEWS COMPLETED IN IAM PORTAL (https://iam.internal:8443) NOTE: DEFAULT CREDENTIALS FOR THE AUDIT PORTAL HAVE BEEN RESET. NEW CREDENTIALS DISTRIBUTED VIA SECURE COURIER. REFERENCE: AUDIT-2024-Q4-0847 VAULT MASTER CODE HINT: FIRST 4 OF ROUTING + BRANCH ZIP (STANDARD FORMAT)`, }, { ID: 3, Date: now.Add(-8 * 24 * time.Hour).Format("01/02/2006"), From: "IT SECURITY", Subj: "PASSWORD POLICY UPDATE", Unread: false, Body: `FROM: IT SECURITY RE: PASSWORD POLICY UPDATE - EFFECTIVE IMMEDIATELY ALL STAFF: PER FEDERAL BANKING REGULATION 12 CFR 748, THE FOLLOWING PASSWORD POLICY IS NOW IN EFFECT: - MINIMUM 12 CHARACTERS - MUST CONTAIN UPPERCASE, LOWERCASE, NUMBER, SPECIAL CHAR - 90-DAY ROTATION CYCLE - NO REUSE OF LAST 24 PASSWORDS LEGACY SYSTEM ACCOUNTS (MAINFRAME, AS/400) ARE EXEMPT UNTIL MIGRATION IS COMPLETE. CURRENT LEGACY ACCESS: MAINFRAME: telnet://10.48.1.50:23 (CICS REGION PROD1) AS/400: tn5250://10.48.1.55 (SUBSYSTEM QINTER) SERVICE ACCOUNT PASSWORDS ARE MANAGED VIA CYBERARK: https://pam.internal.securebank.local:8443 TICKET: SEC-2024-1847`, }, { ID: 4, Date: now.Add(-12 * 24 * time.Hour).Format("01/02/2006"), From: "WIRE OPERATIONS", Subj: "FEDWIRE CUTOFF TIME CHANGE", Unread: false, Body: `FROM: WIRE OPERATIONS RE: FEDWIRE CUTOFF TIME CHANGE EFFECTIVE NEXT MONDAY, FEDWIRE CUTOFF TIMES ARE: DOMESTIC WIRES: 16:30 EST (WAS 17:00) INTERNATIONAL WIRES: 14:00 EST (NO CHANGE) BOOK TRANSFERS: 17:30 EST (NO CHANGE) WIRES SUBMITTED AFTER CUTOFF WILL BE QUEUED FOR NEXT BUSINESS DAY PROCESSING. FOR EMERGENCY SAME-DAY PROCESSING AFTER CUTOFF: CONTACT WIRE ROOM: EXT 4450 AUTH CODE REQUIRED (OBTAIN FROM BRANCH MANAGER) APPROVAL CHAIN: OPS-MGR -> VP-WIRE -> SVP-TREASURY CORRESPONDENT BANK CONTACTS: JPMORGAN: wire.ops@jpmc.com / 212-555-0147 CITI: fedwire@citi.com / 212-555-0283`, }, } return &bankState{ Accounts: accounts, Transactions: transactions, Messages: messages, } } func generateCheckingTxns(now time.Time, endBalance int64) []Transaction { txns := []Transaction{ {Description: "ACH DEPOSIT - PAYROLL", Amount: 485000}, {Description: "CHECK #1847", Amount: -125000}, {Description: "POS DEBIT - WHOLE FOODS #1284", Amount: -18743}, {Description: "ATM WITHDRAWAL - MAIN ST BRANCH", Amount: -40000}, {Description: "ACH DEBIT - MORTGAGE PMT", Amount: -215000}, {Description: "WIRE TRANSFER IN - REF#8847201", Amount: 1250000}, {Description: "POS DEBIT - SHELL OIL #4492", Amount: -6821}, {Description: "ACH DEPOSIT - PAYROLL", Amount: 485000}, {Description: "CHECK #1848", Amount: -75000}, {Description: "ONLINE TRANSFER TO SAVINGS", Amount: -100000}, {Description: "POS DEBIT - AMAZON.COM", Amount: -14599}, {Description: "ACH DEBIT - ELECTRIC COMPANY", Amount: -18742}, {Description: "ATM WITHDRAWAL - PARK AVE BRANCH", Amount: -20000}, {Description: "WIRE TRANSFER OUT - REF#9014882", Amount: -500000}, {Description: "POS DEBIT - COSTCO #0441", Amount: -28734}, {Description: "ACH DEPOSIT - TAX REFUND", Amount: 342100}, } return populateTransactions(txns, now, endBalance) } func generateSavingsTxns(now time.Time, endBalance int64) []Transaction { txns := []Transaction{ {Description: "INTEREST PAYMENT", Amount: 4521}, {Description: "ONLINE TRANSFER FROM CHECKING", Amount: 100000}, {Description: "INTEREST PAYMENT", Amount: 4633}, {Description: "ACH DEPOSIT - DIVIDEND PMT", Amount: 125000}, {Description: "ONLINE TRANSFER FROM CHECKING", Amount: 200000}, {Description: "INTEREST PAYMENT", Amount: 4748}, {Description: "WITHDRAWAL - TRANSFER TO MM", Amount: -500000}, {Description: "INTEREST PAYMENT", Amount: 4812}, } return populateTransactions(txns, now, endBalance) } func generateMoneyMarketTxns(now time.Time, endBalance int64) []Transaction { txns := []Transaction{ {Description: "INTEREST PAYMENT - TIER 3 RATE", Amount: 21847}, {Description: "DEPOSIT - TRANSFER FROM SAVINGS", Amount: 500000}, {Description: "INTEREST PAYMENT - TIER 3 RATE", Amount: 22105}, {Description: "WITHDRAWAL - WIRE TRANSFER", Amount: -1000000}, {Description: "DEPOSIT - ACH TRANSFER", Amount: 750000}, {Description: "INTEREST PAYMENT - TIER 3 RATE", Amount: 22394}, } return populateTransactions(txns, now, endBalance) } func generateCDTxns(now time.Time, endBalance int64) []Transaction { txns := []Transaction{ {Description: "CERTIFICATE OPENED - 12MO TERM", Amount: 25000000}, {Description: "INTEREST ACCRUAL", Amount: 10417}, {Description: "INTEREST ACCRUAL", Amount: 10417}, {Description: "INTEREST ACCRUAL", Amount: 10417}, } return populateTransactions(txns, now, endBalance) } func populateTransactions(txns []Transaction, now time.Time, endBalance int64) []Transaction { // Work backwards from end balance to assign dates and running balances. bal := endBalance for i := len(txns) - 1; i >= 0; i-- { txns[i].Balance = bal txns[i].Date = now.Add(time.Duration(-(len(txns) - i)) * 3 * 24 * time.Hour).Format("01/02/2006") bal -= txns[i].Amount } return txns }