From 54034e1d72ffc39da45c1ce25eeca20cb250fa07 Mon Sep 17 00:00:00 2001 From: Alexander Plutov Date: Thu, 15 Oct 2015 12:43:50 +0700 Subject: [PATCH] GetAccessToken --- README.md | 1 + client.go | 86 ++++++++++++++++++++++++++++++++++++++++++++++++ examples/main.go | 10 +++++- paypal.go | 16 --------- types.go | 22 +++++++++++++ 5 files changed, 118 insertions(+), 17 deletions(-) create mode 100644 README.md create mode 100644 client.go delete mode 100644 paypal.go diff --git a/README.md b/README.md new file mode 100644 index 0000000..35974fb --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +PayPal REST API diff --git a/client.go b/client.go new file mode 100644 index 0000000..b523f06 --- /dev/null +++ b/client.go @@ -0,0 +1,86 @@ +package paypalsdk + +import ( + "encoding/json" + "io/ioutil" + "net/http" + "bytes" + "time" + "fmt" + "io" +) + +// NewClient returns new Client struct +func NewClient(clientID string, secret string, APIBase string) (*Client, error) { + return &Client{ + &http.Client{}, + clientID, + secret, + APIBase, + nil, + }, nil +} + +// GetAcessToken request a new access token from Paypal +func (c *Client) GetAccessToken() (*TokenResponse, error) { + buf := bytes.NewBuffer([]byte("grant_type=client_credentials")) + req, err := http.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/oauth2/token"), buf) + if err != nil { + return nil, err + } + + req.SetBasicAuth(c.ClientID, c.Secret) + req.Header.Set("Content-type", "application/x-www-form-urlencoded") + + t := TokenResponse{} + err = c.Send(req, &t) + if err == nil { + t.ExpiresAt = time.Now().Add(time.Duration(t.ExpiresIn/2) * time.Second) + } + + return &t, err +} + +// Send makes a request to the API, the response body will be +// unmarshaled into v, or if v is an io.Writer, the response will +// be written to it without decoding +func (c *Client) Send(req *http.Request, v interface{}) error { + // Set default headers + req.Header.Set("Accept", "application/json") + req.Header.Set("Accept-Language", "en_US") + + // Default values for headers + if req.Header.Get("Content-type") == "" { + req.Header.Set("Content-type", "application/json") + } + + resp, err := c.client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode < 200 || resp.StatusCode > 299 { + errResp := &ErrorResponse{Response: resp} + data, err := ioutil.ReadAll(resp.Body) + + if err == nil && len(data) > 0 { + json.Unmarshal(data, errResp) + } + + return errResp + } + + if v != nil { + if w, ok := v.(io.Writer); ok { + io.Copy(w, resp.Body) + } else { + err = json.NewDecoder(resp.Body).Decode(v) + if err != nil { + return err + } + } + } + + return nil +} diff --git a/examples/main.go b/examples/main.go index 1749a4b..932e754 100644 --- a/examples/main.go +++ b/examples/main.go @@ -8,11 +8,19 @@ import ( ) func main() { - client, err := paypalsdk.NewClient("123", "123", paypalsdk.APIBaseSandBox) + client, err := paypalsdk.NewClient("AZgwu4yt5Ba0gyTu1dGBH3txHCJbMuFNvrmQxBaQbfDncDiCs6W_rwJD8Ir-0pZrN-_eq7n9zVd8Y-5f", "EBzA1wRl5t73OMugOieDj_tI3vihfJmGl47ukQT-cpctooIzDu0K7IPESNC0cKodlLSOXzwI8qXSM0rd", paypalsdk.APIBaseSandBox) if err == nil { fmt.Println("DEBUG: ClientID=" + client.ClientID + " APIBase=" + client.APIBase) } else { fmt.Println("ERROR: " + err.Error()) os.Exit(1) } + + token, err := client.GetAccessToken() + if err == nil { + fmt.Println("DEBUG: Access token=" + token.Token) + } else { + fmt.Println("ERROR: " + err.Error()) + os.Exit(1) + } } diff --git a/paypal.go b/paypal.go deleted file mode 100644 index f36ecec..0000000 --- a/paypal.go +++ /dev/null @@ -1,16 +0,0 @@ -package paypalsdk - -import ( - "net/http" -) - -// NewClient returns new Client struct -func NewClient(clientID string, secret string, APIBase string) (*Client, error) { - return &Client{ - &http.Client{}, - clientID, - secret, - APIBase, - nil, - }, nil -} diff --git a/types.go b/types.go index b67d3d0..e0d04d8 100644 --- a/types.go +++ b/types.go @@ -3,6 +3,7 @@ package paypalsdk import ( "net/http" "time" + "fmt" ) const ( @@ -32,4 +33,25 @@ type ( ExpiresIn int `json:"expires_in"` // 28800 ExpiresAt time.Time `json:"expires_at"` } + + // ErrorResponse is used when a response has errors + ErrorResponse struct { + Response *http.Response `json:"-"` + Name string `json:"name"` + DebugID string `json:"debug_id"` + Message string `json:"message"` + InformationLink string `json:"information_link"` + Details []ErrorDetail `json:"details"` + } + + // ErrorDetails map to error_details object + ErrorDetail struct { + Field string `json:"field"` + Issue string `json:"issue"` + } ) + +// Error method implementation for ErrorResponse struct +func (r *ErrorResponse) Error() string { + return fmt.Sprintf("%v %v: %d %v\nDetails: %v", r.Response.Request.Method, r.Response.Request.URL, r.Response.StatusCode, r.Message, r.Details) +}