@@ -6,9 +6,16 @@ import (
6
6
"github.com/pkg/errors"
7
7
"github.com/plaid/plaid-go/plaid"
8
8
"github.com/sirupsen/logrus"
9
+ "strings"
10
+ "time"
9
11
)
10
12
11
13
type Client interface {
14
+ GetAccounts (ctx context.Context , accessToken string , options plaid.GetAccountsOptions ) ([]plaid.Account , error )
15
+ GetAllTransactions (ctx context.Context , accessToken string , start , end time.Time , accountIds []string ) ([]plaid.Transaction , error )
16
+ GetAllInstitutions (ctx context.Context , countryCodes []string , options plaid.GetInstitutionsOptions ) ([]plaid.Institution , error )
17
+ GetInstitutions (ctx context.Context , count , offset int , countryCodes []string , options plaid.GetInstitutionsOptions ) (total int , _ []plaid.Institution , _ error )
18
+ GetInstitution (ctx context.Context , institutionId string , includeMetadata bool , countryCodes []string ) (* plaid.Institution , error )
12
19
GetWebhookVerificationKey (ctx context.Context , keyId string ) (plaid.GetWebhookVerificationKeyResponse , error )
13
20
Close () error
14
21
}
@@ -36,8 +43,183 @@ type plaidClient struct {
36
43
client * plaid.Client
37
44
}
38
45
46
+ func (p * plaidClient ) GetAccounts (ctx context.Context , accessToken string , options plaid.GetAccountsOptions ) ([]plaid.Account , error ) {
47
+ span := sentry .StartSpan (ctx , "Plaid - GetAccounts" )
48
+ defer span .Finish ()
49
+ if span .Data == nil {
50
+ span .Data = map [string ]interface {}{}
51
+ }
52
+ span .Data ["options" ] = options
53
+
54
+ result , err := p .client .GetAccountsWithOptions (accessToken , options )
55
+ span .Data ["plaidRequestId" ] = result .RequestID
56
+ if err != nil {
57
+ span .Status = sentry .SpanStatusInternalError
58
+ return nil , errors .Wrap (err , "failed to retrieve plaid accounts" )
59
+ }
60
+
61
+ return result .Accounts , nil
62
+ }
63
+
64
+ func (p * plaidClient ) GetAllTransactions (ctx context.Context , accessToken string , start , end time.Time , accountIds []string ) ([]plaid.Transaction , error ) {
65
+ span := sentry .StartSpan (ctx , "Plaid - GetAllTransactions" )
66
+ defer span .Finish ()
67
+ if span .Data == nil {
68
+ span .Data = map [string ]interface {}{}
69
+ }
70
+
71
+ span .Data ["start" ] = start
72
+ span .Data ["end" ] = start
73
+ if len (accountIds ) > 0 {
74
+ span .Data ["accountIds" ] = accountIds
75
+ }
76
+
77
+ perPage := 100
78
+
79
+ options := plaid.GetTransactionsOptions {
80
+ StartDate : start .Format ("2006-01-02" ),
81
+ EndDate : end .Format ("2006-01-02" ),
82
+ AccountIDs : accountIds ,
83
+ Count : perPage ,
84
+ Offset : 0 ,
85
+ }
86
+
87
+ transactions := make ([]plaid.Transaction , 0 )
88
+ for {
89
+ options .Offset = len (transactions )
90
+ total , items , err := p .GetTransactions (span .Context (), accessToken , options )
91
+ if err != nil {
92
+ return nil , err
93
+ }
94
+
95
+ transactions = append (transactions , items ... )
96
+
97
+ if len (items ) < perPage {
98
+ break
99
+ }
100
+
101
+ if len (transactions ) >= total {
102
+ break
103
+ }
104
+ }
105
+
106
+ return transactions , nil
107
+ }
108
+
109
+ func (p * plaidClient ) GetTransactions (ctx context.Context , accessToken string , options plaid.GetTransactionsOptions ) (total int , _ []plaid.Transaction , _ error ) {
110
+ span := sentry .StartSpan (ctx , "Plaid - GetTransactions" )
111
+ defer span .Finish ()
112
+ if span .Data == nil {
113
+ span .Data = map [string ]interface {}{}
114
+ }
115
+
116
+ span .Data ["options" ] = options
117
+
118
+ result , err := p .client .GetTransactionsWithOptions (accessToken , options )
119
+ span .Data ["plaidRequestId" ] = result .RequestID
120
+ if err != nil {
121
+ return 0 , nil , errors .Wrap (err , "failed to retrieve plaid transactions" )
122
+ }
123
+
124
+ return result .TotalTransactions , result .Transactions , nil
125
+ }
126
+
127
+ func (p * plaidClient ) GetInstitution (ctx context.Context , institutionId string , includeMetadata bool , countryCodes []string ) (* plaid.Institution , error ) {
128
+ span := sentry .StartSpan (ctx , "Plaid - GetInstitution" )
129
+ defer span .Finish ()
130
+ if span .Data == nil {
131
+ span .Data = map [string ]interface {}{}
132
+ }
133
+
134
+ span .Data ["institutionId" ] = institutionId
135
+ span .Data ["includeMetadata" ] = includeMetadata
136
+ span .Data ["countryCodes" ] = countryCodes
137
+
138
+ result , err := p .client .GetInstitutionByIDWithOptions (institutionId , countryCodes , plaid.GetInstitutionByIDOptions {
139
+ IncludeOptionalMetadata : true ,
140
+ IncludePaymentInitiationMetadata : false ,
141
+ IncludeStatus : false ,
142
+ })
143
+ span .Data ["plaidRequestId" ] = result .RequestID
144
+ if err != nil {
145
+ return nil , errors .Wrap (err , "failed to retrieve plaid institution" )
146
+ }
147
+
148
+ return & result .Institution , nil
149
+ }
150
+
151
+ func (p * plaidClient ) GetAllInstitutions (ctx context.Context , countryCodes []string , options plaid.GetInstitutionsOptions ) ([]plaid.Institution , error ) {
152
+ span := sentry .StartSpan (ctx , "Plaid - GetAllInstitutions" )
153
+ defer span .Finish ()
154
+ if span .Data == nil {
155
+ span .Data = map [string ]interface {}{}
156
+ }
157
+ span .Data ["countryCodes" ] = countryCodes
158
+ span .Data ["options" ] = options
159
+
160
+ perPage := 100
161
+ institutions := make ([]plaid.Institution , 0 )
162
+ for {
163
+ total , items , err := p .GetInstitutions (span .Context (), perPage , len (institutions ), countryCodes , options )
164
+ if err != nil {
165
+ span .Status = sentry .SpanStatusInternalError
166
+ return nil , err
167
+ }
168
+
169
+ institutions = append (institutions , items ... )
170
+
171
+ // If we received fewer items than we requested, then we have reached the end.
172
+ if len (items ) < perPage {
173
+ break
174
+ }
175
+
176
+ // If we have received at least what we expect to be the total amount, then we are also done.
177
+ if len (institutions ) >= total {
178
+ break
179
+ }
180
+ }
181
+
182
+ return institutions , nil
183
+ }
184
+
185
+ func (p * plaidClient ) GetInstitutions (ctx context.Context , count , offset int , countryCodes []string , options plaid.GetInstitutionsOptions ) (total int , _ []plaid.Institution , _ error ) {
186
+ span := sentry .StartSpan (ctx , "Plaid - GetInstitutions" )
187
+ defer span .Finish ()
188
+ if span .Data == nil {
189
+ span .Data = map [string ]interface {}{}
190
+ }
191
+
192
+ span .Data ["count" ] = count
193
+ span .Data ["offset" ] = offset
194
+ span .Data ["countryCodes" ] = countryCodes
195
+ span .Data ["options" ] = options
196
+
197
+ log := p .log .WithFields (logrus.Fields {
198
+ "count" : count ,
199
+ "offset" : offset ,
200
+ "countryCodes" : strings .Join (countryCodes , "," ),
201
+ })
202
+
203
+ log .Debug ("retrieving plaid institutions" )
204
+
205
+ result , err := p .client .GetInstitutionsWithOptions (count , offset , countryCodes , options )
206
+ span .Data ["plaidRequestId" ] = result .RequestID
207
+ log = log .WithField ("plaidRequestId" , result .RequestID )
208
+ if err != nil {
209
+ span .Status = sentry .SpanStatusInternalError
210
+ log .WithError (err ).Errorf ("failed to retrieve plaid institutions" )
211
+ return 0 , nil , errors .Wrap (err , "failed to retrieve plaid institutions" )
212
+ }
213
+
214
+ log .Debugf ("successfully retrieved %d institutions" , len (result .Institutions ))
215
+
216
+ span .Status = sentry .SpanStatusOK
217
+
218
+ return result .Total , result .Institutions , nil
219
+ }
220
+
39
221
func (p * plaidClient ) GetWebhookVerificationKey (ctx context.Context , keyId string ) (plaid.GetWebhookVerificationKeyResponse , error ) {
40
- span := sentry .StartSpan (ctx , "GetWebhookVerificationKey" )
222
+ span := sentry .StartSpan (ctx , "Plaid - GetWebhookVerificationKey" )
41
223
defer span .Finish ()
42
224
43
225
result , err := p .client .GetWebhookVerificationKey (keyId )
0 commit comments