@@ -12,10 +12,11 @@ import (
12
12
)
13
13
14
14
type Linker struct {
15
- Results chan string
16
- Errors chan error
17
- Client * plaid.Client
18
- Data * Data
15
+ Results chan string
16
+ Errors chan error
17
+ Client * plaid.Client
18
+ ClientOpts plaid.ClientOptions
19
+ Data * Data
19
20
}
20
21
21
22
type TokenPair struct {
@@ -38,7 +39,8 @@ func (l *Linker) Link(port string) (*TokenPair, error) {
38
39
}
39
40
40
41
func (l * Linker ) link (port string , serveLink func (w http.ResponseWriter , r * http.Request )) (* TokenPair , error ) {
41
- log .Println (fmt .Sprintf ("Starting Plaid Link on port %s" , port ))
42
+ log .Println (fmt .Sprintf ("Starting Plaid Link on port %s..." , port ))
43
+
42
44
go func () {
43
45
http .HandleFunc ("/link" , serveLink )
44
46
err := http .ListenAndServe (fmt .Sprintf (":%s" , port ), nil )
@@ -47,7 +49,9 @@ func (l *Linker) link(port string, serveLink func(w http.ResponseWriter, r *http
47
49
}
48
50
}()
49
51
50
- open .Run (fmt .Sprintf ("http://localhost:%s/link" , port ))
52
+ url := fmt .Sprintf ("http://localhost:%s/link" , port )
53
+ log .Println (fmt .Sprintf ("Your browser should open automatically. If it doesn't, please visit %s to continue linking!" , url ))
54
+ open .Run (url )
51
55
52
56
select {
53
57
case err := <- l .Errors :
@@ -71,20 +75,39 @@ func (l *Linker) exchange(publicToken string) (plaid.ExchangePublicTokenResponse
71
75
return l .Client .ExchangePublicToken (publicToken )
72
76
}
73
77
74
- func NewLinker (data * Data , client * plaid.Client ) * Linker {
78
+ func NewLinker (data * Data , client * plaid.Client , clientOpts plaid. ClientOptions ) * Linker {
75
79
return & Linker {
76
- Results : make (chan string ),
77
- Errors : make (chan error ),
78
- Client : client ,
79
- Data : data ,
80
+ Results : make (chan string ),
81
+ Errors : make (chan error ),
82
+ Client : client ,
83
+ ClientOpts : clientOpts ,
84
+ Data : data ,
80
85
}
81
86
}
82
87
83
88
func handleLink (linker * Linker ) func (w http.ResponseWriter , r * http.Request ) {
84
89
return func (w http.ResponseWriter , r * http.Request ) {
85
90
switch r .Method {
86
91
case http .MethodGet :
87
- http .ServeFile (w , r , "./static/link.html" )
92
+ t := template .New ("link" )
93
+ t , _ = t .Parse (linkTemplate )
94
+
95
+ var env string
96
+ switch linker .ClientOpts .Environment {
97
+ case plaid .Development :
98
+ env = "development"
99
+ case plaid .Production :
100
+ env = "production"
101
+ case plaid .Sandbox :
102
+ env = "sandbox"
103
+ default :
104
+ env = "development"
105
+ }
106
+ d := LinkTmplData {
107
+ PublicKey : linker .ClientOpts .PublicKey ,
108
+ Environment : env ,
109
+ }
110
+ t .Execute (w , d )
88
111
case http .MethodPost :
89
112
r .ParseForm ()
90
113
token := r .Form .Get ("public_token" )
@@ -101,8 +124,15 @@ func handleLink(linker *Linker) func(w http.ResponseWriter, r *http.Request) {
101
124
}
102
125
}
103
126
104
- type RelinkTemplData struct {
127
+ type LinkTmplData struct {
128
+ PublicKey string
129
+ Environment string
130
+ }
131
+
132
+ type RelinkTmplData struct {
105
133
PublicToken string
134
+ PublicKey string
135
+ Environment string
106
136
}
107
137
108
138
func handleRelink (linker * Linker , publicToken string ) func (w http.ResponseWriter , r * http.Request ) {
@@ -111,8 +141,22 @@ func handleRelink(linker *Linker, publicToken string) func(w http.ResponseWriter
111
141
case http .MethodGet :
112
142
t := template .New ("relink" )
113
143
t , _ = t .Parse (relinkTemplate )
114
- d := RelinkTemplData {
144
+
145
+ var env string
146
+ switch linker .ClientOpts .Environment {
147
+ case plaid .Development :
148
+ env = "development"
149
+ case plaid .Production :
150
+ env = "production"
151
+ case plaid .Sandbox :
152
+ env = "sandbox"
153
+ default :
154
+ env = "development"
155
+ }
156
+ d := RelinkTmplData {
115
157
PublicToken : publicToken ,
158
+ PublicKey : linker .ClientOpts .PublicKey ,
159
+ Environment : env ,
116
160
}
117
161
t .Execute (w , d )
118
162
case http .MethodPost :
@@ -131,7 +175,26 @@ func handleRelink(linker *Linker, publicToken string) func(w http.ResponseWriter
131
175
}
132
176
}
133
177
134
- var relinkTemplate string = `<html>
178
+ var linkTemplate string = `<html>
179
+ <head>
180
+ <style>
181
+ .alert-success {
182
+ font-size: 1.2em;
183
+ font-family: Arial, Helvetica, sans-serif;
184
+ background-color: #008000;
185
+ color: #fff;
186
+ display: flex;
187
+ justify-content: center;
188
+ align-items: center;
189
+ border-radius: 15px;
190
+ width: 100%;
191
+ height: 100%;
192
+ }
193
+ .hidden {
194
+ visibility: hidden;
195
+ }
196
+ </style>
197
+ </head>
135
198
<body>
136
199
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.3/jquery.min.js"></script>
137
200
<script src="https://cdn.plaid.com/link/v2/stable/link-initialize.js"></script>
@@ -143,11 +206,11 @@ var relinkTemplate string = `<html>
143
206
// codes to initialize Link; European countries will have GDPR
144
207
// consent panel
145
208
countryCodes: ['US'],
146
- env: 'development ',
209
+ env: '{{ .Environment }} ',
147
210
// Replace with your public_key from the Dashboard
148
- key: '880bb11f8bc9f3c1d8feb4a348f371 ',
211
+ key: '{{ .PublicKey }} ',
149
212
product: ['transactions'],
150
- token: "{{ .PublicToken }}",
213
+ // Optional, specify a language to localize Link
151
214
language: 'en',
152
215
onLoad: function() {
153
216
// Optional, called when Link loads
@@ -169,6 +232,100 @@ var relinkTemplate string = `<html>
169
232
// metadata contains information about the institution
170
233
// that the user selected and the most recent API request IDs.
171
234
// Storing this information can be helpful for support.
235
+
236
+ document.getElementById("alert").classList.remove("hidden");
237
+ },
238
+ onEvent: function(eventName, metadata) {
239
+ // Optionally capture Link flow events, streamed through
240
+ // this callback as your users connect an Item to Plaid.
241
+ // For example:
242
+ // eventName = "TRANSITION_VIEW"
243
+ // metadata = {
244
+ // link_session_id: "123-abc",
245
+ // mfa_type: "questions",
246
+ // timestamp: "2017-09-14T14:42:19.350Z",
247
+ // view_name: "MFA",
248
+ // }
249
+ }
250
+ });
251
+
252
+ handler.open();
253
+
254
+ })(jQuery);
255
+ </script>
256
+
257
+ <div id="alert" class="alert-success hidden">
258
+ <div>
259
+ <h2>All done here!</h2>
260
+ <p>You can close this window and go back to plaid-cli.</p>
261
+ </div>
262
+ </div>
263
+ </body>
264
+ </html> `
265
+
266
+ var relinkTemplate string = `<html>
267
+ <head>
268
+ <style>
269
+ .alert-success {
270
+ font-size: 1.2em;
271
+ font-family: Arial, Helvetica, sans-serif;
272
+ background-color: #008000;
273
+ color: #fff;
274
+ display: flex;
275
+ justify-content: center;
276
+ align-items: center;
277
+ border-radius: 15px;
278
+ width: 100%;
279
+ height: 100%;
280
+ }
281
+ .hidden {
282
+ visibility: hidden;
283
+ }
284
+ </style>
285
+ </head>
286
+ <body>
287
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.3/jquery.min.js"></script>
288
+ <script src="https://cdn.plaid.com/link/v2/stable/link-initialize.js"></script>
289
+ <script type="text/javascript">
290
+ (function($) {
291
+ var handler = Plaid.create({
292
+ clientName: 'plaid-cli',
293
+ // Optional, specify an array of ISO-3166-1 alpha-2 country
294
+ // codes to initialize Link; European countries will have GDPR
295
+ // consent panel
296
+ countryCodes: ['US'],
297
+ env: '{{ .Environment }}',
298
+ // Replace with your public_key from the Dashboard
299
+ key: '{{ .PublicKey }}',
300
+ product: ['transactions'],
301
+ token: "{{ .PublicToken }}",
302
+ language: 'en',
303
+ onLoad: function() {
304
+ // Optional, called when Link loads
305
+ },
306
+ onSuccess: function(public_token, metadata) {
307
+ console.log("onSuccess");
308
+ // Send the public_token to your app server.
309
+ // The metadata object contains info about the institution the
310
+ // user selected and the account ID or IDs, if the
311
+ // Select Account view is enabled.
312
+ $.post('/link', {
313
+ public_token: public_token,
314
+ });
315
+ },
316
+ onExit: function(err, metadata) {
317
+ if (err != null) {
318
+ console.log(err);
319
+
320
+ // Handle manual relink when credential is already valid
321
+ if (err.error_code == "item-no-error") {
322
+ $.post('/link', {
323
+ public_token: "{{ .PublicToken }}",
324
+ });
325
+ }
326
+ }
327
+
328
+ document.getElementById("alert").classList.remove("hidden");
172
329
},
173
330
onEvent: function(eventName, metadata) {
174
331
// Optionally capture Link flow events, streamed through
@@ -188,5 +345,12 @@ var relinkTemplate string = `<html>
188
345
189
346
})(jQuery);
190
347
</script>
348
+
349
+ <div id="alert" class="alert-success hidden">
350
+ <div>
351
+ <h2>All done here!</h2>
352
+ <p>You can close this window and go back to plaid-cli.</p>
353
+ </div>
354
+ </div>
191
355
</body>
192
356
</html>`
0 commit comments