mirror of
https://github.com/we-promise/sure.git
synced 2026-05-12 15:15:01 +00:00
feat(enable-banking): enhance transaction import, metadata handling, and UI (#1406)
* feat(enable-banking): enhance transaction import, metadata handling, and UI * fix(enable-banking): address security, sync edge cases and PR feedback * fix(enable-banking): resolve silent failures, auth overrides, and sync logic bugs * fix(enable-banking): resolve sync logic bugs, trailing whitespaces, and apply safe_psu_headers * test(enable-banking): mock set_current_balance to return success result * fix(budget): properly filter pending transactions and classify synced loan payments * style: fix trailing whitespace detected by rubocop * refactor: address code review feedback for Enable Banking sync and reporting --------- Signed-off-by: Louis <contact@boul2gom.com> Signed-off-by: Juan José Mata <juanjo.mata@gmail.com> Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
This commit is contained in:
@@ -35,13 +35,21 @@ class Provider::EnableBanking
|
||||
# @param aspsp_name [String] Name of the ASPSP from get_aspsps
|
||||
# @param aspsp_country [String] Country code for the ASPSP
|
||||
# @param redirect_url [String] URL to redirect user back to after auth
|
||||
# @param state [String] Optional state parameter to pass through
|
||||
# @param state [String, nil] State parameter to pass through
|
||||
# @param psu_type [String] "personal" or "business"
|
||||
# @param maximum_consent_validity [Integer, nil] Max consent duration in seconds from ASPSP (nil = use 90 days)
|
||||
# @param language [String, nil] Two-letter language code (e.g. "fr", "en")
|
||||
# @return [Hash] Contains :url and :authorization_id
|
||||
def start_authorization(aspsp_name:, aspsp_country:, redirect_url:, state: nil, psu_type: "personal")
|
||||
def start_authorization(aspsp_name:, aspsp_country:, redirect_url:, state: nil,
|
||||
psu_type: "personal", maximum_consent_validity: nil, language: nil)
|
||||
max_seconds = maximum_consent_validity ? [ maximum_consent_validity, 1 ].max : 90.days.to_i
|
||||
valid_until = [ Time.current + max_seconds.seconds, Time.current + 90.days ].min
|
||||
|
||||
body = {
|
||||
access: {
|
||||
valid_until: (Time.current + 90.days).iso8601
|
||||
valid_until: valid_until.iso8601,
|
||||
balances: true,
|
||||
transactions: true
|
||||
},
|
||||
aspsp: {
|
||||
name: aspsp_name,
|
||||
@@ -50,7 +58,9 @@ class Provider::EnableBanking
|
||||
state: state,
|
||||
redirect_url: redirect_url,
|
||||
psu_type: psu_type
|
||||
}.compact
|
||||
}
|
||||
body[:language] = language if language.present?
|
||||
body = body.compact
|
||||
|
||||
response = self.class.post(
|
||||
"#{BASE_URL}/auth",
|
||||
@@ -111,12 +121,13 @@ class Provider::EnableBanking
|
||||
|
||||
# Get account details
|
||||
# @param account_id [String] The account ID (UID from Enable Banking)
|
||||
# @param psu_headers [Hash] Optional PSU context headers required by some ASPSPs
|
||||
# @return [Hash] Account details
|
||||
def get_account_details(account_id:)
|
||||
def get_account_details(account_id:, psu_headers: {})
|
||||
encoded_id = CGI.escape(account_id.to_s)
|
||||
response = self.class.get(
|
||||
"#{BASE_URL}/accounts/#{encoded_id}/details",
|
||||
headers: auth_headers
|
||||
headers: auth_headers.merge(safe_psu_headers(psu_headers))
|
||||
)
|
||||
|
||||
handle_response(response)
|
||||
@@ -126,12 +137,13 @@ class Provider::EnableBanking
|
||||
|
||||
# Get account balances
|
||||
# @param account_id [String] The account ID (UID from Enable Banking)
|
||||
# @param psu_headers [Hash] Optional PSU context headers required by some ASPSPs
|
||||
# @return [Hash] Balance information
|
||||
def get_account_balances(account_id:)
|
||||
def get_account_balances(account_id:, psu_headers: {})
|
||||
encoded_id = CGI.escape(account_id.to_s)
|
||||
response = self.class.get(
|
||||
"#{BASE_URL}/accounts/#{encoded_id}/balances",
|
||||
headers: auth_headers
|
||||
headers: auth_headers.merge(safe_psu_headers(psu_headers))
|
||||
)
|
||||
|
||||
handle_response(response)
|
||||
@@ -144,18 +156,21 @@ class Provider::EnableBanking
|
||||
# @param date_from [Date, nil] Start date for transactions
|
||||
# @param date_to [Date, nil] End date for transactions
|
||||
# @param continuation_key [String, nil] For pagination
|
||||
# @param transaction_status [String, nil] Filter: "BOOK", "PDNG", or nil for all
|
||||
# @param psu_headers [Hash] Optional PSU context headers required by some ASPSPs
|
||||
# @return [Hash] Transactions and continuation_key for pagination
|
||||
def get_account_transactions(account_id:, date_from: nil, date_to: nil, continuation_key: nil)
|
||||
def get_account_transactions(account_id:, date_from: nil, date_to: nil,
|
||||
continuation_key: nil, transaction_status: nil, psu_headers: {})
|
||||
encoded_id = CGI.escape(account_id.to_s)
|
||||
query_params = {}
|
||||
query_params[:transaction_status] = "BOOK" # Only accounted transactions
|
||||
query_params[:transaction_status] = transaction_status if transaction_status.present?
|
||||
query_params[:date_from] = date_from.to_date.iso8601 if date_from
|
||||
query_params[:date_to] = date_to.to_date.iso8601 if date_to
|
||||
query_params[:continuation_key] = continuation_key if continuation_key
|
||||
|
||||
response = self.class.get(
|
||||
"#{BASE_URL}/accounts/#{encoded_id}/transactions",
|
||||
headers: auth_headers,
|
||||
headers: auth_headers.merge(safe_psu_headers(psu_headers)),
|
||||
query: query_params.presence
|
||||
)
|
||||
|
||||
@@ -166,6 +181,10 @@ class Provider::EnableBanking
|
||||
|
||||
private
|
||||
|
||||
def safe_psu_headers(headers)
|
||||
headers.except("Authorization", :Authorization, "Accept", :Accept, "Content-Type", :"Content-Type")
|
||||
end
|
||||
|
||||
def extract_private_key(certificate_pem)
|
||||
# Extract private key from PEM certificate
|
||||
OpenSSL::PKey::RSA.new(certificate_pem)
|
||||
@@ -215,6 +234,8 @@ class Provider::EnableBanking
|
||||
raise EnableBankingError.new("Access forbidden - check your application permissions", :access_forbidden)
|
||||
when 404
|
||||
raise EnableBankingError.new("Resource not found", :not_found)
|
||||
when 408
|
||||
raise EnableBankingError.new("Request timeout from Enable Banking API", :timeout)
|
||||
when 422
|
||||
raise EnableBankingError.new("Validation error from Enable Banking API: #{response.body}", :validation_error)
|
||||
when 429
|
||||
|
||||
Reference in New Issue
Block a user