Skip to content

Commit

Permalink
Merge branch 'bump-v4' into transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
Lauris Z authored Sep 3, 2019
2 parents 49983fc + a07e38a commit bf308b7
Show file tree
Hide file tree
Showing 41 changed files with 183 additions and 460 deletions.
4 changes: 2 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ source 'http://rubygems.org'

gemspec

gem 'activesupport', '~> 6.0'
gem 'activesupport', '~> 5.2.3'
gem 'minitest'
gem 'minitest-reporters'
gem 'net-http-persistent', '>= 3.0.0'
gem 'net-http-persistent'
gem 'rake'
gem 'rubocop'
69 changes: 14 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,66 +12,25 @@ First, you've to install the gem:
gem install figo
```

Now you can create a new session and access data:
Now you can retrieve data with an user bound session

```ruby
require "figo"
require 'figo'
connection = Figo::Connection.new(<client_id>, <client_secret>)

session = Figo::Session.new("ASHWLIkouP2O6_bgA2wWReRhletgWKHYjLqDaqb0LFfamim9RjexTo22ujRIP_cjLiRiSyQXyt2kM1eXU2XLFZQ0Hro15HikJQT_eNeT_9XQ")
# Trade in user credentials, refresh token or authorization code for access token.
token_hash = connection.user_credential_request(<username>, <password>) # or..
token_hash = connection.refresh_token_request(<refresh_token>) # or..
token_hash = connection.authorization_code_request(<authorization_code>, <redirect_uri>)

# Print out list of account numbers and balances.
# Start session.
session = Figo::Session.new(token_hash['access_token'])

# Retrieve data
session.accounts.each do |account|
puts account.account_number
puts account.balance.balance
end

# Print out the list of all transaction originators/recipients of a specific account.
session.get_account("A1.1").transactions.each do |transaction|
puts transaction.name
end
```

It is just as simple to allow users to login through the API:

```ruby
require "figo"
require "launchy"

connection = Figo::Connection.new("<client ID>", "<client secret>", "http://my-domain.org/redirect-url")

def start_login
# Open webbrowser to kick of the login process.
Launchy.open(connection.login_url("qweqwe"))
end

def process_redirect(authorization_code, state)
# Handle the redirect URL invocation from the initial start_login call.

# Ignore bogus redirects.
if state != "qweqwe"
return
end

# Trade in authorization code for access token.
token_hash = connection.obtain_access_token(authorization_code)

# Start session.
session = Figo::Session.new(token_hash["access_token"])

