This is a repost of the orignal article at: https://popadex.com/2024/05/19/adding-plaid-to-rails-app
Integrating Plaid into your Rails application allows you to link bank accounts and retrieve financial data, providing a seamless way to access and manage financial information. This guide will walk you through the process of adding Plaid to your Rails app, assuming you have TailwindCSS already set up for styling. We will cover the necessary steps to configure the backend, set up routes, and create views for linking bank accounts and displaying account details.
Step 1: Add the Plaid Gem
First, you need to add the Plaid gem to your Gemfile. This gem provides the necessary methods to interact with the Plaid API.
Gemfile:
gem 'plaid'
Then, run:
bundle install
Step 2: Set Up Plaid Configuration
Next, you need to initialize Plaid in your application. Add the following code to your initializers:
config/initializers/plaid.rb:
# Initialize Plaid configuration
plaid_config = Plaid::Configuration.new
plaid_config.server_index = Plaid::Configuration::Environment["sandbox"] # or another environment as needed
plaid_config.api_key["PLAID-CLIENT-ID"] = Rails.application.credentials.dig(:development, :plaid, :client_id)
plaid_config.api_key["PLAID-SECRET"] = Rails.application.credentials.dig(:development, :plaid, :secret)
# Create an API client instance
api_client = Plaid::ApiClient.new(plaid_config)
# Create a Plaid API instance to use across the application
PlaidClient = Plaid::PlaidApi.new(api_client)
api_client.create_connection do |builder|
builder.use Faraday::Response::Logger
end
Step 3: Set Up Routes
Add the necessary routes to your config/routes.rb
file:
Rails.application.routes.draw do
root 'plaid#index'
post 'plaid/create_link_token', to: 'plaid#create_link_token'
post 'plaid/exchange_public_token', to: 'plaid#exchange_public_token'
get 'plaid/accounts', to: 'plaid#accounts'
end
Step 4: Create the Plaid Controller
Create a controller to handle Plaid operations:
app/controllers/plaid_controller.rb:
class PlaidController < ApplicationController
before_action :authenticate_user!
protect_from_forgery with: :null_session # to handle CSRF protection for API requests
def index
end
def create_link_token
user = current_user
link_token_create_request = Plaid::LinkTokenCreateRequest.new({
user: { client_user_id: user.id.to_s },
client_name: 'Your App Name',
products: %w[auth transactions],
country_codes: ['US'],
language: 'en'
})
begin
link_token_response = PlaidClient.link_token_create(link_token_create_request)
render json: { link_token: link_token_response.link_token }
rescue Plaid::ApiError => e
Rails.logger.error("Plaid API error: #{e.response_body}")
render json: { error: e.response_body }, status: :internal_server_error
end
end
def exchange_public_token
Rails.logger.debug("Received public_token: #{params[:public_token]}")
if params[:public_token].blank?
Rails.logger.error('No public_token received')
return render json: { error: 'No public_token received' }, status: :bad_request
end
begin
exchange_token(params[:public_token])
render json: { message: 'Bank account linked successfully.' }, status: :ok
rescue Plaid::ApiError => e
Rails.logger.error("Plaid API error: #{e.response_body}")
render json: { error: e.response_body }, status: :internal_server_error
end
end
def exchange_token(public_token)
request = Plaid::ItemPublicTokenExchangeRequest.new({ public_token: public_token })
response = PlaidClient.item_public_token_exchange(request)
access_token = response.access_token
item_id = response.item_id
Rails.logger.debug("Access token: #{access_token}")
Rails.logger.debug("Item ID: #{item_id}")
if current_user.update(plaid_access_token: access_token, plaid_item_id: item_id)
Rails.logger.debug('Access token and item ID saved successfully.')
Rails.logger.debug("Current user after save: #{current_user.inspect}")
else
Rails.logger.error("Failed to save access token and item ID. Errors: #{current_user.errors.full_messages.join(', ')}")
end
end
def accounts
access_token = current_user.plaid_access_token
if access_token.blank?
flash[:error] = 'Access token is missing. Please link your account again.'
return redirect_to root_path
end
begin
accounts_request = Plaid::AccountsGetRequest.new({ access_token: access_token })
accounts_response = PlaidClient.accounts_get(accounts_request)
@accounts = accounts_response.accounts
rescue Plaid::ApiError => e
Rails.logger.error("Plaid API error: #{e.response_body}")
flash[:error] = "Plaid API error: #{e.response_body}"
return redirect_to root_path
rescue StandardError => e
Rails.logger.error("Internal server error: #{e.message}")
flash[:error] = 'Internal server error'
return redirect_to root_path
end
end
end
Step 5: Create the View
Create a view to display the link button and account details.
app/views/plaid/index.html.erb:
<!DOCTYPE html>
<html class="dark">
<head>
<title>Account Details</title>
</head>
<body class="bg-white dark:bg-gray-900 text-black dark:text-white">
<div class="container mx-auto p-4">
<h1 class="text-3xl font-bold mb-4">Link Your Bank Account</h1>
<button id="link-button" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded dark:bg-gray-900 dark:hover:bg-gray-700 dark:text-white">Link Account</button>
<script src="https://cdn.plaid.com/link/v2/stable/link-initialize.js"></script>
<script>
document.getElementById('link-button').onclick = function() {
var csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
fetch('/plaid/create_link_token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken
},
body: JSON.stringify({})
})
.then(response => response.json())
.then(data => {
if (data.error) {
console.error('Error:', data.error);
return;
}
var linkHandler = Plaid.create({
token: data.link_token,
onSuccess: function(public_token, metadata) {
fetch('/plaid/exchange_public_token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken
},
body: JSON.stringify({ public_token: public_token })
})
.then(response => response.json())
.then(data => {
if (data.error) {
console.error('Error:', data.error);
return;
}
window.location.href = '/plaid/accounts';
});
},
onExit: function(err, metadata) {
console.log('Plaid link exit', err, metadata);
}
});
linkHandler.open();
})
.catch(error => {
console.error('Fetch error:', error);
});
};
</script>
</div>
</body>
</html>
app/views/plaid/accounts.html.erb:
<!DOCTYPE html>
<html class="dark">
<head>
<title>Account Details</title>
</head>
<body class="bg-white dark:bg-gray-900 text-black dark:text-white">
<div class="container mx-auto p-4">
<h1 class="text-3xl font-bold mb-4">Account Details</h1>
<% if @error %>
<div class="bg-red-500 text-white p-2 rounded mb-4">
Error: <%= @error %>
</div>
<% else %>
<div class="overflow-x-auto">
<table class="min-w-full bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700">
<thead>
<tr class="bg-gray-200 dark:bg-gray-700">
<th class="py-2 px-4 border-b border-gray-300 dark:border-gray-600">Name</th>
<th class="py-2 px-4 border-b border-gray-300 dark:border-gray-600">Type</th>
<th class="py-2 px-4 border-b border-gray-300 dark:border-gray-600">Subtype</th>
<th class="py-2 px-4 border-b border-gray-300 dark:border-gray-600">Mask</th>
<th class="py-2 px-4 border-b border-gray-300 dark:border-gray-600">Balance</th>
</tr>
</thead>
<tbody>
<% @accounts.each do |account| %>
<tr class="border-b border-gray-300 dark:border-gray-700">
<td class="py-2 px-4"><%= account.name %></td>
<td class="py-2 px-4"><%= account.type %></td>
<td class="py-2 px-4"><%= account.subtype %></td>
<td class="py-2 px-4"><%= account.mask %></td>
<td class="py-2 px-4"><%= number_to_currency(account.balances.available) %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% end %>
</div>
</body>
</html>
Conclusion
By following these steps, you can integrate Plaid into your Rails application to link bank accounts and display account details. This guide covers the necessary configuration, routes, controller actions, and views to set up Plaid. Make sure to handle API keys securely and configure the environment properly for production.
Top comments (1)
You write it in such a simple form. Thanks