idnow 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +43 -0
  5. data/.travis.yml +4 -0
  6. data/Gemfile +8 -0
  7. data/LICENSE.txt +21 -0
  8. data/Makefile +2 -0
  9. data/README.md +142 -0
  10. data/circle.yml +7 -0
  11. data/docs/IDnow_API.pdf +0 -0
  12. data/examples/idnow_automated_testing.rb +35 -0
  13. data/examples/idnow_download_identification.rb +16 -0
  14. data/examples/idnow_get_identification.rb +16 -0
  15. data/examples/idnow_upload_download_default_document.rb +33 -0
  16. data/idnow-client.gemspec +30 -0
  17. data/lib/idnow.rb +68 -0
  18. data/lib/idnow/API/authentication.rb +13 -0
  19. data/lib/idnow/API/automated_testing.rb +19 -0
  20. data/lib/idnow/API/document_definitions.rb +29 -0
  21. data/lib/idnow/API/download_documents.rb +13 -0
  22. data/lib/idnow/API/request_identifications.rb +12 -0
  23. data/lib/idnow/API/retrieve_identifications.rb +36 -0
  24. data/lib/idnow/API/upload_documents.rb +23 -0
  25. data/lib/idnow/client.rb +60 -0
  26. data/lib/idnow/configuration.rb +25 -0
  27. data/lib/idnow/exception.rb +25 -0
  28. data/lib/idnow/get_request.rb +10 -0
  29. data/lib/idnow/helpers.rb +9 -0
  30. data/lib/idnow/http_client.rb +27 -0
  31. data/lib/idnow/json_response.rb +11 -0
  32. data/lib/idnow/models/contact_data.rb +12 -0
  33. data/lib/idnow/models/document_definition.rb +15 -0
  34. data/lib/idnow/models/identification.rb +25 -0
  35. data/lib/idnow/models/identification_data.rb +60 -0
  36. data/lib/idnow/models/identification_document.rb +34 -0
  37. data/lib/idnow/models/identification_process.rb +29 -0
  38. data/lib/idnow/models/identification_request.rb +16 -0
  39. data/lib/idnow/models/login.rb +9 -0
  40. data/lib/idnow/models/login_data.rb +13 -0
  41. data/lib/idnow/models/user_data.rb +39 -0
  42. data/lib/idnow/modules/jsonable.rb +28 -0
  43. data/lib/idnow/post_binary_request.rb +11 -0
  44. data/lib/idnow/post_json_request.rb +12 -0
  45. data/lib/idnow/raw_response.rb +31 -0
  46. data/lib/idnow/sftp_client.rb +24 -0
  47. metadata +187 -0
