├── .github
├── CODEOWNERS
└── workflows
│ ├── codeql.yaml
│ ├── go.yml
│ ├── podman.yml
│ └── release.yml
├── client
├── .openapi-generator
│ └── VERSION
├── .gitignore
├── docs
│ ├── Error.md
│ ├── AchDictionary.md
│ ├── WireDictionary.md
│ ├── WireLocation.md
│ ├── Logo.md
│ ├── AchLocation.md
│ ├── WireParticipant.md
│ ├── AchParticipant.md
│ └── FEDApi.md
├── model_error.go
├── model_ach_dictionary.go
├── model_wire_dictionary.go
├── model_logo.go
├── model_wire_location.go
├── model_ach_location.go
├── .openapi-generator-ignore
├── model_wire_participant.go
├── response.go
├── model_ach_participant.go
├── README.md
└── configuration.go
├── docs
├── favicon.png
├── images
│ ├── ach-search.png
│ └── wire-search.png
├── prometheus.md
├── fpddir.md
├── FedACHdir.md
├── api
│ └── index.html
├── usage-webui.md
├── 404.html
├── kubernetes.md
├── usage-go.md
├── fpddir_FORMAT.md
├── _data
│ ├── docs-menu.yml
│ └── navigation.yml
├── Gemfile
├── usage-configuration.md
├── usage-google-cloud.md
├── Fed_STATE_CODES.md
├── intro.md
├── index.md
├── FEDACHDIR_FORMAT.MD
├── _config.yml
├── file-structure.md
├── usage-binary.md
├── usage-docker.md
├── README.md
└── Gemfile.lock
├── docker-compose.yml
├── version.go
├── renovate.json
├── AUTHORS
├── Dockerfile-fedtest
├── .gitignore
├── Dockerfile
├── const.go
├── validators.go
├── webui
├── webui.go
└── index.html
├── Dockerfile-openshift
├── cmd
├── server
│ ├── main_test.go
│ ├── http.go
│ ├── reader_test.go
│ ├── main.go
│ ├── reader.go
│ ├── search_handlers.go
│ └── search.go
└── fedtest
│ ├── ach.go
│ ├── wire.go
│ └── main.go
├── fileErrors.go
├── go.mod
├── normalize.go
├── openapi-generator
├── pkg
├── strcmp
│ ├── strcmp.go
│ └── strcmp_test.go
└── download
│ ├── download_test.go
│ └── download.go
├── normalize_test.go
├── CODE_OF_CONDUCT.md
├── makefile
├── data
├── fpddir.json
└── fedachdir.json
├── CHANGELOG.md
├── LICENSE
├── api
└── client.yaml
└── openapi.yaml
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @adamdecaf
2 |
--------------------------------------------------------------------------------
/client/.openapi-generator/VERSION:
--------------------------------------------------------------------------------
1 | 4.3.1
--------------------------------------------------------------------------------
/docs/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moov-io/fed/HEAD/docs/favicon.png
--------------------------------------------------------------------------------
/docs/images/ach-search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moov-io/fed/HEAD/docs/images/ach-search.png
--------------------------------------------------------------------------------
/docs/images/wire-search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moov-io/fed/HEAD/docs/images/wire-search.png
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | watchman:
4 | image: moov/fed:latest
5 | ports:
6 | - "8086:8086"
7 | - "9096:9096"
8 |
--------------------------------------------------------------------------------
/version.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 The Moov Authors
2 | // Use of this source code is governed by an Apache License
3 | // license that can be found in the LICENSE file.
4 |
5 | package fed
6 |
7 | // Version is the current version
8 | const Version = "v0.14.1"
9 |
--------------------------------------------------------------------------------
/docs/prometheus.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Prometheus metrics
4 | hide_hero: true
5 | show_sidebar: false
6 | menubar: docs-menu
7 | ---
8 |
9 | # Metrics
10 |
11 | The port `9096` is bound by Fed for our admin service. This HTTP server has endpoints for Prometheus metrics (`GET /metrics`), readiness checks (`GET /ready`), and liveness checks (`GET /live`).
--------------------------------------------------------------------------------
/client/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
2 | *.o
3 | *.a
4 | *.so
5 |
6 | # Folders
7 | _obj
8 | _test
9 |
10 | # Architecture specific extensions/prefixes
11 | *.[568vq]
12 | [568vq].out
13 |
14 | *.cgo1.go
15 | *.cgo2.c
16 | _cgo_defun.c
17 | _cgo_gotypes.go
18 | _cgo_export.*
19 |
20 | _testmain.go
21 |
22 | *.exe
23 | *.test
24 | *.prof
25 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["config:base"],
3 | "prConcurrentLimit": 0,
4 | "prHourlyLimit": 0,
5 | "automerge": true,
6 | "automergeType": "pr",
7 | "packageRules": [
8 | {
9 | "matchUpdateTypes": ["minor", "patch", "pin", "digest"],
10 | "automerge": true
11 | },
12 | {
13 | "matchUpdateTypes": ["major"],
14 | "automerge": false
15 | }
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/docs/fpddir.md:
--------------------------------------------------------------------------------
1 | # FedWire directory
2 |
3 | * Data: [fpddir.txt](../data/fpddir.txt)
4 | * JSON Data Example: [fpddir.json](../data/fpddir.json)
5 | * Format: [fpddir_format.md](fpddir_FORMAT.md)
6 | * Source: [Federal Reserve Bank Services](https://frbservices.org/)
7 |
8 | The effective date of this Fedwire directory is Dec 4th, 2018. These data files are no longer published on the [FRBServices](https://frbservices.org/) website.
--------------------------------------------------------------------------------
/client/docs/Error.md:
--------------------------------------------------------------------------------
1 | # Error
2 |
3 | ## Properties
4 |
5 | Name | Type | Description | Notes
6 | ------------ | ------------- | ------------- | -------------
7 | **Error** | **string** | An error message describing the problem intended for humans. |
8 |
9 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
10 |
11 |
12 |
--------------------------------------------------------------------------------
/client/docs/AchDictionary.md:
--------------------------------------------------------------------------------
1 | # AchDictionary
2 |
3 | ## Properties
4 |
5 | Name | Type | Description | Notes
6 | ------------ | ------------- | ------------- | -------------
7 | **ACHParticipants** | [**[]AchParticipant**](ACHParticipant.md) | | [optional]
8 |
9 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
10 |
11 |
12 |
--------------------------------------------------------------------------------
/docs/FedACHdir.md:
--------------------------------------------------------------------------------
1 | # FedACH directory
2 |
3 | * Data: [FedACHdir.txt](../data/FedACHdir.txt)
4 | * JSON Data Example: [fedachdir.json](../data/fedachdir.json)
5 | * Format: [FEDACHDIR_FORMAT.MD](FEDACHDIR_FORMAT.MD)
6 | * Source: [Federal Reserve Bank Services](https://frbservices.org/)
7 |
8 | The effective date of this FedACH directory is Dec 4, 2018. These data files are no longer published on the [FRBServices](https://frbservices.org/) website.
9 |
--------------------------------------------------------------------------------
/client/docs/WireDictionary.md:
--------------------------------------------------------------------------------
1 | # WireDictionary
2 |
3 | ## Properties
4 |
5 | Name | Type | Description | Notes
6 | ------------ | ------------- | ------------- | -------------
7 | **WIREParticipants** | [**[]WireParticipant**](WIREParticipant.md) | | [optional]
8 |
9 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
10 |
11 |
12 |
--------------------------------------------------------------------------------
/docs/api/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Moov Fed Endpoints
6 |
7 |
8 |
9 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/client/docs/WireLocation.md:
--------------------------------------------------------------------------------
1 | # WireLocation
2 |
3 | ## Properties
4 |
5 | Name | Type | Description | Notes
6 | ------------ | ------------- | ------------- | -------------
7 | **City** | **string** | City | [optional]
8 | **State** | **string** | State | [optional]
9 |
10 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
11 |
12 |
13 |
--------------------------------------------------------------------------------
/docs/usage-webui.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Web UI
4 | hide_hero: true
5 | show_sidebar: false
6 | menubar: docs-menu
7 | ---
8 |
9 | # Web UI
10 |
11 | With the release of `v0.14.1` Moov Fed contains a builtin Web UI that's hosted from the Go binary / Docker image.
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/client/docs/Logo.md:
--------------------------------------------------------------------------------
1 | # Logo
2 |
3 | ## Properties
4 |
5 | Name | Type | Description | Notes
6 | ------------ | ------------- | ------------- | -------------
7 | **Name** | **string** | Company name | [optional]
8 | **Url** | **string** | URL to the company logo | [optional]
9 |
10 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
11 |
12 |
13 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | # This file lists all individuals having contributed content to the repository.
2 | # For how it is generated, see `make AUTHORS`.
3 |
4 | Adam Shannon
5 | Andrew Heavin
6 | Brooke Kline
7 | Kalamity
8 | Nathan Lakritz
9 | Ray Johnson
10 | rayjlinden <42587610+rayjlinden@users.noreply.github.com>
11 | Wade Arnold
12 |
--------------------------------------------------------------------------------
/docs/404.html:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: /404.html
3 | layout: default
4 | ---
5 |
6 |
19 |
20 |
21 |
404
22 |
23 |
Page not found :(
24 |
The requested page could not be found.
25 |
26 |
--------------------------------------------------------------------------------
/.github/workflows/codeql.yaml:
--------------------------------------------------------------------------------
1 | name: CodeQL Analysis
2 |
3 | on:
4 | push:
5 | pull_request:
6 | schedule:
7 | - cron: '0 0 * * 0'
8 |
9 | jobs:
10 | CodeQL-Build:
11 | strategy:
12 | fail-fast: false
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Checkout repository
16 | uses: actions/checkout@v4
17 |
18 | - name: Initialize CodeQL
19 | uses: github/codeql-action/init@v3
20 | with:
21 | languages: go
22 |
23 | - name: Perform CodeQL Analysis
24 | uses: github/codeql-action/analyze@v3
25 |
--------------------------------------------------------------------------------
/docs/kubernetes.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Kubernetes
4 | hide_hero: true
5 | show_sidebar: false
6 | menubar: docs-menu
7 | ---
8 |
9 | # Kubernetes
10 |
11 | The following snippet runs the Fed Server on [Kubernetes](https://kubernetes.io/docs/tutorials/kubernetes-basics/) in the `apps` namespace. You can reach the fed instance at the following URL from inside the cluster.
12 |
13 | ```
14 | # Needs to be ran from inside the cluster
15 | $ curl http://fed.apps.svc.cluster.local:8086/ping
16 | PONG
17 | ```
18 |
19 | Kubernetes manifest - save in a file (`fed.yaml`) and apply with `kubectl apply -f fed.yaml`.
--------------------------------------------------------------------------------
/client/docs/AchLocation.md:
--------------------------------------------------------------------------------
1 | # AchLocation
2 |
3 | ## Properties
4 |
5 | Name | Type | Description | Notes
6 | ------------ | ------------- | ------------- | -------------
7 | **Address** | **string** | Street Address | [optional]
8 | **City** | **string** | City | [optional]
9 | **State** | **string** | State | [optional]
10 | **PostalCode** | **string** | Postal Code | [optional]
11 | **PostalExtension** | **string** | Postal Code Extension | [optional]
12 |
13 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
14 |
15 |
16 |
--------------------------------------------------------------------------------
/Dockerfile-fedtest:
--------------------------------------------------------------------------------
1 | FROM golang:1.25-alpine as builder
2 | RUN apk add -U make git
3 | RUN adduser -D -g '' --shell /bin/false moov
4 |
5 | # Pull api code into image, then build
6 | WORKDIR /go/src/github.com/moov-io/fed/
7 | COPY . .
8 | RUN make build
9 | USER moov
10 |
11 | FROM scratch
12 | LABEL maintainer="Moov "
13 |
14 | COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
15 | COPY --from=builder /go/src/github.com/moov-io/fed/bin/fedtest /bin/fedtest
16 | COPY --from=builder /etc/passwd /etc/passwd
17 |
18 | USER moov
19 | EXPOSE 8080
20 | EXPOSE 9090
21 | ENTRYPOINT ["/bin/fedtest"]
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #### joe made this: http://goel.io/joe
2 |
3 | .DS_Store
4 |
5 | #####=== Go ===#####
6 |
7 | /bin/
8 | openapi-generator*jar
9 |
10 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
11 | *.o
12 | *.a
13 | *.so
14 |
15 | # Folders
16 | _obj
17 | _test
18 |
19 | # Architecture specific extensions/prefixes
20 | *.[568vq]
21 | [568vq].out
22 |
23 | *.cgo1.go
24 | *.cgo2.c
25 | _cgo_defun.c
26 | _cgo_gotypes.go
27 | _cgo_export.*
28 |
29 | _testmain.go
30 |
31 | *.exe
32 | *.test
33 | *.prof
34 |
35 | .vscode/launch.json
36 |
37 | # code coverage
38 | coverage.html
39 | cover.out
40 | coverage.txt
41 | misspell*
42 | /lint-project.sh
43 | gitleaks.tar.gz
44 | .idea/
45 | .vscode/
46 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.25 as builder
2 | WORKDIR /go/src/github.com/moov-io/fed
3 | RUN apt-get update && apt-get install make gcc g++
4 | COPY . .
5 | RUN make build
6 | RUN useradd --shell /bin/false moov
7 |
8 | FROM scratch
9 | LABEL maintainer="Moov "
10 |
11 | COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
12 | COPY --from=builder /go/src/github.com/moov-io/fed/bin/server /bin/server
13 | COPY --from=builder /etc/passwd /etc/passwd
14 |
15 | COPY data/*.txt /data/fed/
16 |
17 | ENV FEDACH_DATA_PATH=/data/fed/FedACHdir.txt
18 | ENV FEDWIRE_DATA_PATH=/data/fed/fpddir.txt
19 |
20 | USER moov
21 | EXPOSE 8086
22 | EXPOSE 9096
23 | ENTRYPOINT ["/bin/server"]
24 |
--------------------------------------------------------------------------------
/const.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 The Moov Authors
2 | // Use of this source code is governed by an Apache License
3 | // license that can be found in the LICENSE file.
4 |
5 | package fed
6 |
7 | const (
8 | // ACHLineLength is the FedACH text file line length
9 | ACHLineLength = 155
10 | // WIRELineLength is the FedACH text file line length
11 | WIRELineLength = 101
12 | // MinimumRoutingNumberDigits is the minimum number of digits needed searching by routing numbers
13 | MinimumRoutingNumberDigits = 2
14 | // MaximumRoutingNumberDigits is the maximum number of digits allowed for searching by routing number
15 | // Based on https://www.frbservices.org/EPaymentsDirectory/search.html
16 | MaximumRoutingNumberDigits = 9
17 | )
18 |
--------------------------------------------------------------------------------
/validators.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 The Moov Authors
2 | // Use of this source code is governed by an Apache License
3 | // license that can be found in the LICENSE file.
4 |
5 | package fed
6 |
7 | import (
8 | "errors"
9 | "regexp"
10 | )
11 |
12 | var (
13 | numericRegex = regexp.MustCompile(`[^0-9]`)
14 | msgNumeric = "is not 0-9"
15 | )
16 |
17 | // validator is common validation and formatting of golang types to fed type strings
18 | type validator struct{}
19 |
20 | // isNumeric checks if a string only contains ASCII numeric (0-9) characters
21 | func (v *validator) isNumeric(s string) error {
22 | if numericRegex.MatchString(s) {
23 | // [^0-9]
24 | return errors.New(msgNumeric)
25 | }
26 | return nil
27 | }
28 |
--------------------------------------------------------------------------------
/docs/usage-go.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Go library
4 | hide_hero: true
5 | show_sidebar: false
6 | menubar: docs-menu
7 | ---
8 |
9 | # Go library
10 |
11 | This project uses [Go Modules](https://go.dev/blog/using-go-modules) and Go v1.18 or newer. See [Golang's install instructions](https://golang.org/doc/install) for help setting up Go. You can download the source code and we offer [tagged and released versions](https://github.com/moov-io/fed/releases/latest) as well. We highly recommend you use a tagged release for production.
12 |
13 | ```
14 | $ git@github.com:moov-io/fed.git
15 |
16 | # Pull down into the Go Module cache
17 | $ go get -u github.com/moov-io/fed
18 |
19 | $ go doc github.com/moov-io/fed ACHDictionary
20 | ```
21 |
--------------------------------------------------------------------------------
/client/model_error.go:
--------------------------------------------------------------------------------
1 | /*
2 | * FED API
3 | *
4 | * FED API is designed to create FEDACH and FEDWIRE dictionaries. The FEDACH dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive ACH entries. The FEDWIRE dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive WIRE entries. This project implements a modern REST HTTP API for FEDACH Dictionary and FEDWIRE Dictionary.
5 | *
6 | * API version: v1
7 | * Generated by: OpenAPI Generator (https://openapi-generator.tech)
8 | */
9 |
10 | package client
11 |
12 | // Error struct for Error
13 | type Error struct {
14 | // An error message describing the problem intended for humans.
15 | Error string `json:"error"`
16 | }
17 |
--------------------------------------------------------------------------------
/client/model_ach_dictionary.go:
--------------------------------------------------------------------------------
1 | /*
2 | * FED API
3 | *
4 | * FED API is designed to create FEDACH and FEDWIRE dictionaries. The FEDACH dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive ACH entries. The FEDWIRE dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive WIRE entries. This project implements a modern REST HTTP API for FEDACH Dictionary and FEDWIRE Dictionary.
5 | *
6 | * API version: v1
7 | * Generated by: OpenAPI Generator (https://openapi-generator.tech)
8 | */
9 |
10 | package client
11 |
12 | // AchDictionary Search results containing ACHDictionary of Participants
13 | type AchDictionary struct {
14 | ACHParticipants []AchParticipant `json:"ACHParticipants,omitempty"`
15 | }
16 |
--------------------------------------------------------------------------------
/client/model_wire_dictionary.go:
--------------------------------------------------------------------------------
1 | /*
2 | * FED API
3 | *
4 | * FED API is designed to create FEDACH and FEDWIRE dictionaries. The FEDACH dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive ACH entries. The FEDWIRE dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive WIRE entries. This project implements a modern REST HTTP API for FEDACH Dictionary and FEDWIRE Dictionary.
5 | *
6 | * API version: v1
7 | * Generated by: OpenAPI Generator (https://openapi-generator.tech)
8 | */
9 |
10 | package client
11 |
12 | // WireDictionary Search results containing WIREDictionary of Participants
13 | type WireDictionary struct {
14 | WIREParticipants []WireParticipant `json:"WIREParticipants,omitempty"`
15 | }
16 |
--------------------------------------------------------------------------------
/webui/webui.go:
--------------------------------------------------------------------------------
1 | package webui
2 |
3 | import (
4 | "embed"
5 | "net/http"
6 | "os"
7 |
8 | "github.com/moov-io/base/log"
9 |
10 | "github.com/gorilla/mux"
11 | )
12 |
13 | //go:embed *.html
14 | var WebRoot embed.FS
15 |
16 | type Controller interface {
17 | AppendRoutes(router *mux.Router) *mux.Router
18 | }
19 |
20 | func NewController(logger log.Logger) Controller {
21 | return &controller{
22 | logger: logger,
23 | basePath: os.Getenv("BASE_PATH"),
24 | }
25 | }
26 |
27 | type controller struct {
28 | logger log.Logger
29 | basePath string
30 | }
31 |
32 | func (c *controller) AppendRoutes(router *mux.Router) *mux.Router {
33 | staticFS := http.FileServer(http.FS(WebRoot))
34 | router.PathPrefix(c.basePath).Handler(http.StripPrefix(c.basePath, staticFS))
35 |
36 | return router
37 | }
38 |
--------------------------------------------------------------------------------
/client/model_logo.go:
--------------------------------------------------------------------------------
1 | /*
2 | * FED API
3 | *
4 | * FED API is designed to create FEDACH and FEDWIRE dictionaries. The FEDACH dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive ACH entries. The FEDWIRE dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive WIRE entries. This project implements a modern REST HTTP API for FEDACH Dictionary and FEDWIRE Dictionary.
5 | *
6 | * API version: v1
7 | * Generated by: OpenAPI Generator (https://openapi-generator.tech)
8 | */
9 |
10 | package client
11 |
12 | // Logo Company logo of the Fed ACH or Wire participant
13 | type Logo struct {
14 | // Company name
15 | Name string `json:"name,omitempty"`
16 | // URL to the company logo
17 | Url string `json:"url,omitempty"`
18 | }
19 |
--------------------------------------------------------------------------------
/client/model_wire_location.go:
--------------------------------------------------------------------------------
1 | /*
2 | * FED API
3 | *
4 | * FED API is designed to create FEDACH and FEDWIRE dictionaries. The FEDACH dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive ACH entries. The FEDWIRE dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive WIRE entries. This project implements a modern REST HTTP API for FEDACH Dictionary and FEDWIRE Dictionary.
5 | *
6 | * API version: v1
7 | * Generated by: OpenAPI Generator (https://openapi-generator.tech)
8 | */
9 |
10 | package client
11 |
12 | // WireLocation WIRELocation is the FEDWIRE delivery address
13 | type WireLocation struct {
14 | // City
15 | City string `json:"city,omitempty"`
16 | // State
17 | State string `json:"state,omitempty"`
18 | }
19 |
--------------------------------------------------------------------------------
/Dockerfile-openshift:
--------------------------------------------------------------------------------
1 | # Step one: build scapresults
2 | FROM registry.access.redhat.com/ubi9/go-toolset as builder
3 | COPY go.mod go.mod
4 | COPY go.sum go.sum
5 | COPY *.go ./
6 | COPY ./cmd/fedtest ./cmd/fedtest
7 | COPY ./cmd/server ./cmd/server
8 | COPY ./client ./client
9 | COPY ./data ./data
10 | COPY ./pkg ./pkg
11 | COPY ./webui ./webui
12 | COPY makefile makefile
13 | RUN make build
14 |
15 | FROM registry.access.redhat.com/ubi9/ubi-minimal
16 |
17 | ARG VERSION=unknown
18 | LABEL maintainer="Moov "
19 | LABEL name="fed"
20 | LABEL version=$VERSION
21 |
22 | COPY --from=builder /opt/app-root/src/bin/server /bin/server
23 |
24 | COPY data/*.txt /data/fed/
25 |
26 | ENV FEDACH_DATA_PATH=/data/fed/FedACHdir.txt
27 | ENV FEDWIRE_DATA_PATH=/data/fed/fpddir.txt
28 |
29 | EXPOSE 8086
30 | EXPOSE 9096
31 |
32 | ENTRYPOINT ["/bin/server"]
33 |
--------------------------------------------------------------------------------
/docs/fpddir_FORMAT.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Fedwire format
4 | hide_hero: true
5 | show_sidebar: false
6 | menubar: docs-menu
7 | ---
8 |
9 | # Fedwire directory file format
10 |
11 | **Source:** [FedWireFormat](https://frbservices.org/EPaymentsDirectory/fedwireFormat.html)
12 |
13 | | Field Name | Length | Columns |
14 | | --- | --- | --- |
15 | | Routing Number | 9 | 1-9 |
16 | | Telegraphic Name | 18 | 10-27 |
17 | | Customer Name | 36 | 28-63 |
18 | | [State or territory abbreviation](Fed_STATE_CODES.md) | 2 | 64-65 |
19 | | [City](https://frbservices.org/EPaymentsDirectory/fedwireCities.html) | 25 | 66-90 |
20 | | Funds transfer status: Y - Eligible N - Ineligible | 1 | 91 |
21 | | Funds settlement-only status: S - Settlement-Only | 1 | 92 |
22 | | Book-Entry Securities transfer status: Y - Eligible N - Ineligible | 1 | 93 |
23 | | Date of last revision: YYYYMMDD, or blank | 8 | 94-101 |
--------------------------------------------------------------------------------
/docs/_data/docs-menu.yml:
--------------------------------------------------------------------------------
1 | - label: Getting started
2 | items:
3 | - name: Overview
4 | link: /
5 | - name: What is Moov Fed?
6 | link: /intro/
7 |
8 | - label: Usage
9 | items:
10 | - name: Docker
11 | link: /usage-docker/
12 | - name: Web UI
13 | link: /usage-webui/
14 | - name: Google Cloud Run
15 | link: /usage-google-cloud/
16 | - name: Binary distribution
17 | link: /usage-binary/
18 | - name: API configuration
19 | link: /usage-configuration/
20 | - name: Go library
21 | link: /usage-go/
22 |
23 | - label: Fed search
24 | items:
25 | - name: FedACH format
26 | link: /FEDACHDIR_FORMAT/
27 | - name: Fedwire format
28 | link: /fpddir_FORMAT/
29 | - name: Fed state codes
30 | link: /Fed_STATE_CODES/
31 |
32 |
33 | - label: Production and monitoring
34 | items:
35 | - name: Kubernetes
36 | link: /kubernetes/
37 | - name: Prometheus metrics
38 | link: /prometheus/
39 |
--------------------------------------------------------------------------------
/cmd/server/main_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 The Moov Authors
2 | // Use of this source code is governed by an Apache License
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "os"
9 | "path/filepath"
10 | "testing"
11 |
12 | "github.com/moov-io/base/log"
13 | )
14 |
15 | func TestSearcher__setup(t *testing.T) {
16 | s := &searcher{logger: log.NewNopLogger()}
17 |
18 | logger := log.NewNopLogger()
19 | achFile, err := os.Open(filepath.Join("..", "..", "data", "FedACHdir.txt"))
20 | if err != nil {
21 | t.Fatal(err)
22 | }
23 | wireFile, err := os.Open(filepath.Join("..", "..", "data", "fpddir.txt"))
24 | if err != nil {
25 | t.Fatal(err)
26 | }
27 |
28 | if err := setupSearcher(logger, s, achFile, wireFile); err != nil {
29 | t.Fatal(err)
30 | }
31 | if err := setupSearcher(logger, s, achFile, nil); err == nil {
32 | t.Errorf("expected error")
33 | }
34 | if err := setupSearcher(logger, s, nil, nil); err == nil {
35 | t.Errorf("expected error")
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/cmd/server/http.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 The Moov Authors
2 | // Use of this source code is governed by an Apache License
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "fmt"
9 | "net/http"
10 | "strings"
11 |
12 | moovhttp "github.com/moov-io/base/http"
13 | "github.com/moov-io/base/log"
14 |
15 | "github.com/go-kit/kit/metrics/prometheus"
16 | stdprometheus "github.com/prometheus/client_golang/prometheus"
17 | )
18 |
19 | var (
20 | routeHistogram = prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
21 | Name: "http_response_duration_seconds",
22 | Help: "Histogram representing the http response durations",
23 | }, []string{"route"})
24 | )
25 |
26 | func wrapResponseWriter(logger log.Logger, w http.ResponseWriter, r *http.Request) http.ResponseWriter {
27 | route := fmt.Sprintf("%s%s", strings.ToLower(r.Method), strings.Replace(r.URL.Path, "/", "-", -1)) // TODO: filter out random ID's later
28 | return moovhttp.Wrap(logger, routeHistogram.With("route", route), w, r)
29 | }
30 |
--------------------------------------------------------------------------------
/client/model_ach_location.go:
--------------------------------------------------------------------------------
1 | /*
2 | * FED API
3 | *
4 | * FED API is designed to create FEDACH and FEDWIRE dictionaries. The FEDACH dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive ACH entries. The FEDWIRE dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive WIRE entries. This project implements a modern REST HTTP API for FEDACH Dictionary and FEDWIRE Dictionary.
5 | *
6 | * API version: v1
7 | * Generated by: OpenAPI Generator (https://openapi-generator.tech)
8 | */
9 |
10 | package client
11 |
12 | // AchLocation ACHLocation is the FEDACH delivery address
13 | type AchLocation struct {
14 | // Street Address
15 | Address string `json:"address,omitempty"`
16 | // City
17 | City string `json:"city,omitempty"`
18 | // State
19 | State string `json:"state,omitempty"`
20 | // Postal Code
21 | PostalCode string `json:"postalCode,omitempty"`
22 | // Postal Code Extension
23 | PostalExtension string `json:"postalExtension,omitempty"`
24 | }
25 |
--------------------------------------------------------------------------------
/cmd/fedtest/ach.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 The Moov Authors
2 | // Use of this source code is governed by an Apache License
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "context"
9 | "fmt"
10 |
11 | client "github.com/moov-io/fed/client"
12 |
13 | "github.com/antihax/optional"
14 | )
15 |
16 | func achSearch(api *client.APIClient, requestID, routingNumber string) error {
17 | opts := &client.SearchFEDACHOpts{
18 | XRequestID: optional.NewString(requestID),
19 | }
20 | if routingNumber != "" {
21 | opts.RoutingNumber = optional.NewString(routingNumber)
22 | }
23 |
24 | dict, resp, err := api.FEDApi.SearchFEDACH(context.Background(), opts)
25 | if err != nil {
26 | return fmt.Errorf("FED ACH error: %v", err)
27 | }
28 | defer resp.Body.Close()
29 |
30 | // Verify the requested routing number was found
31 | for i := range dict.ACHParticipants {
32 | if dict.ACHParticipants[i].RoutingNumber == routingNumber {
33 | return nil
34 | }
35 | }
36 | return fmt.Errorf("FED ACH no participant found for %s", routingNumber)
37 | }
38 |
--------------------------------------------------------------------------------
/cmd/fedtest/wire.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 The Moov Authors
2 | // Use of this source code is governed by an Apache License
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "context"
9 | "fmt"
10 |
11 | client "github.com/moov-io/fed/client"
12 |
13 | "github.com/antihax/optional"
14 | )
15 |
16 | func wireSearch(api *client.APIClient, requestID, routingNumber string) error {
17 | opts := &client.SearchFEDWIREOpts{
18 | XRequestID: optional.NewString(requestID),
19 | }
20 | if routingNumber != "" {
21 | opts.RoutingNumber = optional.NewString(routingNumber)
22 | }
23 |
24 | dict, resp, err := api.FEDApi.SearchFEDWIRE(context.Background(), opts)
25 | if err != nil {
26 | return fmt.Errorf("FED Wire error: %v", err)
27 | }
28 | defer resp.Body.Close()
29 |
30 | // Verify the requested routing number was found
31 | for i := range dict.WIREParticipants {
32 | if dict.WIREParticipants[i].RoutingNumber == routingNumber {
33 | return nil
34 | }
35 | }
36 | return fmt.Errorf("FED Wire no participant found for %s", routingNumber)
37 | }
38 |
--------------------------------------------------------------------------------
/client/.openapi-generator-ignore:
--------------------------------------------------------------------------------
1 | # OpenAPI Generator Ignore
2 | # Generated by openapi-generator https://github.com/openapitools/openapi-generator
3 |
4 | # Use this file to prevent files from being overwritten by the generator.
5 | # The patterns follow closely to .gitignore or .dockerignore.
6 |
7 | # As an example, the C# client generator defines ApiClient.cs.
8 | # You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
9 | #ApiClient.cs
10 |
11 | # You can match any string of characters against a directory, file or extension with a single asterisk (*):
12 | #foo/*/qux
13 | # The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
14 |
15 | # You can recursively match patterns against a directory, file or extension with a double asterisk (**):
16 | #foo/**/qux
17 | # This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
18 |
19 | # You can also negate patterns with an exclamation (!).
20 | # For example, you can ignore all files in a docs folder with the file extension .md:
21 | #docs/*.md
22 | # Then explicitly reverse the ignore rule for a single file:
23 | #!docs/README.md
24 |
--------------------------------------------------------------------------------
/docs/_data/navigation.yml:
--------------------------------------------------------------------------------
1 | - name: API
2 | link: https://moov-io.github.io/fed/api
3 | - name: Go
4 | link: https://pkg.go.dev/github.com/moov-io/fed#section-documentation
5 | - name: Community
6 | dropdown:
7 | - name: Awesome Fintech
8 | link: https://github.com/moov-io/awesome-fintech
9 | - name: Slack
10 | link: https://slack.moov.io/
11 | - name: Terms Dictionary
12 | link: https://github.com/moov-io/terms-dictionary
13 | - name: Other Projects
14 | dropdown:
15 | - name: Moov ACH
16 | link: https://moov-io.github.io/ach/
17 | - name: Moov ACHGateway
18 | link: https://moov-io.github.io/achgateway/
19 | - name: Moov ACH Test Harness
20 | link: https://github.com/moov-io/ach-test-harness
21 | - name: Moov FinCEN
22 | link: https://moov-io.github.io/fincen/
23 | - name: Moov Image Cash Letter
24 | link: https://moov-io.github.io/imagecashletter/
25 | - name: Moov Metro 2
26 | link: https://moov-io.github.io/metro2/
27 | - name: Moov Watchman
28 | link: https://moov-io.github.io/watchman/
29 | - name: Moov Wire
30 | link: https://moov-io.github.io/wire/
31 |
--------------------------------------------------------------------------------
/docs/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 | # Hello! This is where you manage which Jekyll version is used to run.
3 | # When you want to use a different version, change it below, save the
4 | # file and run `bundle install`. Run Jekyll with `bundle exec`, like so:
5 | #
6 | # bundle exec jekyll serve
7 | #
8 | # This will help ensure the proper Jekyll version is running.
9 | # Happy Jekylling!
10 | # This is the default theme for new Jekyll sites. You may change this to anything you like.
11 | gem "bulma-clean-theme"
12 | # If you want to use GitHub Pages, remove the "gem "jekyll"" above and
13 | # uncomment the line below. To upgrade, run `bundle update github-pages`.
14 | gem "github-pages", group: :jekyll_plugins
15 | # If you have any plugins, put them here!
16 | group :jekyll_plugins do
17 | gem "jekyll-feed", "~> 0.12"
18 | end
19 |
20 | # Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem
21 | # and associated library.
22 | platforms :mingw, :x64_mingw, :mswin, :jruby do
23 | gem "tzinfo", "~> 1.2"
24 | gem "tzinfo-data"
25 | end
26 |
27 | # Performance-booster for watching directories on Windows
28 | gem "wdm", "~> 0.2.0", :platforms => [:mingw, :x64_mingw, :mswin]
29 |
30 |
--------------------------------------------------------------------------------
/fileErrors.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 The Moov Authors
2 | // Use of this source code is governed by an Apache License
3 | // license that can be found in the LICENSE file.
4 |
5 | package fed
6 |
7 | import (
8 | "errors"
9 | "fmt"
10 | )
11 |
12 | // ErrFileTooLong is the error given when a file exceeds the maximum possible length
13 | var (
14 | ErrFileTooLong = errors.New("file exceeds maximum possible number of lines")
15 | // Similar to FEDACH site
16 | ErrRoutingNumberNumeric = errors.New("the routing number entered is not numeric")
17 | )
18 |
19 | // RecordWrongLengthErr is the error given when a record is the wrong length
20 | type RecordWrongLengthErr struct {
21 | Message string
22 | LengthRequired int
23 | Length int
24 | }
25 |
26 | // NewRecordWrongLengthErr creates a new error of the RecordWrongLengthErr type
27 | func NewRecordWrongLengthErr(lengthRequired int, length int) RecordWrongLengthErr {
28 | return RecordWrongLengthErr{
29 | Message: fmt.Sprintf("must be %d characters and found %d", lengthRequired, length),
30 | LengthRequired: lengthRequired,
31 | Length: length,
32 | }
33 | }
34 |
35 | func (e RecordWrongLengthErr) Error() string {
36 | return e.Message
37 | }
38 |
--------------------------------------------------------------------------------
/.github/workflows/go.yml:
--------------------------------------------------------------------------------
1 | name: Go
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | build:
11 | name: Go Build
12 | runs-on: ${{ matrix.os }}
13 | strategy:
14 | matrix:
15 | os: [ubuntu-latest, macos-latest, windows-latest]
16 | steps:
17 | - name: Set up Go 1.x
18 | uses: actions/setup-go@v5
19 | with:
20 | go-version: stable
21 | id: go
22 |
23 | - name: Check out code into the Go module directory
24 | uses: actions/checkout@v4
25 |
26 | - name: Install make (Windows)
27 | if: runner.os == 'Windows'
28 | run: choco install -y make mingw
29 |
30 | - name: Build
31 | run: make build
32 |
33 | - name: Check
34 | run: make check
35 |
36 | docker:
37 | name: Docker Build
38 | runs-on: ubuntu-latest
39 | steps:
40 | - name: Set up Go 1.x
41 | uses: actions/setup-go@v5
42 | with:
43 | go-version: 'stable'
44 | id: go
45 |
46 | - name: Check out code into the Go module directory
47 | uses: actions/checkout@v4
48 | with:
49 | fetch-depth: 0
50 |
51 | - name: Docker Build
52 | if: runner.os == 'Linux'
53 | run: make docker && make build && make test-integration && make clean-integration
54 |
--------------------------------------------------------------------------------
/client/docs/WireParticipant.md:
--------------------------------------------------------------------------------
1 | # WireParticipant
2 |
3 | ## Properties
4 |
5 | Name | Type | Description | Notes
6 | ------------ | ------------- | ------------- | -------------
7 | **RoutingNumber** | **string** | The institution's routing number | [optional]
8 | **TelegraphicName** | **string** | Short name of financial institution | [optional]
9 | **CustomerName** | **string** | Financial Institution Name | [optional]
10 | **WireLocation** | [**WireLocation**](WIRELocation.md) | | [optional]
11 | **FundsTransferStatus** | **string** | Designates funds transfer status * `Y` - Eligible * `N` - Ineligible | [optional]
12 | **FundsSettlementOnlyStatus** | **string** | Designates funds settlement only status * `S` - Settlement-Only | [optional]
13 | **BookEntrySecuritiesTransferStatus** | **string** | Designates book entry securities transfer status * `Y` - Eligible * `N` - Ineligible | [optional]
14 | **Date** | **string** | Date of last revision * YYYYMMDD * Blank | [optional]
15 | **CleanName** | **string** | Normalized name of Wire participant | [optional]
16 | **Logo** | [**Logo**](Logo.md) | | [optional]
17 |
18 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
19 |
20 |
21 |
--------------------------------------------------------------------------------
/docs/usage-configuration.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: API configuration
4 | hide_hero: true
5 | show_sidebar: false
6 | menubar: docs-menu
7 | ---
8 |
9 | # Configuration settings
10 |
11 | | Environmental Variable | Description | Default |
12 | |-----|-----|-----|
13 | | `FEDACH_DATA_PATH` | Filepath to FedACH data file | `./data/FedACHdir.txt` |
14 | | `FEDWIRE_DATA_PATH` | Filepath to Fedwire data file | `./data/fpddir.txt` |
15 | | `LOG_FORMAT` | Format for logging lines to be written as. | Options: `json`, `plain` - Default: `plain` |
16 | | `HTTP_BIND_ADDRESS` | Address for Fed to bind its HTTP server on. This overrides the command-line flag `-http.addr`. | Default: `:8086` |
17 | | `HTTP_ADMIN_BIND_ADDRESS` | Address for Fed to bind its admin HTTP server on. This overrides the command-line flag `-admin.addr`. | Default: `:9096` |
18 | | `HTTPS_CERT_FILE` | Filepath containing a certificate (or intermediate chain) to be served by the HTTP server. Requires all traffic be over secure HTTP. | Empty |
19 | | `HTTPS_KEY_FILE` | Filepath of a private key matching the leaf certificate from `HTTPS_CERT_FILE`. | Empty |
20 |
21 | ## Data persistence
22 | By design, Fed **does not persist** (save) any data about the search queries created. The only storage occurs in memory of the process and upon restart Fed will have no files or data saved. Also, no in-memory encryption of the data is performed.
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/moov-io/fed
2 |
3 | go 1.24.0
4 |
5 | toolchain go1.25.5
6 |
7 | require (
8 | github.com/antihax/optional v1.0.0
9 | github.com/docker/docker v28.5.2+incompatible
10 | github.com/go-kit/kit v0.13.0
11 | github.com/gorilla/mux v1.8.1
12 | github.com/moov-io/base v0.60.0
13 | github.com/prometheus/client_golang v1.23.2
14 | github.com/stretchr/testify v1.11.1
15 | github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342
16 | golang.org/x/oauth2 v0.34.0
17 | golang.org/x/text v0.32.0
18 | )
19 |
20 | require (
21 | github.com/beorn7/perks v1.0.1 // indirect
22 | github.com/cespare/xxhash/v2 v2.3.0 // indirect
23 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
24 | github.com/go-kit/log v0.2.1 // indirect
25 | github.com/go-logfmt/logfmt v0.6.0 // indirect
26 | github.com/kr/text v0.2.0 // indirect
27 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
28 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
29 | github.com/prometheus/client_model v0.6.2 // indirect
30 | github.com/prometheus/common v0.66.1 // indirect
31 | github.com/prometheus/procfs v0.16.1 // indirect
32 | github.com/rickar/cal/v2 v2.1.25 // indirect
33 | go.yaml.in/yaml/v2 v2.4.2 // indirect
34 | golang.org/x/sys v0.36.0 // indirect
35 | google.golang.org/protobuf v1.36.10 // indirect
36 | gopkg.in/yaml.v3 v3.0.1 // indirect
37 | )
38 |
--------------------------------------------------------------------------------
/docs/usage-google-cloud.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Google Cloud Run
4 | hide_hero: true
5 | show_sidebar: false
6 | menubar: docs-menu
7 | ---
8 |
9 | # Google Cloud Run
10 |
11 | To get started in a hosted environment you can deploy this project to the Google Cloud Platform.
12 |
13 | From your [Google Cloud dashboard](https://console.cloud.google.com/home/dashboard) create a new project and call it:
14 | ```
15 | moov-fed-demo
16 | ```
17 |
18 | Enable the [Container Registry](https://cloud.google.com/container-registry) API for your project and associate a [billing account](https://cloud.google.com/billing/docs/how-to/manage-billing-account) if needed. Then, open the Cloud Shell terminal and run the following Docker commands, substituting your unique project ID:
19 |
20 | ```
21 | docker pull moov/fed
22 | docker tag moov/fed gcr.io//fed
23 | docker push gcr.io//fed
24 | ```
25 |
26 | Deploy the container to Cloud Run:
27 | ```
28 | gcloud run deploy --image gcr.io//fed --port 8086
29 | ```
30 |
31 | Select your target platform to `1`, service name to `fed`, and region to the one closest to you (enable Google API service if a prompt appears). Upon a successful build you will be given a URL where the API has been deployed:
32 |
33 | ```
34 | https://YOUR-FED-APP-URL.a.run.app
35 | ```
36 |
37 | Now you can ping the server:
38 | ```
39 | curl https://YOUR-FED-APP-URL.a.run.app/ping
40 | ```
41 | You should get this response:
42 | ```
43 | PONG
44 | ```
--------------------------------------------------------------------------------
/docs/Fed_STATE_CODES.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Fed state codes
4 | hide_hero: true
5 | show_sidebar: false
6 | menubar: docs-menu
7 | ---
8 |
9 | # State and territory abbreviations
10 |
11 | **Source:** [Fed States](https://frbservices.org/EPaymentsDirectory/states.html)
12 |
13 | | Abbreviation | State |
14 | | --- | --- |
15 | | AL | Alabama |
16 | | AK | Alaska |
17 | | AZ | Arizona |
18 | | AR | Arkansas |
19 | | CA | California |
20 | | CO | Colorado |
21 | | CT | Connecticut |
22 | | DE | Delaware |
23 | | DC | District Of Columbia |
24 | | FL | Florida |
25 | | GA | Georgia |
26 | | GU | Guam |
27 | | HI | Hawaii |
28 | | ID | Idaho |
29 | | IL | Illinois |
30 | | IN | Indiana |
31 | | IA | Iowa |
32 | | KS | Kansas |
33 | | KY | Kentucky |
34 | | LA | Louisiana |
35 | | ME | Maine |
36 | | MD | Maryland |
37 | | MA | Massachusetts |
38 | | MI | Michigan |
39 | | MN | Minnesota |
40 | | MS | Mississippi |
41 | | MO | Missouri |
42 | | MT | Montana |
43 | | NE | Nebraska |
44 | | NV | Nevada |
45 | | NH | New Hampshire |
46 | | NJ | New Jersey |
47 | | NM | New Mexico |
48 | | NY | New York |
49 | | NC | North Carolina |
50 | | ND | North Dakota |
51 | | MP | Northern Mariana Islands |
52 | | OH | Ohio |
53 | | OK | Oklahoma |
54 | | OR | Oregon |
55 | | PA | Pennsylvania |
56 | | PR | Puerto Rico |
57 | | RI | Rhode Island |
58 | | SC | South Carolina |
59 | | SD | South Dakota |
60 | | TN | Tennessee |
61 | | TX | Texas |
62 | | UT | Utah |
63 | | VT | Vermont |
64 | | VI | Virgin Islands |
65 | | VA | Virginia |
66 | | WA | Washington |
67 | | WV | West Virginia |
68 | | WI | Wisconsin |
69 | | WY | Wyoming |
70 |
--------------------------------------------------------------------------------
/.github/workflows/podman.yml:
--------------------------------------------------------------------------------
1 | name: Podman
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | build:
11 | name: Go Build
12 | runs-on: ${{ matrix.os }}
13 | strategy:
14 | matrix:
15 | os: [ubuntu-latest]
16 | steps:
17 | - name: Set up Go 1.x
18 | uses: actions/setup-go@v5
19 | with:
20 | go-version: stable
21 | id: go
22 |
23 | - name: Clone the repository
24 | uses: actions/checkout@v4
25 |
26 | - name: Build
27 | run: make build
28 |
29 | - name: Check
30 | run: make check
31 |
32 | - name: Buildah Action
33 | id: build-image
34 | if: ${{ github.event.pull_request.head.repo.full_name == 'moov-io/fed' }}
35 | uses: redhat-actions/buildah-build@v2
36 | with:
37 | image: moov/fed
38 | tags: podman-${{ github.sha }}
39 | containerfiles: |
40 | ./Dockerfile
41 |
42 | - name: Log in to the GitHub Container registry
43 | if: ${{ github.event.pull_request.head.repo.full_name == 'moov-io/fed' }}
44 | uses: redhat-actions/podman-login@v1
45 | with:
46 | registry: docker.io
47 | username: ${{ secrets.DOCKER_USERNAME }}
48 | password: ${{ secrets.DOCKER_PASSWORD }}
49 |
50 | - name: Push to GitHub Container Repository
51 | if: ${{ github.event.pull_request.head.repo.full_name == 'moov-io/fed' }}
52 | id: push-to-ghcr
53 | uses: redhat-actions/push-to-registry@v2
54 | with:
55 | image: ${{ steps.build-image.outputs.image }}
56 | tags: ${{ steps.build-image.outputs.tags }}
57 | registry: docker.io
58 |
--------------------------------------------------------------------------------
/client/docs/AchParticipant.md:
--------------------------------------------------------------------------------
1 | # AchParticipant
2 |
3 | ## Properties
4 |
5 | Name | Type | Description | Notes
6 | ------------ | ------------- | ------------- | -------------
7 | **RoutingNumber** | **string** | The institution's routing number | [optional]
8 | **OfficeCode** | **string** | Main/Head Office or Branch * `O` - Main * `B` - Branch | [optional]
9 | **ServicingFRBNumber** | **string** | Servicing Fed's main office routing number | [optional]
10 | **RecordTypeCode** | **string** | The code indicating the ABA number to be used to route or send ACH items to the RDFI * `0` - Institution is a Federal Reserve Bank * `1` - Send items to customer routing number * `2` - Send items to customer using new routing number field | [optional]
11 | **Revised** | **string** | Date of last revision * YYYYMMDD * Blank | [optional]
12 | **NewRoutingNumber** | **string** | Financial Institution's new routing number resulting from a merger or renumber | [optional]
13 | **CustomerName** | **string** | Financial Institution Name | [optional]
14 | **AchLocation** | [**AchLocation**](ACHLocation.md) | | [optional]
15 | **PhoneNumber** | **string** | The Financial Institution's phone number | [optional]
16 | **StatusCode** | **string** | Code is based on the customers receiver code * `1` - Receives Gov/Comm | [optional]
17 | **ViewCode** | **string** | Code is current view * `1` - Current view | [optional]
18 | **CleanName** | **string** | Normalized name of ACH participant | [optional]
19 | **Logo** | [**Logo**](Logo.md) | | [optional]
20 |
21 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
22 |
23 |
24 |
--------------------------------------------------------------------------------
/client/model_wire_participant.go:
--------------------------------------------------------------------------------
1 | /*
2 | * FED API
3 | *
4 | * FED API is designed to create FEDACH and FEDWIRE dictionaries. The FEDACH dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive ACH entries. The FEDWIRE dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive WIRE entries. This project implements a modern REST HTTP API for FEDACH Dictionary and FEDWIRE Dictionary.
5 | *
6 | * API version: v1
7 | * Generated by: OpenAPI Generator (https://openapi-generator.tech)
8 | */
9 |
10 | package client
11 |
12 | // WireParticipant WIREParticipant holds a FedWIRE dir routing record as defined by Fed WIRE Format. https://frbservices.org/EPaymentsDirectory/fedwireFormat.html
13 | type WireParticipant struct {
14 | // The institution's routing number
15 | RoutingNumber string `json:"routingNumber,omitempty"`
16 | // Short name of financial institution
17 | TelegraphicName string `json:"telegraphicName,omitempty"`
18 | // Financial Institution Name
19 | CustomerName string `json:"customerName,omitempty"`
20 | WireLocation WireLocation `json:"wireLocation,omitempty"`
21 | // Designates funds transfer status * `Y` - Eligible * `N` - Ineligible
22 | FundsTransferStatus string `json:"fundsTransferStatus,omitempty"`
23 | // Designates funds settlement only status * `S` - Settlement-Only
24 | FundsSettlementOnlyStatus string `json:"fundsSettlementOnlyStatus,omitempty"`
25 | // Designates book entry securities transfer status * `Y` - Eligible * `N` - Ineligible
26 | BookEntrySecuritiesTransferStatus string `json:"bookEntrySecuritiesTransferStatus,omitempty"`
27 | // Date of last revision * YYYYMMDD * Blank
28 | Date string `json:"date,omitempty"`
29 | // Normalized name of Wire participant
30 | CleanName string `json:"cleanName,omitempty"`
31 | Logo Logo `json:"logo,omitempty"`
32 | }
33 |
--------------------------------------------------------------------------------
/docs/intro.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Intro
4 | hide_hero: true
5 | show_sidebar: false
6 | menubar: docs-menu
7 | ---
8 |
9 | ## What is Moov Fed?
10 |
11 | [Moov Fed](https://github.com/moov-io/fed) implements an HTTP interface to search [Fedwire](https://github.com/moov-io/fed/tree/master/docs/fpddir.md) and [FedACH](https://github.com/moov-io/fed/tree/master/docs/FedACHdir.md) data from the Federal Reserve Bank Services.
12 |
13 | The data and formats below represent a compilation of **Fedwire** and **FedACH** data from the [Federal Reserve Bank Services site](https://frbservices.org/):
14 |
15 | * [FEDACH](https://github.com/moov-io/fed/tree/master/docs/FedACHdir.md)
16 |
17 | * [FEDWire](https://github.com/moov-io/fed/tree/master/docs/fpddir.md)
18 |
19 | Fed can be used standalone to search for routing numbers by Financial Institution name, city, state, postal code, and routing number. It can also be used in conjunction with [Moov ACH](https://github.com/moov-io/ach) and [Moov Wire](https://github.com/moov-io/wire) to validate routing numbers.
20 |
21 | ## Data files
22 |
23 | The data files included in this repository ([`FedACHdir.md`](https://github.com/moov-io/fed/tree/master/docs/FedACHdir.md) and [`fpddir.md`](https://github.com/moov-io/fed/tree/master/docs/fpddir.md)) are **outdated** and from 2018. The Fed no longer releases this data publicly and licensing on more recent files prevents us from distributing them. However, the Fed still complies this data and you can retrieve up-to-date files for use in our project, either from [LexisNexis](https://risk.lexisnexis.com/financial-services/payments-efficiency/payment-routing) or your financial institution.
24 |
25 | Moov Fed can read the data files from anywhere on the filesystem. This allows you to mount the files and set `FEDACH_DATA_PATH` / `FEDWIRE_DATA_PATH` environmental variables. Both official formats from the Federal Reserve (plaintext and JSON) are supported.
26 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Overview
4 | hide_hero: true
5 | show_sidebar: false
6 | menubar: docs-menu
7 | ---
8 |
9 | # Overview
10 |
11 | 
12 |
13 | Moov's mission is to give developers an easy way to create and integrate bank processing into their own software products. Our open source projects are each focused on solving a single responsibility in financial services and designed around performance, scalability, and ease of use.
14 |
15 | Fed implements utility services for searching the United States Federal Reserve System such as [ABA routing numbers](https://en.wikipedia.org/wiki/ABA_routing_transit_number), financial institution name lookup, and [Fedwire](https://en.wikipedia.org/wiki/Fedwire) and [FedACH](https://en.wikipedia.org/wiki/FedACH) routing information. The HTTP server is available in a [Docker image](#docker) and the Go package `github.com/moov-io/fed` is available. Moov's primary usage for this project is with ACH origination in our [paygate](https://github.com/moov-io/paygate) project.
16 |
17 | The data and formats in this repository represent a compilation of **FedWire** and **FedACH** data from the [Federal Reserve Bank Services site](https://frbservices.org/). Both the official Fed plaintext and JSON file formats are supported.
18 |
19 | ## Copyright and terms of use
20 |
21 | Copyright © Federal Reserve Banks
22 |
23 | By accessing the [data](https://github.com/moov-io/fed/tree/master/data) in this repository you agree to the [Federal Reserve Banks' Terms of Use](https://frbservices.org/terms/index.html) and the [E-Payments Routing Directory Terms of Use Agreement](https://www.frbservices.org/EPaymentsDirectory/agreement.html).
24 |
25 | ## Disclaimer
26 |
27 | **THIS REPOSITORY IS NOT AFFILIATED WITH THE FEDERAL RESERVE BANKS AND IS NOT AN OFFICIAL SOURCE FOR FEDWIRE AND FEDACH DATA.**
--------------------------------------------------------------------------------
/client/response.go:
--------------------------------------------------------------------------------
1 | /*
2 | * FED API
3 | *
4 | * FED API is designed to create FEDACH and FEDWIRE dictionaries. The FEDACH dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive ACH entries. The FEDWIRE dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive WIRE entries. This project implements a modern REST HTTP API for FEDACH Dictionary and FEDWIRE Dictionary.
5 | *
6 | * API version: v1
7 | * Generated by: OpenAPI Generator (https://openapi-generator.tech)
8 | */
9 |
10 | package client
11 |
12 | import (
13 | "net/http"
14 | )
15 |
16 | // APIResponse stores the API response returned by the server.
17 | type APIResponse struct {
18 | *http.Response `json:"-"`
19 | Message string `json:"message,omitempty"`
20 | // Operation is the name of the OpenAPI operation.
21 | Operation string `json:"operation,omitempty"`
22 | // RequestURL is the request URL. This value is always available, even if the
23 | // embedded *http.Response is nil.
24 | RequestURL string `json:"url,omitempty"`
25 | // Method is the HTTP method used for the request. This value is always
26 | // available, even if the embedded *http.Response is nil.
27 | Method string `json:"method,omitempty"`
28 | // Payload holds the contents of the response body (which may be nil or empty).
29 | // This is provided here as the raw response.Body() reader will have already
30 | // been drained.
31 | Payload []byte `json:"-"`
32 | }
33 |
34 | // NewAPIResponse returns a new APIResonse object.
35 | func NewAPIResponse(r *http.Response) *APIResponse {
36 |
37 | response := &APIResponse{Response: r}
38 | return response
39 | }
40 |
41 | // NewAPIResponseWithError returns a new APIResponse object with the provided error message.
42 | func NewAPIResponseWithError(errorMessage string) *APIResponse {
43 |
44 | response := &APIResponse{Message: errorMessage}
45 | return response
46 | }
47 |
--------------------------------------------------------------------------------
/docs/FEDACHDIR_FORMAT.MD:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: FedACH format
4 | hide_hero: true
5 | show_sidebar: false
6 | menubar: docs-menu
7 | ---
8 |
9 |
10 | # FedACH directory file format
11 |
12 | **Source:** [achFormat](https://frbservices.org/EPaymentsDirectory/achFormat.html)
13 |
14 | | Field Name | Length | Position | Description |
15 | | --- | --- | --- | --- |
16 | | Routing Number | 9 | 1-9 | The institution's routing number |
17 | | Office Code | 1 | 10 | Main office or branch O=main B=branch |
18 | | Servicing FRB Number | 9 | 11-19 | Servicing Fed's main office routing number |
19 | | Record Type Code | 1 | 20 | The code indicating the ABA number to be used to route or send ACH items to the RFI 0 = Institution is a Federal Reserve Bank 1 = Send items to customer routing number 2 = Send items to customer using new routing number field |
20 | | Change Date | 6 | 21-26 | Date of last change to CRF information (MMDDYY) |
21 | | New Routing Number | 9 | 27-35 | Institution's new routing number resulting from a merger or renumber |
22 | | Customer Name | 36 | 36-71 | Commonly used abbreviated name |
23 | | Address | 36 | 72-107 | Delivery address |
24 | | [City](https://frbservices.org/EPaymentsDirectory/fedachCities.html) | 20 | 108-127 | City name in the delivery address |
25 | | [State Code](Fed_STATE_CODES.md) | 2 | 128-129 | State code of the state in the delivery address |
26 | | Zipcode | 5 | 130-134 | Zipcode in the delivery address |
27 | | Zipcode Extension | 4 | 135-138 | Zipcode extension in the delivery address |
28 | | Telephone Area Code | 3 | 139-141 | Area code of the CRF contact telephone number |
29 | | Telephone Prefix Number | 3 | 142-144 | Prefix of the CRF contact telephone number |
30 | | Telephone Suffix Number | 4 | 145-148 | Suffix of the CRF contact telephone number |
31 | | Institution Status Code | 1 | 149 | Code is based on the customers receiver code 1 = Receives Gov/Comm |
32 | | Data View Code | 1 | 150 | 1 = Current view |
33 | | Filler | 5 | 151-155 | Spaces |
--------------------------------------------------------------------------------
/normalize.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The Moov Authors
2 | // Use of this source code is governed by an Apache License
3 | // license that can be found in the LICENSE file.
4 |
5 | package fed
6 |
7 | import (
8 | "regexp"
9 | "strings"
10 | )
11 |
12 | var (
13 | symbolReplacer = strings.NewReplacer(
14 | ".", "",
15 | ",", "",
16 | ":", "",
17 | "/", " ",
18 | "(", "",
19 | ")", "",
20 | "-", " ", // 'CITIBANK-NEW YORK' => 'CITIBANK NEW YORK'
21 | )
22 | wasteReplacer = strings.NewReplacer(
23 | "N.A.", " ",
24 | )
25 | spaceTrimming = regexp.MustCompile(`\s{2,}`)
26 |
27 | // Replacements for banks that have lots of similar names.
28 | // The big banks will have '$name - Arizona' which confuses logo search tools, so just replace them with known-good names.
29 | //
30 | // Based on https://github.com/wealthsimple/frb-participants/blob/master/data/manually-normalized-institution-names.yml
31 | nameReplacements = map[string]string{
32 | "ALLY BANK": "Ally Bank",
33 | "AMERICAN EXPRESS": "American Express",
34 | "BANK OF AMERICA": "Bank of America",
35 | "CAPITAL ONE": "Capital One",
36 | "CHARLES SCHWAB BANK": "Charles Schwab",
37 | "CITIBANK": "Citibank",
38 | "FIDELITY BANK": "Fidelity",
39 | "HSBC": "HSBC Bank",
40 | "JPMORGAN CHASE": "Chase",
41 | "PNC BANK": "PNC Bank",
42 | "SUNTRUST": "SunTrust",
43 | "TD BANK": "TD Bank",
44 | "WELLS FARGO": "Wells Fargo",
45 | "US BANK": "US Bank",
46 | "USAA": "USAA",
47 | }
48 | )
49 |
50 | func Normalize(name string) string {
51 | for sub, answer := range nameReplacements {
52 | if strings.Contains(name, sub) {
53 | return answer
54 | }
55 | }
56 | return RemoveDuplicatedSpaces(StripSymbols(StripWaste(name)))
57 | }
58 |
59 | func StripSymbols(name string) string {
60 | return symbolReplacer.Replace(name)
61 | }
62 |
63 | func StripWaste(name string) string {
64 | return wasteReplacer.Replace(name)
65 | }
66 |
67 | func RemoveDuplicatedSpaces(name string) string {
68 | return spaceTrimming.ReplaceAllString(name, " ")
69 | }
70 |
--------------------------------------------------------------------------------
/openapi-generator:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | #
3 | # From https://github.com/OpenAPITools/openapi-generator
4 | #
5 | ####
6 | # Save as openapi-generator-cli on your PATH. chmod u+x. Enjoy.
7 | #
8 | # This script will query github on every invocation to pull the latest released version
9 | # of openapi-generator.
10 | #
11 | # If you want repeatable executions, you can explicitly set a version via
12 | # OPENAPI_GENERATOR_VERSION
13 | # e.g. (in Bash)
14 | # export OPENAPI_GENERATOR_VERSION=3.1.0
15 | # openapi-generator-cli.sh
16 | # or
17 | # OPENAPI_GENERATOR_VERSION=3.1.0 openapi-generator-cli.sh
18 | #
19 | # This is also helpful, for example, if you want want to evaluate a SNAPSHOT version.
20 | #
21 | # NOTE: Jars are downloaded on demand from maven into the same directory as this script
22 | # for every 'latest' version pulled from github. Consider putting this under its own directory.
23 | ####
24 | set -o pipefail
25 |
26 | for cmd in {mvn,python,curl}; do
27 | if ! command -v ${cmd} > /dev/null; then
28 | >&2 echo "This script requires '${cmd}' to be installed."
29 | exit 1
30 | fi
31 | done
32 |
33 | function latest.tag {
34 | local uri="https://api.github.com/repos/${1}/tags"
35 | curl -s ${uri} | python -c "import sys, json; print json.load(sys.stdin)[0]['name'][1:]"
36 | }
37 |
38 | ghrepo=openapitools/openapi-generator
39 | groupid=org.openapitools
40 | artifactid=openapi-generator-cli
41 | ver=${OPENAPI_GENERATOR_VERSION:-$(latest.tag $ghrepo)}
42 |
43 | jar=${artifactid}-${ver}.jar
44 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
45 |
46 | if [ ! -f ${DIR}/${jar} ]; then
47 | repo="central::default::https://repo1.maven.org/maven2/"
48 | if [[ ${ver} =~ ^.*-SNAPSHOT$ ]]; then
49 | repo="central::default::https://oss.sonatype.org/content/repositories/snapshots"
50 | fi
51 | mvn org.apache.maven.plugins:maven-dependency-plugin:2.9:get \
52 | -DremoteRepositories=${repo} \
53 | -Dartifact=${groupid}:${artifactid}:${ver} \
54 | -Dtransitive=false \
55 | -Ddest=${DIR}/${jar}
56 | fi
57 |
58 | java -ea \
59 | ${JAVA_OPTS} \
60 | -Xms512M \
61 | -Xmx1024M \
62 | -server \
63 | -jar ${DIR}/${jar} "$@"
--------------------------------------------------------------------------------
/client/model_ach_participant.go:
--------------------------------------------------------------------------------
1 | /*
2 | * FED API
3 | *
4 | * FED API is designed to create FEDACH and FEDWIRE dictionaries. The FEDACH dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive ACH entries. The FEDWIRE dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive WIRE entries. This project implements a modern REST HTTP API for FEDACH Dictionary and FEDWIRE Dictionary.
5 | *
6 | * API version: v1
7 | * Generated by: OpenAPI Generator (https://openapi-generator.tech)
8 | */
9 |
10 | package client
11 |
12 | // AchParticipant ACHParticipant holds a FedACH dir routing record as defined by Fed ACH Format. https://www.frbservices.org/EPaymentsDirectory/achFormat.html
13 | type AchParticipant struct {
14 | // The institution's routing number
15 | RoutingNumber string `json:"routingNumber,omitempty"`
16 | // Main/Head Office or Branch * `O` - Main * `B` - Branch
17 | OfficeCode string `json:"officeCode,omitempty"`
18 | // Servicing Fed's main office routing number
19 | ServicingFRBNumber string `json:"servicingFRBNumber,omitempty"`
20 | // The code indicating the ABA number to be used to route or send ACH items to the RDFI * `0` - Institution is a Federal Reserve Bank * `1` - Send items to customer routing number * `2` - Send items to customer using new routing number field
21 | RecordTypeCode string `json:"recordTypeCode,omitempty"`
22 | // Date of last revision * YYYYMMDD * Blank
23 | Revised string `json:"revised,omitempty"`
24 | // Financial Institution's new routing number resulting from a merger or renumber
25 | NewRoutingNumber string `json:"newRoutingNumber,omitempty"`
26 | // Financial Institution Name
27 | CustomerName string `json:"customerName,omitempty"`
28 | AchLocation AchLocation `json:"achLocation,omitempty"`
29 | // The Financial Institution's phone number
30 | PhoneNumber string `json:"phoneNumber,omitempty"`
31 | // Code is based on the customers receiver code * `1` - Receives Gov/Comm
32 | StatusCode string `json:"statusCode,omitempty"`
33 | // Code is current view * `1` - Current view
34 | ViewCode string `json:"viewCode,omitempty"`
35 | // Normalized name of ACH participant
36 | CleanName string `json:"cleanName,omitempty"`
37 | Logo Logo `json:"logo,omitempty"`
38 | }
39 |
--------------------------------------------------------------------------------
/docs/_config.yml:
--------------------------------------------------------------------------------
1 | # Welcome to Jekyll!
2 | #
3 | # This config file is meant for settings that affect your whole blog, values
4 | # which you are expected to set up once and rarely edit after that. If you find
5 | # yourself editing this file very often, consider using Jekyll's data files
6 | # feature for the data you need to update frequently.
7 | #
8 | # For technical reasons, this file is *NOT* reloaded automatically when you use
9 | # 'bundle exec jekyll serve'. If you change this file, please restart the server process.
10 | #
11 | # If you need help with YAML syntax, here are some quick references for you:
12 | # https://learn-the-web.algonquindesign.ca/topics/markdown-yaml-cheat-sheet/#yaml
13 | # https://learnxinyminutes.com/docs/yaml/
14 | #
15 | # Site settings
16 | # These are used to personalize your new site. If you look in the HTML files,
17 | # you will see them accessed via {{ site.title }}, {{ site.email }}, and so on.
18 | # You can create any custom variable you would like, and they will be accessible
19 | # in the templates via {{ site.myvariable }}.
20 |
21 | title: Moov Fed
22 | email: oss@moov.io
23 | description: >- # this means to ignore newlines until "baseurl:"
24 | Fed implements utility services for searching the United States Federal Reserve System such as ABA routing numbers, financial institution name lookup, and Fedwire and FedACH routing information.
25 | url: "https://moov-io.github.io" # the base hostname & protocol for your site, e.g. http://example.com
26 | baseurl: "/fed" # the subpath of your site, e.g. /blog
27 | source_code: https://github.com/moov-io/fed
28 | permalink: pretty
29 |
30 | # Build settings
31 | remote_theme: moov-io/bulma-clean-theme
32 | plugins:
33 | - jekyll-feed
34 | - github-pages
35 |
36 | # Exclude from processing.
37 | # The following items will not be processed, by default.
38 | # Any item listed under the `exclude:` key here will be automatically added to
39 | # the internal "default list".
40 | #
41 | # Excluded items can be processed by explicitly listing the directories or
42 | # their entries' file path in the `include:` list.
43 | #
44 | # exclude:
45 | # - .sass-cache/
46 | # - .jekyll-cache/
47 | # - gemfiles/
48 | # - Gemfile
49 | # - Gemfile.lock
50 | # - node_modules/
51 | # - vendor/bundle/
52 | # - vendor/cache/
53 | # - vendor/gems/
54 | # - vendor/ruby/
55 |
--------------------------------------------------------------------------------
/client/README.md:
--------------------------------------------------------------------------------
1 | # Go API client for client
2 |
3 | FED API is designed to create FEDACH and FEDWIRE dictionaries. The FEDACH dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive ACH entries. The FEDWIRE dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive WIRE entries. This project implements a modern REST HTTP API for FEDACH Dictionary and FEDWIRE Dictionary.
4 |
5 | ## Overview
6 | This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the [OpenAPI-spec](https://www.openapis.org/) from a remote server, you can easily generate an API client.
7 |
8 | - API version: v1
9 | - Package version: 1.0.0
10 | - Build package: org.openapitools.codegen.languages.GoClientCodegen
11 | For more information, please visit [https://github.com/moov-io/fed](https://github.com/moov-io/fed)
12 |
13 | ## Installation
14 |
15 | Install the following dependencies:
16 |
17 | ```shell
18 | go get github.com/stretchr/testify/assert
19 | go get golang.org/x/oauth2
20 | go get golang.org/x/net/context
21 | go get github.com/antihax/optional
22 | ```
23 |
24 | Put the package under your project folder and add the following in import:
25 |
26 | ```golang
27 | import "./client"
28 | ```
29 |
30 | ## Documentation for API Endpoints
31 |
32 | All URIs are relative to *http://localhost:8086*
33 |
34 | Class | Method | HTTP request | Description
35 | ------------ | ------------- | ------------- | -------------
36 | *FEDApi* | [**Ping**](docs/FEDApi.md#ping) | **Get** /ping | Ping the FED service to check if running
37 | *FEDApi* | [**SearchFEDACH**](docs/FEDApi.md#searchfedach) | **Get** /fed/ach/search | Search FEDACH names and metadata
38 | *FEDApi* | [**SearchFEDWIRE**](docs/FEDApi.md#searchfedwire) | **Get** /fed/wire/search | Search FEDWIRE names and metadata
39 |
40 |
41 | ## Documentation For Models
42 |
43 | - [AchDictionary](docs/AchDictionary.md)
44 | - [AchLocation](docs/AchLocation.md)
45 | - [AchParticipant](docs/AchParticipant.md)
46 | - [Error](docs/Error.md)
47 | - [Logo](docs/Logo.md)
48 | - [WireDictionary](docs/WireDictionary.md)
49 | - [WireLocation](docs/WireLocation.md)
50 | - [WireParticipant](docs/WireParticipant.md)
51 |
52 |
53 | ## Documentation For Authorization
54 |
55 | Endpoints do not require authorization.
56 |
57 |
58 |
59 | ## Author
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/docs/file-structure.md:
--------------------------------------------------------------------------------
1 | # FedACH directory file format
2 |
3 | **Source:** [achFormat](https://frbservices.org/EPaymentsDirectory/achFormat.html)
4 |
5 | | Field Name | Length | Position | Description |
6 | | --- | --- | --- | --- |
7 | | Routing Number | 9 | 1-9 | The institution's routing number |
8 | | Office Code | 1 | 10 | Main office or branch O=main B=branch |
9 | | Servicing FRB Number | 9 | 11-19 | Servicing Fed's main office routing number |
10 | | Record Type Code | 1 | 20 | The code indicating the ABA number to be used to route or send ACH items to the RFI 0 = Institution is a Federal Reserve Bank 1 = Send items to customer routing number 2 = Send items to customer using new routing number field |
11 | | Change Date | 6 | 21-26 | Date of last change to CRF information (MMDDYY) |
12 | | New Routing Number | 9 | 27-35 | Institution's new routing number resulting from a merger or renumber |
13 | | Customer Name | 36 | 36-71 | Commonly used abbreviated name |
14 | | Address | 36 | 72-107 | Delivery address |
15 | | City| 20 | 108-127 | City name in the delivery address |
16 | | State | 2 | 128-129 | State code of the state in the delivery address |
17 | | Zipcode | 5 | 130-134 | Zipcode in the delivery address |
18 | | Zipcode Extension | 4 | 135-138 | Zipcode extension in the delivery address |
19 | | Telephone Area Code | 3 | 139-141 | Area code of the CRF contact telephone number |
20 | | Telephone Prefix Number | 3 | 142-144 | Prefix of the CRF contact telephone number |
21 | | Telephone Suffix Number | 4 | 145-148 | Suffix of the CRF contact telephone number |
22 | | Institution Status Code | 1 | 149 | Code is based on the customers receiver code 1 = Receives Gov/Comm |
23 | | Data View Code | 1 | 150 | 1 = Current view |
24 | | Filler | 5 | 151-155 | Spaces |
25 |
26 | # Fedwire directory file format
27 |
28 | **Source:** [FedWireFormat](https://frbservices.org/EPaymentsDirectory/fedwireFormat.html)
29 |
30 | | Field Name | Length | Columns |
31 | | --- | --- | --- |
32 | | Routing Number | 9 | 1-9 |
33 | | Telegraphic Name | 18 | 10-27 |
34 | | Customer Name | 36 | 28-63 |
35 | | State | 2 | 64-65 |
36 | | City | 25 | 66-90 |
37 | | Funds transfer status: Y - Eligible N - Ineligible | 1 | 91 |
38 | | Funds settlement-only status: S - Settlement-Only | 1 | 92 |
39 | | Book-Entry Securities transfer status: Y - Eligible N - Ineligible | 1 | 93 |
40 | | Date of last revision: YYYYMMDD, or blank | 8 | 94-101 |
41 |
42 |
--------------------------------------------------------------------------------
/docs/usage-binary.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Binary distribution
4 | hide_hero: true
5 | show_sidebar: false
6 | menubar: docs-menu
7 | ---
8 |
9 | # Binary distribution
10 |
11 | Download the [latest Moov Fed server release](https://github.com/moov-io/fed/releases) for your operating system and run it from a terminal.
12 |
13 | ```sh
14 | $ ./fed-darwin-amd64
15 | ts=2019-06-20T23:23:44.870717Z caller=main.go:75 startup="Starting fed server version v0.4.1"
16 | ts=2019-06-20T23:23:44.871623Z caller=main.go:135 transport=HTTP addr=:8086
17 | ts=2019-06-20T23:23:44.871692Z caller=main.go:125 admin="listening on :9096"
18 | ```
19 |
20 | The Moov Fed service will be running on port `8086` (with an admin port on `9096`).
21 |
22 | Confirm that the service is running by issuing the following command or simply visiting [localhost:8086/ping](http://localhost:8086/ping) in your browser.
23 |
24 | ```sh
25 | $ curl http://localhost:8086/ping
26 | PONG
27 | ```
28 |
29 | Search for a routing number:
30 |
31 | ```
32 | $ curl "localhost:8086/fed/ach/search?routingNumber=273976369"
33 | {
34 | "achParticipants": [
35 | {
36 | "routingNumber": "273976369",
37 | "officeCode": "O",
38 | "servicingFRBNumber": "071000301",
39 | "recordTypeCode": "1",
40 | "revised": "041513",
41 | "newRoutingNumber": "000000000",
42 | "customerName": "VERIDIAN CREDIT UNION",
43 | "achLocation": {
44 | "address": "1827 ANSBOROUGH",
45 | "city": "WATERLOO",
46 | "state": "IA",
47 | "postalCode": "50702",
48 | "postalCodeExtension": "0000"
49 | },
50 | "phoneNumber": "3192878332",
51 | "statusCode": "1",
52 | "viewCode": "1"
53 | }
54 | ],
55 | "wireParticipants": null
56 | }
57 | ```
58 |
59 | Search for a financial institution by name:
60 |
61 | ```
62 | $ curl "localhost:8086/fed/ach/search?name=Veridian&limit=1"
63 | {
64 | "achParticipants": [
65 | {
66 | "routingNumber": "273976369",
67 | "officeCode": "O",
68 | "servicingFRBNumber": "071000301",
69 | "recordTypeCode": "1",
70 | "revised": "041513",
71 | "newRoutingNumber": "000000000",
72 | "customerName": "VERIDIAN CREDIT UNION",
73 | "achLocation": {
74 | "address": "1827 ANSBOROUGH",
75 | "city": "WATERLOO",
76 | "state": "IA",
77 | "postalCode": "50702",
78 | "postalCodeExtension": "0000"
79 | },
80 | "phoneNumber": "3192878332",
81 | "statusCode": "1",
82 | "viewCode": "1"
83 | }
84 | ],
85 | "wireParticipants": null
86 | }
87 | ```
--------------------------------------------------------------------------------
/cmd/fedtest/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 The Moov Authors
2 | // Use of this source code is governed by an Apache License
3 | // license that can be found in the LICENSE file.
4 |
5 | // fedtest is a cli tool for testing Moov's FED API endpoints.
6 | //
7 | // fedtest is not a stable tool. Please contact Moov developers if you intend to use this tool,
8 | // otherwise we might change the tool (or remove it) without notice.
9 | package main
10 |
11 | import (
12 | "flag"
13 | "fmt"
14 | "log"
15 | "net/http"
16 | "net/url"
17 | "time"
18 |
19 | "github.com/moov-io/base"
20 | "github.com/moov-io/fed"
21 | client "github.com/moov-io/fed/client"
22 | )
23 |
24 | var (
25 | flagLocal = flag.Bool("local", false, "Use local HTTP addresses")
26 | flagDebug = flag.Bool("debug", false, "Enable verbose debug logging")
27 | flagAddress = flag.String("address", "https://api.moov.io/v1", "HTTP address for FED service")
28 |
29 | flagRoutingNumber = flag.String("routing-number", chaseCaliforniaRouting, "Routing number to lookup in FED")
30 | )
31 |
32 | const (
33 | chaseCaliforniaRouting = "322271627"
34 | )
35 |
36 | func main() {
37 | flag.Parse()
38 |
39 | log.SetFlags(log.Ldate | log.Ltime | log.LUTC | log.Lmicroseconds | log.Lshortfile)
40 | log.Printf("Starting fedtest %s", fed.Version)
41 |
42 | api := client.NewAPIClient(makeConfig())
43 |
44 | requestID, routingNumber := base.ID(), *flagRoutingNumber
45 | log.Printf("[INFO] using x-request-id: %s", requestID)
46 |
47 | // ACH search
48 | if err := achSearch(api, requestID, routingNumber); err != nil {
49 | log.Fatalf("[FAILURE] ACH: error looking up %s: %v", routingNumber, err)
50 | } else {
51 | log.Printf("[SUCCESS] ACH: found %s", routingNumber)
52 | }
53 |
54 | // WIRE search
55 | if err := wireSearch(api, requestID, routingNumber); err != nil {
56 | log.Fatalf("[FAILURE] Wire: error looking up %s: %v", routingNumber, err)
57 | } else {
58 | log.Printf("[SUCCESS] Wire: found %s", routingNumber)
59 | }
60 | }
61 |
62 | func makeConfig() *client.Configuration {
63 | conf := client.NewConfiguration()
64 | if *flagAddress != "" {
65 | u, _ := url.Parse(*flagAddress)
66 | conf.Scheme = u.Scheme
67 | conf.Host = u.Host
68 | conf.BasePath = u.Path
69 | }
70 | if *flagLocal {
71 | conf.Scheme = "http"
72 | conf.Host = "localhost:8086"
73 | conf.BasePath = ""
74 | }
75 | if *flagDebug {
76 | conf.Debug = true
77 | }
78 | conf.UserAgent = fmt.Sprintf("moov fedtest/%s", fed.Version)
79 | conf.HTTPClient = &http.Client{
80 | Timeout: 30 * time.Second,
81 | Transport: &http.Transport{
82 | MaxIdleConns: 100,
83 | MaxIdleConnsPerHost: 100,
84 | MaxConnsPerHost: 100,
85 | IdleConnTimeout: 1 * time.Minute,
86 | },
87 | }
88 | return conf
89 | }
90 |
--------------------------------------------------------------------------------
/pkg/strcmp/strcmp.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 The Moov Authors
2 | // Use of this source code is governed by an Apache License
3 | // license that can be found in the LICENSE file.
4 |
5 | // Package strcomp defines functions to return a percent match between two
6 | // strings. Many algorithms are available, but all are normalized to [0.00, 1.00]
7 | // for use as percentages.
8 | //
9 | // Any value outside of the normalized range represents a bug and should be fixed.
10 |
11 | // ToDo: Move to internal directory
12 |
13 | package strcmp
14 |
15 | import (
16 | "unicode/utf8"
17 |
18 | "github.com/xrash/smetrics"
19 | )
20 |
21 | // JaroWinkler is a more accurate version of the Jaro algorithm. It works by boosting the
22 | // score of exact matches at the beginning of the strings. By doing this, Winkler says that
23 | // typos are less common to happen at the beginning.
24 | //
25 | // For this to happen, it introduces two more parameters: the boostThreshold and the prefixSize.
26 | // These are commonly set to 0.7 and 4, respectively.
27 | //
28 | // From: https://godoc.org/github.com/xrash/smetrics
29 | func JaroWinkler(a, b string) float64 {
30 | if a == "" || b == "" {
31 | return 0.00
32 | }
33 | return smetrics.JaroWinkler(a, b, 0.7, 4)
34 | }
35 |
36 | // Levenshtein is the "edit distance" between two strings. This is the count of operations
37 | // (insert, delete, replace) needed for two strings to be equal.
38 | func Levenshtein(a, b string) float64 {
39 | if a == "" || b == "" {
40 | return 0.00
41 | }
42 |
43 | length := utf8.RuneCountInString(a)
44 | if n := utf8.RuneCountInString(b); n > length {
45 | length = n // set length to larger value of the two strings
46 | }
47 | ed := float64(smetrics.WagnerFischer(a, b, 1, 1, 2))
48 |
49 | score := ed / float64(length)
50 | if score > 1.00 {
51 | // If more edits are required than the string's length a and b aren't equal.
52 | return 0.00
53 | }
54 | return 1 - score
55 | }
56 |
57 | // Soundex is a phonetic algorithm that considers how the words sound in english.
58 | // Soundex maps a name to a 4-byte string consisting of the first letter of the original string and three numbers. Strings that sound similar should map to the same thing.
59 | //
60 | // Retruned is Hamming computed over both phonetic outputs.
61 | func Soundex(a, b string) float64 {
62 | if a == "" || b == "" {
63 | return 0.00
64 | }
65 | return hamming(smetrics.Soundex(a), smetrics.Soundex(b))
66 | }
67 |
68 | // Hamming distance is the minimum number of substitutions required to change one string into the other.
69 | func hamming(a, b string) float64 {
70 | if a == "" || b == "" {
71 | return 0.00
72 | }
73 |
74 | length := utf8.RuneCountInString(a)
75 | if n := utf8.RuneCountInString(b); n != length {
76 | return 0.0
77 | }
78 | n, _ := smetrics.Hamming(a, b)
79 | return 1 - (float64(n) / float64(length))
80 | }
81 |
--------------------------------------------------------------------------------
/docs/usage-docker.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Docker
4 | hide_hero: true
5 | show_sidebar: false
6 | menubar: docs-menu
7 | ---
8 |
9 | # Docker
10 |
11 | We publish a [public Docker image `moov/fed`](https://hub.docker.com/r/moov/fed/) from Docker Hub or use this repository. No configuration is required to serve on `:8086` and metrics at `:9096/metrics` in Prometheus format. We also have Docker images for [OpenShift](https://quay.io/repository/moov/fed?tab=tags) published as `quay.io/moov/fed`.
12 |
13 | Moov ImageCashLetter is dependent on Docker being properly installed and running on your machine. Ensure that Docker is running. If your Docker client has issues connecting to the service, review the [Docker getting started guide](https://docs.docker.com/get-started/).
14 |
15 | ```
16 | docker ps
17 | ```
18 | ```
19 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
20 | ```
21 |
22 | Pull & start the Docker image:
23 | ```
24 | docker pull moov/fed:latest
25 | docker run -p 8086:8086 -p 9096:9096 moov/fed:latest
26 | ```
27 |
28 | #### **ACH routing number example**
29 |
30 | Fed can be used to look up Financial Institutions for Automated Clearing House ([ACH](https://en.wikipedia.org/wiki/Automated_Clearing_House)) transfers by their routing number (`?routingNumber=...`):
31 |
32 | ```
33 | curl "localhost:8086/fed/ach/search?routingNumber=273976369"
34 | ```
35 | ```
36 | {
37 | "achParticipants": [
38 | {
39 | "routingNumber": "273976369",
40 | "officeCode": "O",
41 | "servicingFRBNumber": "071000301",
42 | "recordTypeCode": "1",
43 | "revised": "041513",
44 | "newRoutingNumber": "000000000",
45 | "customerName": "VERIDIAN CREDIT UNION",
46 | "achLocation": {
47 | "address": "1827 ANSBOROUGH",
48 | "city": "WATERLOO",
49 | "state": "IA",
50 | "postalCode": "50702",
51 | "postalCodeExtension": "0000"
52 | },
53 | "phoneNumber": "3192878332",
54 | "statusCode": "1",
55 | "viewCode": "1"
56 | }
57 | ],
58 | "wireParticipants": null
59 | }
60 | ```
61 |
62 | #### **Wire routing number example**
63 |
64 | Fed can be used to look up Financial Institutions for [Fedwire](https://en.wikipedia.org/wiki/Fedwire) messages by their routing number (`?routingNumber=...`):
65 |
66 | ```
67 | curl "localhost:8086/fed/wire/search?routingNumber=273976369"
68 | ```
69 | ```
70 | {
71 | "achParticipants": null,
72 | "wireParticipants": [
73 | {
74 | "routingNumber": "273976369",
75 | "telegraphicName": "VERIDIAN",
76 | "customerName": "VERIDIAN CREDIT UNION",
77 | "wireLocation": {
78 | "city": "WATERLOO",
79 | "state": "IA"
80 | },
81 | "fundsTransferStatus": "Y",
82 | "fundsSettlementOnlyStatus": " ",
83 | "bookEntrySecuritiesTransferStatus": "N",
84 | "date": "20141107"
85 | }
86 | ]
87 | }
88 | ```
--------------------------------------------------------------------------------
/pkg/strcmp/strcmp_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 The Moov Authors
2 | // Use of this source code is governed by an Apache License
3 | // license that can be found in the LICENSE file.
4 |
5 | package strcmp
6 |
7 | import (
8 | "crypto/rand"
9 | "encoding/hex"
10 | "flag"
11 | "math"
12 | mrand "math/rand"
13 | "strings"
14 | "testing"
15 |
16 | "github.com/docker/docker/pkg/namesgenerator"
17 | )
18 |
19 | var (
20 | flagIterations = flag.Int("iterations", 1000, "How many iterations of each algorithm to test")
21 | )
22 |
23 | func randString() string {
24 | size := mrand.Uint32() % 1000 //nolint:gosec // max string size of 1k
25 | bs := make([]byte, size)
26 | n, err := rand.Read(bs)
27 | if err != nil || n == 0 {
28 | return ""
29 | }
30 | return strings.ToLower(hex.EncodeToString(bs))
31 | }
32 |
33 | func TestJaroWinkler(t *testing.T) {
34 | for i := 0; i < *flagIterations; i += 1 {
35 | a, b := randString(), randString()
36 | check(t, a, b, JaroWinkler(a, b))
37 | }
38 | }
39 |
40 | func TestLevenshtein(t *testing.T) {
41 | for i := 0; i < *flagIterations; i += 1 {
42 | a, b := randString(), randString()
43 | check(t, a, b, Levenshtein(a, b))
44 | }
45 | }
46 |
47 | func TestHamming(t *testing.T) {
48 | for i := 0; i < *flagIterations; i += 1 {
49 | a, b := randString(), randString()
50 | check(t, a, b, hamming(a, b))
51 | }
52 | }
53 |
54 | func TestSoundex(t *testing.T) {
55 | for i := 0; i < 500; i += 1 {
56 | parts := strings.Split(namesgenerator.GetRandomName(0), "_")
57 | if len(parts) < 2 {
58 | continue // invalid random name
59 | }
60 |
61 | a, b := parts[0], parts[1]
62 | score := Soundex(a, b)
63 | if score > 1.0 || score < 0.0 {
64 | t.Fatalf("a=%q b=%q got score %.2f", a, b, score)
65 | }
66 |
67 | score = Soundex(a, a)
68 | if !eql(score, 1.0) {
69 | t.Fatalf("a=%q b=%q got score: %.2f", a, a, score)
70 | }
71 | }
72 |
73 | type test struct {
74 | a, b string
75 | score float64
76 | }
77 |
78 | // Static tests
79 | cases := []test{
80 | {"Adam", "Bob", 0.25},
81 | {"Euler", "Ellery", 1.0},
82 | {"Lloyd", "Ladd", 1.0},
83 | }
84 | for i := range cases {
85 | score := Soundex(cases[i].a, cases[i].b)
86 | if !eql(score, cases[i].score) {
87 | t.Fatalf("a=%q b=%q got score: %.2f and expected: %.2f", cases[i].a, cases[i].b, score, cases[i].score)
88 | }
89 | }
90 | }
91 |
92 | func one(n float64) bool {
93 | return eql(n, 1.0)
94 | }
95 |
96 | func zero(n float64) bool {
97 | return eql(n, 0.0)
98 | }
99 |
100 | func eql(a, b float64) bool {
101 | return math.Abs(a-b) < 0.001
102 | }
103 |
104 | func check(t *testing.T, a, b string, score float64) {
105 | t.Helper()
106 |
107 | if one(score) && a != b {
108 | t.Fatalf("a=%q b=%q matched", a, b)
109 | }
110 | if zero(score) && a == b {
111 | t.Fatalf("a=%q b=%q didn't match", a, b)
112 | }
113 | if score > 1.0 || score < 0.0 {
114 | t.Fatalf("a=%q b=%q got score %.2f", a, b, score)
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/normalize_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The Moov Authors
2 | // Use of this source code is governed by an Apache License
3 | // license that can be found in the LICENSE file.
4 |
5 | package fed
6 |
7 | import (
8 | "testing"
9 |
10 | "github.com/stretchr/testify/require"
11 | )
12 |
13 | func TestNormalize(t *testing.T) {
14 | cases := []struct {
15 | input, expected string
16 | }{
17 | {input: "ALLY BANK", expected: "Ally Bank"},
18 | {input: "BANK OF AMERICA, N.A. - ARIZONA", expected: "Bank of America"},
19 | {input: "CITIBANK FSB", expected: "Citibank"},
20 | {input: "CITIBANK /FLORIDA/BR", expected: "Citibank"},
21 | {input: "CITIBANK-NEW YORK STATE", expected: "Citibank"},
22 | {input: "CITIBANK (SOUTH DAKOTA) NA", expected: "Citibank"},
23 | {input: "PNC BANK INC. - BALTIMORE", expected: "PNC Bank"},
24 | {input: "SUNTRUST BANK/ SKYLIGHT", expected: "SunTrust"},
25 | {input: "TD BANK NA (PC)", expected: "TD Bank"},
26 | {input: "WELLS FARGO BANK, N.A.", expected: "Wells Fargo"},
27 | }
28 | for i := range cases {
29 | output := Normalize(cases[i].input)
30 | require.Equal(t, cases[i].expected, output)
31 | }
32 | }
33 |
34 | func TestStripSymbols(t *testing.T) {
35 | cases := []struct {
36 | input, expected string
37 | }{
38 | {input: "ALLY BANK", expected: "ALLY BANK"},
39 | {input: "BANK OF AMERICA, N.A. - ARIZONA", expected: "BANK OF AMERICA NA ARIZONA"},
40 | {input: "CITIBANK FSB", expected: "CITIBANK FSB"},
41 | {input: "CITIBANK /FLORIDA/BR", expected: "CITIBANK FLORIDA BR"},
42 | {input: "CITIBANK-NEW YORK STATE", expected: "CITIBANK NEW YORK STATE"},
43 | {input: "CITIBANK (SOUTH DAKOTA) NA", expected: "CITIBANK SOUTH DAKOTA NA"},
44 | {input: "PNC BANK INC. - BALTIMORE", expected: "PNC BANK INC BALTIMORE"},
45 | {input: "SUNTRUST BANK/ SKYLIGHT", expected: "SUNTRUST BANK SKYLIGHT"},
46 | {input: "TD BANK NA (PC)", expected: "TD BANK NA PC"},
47 | {input: "WELLS FARGO BANK, N.A.", expected: "WELLS FARGO BANK NA"},
48 | }
49 | for i := range cases {
50 | output := StripSymbols(cases[i].input)
51 | require.Equal(t, cases[i].expected, output)
52 | }
53 | }
54 |
55 | func TestStripWaste(t *testing.T) {
56 | cases := []struct {
57 | input, expected string
58 | }{
59 | {input: "BANK OF AMERICA, N.A. - ARIZONA", expected: "BANK OF AMERICA, - ARIZONA"},
60 | {input: "WELLS FARGO BANK, N.A.", expected: "WELLS FARGO BANK, "},
61 | }
62 | for i := range cases {
63 | output := StripWaste(cases[i].input)
64 | require.Equal(t, cases[i].expected, output)
65 | }
66 | }
67 |
68 | func TestRemoveDuplicatedSpaces(t *testing.T) {
69 | cases := []struct {
70 | input, expected string
71 | }{
72 | {input: "ALLY BANK", expected: "ALLY BANK"},
73 | {input: "BANK OF AMERICA NA ARIZONA", expected: "BANK OF AMERICA NA ARIZONA"},
74 | {input: "CITIBANK FLORIDA BR", expected: "CITIBANK FLORIDA BR"},
75 | {input: "PNC BANK INC BALTIMORE", expected: "PNC BANK INC BALTIMORE"},
76 | {input: "PNC BANK INC BALTIMORE", expected: "PNC BANK INC BALTIMORE"},
77 | }
78 | for i := range cases {
79 | output := RemoveDuplicatedSpaces(cases[i].input)
80 | require.Equal(t, cases[i].expected, output)
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | ## Moov Fed
2 |
3 | **[Documentation](https://moov-io.github.io/fed)** | **[Source](https://github.com/moov-io/fed)** | **[Running](https://github.com/moov-io/fed#usage)** | **[Configuration](https://github.com/moov-io/fed#configuration-settings)**
4 |
5 | ### Purpose
6 |
7 | [Moov Fed](https://github.com/moov-io/fed) implements an HTTP interface to search [Fedwire](https://github.com/moov-io/fed/tree/master/docs/fpddir.md) and [FedACH](https://github.com/moov-io/fed/tree/master/docs/FedACHdir.md) data from the Federal Reserve Bank Services.
8 |
9 | The data and formats below represent a compilation of **Fedwire** and **FedACH** data from the [Federal Reserve Bank Services site](https://frbservices.org/):
10 |
11 | * [FEDACH](https://github.com/moov-io/fed/tree/master/docs/FedACHdir.md)
12 |
13 | * [FEDWire](https://github.com/moov-io/fed/tree/master/docs/fpddir.md)
14 |
15 | Fed can be used standalone to search for routing numbers by Financial Institution name, city, state, postal code, and routing number. It can also be used in conjunction with [ACH](https://github.com/moov-io/ach) and [WIRE](https://github.com/moov-io/wire) to validate routing numbers.
16 |
17 | ## FED Data Files
18 |
19 | The data files included in this repository ([`FedACHdir.md`](FedACHdir.md) and [`fpddir.md`](fpddir.md)) are **outdated** and from 2018. The Fed no longer releases this data publicly and licensing on more recent files prevents us from distributing them. However, the Fed still complies this data and you can retrieve up-to-date files for use in our project, either from [LexisNexis](https://risk.lexisnexis.com/financial-services/payments-efficiency/payment-routing) or your financial institution.
20 |
21 | Moov Fed can read the data files from anywhere on the filesystem. This allows you to mount the files and set `FEDACH_DATA_PATH` / `FEDWIRE_DATA_PATH` environmental variables. Both official formats from the Federal Reserve (plaintext and JSON) are supported.
22 |
23 | ### Copyright and Terms of Use
24 |
25 | Copyright © Federal Reserve Banks
26 |
27 | By accessing the [data](https://github.com/moov-io/fed/tree/master/data) in this repository you agree to the [Federal Reserve Banks' Terms of Use](https://frbservices.org/terms/index.html) and the [E-Payments Routing Directory Terms of Use Agreement](https://www.frbservices.org/EPaymentsDirectory/agreement.html).
28 |
29 | ## Disclaimer
30 |
31 | **THIS REPOSITORY IS NOT AFFILIATED WITH THE FEDERAL RESERVE BANKS AND IS NOT AN OFFICIAL SOURCE FOR FEDWIRE AND FEDACH DATA.**
32 |
33 | ## Getting Help
34 |
35 | channel | info
36 | ------- | -------
37 | [Project Documentation](https://moov-io.github.io/fed/) | Our project documentation available online.
38 | Twitter [@moov](https://twitter.com/moov) | You can follow Moov.io's Twitter feed to get updates on our project(s). You can also tweet us questions or just share blogs or stories.
39 | [GitHub Issue](https://github.com/moov-io/fed/issues) | If you are able to reproduce a problem please open a GitHub Issue under the specific project that caused the error.
40 | [moov-io slack](https://slack.moov.io/) | Join our slack channel (`#fed`) to have an interactive discussion about the development of the project.
41 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at wade@wadearnold.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/makefile:
--------------------------------------------------------------------------------
1 | PLATFORM=$(shell uname -s | tr '[:upper:]' '[:lower:]')
2 | VERSION := $(shell grep -Eo '(v[0-9]+[\.][0-9]+[\.][0-9]+(-[a-zA-Z0-9]*)?)' version.go)
3 |
4 | .PHONY: build docker release check
5 |
6 | build:
7 | # main FED binary
8 | CGO_ENABLED=0 go build -o ./bin/server github.com/moov-io/fed/cmd/server
9 | # fedtest binary
10 | CGO_ENABLED=0 go build -o bin/fedtest ./cmd/fedtest
11 |
12 | .PHONY: check
13 | check:
14 | ifeq ($(OS),Windows_NT)
15 | @echo "Skipping checks on Windows, currently unsupported."
16 | else
17 | @wget -O lint-project.sh https://raw.githubusercontent.com/moov-io/infra/master/go/lint-project.sh
18 | @chmod +x ./lint-project.sh
19 | COVER_THRESHOLD=40.0 ./lint-project.sh
20 | endif
21 |
22 | .PHONY: client
23 | client:
24 | ifeq ($(OS),Windows_NT)
25 | @echo "Please generate client on macOS or Linux, currently unsupported on windows."
26 | else
27 | # Versions from https://github.com/OpenAPITools/openapi-generator/releases
28 | @chmod +x ./openapi-generator
29 | @rm -rf ./client
30 | OPENAPI_GENERATOR_VERSION=4.3.1 ./openapi-generator generate --git-user-id=moov-io --git-repo-id=fed --package-name client -i ./api/client.yaml -g go -o ./client
31 | rm -f ./client/go.mod ./client/go.sum ./client/.travis.yml ./client/git_push.sh
32 | go fmt ./...
33 | go build github.com/moov-io/fed/client
34 | go test ./client/...
35 | endif
36 |
37 | .PHONY: clean
38 | clean:
39 | ifeq ($(OS),Windows_NT)
40 | @echo "Skipping cleanup on Windows, currently unsupported."
41 | else
42 | @rm -rf ./bin/ cover.out coverage.txt openapi-generator-cli-*.jar misspell* staticcheck* lint-project.sh
43 | endif
44 |
45 | dist: clean client build
46 | ifeq ($(OS),Windows_NT)
47 | CGO_ENABLED=1 GOOS=windows go build -o bin/fed.exe github.com/moov-io/fed/cmd/server
48 | else
49 | CGO_ENABLED=1 GOOS=$(PLATFORM) go build -o bin/fed-$(PLATFORM)-amd64 github.com/moov-io/fed/cmd/server
50 | endif
51 |
52 | docker: clean
53 | # main FED image
54 | docker build --pull -t moov/fed:$(VERSION) -f Dockerfile .
55 | docker tag moov/fed:$(VERSION) moov/fed:latest
56 | # OpenShift Docker image
57 | docker build --pull -t quay.io/moov/fed:$(VERSION) -f Dockerfile-openshift --build-arg VERSION=$(VERSION) .
58 | docker tag quay.io/moov/fed:$(VERSION) quay.io/moov/fed:latest
59 | # fedtest image
60 | docker build --pull -t moov/fedtest:$(VERSION) -f Dockerfile-fedtest ./
61 | docker tag moov/fedtest:$(VERSION) moov/fedtest:latest
62 |
63 | clean-integration:
64 | docker compose kill
65 | docker compose rm -v -f
66 |
67 | test-integration: clean-integration
68 | docker compose up -d
69 | sleep 5
70 | ./bin/fedtest -local
71 |
72 | release: docker AUTHORS
73 | go vet ./...
74 | go test -coverprofile=cover-$(VERSION).out ./...
75 | git tag -f $(VERSION)
76 |
77 | release-push:
78 | docker push moov/fed:$(VERSION)
79 | docker push moov/fed:latest
80 |
81 | quay-push:
82 | docker push quay.io/moov/fed:$(VERSION)
83 | docker push quay.io/moov/fed:latest
84 |
85 | .PHONY: cover-test cover-web
86 | cover-test:
87 | go test -coverprofile=cover.out ./...
88 | cover-web:
89 | go tool cover -html=cover.out
90 |
91 | # From https://github.com/genuinetools/img
92 | .PHONY: AUTHORS
93 | AUTHORS:
94 | @$(file >$@,# This file lists all individuals having contributed content to the repository.)
95 | @$(file >>$@,# For how it is generated, see `make AUTHORS`.)
96 | @echo "$(shell git log --format='\n%aN <%aE>' | LC_ALL=C.UTF-8 sort -uf)" >> $@
97 |
--------------------------------------------------------------------------------
/cmd/server/reader_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 The Moov Authors
2 | // Use of this source code is governed by an Apache License
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "os"
9 | "path/filepath"
10 | "testing"
11 |
12 | "github.com/moov-io/base/log"
13 |
14 | "github.com/stretchr/testify/require"
15 | )
16 |
17 | func TestReader__fedACHDataFile(t *testing.T) {
18 | t.Setenv("FRB_ROUTING_NUMBER", "")
19 | t.Setenv("FRB_DOWNLOAD_CODE", "")
20 |
21 | r, err := fedACHDataFile(log.NewTestLogger())
22 | require.Nil(t, r)
23 | require.ErrorContains(t, err, "no such file or directory")
24 | }
25 |
26 | func TestReader__fedWireDataFile(t *testing.T) {
27 | t.Setenv("FRB_ROUTING_NUMBER", "")
28 | t.Setenv("FRB_DOWNLOAD_CODE", "")
29 |
30 | r, err := fedWireDataFile(log.NewTestLogger())
31 | require.Nil(t, r)
32 | require.ErrorContains(t, err, "no such file or directory")
33 | }
34 |
35 | func TestReader_inspectInitialDataDirectory(t *testing.T) {
36 | logger := log.NewNopLogger()
37 |
38 | dir := t.TempDir()
39 |
40 | err := os.WriteFile(filepath.Join(dir, "fedach.txt"), nil, 0600)
41 | require.NoError(t, err)
42 | err = os.WriteFile(filepath.Join(dir, "fedwire.txt"), nil, 0600)
43 | require.NoError(t, err)
44 |
45 | // FedACH files
46 | fd, err := inspectInitialDataDirectory(logger, dir, fedachFilenames)
47 | require.NoError(t, err)
48 |
49 | file, ok := fd.(*os.File)
50 | require.True(t, ok)
51 | require.Equal(t, filepath.Join(dir, "fedach.txt"), file.Name())
52 |
53 | // FedWire files
54 | fd, err = inspectInitialDataDirectory(logger, dir, fedwireFilenames)
55 | require.NoError(t, err)
56 |
57 | file, ok = fd.(*os.File)
58 | require.True(t, ok)
59 | require.Equal(t, filepath.Join(dir, "fedwire.txt"), file.Name())
60 | }
61 |
62 | func TestReader__readFEDACHData(t *testing.T) {
63 | s := &searcher{logger: log.NewNopLogger()}
64 |
65 | achFile, err := os.Open(filepath.Join("..", "..", "data", "FedACHdir.txt"))
66 | if err != nil {
67 | t.Fatal(err)
68 | }
69 | if err := s.readFEDACHData(achFile); err != nil {
70 | t.Fatal(err)
71 | }
72 | if len(s.ACHDictionary.ACHParticipants) == 0 {
73 | t.Error("no ACH entries parsed")
74 | }
75 |
76 | // bad path
77 | achFile, err = os.Open("reader_test.go") // invalid fedach file
78 | if err != nil {
79 | t.Fatal(err)
80 | }
81 | defer achFile.Close()
82 | if err := s.readFEDACHData(achFile); err == nil {
83 | t.Error("expected error")
84 | }
85 | }
86 |
87 | func TestReader__readFEDWIREData(t *testing.T) {
88 | s := &searcher{logger: log.NewNopLogger()}
89 |
90 | wireFile, err := os.Open(filepath.Join("..", "..", "data", "fpddir.txt"))
91 | if err != nil {
92 | t.Fatal(err)
93 | }
94 | if err := s.readFEDWIREData(wireFile); err != nil {
95 | t.Fatal(err)
96 | }
97 | if len(s.WIREDictionary.WIREParticipants) == 0 {
98 | t.Error("no Wire entries parsed")
99 | }
100 |
101 | // bad path
102 | wireFile, err = os.Open("reader_test.go")
103 | if err != nil {
104 | t.Fatal(err)
105 | }
106 | defer wireFile.Close()
107 | if err := s.readFEDWIREData(wireFile); err == nil {
108 | t.Error("expected error")
109 | }
110 | }
111 |
112 | func TestReader__readDataFilepath(t *testing.T) {
113 | if v := readDataFilepath("MISSING", "value"); v != "value" {
114 | t.Errorf("got %q", v)
115 | }
116 | if v := readDataFilepath("PATH", "value"); v == "" || v == "value" {
117 | t.Errorf("got %q", v)
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/pkg/download/download_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 The Moov Authors
2 | // Use of this source code is governed by an Apache License
3 | // license that can be found in the LICENSE file.
4 |
5 | package download
6 |
7 | import (
8 | "bytes"
9 | "fmt"
10 | "io"
11 | "net/http"
12 | "net/http/httptest"
13 | "os"
14 | "path/filepath"
15 | "testing"
16 | )
17 |
18 | func TestClient__fedach(t *testing.T) {
19 | client := setupClient(t)
20 |
21 | fedach, err := client.GetList("fedach")
22 | if err != nil {
23 | t.Fatal(err)
24 | }
25 |
26 | bs, _ := io.ReadAll(io.LimitReader(fedach, 10024))
27 | if !bytes.Contains(bs, []byte("fedACHParticipants")) {
28 | t.Errorf("unexpected output:\n%s", string(bs))
29 | }
30 | }
31 |
32 | func TestClient__fedach_custom_url(t *testing.T) {
33 | file, err := os.ReadFile(filepath.Join("..", "..", "data", "fedachdir.json"))
34 | if err != nil {
35 | t.Fatal(err)
36 | }
37 |
38 | mockHTTPServer := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
39 | fmt.Fprint(writer, string(file))
40 | }))
41 | defer mockHTTPServer.Close()
42 |
43 | t.Setenv("FRB_DOWNLOAD_URL_TEMPLATE", mockHTTPServer.URL+"/%s")
44 | t.Setenv("FRB_ROUTING_NUMBER", "123456789")
45 | t.Setenv("FRB_DOWNLOAD_CODE", "a1b2c3d4-123b-9876-1234-z1x2y3a1b2c3")
46 |
47 | client := setupClient(t)
48 |
49 | fedach, err := client.GetList("fedach")
50 | if err != nil {
51 | t.Fatal(err)
52 | }
53 |
54 | bs, _ := io.ReadAll(io.LimitReader(fedach, 10024))
55 | if !bytes.Equal(bs, file) {
56 | t.Errorf("unexpected output:\n%s", string(bs))
57 | }
58 | }
59 |
60 | func TestClient__fedwire(t *testing.T) {
61 | client := setupClient(t)
62 |
63 | fedwire, err := client.GetList("fedwire")
64 | if err != nil {
65 | t.Fatal(err)
66 | }
67 |
68 | bs, _ := io.ReadAll(io.LimitReader(fedwire, 10024))
69 | if !bytes.Contains(bs, []byte("fedwireParticipants")) {
70 | t.Errorf("unexpected output:\n%s", string(bs))
71 | }
72 | }
73 |
74 | func TestClient__wire_custom_url(t *testing.T) {
75 | file, err := os.ReadFile(filepath.Join("..", "..", "data", "fpddir.json"))
76 | if err != nil {
77 | t.Fatal(err)
78 | }
79 | mockHTTPServer := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
80 | if request.URL.Path != "/fedwire" {
81 | writer.WriteHeader(http.StatusNotFound)
82 | }
83 | fmt.Fprint(writer, string(file))
84 | }))
85 | defer mockHTTPServer.Close()
86 |
87 | t.Setenv("FRB_DOWNLOAD_URL_TEMPLATE", mockHTTPServer.URL+"/%s")
88 | t.Setenv("FRB_ROUTING_NUMBER", "123456789")
89 | t.Setenv("FRB_DOWNLOAD_CODE", "a1b2c3d4-123b-9876-1234-z1x2y3a1b2c3")
90 |
91 | client := setupClient(t)
92 |
93 | fedwire, err := client.GetList("fedwire")
94 | if err != nil {
95 | t.Fatal(err)
96 | }
97 |
98 | bs, _ := io.ReadAll(io.LimitReader(fedwire, 10024))
99 | if !bytes.Equal(bs, file) {
100 | t.Errorf("unexpected output:\n%s", string(bs))
101 | }
102 | }
103 |
104 | func setupClient(t *testing.T) *Client {
105 | t.Helper()
106 |
107 | routingNumber := os.Getenv("FRB_ROUTING_NUMBER")
108 | downloadCode := os.Getenv("FRB_DOWNLOAD_CODE")
109 | downloadURL := os.Getenv("FRB_DOWNLOAD_URL_TEMPLATE")
110 | if routingNumber == "" || downloadCode == "" {
111 | t.Skip("missing FRB routing number or download code")
112 | }
113 |
114 | client, err := NewClient(&ClientOpts{
115 | RoutingNumber: routingNumber,
116 | DownloadCode: downloadCode,
117 | DownloadURL: downloadURL,
118 | })
119 | if err != nil {
120 | t.Fatal(err)
121 | }
122 | return client
123 | }
124 |
--------------------------------------------------------------------------------
/pkg/download/download.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 The Moov Authors
2 | // Use of this source code is governed by an Apache License
3 | // license that can be found in the LICENSE file.
4 |
5 | package download
6 |
7 | import (
8 | "bytes"
9 | "errors"
10 | "fmt"
11 | "io"
12 | "net/http"
13 | "net/url"
14 | "os"
15 | "time"
16 | )
17 |
18 | const DefaultFRBDownloadURLTemplate = "https://frbservices.org/EPaymentsDirectory/directories/%s?format=json"
19 |
20 | var (
21 | ErrMissingConfigValue = errors.New("missing config value")
22 | ErrMissingRoutingNumber = errors.New("missing routing number")
23 | ErrMissingDownloadCD = errors.New("missing download code")
24 | )
25 |
26 | type Client struct {
27 | httpClient *http.Client
28 |
29 | routingNumber string // X_FRB_EPAYMENTS_DIRECTORY_ORG_ID header
30 | downloadCode string // X_FRB_EPAYMENTS_DIRECTORY_DOWNLOAD_CD
31 | downloadURL string // defaults to "https://frbservices.org/EPaymentsDirectory/directories/%s?format=json" where %s is the list name
32 |
33 | }
34 |
35 | type ClientOpts struct {
36 | HTTPClient *http.Client
37 | RoutingNumber, DownloadCode, DownloadURL string
38 | }
39 |
40 | func NewClient(opts *ClientOpts) (*Client, error) {
41 | if opts == nil {
42 | opts = &ClientOpts{}
43 | }
44 | if opts.HTTPClient == nil {
45 | opts.HTTPClient = &http.Client{
46 | // These files can be fairly large to buffer to us and the FRB
47 | // services can be slow, so we default to a hefty timeout.
48 | Timeout: 90 * time.Second,
49 | }
50 | }
51 |
52 | routingNum, rnExists := os.LookupEnv("FRB_ROUTING_NUMBER")
53 | downloadcd, dcdExists := os.LookupEnv("FRB_DOWNLOAD_CODE")
54 | downloadurltemp, urlExists := os.LookupEnv("FRB_DOWNLOAD_URL_TEMPLATE")
55 |
56 | if !urlExists || downloadurltemp == "" {
57 | if !rnExists || routingNum == "" {
58 | return nil, fmt.Errorf("%w: %w", ErrMissingConfigValue, ErrMissingRoutingNumber)
59 | }
60 |
61 | if !dcdExists || downloadcd == "" {
62 | return nil, fmt.Errorf("%w: %w", ErrMissingConfigValue, ErrMissingDownloadCD)
63 | }
64 |
65 | downloadurltemp = DefaultFRBDownloadURLTemplate
66 | }
67 |
68 | return &Client{
69 | httpClient: opts.HTTPClient,
70 | routingNumber: routingNum,
71 | downloadCode: downloadcd,
72 | downloadURL: downloadurltemp,
73 | }, nil
74 | }
75 |
76 | // GetList downloads an FRB list and saves it into an io.Reader.
77 | // Example listName values: fedach, fedwire
78 | func (c *Client) GetList(listName string) (io.Reader, error) {
79 | where, err := url.Parse(fmt.Sprintf(c.downloadURL, listName))
80 | if err != nil {
81 | return nil, fmt.Errorf("url: %v", err)
82 | }
83 |
84 | req, err := http.NewRequest("GET", where.String(), nil)
85 | if err != nil {
86 | return nil, fmt.Errorf("building %s url: %v", listName, err)
87 | }
88 |
89 | if c.downloadCode != "" && c.routingNumber != "" {
90 | req.Header.Set("X_FRB_EPAYMENTS_DIRECTORY_ORG_ID", c.routingNumber)
91 | req.Header.Set("X_FRB_EPAYMENTS_DIRECTORY_DOWNLOAD_CD", c.downloadCode)
92 | }
93 |
94 | // perform our request
95 | resp, err := c.httpClient.Do(req)
96 | if err != nil {
97 | return nil, fmt.Errorf("http get: %v", err)
98 | }
99 | if resp != nil && resp.Body != nil {
100 | defer resp.Body.Close()
101 | }
102 |
103 | // Quit if we fail to download
104 | if resp.StatusCode >= 299 {
105 | return nil, fmt.Errorf("unexpected http status: %d", resp.StatusCode)
106 | }
107 |
108 | var out bytes.Buffer
109 | if n, err := io.Copy(&out, resp.Body); n == 0 || err != nil {
110 | return nil, fmt.Errorf("copying n=%d: %v", n, err)
111 | }
112 | if out.Len() > 0 {
113 | return &out, nil
114 | }
115 | return nil, nil
116 | }
117 |
--------------------------------------------------------------------------------
/data/fpddir.json:
--------------------------------------------------------------------------------
1 | {
2 | "fedwireParticipants" : {
3 | "response" : {
4 | "code" : 100
5 | },
6 | "fedwireParticipants" : [
7 | {
8 | "routingNumber" : "011000015",
9 | "telegraphicName" : "FRB-BOS",
10 | "customerName" : "FEDERAL RESERVE BANK OF BOSTON",
11 | "customerState" : "MA",
12 | "customerCity" : "BOSTON",
13 | "fundsEligibility" : "Y",
14 | "fundsSettlementOnlyStatus" : " ",
15 | "securitiesEligibility" : "Y",
16 | "changeDate" : "20040910"
17 | },
18 | {
19 | "routingNumber": "325280039",
20 | "telegraphicName" : "MAC FCU",
21 | "customerName" : "MAC FEDERAL CREDIT UNION",
22 | "customerState" : "AK",
23 | "customerCity" : "FAIRBANKS",
24 | "fundsEligibility" : "Y",
25 | "fundsSettlementOnlyStatus" : " ",
26 | "securitiesEligibility" : "Y",
27 | "changeDate" : "20180629"
28 | },
29 | {
30 | "routingNumber": "324172465",
31 | "telegraphicName" : "ALBERT EFCU BOIS",
32 | "customerName" : "TRUGROCER FEDERAL CREDIT UNION",
33 | "customerState" : "ID",
34 | "customerCity" : "BOISE",
35 | "fundsEligibility" : "Y",
36 | "fundsSettlementOnlyStatus" : " ",
37 | "securitiesEligibility" : "N",
38 | "changeDate" : "20061002"
39 | },
40 | {
41 | "routingNumber": "021000018",
42 | "telegraphicName" : "BK OF NYC",
43 | "customerName" : "THE BANK OF NEW YORK MELLON",
44 | "customerState" : "NY",
45 | "customerCity" : "NEW YORK",
46 | "fundsEligibility" : "Y",
47 | "fundsSettlementOnlyStatus" : " ",
48 | "securitiesEligibility" : "Y",
49 | "changeDate" : "20080701"
50 | },
51 | {
52 | "routingNumber": "031207924",
53 | "telegraphicName" : "FIRST BANK",
54 | "customerName" : "FIRST BANK",
55 | "customerState" : "NJ",
56 | "customerCity" : "HAMILTON",
57 | "fundsEligibility" : "Y",
58 | "fundsSettlementOnlyStatus" : " ",
59 | "securitiesEligibility" : "N",
60 | "changeDate" : "20180307"
61 | },
62 | {
63 | "routingNumber": "021054941",
64 | "telegraphicName" : "FARMER MAC SEC P&I",
65 | "customerName" : "FARMER MAC SEC P&I ACCOUNT",
66 | "customerState" : "NJ",
67 | "customerCity" : "EAST RUTHERFORD",
68 | "fundsEligibility" : "Y",
69 | "fundsSettlementOnlyStatus" : " ",
70 | "securitiesEligibility" : "Y",
71 | "changeDate" : "20160331"
72 | },
73 | {
74 | "routingNumber": "113109898",
75 | "telegraphicName" : "CARMINE STATE BK",
76 | "customerName" : "CARMINE STATE BANK",
77 | "customerState" : "TX",
78 | "customerCity" : "CARMINE",
79 | "fundsEligibility" : "Y",
80 | "fundsSettlementOnlyStatus" : " ",
81 | "securitiesEligibility" : "N",
82 | "changeDate" : " "
83 | },
84 | {
85 | "routingNumber": "053103640",
86 | "telegraphicName" : "FMB GRANITE QUARRY",
87 | "customerName" : "FARMERS & MERCHANTS BANK",
88 | "customerState" : "NC",
89 | "customerCity" : "SALISBURY",
90 | "fundsEligibility" : "Y",
91 | "fundsSettlementOnlyStatus" : " ",
92 | "securitiesEligibility" : "Y",
93 | "changeDate" : "20070504"
94 | },
95 | {
96 | "routingNumber": "011075150",
97 | "telegraphicName" : "SANTANDER BK",
98 | "customerName" : "SANTANDER BANK, N.A.",
99 | "customerState" : "PA",
100 | "customerCity" : "WYOMISSING",
101 | "fundsEligibility" : "Y",
102 | "fundsSettlementOnlyStatus" : " ",
103 | "securitiesEligibility" : "Y",
104 | "changeDate" : "20131029"
105 | },
106 | {
107 | "routingNumber": "211372404",
108 | "telegraphicName" : "READING COOP BANK",
109 | "customerName" : "READING CO-OPERATIVE BANK",
110 | "customerState" : "MA",
111 | "customerCity" : "READING",
112 | "fundsEligibility" : "Y",
113 | "fundsSettlementOnlyStatus" : " ",
114 | "securitiesEligibility" : "Y",
115 | "changeDate" : "19991230"
116 | }
117 | ]
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/data/fedachdir.json:
--------------------------------------------------------------------------------
1 | {
2 | "fedACHParticipants" : {
3 | "response" : {
4 | "code" : 100
5 | },
6 | "fedACHParticipants" : [
7 | {
8 | "routingNumber" : "011000015",
9 | "officeCode" : "O",
10 | "servicingFRBNumber" : "011000015",
11 | "recordTypeCode" : "0",
12 | "changeDate" : "122415",
13 | "newRoutingNumber" : "000000000",
14 | "customerName" : "FEDERAL RESERVE BANK",
15 | "customerAddress" : "1000 PEACHTREE ST N.E.",
16 | "customerCity" : "ATLANTA",
17 | "customerState" : "GA",
18 | "customerZip" : "30309",
19 | "customerZipExt" : "4470",
20 | "customerAreaCode" : "877",
21 | "customerPhonePrefix" : "372",
22 | "customerPhoneSuffix" : "2457",
23 | "institutionStatusCode" : "1",
24 | "dataViewCode" : "1"
25 | },
26 | {
27 | "routingNumber": "073905527",
28 | "officeCode": "O",
29 | "servicingFRBNumber": "071000301",
30 | "recordTypeCode": "1",
31 | "changeDate": "012908",
32 | "newRoutingNumber": "000000000",
33 | "customerName": "LINCOLN SAVINGS BANK",
34 | "customerAddress": "P O BOX E",
35 | "customerCity": "REINBECK",
36 | "customerState": "IA",
37 | "customerZip": "50669",
38 | "customerZipExt": "0159",
39 | "customerAreaCode" : "319",
40 | "customerPhonePrefix" : "788",
41 | "customerPhoneSuffix" : "6441",
42 | "institutionStatusCode": "1",
43 | "dataViewCode": "1"
44 | },
45 | {
46 | "routingNumber": "325183657",
47 | "officeCode": "O",
48 | "servicingFRBNumber": "121000374",
49 | "recordTypeCode": "2",
50 | "changeDate": "110118",
51 | "newRoutingNumber": "325182836",
52 | "customerName": "LOWER VALLEY CU",
53 | "customerAddress": "PO BOX 479",
54 | "customerCity": "SUNNYSIDE",
55 | "customerState": "WA",
56 | "customerZip": "98944",
57 | "customerZipExt": "0000",
58 | "customerAreaCode": "509",
59 | "customerPhonePrefix": "837",
60 | "customerPhoneSuffix": "5295",
61 | "institutionStatusCode": "1",
62 | "dataViewCode": "1"
63 | },
64 | {
65 | "routingNumber": "011000206",
66 | "officeCode": "O",
67 | "servicingFRBNumber": "011000015",
68 | "recordTypeCode": "1",
69 | "changeDate": "072505",
70 | "newRoutingNumber": "000000000",
71 | "customerName": "BANK OF AMERICA N.A",
72 | "customerAddress": "PO BOX 27025",
73 | "customerCity": "RICHMOND",
74 | "customerState": "VA",
75 | "customerZip": "23261",
76 | "customerZipExt": "7025",
77 | "customerAreaCode": "800",
78 | "customerPhonePrefix": "446",
79 | "customerPhoneSuffix": "0135",
80 | "statusCode": "1",
81 | "dataViewCode": "1"
82 | },
83 | {
84 | "routingNumber": "031207924",
85 | "officeCode": "O",
86 | "servicingFRBNumber": "031000040",
87 | "recordTypeCode": "1",
88 | "changeDate": "112614",
89 | "newRoutingNumber": "000000000",
90 | "customerName": "FIRST BANK",
91 | "customerAddress": "2465 RUSER RD",
92 | "customerCity": "HAMILTON",
93 | "customerState": "NJ",
94 | "customerZip": "08690",
95 | "customerZipExt": "0000",
96 | "customerAreaCode": "609",
97 | "customerPhonePrefix": "643",
98 | "customerPhoneSuffix": "0061",
99 | "statusCode": "1",
100 | "dataViewCode": "1"
101 | },
102 | {
103 | "routingNumber": "301271787",
104 | "officeCode": "O",
105 | "servicingFRBNumber": "101000048",
106 | "recordTypeCode": "1",
107 | "changeDate": "012114",
108 | "newRoutingNumber": "000000000",
109 | "customerName": "FARMERS STATE BANK",
110 | "customerAddress": "PO BOX 567",
111 | "customerCity": "CAMERON",
112 | "customerState": "MO",
113 | "customerZip": "64429",
114 | "customerZipExt": "0000",
115 | "customerAreaCode": "816",
116 | "customerPhonePrefix": "632",
117 | "customerPhoneSuffix": "6641",
118 | "statusCode": "1",
119 | "dataViewCode": "1"
120 | }
121 | ]
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/client/configuration.go:
--------------------------------------------------------------------------------
1 | /*
2 | * FED API
3 | *
4 | * FED API is designed to create FEDACH and FEDWIRE dictionaries. The FEDACH dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive ACH entries. The FEDWIRE dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive WIRE entries. This project implements a modern REST HTTP API for FEDACH Dictionary and FEDWIRE Dictionary.
5 | *
6 | * API version: v1
7 | * Generated by: OpenAPI Generator (https://openapi-generator.tech)
8 | */
9 |
10 | package client
11 |
12 | import (
13 | "fmt"
14 | "net/http"
15 | "strings"
16 | )
17 |
18 | // contextKeys are used to identify the type of value in the context.
19 | // Since these are string, it is possible to get a short description of the
20 | // context key for logging and debugging using key.String().
21 |
22 | type contextKey string
23 |
24 | func (c contextKey) String() string {
25 | return "auth " + string(c)
26 | }
27 |
28 | var (
29 | // ContextOAuth2 takes an oauth2.TokenSource as authentication for the request.
30 | ContextOAuth2 = contextKey("token")
31 |
32 | // ContextBasicAuth takes BasicAuth as authentication for the request.
33 | ContextBasicAuth = contextKey("basic")
34 |
35 | // ContextAccessToken takes a string oauth2 access token as authentication for the request.
36 | ContextAccessToken = contextKey("accesstoken")
37 |
38 | // ContextAPIKey takes an APIKey as authentication for the request
39 | ContextAPIKey = contextKey("apikey")
40 | )
41 |
42 | // BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth
43 | type BasicAuth struct {
44 | UserName string `json:"userName,omitempty"`
45 | Password string `json:"password,omitempty"`
46 | }
47 |
48 | // APIKey provides API key based authentication to a request passed via context using ContextAPIKey
49 | type APIKey struct {
50 | Key string
51 | Prefix string
52 | }
53 |
54 | // ServerVariable stores the information about a server variable
55 | type ServerVariable struct {
56 | Description string
57 | DefaultValue string
58 | EnumValues []string
59 | }
60 |
61 | // ServerConfiguration stores the information about a server
62 | type ServerConfiguration struct {
63 | Url string
64 | Description string
65 | Variables map[string]ServerVariable
66 | }
67 |
68 | // Configuration stores the configuration of the API client
69 | type Configuration struct {
70 | BasePath string `json:"basePath,omitempty"`
71 | Host string `json:"host,omitempty"`
72 | Scheme string `json:"scheme,omitempty"`
73 | DefaultHeader map[string]string `json:"defaultHeader,omitempty"`
74 | UserAgent string `json:"userAgent,omitempty"`
75 | Debug bool `json:"debug,omitempty"`
76 | Servers []ServerConfiguration
77 | HTTPClient *http.Client
78 | }
79 |
80 | // NewConfiguration returns a new Configuration object
81 | func NewConfiguration() *Configuration {
82 | cfg := &Configuration{
83 | BasePath: "http://localhost:8086",
84 | DefaultHeader: make(map[string]string),
85 | UserAgent: "OpenAPI-Generator/1.0.0/go",
86 | Debug: false,
87 | Servers: []ServerConfiguration{
88 | {
89 | Url: "http://localhost:8086",
90 | Description: "Local development",
91 | },
92 | },
93 | }
94 | return cfg
95 | }
96 |
97 | // AddDefaultHeader adds a new HTTP header to the default header in the request
98 | func (c *Configuration) AddDefaultHeader(key string, value string) {
99 | c.DefaultHeader[key] = value
100 | }
101 |
102 | // ServerUrl returns URL based on server settings
103 | func (c *Configuration) ServerUrl(index int, variables map[string]string) (string, error) {
104 | if index < 0 || len(c.Servers) <= index {
105 | return "", fmt.Errorf("Index %v out of range %v", index, len(c.Servers)-1)
106 | }
107 | server := c.Servers[index]
108 | url := server.Url
109 |
110 | // go through variables and replace placeholders
111 | for name, variable := range server.Variables {
112 | if value, ok := variables[name]; ok {
113 | found := bool(len(variable.EnumValues) == 0)
114 | for _, enumValue := range variable.EnumValues {
115 | if value == enumValue {
116 | found = true
117 | }
118 | }
119 | if !found {
120 | return "", fmt.Errorf("The variable %s in the server URL has invalid value %v. Must be %v", name, value, variable.EnumValues)
121 | }
122 | url = strings.Replace(url, "{"+name+"}", value, -1)
123 | } else {
124 | url = strings.Replace(url, "{"+name+"}", variable.DefaultValue, -1)
125 | }
126 | }
127 | return url, nil
128 | }
129 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Create Release
2 |
3 | on:
4 | push:
5 | tags: [ "v*.*.*" ]
6 |
7 | permissions:
8 | contents: write
9 |
10 | jobs:
11 | testing:
12 | name: Testing
13 | runs-on: ${{ matrix.os }}
14 | strategy:
15 | matrix:
16 | os: [ubuntu-latest, macos-latest, windows-latest]
17 | steps:
18 | - name: Set up Go 1.x
19 | uses: actions/setup-go@v5
20 | with:
21 | go-version: stable
22 | id: go
23 |
24 | - name: Check out code
25 | uses: actions/checkout@v4
26 |
27 | - name: Check
28 | run: make check
29 |
30 | create_release:
31 | name: Create Release
32 | needs: [testing]
33 | runs-on: ubuntu-latest
34 | steps:
35 | - name: Create Release
36 | id: create_release
37 | uses: actions/create-release@v1
38 | env:
39 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
40 | with:
41 | tag_name: ${{ github.ref }}
42 | release_name: Release ${{ github.ref }}
43 | prerelease: true
44 |
45 | - name: Output Release URL File
46 | run: echo "${{ steps.create_release.outputs.upload_url }}" > release_url.txt
47 |
48 | - name: Save Release URL File for publish
49 | uses: actions/upload-artifact@v6
50 | with:
51 | name: release_url
52 | path: release_url.txt
53 | retention-days: 1
54 |
55 | publish:
56 | name: Publish
57 | needs: [testing, create_release]
58 | runs-on: ${{ matrix.os }}
59 | strategy:
60 | matrix:
61 | os: [ubuntu-latest, macos-latest, windows-latest]
62 | steps:
63 | - name: Set up Go 1.x
64 | uses: actions/setup-go@v5
65 | with:
66 | go-version: stable
67 | id: go
68 |
69 | - name: Check out code
70 | uses: actions/checkout@v4
71 |
72 | - name: Load Release URL File from release job
73 | uses: actions/download-artifact@v7
74 | with:
75 | name: release_url
76 | path: release_url
77 |
78 | - name: Distribute
79 | run: make dist
80 |
81 | - name: Get Release File Name & Upload URL
82 | id: get_release_info
83 | shell: bash
84 | run: |
85 | value=`cat release_url/release_url.txt`
86 | echo ::set-output name=upload_url::$value
87 | env:
88 | TAG_REF_NAME: ${{ github.ref }}
89 | REPOSITORY_NAME: ${{ github.repository }}
90 |
91 | - name: Upload Linux Server Binary
92 | if: runner.os == 'Linux'
93 | uses: actions/upload-release-asset@v1
94 | env:
95 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
96 | with:
97 | upload_url: ${{ steps.get_release_info.outputs.upload_url }}
98 | asset_path: ./bin/fed-linux-amd64
99 | asset_name: fed-linux-amd64
100 | asset_content_type: application/octet-stream
101 |
102 | - name: Upload macOS Server Binary
103 | if: runner.os == 'macOS'
104 | uses: actions/upload-release-asset@v1
105 | env:
106 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
107 | with:
108 | upload_url: ${{ steps.get_release_info.outputs.upload_url }}
109 | asset_path: ./bin/fed-darwin-amd64
110 | asset_name: fed-darwin-amd64
111 | asset_content_type: application/octet-stream
112 |
113 | - name: Upload Windows Server Binary
114 | if: runner.os == 'Windows'
115 | uses: actions/upload-release-asset@v1
116 | env:
117 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
118 | with:
119 | upload_url: ${{ steps.get_release_info.outputs.upload_url }}
120 | asset_path: ./bin/fed.exe
121 | asset_name: fed.exe
122 | asset_content_type: application/octet-stream
123 |
124 | docker:
125 | name: Docker
126 | needs: [testing, create_release]
127 | runs-on: ubuntu-latest
128 | steps:
129 | - name: Set up Go 1.x
130 | uses: actions/setup-go@v5
131 | with:
132 | go-version: stable
133 | id: go
134 |
135 | - name: Check out code
136 | uses: actions/checkout@v4
137 |
138 | - name: Docker
139 | run: make docker
140 |
141 | - name: Docker Push
142 | run: |+
143 | echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
144 | make release-push
145 | env:
146 | DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
147 | DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
148 |
149 | - name: Quay.io Push
150 | run: |+
151 | echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin quay.io
152 | make quay-push
153 | env:
154 | DOCKER_USERNAME: ${{ secrets.QUAY_USERNAME }}
155 | DOCKER_PASSWORD: ${{ secrets.QUAY_PASSWORD }}
156 |
--------------------------------------------------------------------------------
/client/docs/FEDApi.md:
--------------------------------------------------------------------------------
1 | # \FEDApi
2 |
3 | All URIs are relative to *http://localhost:8086*
4 |
5 | Method | HTTP request | Description
6 | ------------- | ------------- | -------------
7 | [**Ping**](FEDApi.md#Ping) | **Get** /ping | Ping the FED service to check if running
8 | [**SearchFEDACH**](FEDApi.md#SearchFEDACH) | **Get** /fed/ach/search | Search FEDACH names and metadata
9 | [**SearchFEDWIRE**](FEDApi.md#SearchFEDWIRE) | **Get** /fed/wire/search | Search FEDWIRE names and metadata
10 |
11 |
12 |
13 | ## Ping
14 |
15 | > Ping(ctx, )
16 |
17 | Ping the FED service to check if running
18 |
19 | ### Required Parameters
20 |
21 | This endpoint does not need any parameter.
22 |
23 | ### Return type
24 |
25 | (empty response body)
26 |
27 | ### Authorization
28 |
29 | No authorization required
30 |
31 | ### HTTP request headers
32 |
33 | - **Content-Type**: Not defined
34 | - **Accept**: text/plain
35 |
36 | [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints)
37 | [[Back to Model list]](../README.md#documentation-for-models)
38 | [[Back to README]](../README.md)
39 |
40 |
41 | ## SearchFEDACH
42 |
43 | > AchDictionary SearchFEDACH(ctx, optional)
44 |
45 | Search FEDACH names and metadata
46 |
47 | ### Required Parameters
48 |
49 |
50 | Name | Type | Description | Notes
51 | ------------- | ------------- | ------------- | -------------
52 | **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc.
53 | **optional** | ***SearchFEDACHOpts** | optional parameters | nil if no parameters
54 |
55 | ### Optional Parameters
56 |
57 | Optional parameters are passed through a pointer to a SearchFEDACHOpts struct
58 |
59 |
60 | Name | Type | Description | Notes
61 | ------------- | ------------- | ------------- | -------------
62 | **xRequestID** | **optional.String**| Optional Request ID allows application developer to trace requests through the systems logs |
63 | **xUserID** | **optional.String**| Optional User ID used to perform this search |
64 | **name** | **optional.String**| FEDACH Financial Institution Name |
65 | **routingNumber** | **optional.String**| FEDACH Routing Number for a Financial Institution |
66 | **state** | **optional.String**| FEDACH Financial Institution State |
67 | **city** | **optional.String**| FEDACH Financial Institution City |
68 | **postalCode** | **optional.String**| FEDACH Financial Institution Postal Code |
69 | **limit** | **optional.Int32**| Maximum results returned by a search |
70 |
71 | ### Return type
72 |
73 | [**AchDictionary**](ACHDictionary.md)
74 |
75 | ### Authorization
76 |
77 | No authorization required
78 |
79 | ### HTTP request headers
80 |
81 | - **Content-Type**: Not defined
82 | - **Accept**: application/json
83 |
84 | [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints)
85 | [[Back to Model list]](../README.md#documentation-for-models)
86 | [[Back to README]](../README.md)
87 |
88 |
89 | ## SearchFEDWIRE
90 |
91 | > WireDictionary SearchFEDWIRE(ctx, optional)
92 |
93 | Search FEDWIRE names and metadata
94 |
95 | ### Required Parameters
96 |
97 |
98 | Name | Type | Description | Notes
99 | ------------- | ------------- | ------------- | -------------
100 | **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc.
101 | **optional** | ***SearchFEDWIREOpts** | optional parameters | nil if no parameters
102 |
103 | ### Optional Parameters
104 |
105 | Optional parameters are passed through a pointer to a SearchFEDWIREOpts struct
106 |
107 |
108 | Name | Type | Description | Notes
109 | ------------- | ------------- | ------------- | -------------
110 | **xRequestID** | **optional.String**| Optional Request ID allows application developer to trace requests through the systems logs |
111 | **xUserID** | **optional.String**| Optional User ID used to perform this search |
112 | **name** | **optional.String**| FEDWIRE Financial Institution Name |
113 | **routingNumber** | **optional.String**| FEDWIRE Routing Number for a Financial Institution |
114 | **state** | **optional.String**| FEDWIRE Financial Institution State |
115 | **city** | **optional.String**| FEDWIRE Financial Institution City |
116 | **limit** | **optional.Int32**| Maximum results returned by a search |
117 |
118 | ### Return type
119 |
120 | [**WireDictionary**](WIREDictionary.md)
121 |
122 | ### Authorization
123 |
124 | No authorization required
125 |
126 | ### HTTP request headers
127 |
128 | - **Content-Type**: Not defined
129 | - **Accept**: application/json
130 |
131 | [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints)
132 | [[Back to Model list]](../README.md#documentation-for-models)
133 | [[Back to README]](../README.md)
134 |
135 |
--------------------------------------------------------------------------------
/cmd/server/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 The Moov Authors
2 | // Use of this source code is governed by an Apache License
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "context"
9 | "crypto/tls"
10 | "errors"
11 | "flag"
12 | "fmt"
13 | "io"
14 | "net/http"
15 | "os"
16 | "os/signal"
17 | "strings"
18 | "syscall"
19 | "time"
20 |
21 | "github.com/moov-io/base/admin"
22 | moovhttp "github.com/moov-io/base/http"
23 | "github.com/moov-io/base/http/bind"
24 | "github.com/moov-io/base/log"
25 | "github.com/moov-io/fed"
26 | "github.com/moov-io/fed/webui"
27 |
28 | "github.com/gorilla/mux"
29 | )
30 |
31 | var (
32 | httpAddr = flag.String("http.addr", bind.HTTP("fed"), "HTTP listen address")
33 | adminAddr = flag.String("admin.addr", bind.Admin("fed"), "Admin HTTP listen address")
34 |
35 | flagLogFormat = flag.String("log.format", "", "Format for log lines (Options: json, plain")
36 | )
37 |
38 | func main() {
39 | flag.Parse()
40 |
41 | var logger log.Logger
42 | if v := os.Getenv("LOG_FORMAT"); v != "" {
43 | *flagLogFormat = v
44 | }
45 | if strings.ToLower(*flagLogFormat) == "json" {
46 | logger = log.NewJSONLogger()
47 | } else {
48 | logger = log.NewDefaultLogger()
49 | }
50 | logger.Info().Logf("Starting fed server version %s", fed.Version)
51 |
52 | // Channel for errors
53 | errs := make(chan error)
54 |
55 | go func() {
56 | c := make(chan os.Signal, 1)
57 | signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
58 | errs <- fmt.Errorf("%s", <-c)
59 | }()
60 |
61 | // Setup business HTTP routes
62 | router := mux.NewRouter()
63 | moovhttp.AddCORSHandler(router)
64 | addPingRoute(router)
65 |
66 | // Start business HTTP server
67 | readTimeout, _ := time.ParseDuration("30s")
68 | writTimeout, _ := time.ParseDuration("30s")
69 | idleTimeout, _ := time.ParseDuration("60s")
70 |
71 | // Check to see if our -http.addr flag has been overridden
72 | if v := os.Getenv("HTTP_BIND_ADDRESS"); v != "" {
73 | *httpAddr = v
74 | }
75 |
76 | serve := &http.Server{
77 | Addr: *httpAddr,
78 | Handler: router,
79 | TLSConfig: &tls.Config{
80 | InsecureSkipVerify: false,
81 | PreferServerCipherSuites: true,
82 | MinVersion: tls.VersionTLS12,
83 | },
84 | ReadTimeout: readTimeout,
85 | ReadHeaderTimeout: readTimeout,
86 | WriteTimeout: writTimeout,
87 | IdleTimeout: idleTimeout,
88 | }
89 | shutdownServer := func() {
90 | if err := serve.Shutdown(context.TODO()); err != nil {
91 | logger.Logf("shutting down: %v", err)
92 | }
93 | }
94 |
95 | // Check to see if our -admin.addr flag has been overridden
96 | if v := os.Getenv("HTTP_ADMIN_BIND_ADDRESS"); v != "" {
97 | *adminAddr = v
98 | }
99 |
100 | // Start Admin server (with Prometheus metrics)
101 | adminServer, err := admin.New(admin.Opts{
102 | Addr: *adminAddr,
103 | })
104 | if err != nil {
105 | logger.LogErrorf("problem creating admin server: %v", err)
106 | os.Exit(1)
107 | }
108 | adminServer.AddVersionHandler(fed.Version) // Setup 'GET /version'
109 | go func() {
110 | logger.Info().Logf(fmt.Sprintf("listening on %s", adminServer.BindAddr()))
111 | if err := adminServer.Listen(); err != nil {
112 | err = fmt.Errorf("problem starting admin http: %v", err)
113 | logger.Logf("admin: %v", err)
114 | errs <- err
115 | }
116 | }()
117 | defer adminServer.Shutdown()
118 |
119 | // Start our searcher
120 | searcher := &searcher{logger: logger}
121 |
122 | fedACHData, err := fedACHDataFile(logger)
123 | if err != nil {
124 | logger.LogErrorf("problem downloading FedACH: %v", err)
125 | os.Exit(1)
126 | }
127 | fedWireData, err := fedWireDataFile(logger)
128 | if err != nil {
129 | logger.LogErrorf("problem downloading FedWire: %v", err)
130 | os.Exit(1)
131 | }
132 |
133 | if err := setupSearcher(logger, searcher, fedACHData, fedWireData); err != nil {
134 | logger.Logf("read: %v", err)
135 | os.Exit(1)
136 | }
137 |
138 | // Add searcher for HTTP routes
139 | addSearchRoutes(logger, router, searcher)
140 |
141 | // Add webui routes
142 | webuiController := webui.NewController(logger)
143 | webuiController.AppendRoutes(router)
144 |
145 | // Start business logic HTTP server
146 | go func() {
147 | if certFile, keyFile := os.Getenv("HTTPS_CERT_FILE"), os.Getenv("HTTPS_KEY_FILE"); certFile != "" && keyFile != "" {
148 | logger.Logf("binding to %s for secure HTTP server", *httpAddr)
149 | if err := serve.ListenAndServeTLS(certFile, keyFile); err != nil {
150 | logger.Logf("listen: %v", err)
151 | }
152 | } else {
153 | logger.Logf("binding to %s for HTTP server", *httpAddr)
154 | if err := serve.ListenAndServe(); err != nil {
155 | logger.Logf("listen: %v", err)
156 | }
157 | }
158 | }()
159 |
160 | // Block/Wait for an error
161 | if err := <-errs; err != nil {
162 | shutdownServer()
163 | logger.Logf("exit: %v", err)
164 | os.Exit(1)
165 | }
166 | }
167 |
168 | func addPingRoute(r *mux.Router) {
169 | r.Methods("GET").Path("/ping").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
170 | moovhttp.SetAccessControlAllowHeaders(w, r.Header.Get("Origin"))
171 | w.Header().Set("Content-Type", "text/plain")
172 | w.WriteHeader(http.StatusOK)
173 | w.Write([]byte("PONG"))
174 | })
175 | }
176 |
177 | func setupSearcher(logger log.Logger, s *searcher, achFile, wireFile io.Reader) error {
178 | if achFile == nil {
179 | return errors.New("missing fedach data file")
180 | }
181 | if wireFile == nil {
182 | return errors.New("missing fedwire data file")
183 | }
184 |
185 | if err := s.readFEDACHData(achFile); err != nil {
186 | return fmt.Errorf("error reading ACH data: %v", err)
187 | }
188 | if err := s.readFEDWIREData(wireFile); err != nil {
189 | return fmt.Errorf("error reading wire data: %v", err)
190 | }
191 |
192 | return s.precompute()
193 | }
194 |
--------------------------------------------------------------------------------
/cmd/server/reader.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 The Moov Authors
2 | // Use of this source code is governed by an Apache License
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "errors"
9 | "fmt"
10 | "io"
11 | "os"
12 | "path/filepath"
13 | "strings"
14 |
15 | "github.com/moov-io/base/log"
16 | "github.com/moov-io/fed"
17 | "github.com/moov-io/fed/pkg/download"
18 | )
19 |
20 | var (
21 | fedachFilenames = []string{"FedACHdir.txt", "fedachdir.json", "fedach.txt", "fedach.json"}
22 | fedwireFilenames = []string{"fpddir.json", "fpddir.txt", "fedwire.txt", "fedwire.json"}
23 | )
24 |
25 | func fedACHDataFile(logger log.Logger) (io.Reader, error) {
26 | initialDir := os.Getenv("INITIAL_DATA_DIRECTORY")
27 | file, err := inspectInitialDataDirectory(logger, initialDir, fedachFilenames)
28 | if err != nil {
29 | return nil, fmt.Errorf("inspecting %s for FedACH file failed: %w", initialDir, err)
30 | }
31 | if file != nil {
32 | logger.Info().Logf("found FedACH file in %s", initialDir)
33 | return file, nil
34 | }
35 |
36 | file, err = attemptFileDownload(logger, "fedach")
37 | if err != nil && !errors.Is(err, download.ErrMissingConfigValue) {
38 | return nil, fmt.Errorf("problem downloading fedach: %v", err)
39 | }
40 |
41 | if file != nil {
42 | logger.Info().Log("search: downloaded ACH file")
43 | return file, nil
44 | }
45 |
46 | path := readDataFilepath("FEDACH_DATA_PATH", "./data/FedACHdir.txt")
47 | logger.Logf("search: loading %s for ACH data", path)
48 |
49 | file, err = os.Open(path)
50 | if err != nil {
51 | return nil, fmt.Errorf("problem opening %s: %v", path, err)
52 | }
53 | return file, nil
54 | }
55 |
56 | func fedWireDataFile(logger log.Logger) (io.Reader, error) {
57 | initialDir := os.Getenv("INITIAL_DATA_DIRECTORY")
58 | file, err := inspectInitialDataDirectory(logger, initialDir, fedwireFilenames)
59 | if err != nil {
60 | return nil, fmt.Errorf("inspecting %s for FedWire file failed: %w", initialDir, err)
61 | }
62 | if file != nil {
63 | logger.Info().Logf("found FedWire file in %s", initialDir)
64 | return file, nil
65 | }
66 |
67 | file, err = attemptFileDownload(logger, "fedwire")
68 | if err != nil && !errors.Is(err, download.ErrMissingConfigValue) {
69 | return nil, fmt.Errorf("problem downloading fedwire: %v", err)
70 | }
71 |
72 | if file != nil {
73 | logger.Info().Log("search: downloaded Wire file")
74 | return file, nil
75 | }
76 |
77 | path := readDataFilepath("FEDWIRE_DATA_PATH", "./data/fpddir.txt")
78 | logger.Logf("search: loading %s for Wire data", path)
79 |
80 | file, err = os.Open(path)
81 | if err != nil {
82 | return nil, fmt.Errorf("problem opening %s: %v", path, err)
83 | }
84 | return file, nil
85 | }
86 |
87 | func inspectInitialDataDirectory(logger log.Logger, dir string, needles []string) (io.Reader, error) {
88 | entries, err := os.ReadDir(dir)
89 | if err != nil {
90 | if os.IsNotExist(err) {
91 | return nil, nil
92 | }
93 | return nil, fmt.Errorf("readdir on %s failed: %w", dir, err)
94 | }
95 |
96 | for _, entry := range entries {
97 | _, filename := filepath.Split(entry.Name())
98 |
99 | for idx := range needles {
100 | if strings.EqualFold(filename, needles[idx]) {
101 | where := filepath.Join(dir, entry.Name())
102 |
103 | fd, err := os.Open(where)
104 | if err != nil {
105 | return nil, fmt.Errorf("opening %s failed: %w", where, err)
106 | }
107 | return fd, nil
108 | }
109 | }
110 | }
111 |
112 | return nil, nil
113 | }
114 |
115 | func attemptFileDownload(logger log.Logger, listName string) (io.Reader, error) {
116 | logger.Logf("download: attempting %s", listName)
117 | client, err := download.NewClient(nil)
118 | if err != nil {
119 | return nil, fmt.Errorf("client setup: %w", err)
120 | }
121 | return client.GetList(listName)
122 | }
123 |
124 | func readDataFilepath(env, fallback string) string {
125 | if v := os.Getenv(env); v != "" {
126 | return v
127 | }
128 | return fallback
129 | }
130 |
131 | // readFEDACHData opens and reads FedACHdir.txt then runs ACHDictionary.Read() to
132 | // parse and define ACHDictionary properties
133 | func (s *searcher) readFEDACHData(reader io.Reader) error {
134 | if s.logger != nil {
135 | s.logger.Logf("Read of FED ACH data from %T", reader)
136 | }
137 |
138 | if closer, ok := reader.(io.Closer); ok {
139 | defer closer.Close()
140 | }
141 |
142 | s.ACHDictionary = fed.NewACHDictionary()
143 | if err := s.ACHDictionary.Read(reader); err != nil {
144 | return fmt.Errorf("ERROR: reading FedACHdir.txt %v", err)
145 | }
146 |
147 | recordCount := len(s.ACHDictionary.ACHParticipants)
148 | if recordCount <= 0 {
149 | return errors.New("read zero records from FedACH file")
150 | } else {
151 | if s.logger != nil {
152 | s.logger.With(log.Fields{
153 | "records": log.Int(recordCount),
154 | }).Logf("Finished refresh of ACH data")
155 | }
156 | }
157 |
158 | return nil
159 | }
160 |
161 | // readFEDWIREData opens and reads fpddir.txt then runs WIREDictionary.Read() to
162 | // parse and define WIREDictionary properties
163 | func (s *searcher) readFEDWIREData(reader io.Reader) error {
164 | if s.logger != nil {
165 | s.logger.Logf("Read of FED Wire data from %T", reader)
166 | }
167 |
168 | if closer, ok := reader.(io.Closer); ok {
169 | defer closer.Close()
170 | }
171 |
172 | s.WIREDictionary = fed.NewWIREDictionary()
173 | if err := s.WIREDictionary.Read(reader); err != nil {
174 | return fmt.Errorf("ERROR: reading fpddir.txt %v", err)
175 | }
176 |
177 | recordCount := len(s.WIREDictionary.WIREParticipants)
178 | if recordCount <= 0 {
179 | return errors.New("read zero records from FedWire file")
180 | } else {
181 | if s.logger != nil {
182 | s.logger.With(log.Fields{
183 | "records": log.Int(recordCount),
184 | }).Logf("Finished refresh of WIRE data")
185 | }
186 | }
187 |
188 | return nil
189 | }
190 |
--------------------------------------------------------------------------------
/cmd/server/search_handlers.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 The Moov Authors
2 | // Use of this source code is governed by an Apache License
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "encoding/json"
9 | "net/http"
10 | "net/url"
11 | "strings"
12 |
13 | "github.com/gorilla/mux"
14 | moovhttp "github.com/moov-io/base/http"
15 | "github.com/moov-io/base/log"
16 | "github.com/moov-io/fed"
17 | )
18 |
19 | func addSearchRoutes(logger log.Logger, r *mux.Router, searcher *searcher) {
20 | r.Methods("GET").Path("/fed/ach/search").HandlerFunc(searchFEDACH(logger, searcher))
21 | r.Methods("GET").Path("/fed/wire/search").HandlerFunc(searchFEDWIRE(logger, searcher))
22 | }
23 |
24 | // fedSearchRequest contains the properties for fed ach search request
25 | type fedSearchRequest struct {
26 | Name string `json:"name"`
27 | RoutingNumber string `json:"routingNumber"`
28 | City string `json:"city"`
29 | State string `json:"state"`
30 | PostalCode string `json:"postalCode"`
31 | }
32 |
33 | // readFEDSearchRequest returns a fedachSearchRequest based on url parameters for fed ach search
34 | func readFEDSearchRequest(u *url.URL) fedSearchRequest {
35 | return fedSearchRequest{
36 | Name: strings.ToUpper(strings.TrimSpace(u.Query().Get("name"))),
37 | RoutingNumber: strings.ToUpper(strings.TrimSpace(u.Query().Get("routingNumber"))),
38 | City: strings.ToUpper(strings.TrimSpace(u.Query().Get("city"))),
39 | State: strings.ToUpper(strings.TrimSpace(u.Query().Get("state"))),
40 | PostalCode: strings.ToUpper(strings.TrimSpace(u.Query().Get("postalCode"))),
41 | }
42 | }
43 |
44 | // empty returns true if all of the properties in fedachSearchRequest are empty
45 | func (req fedSearchRequest) empty() bool {
46 | return req.Name == "" && req.RoutingNumber == "" && req.City == "" &&
47 | req.State == "" && req.PostalCode == ""
48 | }
49 |
50 | // nameOnly returns true if only Name is not ""
51 | func (req fedSearchRequest) nameOnly() bool {
52 | return req.Name != "" && req.RoutingNumber == "" && req.City == "" &&
53 | req.State == "" && req.PostalCode == ""
54 | }
55 |
56 | // routingNumberOnly returns true if only routingNumber is not ""
57 | func (req fedSearchRequest) routingNumberOnly() bool {
58 | return req.Name == "" && req.RoutingNumber != "" && req.City == "" &&
59 | req.State == "" && req.PostalCode == ""
60 | }
61 |
62 | // cityOnly returns true if only city is not ""
63 | func (req fedSearchRequest) cityOnly() bool {
64 | return req.Name == "" && req.RoutingNumber == "" && req.City != "" &&
65 | req.State == "" && req.PostalCode == ""
66 | }
67 |
68 | // stateOnly returns true if only state is not ""
69 | func (req fedSearchRequest) stateOnly() bool {
70 | return req.Name == "" && req.RoutingNumber == "" && req.City == "" &&
71 | req.State != "" && req.PostalCode == ""
72 | }
73 |
74 | // postalCodeOnly returns true if only postal code is not ""
75 | func (req fedSearchRequest) postalCodeOnly() bool {
76 | return req.Name == "" && req.RoutingNumber == "" && req.City == "" &&
77 | req.State == "" && req.PostalCode != ""
78 | }
79 |
80 | // searchFEDACH calls search functions based on the fed ach search request url parameters
81 | func searchFEDACH(logger log.Logger, searcher *searcher) http.HandlerFunc {
82 | return func(w http.ResponseWriter, r *http.Request) {
83 | if logger == nil {
84 | logger = log.NewDefaultLogger()
85 | }
86 |
87 | w = wrapResponseWriter(logger, w, r)
88 | w.Header().Set("Content-Type", "application/json; charset=utf-8")
89 |
90 | requestID, userID := moovhttp.GetRequestID(r), moovhttp.GetUserID(r)
91 | logger = logger.With(log.Fields{
92 | "requestID": log.String(requestID),
93 | "userID": log.String(userID),
94 | })
95 |
96 | req := readFEDSearchRequest(r.URL)
97 | if req.empty() {
98 | logger.Error().Logf("searchFedACH", log.String(errNoSearchParams.Error()))
99 | moovhttp.Problem(w, errNoSearchParams)
100 | return
101 | }
102 |
103 | searchLimit := extractSearchLimit(r)
104 |
105 | var achParticipants []*fed.ACHParticipant
106 | var err error
107 |
108 | switch {
109 | case req.nameOnly():
110 | logger.Logf("searching FED ACH Dictionary by name only %s", req.Name)
111 | achParticipants = searcher.ACHFindNameOnly(searchLimit, req.Name)
112 |
113 | case req.routingNumberOnly():
114 | logger.Logf("searching FED ACH Dictionary by routing number only %s", req.RoutingNumber)
115 | achParticipants, err = searcher.ACHFindRoutingNumberOnly(searchLimit, req.RoutingNumber)
116 | if err != nil {
117 | moovhttp.Problem(w, err)
118 | return
119 | }
120 |
121 | case req.stateOnly():
122 | logger.Logf("searching FED ACH Dictionary by state only %s", req.State)
123 | achParticipants = searcher.ACHFindStateOnly(searchLimit, req.State)
124 |
125 | case req.cityOnly():
126 | logger.Logf("searching FED ACH Dictionary by city only %s", req.City)
127 | achParticipants = searcher.ACHFindCityOnly(searchLimit, req.City)
128 |
129 | case req.postalCodeOnly():
130 | logger.Logf("searching FED ACH Dictionary by postal code only %s", req.PostalCode)
131 | achParticipants = searcher.ACHFindPostalCodeOnly(searchLimit, req.PostalCode)
132 |
133 | default:
134 | logger.Logf("searching FED ACH Dictionary by parameters %v", req.RoutingNumber)
135 | achParticipants, err = searcher.ACHFind(searchLimit, req)
136 | if err != nil {
137 | moovhttp.Problem(w, err)
138 | return
139 | }
140 | }
141 |
142 | w.WriteHeader(http.StatusOK)
143 | json.NewEncoder(w).Encode(&searchResponse{
144 | ACHParticipants: achParticipants,
145 | Stats: &searcher.achStats,
146 | })
147 | }
148 | }
149 |
150 | // searchFEDWIRE calls search functions based on the fed wire search request url parameters
151 | func searchFEDWIRE(logger log.Logger, searcher *searcher) http.HandlerFunc {
152 | return func(w http.ResponseWriter, r *http.Request) {
153 | if logger == nil {
154 | logger = log.NewDefaultLogger()
155 | }
156 |
157 | w = wrapResponseWriter(logger, w, r)
158 | w.Header().Set("Content-Type", "application/json; charset=utf-8")
159 |
160 | requestID, userID := moovhttp.GetRequestID(r), moovhttp.GetUserID(r)
161 | logger = logger.With(log.Fields{
162 | "requestID": log.String(requestID),
163 | "userID": log.String(userID),
164 | })
165 |
166 | req := readFEDSearchRequest(r.URL)
167 | if req.empty() {
168 | logger.Error().Logf("searchFEDWIRE: %v", errNoSearchParams)
169 | moovhttp.Problem(w, errNoSearchParams)
170 | return
171 | }
172 |
173 | searchLimit := extractSearchLimit(r)
174 |
175 | var wireParticipants []*fed.WIREParticipant
176 | var err error
177 |
178 | switch {
179 | case req.nameOnly():
180 | logger.Logf("searchFEDWIRE: searching FED WIRE Dictionary by name only %s", req.Name)
181 | wireParticipants = searcher.WIREFindNameOnly(searchLimit, req.Name)
182 |
183 | case req.routingNumberOnly():
184 | logger.Logf("searchFEDWIRE: searching FED WIRE Dictionary by routing number only %s", req.RoutingNumber)
185 | wireParticipants, err = searcher.WIREFindRoutingNumberOnly(searchLimit, req.RoutingNumber)
186 | if err != nil {
187 | moovhttp.Problem(w, err)
188 | return
189 | }
190 |
191 | case req.stateOnly():
192 | logger.Logf("searchFEDWIRE: searching FED WIRE Dictionary by state only %s", req.State)
193 | wireParticipants = searcher.WIREFindStateOnly(searchLimit, req.State)
194 |
195 | case req.cityOnly():
196 | logger.Logf("searchFEDWIRE: searching FED WIRE Dictionary by city only %s", req.City)
197 | wireParticipants = searcher.WIREFindCityOnly(searchLimit, req.City)
198 |
199 | default:
200 | logger.Logf("searchFEDWIRE: searching FED WIRE Dictionary by parameters %v", req.RoutingNumber)
201 | wireParticipants, err = searcher.WIREFind(searchLimit, req)
202 | if err != nil {
203 | moovhttp.Problem(w, err)
204 | }
205 | }
206 |
207 | w.WriteHeader(http.StatusOK)
208 | json.NewEncoder(w).Encode(&searchResponse{
209 | WIREParticipants: wireParticipants,
210 | Stats: &searcher.wireStats,
211 | })
212 | }
213 | }
214 |
--------------------------------------------------------------------------------
/cmd/server/search.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 The Moov Authors
2 | // Use of this source code is governed by an Apache License
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "errors"
9 | "fmt"
10 | "net/http"
11 | "strconv"
12 | "strings"
13 | "sync"
14 | "time"
15 |
16 | "github.com/moov-io/base/log"
17 | "github.com/moov-io/fed"
18 | )
19 |
20 | var (
21 | errNoSearchParams = errors.New("missing search parameter(s)")
22 | softResultsLimit, hardResultsLimit = 100, 500
23 | )
24 |
25 | // searcher defines a searcher struct
26 | type searcher struct {
27 | ACHDictionary *fed.ACHDictionary
28 | WIREDictionary *fed.WIREDictionary
29 | sync.RWMutex // protects all above fields
30 |
31 | achStats ListStats
32 | wireStats ListStats
33 |
34 | logger log.Logger
35 | }
36 |
37 | type ListStats struct {
38 | Records int `json:"records"`
39 | Latest time.Time `json:"latest"`
40 | }
41 |
42 | func (s *searcher) precompute() error {
43 | if err := s.precomputeACHStats(); err != nil {
44 | return fmt.Errorf("precomputing ACH stats: %w", err)
45 | }
46 | if err := s.precomputeWireStats(); err != nil {
47 | return fmt.Errorf("precomputing wire stats: %w", err)
48 | }
49 | return nil
50 | }
51 |
52 | func (s *searcher) precomputeACHStats() error {
53 | if s.ACHDictionary != nil {
54 | s.achStats.Records = len(s.ACHDictionary.ACHParticipants)
55 | }
56 |
57 | for idx := range s.ACHDictionary.ACHParticipants {
58 | t, err := readDate(s.ACHDictionary.ACHParticipants[idx].Revised)
59 | if err != nil {
60 | return fmt.Errorf("parsing ACH record date: %w", err)
61 | }
62 | if s.achStats.Latest.Before(t) {
63 | s.achStats.Latest = t
64 | }
65 | }
66 |
67 | return nil
68 | }
69 |
70 | func (s *searcher) precomputeWireStats() error {
71 | if s.WIREDictionary != nil {
72 | s.wireStats.Records = len(s.WIREDictionary.WIREParticipants)
73 | }
74 |
75 | for idx := range s.WIREDictionary.WIREParticipants {
76 | t, err := readDate(s.WIREDictionary.WIREParticipants[idx].Date)
77 | if err != nil {
78 | return fmt.Errorf("parsing WIRE record date: %w", err)
79 | }
80 | if s.wireStats.Latest.Before(t) {
81 | s.wireStats.Latest = t
82 | }
83 | }
84 |
85 | return nil
86 | }
87 |
88 | var (
89 | acceptedDateFormats = []string{"060102", "010206", "20060102"}
90 | )
91 |
92 | func readDate(value string) (tt time.Time, err error) {
93 | value = strings.TrimSpace(value)
94 | if value == "" {
95 | return
96 | }
97 | for _, fmt := range acceptedDateFormats {
98 | tt, err = time.Parse(fmt, value)
99 | if err == nil {
100 | return tt, nil
101 | }
102 | }
103 | return
104 | }
105 |
106 | // searchResponse defines a FEDACH search response
107 | type searchResponse struct {
108 | ACHParticipants []*fed.ACHParticipant `json:"achParticipants,omitempty"`
109 | WIREParticipants []*fed.WIREParticipant `json:"wireParticipants,omitempty"`
110 |
111 | Stats *ListStats `json:"stats"`
112 | }
113 |
114 | // ACHFindNameOnly finds ACH Participants by name only
115 | func (s *searcher) ACHFindNameOnly(limit int, participantName string) []*fed.ACHParticipant {
116 | s.RLock()
117 | defer s.RUnlock()
118 |
119 | return s.ACHDictionary.FinancialInstitutionSearch(participantName, limit)
120 | }
121 |
122 | // ACHFindRoutingNumberOnly finds ACH Participants by routing number only
123 | func (s *searcher) ACHFindRoutingNumberOnly(limit int, routingNumber string) ([]*fed.ACHParticipant, error) {
124 | s.RLock()
125 | defer s.RUnlock()
126 |
127 | return s.ACHDictionary.RoutingNumberSearch(routingNumber, limit)
128 | }
129 |
130 | // ACHFindCityOnly finds ACH Participants by city only
131 | func (s *searcher) ACHFindCityOnly(limit int, city string) []*fed.ACHParticipant {
132 | s.RLock()
133 | defer s.RUnlock()
134 |
135 | return achLimit(s.ACHDictionary.CityFilter(city), limit)
136 | }
137 |
138 | // ACHFindSateOnly finds ACH Participants by state only
139 | func (s *searcher) ACHFindStateOnly(limit int, state string) []*fed.ACHParticipant {
140 | s.RLock()
141 | defer s.RUnlock()
142 |
143 | return achLimit(s.ACHDictionary.StateFilter(state), limit)
144 | }
145 |
146 | // ACHFindPostalCodeOnly finds ACH Participants by postal code only
147 | func (s *searcher) ACHFindPostalCodeOnly(limit int, postalCode string) []*fed.ACHParticipant {
148 | s.RLock()
149 | defer s.RUnlock()
150 |
151 | return achLimit(s.ACHDictionary.PostalCodeFilter(postalCode), limit)
152 | }
153 |
154 | // ACHFind finds ACH Participants based on multiple parameters
155 | func (s *searcher) ACHFind(limit int, req fedSearchRequest) ([]*fed.ACHParticipant, error) {
156 | s.RLock()
157 | defer s.RUnlock()
158 | var err error
159 |
160 | out := s.ACHDictionary.FinancialInstitutionSearch(req.Name, limit)
161 | if req.RoutingNumber != "" {
162 | out, err = s.ACHDictionary.ACHParticipantRoutingNumberFilter(out, req.RoutingNumber)
163 | if err != nil {
164 | return nil, err
165 | }
166 | }
167 | if req.State != "" {
168 | out = s.ACHDictionary.ACHParticipantStateFilter(out, req.State)
169 | }
170 | if req.City != "" {
171 | out = s.ACHDictionary.ACHParticipantCityFilter(out, req.City)
172 | }
173 | if req.PostalCode != "" {
174 | out = s.ACHDictionary.ACHParticipantPostalCodeFilter(out, req.PostalCode)
175 | }
176 | return out, nil
177 | }
178 |
179 | // WIRE Searches
180 |
181 | // WIREFindNameOnly finds WIRE Participants by name only
182 | func (s *searcher) WIREFindNameOnly(limit int, participantName string) []*fed.WIREParticipant {
183 | s.RLock()
184 | defer s.RUnlock()
185 | fi := s.WIREDictionary.FinancialInstitutionSearch(participantName, limit)
186 | out := wireLimit(fi, limit)
187 | return out
188 | }
189 |
190 | // WIREFindRoutingNumberOnly finds WIRE Participants by routing number only
191 | func (s *searcher) WIREFindRoutingNumberOnly(limit int, routingNumber string) ([]*fed.WIREParticipant, error) {
192 | s.RLock()
193 | defer s.RUnlock()
194 | fi, err := s.WIREDictionary.RoutingNumberSearch(routingNumber, limit)
195 | if err != nil {
196 | return nil, err
197 | }
198 | out := wireLimit(fi, limit)
199 | return out, nil
200 | }
201 |
202 | // WIREFindCityOnly finds WIRE Participants by city only
203 | func (s *searcher) WIREFindCityOnly(limit int, city string) []*fed.WIREParticipant {
204 | s.RLock()
205 | defer s.RUnlock()
206 | fi := s.WIREDictionary.CityFilter(city)
207 | out := wireLimit(fi, limit)
208 | return out
209 | }
210 |
211 | // WIREFindSateOnly finds WIRE Participants by state only
212 | func (s *searcher) WIREFindStateOnly(limit int, state string) []*fed.WIREParticipant {
213 | s.RLock()
214 | defer s.RUnlock()
215 | fi := s.WIREDictionary.StateFilter(state)
216 | out := wireLimit(fi, limit)
217 | return out
218 | }
219 |
220 | // WIRE Find finds WIRE Participants based on multiple parameters
221 | func (s *searcher) WIREFind(limit int, req fedSearchRequest) ([]*fed.WIREParticipant, error) {
222 | s.RLock()
223 | defer s.RUnlock()
224 | var err error
225 | fi := s.WIREDictionary.FinancialInstitutionSearch(req.Name, limit)
226 |
227 | if req.RoutingNumber != "" {
228 | fi, err = s.WIREDictionary.WIREParticipantRoutingNumberFilter(fi, req.RoutingNumber)
229 | if err != nil {
230 | return nil, err
231 | }
232 | }
233 |
234 | if req.State != "" {
235 | fi = s.WIREDictionary.WIREParticipantStateFilter(fi, req.State)
236 | }
237 |
238 | if req.City != "" {
239 | fi = s.WIREDictionary.WIREParticipantCityFilter(fi, req.City)
240 | }
241 |
242 | out := wireLimit(fi, limit)
243 | return out, nil
244 | }
245 |
246 | // extractSearchLimit extracts the search limit from url query parameters
247 | func extractSearchLimit(r *http.Request) int {
248 | limit := softResultsLimit
249 | if v := r.URL.Query().Get("limit"); v != "" {
250 | n, _ := strconv.Atoi(v)
251 | if n > 0 {
252 | limit = n
253 | }
254 | }
255 | if limit > hardResultsLimit {
256 | limit = hardResultsLimit
257 | }
258 | return limit
259 | }
260 |
261 | // achLimit returns an FEDACH search result based on the search limit
262 | func achLimit(fi []*fed.ACHParticipant, limit int) []*fed.ACHParticipant {
263 | var out []*fed.ACHParticipant
264 | for _, p := range fi {
265 | if len(out) == limit {
266 | break
267 | }
268 | out = append(out, p)
269 | }
270 | return out
271 | }
272 |
273 | // wireLimit returns a FEDWIRE search result based on the search limit
274 | func wireLimit(fi []*fed.WIREParticipant, limit int) []*fed.WIREParticipant {
275 | var out []*fed.WIREParticipant
276 | for _, p := range fi {
277 | if len(out) == limit {
278 | break
279 | }
280 | out = append(out, p)
281 | }
282 | return out
283 | }
284 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## v0.13.0 (Released 2025-05-15)
2 |
3 | ADDITIONS
4 |
5 | - feat: read files from INITIAL_DATA_DIRECTORY
6 |
7 | IMPROVEMENTS
8 |
9 | - cmd/server: fail on startup when zero participants are found
10 |
11 | BUILD
12 |
13 | - chore(deps): update dependency go to v1.24.3 (#338)
14 | - fix(deps): update module github.com/moov-io/base to v0.55.0 (#339)
15 | - fix(deps): update module golang.org/x/oauth2 to v0.30.0 (#336)
16 | - fix(deps): update module golang.org/x/text to v0.25.0 (#337)
17 |
18 | ## v0.12.1 (Released 2025-04-22)
19 |
20 | IMPROVEMENTS
21 |
22 | - cmd/server: improve startup logging
23 | - cmd/server: return list stats in search response
24 |
25 | BUILD
26 |
27 | - build: update dependencies
28 | - chore(deps): update dependency go to v1.24.1 (#319)
29 |
30 | ## v0.11.1 (Released 2024-03-05)
31 |
32 | IMPROVEMENTS
33 |
34 | - fix: wire file parse bug, improve logic for picking json vs plaintext parser
35 |
36 | ## v0.11.0 (Released 2024-02-27)
37 |
38 | IMPROVEMENTS
39 |
40 | - feat: Enable fedach and fedwire download from proxy
41 | - fix: close xml encoder and remove unneeded panics
42 |
43 | BUILD
44 |
45 | - build: use latest stable Go release
46 | - fix(deps): update module golang.org/x/oauth2 to v0.16.0
47 | - fix(deps): update module github.com/moov-io/base to v0.48.5
48 |
49 | ## v0.10.2 (Released 2023-06-15)
50 |
51 | IMPROVEMENTS
52 |
53 | - Use Go 1.20.x in build, update deps
54 |
55 | ## v0.10.1 (Released 2023-04-19)
56 |
57 | REVERTS
58 |
59 | - client: revert openapi-generator back to 4.3.1
60 |
61 | ## v0.10.0 (Released 2023-04-12)
62 |
63 | IMPROVEMENTS
64 |
65 | - chore: update openapi-generator from 4.2.2 to 6.5.0
66 | - fix: lowercase ach/wire participants in OpenAPI
67 |
68 | BUILD
69 |
70 | - build(deps): bump nokogiri from 1.13.10 to 1.14.3 in /docs
71 | - build(deps): bump github.com/moov-io/base from v0.39.0 to v0.40.1
72 |
73 | ## v0.9.2 (Released 2023-04-07)
74 |
75 | IMPROVEMENTS
76 |
77 | - fix: check typecast of Logo
78 |
79 | BUILD
80 |
81 | - build: upgrade golang to 1.20
82 | - fix(deps): update module github.com/moov-io/base to v0.39.0
83 | - bump golang.org/x/net from 0.6.0 to 0.7.0
84 | - build: update github.com/stretchr/testify to v1.8.2
85 |
86 | ## v0.9.1 (Released 2022-09-29)
87 |
88 | BUILD
89 |
90 | - build: remove deprecated ioutil functions
91 | - fix(deps): update golang.org/x/oauth2 digest to f213421
92 | - fix(deps): update module github.com/moov-io/base to v0.35.0
93 |
94 | ## v0.9.0 (Released 2022-08-03)
95 |
96 | IMPROVEMENTS
97 |
98 | - Remove `DOWNLOAD_DIRECTORY` and store downloaded files in memory.
99 |
100 | ## v0.8.1 (Released 2022-08-02)
101 |
102 | IMPROVEMENTS
103 |
104 | - fix: remove achParticipants or wireParticipants from json responses
105 |
106 | BUILD
107 |
108 | - build: require Go 1.18 and set ReadHeaderTimeout
109 | - fix(deps): update module github.com/moov-io/base to v0.33.0
110 | - fix(deps): update golang.org/x/oauth2 digest to 128564f
111 |
112 | ## v0.8.0 (Released 2022-05-25)
113 |
114 | ADDITIONS
115 |
116 | - feat: add clearbit logos in responses when configured
117 | - feat: normalize FRB names prior to clearbit search
118 |
119 | IMPROVEMENTS
120 |
121 | - fix: improve name search by using cleaned name
122 | - refactor: cleanup duplicate code in search logic
123 |
124 | BUILD
125 |
126 | - build: update codeql action
127 | - build(deps): bump nokogiri from 1.13.4 to 1.13.6 in /docs
128 |
129 | ## v0.7.4 (Released 2022-05-18)
130 |
131 | BUILD
132 |
133 | - build: update base images
134 | - build(deps): bump nokogiri from 1.13.3 to 1.13.4 in /docs
135 | - fix(deps): update golang.org/x/oauth2 digest to 9780585
136 |
137 | ## v0.7.3 (Released 2022-04-04)
138 |
139 | IMPROVEMENTS
140 |
141 | - fix: replace deprecated strings.Title
142 |
143 | ## v0.7.2 (Released 2022-04-04)
144 |
145 | BUILD
146 |
147 | - build(deps): bump nokogiri from 1.12.5 to 1.13.3 in /docs
148 | - fix(deps): update golang.org/x/oauth2 commit hash to ee48083
149 | - fix(deps): update module github.com/go-kit/kit to v0.12.0
150 | - fix(deps): update module github.com/moov-io/base to v0.28.1
151 | - fix(deps): update module github.com/prometheus/client_golang to v1.12.1
152 |
153 | ## v0.7.1 (Released 2021-07-16)
154 |
155 | BUILD
156 |
157 | - build(deps): bump addressable from 2.7.0 to 2.8.0 in /docs
158 | - build(deps): bump nokogiri from 1.11.1 to 1.11.5 in /docs
159 | - fix(deps): update golang.org/x/oauth2 commit hash to d040287
160 | - fix(deps): update module github.com/go-kit/kit to v0.11.0
161 |
162 | ## v0.7.0 (Released 2021-05-19)
163 |
164 | ADDITIONS
165 |
166 | - Read `DOWNLOAD_DIRECTORY` environment variable for storing downloaded files.
167 |
168 | IMPROVEMENTS
169 |
170 | - search: rank results based on fuzzy score rather than name, offer exect routing number matching
171 |
172 | BUG FIXES
173 |
174 | - Fix file download errors in Docker images
175 | - De-duplicate search results, improve performance
176 |
177 | BUILD
178 |
179 | - build(deps): bump rexml from 3.2.4 to 3.2.5 in /docs
180 |
181 | ## v0.6.0 (Released 2021-04-14)
182 |
183 | ADDITIONS
184 |
185 | - cmd/server: download files if env vars are populated (`FRB_ROUTING_NUMBER` and `FRB_DOWNLOAD_CODE`)
186 |
187 | BUILD
188 |
189 | - fix(deps): update module github.com/clearbit/clearbit-go to v1.0.1
190 |
191 | ## v0.5.3 (Released 2021-02-23)
192 |
193 | IMPROVEMENTS
194 |
195 | - chore(deps): update golang docker tag to v1.16
196 |
197 | ## v0.5.2 (Released 2021-01-22)
198 |
199 | IMPROVEMENTS
200 |
201 | - chore(deps): update github.com/xrash/smetrics commit hash to 89a2a8a
202 |
203 | BUG FIXES
204 |
205 | - build: fixup for OpenShift image running
206 |
207 | BUILD
208 |
209 | - chore(deps): update golang docker tag to v1.15
210 | - chore(deps): update module gorilla/mux to v1.8.0
211 |
212 | ## v0.5.1 (Released 2020-07-07)
213 |
214 | BUULD
215 |
216 | - build: add OpenShift [`quay.io/moov/fed`](https://quay.io/repository/moov/fed) Docker image
217 | - build: convert to Actions from TravisCI
218 | - chore(deps): update module prometheus/client_golang to v1.7.0
219 | - chore(deps): upgrade github.com/gorilla/websocket to v1.4.2
220 |
221 | ## v0.5.0 (Released 2020-04-14)
222 |
223 | ADDITIONS
224 |
225 | - ach: support reading input files in the official JSON format
226 | - wire: read official JSON data files
227 |
228 | BUILD
229 |
230 | - wire: read official JSON data files
231 |
232 | ## v0.4.3 (Released 2020-03-16)
233 |
234 | BUILD
235 |
236 | - Fix `make dist` on Windows
237 |
238 | ## v0.4.2 (Released 2020-03-16)
239 |
240 | ADDITIONS
241 |
242 | - build: release windows binary
243 |
244 | IMPROVEMENTS
245 |
246 | - api: use shared Error model
247 | - docs: clarify included data files are old
248 |
249 | BUILD
250 |
251 | - chore(deps): update golang docker tag to v1.14
252 | - Update module prometheus/client_golang to v1.3.0
253 | - chore(deps): update golang.org/x/oauth2 commit hash to bf48bf1
254 | - build: run sonatype-nexus-community/nancy in CI
255 |
256 | ## v0.4.1 (Released 2019-12-17)
257 |
258 | IMPROVEMENTS
259 |
260 | - build: slim down final image, run as moov user
261 |
262 | BUILD
263 |
264 | - build: test docker image in CI
265 | - Update module prometheus/client_golang to v1.2.1
266 | - build: upgrade openapi-generator to 4.2.2
267 |
268 | ## v0.4.0 (Released 2019-10-07)
269 |
270 | BUG FIXES
271 |
272 | - changing ach participant model so AchLocation isn't a list (#68)
273 | - cmd/server: return after marshaling errNoSearchParams
274 |
275 | IMPROVEMENTS
276 |
277 | - cmd/fedtest: initial binary to perform ACH and Wire searches
278 | - cmd/server: log x-request-id and x-user-id HTTP headers
279 |
280 | BUILD
281 |
282 | - update module moov-io/base to v0.10.0
283 | - build: upgrade to Go 1.13 and Debian 10
284 |
285 | ## v0.3.0 (Released 2019-08-16)
286 |
287 | BREAKING CHANGES
288 |
289 | We've renamed all OpenAPI fields like `Id` to `ID` to be consistent with Go's style.
290 |
291 | ADDITIONS
292 |
293 | - add environment variables to override command line flags (`LOG_FORMAT`, `HTTP_BIND_ADDRESS`, `HTTP_ADMIN_BIND_ADDRESS`)
294 | - cmd/server: bind HTTP server with TLS if HTTPS_* variables are defined
295 |
296 | IMPROVEMENTS
297 |
298 | - docs: update docs.moov.io links after design refresh
299 | - docs: link to app specific docs.moov.io page
300 | - cmd/server: quit with an exit code of 1 on missing data files
301 |
302 | BUILD
303 |
304 | - chore(deps): update module prometheus/client_golang to v1.1.0
305 | - build: download tools used by TravisCI instead of installing them
306 |
307 | ## v0.2.0 (Released 2019-06-19)
308 |
309 | BUILD
310 |
311 | - Ship old example FED data files in the Docker image. Production deployments need to replace these with updated files from their Financial Institution.
312 |
313 | ## v0.1.x (Released 2019-03-06)
314 |
315 | BUG FIXES
316 |
317 | - Fix automated build steps and Docker setup
318 |
319 | ADDITIONS
320 |
321 | - Added environmental variables for data filepaths
322 |
323 | ## v0.1.0 (Released 2019-03-06)
324 |
325 | - Initial release
326 |
--------------------------------------------------------------------------------
/docs/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | activesupport (7.2.0)
5 | base64
6 | bigdecimal
7 | concurrent-ruby (~> 1.0, >= 1.3.1)
8 | connection_pool (>= 2.2.5)
9 | drb
10 | i18n (>= 1.6, < 2)
11 | logger (>= 1.4.2)
12 | minitest (>= 5.1)
13 | securerandom (>= 0.3)
14 | tzinfo (~> 2.0, >= 2.0.5)
15 | addressable (2.8.7)
16 | public_suffix (>= 2.0.2, < 7.0)
17 | base64 (0.2.0)
18 | bigdecimal (3.1.8)
19 | bulma-clean-theme (0.14.0)
20 | jekyll (>= 3.9, < 5.0)
21 | jekyll-feed (~> 0.15)
22 | jekyll-paginate (~> 1.1)
23 | jekyll-seo-tag (~> 2.7)
24 | jekyll-sitemap (~> 1.4)
25 | kramdown-parser-gfm (~> 1.1)
26 | coffee-script (2.4.1)
27 | coffee-script-source
28 | execjs
29 | coffee-script-source (1.12.2)
30 | colorator (1.1.0)
31 | commonmarker (0.23.10)
32 | concurrent-ruby (1.3.4)
33 | connection_pool (2.4.1)
34 | csv (3.3.0)
35 | dnsruby (1.72.2)
36 | simpleidn (~> 0.2.1)
37 | drb (2.2.1)
38 | em-websocket (0.5.3)
39 | eventmachine (>= 0.12.9)
40 | http_parser.rb (~> 0)
41 | ethon (0.16.0)
42 | ffi (>= 1.15.0)
43 | eventmachine (1.2.7)
44 | execjs (2.9.1)
45 | faraday (2.10.1)
46 | faraday-net_http (>= 2.0, < 3.2)
47 | logger
48 | faraday-net_http (3.1.1)
49 | net-http
50 | ffi (1.17.0-arm64-darwin)
51 | ffi (1.17.0-x86_64-darwin)
52 | ffi (1.17.0-x86_64-linux-gnu)
53 | forwardable-extended (2.6.0)
54 | gemoji (4.1.0)
55 | github-pages (232)
56 | github-pages-health-check (= 1.18.2)
57 | jekyll (= 3.10.0)
58 | jekyll-avatar (= 0.8.0)
59 | jekyll-coffeescript (= 1.2.2)
60 | jekyll-commonmark-ghpages (= 0.5.1)
61 | jekyll-default-layout (= 0.1.5)
62 | jekyll-feed (= 0.17.0)
63 | jekyll-gist (= 1.5.0)
64 | jekyll-github-metadata (= 2.16.1)
65 | jekyll-include-cache (= 0.2.1)
66 | jekyll-mentions (= 1.6.0)
67 | jekyll-optional-front-matter (= 0.3.2)
68 | jekyll-paginate (= 1.1.0)
69 | jekyll-readme-index (= 0.3.0)
70 | jekyll-redirect-from (= 0.16.0)
71 | jekyll-relative-links (= 0.6.1)
72 | jekyll-remote-theme (= 0.4.3)
73 | jekyll-sass-converter (= 1.5.2)
74 | jekyll-seo-tag (= 2.8.0)
75 | jekyll-sitemap (= 1.4.0)
76 | jekyll-swiss (= 1.0.0)
77 | jekyll-theme-architect (= 0.2.0)
78 | jekyll-theme-cayman (= 0.2.0)
79 | jekyll-theme-dinky (= 0.2.0)
80 | jekyll-theme-hacker (= 0.2.0)
81 | jekyll-theme-leap-day (= 0.2.0)
82 | jekyll-theme-merlot (= 0.2.0)
83 | jekyll-theme-midnight (= 0.2.0)
84 | jekyll-theme-minimal (= 0.2.0)
85 | jekyll-theme-modernist (= 0.2.0)
86 | jekyll-theme-primer (= 0.6.0)
87 | jekyll-theme-slate (= 0.2.0)
88 | jekyll-theme-tactile (= 0.2.0)
89 | jekyll-theme-time-machine (= 0.2.0)
90 | jekyll-titles-from-headings (= 0.5.3)
91 | jemoji (= 0.13.0)
92 | kramdown (= 2.4.0)
93 | kramdown-parser-gfm (= 1.1.0)
94 | liquid (= 4.0.4)
95 | mercenary (~> 0.3)
96 | minima (= 2.5.1)
97 | nokogiri (>= 1.16.2, < 2.0)
98 | rouge (= 3.30.0)
99 | terminal-table (~> 1.4)
100 | webrick (~> 1.8)
101 | github-pages-health-check (1.18.2)
102 | addressable (~> 2.3)
103 | dnsruby (~> 1.60)
104 | octokit (>= 4, < 8)
105 | public_suffix (>= 3.0, < 6.0)
106 | typhoeus (~> 1.3)
107 | html-pipeline (2.14.3)
108 | activesupport (>= 2)
109 | nokogiri (>= 1.4)
110 | http_parser.rb (0.8.0)
111 | i18n (1.14.5)
112 | concurrent-ruby (~> 1.0)
113 | jekyll (3.10.0)
114 | addressable (~> 2.4)
115 | colorator (~> 1.0)
116 | csv (~> 3.0)
117 | em-websocket (~> 0.5)
118 | i18n (>= 0.7, < 2)
119 | jekyll-sass-converter (~> 1.0)
120 | jekyll-watch (~> 2.0)
121 | kramdown (>= 1.17, < 3)
122 | liquid (~> 4.0)
123 | mercenary (~> 0.3.3)
124 | pathutil (~> 0.9)
125 | rouge (>= 1.7, < 4)
126 | safe_yaml (~> 1.0)
127 | webrick (>= 1.0)
128 | jekyll-avatar (0.8.0)
129 | jekyll (>= 3.0, < 5.0)
130 | jekyll-coffeescript (1.2.2)
131 | coffee-script (~> 2.2)
132 | coffee-script-source (~> 1.12)
133 | jekyll-commonmark (1.4.0)
134 | commonmarker (~> 0.22)
135 | jekyll-commonmark-ghpages (0.5.1)
136 | commonmarker (>= 0.23.7, < 1.1.0)
137 | jekyll (>= 3.9, < 4.0)
138 | jekyll-commonmark (~> 1.4.0)
139 | rouge (>= 2.0, < 5.0)
140 | jekyll-default-layout (0.1.5)
141 | jekyll (>= 3.0, < 5.0)
142 | jekyll-feed (0.17.0)
143 | jekyll (>= 3.7, < 5.0)
144 | jekyll-gist (1.5.0)
145 | octokit (~> 4.2)
146 | jekyll-github-metadata (2.16.1)
147 | jekyll (>= 3.4, < 5.0)
148 | octokit (>= 4, < 7, != 4.4.0)
149 | jekyll-include-cache (0.2.1)
150 | jekyll (>= 3.7, < 5.0)
151 | jekyll-mentions (1.6.0)
152 | html-pipeline (~> 2.3)
153 | jekyll (>= 3.7, < 5.0)
154 | jekyll-optional-front-matter (0.3.2)
155 | jekyll (>= 3.0, < 5.0)
156 | jekyll-paginate (1.1.0)
157 | jekyll-readme-index (0.3.0)
158 | jekyll (>= 3.0, < 5.0)
159 | jekyll-redirect-from (0.16.0)
160 | jekyll (>= 3.3, < 5.0)
161 | jekyll-relative-links (0.6.1)
162 | jekyll (>= 3.3, < 5.0)
163 | jekyll-remote-theme (0.4.3)
164 | addressable (~> 2.0)
165 | jekyll (>= 3.5, < 5.0)
166 | jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0)
167 | rubyzip (>= 1.3.0, < 3.0)
168 | jekyll-sass-converter (1.5.2)
169 | sass (~> 3.4)
170 | jekyll-seo-tag (2.8.0)
171 | jekyll (>= 3.8, < 5.0)
172 | jekyll-sitemap (1.4.0)
173 | jekyll (>= 3.7, < 5.0)
174 | jekyll-swiss (1.0.0)
175 | jekyll-theme-architect (0.2.0)
176 | jekyll (> 3.5, < 5.0)
177 | jekyll-seo-tag (~> 2.0)
178 | jekyll-theme-cayman (0.2.0)
179 | jekyll (> 3.5, < 5.0)
180 | jekyll-seo-tag (~> 2.0)
181 | jekyll-theme-dinky (0.2.0)
182 | jekyll (> 3.5, < 5.0)
183 | jekyll-seo-tag (~> 2.0)
184 | jekyll-theme-hacker (0.2.0)
185 | jekyll (> 3.5, < 5.0)
186 | jekyll-seo-tag (~> 2.0)
187 | jekyll-theme-leap-day (0.2.0)
188 | jekyll (> 3.5, < 5.0)
189 | jekyll-seo-tag (~> 2.0)
190 | jekyll-theme-merlot (0.2.0)
191 | jekyll (> 3.5, < 5.0)
192 | jekyll-seo-tag (~> 2.0)
193 | jekyll-theme-midnight (0.2.0)
194 | jekyll (> 3.5, < 5.0)
195 | jekyll-seo-tag (~> 2.0)
196 | jekyll-theme-minimal (0.2.0)
197 | jekyll (> 3.5, < 5.0)
198 | jekyll-seo-tag (~> 2.0)
199 | jekyll-theme-modernist (0.2.0)
200 | jekyll (> 3.5, < 5.0)
201 | jekyll-seo-tag (~> 2.0)
202 | jekyll-theme-primer (0.6.0)
203 | jekyll (> 3.5, < 5.0)
204 | jekyll-github-metadata (~> 2.9)
205 | jekyll-seo-tag (~> 2.0)
206 | jekyll-theme-slate (0.2.0)
207 | jekyll (> 3.5, < 5.0)
208 | jekyll-seo-tag (~> 2.0)
209 | jekyll-theme-tactile (0.2.0)
210 | jekyll (> 3.5, < 5.0)
211 | jekyll-seo-tag (~> 2.0)
212 | jekyll-theme-time-machine (0.2.0)
213 | jekyll (> 3.5, < 5.0)
214 | jekyll-seo-tag (~> 2.0)
215 | jekyll-titles-from-headings (0.5.3)
216 | jekyll (>= 3.3, < 5.0)
217 | jekyll-watch (2.2.1)
218 | listen (~> 3.0)
219 | jemoji (0.13.0)
220 | gemoji (>= 3, < 5)
221 | html-pipeline (~> 2.2)
222 | jekyll (>= 3.0, < 5.0)
223 | kramdown (2.4.0)
224 | rexml
225 | kramdown-parser-gfm (1.1.0)
226 | kramdown (~> 2.0)
227 | liquid (4.0.4)
228 | listen (3.9.0)
229 | rb-fsevent (~> 0.10, >= 0.10.3)
230 | rb-inotify (~> 0.9, >= 0.9.10)
231 | logger (1.6.0)
232 | mercenary (0.3.6)
233 | minima (2.5.1)
234 | jekyll (>= 3.5, < 5.0)
235 | jekyll-feed (~> 0.9)
236 | jekyll-seo-tag (~> 2.1)
237 | minitest (5.24.1)
238 | net-http (0.4.1)
239 | uri
240 | nokogiri (1.18.8-arm64-darwin)
241 | racc (~> 1.4)
242 | nokogiri (1.18.8-x86_64-darwin)
243 | racc (~> 1.4)
244 | nokogiri (1.18.8-x86_64-linux-gnu)
245 | racc (~> 1.4)
246 | octokit (4.25.1)
247 | faraday (>= 1, < 3)
248 | sawyer (~> 0.9)
249 | pathutil (0.16.2)
250 | forwardable-extended (~> 2.6)
251 | public_suffix (5.1.1)
252 | racc (1.8.1)
253 | rb-fsevent (0.11.2)
254 | rb-inotify (0.11.1)
255 | ffi (~> 1.0)
256 | rexml (3.3.9)
257 | rouge (3.30.0)
258 | rubyzip (2.3.2)
259 | safe_yaml (1.0.5)
260 | sass (3.7.4)
261 | sass-listen (~> 4.0.0)
262 | sass-listen (4.0.0)
263 | rb-fsevent (~> 0.9, >= 0.9.4)
264 | rb-inotify (~> 0.9, >= 0.9.7)
265 | sawyer (0.9.2)
266 | addressable (>= 2.3.5)
267 | faraday (>= 0.17.3, < 3)
268 | securerandom (0.3.1)
269 | simpleidn (0.2.3)
270 | terminal-table (1.8.0)
271 | unicode-display_width (~> 1.1, >= 1.1.1)
272 | typhoeus (1.4.1)
273 | ethon (>= 0.9.0)
274 | tzinfo (2.0.6)
275 | concurrent-ruby (~> 1.0)
276 | unicode-display_width (1.8.0)
277 | uri (0.13.2)
278 | webrick (1.8.2)
279 |
280 | PLATFORMS
281 | universal-darwin-20
282 | universal-darwin-22
283 | x86_64-linux
284 |
285 | DEPENDENCIES
286 | bulma-clean-theme
287 | github-pages
288 | jekyll-feed (~> 0.12)
289 | tzinfo (~> 1.2)
290 | tzinfo-data
291 | wdm (~> 0.2.0)
292 |
293 | BUNDLED WITH
294 | 2.2.17
295 |
--------------------------------------------------------------------------------
/webui/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Routing Number Search
7 |
142 |
143 |
144 |
145 |
Routing Number Search
146 |
147 |
148 | ACH Search
149 | Wire Search
150 |
151 |
152 | Search
153 |
154 |
155 |
156 |
157 |
233 |
234 |
235 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/api/client.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.0.2
2 | info:
3 | description: FED API is designed to create FEDACH and FEDWIRE dictionaries. The FEDACH dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive ACH entries. The FEDWIRE dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive WIRE entries. This project implements a modern REST HTTP API for FEDACH Dictionary and FEDWIRE Dictionary.
4 | version: v1
5 | title: FED API
6 | contact:
7 | name: FED API Support
8 | url: 'https://github.com/moov-io/fed'
9 | license:
10 | name: Apache 2.0
11 | url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
12 | servers:
13 | - url: 'http://localhost:8086'
14 | description: Local development
15 | tags:
16 | - name: FED
17 | description: FEDACH Dictionary and FEDWIRE Dictionary
18 | paths:
19 | /ping:
20 | get:
21 | tags:
22 | - FED
23 | summary: Ping the FED service to check if running
24 | operationId: ping
25 | responses:
26 | '200':
27 | description: Service is running properly
28 | content:
29 | text/plain:
30 | example: PONG
31 | /fed/ach/search:
32 | get:
33 | tags:
34 | - FED
35 | summary: Search FEDACH names and metadata
36 | operationId: searchFEDACH
37 | parameters:
38 | - name: X-Request-ID
39 | in: header
40 | description: Optional Request ID allows application developer to trace requests through the systems logs
41 | example: rs4f9915
42 | schema:
43 | type: string
44 | - name: X-User-ID
45 | in: header
46 | description: Optional User ID used to perform this search
47 | schema:
48 | type: string
49 | - name: name
50 | in: query
51 | schema:
52 | type: string
53 | example: Farmers
54 | description: FEDACH Financial Institution Name
55 | - name: routingNumber
56 | in: query
57 | schema:
58 | type: string
59 | example: 044112187
60 | description: FEDACH Routing Number for a Financial Institution
61 | - name: state
62 | in: query
63 | schema:
64 | type: string
65 | example: OH
66 | description: FEDACH Financial Institution State
67 | - name: city
68 | in: query
69 | schema:
70 | type: string
71 | example: CALDWELL
72 | description: FEDACH Financial Institution City
73 | - name: postalCode
74 | in: query
75 | schema:
76 | type: string
77 | example: 43724
78 | description: FEDACH Financial Institution Postal Code
79 | - name: limit
80 | in: query
81 | schema:
82 | type: integer
83 | example: 499
84 | description: Maximum results returned by a search
85 | responses:
86 | '200':
87 | description: FEDACH Participants returned from a search
88 | content:
89 | application/json:
90 | schema:
91 | $ref: '#/components/schemas/ACHDictionary'
92 | '400':
93 | description: Invalid, check error(s).
94 | content:
95 | application/json:
96 | schema:
97 | $ref: 'https://raw.githubusercontent.com/moov-io/base/master/api/common.yaml#/components/schemas/Error'
98 | '500':
99 | description: Internal error, check error(s) and report the issue.
100 | /fed/wire/search:
101 | get:
102 | tags:
103 | - FED
104 | summary: Search FEDWIRE names and metadata
105 | operationId: searchFEDWIRE
106 | parameters:
107 | - name: X-Request-ID
108 | in: header
109 | description: Optional Request ID allows application developer to trace requests through the systems logs
110 | example: rs4f9915
111 | schema:
112 | type: string
113 | - name: X-User-ID
114 | in: header
115 | description: Optional User ID used to perform this search
116 | schema:
117 | type: string
118 | - name: name
119 | in: query
120 | schema:
121 | type: string
122 | example: MIDWEST
123 | description: FEDWIRE Financial Institution Name
124 | - name: routingNumber
125 | in: query
126 | schema:
127 | type: string
128 | example: 091905114
129 | description: FEDWIRE Routing Number for a Financial Institution
130 | - name: state
131 | in: query
132 | schema:
133 | type: string
134 | example: IA
135 | description: FEDWIRE Financial Institution State
136 | - name: city
137 | in: query
138 | schema:
139 | type: string
140 | example: IOWA CITY
141 | description: FEDWIRE Financial Institution City
142 | - name: limit
143 | in: query
144 | schema:
145 | type: integer
146 | example: 499
147 | description: Maximum results returned by a search
148 | responses:
149 | '200':
150 | description: FEDWIRE Participants returned from a search
151 | content:
152 | application/json:
153 | schema:
154 | $ref: '#/components/schemas/WIREDictionary'
155 | '400':
156 | description: Invalid, check error(s).
157 | content:
158 | application/json:
159 | schema:
160 | $ref: 'https://raw.githubusercontent.com/moov-io/base/master/api/common.yaml#/components/schemas/Error'
161 | '500':
162 | description: Internal error, check error(s) and report the issue.
163 |
164 | components:
165 | schemas:
166 | ACHDictionary:
167 | description: Search results containing ACHDictionary of Participants
168 | properties:
169 | ACHParticipants:
170 | type: array
171 | items:
172 | $ref: '#/components/schemas/ACHParticipant'
173 | ACHParticipant:
174 | description: ACHParticipant holds a FedACH dir routing record as defined by Fed ACH Format. https://www.frbservices.org/EPaymentsDirectory/achFormat.html
175 | properties:
176 | routingNumber:
177 | type: string
178 | minLength: 9
179 | maxLength: 9
180 | description: The institution's routing number
181 | example: '044112187'
182 | officeCode:
183 | type: string
184 | minLength: 1
185 | maxLength: 1
186 | description: |
187 | Main/Head Office or Branch
188 |
189 | * `O` - Main
190 | * `B` - Branch
191 | enum:
192 | - O
193 | - B
194 | example: 'O'
195 | servicingFRBNumber:
196 | type: string
197 | minLength: 9
198 | maxLength: 9
199 | description: Servicing Fed's main office routing number
200 | example: '041000014'
201 | recordTypeCode:
202 | type: string
203 | minLength: 1
204 | maxLength: 1
205 | description: |
206 | The code indicating the ABA number to be used to route or send ACH items to the RDFI
207 |
208 | * `0` - Institution is a Federal Reserve Bank
209 | * `1` - Send items to customer routing number
210 | * `2` - Send items to customer using new routing number field
211 | enum:
212 | - 0
213 | - 1
214 | - 2
215 | example: '1'
216 | revised:
217 | type: string
218 | maxLength: 8
219 | description: |
220 | Date of last revision
221 |
222 | * YYYYMMDD
223 | * Blank
224 | example: '20190311'
225 | newRoutingNumber:
226 | type: string
227 | minLength: 9
228 | maxLength: 9
229 | description: Financial Institution's new routing number resulting from a merger or renumber
230 | example: '000000000'
231 | customerName:
232 | type: string
233 | maxLength: 36
234 | description: Financial Institution Name
235 | example: FARMERS & MERCHANTS BANK
236 | achLocation:
237 | $ref: '#/components/schemas/ACHLocation'
238 | phoneNumber:
239 | type: string
240 | minLength: 10
241 | maxLength: 10
242 | description: The Financial Institution's phone number
243 | example: '7407325621'
244 | statusCode:
245 | type: string
246 | minLength: 1
247 | maxLength: 1
248 | description: |
249 | Code is based on the customers receiver code
250 |
251 | * `1` - Receives Gov/Comm
252 | enum:
253 | - 1
254 | example: '1'
255 | viewCode:
256 | type: string
257 | minLength: 1
258 | maxLength: 1
259 | description: |-
260 | Code is current view
261 |
262 | * `1` - Current view
263 | enum:
264 | - 1
265 | example: '1'
266 | cleanName:
267 | type: string
268 | description: Normalized name of ACH participant
269 | example: Chase
270 | ACHLocation:
271 | description: ACHLocation is the FEDACH delivery address
272 | properties:
273 | address:
274 | type: string
275 | maxLength: 36
276 | description: Street Address
277 | example: '430 NORTH ST'
278 | city:
279 | type: string
280 | maxLength: 20
281 | description: City
282 | example: 'CALDWELL'
283 | state:
284 | type: string
285 | minLength: 2
286 | maxLength: 2
287 | description: State
288 | example: 'OH'
289 | postalCode:
290 | type: string
291 | minLength: 5
292 | maxLength: 5
293 | description: Postal Code
294 | example: '43724'
295 | postalExtension:
296 | type: string
297 | minLength: 4
298 | maxLength: 4
299 | description: Postal Code Extension
300 | example: '0000'
301 | WIREDictionary:
302 | description: Search results containing WIREDictionary of Participants
303 | properties:
304 | WIREParticipants:
305 | type: array
306 | items:
307 | $ref: '#/components/schemas/WIREParticipant'
308 | WIREParticipant:
309 | description: WIREParticipant holds a FedWIRE dir routing record as defined by Fed WIRE Format. https://frbservices.org/EPaymentsDirectory/fedwireFormat.html
310 | properties:
311 | routingNumber:
312 | type: string
313 | minLength: 9
314 | maxLength: 9
315 | description: The institution's routing number
316 | example: '091905114'
317 | telegraphicName:
318 | type: string
319 | maxLength: 18
320 | description: Short name of financial institution
321 | example: 'MIDWESTONE B&T'
322 | customerName:
323 | type: string
324 | maxLength: 36
325 | description: Financial Institution Name
326 | example: 'MIDWESTONE BK'
327 | wireLocation:
328 | $ref: '#/components/schemas/WIRELocation'
329 | fundsTransferStatus:
330 | type: string
331 | minLength: 1
332 | maxLength: 1
333 | description: |
334 | Designates funds transfer status
335 |
336 | * `Y` - Eligible
337 | * `N` - Ineligible
338 | enum:
339 | - Y
340 | - N
341 | example: 'Y'
342 | fundsSettlementOnlyStatus:
343 | type: string
344 | maxLength: 1
345 | description: |
346 | Designates funds settlement only status
347 |
348 | * `S` - Settlement-Only
349 | enum:
350 | - S
351 | example: ''
352 | bookEntrySecuritiesTransferStatus:
353 | type: string
354 | minLength: 1
355 | maxLength: 1
356 | description: |
357 | Designates book entry securities transfer status
358 |
359 | * `Y` - Eligible
360 | * `N` - Ineligible
361 | enum:
362 | - Y
363 | - N
364 | example: 'N'
365 | date:
366 | type: string
367 | maxLength: 8
368 | description: |
369 | Date of last revision
370 |
371 | * YYYYMMDD
372 | * Blank
373 | example: '20190401'
374 | cleanName:
375 | type: string
376 | description: Normalized name of Wire participant
377 | example: Chase
378 | WIRELocation:
379 | description: WIRELocation is the FEDWIRE delivery address
380 | properties:
381 | city:
382 | type: string
383 | maxLength: 25
384 | description: City
385 | example: 'IOWA CITY'
386 | state:
387 | type: string
388 | minLength: 2
389 | maxLength: 2
390 | description: State
391 | example: 'IA'
392 |
--------------------------------------------------------------------------------
/openapi.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.0.2
2 | info:
3 | description: FED API is designed to create FEDACH and FEDWIRE dictionaries. The FEDACH dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive ACH entries. The FEDWIRE dictionary contains receiving depository financial institutions (RDFI’s) which are qualified to receive WIRE entries. This project implements a modern REST HTTP API for FEDACH Dictionary and FEDWIRE Dictionary.
4 | version: v1
5 | title: FED API
6 | contact:
7 | name: FED API Support
8 | url: 'https://github.com/moov-io/fed'
9 | license:
10 | name: Apache 2.0
11 | url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
12 | servers:
13 | - url: 'http://localhost:8086'
14 | description: Local development
15 | tags:
16 | - name: FED
17 | description: FEDACH Dictionary and FEDWIRE Dictionary
18 | paths:
19 | /ping:
20 | get:
21 | tags:
22 | - FED
23 | summary: Ping the FED service to check if running
24 | operationId: ping
25 | responses:
26 | '200':
27 | description: Service is running properly
28 | content:
29 | text/plain:
30 | example: PONG
31 | /fed/ach/search:
32 | get:
33 | tags:
34 | - FED
35 | summary: Search FEDACH names and metadata
36 | operationId: searchFEDACH
37 | parameters:
38 | - name: X-Request-ID
39 | in: header
40 | description: Optional Request ID allows application developer to trace requests through the systems logs
41 | example: rs4f9915
42 | schema:
43 | type: string
44 | - name: X-User-ID
45 | in: header
46 | description: Optional User ID used to perform this search
47 | schema:
48 | type: string
49 | - name: name
50 | in: query
51 | schema:
52 | type: string
53 | example: Farmers
54 | description: FEDACH Financial Institution Name
55 | - name: routingNumber
56 | in: query
57 | schema:
58 | type: string
59 | example: 044112187
60 | description: FEDACH Routing Number for a Financial Institution
61 | - name: state
62 | in: query
63 | schema:
64 | type: string
65 | example: OH
66 | description: FEDACH Financial Institution State
67 | - name: city
68 | in: query
69 | schema:
70 | type: string
71 | example: CALDWELL
72 | description: FEDACH Financial Institution City
73 | - name: postalCode
74 | in: query
75 | schema:
76 | type: string
77 | example: 43724
78 | description: FEDACH Financial Institution Postal Code
79 | - name: limit
80 | in: query
81 | schema:
82 | type: integer
83 | example: 499
84 | description: Maximum results returned by a search
85 | responses:
86 | '200':
87 | description: FEDACH Participants returned from a search
88 | content:
89 | application/json:
90 | schema:
91 | $ref: '#/components/schemas/ACHDictionary'
92 | '400':
93 | description: Invalid, check error(s).
94 | content:
95 | application/json:
96 | schema:
97 | $ref: 'https://raw.githubusercontent.com/moov-io/base/master/api/common.yaml#/components/schemas/Error'
98 | '500':
99 | description: Internal error, check error(s) and report the issue.
100 | /fed/wire/search:
101 | get:
102 | tags:
103 | - FED
104 | summary: Search FEDWIRE names and metadata
105 | operationId: searchFEDWIRE
106 | parameters:
107 | - name: X-Request-ID
108 | in: header
109 | description: Optional Request ID allows application developer to trace requests through the systems logs
110 | example: rs4f9915
111 | schema:
112 | type: string
113 | - name: X-User-ID
114 | in: header
115 | description: Optional User ID used to perform this search
116 | schema:
117 | type: string
118 | - name: name
119 | in: query
120 | schema:
121 | type: string
122 | example: MIDWEST
123 | description: FEDWIRE Financial Institution Name
124 | - name: routingNumber
125 | in: query
126 | schema:
127 | type: string
128 | example: 091905114
129 | description: FEDWIRE Routing Number for a Financial Institution
130 | - name: state
131 | in: query
132 | schema:
133 | type: string
134 | example: IA
135 | description: FEDWIRE Financial Institution State
136 | - name: city
137 | in: query
138 | schema:
139 | type: string
140 | example: IOWA CITY
141 | description: FEDWIRE Financial Institution City
142 | - name: limit
143 | in: query
144 | schema:
145 | type: integer
146 | example: 499
147 | description: Maximum results returned by a search
148 | responses:
149 | '200':
150 | description: FEDWIRE Participants returned from a search
151 | content:
152 | application/json:
153 | schema:
154 | $ref: '#/components/schemas/WIREDictionary'
155 | '400':
156 | description: Invalid, check error(s).
157 | content:
158 | application/json:
159 | schema:
160 | $ref: 'https://raw.githubusercontent.com/moov-io/base/master/api/common.yaml#/components/schemas/Error'
161 | '500':
162 | description: Internal error, check error(s) and report the issue.
163 |
164 | components:
165 | schemas:
166 | ACHDictionary:
167 | description: Search results containing ACHDictionary of Participants
168 | properties:
169 | achParticipants:
170 | type: array
171 | items:
172 | $ref: '#/components/schemas/ACHParticipant'
173 | stats:
174 | $ref: '#/components/schemas/ListStats'
175 | ACHParticipant:
176 | description: ACHParticipant holds a FedACH dir routing record as defined by Fed ACH Format. https://www.frbservices.org/EPaymentsDirectory/achFormat.html
177 | properties:
178 | routingNumber:
179 | type: string
180 | minLength: 9
181 | maxLength: 9
182 | description: The institution's routing number
183 | example: '044112187'
184 | officeCode:
185 | type: string
186 | minLength: 1
187 | maxLength: 1
188 | description: |
189 | Main/Head Office or Branch
190 |
191 | * `O` - Main
192 | * `B` - Branch
193 | enum:
194 | - O
195 | - B
196 | example: 'O'
197 | servicingFRBNumber:
198 | type: string
199 | minLength: 9
200 | maxLength: 9
201 | description: Servicing Fed's main office routing number
202 | example: '041000014'
203 | recordTypeCode:
204 | type: string
205 | minLength: 1
206 | maxLength: 1
207 | description: |
208 | The code indicating the ABA number to be used to route or send ACH items to the RDFI
209 |
210 | * `0` - Institution is a Federal Reserve Bank
211 | * `1` - Send items to customer routing number
212 | * `2` - Send items to customer using new routing number field
213 | enum:
214 | - 0
215 | - 1
216 | - 2
217 | example: '1'
218 | revised:
219 | type: string
220 | maxLength: 8
221 | description: |
222 | Date of last revision
223 |
224 | * YYYYMMDD
225 | * Blank
226 | example: '20190311'
227 | newRoutingNumber:
228 | type: string
229 | minLength: 9
230 | maxLength: 9
231 | description: Financial Institution's new routing number resulting from a merger or renumber
232 | example: '000000000'
233 | customerName:
234 | type: string
235 | maxLength: 36
236 | description: Financial Institution Name
237 | example: FARMERS & MERCHANTS BANK
238 | achLocation:
239 | $ref: '#/components/schemas/ACHLocation'
240 | phoneNumber:
241 | type: string
242 | minLength: 10
243 | maxLength: 10
244 | description: The Financial Institution's phone number
245 | example: '7407325621'
246 | statusCode:
247 | type: string
248 | minLength: 1
249 | maxLength: 1
250 | description: |
251 | Code is based on the customers receiver code
252 |
253 | * `1` - Receives Gov/Comm
254 | enum:
255 | - 1
256 | example: '1'
257 | viewCode:
258 | type: string
259 | minLength: 1
260 | maxLength: 1
261 | description: |-
262 | Code is current view
263 |
264 | * `1` - Current view
265 | enum:
266 | - 1
267 | example: '1'
268 | ACHLocation:
269 | description: ACHLocation is the FEDACH delivery address
270 | properties:
271 | address:
272 | type: string
273 | maxLength: 36
274 | description: Street Address
275 | example: '430 NORTH ST'
276 | city:
277 | type: string
278 | maxLength: 20
279 | description: City
280 | example: 'CALDWELL'
281 | state:
282 | type: string
283 | minLength: 2
284 | maxLength: 2
285 | description: State
286 | example: 'OH'
287 | postalCode:
288 | type: string
289 | minLength: 5
290 | maxLength: 5
291 | description: Postal Code
292 | example: '43724'
293 | postalExtension:
294 | type: string
295 | minLength: 4
296 | maxLength: 4
297 | description: Postal Code Extension
298 | example: '0000'
299 |
300 | ListStats:
301 | type: object
302 | properties:
303 | records:
304 | type: integer
305 | example: 17124
306 | latest:
307 | type: string
308 | format: date-time
309 |
310 | WIREDictionary:
311 | description: Search results containing WIREDictionary of Participants
312 | properties:
313 | wireParticipants:
314 | type: array
315 | items:
316 | $ref: '#/components/schemas/WIREParticipant'
317 | stats:
318 | $ref: '#/components/schemas/ListStats'
319 | WIREParticipant:
320 | description: WIREParticipant holds a FedWIRE dir routing record as defined by Fed WIRE Format. https://frbservices.org/EPaymentsDirectory/fedwireFormat.html
321 | properties:
322 | routingNumber:
323 | type: string
324 | minLength: 9
325 | maxLength: 9
326 | description: The institution's routing number
327 | example: '091905114'
328 | telegraphicName:
329 | type: string
330 | maxLength: 18
331 | description: Short name of financial institution
332 | example: 'MIDWESTONE B&T'
333 | customerName:
334 | type: string
335 | maxLength: 36
336 | description: Financial Institution Name
337 | example: 'MIDWESTONE BK'
338 | wireLocation:
339 | $ref: '#/components/schemas/WIRELocation'
340 | fundsTransferStatus:
341 | type: string
342 | minLength: 1
343 | maxLength: 1
344 | description: |
345 | Designates funds transfer status
346 |
347 | * `Y` - Eligible
348 | * `N` - Ineligible
349 | enum:
350 | - Y
351 | - N
352 | example: 'Y'
353 | fundsSettlementOnlyStatus:
354 | type: string
355 | maxLength: 1
356 | description: |
357 | Designates funds settlement only status
358 |
359 | * `S` - Settlement-Only
360 | enum:
361 | - S
362 | example: ''
363 | bookEntrySecuritiesTransferStatus:
364 | type: string
365 | minLength: 1
366 | maxLength: 1
367 | description: |
368 | Designates book entry securities transfer status
369 |
370 | * `Y` - Eligible
371 | * `N` - Ineligible
372 | enum:
373 | - Y
374 | - N
375 | example: 'N'
376 | date:
377 | type: string
378 | maxLength: 8
379 | description: |
380 | Date of last revision
381 |
382 | * YYYYMMDD
383 | * Blank
384 | example: '20190401'
385 | WIRELocation:
386 | description: WIRELocation is the FEDWIRE delivery address
387 | properties:
388 | city:
389 | type: string
390 | maxLength: 25
391 | description: City
392 | example: 'IOWA CITY'
393 | state:
394 | type: string
395 | minLength: 2
396 | maxLength: 2
397 | description: State
398 | example: 'IA'
399 |
--------------------------------------------------------------------------------