bankscrap-openbank 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b1b65647a5159a87170071f62b9e436746cc82e0
4
- data.tar.gz: f29fa5cf4c2ab71265e6c73beb806e71ce92ea0d
3
+ metadata.gz: 7ecc804e009d7b76359f51dddb7fcf92bd1cf48f
4
+ data.tar.gz: 878ed70101cccb3a52f9a76068ac96a368269147
5
5
  SHA512:
6
- metadata.gz: 9e0d44b99398209c21b48c6c34ac64b6c6c2d9af3d3aaaa4ea5da443024f0bb5a5687493841b6d16a22c9738ef62ec4b2937efefa567b8f6cf94777759e0bbfd
7
- data.tar.gz: 7deac7f651ea8e0445b4bcd390358755a0ea89ec4af3976a706ebde8c6b1a642e2baff4df3eae68b2b7e34192d2ba72fba10fc71ce2979a72eca4fa138961fe5
6
+ metadata.gz: c9ef1956b469746dc95cabc64051a824018ff3e24655b553fe315b174dfe1d1dfb4cefc31c66ddc2ab028c4492f1cd55578d455ba88f9c1555d57f823c18bacf
7
+ data.tar.gz: 38274e655d05e2ba0f6a103d38cfc4e597c7e0101d88a02b0028921131473d607f3cdf2bfe0400b1380c9be8e72525f4fb5658954dd47a526c362870de1cad2c
@@ -7,7 +7,7 @@ module Bankscrap
7
7
 
8
8
  attr_accessor :contract_id
9
9
 
10
- ACCOUNT_ENDPOINT = '/OPB_BAMOBI_WS_ENS/ws/BAMOBI_WS_Def_Listener'.freeze
10
+ ACCOUNT_ENDPOINT = '/my-money/cuentas/movimientos'.freeze
11
11
 
12
12
  # Fetch transactions for the given account.
13
13
  # By default it fetches transactions for the last month,
@@ -15,63 +15,32 @@ module Bankscrap
15
15
  # Returns an array of BankScrap::Transaction objects
16
16
  def fetch_transactions_for(connection, start_date: Date.today - 1.month, end_date: Date.today)
17
17
  transactions = []
18
- end_page = false
19
- repo = nil
20
- importe_cta = nil
21
18
 
19
+ fields = { producto: contract_id,
20
+ numeroContrato: id,
21
+ fechaDesde: format_date(start_date),
22
+ fechaHasta: format_date(end_date),
23
+ concepto: '000' }
22
24
  # Loop over pagination
23
- until end_page
24
- document = connection.post(ACCOUNT_ENDPOINT, fields: xml_account(connection, start_date, end_date, repo, importe_cta))
25
-
26
- transactions += document.xpath('//listadoMovimientos/movimiento').map { |data| build_transaction(data) }
27
-
28
- repo = document.at_xpath('//methodResult/repo')
29
- importe_cta = document.at_xpath('//methodResult/importeCta')
30
- end_page = value_at_xpath(document, '//methodResult/finLista') != 'N'
25
+ until fields.empty?
26
+ data = connection.get(ACCOUNT_ENDPOINT, fields: fields)
27
+ transactions += data['movimientos'].map { |item| build_transaction(item) }
28
+ fields = next_page_fields(data)
31
29
  end
32
30
 
33
31
  transactions
34
32
  end
35
33
 