# Print out list of account numbers.
session.accounts.each do |account|
puts account.account_number
end
end
...
}
```

You can find more documentation at http://rubydoc.info/github/figo-connect/ruby-figo/master/frames

Demos
-----
In this repository you can also have a look at a simple console(`console_demo.rb`) and web demo(`web_demo`). While the console demo simply accesses the figo API, the web demo implements the full OAuth flow.

Requirements
------------

This gem requires Ruby 1.9.
You can find more documentation at http://rubydoc.info/github/figo-connect/ruby-figo/master/frames
2 changes: 0 additions & 2 deletions config.yml

This file was deleted.

16 changes: 0 additions & 16 deletions console_demo.rb

This file was deleted.

4 changes: 2 additions & 2 deletions lib/access/api_call.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ def accesses
# @return [Access] access object created
def add_access(access_method_id, credentials, consent)
data = { access_method_id: access_method_id, credentials: credentials, consent: consent }
query_api('/rest/accesses', data = data, method = 'POST')
query_api('/rest/accesses', data, 'POST')
end

# Retrieve specific access.
#
# @param access_id [String] ID of the access to be retrieved.
# @return [Access] access object
def get_access(access_id)
query_api_object Access, "/rest/accesses/#{access_id}", method = 'GET'
query_api_object Access, "/rest/accesses/#{access_id}", 'GET'
end

# Remove stored PIN.
Expand Down
6 changes: 4 additions & 2 deletions lib/account/api_call.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ def get_account(account_id, cents = false)
#
# @param account [Account, String] the account to be removed or its ID
def remove_account(account)
query_api account.is_a?(String) ? "/rest/accounts/#{account}" : "/rest/accounts/#{account.account_id}", nil, 'DELETE'
path = account.is_a?(String) ? "/rest/accounts/#{account}" : "/rest/accounts/#{account.account_id}"
query_api path, nil, 'DELETE'
end

# # Set bank account sort order
# #
# # @param accounts [Array] List of JSON objects with the field account_id set to the internal figo Connect account ID (the accounts will be sorted in the list order)
# # @param accounts [Array] List of JSON objects with the field account_id
# # set to the internal figo Connect account ID (the accounts will be sorted in the list order)
# def account_sort_order (accounts)
# query_api "/rest/accounts", accounts, "PUT"
# end
Expand Down
3 changes: 2 additions & 1 deletion lib/account/model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ def initialize(session, json)
#
# @param since [String, Date] this parameter can either be a transaction ID or a date
# @param count [Integer] limit the number of returned transactions
# @param offset [Integer] which offset into the result set should be used to determin the first transaction to return (useful in combination with count)
# @param offset [Integer] which offset into the result set should be used to determin
# the first transaction to return (useful in combination with count)
# @param include_pending [Boolean] this flag indicates whether pending transactions should be included
# in the response; pending transactions are always included as a complete set, regardless of
# the `since` parameter
Expand Down
39 changes: 0 additions & 39 deletions lib/authentification/api_call.rb
Original file line number Diff line number Diff line change
@@ -1,45 +1,6 @@
# frozen_string_literal: true

module Figo
# Get the URL a user should open in the web browser to start the login process.
#
# When the process is completed, the user is redirected to the URL provided to
# the constructor and passes on an authentication code. This code can be converted
# into an access token for data access.
#
# @param state [String] this string will be passed on through the complete login
# process and to the redirect target at the end. It should be used to
# validated the authenticity of the call to the redirect URL
# @param scope [String] optional scope of data access to ask the user for,
# e.g. `accounts=ro`
# @return [String] the URL to be opened by the user.
def login_url(state, scope = nil)
data = { 'response_type' => 'code', 'client_id' => @client_id, 'state' => state }
data['redirect_uri'] = @redirect_uri unless @redirect_uri.nil?
data['scope'] = scope unless scope.nil?
"https://#{$api_endpoint}/auth/code?" + URI.encode_www_form(data)
end

# Exchange authorization code or refresh token for access token.
#
# @param authorization_code_or_refresh_token [String] either the authorization
# code received as part of the call to the redirect URL at the end of the
# logon process, or a refresh token
# @param scope [String] optional scope of data access to ask the user for,
# e.g. `accounts=ro`
# @return [Hash] object with the keys `access_token`, `refresh_token` and `expires`, as documented in the figo Connect API specification.
def obtain_access_token(authorization_code_or_refresh_token, scope = nil)
# Authorization codes always start with "O" and refresh tokens always start with "R".
if authorization_code_or_refresh_token[0] == 'O'
data = { 'grant_type' => 'authorization_code', 'code' => authorization_code_or_refresh_token }
data['redirect_uri'] = @redirect_uri unless @redirect_uri.nil?
elsif authorization_code_or_refresh_token[0] == 'R'
data = { 'grant_type' => 'refresh_token', 'refresh_token' => authorization_code_or_refresh_token }
data['scope'] = scope unless scope.nil?
end
query_api '/auth/token', data
end

# Return a Token dictionary which tokens are used for further API calls.
#
# @param username [String] figo username
Expand Down
3 changes: 2 additions & 1 deletion lib/bank/api_call.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ def modify_bank(bank)
# @param bank [Bank, String] the bank whose stored PIN should be removed or its ID
# @return [nil]
def remove_bank_pin(bank)
query_api bank.is_a?(String) ? "/rest/banks/#{bank}/remove_pin" : "/rest/banks/#{bank.bank_id}/remove_pin", nil, 'POST'
path = bank.is_a?(String) ? "/rest/banks/#{bank}/remove_pin" : "/rest/banks/#{bank.bank_id}/remove_pin"
query_api path, nil, 'POST'
end

# Get bank information from standard bank code
Expand Down
6 changes: 4 additions & 2 deletions lib/catalog/api_call.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ def self.included(klass)
# The real catalog contains thousands of entries, so expect the call to cause some traffic and delay.
#
# @param q [String] Two-letter country code. Show only resources within this country. Country code is case-insensitve.
# @param country [String] Query for the entire catalog. Will match banks on domestic bank code, BIC, name or figo-ID. Will match services based on name or figo-ID. Only exact matches are returned.
# @params objects [Enum] "banks" or "services", decide what is included in the api response. If not specified, it returns both
# @param country [String] Query for the entire catalog. Will match banks on domestic bank code, BIC, name or figo-ID.
# Will match services based on name or figo-ID. Only exact matches are returned.
# @params objects [Enum] "banks" or "services", decide what is included in the api response.
# If not specified, it returns both
# @return [Catalog] modified bank object returned by server
def get_supported_payment_services(q: nil, country: nil, objects: nil)
list_catalog(q, country, complete_path("#{@prefix}/catalog", objects))
Expand Down
22 changes: 8 additions & 14 deletions lib/figo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,15 @@
#

require 'json'
require 'yaml'
require 'active_support/core_ext/object/to_query'

require_relative './helpers/https.rb'
require_relative './helpers/error.rb'

require_relative './authentification/api_call.rb'

require 'active_support/core_ext/object/to_query'

# Ruby bindings for the figo Connect API: http://developer.figo.me
module Figo
$config = YAML.load_file(File.join(__dir__, '../config.yml'))
$api_endpoint = $config['API_ENDPOINT']
API_ENDPOINT = 'api-preview.figo.me'

# Represents a non user-bound connection to the figo Connect API.
#
Expand All @@ -50,12 +46,11 @@ class Connection
# @param client_id [String] the client ID
# @param client_secret [String] the client secret
# @param redirect_uri [String] optional redirect URI
def initialize(client_id, client_secret, redirect_uri = nil, api_endpoint = $api_endpoint)
def initialize(client_id, client_secret, redirect_uri = nil)
@client_id = client_id
@client_secret = client_secret
@redirect_uri = redirect_uri
@https = HTTPS.new("figo-#{client_id}", nil)
@api_endpoint = api_endpoint
end

# Helper method for making a OAuth 2.0 request.
Expand All @@ -64,7 +59,7 @@ def initialize(client_id, client_secret, redirect_uri = nil, api_endpoint = $api
# @param data [Hash] this optional object will be used as url-encoded POST content.
# @return [Hash] JSON response
def query_api(path, data = nil, method = 'POST')
uri = URI("https://#{@api_endpoint}#{path}")
uri = URI("https://#{API_ENDPOINT}#{path}")

# Setup HTTP request.
request = method == 'POST' ? Net::HTTP::Post.new(path) : Net::HTTP::Get.new(path)
Expand All @@ -89,7 +84,7 @@ def query_api_object(type, path, data = nil, method = 'GET') # :nodoc:
end

def get_version
query_api '/version', data = nil, method = 'GET'
query_api '/version', nil, 'GET'
end
end

Expand Down Expand Up @@ -142,10 +137,9 @@ class Session
# Create session object with access token.
#
# @param access_token [String] the access token
def initialize(access_token, api_endpoint = $api_endpoint)
def initialize(access_token)
@access_token = access_token
@https = HTTPS.new("figo-#{access_token}", nil)
@api_endpoint = api_endpoint
end

# Helper method for making a REST request.
Expand All @@ -155,7 +149,7 @@ def initialize(access_token, api_endpoint = $api_endpoint)
# @param method [String] the HTTP method
# @return [Hash] JSON response
def query_api(path, data = nil, method = 'GET') # :nodoc:
uri = URI("https://#{@api_endpoint}#{path}")
uri = URI("https://#{API_ENDPOINT}#{path}")

# Setup HTTP request.
request = case method
Expand All @@ -167,7 +161,7 @@ def query_api(path, data = nil, method = 'GET') # :nodoc:
Net::HTTP::Delete.new(path)
else
Net::HTTP::Get.new(path)
end
end

request['Authorization'] = "Bearer #{@access_token}"
request['Accept'] = 'application/json'
Expand Down
4 changes: 3 additions & 1 deletion lib/notification/api_call.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ def modify_notification(notification)
#
# @param notification [Notification, String] notification object which should be deleted or its ID
def remove_notification(notification)
query_api notification.is_a?(String) ? "/rest/notifications/#{notification}" : "/rest/notifications/#{notification.notification_id}", nil, 'DELETE'
path = '/rest/notifications/'
path += notification.is_a?(String) ? notification : notification.notification_id
query_api path, nil, 'DELETE'
end
end
4 changes: 0 additions & 4 deletions lib/notification/model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ module Figo
class Notification < Base
@dump_attributes = %i[observe_key notify_uri state]

def initialize(session, json)
super(session, json)
end

# Internal figo Connect notification ID from the notification registration response
# @return [String]
attr_accessor :notification_id
Expand Down
9 changes: 5 additions & 4 deletions lib/payment/api_call.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def get_payment(account_id, payment_id, cents = false)
#
# @param payment [Payment] payment object to be created. It should not have a payment_id set, Required
# @return [Payment] newly created `Payment` object
def add_payment(payment)
def create_payment(payment)
query_api_object Payment, "/rest/accounts/#{payment.account_id}/payments", payment.dump, 'POST'
end

Expand All @@ -51,9 +51,10 @@ def modify_payment(payment)
# @param payment [Payment] payment to be submitted
# @param tan_scheme_id [String] TAN scheme ID of user-selected TAN scheme, Required
# @param state [String] Any kind of string that will be forwarded in the callback response message, Required
# @param redirect_uri [String] At the end of the submission process a response will be sent to this callback URL, Optional
# @param redirect_uri [String] At the end of the submission process a response will
# be sent to this callback URL, Optional
# @return [String] The result parameter is the URL to be opened by the user.
def submit_payment(payment, tan_scheme_id, state, redirect_uri)
def initiate_payment(payment, tan_scheme_id, state, redirect_uri)
params = { tan_scheme_id: tan_scheme_id, state: state, redirect_uri: redirect_uri }.delete_if { |_k, v| v.nil? }

query_api('/rest/accounts/' + payment.account_id + '/payments/' + payment.payment_id + '/init', params, 'POST')
Expand All @@ -65,7 +66,7 @@ def submit_payment(payment, tan_scheme_id, state, redirect_uri)
# @param payment_id [String] figo ID of the payment to retrieve the initiation status for, Required
# @param init_id [String] figo ID of the payment initation, Required
# @return [Object] Initiation status of the payment
def submit_payment(account_id, payment_id, init_id)
def get_payment_initiation_status(account_id, payment_id, init_id)
query_api("/rest/accounts/#{account_id}/payments/#{payment_id}/init#{init_id}", nil, 'GET')
end

Expand Down
3 changes: 2 additions & 1 deletion lib/payment/model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ def initialize(session, json)
# @return [DateTime]
attr_accessor :modification_timestamp

# ID of the transaction corresponding to this payment. This field is only set if the payment has been matched to a transaction
# ID of the transaction corresponding to this payment.
# This field is only set if the payment has been matched to a transaction
# @return [String]
attr_accessor :transaction_id
end
Expand Down
Loading

0 comments on commit bf308b7

Please sign in to comment.