mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-07-19 13:19:39 +02:00
parent
4bf72506d5
commit
53f4b32c33
19 changed files with 91 additions and 86 deletions
|
@ -52,12 +52,21 @@ module AccountableResource
|
||||||
|
|
||||||
private
|
private
|
||||||
def set_link_token
|
def set_link_token
|
||||||
@link_token = Current.family.get_link_token(
|
@us_link_token = Current.family.get_link_token(
|
||||||
webhooks_url: webhooks_url,
|
webhooks_url: webhooks_url,
|
||||||
redirect_url: accounts_url,
|
redirect_url: accounts_url,
|
||||||
accountable_type: accountable_type.name,
|
accountable_type: accountable_type.name,
|
||||||
region: Current.family.country.to_s.downcase == "us" ? :us : :eu
|
region: :us
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if Current.family.eu?
|
||||||
|
@eu_link_token = Current.family.get_link_token(
|
||||||
|
webhooks_url: webhooks_url,
|
||||||
|
redirect_url: accounts_url,
|
||||||
|
accountable_type: accountable_type.name,
|
||||||
|
region: :eu
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def webhooks_url
|
def webhooks_url
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Controller } from "@hotwired/stimulus";
|
||||||
export default class extends Controller {
|
export default class extends Controller {
|
||||||
static values = {
|
static values = {
|
||||||
linkToken: String,
|
linkToken: String,
|
||||||
region: { type: String, default: "us" }
|
region: { type: String, default: "us" },
|
||||||
};
|
};
|
||||||
|
|
||||||
open() {
|
open() {
|
||||||
|
@ -19,7 +19,7 @@ export default class extends Controller {
|
||||||
handler.open();
|
handler.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSuccess(public_token, metadata) {
|
handleSuccess = (public_token, metadata) => {
|
||||||
window.location.href = "/accounts";
|
window.location.href = "/accounts";
|
||||||
|
|
||||||
fetch("/plaid_items", {
|
fetch("/plaid_items", {
|
||||||
|
@ -32,7 +32,7 @@ export default class extends Controller {
|
||||||
plaid_item: {
|
plaid_item: {
|
||||||
public_token: public_token,
|
public_token: public_token,
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
region: this.regionValue
|
region: this.regionValue,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
|
@ -40,17 +40,17 @@ export default class extends Controller {
|
||||||
window.location.href = response.url;
|
window.location.href = response.url;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
handleExit(err, metadata) {
|
handleExit = (err, metadata) => {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
};
|
||||||
|
|
||||||
handleEvent(eventName, metadata) {
|
handleEvent = (eventName, metadata) => {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
};
|
||||||
|
|
||||||
handleLoad() {
|
handleLoad = () => {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ class Account::BalanceCalculator
|
||||||
holdings_value = converted_holdings.select { |h| h.date == balance.date }.sum(&:amount)
|
holdings_value = converted_holdings.select { |h| h.date == balance.date }.sum(&:amount)
|
||||||
balance.balance = balance.balance + holdings_value
|
balance.balance = balance.balance + holdings_value
|
||||||
balance
|
balance
|
||||||
end
|
end.compact
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -76,29 +76,33 @@ class Account::Syncer
|
||||||
exchange_rates = ExchangeRate.find_rates(
|
exchange_rates = ExchangeRate.find_rates(
|
||||||
from: from_currency,
|
from: from_currency,
|
||||||
to: to_currency,
|
to: to_currency,
|
||||||
start_date: balances.first.date
|
start_date: balances.min_by(&:date).date
|
||||||
)
|
)
|
||||||
|
|
||||||
converted_balances = balances.map do |balance|
|
converted_balances = balances.map do |balance|
|
||||||
exchange_rate = exchange_rates.find { |er| er.date == balance.date }
|
exchange_rate = exchange_rates.find { |er| er.date == balance.date }
|
||||||
|
|
||||||
|
next unless exchange_rate.present?
|
||||||
|
|
||||||
account.balances.build(
|
account.balances.build(
|
||||||
date: balance.date,
|
date: balance.date,
|
||||||
balance: exchange_rate.rate * balance.balance,
|
balance: exchange_rate.rate * balance.balance,
|
||||||
currency: to_currency
|
currency: to_currency
|
||||||
) if exchange_rate.present?
|
)
|
||||||
end
|
end.compact
|
||||||
|
|
||||||
converted_holdings = holdings.map do |holding|
|
converted_holdings = holdings.map do |holding|
|
||||||
exchange_rate = exchange_rates.find { |er| er.date == holding.date }
|
exchange_rate = exchange_rates.find { |er| er.date == holding.date }
|
||||||
|
|
||||||
|
next unless exchange_rate.present?
|
||||||
|
|
||||||
account.holdings.build(
|
account.holdings.build(
|
||||||
security: holding.security,
|
security: holding.security,
|
||||||
date: holding.date,
|
date: holding.date,
|
||||||
amount: exchange_rate.rate * holding.amount,
|
amount: exchange_rate.rate * holding.amount,
|
||||||
currency: to_currency
|
currency: to_currency
|
||||||
) if exchange_rate.present?
|
)
|
||||||
end
|
end.compact
|
||||||
|
|
||||||
Account.transaction do
|
Account.transaction do
|
||||||
load_balances(converted_balances)
|
load_balances(converted_balances)
|
||||||
|
|
|
@ -2,22 +2,25 @@ module Plaidable
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
class_methods do
|
class_methods do
|
||||||
def plaid_provider
|
def plaid_us_provider
|
||||||
Provider::Plaid.new if Rails.application.config.plaid
|
Provider::Plaid.new(Rails.application.config.plaid, :us) if Rails.application.config.plaid
|
||||||
end
|
end
|
||||||
|
|
||||||
def plaid_eu_provider
|
def plaid_eu_provider
|
||||||
Provider::Plaid.new if Rails.application.config.plaid_eu
|
Provider::Plaid.new(Rails.application.config.plaid_eu, :eu) if Rails.application.config.plaid_eu
|
||||||
end
|
end
|
||||||
|
|
||||||
def plaid_provider_for(plaid_item)
|
def plaid_provider_for_region(region)
|
||||||
return nil unless plaid_item
|
region.to_sym == :eu ? plaid_eu_provider : plaid_us_provider
|
||||||
plaid_item.eu? ? plaid_eu_provider : plaid_provider
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def plaid_provider_for(plaid_item)
|
def eu?
|
||||||
self.class.plaid_provider_for(plaid_item)
|
raise "eu? is not implemented for #{self.class.name}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def plaid_provider
|
||||||
|
eu? ? self.class.plaid_eu_provider : self.class.plaid_us_provider
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# rubocop:disable Layout/ElseAlignment, Layout/IndentationWidth
|
|
||||||
class Family < ApplicationRecord
|
class Family < ApplicationRecord
|
||||||
include Plaidable, Syncable
|
include Plaidable, Syncable
|
||||||
|
|
||||||
|
@ -48,22 +47,22 @@ class Family < ApplicationRecord
|
||||||
super || accounts.manual.any?(&:syncing?) || plaid_items.any?(&:syncing?)
|
super || accounts.manual.any?(&:syncing?) || plaid_items.any?(&:syncing?)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_link_token(webhooks_url:, redirect_url:, accountable_type: nil, region: :us)
|
def eu?
|
||||||
provider = case region
|
country != "US" && country != "CA"
|
||||||
when :eu
|
end
|
||||||
self.class.plaid_eu_provider
|
|
||||||
else
|
|
||||||
self.class.plaid_provider
|
|
||||||
end
|
|
||||||
|
|
||||||
return nil unless provider
|
def get_link_token(webhooks_url:, redirect_url:, accountable_type: nil, region: :us)
|
||||||
|
provider = if region.to_sym == :eu
|
||||||
|
self.class.plaid_eu_provider
|
||||||
|
else
|
||||||
|
self.class.plaid_us_provider
|
||||||
|
end
|
||||||
|
|
||||||
provider.get_link_token(
|
provider.get_link_token(
|
||||||
user_id: id,
|
user_id: id,
|
||||||
webhooks_url: webhooks_url,
|
webhooks_url: webhooks_url,
|
||||||
redirect_url: redirect_url,
|
redirect_url: redirect_url,
|
||||||
accountable_type: accountable_type,
|
accountable_type: accountable_type,
|
||||||
eu: region == :eu
|
|
||||||
).link_token
|
).link_token
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -238,4 +237,3 @@ class Family < ApplicationRecord
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# rubocop:enable Layout/ElseAlignment, Layout/IndentationWidth
|
|
||||||
|
|
|
@ -21,8 +21,8 @@ class PlaidItem < ApplicationRecord
|
||||||
scope :ordered, -> { order(created_at: :desc) }
|
scope :ordered, -> { order(created_at: :desc) }
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def create_from_public_token(token, item_name:, region: "us")
|
def create_from_public_token(token, item_name:, region:)
|
||||||
response = plaid_provider.exchange_public_token(token)
|
response = plaid_provider_for_region(region).exchange_public_token(token)
|
||||||
|
|
||||||
new_plaid_item = create!(
|
new_plaid_item = create!(
|
||||||
name: item_name,
|
name: item_name,
|
||||||
|
@ -59,11 +59,10 @@ class PlaidItem < ApplicationRecord
|
||||||
private
|
private
|
||||||
def fetch_and_load_plaid_data
|
def fetch_and_load_plaid_data
|
||||||
data = {}
|
data = {}
|
||||||
provider = plaid_provider_for(self)
|
item = plaid_provider.get_item(access_token).item
|
||||||
item = provider.get_item(access_token).item
|
|
||||||
update!(available_products: item.available_products, billed_products: item.billed_products)
|
update!(available_products: item.available_products, billed_products: item.billed_products)
|
||||||
|
|
||||||
fetched_accounts = provider.get_item_accounts(self).accounts
|
fetched_accounts = plaid_provider.get_item_accounts(self).accounts
|
||||||
data[:accounts] = fetched_accounts || []
|
data[:accounts] = fetched_accounts || []
|
||||||
|
|
||||||
internal_plaid_accounts = fetched_accounts.map do |account|
|
internal_plaid_accounts = fetched_accounts.map do |account|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
class Provider::Plaid
|
class Provider::Plaid
|
||||||
attr_reader :client
|
attr_reader :client, :region
|
||||||
|
|
||||||
MAYBE_SUPPORTED_PLAID_PRODUCTS = %w[transactions investments liabilities].freeze
|
MAYBE_SUPPORTED_PLAID_PRODUCTS = %w[transactions investments liabilities].freeze
|
||||||
MAX_HISTORY_DAYS = Rails.env.development? ? 90 : 730
|
MAX_HISTORY_DAYS = Rails.env.development? ? 90 : 730
|
||||||
|
@ -54,27 +54,22 @@ class Provider::Plaid
|
||||||
actual_hash = Digest::SHA256.hexdigest(raw_body)
|
actual_hash = Digest::SHA256.hexdigest(raw_body)
|
||||||
raise JWT::VerificationError, "Invalid webhook body hash" unless ActiveSupport::SecurityUtils.secure_compare(expected_hash, actual_hash)
|
raise JWT::VerificationError, "Invalid webhook body hash" unless ActiveSupport::SecurityUtils.secure_compare(expected_hash, actual_hash)
|
||||||
end
|
end
|
||||||
|
|
||||||
def client
|
|
||||||
api_client = Plaid::ApiClient.new(
|
|
||||||
Rails.application.config.plaid
|
|
||||||
)
|
|
||||||
|
|
||||||
Plaid::PlaidApi.new(api_client)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize
|
def initialize(config, region)
|
||||||
@client = self.class.client
|
@client = Plaid::PlaidApi.new(
|
||||||
|
Plaid::ApiClient.new(config)
|
||||||
|
)
|
||||||
|
@region = region
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_link_token(user_id:, webhooks_url:, redirect_url:, accountable_type: nil, eu: false)
|
def get_link_token(user_id:, webhooks_url:, redirect_url:, accountable_type: nil)
|
||||||
request = Plaid::LinkTokenCreateRequest.new({
|
request = Plaid::LinkTokenCreateRequest.new({
|
||||||
user: { client_user_id: user_id },
|
user: { client_user_id: user_id },
|
||||||
client_name: "Maybe Finance",
|
client_name: "Maybe Finance",
|
||||||
products: [ get_primary_product(accountable_type) ],
|
products: [ get_primary_product(accountable_type) ],
|
||||||
additional_consented_products: get_additional_consented_products(accountable_type),
|
additional_consented_products: get_additional_consented_products(accountable_type),
|
||||||
country_codes: get_country_codes(eu),
|
country_codes: country_codes,
|
||||||
language: "en",
|
language: "en",
|
||||||
webhook: webhooks_url,
|
webhook: webhooks_url,
|
||||||
redirect_uri: redirect_url,
|
redirect_uri: redirect_url,
|
||||||
|
@ -199,8 +194,8 @@ class Provider::Plaid
|
||||||
MAYBE_SUPPORTED_PLAID_PRODUCTS - [ get_primary_product(accountable_type) ]
|
MAYBE_SUPPORTED_PLAID_PRODUCTS - [ get_primary_product(accountable_type) ]
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_country_codes(eu)
|
def country_codes
|
||||||
if eu
|
if region.to_sym == :eu
|
||||||
[ "ES", "NL", "FR", "IE", "DE", "IT", "PL", "DK", "NO", "SE", "EE", "LT", "LV", "PT", "BE" ] # EU supported countries
|
[ "ES", "NL", "FR", "IE", "DE", "IT", "PL", "DK", "NO", "SE", "EE", "LT", "LV", "PT", "BE" ] # EU supported countries
|
||||||
else
|
else
|
||||||
[ "US", "CA" ] # US + CA only
|
[ "US", "CA" ] # US + CA only
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<%# locals: (path:, link_token: nil) %>
|
<%# locals: (path:, us_link_token: nil, eu_link_token: nil) %>
|
||||||
|
|
||||||
<%= render layout: "accounts/new/container", locals: { title: t(".title"), back_path: new_account_path } do %>
|
<%= render layout: "accounts/new/container", locals: { title: t(".title"), back_path: new_account_path } do %>
|
||||||
<div class="text-sm">
|
<div class="text-sm">
|
||||||
|
@ -9,24 +9,24 @@
|
||||||
<%= t("accounts.new.method_selector.manual_entry") %>
|
<%= t("accounts.new.method_selector.manual_entry") %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if link_token.present? %>
|
<% if us_link_token %>
|
||||||
<%# Default US-only Link %>
|
<%# Default US-only Link %>
|
||||||
<button data-controller="plaid" data-action="plaid#open modal#close" data-plaid-link-token-value="<%= @link_token %>" class="flex items-center gap-4 w-full text-center focus:outline-none focus:bg-gray-50 border border-transparent focus:border focus:border-gray-200 px-2 hover:bg-gray-50 rounded-lg p-2">
|
<button data-controller="plaid" data-action="plaid#open modal#close" data-plaid-region-value="us" data-plaid-link-token-value="<%= us_link_token %>" class="flex items-center gap-4 w-full text-center focus:outline-none focus:bg-gray-50 border border-transparent focus:border focus:border-gray-200 px-2 hover:bg-gray-50 rounded-lg p-2">
|
||||||
<span class="flex w-8 h-8 shrink-0 grow-0 items-center justify-center rounded-lg bg-alpha-black-50 shadow-[inset_0_0_0_1px_rgba(0,0,0,0.02)]">
|
<span class="flex w-8 h-8 shrink-0 grow-0 items-center justify-center rounded-lg bg-alpha-black-50 shadow-[inset_0_0_0_1px_rgba(0,0,0,0.02)]">
|
||||||
<%= lucide_icon("link-2", class: "text-gray-500 w-5 h-5") %>
|
<%= lucide_icon("link-2", class: "text-gray-500 w-5 h-5") %>
|
||||||
</span>
|
</span>
|
||||||
<%= t(".connected_entry") %>
|
<%= t("accounts.new.method_selector.connected_entry") %>
|
||||||
</button>
|
</button>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<%# EU Link %>
|
<%# EU Link %>
|
||||||
<% unless Current.family.country == "US" %>
|
<% if eu_link_token %>
|
||||||
<button data-controller="plaid" data-action="plaid#open modal#close" data-plaid-link-token-value="<%= Current.family.get_link_token(webhooks_url: webhooks_plaid_url, redirect_url: accounts_url, accountable_type: accountable_type.name, region: :eu) %>" class="flex items-center gap-4 w-full text-center focus:outline-none focus:bg-gray-50 border border-transparent focus:border focus:border-gray-200 px-2 hover:bg-gray-50 rounded-lg p-2">
|
<button data-controller="plaid" data-action="plaid#open modal#close" data-plaid-region-value="eu" data-plaid-link-token-value="<%= eu_link_token %>" class="flex items-center gap-4 w-full text-center focus:outline-none focus:bg-gray-50 border border-transparent focus:border focus:border-gray-200 px-2 hover:bg-gray-50 rounded-lg p-2">
|
||||||
<span class="flex w-8 h-8 shrink-0 grow-0 items-center justify-center rounded-lg bg-alpha-black-50 shadow-[inset_0_0_0_1px_rgba(0,0,0,0.02)]">
|
<span class="flex w-8 h-8 shrink-0 grow-0 items-center justify-center rounded-lg bg-alpha-black-50 shadow-[inset_0_0_0_1px_rgba(0,0,0,0.02)]">
|
||||||
<%= lucide_icon("link-2", class: "text-gray-500 w-5 h-5") %>
|
<%= lucide_icon("link-2", class: "text-gray-500 w-5 h-5") %>
|
||||||
</span>
|
</span>
|
||||||
<%= t(".connected_entry_eu") %>
|
<%= t("accounts.new.method_selector.connected_entry_eu") %>
|
||||||
</button>
|
</button>
|
||||||
<% end %>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<% if params[:step] == "method_select" %>
|
<% if params[:step] == "method_select" %>
|
||||||
<%= render "accounts/new/method_selector", path: new_credit_card_path(return_to: params[:return_to]), link_token: @link_token %>
|
<%= render "accounts/new/method_selector", path: new_credit_card_path(return_to: params[:return_to]), us_link_token: @us_link_token, eu_link_token: @eu_link_token %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= modal_form_wrapper title: t(".title") do %>
|
<%= modal_form_wrapper title: t(".title") do %>
|
||||||
<%= render "credit_cards/form", account: @account, url: credit_cards_path %>
|
<%= render "credit_cards/form", account: @account, url: credit_cards_path %>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<% if params[:step] == "method_select" %>
|
<% if params[:step] == "method_select" %>
|
||||||
<%= render "accounts/new/method_selector", path: new_crypto_path(return_to: params[:return_to]), link_token: @link_token %>
|
<%= render "accounts/new/method_selector", path: new_crypto_path(return_to: params[:return_to]), us_link_token: @us_link_token, eu_link_token: @eu_link_token %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= modal_form_wrapper title: t(".title") do %>
|
<%= modal_form_wrapper title: t(".title") do %>
|
||||||
<%= render "cryptos/form", account: @account, url: cryptos_path %>
|
<%= render "cryptos/form", account: @account, url: cryptos_path %>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<% if params[:step] == "method_select" %>
|
<% if params[:step] == "method_select" %>
|
||||||
<%= render "accounts/new/method_selector", path: new_depository_path(return_to: params[:return_to]), link_token: @link_token %>
|
<%= render "accounts/new/method_selector", path: new_depository_path(return_to: params[:return_to]), us_link_token: @us_link_token, eu_link_token: @eu_link_token %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= modal_form_wrapper title: t(".title") do %>
|
<%= modal_form_wrapper title: t(".title") do %>
|
||||||
<%= render "depositories/form", account: @account, url: depositories_path %>
|
<%= render "depositories/form", account: @account, url: depositories_path %>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<% if params[:step] == "method_select" %>
|
<% if params[:step] == "method_select" %>
|
||||||
<%= render "accounts/new/method_selector", path: new_investment_path(return_to: params[:return_to]), link_token: @link_token %>
|
<%= render "accounts/new/method_selector", path: new_investment_path(return_to: params[:return_to]), us_link_token: @us_link_token, eu_link_token: @eu_link_token %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= modal_form_wrapper title: t(".title") do %>
|
<%= modal_form_wrapper title: t(".title") do %>
|
||||||
<%= render "investments/form", account: @account, url: investments_path %>
|
<%= render "investments/form", account: @account, url: investments_path %>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<% if params[:step] == "method_select" %>
|
<% if params[:step] == "method_select" %>
|
||||||
<%= render "accounts/new/method_selector", path: new_loan_path(return_to: params[:return_to]), link_token: @link_token %>
|
<%= render "accounts/new/method_selector", path: new_loan_path(return_to: params[:return_to]), us_link_token: @us_link_token, eu_link_token: @eu_link_token %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= modal_form_wrapper title: t(".title") do %>
|
<%= modal_form_wrapper title: t(".title") do %>
|
||||||
<%= render "loans/form", account: @account, url: loans_path %>
|
<%= render "loans/form", account: @account, url: loans_path %>
|
||||||
|
|
|
@ -4,13 +4,12 @@ require "ostruct"
|
||||||
class PlaidItemsControllerTest < ActionDispatch::IntegrationTest
|
class PlaidItemsControllerTest < ActionDispatch::IntegrationTest
|
||||||
setup do
|
setup do
|
||||||
sign_in @user = users(:family_admin)
|
sign_in @user = users(:family_admin)
|
||||||
|
|
||||||
@plaid_provider = mock
|
|
||||||
|
|
||||||
PlaidItem.stubs(:plaid_provider).returns(@plaid_provider)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "create" do
|
test "create" do
|
||||||
|
@plaid_provider = mock
|
||||||
|
PlaidItem.expects(:plaid_provider_for_region).with("us").returns(@plaid_provider)
|
||||||
|
|
||||||
public_token = "public-sandbox-1234"
|
public_token = "public-sandbox-1234"
|
||||||
|
|
||||||
@plaid_provider.expects(:exchange_public_token).with(public_token).returns(
|
@plaid_provider.expects(:exchange_public_token).with(public_token).returns(
|
||||||
|
|
|
@ -4,9 +4,7 @@ module AccountableResourceInterfaceTest
|
||||||
extend ActiveSupport::Testing::Declarative
|
extend ActiveSupport::Testing::Declarative
|
||||||
|
|
||||||
test "shows new form" do
|
test "shows new form" do
|
||||||
Plaid::PlaidApi.any_instance.stubs(:link_token_create).returns(
|
Family.any_instance.stubs(:get_link_token).returns("test-link-token")
|
||||||
Plaid::LinkTokenCreateResponse.new(link_token: "test-link-token")
|
|
||||||
)
|
|
||||||
|
|
||||||
get new_polymorphic_url(@account.accountable)
|
get new_polymorphic_url(@account.accountable)
|
||||||
assert_response :success
|
assert_response :success
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue