-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
9 changed files
with
349 additions
and
28 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 |
---|---|---|
@@ -1 +1,3 @@ | ||
/exchange-rates | ||
/check-iban | ||
/check-vat |
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
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
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,20 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
|
||
"github.com/pieterclaerhout/go-finance" | ||
) | ||
|
||
func main() { | ||
|
||
info, err := finance.CheckIBAN("738120256174") | ||
if err != nil { | ||
fmt.Println("ERROR:", err.Error()) | ||
os.Exit(1) | ||
} | ||
|
||
fmt.Println(info) | ||
|
||
} |
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,111 @@ | ||
package finance | ||
|
||
import ( | ||
"encoding/xml" | ||
"io/ioutil" | ||
"net/http" | ||
"net/url" | ||
"strings" | ||
"time" | ||
|
||
"github.com/pkg/errors" | ||
) | ||
|
||
// DefaultIBANBICServiceURL is the default IBANBIC service URL to use | ||
const DefaultIBANBICServiceURL = "https://www.ibanbic.be/IBANBIC.asmx" | ||
|
||
// IBANBICServiceURL is the SOAP URL to be used when checking a bank account number | ||
var IBANBICServiceURL = DefaultIBANBICServiceURL | ||
|
||
// DefaultIBANBICTimeout is the default timeout to use when checking the bank account number | ||
const DefaultIBANBICTimeout = 5 * time.Second | ||
|
||
// IBANBICTimeout is the timeout to use when checking the bank account number | ||
var IBANBICTimeout = DefaultIBANBICTimeout | ||
|
||
var ( | ||
// ErrIBANBICServiceUnreachable is the error returned when the IBANBIC service is unreachable | ||
ErrIBANBICServiceUnreachable = errors.New("IBANBIC service is unreachable") | ||
|
||
// ErrIBANBICInvalidInput is the error returned when the bank account number is invalid | ||
ErrIBANBICInvalidInput = errors.New("Number is not a valid bank account number") | ||
|
||
// ErrIBANBICServiceError is the error returned when we get a non-standard error from the IBANBIC service | ||
ErrIBANBICServiceError = "IBANBIC service returns an error: " | ||
) | ||
|
||
// IBANBICInfo contains the info about a Belgian Bank Account number | ||
type IBANBICInfo struct { | ||
BBAN string // The Belgian Bank Account Number | ||
BankName string // The name of the bank which issues the account | ||
IBAN string // The IBAN number of the bank account | ||
BIC string // The Bank Identification Code of the bank | ||
} | ||
|
||
// CheckIBAN checks the Bank Account Number and returns the IBAN and BIC information | ||
func CheckIBAN(number string) (*IBANBICInfo, error) { | ||
|
||
if len(number) == 0 { | ||
return nil, ErrIBANBICInvalidInput | ||
} | ||
|
||
result := &IBANBICInfo{ | ||
BBAN: number, | ||
} | ||
|
||
bankName, err := performIBANBICRequest("BBANtoBANKNAME", number) | ||
if err != nil { | ||
return nil, err | ||
} | ||
result.BankName = bankName | ||
|
||
ibanAndBic, err := performIBANBICRequest("BBANtoIBANandBIC", number) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
ibanBicParts := strings.SplitN(ibanAndBic, "#", 2) | ||
if len(ibanBicParts) < 2 { | ||
return nil, errors.New(ErrIBANBICServiceError + "Failed to get BIC and IBAN code") | ||
} | ||
|
||
result.IBAN = ibanBicParts[0] | ||
result.BIC = ibanBicParts[1] | ||
|
||
return result, nil | ||
|
||
} | ||
|
||
func performIBANBICRequest(action string, value string) (string, error) { | ||
|
||
url := IBANBICServiceURL + "/" + url.PathEscape(action) + "?Value=" + url.QueryEscape(value) | ||
|
||
client := http.Client{ | ||
Timeout: VATTimeout, | ||
} | ||
|
||
res, err := client.Get(url) | ||
if err != nil { | ||
return "", ErrIBANBICServiceUnreachable | ||
} | ||
defer res.Body.Close() | ||
|
||
xmlRes, err := ioutil.ReadAll(res.Body) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
xmlString := string(xmlRes) | ||
if strings.Contains(xmlString, "Exception") { | ||
exceptionParts := strings.Split(xmlString, "\n") | ||
return "", errors.New(ErrIBANBICServiceError + strings.TrimSpace(exceptionParts[0])) | ||
} | ||
|
||
var result string | ||
if err := xml.Unmarshal(xmlRes, &result); err != nil { | ||
return "", err | ||
} | ||
|
||
return result, 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,166 @@ | ||
package finance_test | ||
|
||
import ( | ||
"net/http" | ||
"net/http/httptest" | ||
"strings" | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/assert" | ||
|
||
"github.com/pieterclaerhout/go-finance" | ||
) | ||
|
||
func Test_CheckIBAN(t *testing.T) { | ||
|
||
type test struct { | ||
number string | ||
expectedBankName string | ||
expectedIBAN string | ||
expectedBIC string | ||
expectsError bool | ||
} | ||
|
||
var tests = []test{ | ||
{"738-1202561-74", "KBC Bank", "BE16 7381 2025 6174", "KRED BE BB", false}, | ||
{"738120256174", "KBC Bank", "BE16 7381 2025 6174", "KRED BE BB", false}, | ||
{"7381202561-74", "KBC Bank", "BE16 7381 2025 6174", "KRED BE BB", false}, | ||
{"738-AAAAAAA-74", "", "", "", true}, | ||
{"738-AAAAAAA-AA", "", "", "", true}, | ||
{"", "", "", "", true}, | ||
} | ||
|
||
for _, tc := range tests { | ||
t.Run(tc.number, func(t *testing.T) { | ||
|
||
info, err := finance.CheckIBAN(tc.number) | ||
|
||
if tc.expectsError { | ||
|
||
assert.Error(t, err, "error") | ||
assert.Nil(t, info, "info") | ||
|
||
} else { | ||
|
||
assert.NoError(t, err, "error") | ||
assert.NotNil(t, info, "info") | ||
|
||
if info != nil { | ||
assert.Equal(t, tc.expectedBankName, info.BankName, "bank-name") | ||
assert.Equal(t, tc.expectedIBAN, info.IBAN, "IBAN") | ||
assert.Equal(t, tc.expectedBIC, info.BIC, "BIC") | ||
} | ||
|
||
} | ||
|
||
}) | ||
} | ||
|
||
} | ||
|
||
func Test_CheckIBAN_InvalidURL(t *testing.T) { | ||
|
||
finance.IBANBICServiceURL = "ht&@-tp://:aa" | ||
defer func() { | ||
finance.IBANBICServiceURL = finance.DefaultIBANBICServiceURL | ||
}() | ||
|
||
result, err := finance.CheckIBAN("738120256174") | ||
|
||
assert.Nil(t, result, "result") | ||
assert.Error(t, err, "error") | ||
|
||
} | ||
|
||
func Test_CheckIBAN_Timeout(t *testing.T) { | ||
|
||
s := httptest.NewServer( | ||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
time.Sleep(500 * time.Millisecond) | ||
w.Header().Set("Content-Type", "text/xml") | ||
w.Write([]byte("hello")) | ||
}), | ||
) | ||
defer s.Close() | ||
|
||
finance.IBANBICTimeout = 250 * time.Millisecond | ||
finance.IBANBICServiceURL = s.URL | ||
defer func() { | ||
finance.IBANBICTimeout = finance.DefaultIBANBICTimeout | ||
finance.IBANBICServiceURL = finance.DefaultIBANBICServiceURL | ||
}() | ||
|
||
result, err := finance.CheckIBAN("738120256174") | ||
|
||
assert.Nil(t, result, "result") | ||
assert.Error(t, err, "error") | ||
|
||
} | ||
|
||
func Test_CheckIBAN_ReadBodyError(t *testing.T) { | ||
|
||
s := httptest.NewServer( | ||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
w.Write([]byte(`<string xmlns="http://tempuri.org/">KBC Bank</string>`)) | ||
}), | ||
) | ||
defer s.Close() | ||
|
||
finance.IBANBICServiceURL = s.URL | ||
defer func() { | ||
finance.IBANBICServiceURL = finance.DefaultIBANBICServiceURL | ||
}() | ||
|
||
result, err := finance.CheckIBAN("738120256174") | ||
|
||
assert.Nil(t, result, "result") | ||
assert.Error(t, err, "error") | ||
|
||
} | ||
|
||
func Test_CheckIBAN_InvalidBody(t *testing.T) { | ||
|
||
s := httptest.NewServer( | ||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
w.Header().Set("Content-Length", "1") | ||
}), | ||
) | ||
defer s.Close() | ||
|
||
finance.IBANBICServiceURL = s.URL | ||
defer func() { | ||
finance.IBANBICServiceURL = finance.DefaultIBANBICServiceURL | ||
}() | ||
|
||
result, err := finance.CheckIBAN("738120256174") | ||
|
||
assert.Nil(t, result, "result") | ||
assert.Error(t, err, "error") | ||
|
||
} | ||
|
||
func Test_CheckIBAN_PartialFail(t *testing.T) { | ||
|
||
s := httptest.NewServer( | ||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
if strings.Contains(r.RequestURI, "BBANtoIBANandBIC") { | ||
w.Header().Set("Content-Length", "1") | ||
return | ||
} | ||
w.Write([]byte(`<string xmlns="http://tempuri.org/">KBC Bank</string>`)) | ||
}), | ||
) | ||
defer s.Close() | ||
|
||
finance.IBANBICServiceURL = s.URL | ||
defer func() { | ||
finance.IBANBICServiceURL = finance.DefaultIBANBICServiceURL | ||
}() | ||
|
||
result, err := finance.CheckIBAN("738120256174") | ||
|
||
assert.Nil(t, result, "result") | ||
assert.Error(t, err, "error") | ||
|
||
} |
Oops, something went wrong.