My website can create a link token with Plaid, but whenever I attempt to send this data back to the server, some of the data is missing.
Heres’ my .csthml file
@section scripts_upper
{
<!-- Include the Plaid Link initialize script on each page of your site. -->
<!-- It should always be loaded directly from https://cdn.plaid.com, rather -->
<!-- than included in a bundle or hosted yourself. -->
<script src="https://cdn.plaid.com/link/v2/stable/link-initialize.js"></script>
}
@model SS.PFS.WebApp.Models.PlaidLinkResults
@{
ViewBag.Title = "Link";
}
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<h2>Link</h2>
<p>
LinkToken: @ViewBag.LinkToken
</p>
<p>
Accesstoken: @ViewBag.Accesstoken
</p>
<div class="col-md-offset-2 col-md-10">
<input type="button" value="Link" onclick="launchPlaid()" class="btn btn-default" />
</div>
<label>Plaid Public Token</label>
<br>
<p id='plaidToken'></p>
<BR>
<label>MetaData</label>
<br>
<textarea id='plaidMetadata2' rows='30' cols='100'></textarea>
}
@section scripts
{
<script>
function launchPlaid() {
const handler = Plaid.create({
//token: document.getElementById('linkToken').value,
token: '@ViewBag.LinkToken',
// The onSuccess() function of the Plaid plugin will return a public
// token and metadata object to your code. Please use our API to send
// us the public token, and we will query the routing number and account
// number of the bank account from Plaid and store it securely on our systems.
onSuccess: (public_token, metadata) => {
document.getElementById('plaidToken').innerHTML = public_token;
document.getElementById('plaidMetadata2').value = JSON.stringify(metadata, null, 2);
//testPost(JSON.stringify(metadata, null, 2));
// testPost(metadata);
testPost(JSON.stringify(metadata));
return true;
},
onLoad: () => { },
onExit: (err, metadata) => {
document.getElementById('plaidToken').innerHTML = err;
document.getElementById('plaidMetadata2').value = JSON.stringify(metadata, null, 2);
},
onEvent: (eventName, metadata) => { },
receivedRedirectUri: null,
});
// Calling open() will display the "Institution Select" view to your user,
// starting the Link flow.
handler.open();
}
function testPost(valueToPost) {
$.ajax({
type: "POST",
url: "/BankAccount/Link",
data: '{ results: ' + valueToPost+ '}',
// data: valueToPost,
//contentType: "application/json; charset=utf-8",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (response) {
alert("Hello: " + response.Name + " .nCurrent Date and Time: " + response.DateTime);
},
failure: function (response) {
alert('failure: ' + response.responseText);
},
error: function (response) {
//alert('error: ' + response.responseText);
//document.getElementById('plaidMetadata2').value = response.responseText;
document.getElementById('plaidMetadata2').value = valueToPost;
}
});
}
</script>
}
Here’s the /BankAccount/Link controller/action
[HttpPost]
public async Task<ActionResult> Link(Models.PlaidLinkResults results)
{
var resultsetToSaveAsLinkedAccounts = results;
return View("Index");
}
I can see data filled in as the final result is displayed in plaidMetadata2. it contains values for Accounts, but properties like Institution->Id, LinkSessionId, and PublicToken are empty. Accounts has some data too.
Here’s an example of the data from the browser debugging tools
Here’s the PlaidLinkResults model
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace SS.PFS.WebApp.Models
{
public class PlaidLinkResults
{
[JsonProperty("institution") ]
public Acklann.Plaid.Entity.Institution Institution { get; set; }
public Acklann.Plaid.Entity.Account Account { get; set; }
public string AccountId { get; set; }
public List<Acklann.Plaid.Entity.Account> Accounts { get; set; }
[JsonProperty("link_session_id")]
public string LinkSessionId { get; set; }
[JsonProperty("public_token")]
public string PublicToken { get; set; }
}
}
Here’s the code for the institution model
using Acklann.Plaid.Institution;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace Acklann.Plaid.Entity
{
/// <summary>
/// Represents a banking institution.
/// </summary>
public class Institution
{
/// <summary>
/// Gets or sets the identifier.
/// </summary>
/// <value>The identifier.</value>
[JsonProperty("institution_id")]
public string Id { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
[JsonProperty("name")]
public string Name { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance has Multi-Factor Authentication.
/// </summary>
/// <value><c>true</c> if this instance has Multi-Factor Authentication; otherwise, <c>false</c>.</value>
[JsonProperty("has_mfa")]
public bool HasMfa { get; set; }
/// <summary>
/// Gets or sets the Multi-Factor Authentication selections.
/// </summary>
/// <value>The mfa selections.</value>
[JsonProperty("mfa")]
public string[] MfaSelections { get; set; }
[JsonProperty("mfa_code_type")]
public string MfaType { get; set; }
/// <summary>
/// Gets or sets the hexadecimal representation of the primary color used by the institution.
/// </summary>
[JsonProperty("primary_color")]
public string PrimaryColor { get; set; }
/// <summary>
/// Gets or sets the Base64 encoded representation of the institution's logo.
/// </summary>
[JsonProperty("logo")]
public string Logo { get; set; }
/// <summary>
/// Gets or sets the URL for the institution's website.
/// </summary>
/// <value>
/// The URL.
/// </value>
[JsonProperty("url")]
public string Url { get; set; }
/// <summary>
/// Gets or sets the credentials.
/// </summary>
/// <value>The credentials.</value>
[JsonProperty("credentials")]
public Credential[] Credentials { get; set; }
/// <summary>
/// Gets or sets the products.
/// </summary>
/// <value>The products.</value>
[JsonProperty("products")]
public string[] Products { get; set; }
/// <summary>
/// Gets or sets the country codes using the ISO-3166-1 alpha-2 country code standard.
/// </summary>
/// <value>
/// The countries.
/// </value>
[JsonProperty("country_codes")]
public string[] Countries { get; set; }
/// <summary>
/// Gets or sets the information about the institution's current status.
/// </summary>
[JsonProperty("status")]
public StatusSchema Status { get; set; }
/// <summary>
/// Represents an <see cref="Institution"/> login credentials.
/// </summary>
public struct Credential
{
/// <summary>
/// Gets or sets the label.
/// </summary>
/// <value>The label.</value>
[JsonProperty("label")]
public string Label { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
[JsonProperty("name")]
public string Name { get; set; }
/// <summary>
/// Gets or sets the type of the data.
/// </summary>
/// <value>The type of the data.</value>
[JsonProperty("type")]
public string DataType { get; set; }
}
public class StatusSchema
{
/// <summary>
/// Gets or sets status information regarding Item adds via Link.
/// </summary>
[JsonProperty("item_logins")]
public InstitutionStatus ItemLogin { get; set; }
/// <summary>
/// Gets or sets the status information regarding transactions updates.
/// </summary>
[JsonProperty("transactions_updates")]
public InstitutionStatus Transactions { get; set; }
/// <summary>
/// Gets or sets the status information regarding Auth requests..
/// </summary>
/// <value>
/// The authentication.
/// </value>
[JsonProperty("auth")]
public InstitutionStatus Auth { get; set; }
/// <summary>
/// Gets or sets the status information regarding Balance requests.
/// </summary>
/// <value>
/// The balance.
/// </value>
[JsonProperty("balacne")]
public InstitutionStatus Balance { get; set; }
/// <summary>
/// Gets or sets the status information regarding Identity requests.
/// </summary>
[JsonProperty("identity")]
public InstitutionStatus Identity { get; set; }
}
}
}
2
Answers
Most times this happens is because of malformed data being sent.
Noticed some issues with how you are formatting the data to be sent which leads to your JSON not being well formatted.
Note how the
results
field has no quotesbecause you try to build the string yourself
The model binder is then tasked with trying to infer the data needed from the malformed payload and map them to the model.
I would first suggest just passing the metadata to
testPost
function as isand let that function handle the formatting.
The action should also explicitly tell the model binder where to look for the data
So that it does not have to infer the data from one of the possible sources
Okay maybe I’m crazy but reading the Plaid HTML comment:
Now when I look at your onSuccess code:
You get both the token and metadata, but it looks to me like you keep trying to send them the metadata. They only ask for the public token. Why send metadata? Did I miss something?