idnow 1.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.
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