Skip to content

Commit a1fd879

Browse files
author
Paul Daigle
committed
Add a generic "post" endpoint to the go library
Remove web driver tests, make compatible with test helper utility Added basic Post endpoint Fixed travis tests to use environment PEM and existing merchant tokens. removed go gets from .travis Ginko command changed Change env variable to use BITPAYAPI Update readme
1 parent 0c898f0 commit a1fd879

12 files changed

+229
-151
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ _testmain.go
2525
*.log
2626

2727
enviro.sh
28+
.bpenv
29+
2830

2931
src/
3032
temp/
33+
/bin
34+
.pem.data

.python-version

-1
This file was deleted.

.travis.yml

+3-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
language: go
22
# command to install dependencies
3-
4-
install:
5-
- sudo pip install -r requirements.txt
6-
- python helpers/pair_steps.py
7-
3+
sudo: false
4+
# command to run tests
85
before_script:
96
- go get github.com/onsi/ginkgo/ginkgo
107
- go get github.com/onsi/gomega
@@ -15,6 +12,5 @@ before_script:
1512
- go get github.com/axw/gocov/gocov
1613
- go get github.com/mattn/goveralls
1714

18-
# command to run tests
1915
script:
20-
- ginkgo -r --keepgoing
16+
- ginkgo -r -keepGoing

GUIDE.md

+6-8
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ The bitpay client creates a cryptographically secure connection to your server b
1818
## Pairing
1919
Most calls to the BitPay REST API require that your client is paired with the bitpay.com server. To pair with bitpay.com you need to have an approved merchant account.
2020

