mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-07-19 13:19:39 +02:00
fix: Plaid webhook verification (#1824)
* Fix Plaid webhook verification * Fix client creation in webhook controller
This commit is contained in:
parent
331de2f997
commit
5eb5ec7aef
5 changed files with 87 additions and 62 deletions
|
@ -53,7 +53,7 @@ module AccountableResource
|
|||
private
|
||||
def set_link_token
|
||||
@us_link_token = Current.family.get_link_token(
|
||||
webhooks_url: webhooks_url,
|
||||
webhooks_url: plaid_us_webhooks_url,
|
||||
redirect_url: accounts_url,
|
||||
accountable_type: accountable_type.name,
|
||||
region: :us
|
||||
|
@ -61,7 +61,7 @@ module AccountableResource
|
|||
|
||||
if Current.family.eu?
|
||||
@eu_link_token = Current.family.get_link_token(
|
||||
webhooks_url: webhooks_url,
|
||||
webhooks_url: plaid_eu_webhooks_url,
|
||||
redirect_url: accounts_url,
|
||||
accountable_type: accountable_type.name,
|
||||
region: :eu
|
||||
|
@ -69,11 +69,16 @@ module AccountableResource
|
|||
end
|
||||
end
|
||||
|
||||
def webhooks_url
|
||||
def plaid_us_webhooks_url
|
||||
return webhooks_plaid_url if Rails.env.production?
|
||||
|
||||
base_url = ENV.fetch("DEV_WEBHOOKS_URL", root_url.chomp("/"))
|
||||
base_url + "/webhooks/plaid"
|
||||
ENV.fetch("DEV_WEBHOOKS_URL", root_url.chomp("/")) + "/webhooks/plaid"
|
||||
end
|
||||
|
||||
def plaid_eu_webhooks_url
|
||||
return webhooks_plaid_eu_url if Rails.env.production?
|
||||
|
||||
ENV.fetch("DEV_WEBHOOKS_URL", root_url.chomp("/")) + "/webhooks/plaid_eu"
|
||||
end
|
||||
|
||||
def accountable_type
|
||||
|
|
|
@ -6,8 +6,25 @@ class WebhooksController < ApplicationController
|
|||
webhook_body = request.body.read
|
||||
plaid_verification_header = request.headers["Plaid-Verification"]
|
||||
|
||||
Provider::Plaid.validate_webhook!(plaid_verification_header, webhook_body)
|
||||
Provider::Plaid.process_webhook(webhook_body)
|
||||
client = Provider::Plaid.new(Rails.application.config.plaid, region: :us)
|
||||
|
||||
client.validate_webhook!(plaid_verification_header, webhook_body)
|
||||
client.process_webhook(webhook_body)
|
||||
|
||||
render json: { received: true }, status: :ok
|
||||
rescue => error
|
||||
Sentry.capture_exception(error)
|
||||
render json: { error: "Invalid webhook: #{error.message}" }, status: :bad_request
|
||||
end
|
||||
|
||||
def plaid_eu
|
||||
webhook_body = request.body.read
|
||||
plaid_verification_header = request.headers["Plaid-Verification"]
|
||||
|
||||
client = Provider::Plaid.new(Rails.application.config.plaid_eu, region: :eu)
|
||||
|
||||
client.validate_webhook!(plaid_verification_header, webhook_body)
|
||||
client.process_webhook(webhook_body)
|
||||
|
||||
render json: { received: true }, status: :ok
|
||||
rescue => error
|
||||
|
|
|
@ -3,11 +3,11 @@ module Plaidable
|
|||
|
||||
class_methods do
|
||||
def plaid_us_provider
|
||||
Provider::Plaid.new(Rails.application.config.plaid, :us) if Rails.application.config.plaid
|
||||
Provider::Plaid.new(Rails.application.config.plaid, region: :us) if Rails.application.config.plaid
|
||||
end
|
||||
|
||||
def plaid_eu_provider
|
||||
Provider::Plaid.new(Rails.application.config.plaid_eu, :eu) if Rails.application.config.plaid_eu
|
||||
Provider::Plaid.new(Rails.application.config.plaid_eu, region: :eu) if Rails.application.config.plaid_eu
|
||||
end
|
||||
|
||||
def plaid_provider_for_region(region)
|
||||
|
|
|
@ -4,9 +4,16 @@ class Provider::Plaid
|
|||
MAYBE_SUPPORTED_PLAID_PRODUCTS = %w[transactions investments liabilities].freeze
|
||||
MAX_HISTORY_DAYS = Rails.env.development? ? 90 : 730
|
||||
|
||||
class << self
|
||||
def initialize(config, region: :us)
|
||||
@client = Plaid::PlaidApi.new(
|
||||
Plaid::ApiClient.new(config)
|
||||
)
|
||||
@region = region
|
||||
end
|
||||
|
||||
def process_webhook(webhook_body)
|
||||
parsed = JSON.parse(webhook_body)
|
||||
|
||||
type = parsed["webhook_type"]
|
||||
code = parsed["webhook_code"]
|
||||
|
||||
|
@ -22,6 +29,9 @@ class Provider::Plaid
|
|||
else
|
||||
Rails.logger.warn("Unhandled Plaid webhook type: #{type}:#{code}")
|
||||
end
|
||||
rescue => error
|
||||
# Processing errors shouldn't return a 400 to Plaid since they are internal, so capture silently
|
||||
Sentry.capture_exception(error)
|
||||
end
|
||||
|
||||
def validate_webhook!(verification_header, raw_body)
|
||||
|
@ -54,14 +64,6 @@ class Provider::Plaid
|
|||
actual_hash = Digest::SHA256.hexdigest(raw_body)
|
||||
raise JWT::VerificationError, "Invalid webhook body hash" unless ActiveSupport::SecurityUtils.secure_compare(expected_hash, actual_hash)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(config, region)
|
||||
@client = Plaid::PlaidApi.new(
|
||||
Plaid::ApiClient.new(config)
|
||||
)
|
||||
@region = region
|
||||
end
|
||||
|
||||
def get_link_token(user_id:, webhooks_url:, redirect_url:, accountable_type: nil)
|
||||
request = Plaid::LinkTokenCreateRequest.new({
|
||||
|
|
|
@ -182,6 +182,7 @@ Rails.application.routes.draw do
|
|||
|
||||
namespace :webhooks do
|
||||
post "plaid"
|
||||
post "plaid_eu"
|
||||
post "stripe"
|
||||
end
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue