experian 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 73a404dfa3f7928172bb01a332a7d7acd008963a
4
+ data.tar.gz: 1cbe21e733ba63c7be1e096f726b1805a48d44ae
5
+ SHA512:
6
+ metadata.gz: e4c01acf12f4073d200c2c9b8613aa1ad46536dcf9ee44a31fed93d8a52f797f79812bf832210db0b97107f8877581488112bdf15bb1f10fd1d9000ee773b769
7
+ data.tar.gz: 4fa06a3a87bbec3a8736d546693c4b20da618d4dd71e4eff41a9e7969617c42051aa8c8154b95aa09ad64c7ddfb05374473d491ba6b8c72a8c73374c3402695a
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rake'
4
+
5
+ group :development do
6
+ gem 'pry'
7
+ end
8
+
9
+ group :test do
10
+ gem 'minitest'
11
+ gem 'webmock'
12
+ gem 'mocha', require: false
13
+ gem 'timecop'
14
+ end
15
+
16
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Eric Hutzelman
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,157 @@
1
+ # Experian Ruby Gem
2
+
3
+ Ruby Wrapper for portions of the Experian Net Connect API. Experian exposes nearly 30 different services through the Net Connect API.
4
+ This gem currently only implements the Connect Check product (consumer credit scoring and identity validation), although
5
+ expanding it to support the other products should be straightforward.
6
+
7
+ *Net Connect is a business-to-business application gateway designed to allow access to Experian legacy systems via the public
8
+ Internet or Experian’s private TCP/IP extranet transport. It is a secure 168-bit encrypted transaction, using HTTPS.
9
+ Net Connect is a non-browser-based system requiring Experian certified client or vendor software at the user's location.
10
+ It utilizes XML for the input inquiry and has the capability of returning field-level XML, as well as our standard Automated
11
+ Response Format (ARF) (computer readable), Teletype Response Format (TTY) (human readable) and Parallel Profile
12
+ (both ARF and TTY in one response). Net Connect meets the encryption standards requirement in the Safeguards section of the
13
+ Gramm- Leach-Bliley (GLB) Act.*
14
+
15
+ #### Net Connect Products
16
+
17
+ * Address Update
18
+ * Authentication Services
19
+ * BizID
20
+ * Bullseye
21
+ * Checkpoint - File One Verification Solution
22
+ * Collection Advantage interactive
23
+ * Collection Report
24
+ * **Connect Check (implemented in this gem)**
25
+ * Credit Profile
26
+ * Custom Solution
27
+ * Cross View
28
+ * CU Decision Expert
29
+ * Custom Strategist
30
+ * Decode
31
+ * Demographics
32
+ * Direct Check
33
+ * Employment Insight
34
+ * Fraud Shield
35
+ * Instant Prescreen
36
+ * New Consumer Identification
37
+ * Numeric Inquiry
38
+ * Parallel Profile
39
+ * Profile Summary
40
+ * Precise ID
41
+ * Precise ID Distributed
42
+ * Risk Models
43
+ * Social Search
44
+ * Truvue
45
+
46
+ ## Installation
47
+
48
+ Add this line to your application's Gemfile:
49
+
50
+ gem 'experian'
51
+
52
+ And then execute:
53
+
54
+ $ bundle
55
+
56
+ Or install it yourself as:
57
+
58
+ $ gem install experian
59
+
60
+ ## Usage
61
+
62
+ ### Configuration
63
+
64
+ Experian will provide you with the following authentication credentials when you sign up for their service:
65
+ ```ruby
66
+ # Provide authentication credentials
67
+ Experian.configure do |config|
68
+ config.eai = "X42PB93F"
69
+ config.preamble = "FCD2"
70
+ config.op_initials = "AB"
71
+ config.subcode = "1968543"
72
+ config.user = "user"
73
+ config.password = "password"
74
+ config.vendor_number = "P55"
75
+ end
76
+
77
+ # Route requests to Experian test server instead of production
78
+ Experian.test_mode = true
79
+
80
+ ```
81
+
82
+ ### Using a product client
83
+
84
+ Products are namespaced under the Experian module. Example of how to create a client for the Connect Check product:
85
+ ```ruby
86
+ client = Experian::ConnectCheck::Client.new
87
+ ```
88
+
89
+ Once you have a client, you can make requests:
90
+ ```ruby
91
+ response = client.check_credit(first_name: "Homer", last_name: "Simpson", ssn: "123456789")
92
+
93
+ response.success?
94
+ # => true
95
+ response.completion_message
96
+ # => "Request processed successfully"
97
+ response.credit_match_code
98
+ # => "C"
99
+ response.credit_match_code_message
100
+ # => "ID Match"
101
+ response.credit_score
102
+ # => 846
103
+ response.customer_message
104
+ # => "Credit and ID have been verified and no deposit is required."
105
+ response.customer_names
106
+ # => ["HOMER J SIMPSON", "HOMER SIMPSON", "H SIMPSON", "PLOW KING"]
107
+ response.customer_addresses
108
+ # => ["96 JAMESTOWN BLVD /HAMMONTON NJ 080372110",
109
+ # => "1466 BALLY BUNION DR /EGG HARBOR CITY NJ 082155118",
110
+ # => "100 WEST LN /HAMMONTON NJ 080371151"]
111
+ ```
112
+
113
+ Alternatively, you can skip the explicit client instantiation and use the module level convenience method instead:
114
+ ```ruby
115
+ response = Experian::ConnectCheck.check_credit(...)
116
+ ```
117
+
118
+
119
+ ### Handling errors from Experian
120
+ ```ruby
121
+ response = client.check_credit(first_name: "Homer", last_name: "Simpson", ssn: "NaN")
122
+
123
+ response.success?
124
+ # => false
125
+ response.error_message
126
+ # => "Invalid request format"
127
+ response.error_action_indicator_message
128
+ # => "Correct and/or resubmit"
129
+ ```
130
+
131
+ ### Examine raw request and response XML
132
+ If you need to troubleshoot by viewing the raw xml, it is accesssible on the request and response objects of the client:
133
+ ```ruby
134
+ # Inspect the request XML that was sent to Experian
135
+ client.request.xml
136
+ # => "<?xml version='1.0' encoding='utf-8'?>..."
137
+
138
+ # Inspect the response XML that was received from Experian
139
+ client.response.xml
140
+ # => "<?xml version='1.0' encoding='utf-8'?>..."
141
+ ```
142
+
143
+
144
+ ## Contributing
145
+
146
+ 1. Fork it
147
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
148
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
149
+ 4. Push to the branch (`git push origin my-new-feature`)
150
+ 5. Create new Pull Request
151
+
152
+ ## Copyright
153
+
154
+ Copyright (c) 2012-2013 Eric Hutzelman.
155
+ See [LICENSE][] for details.
156
+
157
+ [license]: LICENSE.txt
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ t.pattern = "test/**/*_test.rb"
7
+ end
8
+
9
+ task :default => :test
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'experian/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "experian"
8
+ spec.version = Experian::VERSION
9
+ spec.authors = ["Eric Hutzelman"]
10
+ spec.email = ["ehutzelman@gmail.com"]
11
+ spec.description = "Ruby gem wrapper for the Experian net connect API."
12
+ spec.summary = "Ruby gem wrapper for the Experian net connect API."
13
+ spec.homepage = "http://github.com/ehutzelman/experian"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "excon"
22
+ spec.add_dependency "builder"
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ end
@@ -0,0 +1,73 @@
1
+ require "excon"
2
+ require "builder"
3
+ require "experian/version"
4
+ require "experian/constants"
5
+ require "experian/error"
6
+ require "experian/client"
7
+ require "experian/request"
8
+ require "experian/response"
9
+ require "experian/connect_check"
10
+
11
+ module Experian
12
+ include Experian::Constants
13
+
14
+ class << self
15
+
16
+ attr_accessor :eai, :preamble, :op_initials, :subcode, :user, :password, :vendor_number
17
+ attr_accessor :test_mode
18
+
19
+ def configure
20
+ yield self
21
+ end
22
+
23
+ def test_mode?
24
+ !!test_mode
25
+ end
26
+
27
+ def ecals_uri
28
+ uri = URI(Experian::LOOKUP_SERVLET_URL)
29
+ uri.query = URI.encode_www_form(
30
+ 'lookupServiceName' => Experian::LOOKUP_SERVICE_NAME,
31
+ 'lookupServiceVersion' => Experian::LOOKUP_SERVICE_VERSION,
32
+ 'serviceName' => service_name,
33
+ 'serviceVersion' => Experian::SERVICE_VERSION,
34
+ 'responseType' => 'text/plain'
35
+ )
36
+ uri
37
+ end
38
+
39
+ def net_connect_uri
40
+ perform_ecals_lookup if ecals_lookup_required?
41
+
42
+ # setup basic authentication
43
+ @net_connect_uri.user = Experian.user
44
+ @net_connect_uri.password = Experian.password
45
+
46
+ @net_connect_uri
47
+ end
48
+
49
+ def perform_ecals_lookup
50
+ @net_connect_uri = URI.parse(Excon.get(ecals_uri.to_s).body)
51
+ assert_experian_domain
52
+ @ecals_last_update = Time.now
53
+ rescue Excon::Errors::SocketError => e
54
+ raise Experian::ClientError, "Could not connect to Experian: #{e.message}"
55
+ end
56
+
57
+ def ecals_lookup_required?
58
+ @net_connect_uri.nil? || @ecals_last_update.nil? || Time.now - @ecals_last_update > Experian::ECALS_TIMEOUT
59
+ end
60
+
61
+ def assert_experian_domain
62
+ unless @net_connect_uri.host.end_with?('.experian.com')
63
+ @net_connect_uri = nil
64
+ raise Experian::ClientError, "Could not authenticate connection to Experian, unexpected host name."
65
+ end
66
+ end
67
+
68
+ def service_name
69
+ test_mode? ? Experian::SERVICE_NAME_TEST : Experian::SERVICE_NAME
70
+ end
71
+
72
+ end
73
+ end
@@ -0,0 +1,29 @@
1
+ module Experian
2
+ class Client
3
+
4
+ attr_reader :request, :response
5
+
6
+ def submit_request
7
+ connection = Excon.new(Experian.net_connect_uri.to_s, idempotent: true)
8
+ @raw_response = connection.post(body: request_body, headers: request_headers)
9
+ raise Experian::Forbidden, "Invalid Experian login credentials" if invalid_login?
10
+ @raw_response.body
11
+
12
+ rescue Excon::Errors::SocketError => e
13
+ raise Experian::ClientError, "Could not connect to Experian: #{e.message}"
14
+ end
15
+
16
+ def request_body
17
+ URI.encode_www_form('NETCONNECT_TRANSACTION' => request.xml)
18
+ end
19
+
20
+ def request_headers
21
+ { "Content-Type" => "application/x-www-form-urlencoded" }
22
+ end
23
+
24
+ def invalid_login?
25
+ !!(@raw_response.headers["Location"] =~ /sso_logon/)
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ require 'experian/connect_check/client'
2
+ require 'experian/connect_check/request'
3
+ require 'experian/connect_check/response'
4
+
5
+ module Experian
6
+ module ConnectCheck
7
+
8
+ MATCH_CODES = {
9
+ "A" => "Deceased/Non-Issued Social Security Number",
10
+ "B" => "No Record Found",
11
+ "C" => "ID Match",
12
+ "D" => "ID Match to Other Name",
13
+ "E" => "ID No Match"
14
+ }
15
+
16
+ DB_HOST = "CIS"
17
+ DB_HOST_TEST = "STAR"
18
+
19
+ def self.db_host
20
+ Experian.test_mode? ? DB_HOST_TEST : DB_HOST
21
+ end
22
+
23
+ # convenience method
24
+ def self.check_credit(options = {})
25
+ Client.new.check_credit(options)
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ module Experian
2
+ module ConnectCheck
3
+ class Client < Experian::Client
4
+
5
+ def check_credit(options = {})
6
+ assert_check_credit_options(options)
7
+ @request = Request.new(options)
8
+ @response = Response.new(submit_request)
9
+ end
10
+
11
+ def assert_check_credit_options(options)
12
+ return if options[:first_name] && options[:last_name] && options[:ssn]
13
+ return if options[:first_name] && options[:last_name] && options[:street] && options[:zip]
14
+ raise Experian::ArgumentError, "Required options missing: first_name, last_name, ssn OR first_name, last_name, street, zip"
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,116 @@
1
+ module Experian
2
+ module ConnectCheck
3
+ class Request < Experian::Request
4
+
5
+ def build_request
6
+ super do |xml|
7
+ xml.tag!('EAI', Experian.eai)
8
+ xml.tag!('DBHost', ConnectCheck.db_host)
9
+ add_reference_id(xml)
10
+ xml.tag!('Request', :xmlns => Experian::XML_REQUEST_NAMESPACE, :version => '1.0') do
11
+ xml.tag!('Products') do
12
+ xml.tag!('ConnectCheck') do
13
+ add_subscriber(xml)
14
+ add_applicant(xml)
15
+ add_output_type(xml)
16
+ add_vendor(xml)
17
+ add_options(xml)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ def add_reference_id(xml)
25
+ xml.tag!('ReferenceId', @options[:reference_id]) if @options[:reference_id]
26
+ end
27
+
28
+ def add_subscriber(xml)
29
+ xml.tag!('Subscriber') do
30
+ xml.tag!('Preamble', Experian.preamble)
31
+ xml.tag!('OpInitials', Experian.op_initials)
32
+ xml.tag!('SubCode', Experian.subcode)
33
+ end
34
+ end
35
+
36
+ def add_applicant(xml)
37
+ xml.tag!('PrimaryApplicant') do
38
+ xml.tag!('Name') do
39
+ xml.tag!('Surname', @options[:last_name])
40
+ xml.tag!('First', @options[:first_name])
41
+ xml.tag!('Middle', @options[:middle_name]) if @options[:middle_name]
42
+ xml.tag!('Gen', @options[:generation_code]) if @options[:generation_code]
43
+ end
44
+ xml.tag!('SSN', @options[:ssn]) if @options[:ssn]
45
+ add_current_address(xml)
46
+ add_previous_address(xml)
47
+ add_driver_license(xml)
48
+ add_employment(xml)
49
+ xml.tag!('Age', @options[:age]) if @options[:age]
50
+ xml.tag!('DOB', @options[:dob]) if @options[:dob]
51
+ xml.tag!('YOB', @options[:yob]) if @options[:yob]
52
+ end
53
+ end
54
+
55
+ def add_current_address(xml)
56
+ xml.tag!('CurrentAddress') do
57
+ xml.tag!('Street', @options[:street])
58
+ xml.tag!('City', @options[:city])
59
+ xml.tag!('State', @options[:state])
60
+ xml.tag!('Zip', @options[:zip])
61
+ end if @options[:zip]
62
+ end
63
+
64
+ def add_previous_address(xml)
65
+ xml.tag!('PreviousAddress') do
66
+ xml.tag!('Street', @options[:previous_street])
67
+ xml.tag!('City', @options[:previous_city])
68
+ xml.tag!('State', @options[:previous_state])
69
+ xml.tag!('Zip', @options[:previous_zip])
70
+ end if @options[:previous_zip]
71
+ end
72
+
73
+ def add_driver_license(xml)
74
+ xml.tag!('DriverLicense') do
75
+ xml.tag!('State', @options[:driver_license_state])
76
+ xml.tag!('Number', @options[:driver_license_number])
77
+ end if @options[:driver_license_number]
78
+ end
79
+
80
+ def add_employment(xml)
81
+ # Not Implemented
82
+ end
83
+
84
+ def add_account_type(xml)
85
+ xml.tag!('AccountType') do
86
+ xml.tag!('Type', @options[:account_type])
87
+ xml.tag!('Terms', @options[:account_terms])
88
+ xml.tag!('FullAmount', @options[:account_full_amount])
89
+ xml.tag!('AbbreviatedAmount', @options[:account_abbreviated_amount])
90
+ end if @options[:account_type]
91
+ end
92
+
93
+ def add_output_type(xml)
94
+ xml.tag!('OutputType') do
95
+ xml.tag!('ARF') do
96
+ xml.tag!('ARFVersion', Experian::ARF_VERSION)
97
+ end
98
+ end
99
+ end
100
+
101
+ def add_vendor(xml)
102
+ xml.tag!('Vendor') do
103
+ xml.tag!('VendorNumber', Experian.vendor_number)
104
+ end
105
+ end
106
+
107
+ def add_options(xml)
108
+ xml.tag!('Options') do
109
+ xml.tag!('ReferenceNumber', @options[:reference_number])
110
+ xml.tag!('EndUser', @options[:end_user])
111
+ end if @options[:reference_number]
112
+ end
113
+
114
+ end
115
+ end
116
+ end