36
- def xml_account(connection, from_date, to_date, repo, importe_cta)
37
- is_pagination = repo ? 'S' : 'N'
38
- xml_from_date = xml_date(from_date)
39
- xml_to_date = xml_date(to_date)
40
- <<-account
41
- <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://www.isban.es/webservices/BAMOBI/Cuentas/F_bamobi_cuentas_lip/internet/BAMOBICTA/v1">
42
- #{connection.xml_security_header}
43
- <soapenv:Body>
44
- <v1:listaMovCuentasFechas_LIP facade="BAMOBICTA">
45
- <entrada>
46
- #{connection.xml_datos_cabecera}
47
- <datosConexion>#{connection.user_data.children}</datosConexion>
48
- <contratoID>#{contract_id}</contratoID>
49
- <fechaDesde>#{xml_from_date}</fechaDesde>
50
- <fechaHasta>#{xml_to_date}</fechaHasta>
51
- #{importe_cta}
52
- <esUnaPaginacion>#{is_pagination}</esUnaPaginacion>
53
- #{repo}
54
- </entrada>
55
- </v1:listaMovCuentasFechas_LIP>
56
- </soapenv:Body>
57
- </soapenv:Envelope>
58
- account
59
- end
60
-
61
- def xml_date(date)
62
- "<dia>#{date.day}</dia><mes>#{date.month}</mes><anyo>#{date.year}</anyo>"
63
- end
64
-
65
34
  # Build a transaction object from API data
66
35
  def build_transaction(data)
67
36
  Transaction.new(
68
37
  account: self,
69
- id: value_at_xpath(data, 'numeroMovimiento'),
70
- amount: money(data, 'importe'),
71
- description: value_at_xpath(data, 'descripcion'),
72
- effective_date: Date.strptime(value_at_xpath(data, 'fechaValor'), '%Y-%m-%d'),
73
- # TODO: Bankscrap has no interface to add operation date
74
- balance: money(data, 'importeSaldo')
38
+ id: data['nummov'],
39
+ amount: money(data['importe']),
40
+ description: data['conceptoTabla'],
41
+ effective_date: parse_date(data['fechaValor']),
42
+ operation_date: parse_date(data['fechaOperacion']),
43
+ balance: money(data['saldo'])
75
44
  )
76
45
  end
77
46
  end
@@ -1,5 +1,6 @@
1
1
  require 'bankscrap'
2
2
  require 'securerandom'
3
+ require 'json'
3
4
  require 'byebug'
4
5
  require_relative 'account.rb'
5
6
  require_relative 'card.rb'
@@ -10,20 +11,18 @@ module Bankscrap
10
11
  class Bank < ::Bankscrap::Bank
11
12
  include Utils
12
13
 
13
- attr_accessor :user_data
14
-
15
14
  # Define the endpoints for the Bank API here
16
- BASE_ENDPOINT = 'https://www.openbank.mobi'.freeze
17
- LOGIN_ENDPOINT = '/OPBMOV_IPAD_NSeg_ENS/ws/QUIZ_Def_Listener'.freeze
18
- PRODUCTS_ENDPOINT = '/OPB_BAMOBI_WS_ENS/ws/BAMOBI_WS_Def_Listener'.freeze
19
- USER_AGENT = 'Dalvik/1.6.0 (Linux; U; Android 4.4.4; XT1032 Build/KXB21.14-L1.40)'.freeze
15
+ BASE_ENDPOINT = 'https://api.openbank.es'.freeze
16
+ LOGIN_ENDPOINT = '/authenticationcomposite/login'.freeze
17
+ PRODUCTS_ENDPOINT = '/posicion-global-total'.freeze
18
+ USER_AGENT = 'okhttp/3.9.1'.freeze
20
19
 
21
20
  def initialize(credentials = {})
21
+ @token_credential = nil
22
22
  super do
23
23
  add_headers(
24
- 'Content-Type' => 'text/xml; charset=utf-8',
24
+ 'Content-Type' => 'application/json; charset=utf-8',
25
25
  'User-Agent' => USER_AGENT,
26
- 'Host' => 'www.openbank.mobi',
27
26
  'Connection' => 'Keep-Alive',
28
27
  'Accept-Encoding' => 'gzip'
29
28
  )
@@ -32,7 +31,20 @@ module Bankscrap
32
31
 
33
32
  def login
34
33
  log 'login'