21-
Your client can be paired via the `pos` (point-of-sale) facade. The `pos` facade allows for invoices to be created and retrieved.
22-
23-
_For development or quick deployment purposes, consider the [BitPay Go Command-Line Interface](https://github.com/philosodad/bitpay-go-cli) to simplify the deployment process_
21+
The BitPay test helper is probably the easi
2422

2523
### Pairing Programatically
2624

@@ -32,9 +30,10 @@ The example below demonstrates this, assuming that the bitpay-go client is impor
3230

3331
```go
3432
pemString := ku.GeneratePem()
35-
apiUri := "https://bitpay.com"
36-
webClient := Client{ApiUri: apiUri, Pem: pemString}
37-
token := webClient.PairWithCode(pairingCode)
33+
id := ku.GenerateSinFromPem(pemString)
34+
apiUri := "https://bitpay.com"
35+
webClient := Client{ApiUri: apiUri, Pem: pemString, ClientId: id}
36+
token := webClient.PairWithCode(pairingCode)
3837
webClient.Token = token
3938
```
4039

@@ -56,11 +55,10 @@ client.GetInvoice('PvVhgBfA7wKPWhuVC24rJo')
5655

5756
## Testnet Usage
5857

59-
During development and testing, take advantage of the [Bitcoin TestNet](https://en.bitcoin.it/wiki/Testnet) by creating a client with the ApiUri value "https://test.bitpay.com"
58+
During development and testing, take advantage of the [Bitcoin TestNet](https://en.bitcoin.it/wiki/Testnet) by creating a client with the ApiUri value "https://test.bitpay.com"
6059

6160
Note that in order to pair with testnet, you will need a pairing code from test.bitpay.com.
6261

6362
## API Documentation
6463

6564
API Documentation is available on the [BitPay site](https://bitpay.com/api).
66-

README.md

+19-20
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
# BitPay Library for Go
1+
# BitPay Library for Go
22
[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://raw.githubusercontent.com/bitpay/bitpay-go/master/LICENSE)
33
[![Travis Build](https://img.shields.io/travis/bitpay/bitpay-go.svg?style=flat-square)](http://travis-ci.org/bitpay/bitpay-go)
44

55

66
Powerful, flexible, lightweight interface to the BitPay Bitcoin Payment Gateway API.
77

8-
## [Getting Started »](http://dev.bitpay.com/guides/go.html)
8+
## [Getting Started »](https://github.com/bitpay/bitpay-go/blob/master/GUIDE.md)
99

1010
Code documentation is available on [godoc](http://godoc.org/github.com/bitpay/bitpay-go).
1111
## API Documentation
@@ -14,36 +14,35 @@ API Documentation is available on the [BitPay site](https://bitpay.com/api).
1414

1515
## Running the Tests
1616

17-
The reference project is at https://github.com/bitpay/bitpay-go-cli. You will need a working go installation to follow these instructions.
18-
1917
In order to run the tests, follow these steps:
2018

21-
1. Clone the repository
19+
1. Set the $GOPATH and $PATH variables
2220

23-
`git clone https://github.com/bitpay/bitpay-go-cli.git`
21+
1. Install the dependencies
2422

25-
1. Set the $GOPATH and $PATH variables
23+
```bash
24+
$ go get github.com/btcsuite/btcutil
25+
$ go get github.com/gorilla/mux
26+
$ go get github.com/onsi/ginkgo
27+
$ go get golang.org/x/crypto
28+
```
2629

27-
`source helpers/enviro.sh`
30+
1. Clone the repository
31+
32+
`git clone https://github.com/bitpay/bitpay-go.git`
2833

29-
1. Set your test api url (such as https://test.bitpay.com) and your username and password.
34+
Into src/github.com/bitpay/bitpay-go/
3035

31-
`source helpers/set_constants.sh <url> <username> <password>`
32-
33-
`python helpers/pair_steps.py`
34-
35-
The python script should retrieve three pairing codes from the server and store them in three files in a `temp` directory in the main project directory, `temp/retrievecode.txt`, `temp/paircode.txt`, and `temp/invoicecode.txt`. If this does not go smoothly, you can manually add the pairing codes to those files by visiting (https://test.bitpay.com/dashboard/merchant/api-tokens) and creating three tokens, saving each pairing code into one of the files in temp.
36+
1. Set the environment variables `BITPAYAPI` & `BITPAYPEM` to "https://test.bitpay.com" and a valid PEM value.
3637

37-
1. For reasons that are not entirely clear, we need to delete all of the required files and re-import them.
38-
39-
`rm -rf src/github.com src/golang`
38+
This is slightly tricky, the PEM file has to already be paired with a merchant token on your bitpay account. To do this it is probably best to use the [bitpay test helper](https://github.com/bitpay/bitpay-test-helper).
4039

41-
`go get -u -t github.com/bitpay/bitpay-go/client`
40+
1. You will also need a paid invoice on the server. Set the environment variable `INVOICEID` to the id of a paid invoice on the server.
4241

4342
1. We are now ready to run the tests.
44-
43+
4544
`ginkgo -r src/github.com/bitpay/`
46-
45+
4746
## Found a bug?
4847
Let us know! Send a pull request or a patch. Questions? Ask! We're here to help. We will respond to all filed issues.
4948

client/client.go

+70-15
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,6 @@ func (client *Client) CreateInvoice(price float64, currency string) (inv invoice
6161
err = errors.New("BitPayArgumentError: invalid currency code")
6262
return inv, err
6363
}
64-
url := client.ApiUri + "/invoices"
65-
htclient := setHttpClient(client)
6664
paylo := make(map[string]string)
6765
var floatPrec int
6866
if currency == "BTC" {
@@ -75,34 +73,38 @@ func (client *Client) CreateInvoice(price float64, currency string) (inv invoice
7573
paylo["currency"] = currency
7674
paylo["token"] = client.Token.Token
7775
paylo["id"] = client.ClientId
78-
payload, _ := json.Marshal(paylo)
79-
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(payload))
80-
req.Header.Add("content-type", "application/json")
81-
req.Header.Add("accept", "application/json")
82-
req.Header.Add("X-accept-version", "2.0.0")
83-
publ := ku.ExtractCompressedPublicKey(client.Pem)
84-
req.Header.Add("X-Identity", publ)
85-
sig := ku.Sign(url+string(payload), client.Pem)
86-
req.Header.Add("X-Signature", sig)
87-
response, _ := htclient.Do(req)
76+
response, _ := client.Post("invoices", paylo)
8877
inv, err = processInvoice(response)
8978
return inv, err
9079
}
9180

81+
// PairWithFacade
82+
func (client *Client) PairWithFacade(str string) (tok Token, err error) {
83+
paylo := make(map[string]string)
84+
paylo["facade"] = str
85+
tok, err = client.PairClient(paylo)
86+
return tok, err
87+
}
88+
9289
// PairWithCode retrieves a token from the server and authenticates the keys of the calling client. The string passed to the client is a "pairing code" that must be retrieved from https://bitpay.com/dashboard/merchant/api-tokens. PairWithCode returns a Token type that must be assigned to the Token field of a client in order for that client to create invoices. For example `client.Token = client.PairWithCode("abcdefg")`.
9390
func (client *Client) PairWithCode(str string) (tok Token, err error) {
9491
match, _ := regexp.MatchString("^[[:alnum:]]{7}$", str)
9592
if !match {
9693
err = errors.New("BitPayArgumentError: invalid pairing code")
9794
return tok, err
9895
}
96+
paylo := make(map[string]string)
97+
paylo["pairingCode"] = str
98+
tok, err = client.PairClient(paylo)
99+
return tok, err
100+
}
101+
102+
func (client *Client) PairClient(paylo map[string]string) (tok Token, err error) {
103+
paylo["id"] = client.ClientId
99104
sin := ku.GenerateSinFromPem(client.Pem)
100105
client.ClientId = sin
101106
url := client.ApiUri + "/tokens"
102107
htclient := setHttpClient(client)
103-
paylo := make(map[string]string)
104-
paylo["id"] = client.ClientId
105-
paylo["pairingCode"] = str
106108
payload, _ := json.Marshal(paylo)
107109
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(payload))
108110
req.Header.Add("content-type", "application/json")
@@ -122,6 +124,22 @@ func (client *Client) PairWithCode(str string) (tok Token, err error) {
122124
return tok, err
123125
}
124126

127+
func (client *Client) Post(path string, paylo map[string]string) (response *http.Response, err error) {
128+
url := client.ApiUri + "/" + path
129+
htclient := setHttpClient(client)
130+
payload, _ := json.Marshal(paylo)
131+
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(payload))
132+
req.Header.Add("content-type", "application/json")
133+
req.Header.Add("accept", "application/json")
134+
req.Header.Add("X-accept-version", "2.0.0")
135+
publ := ku.ExtractCompressedPublicKey(client.Pem)
136+
req.Header.Add("X-Identity", publ)
137+
sig := ku.Sign(url+string(payload), client.Pem)
138+
req.Header.Add("X-Signature", sig)
139+
response, err = htclient.Do(req)
140+
return response, err
141+
}
142+
125143
// GetInvoice is a public facade method, any client which has the ApiUri field set can retrieve an invoice from that endpoint, provided they have the invoice id.
126144
func (client *Client) GetInvoice(invId string) (inv invoice, err error) {
127145
url := client.ApiUri + "/invoices/" + invId
@@ -131,6 +149,43 @@ func (client *Client) GetInvoice(invId string) (inv invoice, err error) {
131149
return inv, err
132150
}
133151

152+
func (client *Client) GetTokens() (tokes []map[string]string, err error) {
153+
url := client.ApiUri + "/tokens"
154+
htclient := setHttpClient(client)
155+
req, _ := http.NewRequest("GET", url, nil)
156+
req.Header.Add("content-type", "application/json")
157+
req.Header.Add("accept", "application/json")
158+
req.Header.Add("X-accept-version", "2.0.0")
159+
publ := ku.ExtractCompressedPublicKey(client.Pem)
160+
req.Header.Add("X-Identity", publ)
161+
sig := ku.Sign(url, client.Pem)
162+
req.Header.Add("X-Signature", sig)
163+
response, _ := htclient.Do(req)
164+
defer response.Body.Close()
165+
contents, _ := ioutil.ReadAll(response.Body)
166+
var jsonContents map[string]interface{}
167+
json.Unmarshal(contents, &jsonContents)
168+
if response.StatusCode/100 != 2 {
169+
err = processErrorMessage(response, jsonContents)
170+
} else {
171+
this, _ := json.Marshal(jsonContents["data"])
172+
json.Unmarshal(this, &tokes)
173+
err = nil
174+
}
175+
return tokes, nil
176+
}
177+
178+
func (client *Client) GetToken(facade string) (token string, err error) {
179+
tokens, _ := client.GetTokens()
180+
for _, token := range tokens {
181+
toke, ok := token[facade]
182+
if ok {
183+
return toke, nil
184+
}
185+
}
186+
return "error", errors.New("facade not available in tokens")
187+
}
188+
134189
func setHttpClient(client *Client) *http.Client {
135190
if client.Insecure {
136191
trans := &http.Transport{

client/client_get_invoice_test.go

+5-23
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,24 @@ package client_test
22

33
import (
44
. "github.com/bitpay/bitpay-go/client"
5-
ku "github.com/bitpay/bitpay-go/key_utils"
65
. "github.com/onsi/ginkgo"
76
. "github.com/onsi/gomega"
8-
"io/ioutil"
7+
//"io/ioutil"
98
"os"
109
"time"
1110
)
1211

1312
var _ = Describe("RetrieveInvoice", func() {
1413
It("Retrieves an invoice from the server with an id", func() {
1514
time.Sleep(3 * time.Second)
16-
pm := ku.GeneratePem()
17-
apiuri := os.ExpandEnv("$RCROOTADDRESS")
15+
pm := os.ExpandEnv("$BITPAYPEM")
16+
apiuri := os.ExpandEnv("$BITPAYAPI")
1817
webClient := Client{ApiUri: apiuri, Insecure: true, Pem: pm}
19-
gopath := os.ExpandEnv("$GOPATH")
20-
tempFolder := gopath + "/temp/"
21-
code, err := ioutil.ReadFile(tempFolder + "retrievecode.txt")
22-
if err != nil {
23-
println(err.Error())
24-
}
25-
token, err := webClient.PairWithCode(string(code))
26-
if err != nil {
27-
println(err.Error())
28-
}
29-
Expect(token.Token).NotTo(BeNil())
30-
webClient.Token = token
31-
response, err := webClient.CreateInvoice(10, "USD")
32-
if err != nil {
33-
println("the retrieve test errored while creating an invoice: Error - " + err.Error())
34-
}
35-
invoiceId := response.Id
18+
invoiceId := os.ExpandEnv("$INVOICEID")
3619
retrievedInvoice, err := webClient.GetInvoice(invoiceId)
3720
if err != nil {
38-
println(webClient.ApiUri + " errored retrieving an invoice: Error - " + err.Error())
21+
println("\n" + webClient.ApiUri + " errored retrieving an invoice: Error - " + err.Error())
3922
}
4023
Expect(retrievedInvoice.Id).To(Equal(invoiceId))
41-
Expect(retrievedInvoice.Price).To(Equal(response.Price))
4224
})
4325
})

client/client_invoice_test.go

+24-9
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package client_test
22

33
import (
4+
"encoding/json"
45
. "github.com/bitpay/bitpay-go/client"
56
ku "github.com/bitpay/bitpay-go/key_utils"
7+
"strings"
68

79
. "github.com/onsi/ginkgo"
810
. "github.com/onsi/gomega"
@@ -15,18 +17,31 @@ import (
1517
var _ = Describe("CreateInvoice", func() {
1618
It("creates an invoice for the price and currency sent", func() {
1719
time.Sleep(3 * time.Second)
18-
pm := ku.GeneratePem()
19-
apiuri := os.ExpandEnv("$RCROOTADDRESS")
20-
webClient := Client{ApiUri: apiuri, Insecure: true, Pem: pm}
21-
gopath := os.ExpandEnv("$GOPATH")
22-
tempFolder := gopath + "/temp/"
23-
code, err := ioutil.ReadFile(tempFolder + "invoicecode.txt")
24-
if err != nil {
25-
println(err.Error())
20+
pm := os.ExpandEnv("$BITPAYPEM")
21+
pm = strings.Replace(pm, "\\n", "\n", -1)
22+
apiuri := os.ExpandEnv("$BITPAYAPI")
23+
webClient := Client{ApiUri: apiuri, Insecure: true, Pem: pm, ClientId: ku.GenerateSinFromPem(pm)}
24+
mertok, _ := webClient.GetToken("merchant")
25+
params := make(map[string]string)
26+
params["token"] = mertok
27+
params["facade"] = "pos"
28+
params["id"] = webClient.ClientId
29+
res, err := webClient.Post("tokens", params)
30+
defer res.Body.Close()
31+
contents, _ := ioutil.ReadAll(res.Body)
32+
var jsonContents map[string]interface{}
33+
json.Unmarshal(contents, &jsonContents)
34+
var tok Token
35+
if res.StatusCode/100 != 2 {
36+
Expect(res.StatusCode).To(Equal(200))
37+
} else {
38+
tok, err = processToken(res, jsonContents)
39+
err = nil
2640
}
41+
code := tok.PairingCode
2742
token, err := webClient.PairWithCode(string(code))
2843
if err != nil {
29-
println(err.Error())
44+
Expect(err.Error).To(Equal("Should be no error"))
3045
}
3146
webClient.Token = token
3247
response, err := webClient.CreateInvoice(10, "USD")

0 commit comments

Comments
 (0)