package handlers import ( "database/sql" "encoding/json" "net/http" "time" "github.com/go-chi/chi/v5" ) type BankHandler struct { DB *sql.DB } func NewBankHandler(db *sql.DB) *BankHandler { return &BankHandler{DB: db} } type BankAccount struct { ID string `json:"id"` Name string `json:"name"` BankName string `json:"bank_name"` AccountNumber string `json:"account_number"` IBAN string `json:"iban"` BIC string `json:"bic"` Currency string `json:"currency"` Balance float64 `json:"balance"` IsDefault bool `json:"is_default"` Status string `json:"status"` LastSync *time.Time `json:"last_sync"` CreatedAt time.Time `json:"created_at"` } type BankTransaction struct { ID string `json:"id"` AccountID string `json:"account_id"` TransactionDate time.Time `json:"transaction_date"` Amount float64 `json:"amount"` Currency string `json:"currency"` Description string `json:"description"` Counterparty string `json:"counterparty"` Reference string `json:"reference"` ExternalID string `json:"external_id"` Status string `json:"status"` MatchedToType string `json:"matched_to_type"` MatchedToID string `json:"matched_to_id"` CreatedAt time.Time `json:"created_at"` } func (h *BankHandler) ListAccounts(w http.ResponseWriter, r *http.Request) { rows, err := h.DB.Query(` SELECT id, name, bank_name, account_number, iban, bic, currency, balance, is_default, status, last_sync, created_at FROM boc_bank_accounts WHERE status = 'active' ORDER BY name `) if err != nil { writeError(w, http.StatusInternalServerError, "database error") return } defer rows.Close() accounts := []BankAccount{} for rows.Next() { var a BankAccount if err := rows.Scan(&a.ID, &a.Name, &a.BankName, &a.AccountNumber, &a.IBAN, &a.BIC, &a.Currency, &a.Balance, &a.IsDefault, &a.Status, &a.LastSync, &a.CreatedAt); err != nil { continue } accounts = append(accounts, a) } writeJSON(w, http.StatusOK, map[string]interface{}{ "accounts": accounts, "total": len(accounts), }) } func (h *BankHandler) CreateAccount(w http.ResponseWriter, r *http.Request) { var req BankAccount if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid request") return } var id string err := h.DB.QueryRow(` INSERT INTO boc_bank_accounts (name, bank_name, account_number, iban, bic, currency, is_default) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id `, req.Name, req.BankName, req.AccountNumber, req.IBAN, req.BIC, req.Currency, req.IsDefault).Scan(&id) if err != nil { writeError(w, http.StatusInternalServerError, "failed to create account") return } writeJSON(w, http.StatusCreated, map[string]interface{}{ "id": id, "message": "Bank account created", }) } func (h *BankHandler) ListTransactions(w http.ResponseWriter, r *http.Request) { accountID := r.URL.Query().Get("account_id") status := r.URL.Query().Get("status") var query string var args []interface{} if accountID != "" { if status != "" { query = `SELECT id, account_id, transaction_date, amount, currency, description, counterparty, reference, external_id, status, matched_to_type, matched_to_id, created_at FROM boc_bank_transactions WHERE account_id = $1 AND status = $2 ORDER BY transaction_date DESC LIMIT 200` args = append(args, accountID, status) } else { query = `SELECT id, account_id, transaction_date, amount, currency, description, counterparty, reference, external_id, status, matched_to_type, matched_to_id, created_at FROM boc_bank_transactions WHERE account_id = $1 ORDER BY transaction_date DESC LIMIT 200` args = append(args, accountID) } } else { query = `SELECT id, account_id, transaction_date, amount, currency, description, counterparty, reference, external_id, status, matched_to_type, matched_to_id, created_at FROM boc_bank_transactions ORDER BY transaction_date DESC LIMIT 200` } rows, err := h.DB.Query(query, args...) if err != nil { writeError(w, http.StatusInternalServerError, "database error") return } defer rows.Close() transactions := []BankTransaction{} for rows.Next() { var t BankTransaction if err := rows.Scan(&t.ID, &t.AccountID, &t.TransactionDate, &t.Amount, &t.Currency, &t.Description, &t.Counterparty, &t.Reference, &t.ExternalID, &t.Status, &t.MatchedToType, &t.MatchedToID, &t.CreatedAt); err != nil { continue } transactions = append(transactions, t) } writeJSON(w, http.StatusOK, map[string]interface{}{ "transactions": transactions, "total": len(transactions), }) } func (h *BankHandler) SyncTransactions(w http.ResponseWriter, r *http.Request) { var req struct { AccountID string `json:"account_id"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid request") return } // TODO: Implement actual bank API sync (PSD2/Open Banking) // For now, simulate sync _, err := h.DB.Exec(` UPDATE boc_bank_accounts SET last_sync = NOW() WHERE id = $1 `, req.AccountID) if err != nil { writeError(w, http.StatusInternalServerError, "failed to sync") return } writeJSON(w, http.StatusOK, map[string]interface{}{ "message": "Sync completed", "synced": 0, }) } func (h *BankHandler) MatchTransaction(w http.ResponseWriter, r *http.Request) { transactionID := chi.URLParam(r, "id") var req struct { MatchType string `json:"match_type"` MatchID string `json:"match_id"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid request") return } _, err := h.DB.Exec(` UPDATE boc_bank_transactions SET status = 'matched', matched_to_type = $1, matched_to_id = $2 WHERE id = $3 `, req.MatchType, req.MatchID, transactionID) if err != nil { writeError(w, http.StatusInternalServerError, "failed to match transaction") return } writeJSON(w, http.StatusOK, map[string]interface{}{ "message": "Transaction matched", }) }