35
- post(LOGIN_ENDPOINT, fields: xml_login(public_ip))
34
+ login_data = {
35
+ document: @user,
36
+ documentType: "N",
37
+ password: @password,
38
+ force: true,
39
+ osVersion: "8.1.0",
40
+ uuid: "#{SecureRandom.hex(10)[0..2]}-#{SecureRandom.hex(10)[0..6]}",
41
+ mobileDeviceInfo: { pushEnabled:false,
42
+ rooted: false,
43
+ version: "1.1.16",
44
+ device: "SAMSUNG",
45
+ platform: "ANDROID" }
46
+ }
47
+ post(LOGIN_ENDPOINT, fields: login_data)
36
48
  end
37
49
 
38
50
  # Fetch all the accounts for the given user
@@ -40,11 +52,19 @@ module Bankscrap
40
52
  # Should returns an array of Bankscrap::Account objects
41
53
  def fetch_accounts
42
54
  log 'fetch_accounts'
43
- document = post(PRODUCTS_ENDPOINT, fields: xml_products)
44
- document.xpath('//cuentas/cuenta').map { |data| build_account(data) } +
45
- document.xpath('//tarjetas/tarjeta').map { |data| build_card(data) }
55
+ data = get(PRODUCTS_ENDPOINT, fields: { carteras: false,listaSolicitada: 'TODOS',indicadorSaldoPreTarj: false })
56
+ cuentas = data['datosSalidaCuentas']['cuentas'].zip(data['datosSalidaCodIban']['datosIban'])
57
+ cuentas.map{ |data| build_account(data) }
46
58
  end
47
59
 
60
+ # Fetch all the cards for the given user
61
+ #
62
+ # Should returns an array of Bankscrap::Card objects
63
+ def fetch_cards
64
+ log 'fetch_cards'
65
+ data = get(PRODUCTS_ENDPOINT, fields: { carteras: false,listaSolicitada: 'TODOS',indicadorSaldoPreTarj: false })
66
+ data['datosSalidaTarjetas']['tarjetas'].map{ |data| build_card(data) }
67
+ end
48
68
  # Fetch transactions for the given account.
49
69
  #
50
70
  # Account should be a Bankscrap::Account object
@@ -53,57 +73,32 @@ module Bankscrap
53
73
  product.fetch_transactions_for(self, start_date: start_date, end_date: end_date)
54
74
  end
55
75
 
56
- def xml_security_header
57
- <<-security
58
- <soapenv:Header>
59
- <wsse:Security SOAP-ENV:actor="http://www.isban.es/soap/actor/wssecurityB64" SOAP-ENV:mustUnderstand="1" S12:role="wsssecurity" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:S12="http://www.w3.org/2003/05/soap-envelope" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
60
- <wsse:BinarySecurityToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SSOToken" ValueType="esquema" EncodingType="hwsse:Base64Binary">#{@token_credential}</wsse:BinarySecurityToken>
61
- </wsse:Security>
62
- </soapenv:Header>
63
- security
64
- end
65
-
66
- def xml_datos_cabecera
67
- <<-datos
68
- <datosCabecera>
69
- <version>3.0.4</version>
70
- <terminalID>Android</terminalID>
71
- <idioma>es-ES</idioma>
72
- </datosCabecera>
73
- datos
76
+ def post(method, fields: {})
77
+ set_auth_headers()
78
+ response = super(BASE_ENDPOINT + method, fields: JSON.generate(fields))
79
+ parse_context(response)
74
80
  end
75
81
 
76
- def post(method, fields: {})
77
- response = super(BASE_ENDPOINT + method, fields: fields)
82
+ def get(method, fields: {})
83
+ set_auth_headers()
84
+ response = super(BASE_ENDPOINT + method, params: fields)
78
85
  parse_context(response)
79
86
  end
80
87
 
81
88
  private
82
89
 
