Skip to content
This repository has been archived by the owner on Feb 25, 2022. It is now read-only.

Testing SaltEdge API to get access to hundreds of PSD2 banks #76

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ gem 'activesupport'
gem 'base32'
gem 'colorize'

# Dumper: SaltEdge
gem 'rest-client'
gem 'json'
gem 'pry'

group :development do
gem 'rubocop', '~> 0.52.1', require: false
gem 'rubocop-rspec'
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ This is a ruby script that **pulls your transactions from your banks** and impor
* Most German and Austrian banks _(all banks that implement the FinTS standard)_
* BBVA Spain _(private accounts only)_
* N26
* PSD2 Banks via SaltEdge API (tested with German bank: DKB)

**💡 Check out the [configuration guides for the dumpers and banks](https://github.com/schurig/ynab-bank-importer/wiki#supported-dumpers)**.

Expand All @@ -37,8 +38,6 @@ The script also includes some additional logic like detecting internal transacti

# Known Problems

* [Most banks can't be used anymore because of the PSD2](https://github.com/schurig/ynab-bank-importer/issues/74)
> We're investigating in alternative approaches to gain access to the latest transactions..
* Please read the notes in each Dumper _[(see Wiki)](https://github.com/schurig/ynab-bank-importer/wiki#supported-dumpers)_ to understand the limitations

____________________
Expand Down
3 changes: 3 additions & 0 deletions lib/dumper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ def self.get_dumper(name)
Dumper::Bbva
when :n26
Dumper::N26
when :saltedge
Dumper::SaltEdge
when :fints
Dumper::Fints
else
Expand All @@ -16,6 +18,7 @@ def self.get_dumper(name)

# rubocop:disable Metrics/MethodLength
def to_ynab_transaction(transaction)
#return nil if date(transaction) < Date.parse('2019-09-05')
return nil if date(transaction).nil? || date(transaction) > Date.today
::TransactionCreator.call(
account_id: account_id,
Expand Down
131 changes: 131 additions & 0 deletions lib/dumper/saltedge.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
class Dumper
class SaltEdge < Dumper
require 'digest/md5'
require 'rest-client'
require 'json'

WITHDRAWAL_CATEGORIES = [
'micro-v2-atm',
'micro-v2-cash26'
].freeze


def initialize(params = {})

@merchant_lookup = {}
@ynab_id = params.fetch('ynab_id')
@saltedge_app_id = params.fetch('saltedge_app_id')
@saltedge_secret = params.fetch('saltedge_secret')
@saltedge_connection_id = params.fetch('saltedge_connection_id')
@saltedge_account_id = params.fetch('saltedge_account_id')
@saltedge_transaction_count = params.fetch('saltedge_transaction_count')
@iban = params.fetch('iban')
end

def request(method, url, params={}, payload={})

RestClient::Request.execute(
method: method,
url: url,
payload: payload,
log: Logger.new(STDOUT),
headers: {
"Accept" => "application/json",
"Content-type" => "application/json",
:params => params,
"App-Id" => @saltedge_app_id,
"Secret" => @saltedge_secret
}
)
rescue RestClient::Exception => error
pp JSON.parse(error.response)
end

def fetch_transactions

data = request(:get, "https://www.saltedge.com/api/v5/transactions",
{
:connection_id => @saltedge_connection_id,
:account_id => @saltedge_account_id,
:per_page => @saltedge_transaction_count
})

transactions = JSON.parse(data.body)['data']

merchants = Set[]

transactions.each do |t|
merchants.add(t['extra']['merchant_id'])
end

merchants_details = request(:post, "https://www.saltedge.com/api/v4/merchants",
{
:connection_id => @saltedge_connection_id,
:account_id => @saltedge_account_id,
:per_page => @saltedge_transaction_count
}, '{"data": ' + merchants.to_json + '}')


merchants_result = JSON.parse(merchants_details.body)['data']

merchants_result.each do |t|
@merchant_lookup[t['id']] = t['names'][0]['value']
end

transactions.select { |t| accept?(t) }
.map { |t| to_ynab_transaction(t) }
end

def accept?(transaction)
return transaction['status'] == 'posted'
end

private

def account_id
@ynab_id
end

def date(transaction)
Date.parse(transaction['made_on'])
end

def payee_name(transaction)
merchant_id = transaction['extra']['merchant_id']
@merchant_lookup[merchant_id].try(:strip)

end

def payee_iban(transaction)
return nil
end

def category_name(transaction)
return nil
end

def memo(transaction)
[
transaction['description'],
].join(' ').try(:strip)
end

def amount(transaction)
(transaction['amount'].to_f * 1000).to_i
end

def withdrawal?(transaction)
WITHDRAWAL_CATEGORIES.include?(transaction['category'])
end

def import_id(transaction)
data = [transaction['id'],
transaction['account_id'],
transaction['amount'],
transaction['description']].join

Digest::MD5.hexdigest(data)
end

end
end