2017-04-10 20:36:47 -04:00
|
|
|
# OFXGo
|
|
|
|
|
2017-04-08 11:30:55 -04:00
|
|
|
[![Go Report Card](https://goreportcard.com/badge/github.com/aclindsa/ofxgo)](https://goreportcard.com/report/github.com/aclindsa/ofxgo)
|
2020-11-25 07:11:24 -05:00
|
|
|
[![Build Status](https://github.com/aclindsa/ofxgo/workflows/ofxgo%20CI%20Test/badge.svg?branch=master)](https://github.com/aclindsa/ofxgo/actions?query=workflow%3A%22ofxgo+CI+Test%22+branch%3Amaster)
|
2017-04-10 08:17:34 -04:00
|
|
|
[![Coverage Status](https://coveralls.io/repos/github/aclindsa/ofxgo/badge.svg?branch=master)](https://coveralls.io/github/aclindsa/ofxgo?branch=master)
|
2020-10-06 23:13:23 -04:00
|
|
|
[![PkgGoDev](https://pkg.go.dev/badge/github.com/aclindsa?ofxgo)](https://pkg.go.dev/github.com/aclindsa/ofxgo)
|
2017-04-08 11:14:25 -04:00
|
|
|
|
2017-04-10 20:36:47 -04:00
|
|
|
**OFXGo** is a library for querying OFX servers and/or parsing the responses. It
|
|
|
|
also provides an example command-line client to demonstrate the use of the
|
|
|
|
library.
|
2017-03-23 05:48:01 -04:00
|
|
|
|
|
|
|
## Goals
|
|
|
|
|
2017-03-26 20:44:55 -04:00
|
|
|
The main purpose of this project is to provide a library to make it easier to
|
|
|
|
query financial information with OFX from the comfort of Golang, without having
|
2021-01-03 03:21:01 -05:00
|
|
|
to marshal/unmarshal to SGML or XML. The library does _not_ intend to abstract
|
2017-04-10 20:36:47 -04:00
|
|
|
away all of the details of the OFX specification, which would be difficult to do
|
|
|
|
well. Instead, it exposes the OFX SGML/XML hierarchy as structs which mostly
|
2017-06-05 21:12:04 -04:00
|
|
|
resemble it. Its primary goal is to enable the creation of other personal
|
|
|
|
finance software in Go (as it was created to allow me to fetch OFX transactions
|
|
|
|
for my own project, [MoneyGo](https://github.com/aclindsa/moneygo)).
|
2017-03-26 20:44:55 -04:00
|
|
|
|
|
|
|
Because the OFX specification is rather... 'comprehensive,' it can be difficult
|
|
|
|
for those unfamiliar with it to figure out where to start. To that end, I have
|
|
|
|
created a sample command-line client which uses the library to do simple tasks
|
|
|
|
(currently it does little more than list accounts and query for balances and
|
|
|
|
transactions). My hope is that by studying its code, new users will be able to
|
|
|
|
figure out how to use the library much faster than staring at the OFX
|
2021-10-17 21:48:23 -04:00
|
|
|
specification (or this library's [API
|
|
|
|
documentation](https://pkg.go.dev/github.com/aclindsa/ofxgo)). The command-line client
|
2017-03-26 20:44:55 -04:00
|
|
|
also serves as an easy way for me to test/debug the library with actual
|
|
|
|
financial institutions, which frequently have 'quirks' in their implementations.
|
|
|
|
The command-line client can be found in the [cmd/ofx
|
|
|
|
directory](https://github.com/aclindsa/ofxgo/tree/master/cmd/ofx) of this
|
|
|
|
repository.
|
|
|
|
|
|
|
|
## Library documentation
|
2017-03-23 05:48:01 -04:00
|
|
|
|
2017-03-26 20:44:55 -04:00
|
|
|
Documentation can be found with the `go doc` tool, or at
|
2020-10-06 22:40:24 -04:00
|
|
|
https://pkg.go.dev/github.com/aclindsa/ofxgo
|
2017-03-26 20:44:55 -04:00
|
|
|
|
2017-11-18 19:43:41 -05:00
|
|
|
## Example Usage
|
|
|
|
|
|
|
|
The following code snippet demonstrates how to use OFXGo to query and parse
|
|
|
|
OFX code from a checking account, printing the balance and returned transactions:
|
|
|
|
|
|
|
|
```go
|
2018-10-02 20:55:25 -04:00
|
|
|
client := ofxgo.BasicClient{} // Accept the default Client settings
|
2017-11-18 19:43:41 -05:00
|
|
|
|
2018-04-14 09:06:32 -04:00
|
|
|
// These values are specific to your bank
|
2017-11-18 19:43:41 -05:00
|
|
|
var query ofxgo.Request
|
|
|
|
query.URL = "https://secu.example.com/ofx"
|
|
|
|
query.Signon.Org = ofxgo.String("SECU")
|
|
|
|
query.Signon.Fid = ofxgo.String("1234")
|
|
|
|
|
2018-04-14 09:06:32 -04:00
|
|
|
// Set your username/password
|
2017-11-18 19:43:41 -05:00
|
|
|
query.Signon.UserID = ofxgo.String("username")
|
|
|
|
query.Signon.UserPass = ofxgo.String("hunter2")
|
|
|
|
|
2018-04-14 09:06:32 -04:00
|
|
|
uid, _ := ofxgo.RandomUID() // Handle error in real code
|
|
|
|
query.Bank = append(query.Bank, &ofxgo.StatementRequest{
|
2017-11-18 19:43:41 -05:00
|
|
|
TrnUID: *uid,
|
|
|
|
BankAcctFrom: ofxgo.BankAcct{
|
2018-04-14 09:06:32 -04:00
|
|
|
BankID: ofxgo.String("123456789"), // Possibly your routing number
|
|
|
|
AcctID: ofxgo.String("00011122233"), // Possibly your account number
|
2017-11-18 19:43:41 -05:00
|
|
|
AcctType: ofxgo.AcctTypeChecking,
|
|
|
|
},
|
2018-04-14 09:06:32 -04:00
|
|
|
Include: true, // Include transactions (instead of only balance information)
|
2017-11-18 19:43:41 -05:00
|
|
|
})
|
|
|
|
|
2018-04-14 09:06:32 -04:00
|
|
|
response, _ := client.Request(&query) // Handle error in real code
|
2017-11-18 19:43:41 -05:00
|
|
|
|
2018-04-14 09:06:32 -04:00
|
|
|
// Was there an OFX error while processing our request?
|
2017-11-18 19:43:41 -05:00
|
|
|
if response.Signon.Status.Code != 0 {
|
|
|
|
meaning, _ := response.Signon.Status.CodeMeaning()
|
|
|
|
fmt.Printf("Nonzero signon status (%d: %s) with message: %s\n", response.Signon.Status.Code, meaning, response.Signon.Status.Message)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(response.Bank) < 1 {
|
|
|
|
fmt.Println("No banking messages received")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
if stmt, ok := response.Bank[0].(*ofxgo.StatementResponse); ok {
|
|
|
|
fmt.Printf("Balance: %s %s (as of %s)\n", stmt.BalAmt, stmt.CurDef, stmt.DtAsOf)
|
|
|
|
fmt.Println("Transactions:")
|
|
|
|
for _, tran := range stmt.BankTranList.Transactions {
|
2018-04-15 01:28:30 -04:00
|
|
|
currency := stmt.CurDef
|
2017-11-18 19:43:41 -05:00
|
|
|
if ok, _ := tran.Currency.Valid(); ok {
|
|
|
|
currency = tran.Currency.CurSym
|
|
|
|
}
|
|
|
|
fmt.Printf("%s %-15s %-11s %s%s%s\n", tran.DtPosted, tran.TrnAmt.String()+" "+currency.String(), tran.TrnType, tran.Name, tran.Payee.Name, tran.Memo)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2021-01-03 03:21:01 -05:00
|
|
|
Similarly, if you have an OFX file available locally, you can parse it directly:
|
|
|
|
|
|
|
|
```go
|
|
|
|
func main() {
|
|
|
|
f, err := os.Open("./transactions.qfx")
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("can't open file: %v\n", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
resp, err := ofxgo.ParseResponse(f)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("can't parse response: %v\n", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// do something with resp (*ofxgo.Response)
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2017-10-27 09:03:57 -04:00
|
|
|
## Requirements
|
|
|
|
|
2020-11-17 09:57:15 -05:00
|
|
|
OFXGo requires go >= 1.12
|
2017-10-27 09:03:57 -04:00
|
|
|
|
2017-03-26 20:44:55 -04:00
|
|
|
## Using the command-line client
|
2017-03-23 05:48:01 -04:00
|
|
|
|
|
|
|
To install the command-line client and test it out, you may do the following:
|
|
|
|
|
|
|
|
$ go get -v github.com/aclindsa/ofxgo/cmd/ofx && go install -v github.com/aclindsa/ofxgo/cmd/ofx
|
2017-03-26 20:44:55 -04:00
|
|
|
|
|
|
|
Once installed (at ~/go/bin/ofx by default, if you haven't set $GOPATH), the
|
|
|
|
command's usage should help you to use it (`./ofx --help` for a listing of the
|
|
|
|
available subcommands and their purposes, `./ofx subcommand --help` for
|
|
|
|
individual subcommand usage).
|