83
- def xml_products
84
- <<-products
85
- <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://www.isban.es/webservices/BAMOBI/Posglobal/F_bamobi_posicionglobal_lip/internet/BAMOBIPGL/v1">
86
- #{xml_security_header}
87
- <soapenv:Body>
88
- <v1:obtenerPosGlobal_LIP facade="BAMOBIPGL">
89
- <entrada>#{xml_datos_cabecera}</entrada>
90
- </v1:obtenerPosGlobal_LIP>
91
- </soapenv:Body>
92
- </soapenv:Envelope>
93
- products
94
- end
95
-
96
90
  # Build an Account object from API data
97
91
  def build_account(data)
92
+ account, iban = data
98
93
  Account.new(
99
94
  bank: self,
100
- id: value_at_xpath(data, 'comunes/contratoID/NUMERO_DE_CONTRATO'),
101
- name: value_at_xpath(data, 'comunes/descContrato'),
102
- available_balance: money(data, 'importeDispAut'),
103
- balance: money(data, 'impSaldoActual'),
104
- iban: value_at_xpath(data, 'IBAN').tr(' ', ''),
105
- description: value_at_xpath(data, 'comunes/alias') || value_at_xpath(data, 'comunes/descContrato'),
106
- contract_id: data.at_xpath('contratoIDViejo').children.to_s
95
+ id: account['cviejo']['numerodecontrato'],
96
+ name: account.fetch('descripcion', '').strip(),
97
+ available_balance: money(account['saldoActual']),
98
+ balance: money(account['saldoActual']),
99
+ iban: iban['codIban'].values().join.strip,
100
+ description: account.fetch('descripcion', '').strip(),
101
+ contract_id: account['cviejo']['subgrupo']
107
102
  )
108
103
  end
109
104
 
@@ -111,53 +106,31 @@ module Bankscrap
111
106
  def build_card(data)
112
107
  Card.new(
113
108
  bank: self,
114
- id: value_at_xpath(data, 'pan'),
115
- name: value_at_xpath(data, 'comunes/descContrato'),
116
- available_balance: money(data, 'impDisponible'),
117
- balance: money(data, 'impSaldoDispuesto'),
118
- iban: value_at_xpath(data, 'comunes/contratoID'),
119
- description: "#{value_at_xpath(data, 'comunes/alias') || value_at_xpath(data, 'comunes/descContrato')} (#{value_at_xpath(data, 'descTipoTarjeta')})",
120
- contract_id: data.at_xpath('comunes/contratoID').children.to_s
109
+ id: data['contrato']['numerodecontrato'],
110
+ name: data['panmdp'],
111
+ avaliable: money(data['saldoDisponible']),
112
+ amount: money(data['saldoDispuesto']),
113
+ pan: data['panmdp'],
114
+ description: data['descripcion'],
115
+ contract_id: data['contrato']['producto'],
116
+ is_credit: data['tipoTarjeta'].to_s == "credit"
121
117
  )
122
118
  end
123
119
 
124
- def parse_context(xml)
125
- document = Nokogiri::XML(xml)
126
- @cookie_credential = value_at_xpath(document, '//cookieCredential', @cookie_credential)
127
- @token_credential = value_at_xpath(document, '//tokenCredential', @token_credential)
128
- self.user_data = document.at_xpath('//methodResult/datosUsuario') || user_data
129
- document
130
- end
131
-
132
- def public_ip
133
- log 'getting public ip'
134
- ip = open('http://api.ipify.org').read
135
- log "public ip: [#{ip}]"
136
- ip
120
+ def parse_context(data)
121
+ context = JSON.parse(data)
122
+ @token_credential = context.fetch('tokenCredential', @token_credential)
123
+ context
137
124
  end
138
125
 
139
126
  def format_user(user)
140
127
  user.upcase
141
128
  end
142
129
 
