fintecture 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # Fintecture
2
+
3
+ Fintecture is connected with most European banks and enables a user to initiate a payment directly from their bank account. This results to a bank transfer sent from the user's bank account directly to your bank account, skipping all intermediaries. Within the SEPA region, transfers take between 10 seconds to 1 business day to arrive on your bank account. No hidden fees. Check out [our website](https://fintecture.com/).
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'fintecture', '~> 0.1.0'
11
+ ```
12
+
13
+ or install it through our github repository
14
+
15
+ ```ruby
16
+ gem 'fintecture', github: 'Fintecture/fintecture-sdk-ruby'
17
+ ```
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install fintecture
26
+
27
+ ## Usage
28
+
29
+ Get started by subscribing to a free developer account. Join today to get access to our sandbox by registering on the [developer console](https://console.fintecture.com) by creating your first sets of API Keys. When creating an account, specify you are an ECOMMERCE. When you’re ready to deploy to production, just go through the Activation Form in your console. Once the Fintecture Team activates your account, you’ll be ready to start receiving real bank transfers directly on the bank account specified during activation.
30
+
31
+ Initialize your client credentials
32
+
33
+ ```ruby
34
+ Fintecture.app_id = 'your_app_id'
35
+ Fintecture.app_secret = 'your_app_secret'
36
+ Fintecture.app_private_key = %q(your_app_private_key)
37
+ ```
38
+
39
+
40
+ #### Environments
41
+
42
+ By default `sandbox` is the initial environment, but you can change to sandbox by doing
43
+
44
+ ```ruby
45
+ Fintecture.environment = 'sandbox'
46
+ ```
47
+
48
+ You can also see the available environments
49
+
50
+ Fintecture::ENVIRONMENTS
51
+ => ["sandbox", "production"]
52
+
53
+ ### Authentication
54
+
55
+
56
+ #### Access token
57
+
58
+ ```ruby
59
+ Fintecture::Authentication.access_token
60
+ ```
61
+
62
+ ### Connect
63
+
64
+ #### Get connect URL
65
+ ```ruby
66
+ payment_attrs = {
67
+ amount: 123,
68
+ currency: 'EUR',
69
+ order_id: 123,
70
+ customer_id: 123,
71
+ customer_full_name: 'John Doe',
72
+ customer_email: 'john.doe@email.com',
73
+ customer_ip: '127.0.0.1',
74
+ end_to_end_id: '5f78e902907e4209aa8df63659b05d24', # uuid optional
75
+ redirect_uri: '',
76
+ origin_uri: ''
77
+ }
78
+ url = Fintecture::Connect.connect_url_pis payment_attrs
79
+ ```
80
+
81
+ #### Verify URL parameters
82
+
83
+ ```ruby
84
+ callback_params = {
85
+ session_id: 'uri_session_id',
86
+ status: 'uri_status',
87
+ customer_id: 'uri_customer_id',
88
+ provider: 'uri_provider',
89
+ state: 'uri_state',
90
+ s: 'uri_s'
91
+ }
92
+
93
+ Fintecture::Connect.verify_url_parameters callback_params
94
+ ```
95
+ This function returns `true` if the parameters have verified, `false` in other case.
96
+
97
+ ## Development
98
+
99
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
100
+
101
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
102
+
103
+ ## Contributing
104
+
105
+ Bug reports and pull requests are welcome on GitHub at https://github.com/Fintecture/fintecture-sdk-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
106
+
107
+ ## License
108
+
109
+ The gem is available as open source under the terms of the [GPL-3.0 License](http://www.gnu.org/licenses/gpl-3.0.txt).
110
+
111
+ ## Code of Conduct
112
+
113
+ Everyone interacting in the Fintecture project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/Fintecture/fintecture-sdk-ruby/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "fintecture"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,43 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'fintecture/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "fintecture"
8
+ spec.version = Fintecture::VERSION
9
+ spec.authors = ['Fintecture']
10
+ spec.email = ["alvaro.fernandez@nazaries.com"]
11
+
12
+ spec.summary = 'Short summary'
13
+ spec.description = 'Longer summary'
14
+ spec.homepage = "http://fintecture.com"
15
+ spec.license = "GPL-3.0"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata)
20
+ # spec.metadata["allowed_push_host"] = 'http://mygemserver.com'
21
+
22
+ spec.metadata["homepage_uri"] = spec.homepage
23
+ spec.metadata["source_code_uri"] = "https://github.com/Fintecture/fintecture-sdk-ruby"
24
+ # spec.metadata["changelog_uri"] = spec.homepage
25
+ else
26
+ raise "RubyGems 2.0 or newer is required to protect against " \
27
+ "public gem pushes."
28
+ end
29
+
30
+ # Specify which files should be added to the gem when it is released.
31
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
32
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
33
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
34
+ end
35
+ spec.bindir = "exe"
36
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
37
+ spec.require_paths = ["lib"]
38
+
39
+ spec.add_development_dependency "bundler", "~> 2.0"
40
+ spec.add_development_dependency "rake", "~> 10.0"
41
+ spec.add_development_dependency "rspec", "~> 3.0"
42
+ spec.add_dependency'faraday'
43
+ end
@@ -0,0 +1,28 @@
1
+ module Fintecture
2
+ module Api
3
+ module BaseUrl
4
+
5
+ FINTECTURE_OAUTH_URL = {
6
+ local: 'http://localhost:3000/oauth',
7
+ test: 'https://oauth-sandbox-test.fintecture.com/oauth',
8
+ sandbox: 'https://oauth-sandbox.fintecture.com/oauth',
9
+ production: 'https://oauth.fintecture.com/oauth'
10
+ }
11
+
12
+ FINTECTURE_API_URL = {
13
+ local: 'http://localhost:3000',
14
+ test: 'https://api-sandbox-test.fintecture.com',
15
+ sandbox: 'https://api-sandbox.fintecture.com',
16
+ production: 'https://api.fintecture.com'
17
+ }
18
+
19
+ FINTECTURE_CONNECT_URL = {
20
+ local: 'http://localhost:4201',
21
+ test: 'https://connect-test.fintecture.com',
22
+ sandbox: 'https://connect-sandbox.fintecture.com',
23
+ production: 'https://connect.fintecture.com'
24
+ }
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,13 @@
1
+ module Fintecture
2
+ module Api
3
+ module Endpoints
4
+ module Authentication
5
+
6
+ OAUTH_TOKEN_AUTHORIZE = '/token/authorize'
7
+ OAUTH_ACCESS_TOKEN = '/accesstoken'
8
+ OAUTH_REFRESH_TOKEN = '/refreshtoken'
9
+
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,76 @@
1
+ require 'json'
2
+ require 'faraday'
3
+
4
+ module Fintecture
5
+ class Authentication
6
+ class << self
7
+
8
+ def authorize(redirect_uri, state = nil)
9
+ query_string = "?#{{
10
+ response_type: 'code',
11
+ app_id: Fintecture.app_id,
12
+ redirect_uri: redirect_uri,
13
+ state: state
14
+ }.map{|key, value| "#{key}=#{value}"}.join('&')}"
15
+
16
+ ::Faraday.get "#{token_authorize_endpoint}#{query_string}"
17
+ end
18
+
19
+ def access_token(auth_code = nil)
20
+ body = access_token_data auth_code
21
+
22
+ Fintecture::Faraday::Authentication::Connection.post url: access_toke_url, req_body: body
23
+ end
24
+
25
+ def refresh_token(refresh_token)
26
+ body = refresh_token_data refresh_token
27
+
28
+ Fintecture::Faraday::Authentication::Connection.post url: refresh_token_url, req_body: body
29
+ end
30
+
31
+ private
32
+
33
+ def base_url
34
+ Fintecture::Api::BaseUrl::FINTECTURE_OAUTH_URL[Fintecture.environment.to_sym]
35
+ end
36
+
37
+ def token_authorize_endpoint
38
+ "#{base_url}#{Fintecture::Api::Endpoints::Authentication::OAUTH_TOKEN_AUTHORIZE}"
39
+ end
40
+
41
+ def access_toke_url
42
+ "#{base_url}#{Fintecture::Api::Endpoints::Authentication::OAUTH_ACCESS_TOKEN}"
43
+ end
44
+
45
+ def refresh_token_url
46
+ "#{base_url}#{Fintecture::Api::Endpoints::Authentication::OAUTH_REFRESH_TOKEN}"
47
+ end
48
+
49
+ def access_token_data(auth_code)
50
+ data = {
51
+ scope: 'PIS',
52
+ app_id: Fintecture.app_id,
53
+ grant_type: 'client_credentials'
54
+ }
55
+
56
+ if auth_code
57
+ data = {
58
+ scope: 'AIS',
59
+ code: auth_code,
60
+ grant_type: 'authorization_code'
61
+ }
62
+ end
63
+
64
+ data
65
+ end
66
+
67
+ def refresh_token_data(refresh_token)
68
+ {
69
+ grant_type: 'refresh_token',
70
+ refresh_token: refresh_token
71
+ }
72
+ end
73
+
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,130 @@
1
+ require 'base64'
2
+ require 'json'
3
+
4
+ module Fintecture
5
+ class Connect
6
+ class << self
7
+ SIGNATURE_TYPE = 'rsa-sha256'.freeze
8
+
9
+ def connect_url_pis(payment_attrs = nil)
10
+ connect_url(payment_attrs: payment_attrs, type: 'pis')
11
+ end
12
+
13
+ def connect_url(payment_attrs: nil, type: 'pis')
14
+ @payment_attrs = payment_attrs
15
+ @type = type
16
+
17
+ validate_payment_integrity
18
+
19
+ payment_attrs[:end_to_end_id] ||= Fintecture::Utils::Crypto.generate_uuid
20
+ payload = build_payload
21
+ state = build_state(payload).to_json.to_s
22
+
23
+ "#{base_url}/#{type}?state=#{Base64.strict_encode64(state)}"
24
+ end
25
+
26
+ def verify_url_parameters(parameters = nil)
27
+ @post_payment_attrs = parameters
28
+
29
+ validate_post_payment_integrity
30
+
31
+ decrypted = Fintecture::Utils::Crypto.decrypt_private parameters[:s]
32
+ local_digest = build_local_digest parameters
33
+
34
+ decrypted == local_digest
35
+ end
36
+
37
+ private
38
+
39
+ def raise_if_klass_mismatch(target, klass, param_name = nil)
40
+ return if target.is_a? klass
41
+
42
+ raise "invalid #{param_name ? param_name : 'parameter'} format, the parameter should be a #{klass} instead a #{target.class.name}"
43
+ end
44
+
45
+ def validate_payment_integrity
46
+ raise_if_klass_mismatch @payment_attrs, Hash, 'payment_attrs'
47
+
48
+ error_msg = 'invalid payment payload parameter'
49
+
50
+ raise "#{error_msg} type" unless %w[pis ais].include? @type
51
+
52
+ %i[amount currency order_id customer_id customer_full_name customer_email customer_ip].each do |param|
53
+ raise "#{error_msg} #{param.to_s}" if @payment_attrs[param].nil?
54
+ end
55
+ end
56
+
57
+ def validate_post_payment_integrity
58
+ raise_if_klass_mismatch @post_payment_attrs, Hash, 'post_payment_attrs'
59
+
60
+ %i[s state status session_id customer_id provider].each do |param|
61
+ raise "invalid post payment parameter #{param.to_s}" if @post_payment_attrs[param].nil?
62
+ end
63
+ end
64
+
65
+ def build_payload
66
+ attributes = {
67
+ amount: @payment_attrs[:amount],
68
+ currency: @payment_attrs[:currency],
69
+ communication: @payment_attrs[:order_id].to_s,
70
+ end_to_end_id: @payment_attrs[:end_to_end_id]
71
+ }
72
+
73
+ meta = {
74
+ psu_local_id: @payment_attrs[:customer_id],
75
+ psu_name: @payment_attrs[:customer_full_name],
76
+ psu_email: @payment_attrs[:customer_email],
77
+ psu_ip: @payment_attrs[:customer_ip]
78
+ }
79
+
80
+ data = {
81
+ type: 'SEPA',
82
+ attributes: attributes,
83
+ }
84
+
85
+ {
86
+ data: data,
87
+ meta: meta
88
+ }
89
+ end
90
+
91
+ def build_signature(payload)
92
+ Fintecture::Utils::Crypto.sign_payload payload
93
+ end
94
+
95
+ def build_state(payload)
96
+ access_token_response = Fintecture::Authentication.access_token
97
+ access_token = JSON.parse(access_token_response.body)['access_token']
98
+ {
99
+ app_id: Fintecture.app_id,
100
+ access_token: access_token,
101
+ signature_type: SIGNATURE_TYPE,
102
+ signature: build_signature(payload),
103
+ redirect_uri: @payment_attrs[:redirect_uri] || '',
104
+ origin_uri: @payment_attrs[:origin_uri] || '',
105
+ order_id: @payment_attrs[:order_id],
106
+ payload: payload,
107
+ version: Fintecture::VERSION,
108
+ }
109
+ end
110
+
111
+ def build_local_digest(parameters)
112
+ test_string = Base64.strict_encode64({
113
+ app_id: Fintecture.app_id,
114
+ app_secret: Fintecture.app_secret,
115
+ session_id: parameters[:session_id],
116
+ status: parameters[:status],
117
+ customer_id: parameters[:customer_id],
118
+ provider: parameters[:provider],
119
+ state: parameters[:state]
120
+ }.to_json.to_s)
121
+
122
+ Fintecture::Utils::Crypto.hash_base64 test_string
123
+ end
124
+
125
+ def base_url
126
+ Fintecture::Api::BaseUrl::FINTECTURE_CONNECT_URL[Fintecture.environment.to_sym]
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,41 @@
1
+ require 'base64'
2
+ require 'faraday'
3
+
4
+ module Fintecture
5
+ module Faraday
6
+ module Authentication
7
+ class Connection
8
+ class << self
9
+
10
+ def connection(url)
11
+ ::Faraday.new(url: url) do |faraday|
12
+ faraday.request :url_encoded
13
+ faraday.adapter ::Faraday.default_adapter
14
+ end
15
+ end
16
+
17
+ def post(url:, req_body:)
18
+ conn = connection(url)
19
+
20
+ conn.post do |req|
21
+ req.headers = req_headers
22
+ req.body = req_body
23
+ end
24
+ end
25
+
26
+ def req_headers
27
+ client_token = Base64.strict_encode64("#{Fintecture.app_id}:#{Fintecture.app_secret}")
28
+
29
+ {
30
+ 'Accept' => 'application/json',
31
+ 'User-Agent' => "Fintecture Ruby SDK v #{Fintecture::VERSION}",
32
+ 'Authorization' => "Basic #{client_token}",
33
+ 'Content-Type' => 'application/x-www-form-urlencoded',
34
+ }
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,48 @@
1
+ require 'securerandom'
2
+ require 'openssl'
3
+ require 'base64'
4
+ require 'json'
5
+
6
+
7
+ module Fintecture
8
+ module Utils
9
+ class Crypto
10
+ class << self
11
+
12
+ def generate_uuid
13
+ SecureRandom.uuid.gsub!('-','')
14
+ end
15
+
16
+ def sign_payload(payload)
17
+ payload = payload.to_json.to_s if payload.is_a? Hash
18
+
19
+ digest = OpenSSL::Digest::SHA256.new
20
+ private_key = OpenSSL::PKey::RSA.new(Fintecture.app_private_key)
21
+
22
+ begin
23
+ signature = private_key.sign(digest, payload)
24
+ Base64.strict_encode64(signature)
25
+ rescue
26
+ raise 'error during signature'
27
+ end
28
+ end
29
+
30
+ def decrypt_private(digest)
31
+ encrypted_string = Base64.decode64(digest)
32
+ private_key = OpenSSL::PKey::RSA.new(Fintecture.app_private_key)
33
+
34
+ begin
35
+ private_key.private_decrypt(encrypted_string, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
36
+ rescue
37
+ raise 'error during decryption'
38
+ end
39
+ end
40
+
41
+ def hash_base64(plain_text)
42
+ Base64.strict_encode64(Digest::SHA2.new(256).hexdigest(plain_text))
43
+ end
44
+
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,3 @@
1
+ module Fintecture
2
+ VERSION = "0.1.1"
3
+ end
data/lib/fintecture.rb ADDED
@@ -0,0 +1,80 @@
1
+ require 'logger'
2
+ require 'uri'
3
+ require 'faraday'
4
+
5
+ #ToRemove
6
+ require 'openssl'
7
+ require 'cgi'
8
+
9
+ # Version
10
+ require 'fintecture/version'
11
+
12
+ # Modules
13
+ require 'fintecture/connect'
14
+ require 'fintecture/authentication'
15
+
16
+ # Utilities
17
+ require 'fintecture/utils/crypto'
18
+
19
+ # Endpoints
20
+ require 'fintecture/api/base_url'
21
+ require 'fintecture/api/endpoints/authentication'
22
+
23
+ # Connections
24
+ require 'fintecture/faraday/authentication/connection'
25
+
26
+
27
+ module Fintecture
28
+ @log_level = nil
29
+ @logger = nil
30
+ @environment = 'sandbox'
31
+
32
+ ENVIRONMENTS = %w[sandbox production].freeze
33
+
34
+ class << self
35
+ attr_accessor :app_id, :app_secret, :app_private_key
36
+
37
+ def environment=(environment)
38
+ environment = environment.downcase
39
+
40
+ raise "#{environment} not a valid environment, options are [#{ENVIRONMENTS.join(', ')}]" unless ENVIRONMENTS.include?(environment)
41
+
42
+ @environment = environment
43
+ end
44
+
45
+ def environment
46
+ @environment
47
+ end
48
+
49
+ # Logging
50
+ LEVEL_DEBUG = Logger::DEBUG
51
+ LEVEL_ERROR = Logger::ERROR
52
+ LEVEL_INFO = Logger::INFO
53
+
54
+ def log_level
55
+ @log_level
56
+ end
57
+
58
+ def log_level=(val)
59
+ if val == "debug"
60
+ val = LEVEL_DEBUG
61
+ elsif val == "info"
62
+ val = LEVEL_INFO
63
+ end
64
+
65
+ if !val.nil? && ![LEVEL_DEBUG, LEVEL_ERROR, LEVEL_INFO].include?(val)
66
+ raise ArgumentError, 'log_level should only be set to `nil`, `debug` or `info`'
67
+ end
68
+ @log_level = val
69
+ end
70
+
71
+ def logger
72
+ @logger
73
+ end
74
+
75
+ def logger=(val)
76
+ @logger = val
77
+ end
78
+
79
+ end
80
+ end