jumio 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/gem-push.yml +33 -0
  3. data/.github/workflows/specs.yml +35 -0
  4. data/.gitignore +14 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +8 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +77 -0
  9. data/Rakefile +8 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/jumio.gemspec +42 -0
  13. data/lib/jumio/client.rb +43 -0
  14. data/lib/jumio/config.rb +19 -0
  15. data/lib/jumio/container.rb +23 -0
  16. data/lib/jumio/entities/address.rb +32 -0
  17. data/lib/jumio/entities/api_entity.rb +14 -0
  18. data/lib/jumio/entities/document.rb +28 -0
  19. data/lib/jumio/entities/identity_verification.rb +15 -0
  20. data/lib/jumio/entities/reject_reason.rb +22 -0
  21. data/lib/jumio/entities/reject_reason_details.rb +13 -0
  22. data/lib/jumio/entities/requestable_record.rb +16 -0
  23. data/lib/jumio/entities/scan.rb +27 -0
  24. data/lib/jumio/entities/scan_status.rb +21 -0
  25. data/lib/jumio/entities/transaction.rb +21 -0
  26. data/lib/jumio/entities/types.rb +12 -0
  27. data/lib/jumio/entities/verification.rb +18 -0
  28. data/lib/jumio/entities/verification_response.rb +16 -0
  29. data/lib/jumio/entities.rb +15 -0
  30. data/lib/jumio/factories.rb +233 -0
  31. data/lib/jumio/http_client.rb +72 -0
  32. data/lib/jumio/operations/get_scan_details.rb +21 -0
  33. data/lib/jumio/operations/get_scan_status.rb +20 -0
  34. data/lib/jumio/operations/initiate_verification.rb +24 -0
  35. data/lib/jumio/operations.rb +5 -0
  36. data/lib/jumio/repositories/scan.rb +30 -0
  37. data/lib/jumio/repositories/verification.rb +22 -0
  38. data/lib/jumio/repositories.rb +4 -0
  39. data/lib/jumio/schemas/verification_request.rb +39 -0
  40. data/lib/jumio/schemas.rb +3 -0
  41. data/lib/jumio/version.rb +5 -0
  42. data/lib/jumio.rb +32 -0
  43. metadata +280 -0
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-types'
4
+
5
+ module Jumio
6
+ module Entities
7
+ # A base entity including all of the default dry-types
8
+ module Types
9
+ include Dry.Types(default: :nominal)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'identity_verification'
4
+ require_relative 'reject_reason'
5
+ require_relative 'requestable_record'
6
+
7
+ module Jumio
8
+ module Entities
9
+ # Document authenticity details
10
+ #
11
+ # https://github.com/Jumio/implementation-guides/blob/master/netverify/netverify-retrieval-api.md#retrieving-verification-data-only
12
+ class Verification < RequestableRecord
13
+ attribute :identityVerification, IdentityVerification.meta(omittable: true)
14
+ attribute :rejectReason, RejectReason.meta(omittable: true)
15
+ attribute :mrzCheck, Types::Strict::String.meta(omittable: true)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_entity'
4
+
5
+ module Jumio
6
+ module Entities
7
+ # Successful initiation of a new Netverify Scan
8
+ #
9
+ # https://github.com/Jumio/implementation-guides/blob/master/netverify/netverify-web-v4.md#response
10
+ class VerificationResponse < ApiEntity
11
+ attribute :timestamp, Types::Strict::String # Timestamp (UTC) of the response. Format: YYYY-MM-DDThh:mm:ss.SSSZ
12
+ attribute :redirectUrl, Types::Strict::String # URL used to load the Netverify client.
13
+ attribute :transactionReference, Types::Strict::String # Jumio reference number for the transaction.
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jumio/entities/address'
4
+ require 'jumio/entities/api_entity'
5
+ require 'jumio/entities/document'
6
+ require 'jumio/entities/identity_verification'
7
+ require 'jumio/entities/reject_reason_details'
8
+ require 'jumio/entities/reject_reason'
9
+ require 'jumio/entities/requestable_record'
10
+ require 'jumio/entities/scan_status'
11
+ require 'jumio/entities/scan'
12
+ require 'jumio/entities/transaction'
13
+ require 'jumio/entities/types'
14
+ require 'jumio/entities/verification_response'
15
+ require 'jumio/entities/verification'
@@ -0,0 +1,233 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jumio/entities'
4
+ require 'active_support/core_ext/hash/deep_merge'
5
+
6
+ module Jumio
7
+ module Factories
8
+ def self.scan_status_failed(options = {})
9
+ Entities::ScanStatus.new({
10
+ timestamp: '2018-10-29T15:56:54.398Z',
11
+ scanReference: 'WEB_UPLOAD',
12
+ status: 'FAILED'
13
+ }.deep_merge(options))
14
+ end
15
+
16
+ def self.scan_status_pending(options = {})
17
+ Entities::ScanStatus.new({
18
+ timestamp: '2018-10-29T15:56:54.398Z',
19
+ scanReference: 'WEB_UPLOAD',
20
+ status: 'PENDING'
21
+ }.deep_merge(options))
22
+ end
23
+
24
+ def self.scan_denied_fraud(options = {})
25
+ Entities::Scan.new({
26
+ document: {
27
+ status: 'DENIED_FRAUD'
28
+ },
29
+ transaction: {
30
+ date: '2018-10-29T15:56:54.398Z',
31
+ source: 'WEB_UPLOAD',
32
+ status: 'DONE',
33
+ updatedAt: '2018-10-29T15:56:54.398Z'
34
+ },
35
+ verification: {
36
+ rejectReason: {
37
+ rejectReasonCode: '106',
38
+ rejectReasonDescription: 'FAKE'
39
+ }
40
+ }
41
+ }.deep_merge(options))
42
+ end
43
+
44
+ def self.scan_with_identity_match(options = {})
45
+ Entities::Scan.new({
46
+ document: {
47
+ status: 'APPROVED_VERIFIED',
48
+ },
49
+ transaction: {
50
+ date: '2018-10-29T15:56:54.398Z',
51
+ source: 'WEB_UPLOAD',
52
+ status: 'DONE',
53
+ updatedAt: '2018-10-29T15:56:54.398Z',
54
+ },
55
+ verification: {
56
+ identityVerification: {
57
+ similarity: 'MATCH',
58
+ validity: 'true'
59
+ }
60
+ }
61
+ }.deep_merge(options))
62
+ end
63
+
64
+ def self.scan_with_identity_match_and_us_id(options = {})
65
+ Entities::Scan.new({
66
+ document: {
67
+ address: {
68
+ city: "SAN FRANCISCO",
69
+ country: "USA",
70
+ stateCode: "US-CA",
71
+ streetName: "20TH",
72
+ streetNumber: "560",
73
+ streetSuffix: "ST",
74
+ zip: "94107"
75
+ },
76
+ dob: '1980-01-02',
77
+ expiry: "2022-01-02",
78
+ firstName: 'John',
79
+ issuingCountry: "USA",
80
+ lastName: 'Doe',
81
+ number: 'A1234567',
82
+ status: 'APPROVED_VERIFIED',
83
+ usState: 'CA'
84
+ },
85
+ transaction: {
86
+ date: '2018-10-29T15:56:54.398Z',
87
+ source: 'WEB_UPLOAD',
88
+ status: 'DONE',
89
+ updatedAt: '2018-10-29T15:56:54.398Z',
90
+ },
91
+ verification: {
92
+ identityVerification: {
93
+ similarity: 'MATCH',
94
+ validity: 'true'
95
+ }
96
+ }
97
+ }.deep_merge(options))
98
+ end
99
+
100
+ def self.scan_with_match_not_possible(options = {})
101
+ params = {
102
+ verification: {
103
+ identityVerification: {
104
+ similarity: 'NOT_POSSIBLE',
105
+ validity: 'true'
106
+ }
107
+ }
108
+ }.deep_merge(options)
109
+ scan_with_identity_match(params)
110
+ end
111
+
112
+ def self.scan_with_wrong_person(options = {})
113
+ Entities::Scan.new({
114
+ document: {
115
+ status: 'APPROVED_VERIFIED'
116
+ },
117
+ transaction: {
118
+ date: '2018-10-29T15:56:54.398Z',
119
+ source: 'WEB_UPLOAD',
120
+ status: 'DONE',
121
+ updatedAt: '2018-10-29T15:56:54.398Z'
122
+ },
123
+ verification: {
124
+ identityVerification: {
125
+ similarity: 'NO_MATCH',
126
+ validity: 'true'
127
+ }
128
+ }
129
+ }.deep_merge(options))
130
+ end
131
+
132
+ def self.scan_with_unacceptable_selfie(reason, options = {})
133
+ Entities::Scan.new({
134
+ document: {
135
+ status: 'APPROVED_VERIFIED'
136
+ },
137
+ transaction: {
138
+ date: '2018-10-29T15:56:54.398Z',
139
+ source: 'WEB_UPLOAD',
140
+ status: 'DONE',
141
+ updatedAt: '2018-10-29T15:56:54.398Z'
142
+ },
143
+ verification: {
144
+ identityVerification: {
145
+ reason: reason,
146
+ similarity: 'NOT_POSSIBLE',
147
+ validity: 'false'
148
+ }
149
+ }
150
+ }.deep_merge(options))
151
+ end
152
+
153
+ def self.scan_with_unsupported_id_type(options = {})
154
+ Entities::Scan.new({
155
+ document: {
156
+ status: 'DENIED_UNSUPPORTED_ID_TYPE'
157
+ },
158
+ transaction: {
159
+ date: '2018-10-29T15:56:54.398Z',
160
+ source: 'WEB_UPLOAD',
161
+ status: 'DONE',
162
+ updatedAt: '2018-10-29T15:56:54.398Z'
163
+ },
164
+ }.deep_merge(options))
165
+ end
166
+
167
+ def self.scan_with_unsupported_id_country(options = {})
168
+ params = {
169
+ document: { status: 'DENIED_UNSUPPORTED_ID_COUNTRY' }
170
+ }.deep_merge(options)
171
+ scan_with_unsupported_id_type(params)
172
+ end
173
+
174
+ def self.scan_with_error_not_readable_id(options = {})
175
+ Entities::Scan.new({
176
+ document: {
177
+ status: 'ERROR_NOT_READABLE_ID'
178
+ },
179
+ transaction: {
180
+ date: '2018-10-29T15:56:54.398Z',
181
+ source: 'WEB_UPLOAD',
182
+ status: 'DONE',
183
+ updatedAt: '2018-10-29T15:56:54.398Z'
184
+ },
185
+ verification: {
186
+ rejectReason: {
187
+ rejectReasonCode: '200',
188
+ rejectReasonDescription: 'NOT_READABLE_DOCUMENT'
189
+ }
190
+ }
191
+ }.deep_merge(options))
192
+ end
193
+
194
+ def self.scan_with_no_id_uploaded(options = {})
195
+ Entities::Scan.new({
196
+ document: {
197
+ status: 'NO_ID_UPLOADED'
198
+ },
199
+ transaction: {
200
+ date: '2018-10-29T15:56:54.398Z',
201
+ source: 'WEB_UPLOAD',
202
+ status: 'FAILED',
203
+ updatedAt: '2018-10-29T15:56:54.398Z'
204
+ },
205
+ }.deep_merge(options))
206
+ end
207
+
208
+ def self.scan_with_no_identity_verification(options = {})
209
+ Entities::Scan.new({
210
+ document: {
211
+ status: 'APPROVED_VERIFIED'
212
+ },
213
+ transaction: {
214
+ date: '2018-10-29T15:56:54.398Z',
215
+ source: 'WEB_UPLOAD',
216
+ status: 'DONE',
217
+ updatedAt: '2018-10-29T15:56:54.398Z'
218
+ },
219
+ verification: {
220
+ mrzCheck: 'NOT_AVAILABLE'
221
+ }
222
+ }.deep_merge(options))
223
+ end
224
+
225
+ def self.verfication_response(options = {})
226
+ Entities::VerificationResponse.new({
227
+ timestamp: '2018-10-29T15:56:54.398Z',
228
+ transactionReference: 'a527ae56-ea7a-4476-a2de-1a678bd6189d',
229
+ redirectUrl: 'https://netverify.com/web/v4/app?authorizationToken=TOKEN&locale=en-US'
230
+ }.deep_merge(options))
231
+ end
232
+ end
233
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'net/http'
5
+ require 'uri'
6
+
7
+ module Jumio
8
+ class RecordNotFound < StandardError; end
9
+
10
+ # Makes HTTPS requests to Jumio JSON apis
11
+ class HttpClient
12
+ # @param token [String, nil]
13
+ # @param secret [String, nil]
14
+ # @param base_url [String, nil]
15
+ def initialize(token: nil, secret: nil, base_url: nil)
16
+ @api_token = (token || Jumio.api.token)
17
+ @api_secret = (secret || Jumio.api.secret)
18
+ @api_base_url = (base_url || Jumio.api.base_url)
19
+ end
20
+
21
+ # @param url [String]
22
+ # @raise [RecordNotFound] On 404 response
23
+ # @raise [Net::HTTPError] On JSON response parsing error
24
+ # @return [Hash] The response
25
+ def get(url)
26
+ uri = URI.join(@api_base_url, url)
27
+ response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
28
+ req = Net::HTTP::Get.new(uri)
29
+ req.basic_auth(@api_token, @api_secret)
30
+ req['Accept'] = 'application/json'
31
+ req['Content-Type'] = 'application/json'
32
+ req['User-Agent'] = user_agent
33
+ http.request(req)
34
+ end
35
+ handle_response(response)
36
+ end
37
+
38
+ # @param url [String]
39
+ # @param body [String]
40
+ # @raise [RecordNotFound] On 404 response
41
+ # @raise [Net::HTTPError] On JSON response parsing error
42
+ # @return [Hash] The response
43
+ def post(url, body)
44
+ uri = URI.join(@api_base_url, url)
45
+ response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
46
+ req = Net::HTTP::Post.new(uri)
47
+ req.basic_auth(@api_token, @api_secret)
48
+ req['Accept'] = 'application/json'
49
+ req['Content-Type'] = 'application/json'
50
+ req['User-Agent'] = user_agent
51
+ req.body = body.to_json
52
+ http.request(req)
53
+ end
54
+ handle_response(response)
55
+ end
56
+
57
+ private
58
+
59
+ def handle_response(response)
60
+ JSON.parse(response.body).tap do |body|
61
+ response.error! if body['error']
62
+ raise RecordNotFound, body['message'] if body['httpStatus'] == '404'
63
+ end
64
+ rescue JSON::ParserError => error
65
+ raise Net::HTTPError.new("JSON::ParserError: #{error.message}", response)
66
+ end
67
+
68
+ def user_agent
69
+ "#{Jumio.merchant.company_name} #{Jumio.merchant.app_name}/v#{Jumio.merchant.version}"
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/string'
4
+ require 'jumio/container'
5
+
6
+ module Jumio
7
+ module Operations
8
+ # Fetch all available information for a Netverify transaction
9
+ class GetScanDetails
10
+ include JDC_CONTAINER[scan_repository: 'repositories.scan']
11
+
12
+ # @param scan_reference [String] Jumio’s reference number of an existing scan from your account
13
+ # @param client [Jumio::HttpClient, nil] The HTTP client to use
14
+ # @return [Entities::Scan]
15
+ def call(scan_reference, client = nil)
16
+ raise ArgumentError, 'Scan reference required' unless scan_reference.present?
17
+ scan_repository.get_details(scan_reference, client)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/string'
4
+ require 'jumio/container'
5
+
6
+ module Jumio
7
+ module Operations
8
+ # Fetch only the upload status of a Netverify transaction
9
+ class GetScanStatus
10
+ include JDC_CONTAINER[scan_repository: 'repositories.scan']
11
+
12
+ # @param scan_reference [String] Jumio’s reference number of an existing scan from your account
13
+ # @return [Entities::ScanStatus]
14
+ def call(scan_reference, client = nil)
15
+ raise ArgumentError, 'Scan reference required' unless scan_reference.present?
16
+ scan_repository.get_status(scan_reference, client)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/monads/result'
4
+ require 'jumio/container'
5
+ require 'jumio/schemas'
6
+
7
+ module Jumio
8
+ module Operations
9
+ # Request a new Netverify session
10
+ class InitiateVerification
11
+ include Dry::Monads::Result::Mixin
12
+ include JDC_CONTAINER[verification_repository: 'repositories.verification']
13
+
14
+ # @param verification_params [Hash] Request payload for initiating a new Netverify scan.
15
+ # @param client [Jumio::HttpClient, nil] The HTTP client to use
16
+ # @return [Failure, Entities::VerificationResponse]
17
+ def call(verification_params, client = nil)
18
+ validation = Schemas::VerificationRequest.call(verification_params)
19
+ return Failure.new(validation.message_set.to_h) unless validation.success?
20
+ verification_repository.initiate(verification_params, client)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jumio/operations/get_scan_details'
4
+ require 'jumio/operations/get_scan_status'
5
+ require 'jumio/operations/initiate_verification'
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jumio/entities'
4
+
5
+ module Jumio
6
+ module Repositories
7
+ # A wrapper for the Netverify scan retreival API
8
+ #
9
+ # https://github.com/Jumio/implementation-guides/blob/master/netverify/netverify-retrieval-api.md
10
+ class Scan
11
+ include JDC_CONTAINER[default_client: 'http_client']
12
+
13
+ # @param scan_reference [String] Jumio’s reference number of an existing scan from your account
14
+ # @param client [Jumio::HttpClient, nil] The HTTP client to use
15
+ # @return [Entities::ScanStatus]
16
+ def get_status(scan_reference, client = nil)
17
+ raw_scan_status = (client || default_client.new).get("netverify/v2/scans/#{scan_reference}")
18
+ Entities::ScanStatus.new(raw_scan_status)
19
+ end
20
+
21
+ # @param scan_reference [String] Jumio’s reference number of an existing scan from your account
22
+ # @param client [Jumio::HttpClient, nil] The HTTP client to use
23
+ # @return [Entities::Scan]
24
+ def get_details(scan_reference, client = nil)
25
+ raw_scan = (client || default_client.new).get("netverify/v2/scans/#{scan_reference}/data")
26
+ Entities::Scan.new(raw_scan)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jumio/entities'
4
+
5
+ module Jumio
6
+ module Repositories
7
+ class Verification
8
+ # A wrapper for the Netverify web client api
9
+ #
10
+ # https://github.com/Jumio/implementation-guides/blob/master/netverify/netverify-web-v4.md
11
+ include JDC_CONTAINER[default_client: 'http_client']
12
+
13
+ # @param verification_params [Hash] Request payload for initiating a new Netverify scan.
14
+ # @param client [Jumio::HttpClient, nil] The HTTP client to use
15
+ # @return [Entities::VerificationResponse]
16
+ def initiate(verification_params, client = nil)
17
+ raw_verification = (client || default_client.new).post('v4/initiate', verification_params)
18
+ Entities::VerificationResponse.new(raw_verification)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jumio/repositories/scan'
4
+ require 'jumio/repositories/verification'
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-schema'
4
+
5
+ module Jumio
6
+ module Schemas
7
+ # Request payload for initiating a new Netverify scan.
8
+ # These options customize the upload flow on a per-request basis.
9
+ # Default values for many of these options can be set in the Customer Portal.
10
+ #
11
+ # https://github.com/Jumio/implementation-guides/blob/master/netverify/netverify-web-v4.md#request-body
12
+ VerificationRequest = Dry::Schema.define do
13
+ # Your internal reference for the transaction.
14
+ required(:customerInternalReference).filled(:str?)
15
+ # Your internal reference for the user.
16
+ required(:userReference).filled(:str?)
17
+
18
+ # Sends verification result to this URL upon completion.
19
+ optional(:callbackUrl).filled(:str?)
20
+ # Redirects to this URL after an unsuccessful transaction.
21
+ optional(:errorUrl).filled(:str?)
22
+ # Renders content in the specified language.
23
+ optional(:locale).filled(:str?)
24
+ # Preset the country and document type to bypass the selection screen.
25
+ optional(:presets).schema do
26
+ required(:index).value(eql?: 1)
27
+ # Possible values: ISO 3166-1 alpha-3 country code
28
+ required(:country).filled(:str?).value(size?: 3)
29
+ required(:type).value(included_in?: ['PASSPORT', 'DRIVING_LICENSE', 'ID_CARD'])
30
+ end
31
+ # Your reporting criteria for the transaction.
32
+ optional(:reportingCriteria).filled(:str?)
33
+ # Redirects to this URL after a successful transaction.
34
+ optional(:successUrl).filled(:str?)
35
+ # Applies this acquisition workflow to the transaction.
36
+ optional(:workflowId).filled(:int?)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jumio/schemas/verification_request'
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jumio
4
+ VERSION = '1.0.0'
5
+ end
data/lib/jumio.rb ADDED
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jumio/client'
4
+ require 'jumio/http_client'
5
+ require 'jumio/config'
6
+ require 'jumio/container'
7
+ require 'jumio/entities'
8
+ require 'jumio/factories'
9
+ require 'jumio/operations'
10
+ require 'jumio/repositories'
11
+ require 'jumio/version'
12
+
13
+ module Jumio
14
+ # Configuration free methods
15
+ #
16
+ # These use the default http_client configured with Jumio.api
17
+ #
18
+ # To use a per instance configuration client, use
19
+ # Jumio::Client.new(:token, :secret)
20
+
21
+ def self.get_scan_details
22
+ Container.resolve('get_scan_details')
23
+ end
24
+
25
+ def self.get_scan_status
26
+ Container.resolve('get_scan_status')
27
+ end
28
+
29
+ def self.initiate_verification
30
+ Container.resolve('initiate_verification')
31
+ end
32
+ end