143
- def xml_login(public_ip)
144
- <<-login
145
- <v:Envelope xmlns:v="http://schemas.xmlsoap.org/soap/envelope/" xmlns:c="http://schemas.xmlsoap.org/soap/encoding/" xmlns:d="http://www.w3.org/2001/XMLSchema" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
146
- <v:Header />
147
- <v:Body>
148
- <n0:authenticateCredential xmlns:n0="http://www.isban.es/webservices/TECHNICAL_FACADES/Security/F_facseg_security/internet/loginServicesNSegWS/v1" facade="loginServicesNSegWS">
149
- <CB_AuthenticationData i:type=":CB_AuthenticationData">
150
- <documento i:type=":documento">
151
- <CODIGO_DOCUM_PERSONA_CORP i:type="d:string">#{@user}</CODIGO_DOCUM_PERSONA_CORP>
152
- <TIPO_DOCUM_PERSONA_CORP i:type="d:string">N</TIPO_DOCUM_PERSONA_CORP>
153
- </documento>
154
- <password i:type="d:string">#{@password}</password>
155
- </CB_AuthenticationData>
156
- <userAddress i:type="d:string">#{public_ip}</userAddress>
157
- </n0:authenticateCredential>
158
- </v:Body>
159
- </v:Envelope>
160
- login
130
+ def set_auth_headers
131
+ headers = {}
132
+ headers['openbankauthtoken'] = @token_credential unless @token_credential.nil?
133
+ add_headers(headers) unless headers.empty?
161
134
  end
162
135
  end
163
136
  end
@@ -2,10 +2,12 @@ require_relative 'utils.rb'
2
2
 
3
3
  module Bankscrap
4
4
  module Openbank
5
- class Card < Account
5
+ class Card < ::Bankscrap::Card
6
6
  include Utils
7
7
 
8
- CARD_ENDPOINT = '/OPB_BAMOBI_WS_ENS/ws/BAMOBI_WS_Def_Listener'.freeze
8
+ attr_accessor :contract_id
9
+
10
+ CARD_ENDPOINT = '/my-money/tarjetas/movimientosCategoria'.freeze
9
11
 
10
12
  # Fetch transactions for the given account.
11
13
  # By default it fetches transactions for the last month,
@@ -13,60 +15,36 @@ module Bankscrap
13
15
  # Returns an array of BankScrap::Transaction objects
14
16
  def fetch_transactions_for(connection, start_date: Date.today - 1.month, end_date: Date.today)
15
17
  transactions = []
16
- end_page = false
17
- repos = nil
18
-
18
+ fields = { producto: contract_id,
19
+ numeroContrato: id,
20
+ pan: pan,
21
+ fechaDesde: format_date(start_date),
22
+ fechaHasta: format_date(end_date)
23
+ }
19
24
  # Loop over pagination
20
- until end_page
21
- document = connection.post(CARD_ENDPOINT, fields: xml_card(connection, start_date, end_date, repos))
22
-
23
- transactions += document.xpath('//lista/dato').map { |data| build_transaction(data) }
24
-
25
- repos = document.at_xpath('//methodResult/repos')
26
- end_page = value_at_xpath(document, '//methodResult/finLista') != 'N'
25
+ until fields.empty?
26
+ data = connection.get(CARD_ENDPOINT, fields: fields)
27
+ transactions += data['lista']['movimientos'].map { |data| build_transaction(data) }.compact
28
+ fields = next_page_fields(data)
27
29
  end
28
30
 
29
31
  transactions
30
32
  end
31
33
 
