Dwolla partners with Plaid to provide customers with bank account verification through Secure Exchange solution. Bank accounts are verified and linked with purpose-built exchanges between the Dwolla and Plaid platforms.
While bank account verification is required before initiating an ACH transaction from a funding source, Dwolla clients are able to choose between Dwolla's micro-deposit solution, Open Banking solution, other third-party data aggregators via Secure Exchange, such as Mastercard, MX, or, as we will discuss in further detail in this guide, Plaid.
Let's first go over a few items you need to check off before you begin your integration.
Adding a bank is a one-time process that verifies the account ownership of the underlying bank account being added by an end user. At the end of this guide, you'll obtain a Funding Source URL, which is a unique identifier that represents a bank account being used for ACH debits and/or credits.
Once you have your Dwolla and Plaid accounts set up, you can begin the integration process. For the following steps, we will make use of two Plaid libraries: Plaid Link (more specifically, react-plaid-link), Plaid Link's React bindings, and plaid-node, Plaid's server-side Node SDK.
In order to instantiate a Plaid Link instance — in our case, by using usePlaidLink
— you first need to have your server generate a Link token. To learn more about what properties are available, check out Plaid's Create Link Token documentation.
Though you are welcome to use additional Plaid products and customize the LinkTokenCreateRequest
object to your liking, a couple properties are required in order to be compatible with Dwolla:
country_codes
must include "US" (or an enumerative equivalent), as Dwolla is currently only able to transact with U.S.-based bank accounts.products
must include "auth" (or an enumerative equivalent), as Dwolla will fetch the associated account and routing number using Plaid Auth.import type { LinkTokenCreateRequest, LinkTokenCreateResponse } from "plaid";
import { CountryCode, Products } from "plaid";
import { v4 as uuidv4 } from "uuid";
const createLinkToken = async (): Promise<LinkTokenCreateResponse> => {
const request: LinkTokenCreateRequest = {
client_name: "Dwolla-Plaid Integration Example",
country_codes: [CountryCode.Us],
language: "en",
products: [Products.Auth],
user: { client_user_id: uuidv4() },
redirect_uri: "http://localhost:3000",
};
return (await plaidClient.linkTokenCreate(request)).data;
};
Once your client environment has made the necessary requests to your server to fetch the Link token, you can now give that token to usePlaidLink
with an additional onSuccess
handler.
The onSuccess
handler will be responsible for proxying client requests to your server to, at a minimum,
import type { PlaidLinkOnSuccess } from "react-plaid-link";
import { usePlaidLink } from "react-plaid-link";
const handlePlaidLinkSuccess: PlaidLinkOnSuccess = async (
publicToken,
metadata
) => {
// When handling the Plaid Link token, you'll want to call the following functions
// in order to create a Dwolla funding source. Function implementations can be found below.
//
// [1] Exchange public token for an access token
// [2] Exchange access token & accountID for a processor token
// [3] Create a Dwolla exchange resource using the processor token
// [4] Create a funding source using the exchange
};
const { open, ready } = usePlaidLink({
onSuccess: handlePlaidLinkSuccess,
token: linkToken, // linkToken is the Link token that your server sent
});
Now that you have your public token, you will need to exchange it for an access token back in your server environment. An access token is an intermediate step between a public token and a processor token, and can be generated in a one-line JavaScript function using Plaid's Node SDK.
// 1. Exchange public token for an access token
export const exchangeForAccessToken = async (
publicToken: string
): Promise<string> =>
(await plaidClient.itemPublicTokenExchange({ public_token: publicToken }))
.data.access_token;
Continuing in your server environment, once you have an access token and an account ID (account ID is returned by Plaid Link in your client environment), you can now create a processor token!
Similar to how you created a Link token, when creating a processor token, you must specify Dwolla ("dwolla") as the processor. If you are using Plaid's SDK, you can use an enumerative equivalent, such as ProcessorTokenCreateRequestProcessorEnum.Dwolla
.
// 2. Exchange access token & accountID for a processor token
import { ProcessorTokenCreateRequestProcessorEnum } from "plaid";
export const exchangeForProcessorToken = async (
accessToken: string,
accountId: string
): Promise<string> =>
(
await plaidClient.processorTokenCreate({
access_token: accessToken,
account_id: accountId,
processor: ProcessorTokenCreateRequestProcessorEnum.Dwolla,
})
).data.processor_token;
Once your application has generated the processor token, you are now ready to integrate with Dwolla!
Before creating a funding source, your application will need to create a Customer. If you have not created a customer yet, check out our Create a Customer API reference, or our Create a Business Verified Customer or Create a Personal Verified Customer guides.
Once you have a customer set up, we will use Dwolla's server-side Node SDK to create the exchange.
The creation of an exchange serves as a “hand-shake” between Dwolla and Plaid. To create the exchange for a customer, you will supply two required properties: _link
and token
. In the API request, _link
defines a JSON object containing an exchange partner link relation, and token
defines the Plaid processor token that was generated in the previous step.
In the following function, once a response is received, it will extract the location
header value, which is the fully-qualified URL specifying the resource location of your exchange resource.
// 3. Create an exchange
// 3a. Retrieve the exchange partner link for Plaid
async function getExchangeHref(): Promise<string> {
const response = await dwollaClient.get("exchange-partners");
const partnersList = response.body._embedded["exchange-partners"];
const plaidPartner = partnersList.filter(
(obj: { name: string }) => "Plaid"
)[0];
return plaidPartner._links.self.href;
}
const exchangePartnerHref = getExchangeHref();
// 3b. Create an exchange using the exchange partner link and the processor token
interface CreateExchangeOptions {
customerId: string;
exchangePartnerHref: string;
token: string;
}
const createExchange = async (
options: CreateExchangeOptions
): Promise<string> =>
(
await dwollaClient.post(`customers/${options.customerId}/exchanges`, {
_links: {
"exchange-partner": {
href: options.exchangePartnerHref,
},
},
token: options.token,
})
).headers.get("location");
To create a verified funding source for a customer, you will supply three required properties: _link
, bankAccountType
, and name
. In the API request, _link
defines a JSON object containing an exchange link relation, bankAccountType
defines the type of the bank account: checking or savings, and name
defines an arbitrary name that you or your user will assign to the funding source.
In the following function, once a response is received, it will extract the location
header value, which is the fully-qualified URL specifying the resource location of your funding source.
// 4. Create a funding source using the exchange
interface CreateFundingSourceOptions {
customerId: string;
exchangeUrl: string;
fundingSourceName: string;
type: "checking" | "savings";
}
const createFundingSource = async (
options: CreateFundingSourceOptions
): Promise<string> =>
(
await dwollaClient.post(`customers/${options.customerId}/funding-sources`, {
_links: {
exchange: {
href: options.exchangeUrl,
},
},
bankAccountType: options.type,
name: options.fundingSourceName,
})
).headers.get("location");
processor_token
is received?When creating a funding source, a Plaid processor_token
value is passed in via an exchange resource. With this information, Dwolla executes a call to Plaid's API to securely retrieve the account and routing number and creates a funding source on your behalf. Upon success, Dwolla returns a URL that represents the new funding source via the location
response header.
Yes, if the end user has 2FA enabled with their bank, Plaid will attempt to mimic their bank's login flow, meaning that the user should receive a 2FA code, and is subsequently prompted to enter it in on the next screen.
access_token
and processor_token
changes or expires, what happens to the Dwolla funding source?If the processor_token
has already been used to create a funding source, then the token changing or expiring will not affect it; Dwolla only uses the processor_token
to fetch the account and routing number from Plaid and then immediately discards it.
However, if you have not yet used the processor_token
to create a funding source, then you will need to follow Plaid's process for creating a new token before sending it over to Dwolla.
Yes, it is possible to use Plaid Link to both automatically verify bank accounts and manually verify via micro-deposits if the bank is not supported or an error occurs during automatic verification.
No. Since Dwolla only uses Plaid to securely retrieve the account and routing number, any changes to the underlying Plaid account will not affect the funding source(s) that have already been created in Dwolla.
This issue can occur when the user's bank utilizes Tokenized Account Numbers (TANs) for bank verification, and Plaid provides Dwolla with the TAN instead of the actual Account Number. Dwolla processes the ACH payment using the TAN. If the user has disabled TAN transactions on their bank's website, the ACH payment made by Dwolla will fail with an ACH return (e.g., R04 - Invalid Account Number).
To resolve this issue, please advise the user to contact their bank or visit their bank's website and enable TANs for future transfers. In some cases, it might be necessary to re-add the Plaid verified bank using a new processor token.
All funds transfers made using the Dwolla Platform are performed by a financial institution partner, and any funds held in a Dwolla Balance are held by a financial institution partner. Learn more about our financial institution partners.