-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
jcobhams
authored and
jcobhams
committed
Dec 31, 2020
0 parents
commit c1380d8
Showing
9 changed files
with
1,079 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.idea | ||
cover.out | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
Copyright 2020 Joseph Cobhams | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
# GoMono | ||
GoMono is a Golang API wrapper around Mono's REST API. | ||
|
||
It implements the `Authentication`, `Account`, `User` and `Misc` endpoints as documented on the [Mono's v1 API docs](https://docs.mono.co/reference) | ||
|
||
## Install | ||
``` | ||
$ go get github.com/jcobhams/gomono | ||
``` | ||
|
||
## Usage | ||
|
||
```go | ||
package main | ||
|
||
import ( | ||
"github.com/jcobhams/gomono" | ||
"net/http" | ||
) | ||
|
||
func main() { | ||
gm, err := gomono.New(gomono.NewDefaultConfig("YOUR_SECRET_KEY")) // If you'd like to use the default config | ||
|
||
//Or you could configure the HTTP Client how you'd like (timeouts, Transports etc | ||
//gm, err := gomono.New(gomono.Config{ | ||
// SecretKey: "YOUR_SECRET_KEY", | ||
// HttpClient: &http.Client{ | ||
// Transport: nil, | ||
// CheckRedirect: nil, | ||
// Jar: nil, | ||
// Timeout: 0, | ||
// }, | ||
// ApiUrl: "https://api.withmono.com", | ||
//}) | ||
|
||
if err != nil { | ||
//do something with error | ||
} | ||
|
||
//Exchange Token | ||
id, err := gm.ExchangeToken("CODE") | ||
|
||
//Get Bank Account Details | ||
infResponse, err := gm.Information(id) | ||
|
||
//Get Bank Statement - Period => in months (1-12) | output => json or pdf | ||
stmtResponse, err := gm.Statement(id, "period", "output") | ||
|
||
// Query PDF Job Status - If the Statement call above had a pdf output, the response will contain a PDF struct with and ID | ||
pdfStmtResponse, err := gm.PdfStatementJobStatus(id, stmtResponse.PDF.ID) | ||
|
||
// Get user transactions - tnxType => debit or credit | paginate => bool | ||
tnxResponse, err := gm.Transactions(id, "start", "end", "narration", "tnxType", "paginate") | ||
|
||
// Get Credit Transactions | ||
crdTnxResponse, err := gm.CreditTransactions(id) | ||
|
||
// Get Debit Transactions | ||
dbtTnxResponse, err := gm.DebitTransactions(id) | ||
|
||
// Get Income Information | ||
incResponse, err := gm.Income(id) | ||
|
||
// Get Identity Information | ||
idyResponse, err := gm.Identity(id) | ||
|
||
// Get Institutions List | ||
insResponse, err := gm.Institutions() | ||
|
||
// LookupBVN | ||
bvnResponse, err := gm.LookupBVN("1234567890") | ||
|
||
} | ||
|
||
``` | ||
|
||
In all the examples, error handling has been ignore/suppressed. Please handle errors properly to avoid `nil pointer` panics. | ||
|
||
## Integration Testing | ||
`Gomono` is an interface that can easily be mocked to ease testing. | ||
|
||
You could also use the explicit configuration option shown earlier to create your clients. | ||
|
||
That way you can set a test API Url or intercept HTTP calls using a fake http client - Whatever works best for you :) | ||
|
||
## What's Not Covered/Supported | ||
1. Data Sync / Reauth Code | ||
|
||
## Run Tests | ||
go test -race -v -coverprofile cover.out | ||
|
||
## View Coverage | ||
go tool cover -html=cover.out |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
// Copyright 2020 Joseph Cobhams. All rights reserved. | ||
// Use of this source code is governed by the MIT license | ||
// that can be found in the LICENSE.md file. | ||
// | ||
package gomono | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"net/url" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
//Auth Endpoints | ||
|
||
//ExchangeToken - https://docs.mono.co/reference#authentication-endpoint | ||
func (g *gomono) ExchangeToken(code string) (string, error) { | ||
if code == "" { | ||
return "", errors.New("gomono: Code cannot be blank") | ||
} | ||
|
||
payload, err := g.preparePayload(map[string]string{"code": code}) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
respTarget := make(map[string]string) | ||
|
||
err = g.makeRequest("POST", fmt.Sprintf("%v/account/auth", g.apiUrl), payload, nil, &respTarget) | ||
if err != nil { | ||
return "", err | ||
} | ||
return respTarget["id"], nil | ||
} | ||
|
||
//Account Endpoints | ||
|
||
//Information - https://docs.mono.co/reference#bank-account-details | ||
func (g *gomono) Information(id string) (*InformationResponse, error) { | ||
if id == "" { | ||
return nil, errors.New("gomono: ID is required") | ||
} | ||
|
||
var respTarget InformationResponse | ||
err := g.makeRequest("POST", fmt.Sprintf("%v/accounts/%v", g.apiUrl, id), nil, nil, &respTarget) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &respTarget, nil | ||
} | ||
|
||
//Statement - https://docs.mono.co/reference#bank-statement | ||
func (g *gomono) Statement(id, period, output string) (*StatementResponse, error) { | ||
if id == "" { | ||
return nil, errors.New("gomono: ID is required") | ||
} | ||
|
||
output = strings.ToLower(output) | ||
if output != "" && output != "pdf" && output != "json" { | ||
return nil, errors.New("gomono: only json or pdf output supported") | ||
} | ||
|
||
params := url.Values{} | ||
if output != "" { | ||
params.Add("output", output) | ||
} | ||
|
||
if period != "" { | ||
params.Add("period", period) | ||
} | ||
|
||
endpoint := fmt.Sprintf("%v/accounts/%v/statement?%v", g.apiUrl, id, params.Encode()) | ||
|
||
var result StatementResponse | ||
|
||
switch output { | ||
case "pdf": | ||
var pdfRespTarget StatementResponsePdf | ||
err := g.makeRequest("GET", endpoint, nil, nil, &pdfRespTarget) | ||
if err != nil { | ||
return nil, err | ||
} | ||
result.PDF = &pdfRespTarget | ||
case "json": | ||
var jsonRespTarget StatementResponseJson | ||
err := g.makeRequest("POST", endpoint, nil, nil, &jsonRespTarget) | ||
if err != nil { | ||
return nil, err | ||
} | ||
result.JSON = &jsonRespTarget | ||
} | ||
|
||
return &result, nil | ||
} | ||
|
||
//PdfStatementJobStatus - https://docs.mono.co/reference#poll-statement-status | ||
func (g *gomono) PdfStatementJobStatus(id, jobId string) (*StatementResponsePdf, error) { | ||
if id == "" { | ||
return nil, errors.New("gomono: ID is required") | ||
} | ||
|
||
if jobId == "" { | ||
return nil, errors.New("gomono: JOBID is required") | ||
} | ||
|
||
var respTarget StatementResponsePdf | ||
err := g.makeRequest("GET", fmt.Sprintf("%v/accounts/%v/statement/jobs/%v?", g.apiUrl, id, jobId), nil, nil, &respTarget) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &respTarget, nil | ||
} | ||
|
||
// User Endpoints | ||
|
||
//Transactions - https://docs.mono.co/reference#poll-statement-status | ||
func (g *gomono) Transactions(id, start, end, narration, tnxType string, paginate bool) (*TransactionsResponse, error) { | ||
if id == "" { | ||
return nil, errors.New("gomono: ID is required") | ||
} | ||
|
||
params := url.Values{} | ||
if start != "" { | ||
params.Add("start", start) | ||
} | ||
|
||
if end != "" { | ||
params.Add("end", end) | ||
} | ||
|
||
if narration != "" { | ||
params.Add("narration", narration) | ||
} | ||
|
||
if tnxType != "" { | ||
params.Add("type", tnxType) | ||
} | ||
|
||
params.Add("paginate", strconv.FormatBool(paginate)) | ||
|
||
var respTarget TransactionsResponse | ||
err := g.makeRequest("GET", fmt.Sprintf("%v/accounts/%v/transactions?%v", g.apiUrl, id, params.Encode()), nil, nil, &respTarget) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &respTarget, nil | ||
} | ||
|
||
//CreditTransactions - https://docs.mono.co/reference#credits | ||
func (g *gomono) CreditTransactions(id string) (*TransactionByTypeResponse, error) { | ||
return g.transactionByType(id, "credit") | ||
} | ||
|
||
//DebitTransactions - https://docs.mono.co/reference#debits | ||
func (g *gomono) DebitTransactions(id string) (*TransactionByTypeResponse, error) { | ||
return g.transactionByType(id, "debit") | ||
} | ||
|
||
func (g *gomono) transactionByType(id, tnxType string) (*TransactionByTypeResponse, error) { | ||
if id == "" { | ||
return nil, errors.New("gomono: ID is required") | ||
} | ||
|
||
var respTarget TransactionByTypeResponse | ||
err := g.makeRequest("GET", fmt.Sprintf("%v/accounts/%v/%v", g.apiUrl, id, tnxType), nil, nil, &respTarget) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &respTarget, nil | ||
} | ||
|
||
//Income - https://docs.mono.co/reference#income | ||
func (g *gomono) Income(id string) (*IncomeResponse, error) { | ||
if id == "" { | ||
return nil, errors.New("gomono: ID is required") | ||
} | ||
|
||
var respTarget IncomeResponse | ||
err := g.makeRequest("GET", fmt.Sprintf("%v/accounts/%v/income", g.apiUrl, id), nil, nil, &respTarget) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &respTarget, nil | ||
} | ||
|
||
//Identity - https://docs.mono.co/reference#identity | ||
func (g *gomono) Identity(id string) (*IdentityResponse, error) { | ||
if id == "" { | ||
return nil, errors.New("gomono: ID is required") | ||
} | ||
|
||
var respTarget IdentityResponse | ||
err := g.makeRequest("GET", fmt.Sprintf("%v/accounts/%v/identity", g.apiUrl, id), nil, nil, &respTarget) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &respTarget, nil | ||
} | ||
|
||
//MISC Endpoints | ||
|
||
//Institutions - https://docs.mono.co/reference#list-institutions | ||
func (g *gomono) Institutions() (*InstitutionsResponse, error) { | ||
var respTarget []Institution | ||
err := g.makeRequest("GET", fmt.Sprintf("%v/coverage", g.apiUrl), nil, nil, &respTarget) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &InstitutionsResponse{ | ||
Institutions: respTarget, | ||
}, nil | ||
} | ||
|
||
func (g *gomono) LookupBVN(bvn string) (*IdentityResponse, error) { | ||
if bvn == "" { | ||
return nil, errors.New("gomono: BVN is required") | ||
} | ||
|
||
payload, err := g.preparePayload(map[string]string{"bvn": bvn}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var respTarget IdentityResponse | ||
err = g.makeRequest("POST", fmt.Sprintf("%v/v1/lookup/bvn/identity", g.apiUrl), payload, nil, &respTarget) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &respTarget, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
module github.com/jcobhams/gomono | ||
|
||
go 1.14 | ||
|
||
require ( | ||
github.com/stretchr/objx v0.3.0 // indirect | ||
github.com/stretchr/testify v1.6.1 | ||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= | ||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= | ||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= | ||
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= | ||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= | ||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= | ||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
Oops, something went wrong.