32
- def xml_card(connection, from_date, to_date, repos)
33
- is_pagination = repos ? 'S' : 'N'
34
- xml_from_date = xml_date(from_date)
35
- xml_to_date = xml_date(to_date)
36
- <<-card
37
- <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://www.isban.es/webservices/BAMOBI/Tarjetas/F_bamobi_tarjetas_lip/internet/BAMOBITAJ/v1">
38
- #{connection.xml_security_header}
39
- <soapenv:Body>
40
- <v1:listaMovTarjetasFechas_LIP facade="BAMOBITAJ">
41
- <entrada>
42
- <datosConexion>#{connection.user_data.children}</datosConexion>
43
- #{connection.xml_datos_cabecera}
44
- <contratoTarjeta>#{contract_id}</contratoTarjeta>
45
- <numeroTarj>#{id}</numeroTarj>
46
- <fechaDesde>#{xml_from_date}</fechaDesde>
47
- <fechaHasta>#{xml_to_date}</fechaHasta>
48
- <esUnaPaginacion>#{is_pagination}</esUnaPaginacion>
49
- #{repos}
50
- </entrada>
51
- </v1:listaMovTarjetasFechas_LIP>
52
- </soapenv:Body>
53
- </soapenv:Envelope>
54
- card
55
- end
56
-
57
- def xml_date(date)
58
- "<dia>#{date.day}</dia><mes>#{date.month}</mes><anyo>#{date.year}</anyo>"
34
+ def fetch_transactions(start_date: Date.today - 2.years, end_date: Date.today)
35
+ fetch_transactions_for(bank, start_date: start_date, end_date: end_date)
59
36
  end
60
37
 
61
38
  # Build a transaction object from API data
62
39
  def build_transaction(data)
40
+ return if data['estadoPeticion'] == 'L'
63
41
  Transaction.new(
64
42
  account: self,
65
- id: value_at_xpath(data, 'movimDia'),
66
- amount: money(data, 'importeMovto'),
67
- description: value_at_xpath(data, 'descMovimiento'),
68
- effective_date: Date.strptime(value_at_xpath(data, 'fechaAnota'), '%Y-%m-%d'),
69
- # operation_date: Date.strptime(value_at_xpath(data, 'fechaOpera'), "%Y-%m-%d"),
43
+ id: data['numeroMovimintoEnDia'],
44
+ amount: money(data['impOperacion']),
45
+ description: data['txtCajero'],
46
+ effective_date: parse_date(data['fechaAnotacionMovimiento']),
47
+ operation_date: parse_date(data['fechaMovimiento']),
70
48
  balance: Money.new(0, 'EUR') # TODO: Prepaid/debit cards don't have a Balance - maybe Credit ones do.
71
49
  )
72
50
  end
@@ -1,17 +1,28 @@
1
1
  module Bankscrap
2
2
  module Openbank
3
3
  module Utils
4
- def value_at_xpath(node, xpath, default = '')
5
- value = node.at_xpath(xpath)
6
- value ? value.content.strip : default
7
- end
8
4
 
9
- def money(data, xpath)
5
+ def money(data)
10
6
  Money.new(
11
- value_at_xpath(data, xpath + '/IMPORTE').delete('.'),
12
- value_at_xpath(data, xpath + '/DIVISA')
7
+ data['importe'] * 100.0,
8
+ data['divisa'].presence || 'EUR'
13
9
  )
14
10
  end
11
+
12
+ def parse_date(date)
13
+ Date.strptime(date, '%Y-%m-%d')
14
+ end
15
+
16
+ def format_date(date)
17
+ "%04d-%02d-%02d" % [date.year, date.month, date.day]
18
+ end
19
+
20
+ def next_page_fields(data)
21
+ link = data&.fetch('_links', nil)&.fetch('nextPage', nil)&.fetch('href', nil)
22
+ return {} unless link
23
+ uri = URI.parse(link)
24
+ URI::decode_www_form(uri.query).to_h
25
+ end
15
26
  end
16
27
  end
17
28
  end
@@ -1,5 +1,5 @@
1
1
  module Bankscrap
2
2
  module Openbank
3
- VERSION = '2.0.0'.freeze
3
+ VERSION = '3.0.0'.freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bankscrap-openbank
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - zjuanma
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-04-29 00:00:00.000000000 Z
11
+ date: 2018-08-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bankscrap
@@ -104,7 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
104
104
  version: '0'
105
105
  requirements: []
106
106
  rubyforge_project:
107
- rubygems_version: 2.4.8
107
+ rubygems_version: 2.6.14
108
108
  signing_key:
109
109
  specification_version: 4
110
110
  summary: Openbank adapter for Bankscrap