jumio 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 (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