NerdWallet has evolved over the years, from providing long-form financial information and interactive calculators to offering a diverse set of logged-in services. Now, users can link their bank accounts, see where they are spending money, get their credit score, etc. As time went by, we wanted to do more for our users so that they can not only view their financial situation, but take direct action to save money. One of the most crucial utilities for saving money is the ability to move money, for example, allowing a user to move money from a checking account to a high-yield savings account where they can earn more interest.
ACH Network & Dwolla
One of the most common ways of moving money between bank accounts in the US is through the Automated Clearing House (ACH) network run by the National Automated Clearing House Associated (NACHA). The ACH network is extremely robust and is commonly used for direct deposits, paying mortgages and other bills, and much more. According to www.nacha.org, 23 billion payments were processed in the ACH network in 2018, totaling more than $50 trillion. While extremely popular, ACH is difficult to use directly. To make it easier to use the ACH network, NerdWallet decided to partner with an ACH facilitator: Dwolla.
Dwolla provides a modern interface to leverage the relatively old (but extremely stable) ACH network. Dwolla’s RESTful APIs enable us to onboard a user, add funding sources, and initiate ACH transfers. Dwolla also sends webhooks to notify us of various state changes that happen which our systems need to react to appropriately.
High Level Requirements
In order to provide users with the utility to move money, we identified some core requirements for what the system must enable:
Users will undergo additional levels of verification.
We will have all users of this utility undergo a user verification process called Know Your Customer (KYC). We want to be confident that users that move money on our platform do not have malicious intentions, and are who they say they are.
Users can select NerdWallet-linked accounts for transferring.
NerdWallet already allows users to link their bank accounts (more details in the Account Aggregator section). We want to leverage this when the user goes to make a transfer, allowing them to simply select accounts they have linked.
Users can transfer money between their selected accounts (ACH).
Perhaps the most straightforward requirement – users must be able to transfer money. They must be allowed to select the amount (within limits) and choose the source and destination accounts (again within limits). Users must also have the ability to cancel their transfers when possible.
Users will receive emails about relevant events.
Users must receive email communications when certain events occur. Examples of this include: user passed KYC, a transfer was cancelled, a transfer is on its way, a transfer completed, a transfer failed, etc. The purpose of these communications is to keep the user informed on relevant bits of information around this experience while also complying with industry best-practices.
NerdWallet will attempt to verify the user owns their selected accounts.
NerdWallet will use its account aggregation partner to perform Instant Account Verification (IAV). This purpose of this step is to verify the user actually owns the account they want to use in the transfer.
NerdWallet will enforce granular limits on user actions.
NerdWallet has many product requirements around money transfer. For example, how many transfers a user can do in a day / week / month, how many transfers can be done per account in a day / week / month, etc.
NerdWallet will attempt to prevent transfers that will fail.
NerdWallet will do its best to prevent initiation of transfers that will end up failing. For example, we will perform balance checks on the source account in order to avoid overdrafting.
NerdWallet will handle the lifecycle of the transfer.
A transfer has a surprisingly complicated lifecycle from the moment it is initiated to when it reaches a terminal state, and even beyond. From a user’s point of view, a transfer can be pending, successful, canceled, or failed. However, what a user sees as a transfer between two accounts is much more complicated with numerous internal states. The section on Multi-Hop Transfers touches on some of the complexity.
Nerdwallet will attempt to prevent fraudulent behavior.
NerdWallet will attempt to prevent fraudulent actions by users. As mentioned before, KYC will prevent many malicious users from using the money transfer system. For those malicious users who make it past KYC, we have other safeguards. One example is an integration with a partner that specializes in detecting fraudulent behavior. We will call out to this anti-fraud system at various points and will block actions as necessary.
NerdWallet will keep detailed records of all events.
NerdWallet will keep relevant details of a user’s historical state in regards to money transfer persisted in a durable datastore.
Money Movement Platform
The scope of items to consider while building the money movement platform can be overwhelming if we didn’t logically separate them into groups of related items. Our groups are modeled very similarly to how Dwolla modeled their API. The three main groups we have are user, account, and transfer.
The state machine below shows a simplified user journey. First, the user onboards with our money movement platform. Second, the user onboards at least two bank accounts. Third, the user initiates a transfer between two accounts they onboarded.
This is the stage where the user is onboarded to our money movement platform (and with Dwolla).
KYC and KBA
NerdWallet only collects the minimum information needed for each of its services. Because of that, a user can become a registered user without much effort and without providing many personal details. However, transferring money is a powerful utility and we need to collect more details about the user to verify their identity and to be confident that the user is not malicious.
As mentioned before, we need to go through a process called Know Your Customer (KYC) with Dwolla for each user we want to onboard on our money movement platform. We collect information about the user such as their legal name, date of birth, SSN, etc. We provide this to Dwolla to perform this verification. If Dwolla is unable to perform the verification with these standard personal information, then we can make use of Dwolla’s Knowledge Based Authentication (KBA). KBA allows users to be presented with personal questions that they can answer to try to get verified.
This is the stage where a linked account is added to the money movement platform (and with Dwolla).
Many of the services that NerdWallet currently offer make use of data from account aggregators (e.g. Yodlee, Plaid). If a user links their banking institution (such as Wells Fargo) with an account aggregator via NerdWallet, then NerdWallet is able to pull data on these accounts (transactions, balances, etc.). NerdWallet uses this data to provide meaningful services to the end user.
When building the money movement functionality for users, we want users to have the same seamless experience when selecting their accounts to use for money transfer. By leveraging an account aggregator, we can pull the necessary information needed for transferring as well (account number, routing number, and account holder names). This enables us to not require users to manually enter their account numbers (which may not be easily accessible) and it allows us to implement additional security measures (we know the account holder names from a trustable source which can be compared with their legal name).
This is the stage where the user initiates a transfer between two accounts using the money movement platform (and with Dwolla).
Eligibility and Limits
Before a transfer can be initiated, we need to check many things. We need to make sure the user is in a good state (e.g. the user didn’t get suspended), the two accounts selected for transfer are in a good state (e.g. an account didn’t get unlinked), and the user has not reached any of the transfer limits. We have many product defined transfer limits designed to prevent abuse of our systems in order to help protect our users and NerdWallet. We have limits around how many transfers a user can do in a day, week, and month. We have limits around how much money a user can move in a day, week, month. We have limits based on the account types in question (i.e. savings or checking).
If we attempt to perform a transfer without enough money in the source account, it could result in overdraft fees for the user. Before we start the transfer, we will try to check if there is sufficient balance in the source account. Since the balance data comes from the account aggregator, we will check to see if the data is fresh. If the data isn’t fresh, we will trigger a refresh and queue up the transfer to be retried by an offline task. The “freshness” is determined by an internal formula. We can never be 100% sure that there will be enough money for the transfer to be successful, but we can do our best with the information we have at hand (e.g. a user can always withdraw money right after the transfer is initiated, but before being processed by Dwolla).
When we were getting ready to build our money movement platform a little over a year ago, one of the biggest things we needed to account for was that Dwolla did not have support for transferring money between a user’s own bank accounts. For example, if the user onboarded two bank accounts with Dwolla (let’s say a checking account and a savings account), a transfer cannot be made to move money between them directly.
However, a user can do the following types of transfers:
- From the user’s bank account to another user’s bank account.
- From the user’s bank account to their internal Dwolla balance.
- From the user’s internal Dwolla balance to their bank account.
Since NerdWallet’s primary reason for initially building this platform was to allow users to move money between their own accounts, we clearly needed to bridge this gap ourselves. From the list of allowable transfer types above, one can guess how we can potentially utilize transfers into and out of the user’s internal Dwolla balance to build out the capability to transfer between two bank accounts owned by the same user.
What we need to do is create two intermediate transfers for every transfer the user attempts:
- A transfer from the source bank account to the user’s internal Dwolla balance.
- A transfer from the user’s internal Dwolla balance to the destination bank account.
The completion of both these intermediate transfers will result in what is exposed to the end user as a single transfer between two accounts owned by the user.
When doing two intermediate transfers for a single user transfer, we run into many corner cases. For example, what happens when the first intermediate transfer succeeds, but the second intermediate transfer fails? Money is stuck in the user’s Dwolla balance! To handle this (and other cases), we have built out reversal logic. In this case, the system will automatically create an intermediate transfer to return the money from the Dwolla balance back to the source account. We purposefully delay this reversal until we are confident that a malicious user is not forcing us to move money out of the Dwolla balance.
Dwolla allows cancellation up until the first 4 PM CT on a business day after initiating a transfer. We offer users the same criteria for cancelling their transfer, however, NerdWallet is able to cancel for an extended period of time due to the intermediate transfer design. Internally, a user transfer is not complete until both the intermediate transfers are completed. This means we can cancel at the following stages of the transfer lifecycle:
1. The first intermediate transfer was initiated and has not passed the cancel window.
The user and NerdWallet can both cancel the transfer at this time. We simply cancel the first intermediate transfer and stop the second intermediate transfer from starting.
2. The first intermediate transfer has passed the cancel window, but has not completed.
This one is a little tricky: we cannot cancel the first intermediate transfer anymore. We get around this by first marking it for cancellation and when it finally completes, we reverse it. Then we prevent the second intermediate transfer from starting.
3. The first intermediate transfer has been completed, the second has started but hasn’t passed the cancel window.
We simply cancel the second transfer and allow the reversal logic to return money from the Dwolla balance back to the source.
Standard versus Next-day Transfers
Another consideration about transfers is the processing speed. Dwolla provides us with some options in terms of how quickly a transfer is processed. However, there is some risk of fraud associated with speeding up transfers from their standard processing times, thus we have a formula for dynamically determining the transfer speed based on risk.
The focus of this article was on the core pieces of the backend money movement platform. To avoid making this even longer, we left out many crucial pieces such as the mobile and web clients which takes the user through the journey by interacting with multiple backend services, the integration with our comms platform which leverages Iterable to send emails, internal admin tools, monitoring & alerting, and much more. We also didn’t get a chance to discuss the tech stack and the design decisions around why we have multiple services that make up the money movement platform (i.e. we have a Flask application in Python and a Spring Boot application in Kotlin). It took many people, across multiple disciplines to come together to make money transfer a successful offering at NerdWallet. One of the biggest takeaways from building a system like this is that there will always be ways to improve and iterate on the platform as use-cases evolve.