@@ -0,0 +1,13 @@
1
+ module Idnow
2
+ module API
3
+ module Authentication
4
+ def login
5
+ path = full_path_for('login')
6
+ login_data = Idnow::LoginData.new(@api_key)
7
+ request = Idnow::PostJsonRequest.new(path, login_data)
8
+ response = execute(request)
9
+ @auth_token = Idnow::Login.new(response.data).auth_token
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ module Idnow
2
+ module API
3
+ module AutomatedTesting
4
+ HOST = 'https://api.test.idnow.de'.freeze
5
+
6
+ def testing_start(transaction_number:)
7
+ path = full_path_for("identifications/#{transaction_number}/start")
8
+ request = Idnow::PostJsonRequest.new(path, {})
9
+ execute(request, 'X-API_KEY' => @api_key, http_client: automated_testing_http_client)
10
+ end
11
+
12
+ def testing_request_video_chat(transaction_number:)
13
+ path = full_path_for("identifications/#{transaction_number}/requestVideoChat")
14
+ request = Idnow::PostJsonRequest.new(path, {})
15
+ execute(request, 'X-API_KEY' => @api_key, http_client: automated_testing_http_client)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,29 @@
1
+ module Idnow
2
+ module API
3
+ module DocumentDefinitions
4
+ def create_document_definition(document_data)
5
+ fail Idnow::AuthenticationException if @auth_token.nil?
6
+
7
+ path = full_path_for('documentdefinitions')
8
+ request = Idnow::PostJsonRequest.new(path, document_data)
9
+ execute(request, 'X-API-LOGIN-TOKEN' => @auth_token)
10
+ end
11
+
12
+ def list_document_definitions
13
+ fail Idnow::AuthenticationException if @auth_token.nil?
14
+
15
+ path = full_path_for('documentdefinitions')
16
+ request = Idnow::GetRequest.new(path)
17
+ response = execute(request, 'X-API-LOGIN-TOKEN' => @auth_token)
18
+ response.data.map do |data|
19
+ Idnow::DocumentDefinition.new(data)
20
+ end
21
+ end
22
+
23
+ def list_cached_document_definitions(refresh = false)
24
+ return @document_definitions = list_document_definitions if refresh
25
+ @document_definitions ||= list_document_definitions
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ module Idnow
2
+ module API
3
+ module DownloadDocuments
4
+ def download_default_document(document_definition_identifier)
5
+ fail Idnow::AuthenticationException if @auth_token.nil?
6
+
7
+ path = full_path_for("documentdefinitions/#{document_definition_identifier}/data")
8
+ request = Idnow::GetRequest.new(path, '')
9
+ execute(request, 'X-API-LOGIN-TOKEN' => @auth_token)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ module Idnow
2
+ module API
3
+ module RequestIdentifications
4
+ def request_identification(transaction_number:, identification_data:)
5
+ path = full_path_for("identifications/#{transaction_number}/start")
6
+ request = Idnow::PostJsonRequest.new(path, identification_data)
7
+ response = execute(request, 'X-API-KEY' => @api_key)
8
+ Idnow::IdentificationRequest.new(response.data, transaction_number, @target_host, @company_id)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,36 @@
1
+ module Idnow
2
+ module API
3
+ module RetrieveIdentifications
4
+ IDENTIFICATION_STATUSES = %w(pending failed).freeze
5
+
6
+ def list_identifications(status: nil)
7
+ fail Idnow::AuthenticationException if @auth_token.nil?
8
+
9
+ unless status.nil? || IDENTIFICATION_STATUSES.include?(status)
10
+ fail Idnow::InvalidArguments, "Status #{status} not defined, possible options are: #{IDENTIFICATION_STATUSES.join(',')}"
11
+ end
12
+ partial_path = status.nil? ? 'identifications' : "identifications?#{status}=true"
13
+ path = full_path_for(partial_path)
14
+ request = Idnow::GetRequest.new(path)
15
+ response = execute(request, 'X-API-LOGIN-TOKEN' => @auth_token)
16
+ response.data['identifications'].map do |data|
17
+ Idnow::Identification.new(data)
18
+ end
19
+ end
20
+
21
+ def get_identification(transaction_number:)
22
+ fail Idnow::AuthenticationException if @auth_token.nil?
23
+
24
+ path = full_path_for("identifications/#{transaction_number}")
25
+ request = Idnow::GetRequest.new(path)
26
+ response = execute(request, 'X-API-LOGIN-TOKEN' => @auth_token)
27
+ Idnow::Identification.new(response.data)
28
+ end
29
+
30
+ def download_identification(transaction_number:)
31
+ path = "#{transaction_number}.zip"
32
+ @sftp_client.download(path)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,23 @@
1
+ module Idnow
2
+ module API
3
+ module UploadDocuments
4
+ def upload_identification_document(transaction_number, document_definition_identifier, file_data)
5
+ request_path = full_path_for("identifications/#{transaction_number}/documents/#{document_definition_identifier}/data")
6
+ upload_document(file_data, request_path)
7
+ end
8
+
9
+ def upload_default_document(document_definition_identifier, file_data)
10
+ request_path = full_path_for("documentdefinitions/#{document_definition_identifier}/data")
11
+ upload_document(file_data, request_path)
12
+ end
13
+
14
+ private
15
+
16
+ def upload_document(file_data, request_path)
17
+ fail Idnow::AuthenticationException if @auth_token.nil?
18
+ request = Idnow::PostBinaryRequest.new(request_path, file_data)
19
+ execute(request, 'X-API-LOGIN-TOKEN' => @auth_token)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,60 @@
1
+ require 'idnow/API/authentication'
2
+ require 'idnow/API/automated_testing' # shouldn't be included by default
3
+ require 'idnow/API/request_identifications'
4
+ require 'idnow/API/retrieve_identifications'
5
+ require 'idnow/API/document_definitions'
6
+ require 'idnow/API/upload_documents'
7
+ require 'idnow/API/download_documents'
8
+
9
+ module Idnow
10
+ class Client
11
+ include Idnow::API::Authentication
12
+ include Idnow::API::RetrieveIdentifications
13
+ include Idnow::API::RequestIdentifications
14
+ include Idnow::API::AutomatedTesting
15
+ include Idnow::API::DocumentDefinitions
16
+ include Idnow::API::UploadDocuments
17
+ include Idnow::API::DownloadDocuments
18
+
19
+ API_VERSION = 'v1'.freeze
20
+
21
+ attr_reader :host
22
+
23
+ def initialize(env:, company_id:, api_key:)
24
+ fail 'Please set env to :test or :live' unless Idnow::ENVIRONMENTS.keys.include?(env)
25
+ fail 'Please set your company_id' if company_id.nil?
26
+ fail 'Please set your api_key' if api_key.nil?
27
+ @host = Idnow::ENVIRONMENTS[env][:host]
28
+ @target_host = Idnow::ENVIRONMENTS[env][:target_host]
29
+ @company_id = company_id
30
+ @api_key = api_key
31
+
32
+ @http_client = HttpClient.new(host: @host)
33
+ @sftp_client = SftpClient.new(host: @host, username: @company_id, password: @api_key)
34
+ end
35
+
36
+ private
37
+
38
+ def execute(request, headers = {}, http_client: @http_client)
39
+ http_response = http_client.execute(request, headers)
40
+
41
+ response = if request.content_type == 'application/json'
42
+ Idnow::JsonResponse.new(http_response.body)
43
+ else
44
+ Idnow::RawResponse.new(http_response.body)
45
+ end
46
+
47
+ response.tap do |r|
48
+ fail Idnow::ResponseException, r.errors if r.errors?
49
+ end
50
+ end
51
+
52
+ def full_path_for(partial_path)
53
+ File.join('/api', API_VERSION, @company_id, partial_path)
54
+ end
55
+
56
+ def automated_testing_http_client
57
+ @automated_testing_http_client ||= HttpClient.new(host: Idnow::API::AutomatedTesting::HOST)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,25 @@
1
+ module Idnow
2
+ class Configuration
3
+ # TODO: This class is not being used right now, but its aim is to be able for the developers
4
+ # using the gem to set its configuration with something like
5
+ # Idnow.config do |config|
6
+ # config.url = Idnow::TEST_SERVER
7
+ # config.env = :test
8
+ # config.company_id = company_id
9
+ # end
10
+
11
+ attr_accessor :api_key
12
+
13
+ attr_accessor :company_id
14
+
15
+ module Idnow
16
+ module Host
17
+ TEST = 'https://gateway.test.idnow.de'.freeze
18
+ PRODUCTION = 'https://gateway.idnow.de'.freeze
19
+ end
20
+ end
21
+ attr_accessor :host
22
+
23
+ attr_accessor :api_version
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module Idnow
2
+ class Exception < StandardError
3
+ def initialize(message)
4
+ super("Request to IDnow failed: #{message}")
5
+ end
6
+ end
7
+
8
+ class ConnectionException < Idnow::Exception
9
+ def initialize(exception)
10
+ super("#{exception.class}, #{exception.message}")
11
+ end
12
+ end
13
+
14
+ class ResponseException < Idnow::Exception
15
+ end
16
+
17
+ class AuthenticationException < Idnow::Exception
18
+ def initialize
19
+ super('Not authenticaton token found, please log in first')
20
+ end
21
+ end
22
+
23
+ class InvalidArguments < Idnow::Exception
24
+ end
25
+ end
@@ -0,0 +1,10 @@
1
+ require 'net/http'
2
+
3
+ module Idnow
4
+ class GetRequest < Net::HTTP::Get
5
+ def initialize(path, content_type = 'application/json')
6
+ super(path)
7
+ self['Content-Type'] = content_type
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ module Idnow
2
+ module Helpers
3
+ PROJECT_ROOT = File.dirname(File.dirname(File.dirname(__FILE__)))
4
+
5
+ module Factories
6
+ Dir[File.join(PROJECT_ROOT, 'spec', 'support', 'factories', '*.rb')].each { |file| require(file) }
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,27 @@
1
+ module Idnow
2
+ class HttpClient
3
+ def initialize(host:)
4
+ @uri = URI.parse(host)
5
+ end
6
+
7
+ def execute(request, headers = {})
8
+ headers.each do |k, v|
9
+ request[k] = v
10
+ end
11
+ begin
12
+ client.request(request)
13
+ rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError,
14
+ Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
15
+ raise Idnow::ConnectionException, e
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def client
22
+ Net::HTTP.new(@uri.host, @uri.port).tap do |http|
23
+ http.use_ssl = true
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,11 @@
1
+ module Idnow
2
+ class JsonResponse < RawResponse
3
+ attr_reader :data
4
+
5
+ def initialize(raw_response)
6
+ super
7
+ raw_response = (raw_response == '') ? '{}' : raw_response
8
+ @data = JSON.parse(raw_response)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ module Idnow
2
+ class ContactData
3
+ include Idnow::Jsonable
4
+
5
+ attr_accessor :mobilephone, :email
6
+
7
+ def initialize(data)
8
+ @mobilephone = data['mobilephone']
9
+ @email = data['email']
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ module Idnow
2
+ class DocumentDefinition
3
+ include Idnow::Jsonable
4
+
5
+ attr_accessor :optional, :name, :identifier, :mime_type, :sort_order
6
+
7
+ def initialize(data)
8
+ @optional = data['optional']
9
+ @name = data['name']
10
+ @identifier = data['identifier']
11
+ @mime_type = data['mimeType']
12
+ @sort_order = data['sortOrder']
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ require 'forwardable'
2
+ require 'pry'
3
+ module Idnow
4
+ class Identification
5
+ extend Forwardable
6
+ include Idnow::Jsonable
7
+
8
+ attr_accessor :identification_process, :contact_data, :user_data,
9
+ :identification_document, :attachments, :esigning
10
+
11
+ def initialize(data)
12
+ @identification_process = IdentificationProcess.new(data['identificationprocess'])
13
+ @contact_data = ContactData.new(data['contactdata'])
14
+ @user_data = UserData.new(data['userdata'])
15
+ @identification_document = IdentificationDocument.new(data.fetch('identificationdocument', {}))
16
+ @attachments = data['attachments']
17
+ @esigning = data['esigning']
18
+ end
19
+
20
+ def esigning?
21
+ !@esigning.nil?
22
+ end
23
+ def_delegators :identification_process, :successful?, :result, :reason, :id, :transaction_number, :review_pending?
24
+ end
25
+ end
@@ -0,0 +1,60 @@
1
+ require 'json'
2
+
3
+ module Idnow
4
+ class IdentificationData
5
+ module Gender
6
+ MALE = 'MALE'.freeze
7
+ FEMALE = 'FEMALE'.freeze
8
+ end
9
+
10
+ attr_accessor :birthplace, :birthname, :city, :country, :custom1,
11
+ :custom2, :custom3, :custom4, :custom5, :trackingid, :email, :firstname, :gender,
12
+ :lastname, :mobilephone, :nationality, :street, :streetnumber, :title, :zipcode
13
+
14
+ def initialize(params = {})
15
+ params.keys.each do |key|
16
+ fail ArgumentError, "Attribute #{key} is not supported!" unless respond_to?(key.to_sym)
17
+
18
+ send("#{key}=", params[key])
19
+ end
20
+ end
21
+
22
+ def to_json
23
+ result = {}
24
+ instance_variables.each do |attribute|
25
+ result[attribute.to_s.delete('@')] = instance_variable_get(attribute)
26
+ end
27
+ result.to_json
28
+ end
29
+
30
+ ########### Getter / Setter ############
31
+
32
+ def birthday
33
+ @birthday.strftime('%Y-%m-%d') if @birthday
34
+ end
35
+
36
+ def birthday=(birthday)
37
+ @birthday = if birthday.instance_of?(Date) || birthday.instance_of?(DateTime)
38
+ birthday
39
+ else
40
+ Date.parse(birthday)
41
+ end
42
+ end
43
+
44
+ def country=(country)
45
+ fail ArgumentError, 'Country must be ISO 3166 two letter country code' unless country.instance_of?(String) && country.size == 2
46
+
47
+ @country = country.upcase
48
+ end
49
+
50
+ def gender=(gender)
51
+ if %w(M MALE).include?(gender.to_s.strip.upcase)
52
+ @gender = Gender::MALE
53
+ elsif %w(F FEMALE).include?(gender.to_s.strip.upcase)
54
+ @gender = Gender::FEMALE
55
+ else
56
+ fail ArgumentError, 'Provide valid value for gender: MALE or FEMALE'
57
+ end
58
+ end
59
+ end
60
+ end