I'm trying to use the Plaid development environment to test out the plaid API. I am creating a command line cli for plaid so I currently don't have a need for a server, so currently I generate a link token with Go and render the token into an HTML blob. When I open that HTML file in my browser and click the "Link account" button it hangs indefinitely.
Here is the go code generating the link and rendering into the HTML:
package main
import (
"context"
_ "embed"
"html/template"
"log"
"os"
"time"
"github.com/plaid/plaid-go/plaid"
"github.com/vrischmann/envconfig"
)
type config struct {
PlaidApi struct {
ClientId string `envconfig:"PLAID_CLIENT_ID"`
Secret string `envconfig:"PLAID_SECRET"`
}
}
//go:embed token.html.tmpl
var tokenPage string
func main() {
cfg := config{}
if err := envconfig.Init(&cfg); err != nil {
log.Fatalf("failed to load config from environment: %s", err)
}
ctx := context.TODO()
plaidCfg := plaid.NewConfiguration()
plaidCfg.UseEnvironment(plaid.Development)
cli := plaid.NewAPIClient(plaidCfg)
phoneNumber := "+1 888 888-8888"
user := plaid.LinkTokenCreateRequestUser{
ClientUserId: "1",
PhoneNumber: &phoneNumber,
}
request := plaid.NewLinkTokenCreateRequest(
"Personal Finance App",
"en",
[]plaid.CountryCode{plaid.COUNTRYCODE_US},
user,
)
request.SetProducts([]plaid.Products{plaid.PRODUCTS_TRANSACTIONS})
request.SetSecret(cfg.PlaidApi.Secret)
request.SetClientId(cfg.PlaidApi.ClientId)
linkTokenCreateResp, _, err := cli.PlaidApi.LinkTokenCreate(ctx).LinkTokenCreateRequest(*request).Execute()
if err != nil {
if pErr, err := plaid.ToPlaidError(err); err == nil {
log.Printf("Error from plaid: %s", pErr.ErrorMessage)
}
log.Fatalf("failed to get link request: %s", err.Error())
}
log.Printf("link token: %s", linkTokenCreateResp.GetLinkToken())
tmplate, err := template.New("token.html").Parse(tokenPage)
if err != nil {
log.Printf("Html tmplate:\n%s", tokenPage)
log.Fatalf("Failed to create HTML template: %s", err)
}
page, err := os.CreateTemp("", "catnip_*.html")
if err != nil {
log.Fatalf("Failed to create temp file: %s", err)
}
defer os.Remove(page.Name())
data := struct {
LinkToken string
}{LinkToken: linkTokenCreateResp.LinkToken}
if err := tmplate.Execute(page, data); err != nil {
log.Printf("template data: %#v", data)
log.Printf("html template:\n%#v", tokenPage)
log.Fatalf("Failed to render token page: %s", err)
}
log.Printf("token page path:\n%s", page.Name())
time.Sleep(2 * time.Minute)
}
This is the template (token.html.tmpl
):
<button id="link-button">Link Account</button>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdn.plaid.com/link/v2/stable/link-initialize.js"></script>
<script type="text/javascript">
(async function($) {
var handler = Plaid.create({
// Create a new link_token to initialize Link
token: "{{ .LinkToken }}",
onLoad: function() {
console.log("link token: '{{ .LinkToken }}")
},
onSuccess: function(public_token, metadata) {
console.log("Success!");
},
onExit: function(err, metadata) {
if (err != null) {
console.log(err);
}
},
onEvent: function(eventName, metadata) {}
});
$('#link-button').on('click', function(e) {
handler.open();
});
})(jQuery);
</script>
I tried manually creating a link token (using the Plaid Postman collection) and hard-coding that link token into your HTML file. When I did that, it worked correctly for me: after pressing the button I briefly saw the spinner and then a Link window popped up.
I suggest you try the same thing. If it works, then the issue is either something related to how you are generating the Link token in code or how it is being sent to / ingested by the template. If it doesn't work, then the issue may be something client side specific to your configuration (maybe a browser plugin preventing Link from opening, for example)?