Skip to content

Commit 3c289a8

Browse files
committed
feat: support cache token
1 parent 4cdb267 commit 3c289a8

File tree

3 files changed

+74
-3
lines changed

3 files changed

+74
-3
lines changed

cache.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package paypal
2+
3+
import "context"
4+
5+
type CacheToken struct {
6+
RefreshToken string `json:"refresh_token"`
7+
Token string `json:"access_token"`
8+
Type string `json:"token_type"`
9+
ExpiresIn int64 `json:"expires_in"`
10+
ExpiresAt int64 `json:"expires_at"`
11+
}
12+
13+
type Cache interface {
14+
GetToken(ctx context.Context, clientId string) (*CacheToken, error)
15+
SetToken(ctx context.Context, clientId string, token *CacheToken) error
16+
}

client.go

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,48 @@ import (
1616

1717
var ErrRateLimited = errors.New("rate limited")
1818

19+
type Option func(o *Client) *Client
20+
21+
func WithCache(cache Cache) Option {
22+
return func(c *Client) *Client {
23+
c.cache = cache
24+
return c
25+
}
26+
}
27+
1928
// NewClient returns new Client struct
2029
// APIBase is a base API URL, for testing you can use paypal.APIBaseSandBox
21-
func NewClient(clientID string, secret string, APIBase string) (*Client, error) {
30+
func NewClient(ctx context.Context, clientID string, secret string, APIBase string, options ...Option) (*Client, error) {
2231
if clientID == "" || secret == "" || APIBase == "" {
2332
return nil, errors.New("ClientID, Secret and APIBase are required to create a Client")
2433
}
2534

26-
return &Client{
35+
cli := &Client{
2736
Client: &http.Client{},
2837
ClientID: clientID,
2938
Secret: secret,
3039
APIBase: APIBase,
31-
}, nil
40+
}
41+
42+
for _, option := range options {
43+
cli = option(cli)
44+
}
45+
cli.checkCacheToken(ctx)
46+
return cli, nil
47+
}
48+
49+
func (c *Client) checkCacheToken(ctx context.Context) {
50+
if c.cache == nil {
51+
return
52+
}
53+
54+
token, err := c.cache.GetToken(ctx, c.ClientID)
55+
if err != nil {
56+
return
57+
}
58+
if token != nil && token.Token != "" {
59+
c.SetAccessTokenWithExpiry(token.Token, token.ExpiresAt)
60+
}
3261
}
3362

3463
// GetAccessToken returns struct of TokenResponse
@@ -50,6 +79,20 @@ func (c *Client) GetAccessToken(ctx context.Context) (*TokenResponse, error) {
5079
if response.Token != "" {
5180
c.Token = response
5281
c.tokenExpiresAt = time.Now().Add(time.Duration(response.ExpiresIn) * time.Second)
82+
83+
if c.cache != nil {
84+
token := &CacheToken{
85+
RefreshToken: response.RefreshToken,
86+
Token: response.Token,
87+
Type: response.Type,
88+
ExpiresIn: int64(response.ExpiresIn),
89+
ExpiresAt: c.tokenExpiresAt.Unix(),
90+
}
91+
err = c.cache.SetToken(ctx, c.ClientID, token)
92+
if err != nil {
93+
return response, err
94+
}
95+
}
5396
}
5497

5598
return response, err
@@ -68,6 +111,16 @@ func (c *Client) SetAccessToken(token string) {
68111
c.tokenExpiresAt = time.Time{}
69112
}
70113

114+
// SetAccessTokenWithExpiry sets saved token to current client with expiry time
115+
func (c *Client) SetAccessTokenWithExpiry(token string, expiresAt int64) {
116+
c.Token = &TokenResponse{
117+
Token: token,
118+
}
119+
if expiresAt > 0 {
120+
c.tokenExpiresAt = time.Unix(expiresAt, 0)
121+
}
122+
}
123+
71124
// SetLog will set/change the output destination.
72125
// If log file is set paypal will log all requests and responses to this Writer
73126
func (c *Client) SetLog(log io.Writer) {

types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,8 @@ type (
650650
tokenExpiresAt time.Time
651651
returnRepresentation bool
652652

653+
cache Cache
654+
653655
// optional rate limiter; if set, SendWithAuth will check it before making requests
654656
rateLimiter limiter.RateLimiter
655657
rateLimiterKey string

0 commit comments

Comments
 (0)