callcredit 0.3.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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/CHANGELOG.md +26 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +67 -0
- data/callcredit.gemspec +22 -0
- data/lib/callcredit.rb +50 -0
- data/lib/callcredit/checks/id_enhanced.rb +34 -0
- data/lib/callcredit/client.rb +42 -0
- data/lib/callcredit/config.rb +32 -0
- data/lib/callcredit/constants.rb +9 -0
- data/lib/callcredit/errors/api_error.rb +4 -0
- data/lib/callcredit/errors/authentication_error.rb +4 -0
- data/lib/callcredit/errors/callcredit_error.rb +18 -0
- data/lib/callcredit/errors/invalid_request_error.rb +10 -0
- data/lib/callcredit/middleware/check_response.rb +26 -0
- data/lib/callcredit/request.rb +105 -0
- data/lib/callcredit/response.rb +31 -0
- data/lib/callcredit/util.rb +16 -0
- data/lib/callcredit/version.rb +3 -0
- data/spec/callcredit_spec.rb +47 -0
- data/spec/checks/id_enhanced_spec.rb +65 -0
- data/spec/client_spec.rb +46 -0
- data/spec/fixtures/access_denied.xml +10 -0
- data/spec/fixtures/bad_request.xml +5 -0
- data/spec/fixtures/request.xml +50 -0
- data/spec/request_spec.rb +104 -0
- data/spec/spec_helper.rb +17 -0
- metadata +163 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 240b5ca3357002fdecfe94ff1b75791f1b26e987
|
4
|
+
data.tar.gz: c1f970fec32aa70def4515a9b8e8e6ae81748289
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b653cfdaa53a961b7c1316af1f07446fb2ba9dcdfa69f73a646a33b542d8782ca06300aee0522c8dca720573659fb42da8a5a88050bf8a6b3e12b539b04c1941
|
7
|
+
data.tar.gz: 1573d606cd8d7128dc861427614e0758724adec9e1ace6b18a02eedc53633a99f39bab9af1578f60bf1bf830656f72f2cb727dd30b932b10547a4b83f77bbeea
|
data/.gitignore
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
## 0.2.1 - February 10, 2014
|
2
|
+
|
3
|
+
- Wrap responses in a Response object. Make config accessible from the client
|
4
|
+
|
5
|
+
## 0.2.1 - February 9, 2014
|
6
|
+
|
7
|
+
- Support Date, Time, and Datetime instances for `date_of_birth` param
|
8
|
+
|
9
|
+
## 0.2.0 - February 7, 2014
|
10
|
+
|
11
|
+
- Major refactor to make setup and use more explicit
|
12
|
+
- Moved error checking to middleware
|
13
|
+
- Fixed error handling for multiple errors
|
14
|
+
|
15
|
+
## 0.1.1 - February 6, 2014
|
16
|
+
|
17
|
+
- Added `id_enhanced_check` method to `Client`
|
18
|
+
|
19
|
+
## 0.1.0 - February 6, 2014
|
20
|
+
|
21
|
+
- Implemented `check` method on `Request` module to allow ID checks
|
22
|
+
- Added readme
|
23
|
+
|
24
|
+
## 0.0.1 - February 5, 2014
|
25
|
+
|
26
|
+
- Initial commit
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Grey Baker
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# Callcredit Ruby Client Library
|
2
|
+
|
3
|
+
Simple Ruby Gem for interacting with Callcredit's CallValidate API. Wraps the
|
4
|
+
XML submissions and responses required so you can deal with Ruby hashes
|
5
|
+
instead.
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
### Initialising the gem
|
10
|
+
Requires your Callcredit credentials. If you don't have any, you'll need to
|
11
|
+
call the sales team at [Callcredit](https://callcredit.co.uk).
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
Callcredit.configure do |config|
|
15
|
+
config[:company] = YOUR_COMPANY_NAME
|
16
|
+
config[:username] = YOUR_USERNAME
|
17
|
+
config[:password] = YOUR_PASSWORD
|
18
|
+
config[:application_name] = YOUR_APPLICATION_NAME
|
19
|
+
config[:api_endpoint] = YOUR_API_ENDPOINT
|
20
|
+
end
|
21
|
+
```
|
22
|
+
|
23
|
+
### Performing checks
|
24
|
+
|
25
|
+
#### Standard AML checks
|
26
|
+
To perform an IDEnhanced check (the standard AML check) use:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
Callcredit.id_enhanced_check(first_name: "Grey", last_name: "Baker")
|
30
|
+
```
|
31
|
+
|
32
|
+
The library will raise an error if you're missing any of the required
|
33
|
+
parameters (first_name, last_name, date_of_birth, building_number and
|
34
|
+
postcode).
|
35
|
+
|
36
|
+
#### Other checks
|
37
|
+
For any other check, simply pass the name of the check you'd like to perform
|
38
|
+
into the `perform_check` method, along with details of the individual you're
|
39
|
+
checking. Note that the gem **won't** validate your inputs for these checks.
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
data_hash = { personal_data: { first_name: "Grey", last_name: "Baker" } }
|
43
|
+
Callcredit.perform_check(:id_enhanced, data_hash)
|
44
|
+
```
|
45
|
+
|
46
|
+
If you'd like to perform multiple checks at once you can pass an array of
|
47
|
+
checks.
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
data_hash = { personal_data: { first_name: "Grey", last_name: "Baker" } }
|
51
|
+
Callcredit.perform_check([:id_enhanced, :credit_score], data_hash)
|
52
|
+
```
|
53
|
+
|
54
|
+
NOTE: Currently, this gem only supports checks on the payer's personal
|
55
|
+
information (other information won't be passed through to Callsredit).
|
56
|
+
Extending the gem should be trivial if Callcredit have given you access to
|
57
|
+
other checks.
|
58
|
+
|
59
|
+
### Parsing responses
|
60
|
+
|
61
|
+
By default the body of Callcredit's response is parsed and returned as a hash.
|
62
|
+
Set the "raw" argument to true if you need the full, unprocessed response
|
63
|
+
(including headers, etc.).
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
Callcredit.config[:raw] = true
|
67
|
+
```
|
data/callcredit.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.expand_path('../lib/callcredit/version', __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.add_runtime_dependency 'faraday'
|
5
|
+
gem.add_runtime_dependency 'faraday_middleware'
|
6
|
+
gem.add_runtime_dependency 'multi_xml'
|
7
|
+
gem.add_runtime_dependency 'nokogiri'
|
8
|
+
|
9
|
+
gem.add_development_dependency 'rspec'
|
10
|
+
gem.add_development_dependency 'webmock'
|
11
|
+
|
12
|
+
gem.authors = ['Grey Baker']
|
13
|
+
gem.description = %q{Ruby wrapper for Callcredit's CallValidate API}
|
14
|
+
gem.email = ['grey@gocardless.com']
|
15
|
+
gem.files = `git ls-files`.split("\n")
|
16
|
+
gem.homepage = 'https://github.com/gocardless/callcredit-ruby'
|
17
|
+
gem.name = 'callcredit'
|
18
|
+
gem.require_paths = ['lib']
|
19
|
+
gem.summary = %q{Ruby wrapper for Callcredit's CallValidate API}
|
20
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
gem.version = Callcredit::VERSION.dup
|
22
|
+
end
|
data/lib/callcredit.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'faraday_middleware'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
require 'callcredit/version'
|
5
|
+
require 'callcredit/util'
|
6
|
+
require 'callcredit/config'
|
7
|
+
require 'callcredit/request'
|
8
|
+
require 'callcredit/constants'
|
9
|
+
require 'callcredit/client'
|
10
|
+
require 'callcredit/response'
|
11
|
+
require 'callcredit/checks/id_enhanced'
|
12
|
+
require 'callcredit/middleware/check_response'
|
13
|
+
|
14
|
+
# Errors
|
15
|
+
require 'callcredit/errors/callcredit_error'
|
16
|
+
require 'callcredit/errors/api_error'
|
17
|
+
require 'callcredit/errors/authentication_error'
|
18
|
+
require 'callcredit/errors/invalid_request_error'
|
19
|
+
|
20
|
+
module Callcredit
|
21
|
+
def self.configure(&block)
|
22
|
+
@config = Config.new(&block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.id_enhanced_check(*args)
|
26
|
+
client.id_enhanced_check(*args)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.perform_check(*args)
|
30
|
+
client.perform_check(*args)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Require configuration before use
|
34
|
+
def self.config
|
35
|
+
if @config
|
36
|
+
@config
|
37
|
+
else
|
38
|
+
msg = "No config found. Use Callcredit.configure to set username, " +
|
39
|
+
"password, company and application name. See " +
|
40
|
+
"https://github.com/gocardless/callcredit for details."
|
41
|
+
raise CallcreditError.new(msg)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.client
|
46
|
+
@client ||= Client.new(config)
|
47
|
+
end
|
48
|
+
private_class_method :client
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Callcredit
|
2
|
+
module Checks
|
3
|
+
class IDEnhanced
|
4
|
+
REQUIRED_INPUTS = [:date_of_birth, :first_name, :last_name, :postcode]
|
5
|
+
|
6
|
+
def initialize(client)
|
7
|
+
@client = client
|
8
|
+
end
|
9
|
+
|
10
|
+
def perform(data = {})
|
11
|
+
check_params(data)
|
12
|
+
response = @client.perform_check(:id_enhanced, personal_data: data)
|
13
|
+
@client.config[:raw] ? response : Response.new(response)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def check_params(data)
|
19
|
+
REQUIRED_INPUTS.each do |param|
|
20
|
+
if data[param].nil?
|
21
|
+
msg = "An IDEnhanced check requires a #{param}"
|
22
|
+
raise InvalidRequestError.new(msg, param)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# For and ID Enhanced check, we also need a building name or number
|
27
|
+
unless data[:building_number] || data[:building_name]
|
28
|
+
msg = "An IDEnhanced check requires a building number or name"
|
29
|
+
raise InvalidRequestError.new(msg, :building_number)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Callcredit
|
2
|
+
class Client
|
3
|
+
def initialize(config=nil)
|
4
|
+
@config = (config || Callcredit.config).clone
|
5
|
+
end
|
6
|
+
|
7
|
+
def id_enhanced_check(check_data)
|
8
|
+
check = Checks::IDEnhanced.new(self)
|
9
|
+
check.perform(check_data)
|
10
|
+
end
|
11
|
+
|
12
|
+
def perform_check(check_types, check_data)
|
13
|
+
request = Request.new(connection, @config)
|
14
|
+
request.perform(check_types, check_data)
|
15
|
+
end
|
16
|
+
|
17
|
+
def config
|
18
|
+
@config
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def connection
|
24
|
+
options = {
|
25
|
+
ssl: { verify: false },
|
26
|
+
url: @config[:api_endpoint],
|
27
|
+
headers: {
|
28
|
+
'Accept' => "application/xml",
|
29
|
+
'User-Agent' => @config[:user_agent]
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
Faraday.new(options) do |conn|
|
34
|
+
conn.response :check_response unless @config[:raw] # Check XML
|
35
|
+
conn.response :xml unless @config[:raw] # Parse response
|
36
|
+
conn.response :follow_redirects, limit: 3 # Follow redirect
|
37
|
+
conn.response :raise_error # Raise errors
|
38
|
+
conn.adapter @config[:adapter]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Callcredit
|
2
|
+
class Config
|
3
|
+
DEFAULT_OPTIONS = {
|
4
|
+
adapter: Faraday.default_adapter,
|
5
|
+
company: nil,
|
6
|
+
username: nil,
|
7
|
+
password: nil,
|
8
|
+
application_name: nil,
|
9
|
+
raw: false,
|
10
|
+
api_endpoint: "https://ct.callcreditsecure.co.uk/callvalidateapi/incomingserver.php",
|
11
|
+
user_agent: "Callcredit Ruby Gem #{Callcredit::VERSION}".freeze
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@config = {}
|
16
|
+
yield self if block_given?
|
17
|
+
end
|
18
|
+
|
19
|
+
def [](name)
|
20
|
+
@config.fetch(name, DEFAULT_OPTIONS[name])
|
21
|
+
end
|
22
|
+
|
23
|
+
def []=(name, val)
|
24
|
+
@config[name] = val
|
25
|
+
end
|
26
|
+
|
27
|
+
def clone
|
28
|
+
Config.new { |config| @config.each { |k,v| config[k] = v } }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Callcredit
|
2
|
+
module Constants
|
3
|
+
# Check types suported by the CallValidate API
|
4
|
+
CHECKS = %w(BankStandard BankEnhanced CardLive CardEnhanced IDEnhanced
|
5
|
+
NCOAAlert CallValidate3D TheAffordabilityReport DeliveryFraud
|
6
|
+
EmailValidate CreditScore Zodiac IPAddress BankAccountPlus
|
7
|
+
BankOFA CardOFA)
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Callcredit
|
2
|
+
class CallcreditError < StandardError
|
3
|
+
attr_reader :message
|
4
|
+
attr_reader :status
|
5
|
+
attr_reader :response_body
|
6
|
+
|
7
|
+
def initialize(message=nil, status=nil, response=nil)
|
8
|
+
@message = message
|
9
|
+
@status = status
|
10
|
+
@response_body = response_body
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
status_string = @status.nil? ? "" : "(Status #{@status}) "
|
15
|
+
"#{status_string}#{@message}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Callcredit
|
2
|
+
module Middleware
|
3
|
+
class CheckResponse < Faraday::Middleware
|
4
|
+
def call(env)
|
5
|
+
@app.call(env).on_complete do |env|
|
6
|
+
unless results = env[:body]["Results"]
|
7
|
+
raise APIError.new("Received unexpected XML (Results tag missing)")
|
8
|
+
end
|
9
|
+
|
10
|
+
if results["Errors"]
|
11
|
+
errors = results["Errors"].values.flatten
|
12
|
+
message = errors.map { |e| e["__content__"] }.join(" | ")
|
13
|
+
raise APIError.new(message, env[:status], env)
|
14
|
+
end
|
15
|
+
response_values(env)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def response_values(env)
|
20
|
+
{ status: env[:status], headers: env[:headers], body: env[:body] }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Faraday.register_middleware :response, check_response: CheckResponse
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Callcredit
|
2
|
+
class Request
|
3
|
+
def initialize(connection, config)
|
4
|
+
@connection = connection
|
5
|
+
@config = config
|
6
|
+
end
|
7
|
+
|
8
|
+
# Perform a credit check
|
9
|
+
def perform(checks, check_data = {})
|
10
|
+
response = @connection.get do |request|
|
11
|
+
request.path = @config[:api_endpoint]
|
12
|
+
request.body = build_request_xml(checks, check_data).to_s
|
13
|
+
end
|
14
|
+
@config[:raw] ? response : response.body
|
15
|
+
rescue Faraday::Error::ClientError => e
|
16
|
+
raise APIError.new(e.response[:body], e.response[:status], e.response)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Compile the complete XML request to send to Callcredit
|
20
|
+
def build_request_xml(checks, check_data={})
|
21
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
22
|
+
xml.callvalidate do
|
23
|
+
authentication(xml)
|
24
|
+
xml.sessions do
|
25
|
+
xml.session do
|
26
|
+
xml.data do
|
27
|
+
required_checks(xml, checks)
|
28
|
+
personal_data(xml, check_data[:personal_data])
|
29
|
+
card_data(xml, check_data[:card_data])
|
30
|
+
bank_data(xml, check_data[:bank_data])
|
31
|
+
income_data(xml, check_data[:income_data])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
xml.application @config[:application_name]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
builder.doc
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# Authentication details
|
44
|
+
def authentication(xml)
|
45
|
+
xml.authentication do
|
46
|
+
xml.company @config[:company]
|
47
|
+
xml.username @config[:username]
|
48
|
+
xml.password @config[:password]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Checks to be performed
|
53
|
+
def required_checks(xml, checks)
|
54
|
+
required_checks = [*checks].map { |c| Util.underscore(c).to_sym }
|
55
|
+
xml.ChecksRequired do
|
56
|
+
Callcredit::Constants::CHECKS.each do |check|
|
57
|
+
included = required_checks.include?(Util.underscore(check).to_sym)
|
58
|
+
xml.send(check, included ? "yes" : "no")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def personal_data(xml, data)
|
64
|
+
unless data.is_a? Hash
|
65
|
+
raise InvalidRequestError.new(
|
66
|
+
"All checks require personal_data",
|
67
|
+
:personal_data)
|
68
|
+
end
|
69
|
+
|
70
|
+
if data[:date_of_birth].is_a? String
|
71
|
+
data[:date_of_birth] = Date.parse(data[:date_of_birth])
|
72
|
+
end
|
73
|
+
|
74
|
+
xml.Personal do
|
75
|
+
xml.Individual do
|
76
|
+
xml.Dateofbirth data[:date_of_birth].strftime("%d/%m/%Y")
|
77
|
+
xml.Title "Mr" # Title is mandatory but ignored...
|
78
|
+
xml.Firstname data[:first_name]
|
79
|
+
xml.Othernames data[:middle_names]
|
80
|
+
xml.Surname data[:last_name]
|
81
|
+
xml.Phonenumber data[:phone]
|
82
|
+
xml.Drivinglicensenumber data[:driving_license]
|
83
|
+
end
|
84
|
+
xml.Address do
|
85
|
+
xml.Buildingnumber data[:building_number]
|
86
|
+
xml.Buildingname data[:building_name]
|
87
|
+
xml.Address1 data[:address_line_1]
|
88
|
+
xml.Postcode data[:postcode]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def card_data(xml, data)
|
94
|
+
# Not implemented
|
95
|
+
end
|
96
|
+
|
97
|
+
def bank_data(xml, data)
|
98
|
+
# Not implemented
|
99
|
+
end
|
100
|
+
|
101
|
+
def income_data(xml, data)
|
102
|
+
# Not implemented
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Callcredit
|
2
|
+
class Response
|
3
|
+
def initialize(response_data)
|
4
|
+
@response_data = response_data
|
5
|
+
end
|
6
|
+
|
7
|
+
def rid
|
8
|
+
@response_data["Results"]["Result"]["RID"]
|
9
|
+
end
|
10
|
+
|
11
|
+
def pid
|
12
|
+
@response_data["Results"]["Result"]["PID"]
|
13
|
+
end
|
14
|
+
|
15
|
+
def input
|
16
|
+
@response_data["Results"]["Result"]["Displays"]["InputData"]
|
17
|
+
end
|
18
|
+
|
19
|
+
def result
|
20
|
+
@response_data["Results"]["Result"]["Displays"]["IdentityCheck"]
|
21
|
+
end
|
22
|
+
|
23
|
+
def warnings
|
24
|
+
@response_data["Results"]["Result"]["Displays"]["Warnings"]
|
25
|
+
end
|
26
|
+
|
27
|
+
def full_result
|
28
|
+
@response_data
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Callcredit
|
2
|
+
module Util
|
3
|
+
extend self
|
4
|
+
# String helper
|
5
|
+
def camelize(str)
|
6
|
+
str.split('_').map(&:capitalize).join
|
7
|
+
end
|
8
|
+
|
9
|
+
def underscore(str)
|
10
|
+
str.to_s.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
11
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
12
|
+
tr("-", "_").
|
13
|
+
downcase
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Callcredit do
|
4
|
+
before { Callcredit.instance_variable_set(:@config, nil) }
|
5
|
+
|
6
|
+
describe '#configure' do
|
7
|
+
subject { Callcredit.config }
|
8
|
+
Callcredit::Config::DEFAULT_OPTIONS.keys.map(&:to_sym).each do |key|
|
9
|
+
context "setting #{key}" do
|
10
|
+
before { Callcredit.configure { |config| config[key] = key } }
|
11
|
+
its([key]) { should == key }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#config" do
|
17
|
+
subject(:config) { Callcredit.config }
|
18
|
+
|
19
|
+
it "raises a CallcreditError if Callcredit hasn't been configured" do
|
20
|
+
expect { config }.to raise_error Callcredit::CallcreditError
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#id_enhanced_check' do
|
25
|
+
before { configure_callcredit }
|
26
|
+
let(:data) { { first_name: "Grey", last_name: "Baker" } }
|
27
|
+
|
28
|
+
it "delegates to the client" do
|
29
|
+
Callcredit::Client.any_instance.
|
30
|
+
should_receive(:id_enhanced_check).with(data)
|
31
|
+
Callcredit.id_enhanced_check(data)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#perform_check' do
|
36
|
+
before { configure_callcredit }
|
37
|
+
let(:data) do
|
38
|
+
{ personal_data: { first_name: "Grey", last_name: "Baker" } }
|
39
|
+
end
|
40
|
+
|
41
|
+
it "delegates to the client" do
|
42
|
+
Callcredit::Client.any_instance.
|
43
|
+
should_receive(:perform_check).with(:id_enhanced_check, data)
|
44
|
+
Callcredit.perform_check(:id_enhanced_check, data)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Callcredit::Checks::IDEnhanced do
|
4
|
+
let(:id_enhanced_check) { Callcredit::Checks::IDEnhanced.new(client) }
|
5
|
+
let(:client) { Callcredit::Client.new(config) }
|
6
|
+
let(:config) { Callcredit::Config.new }
|
7
|
+
|
8
|
+
let(:response_hash) { { status: status, body: body } }
|
9
|
+
let(:status) { 200 }
|
10
|
+
let(:body) do
|
11
|
+
"<Results>
|
12
|
+
<Result>
|
13
|
+
<Displays>
|
14
|
+
<InputData/>
|
15
|
+
<IdentityCheck/>
|
16
|
+
</Displays>
|
17
|
+
</Result>
|
18
|
+
<Errors/>
|
19
|
+
</Results>"
|
20
|
+
end
|
21
|
+
before { stub_request(:get, config[:api_endpoint]).to_return(response_hash) }
|
22
|
+
|
23
|
+
describe "#perform" do
|
24
|
+
subject(:perform_check) { id_enhanced_check.perform(check_data) }
|
25
|
+
|
26
|
+
let(:check_data) do
|
27
|
+
{ first_name: "Grey",
|
28
|
+
last_name: "Baker",
|
29
|
+
date_of_birth: "01/01/2000",
|
30
|
+
postcode: "EC2A 1DX",
|
31
|
+
building_number: "22-25" }
|
32
|
+
end
|
33
|
+
|
34
|
+
it "makes a get request" do
|
35
|
+
perform_check
|
36
|
+
a_request(:get, config[:api_endpoint]).should have_been_made
|
37
|
+
end
|
38
|
+
|
39
|
+
it { should be_a Callcredit::Response }
|
40
|
+
|
41
|
+
context "when the config[:raw] is true" do
|
42
|
+
before { config[:raw] = true }
|
43
|
+
it { should be_a Faraday::Response }
|
44
|
+
its(:body) { should be_a String }
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "validates inputs" do
|
48
|
+
it_behaves_like "it validates presence", :first_name
|
49
|
+
it_behaves_like "it validates presence", :last_name
|
50
|
+
it_behaves_like "it validates presence", :postcode
|
51
|
+
it_behaves_like "it validates presence", :date_of_birth
|
52
|
+
it_behaves_like "it validates presence", :building_number
|
53
|
+
|
54
|
+
context "with a building_name instead of building number" do
|
55
|
+
before { check_data.delete(:building_number) }
|
56
|
+
before { check_data[:building_name] = "The Mill" }
|
57
|
+
|
58
|
+
it "makes a get request" do
|
59
|
+
perform_check
|
60
|
+
a_request(:get, config[:api_endpoint]).should have_been_made
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Callcredit::Client do
|
4
|
+
let(:client) { Callcredit::Client.new(config) }
|
5
|
+
let(:config) { Callcredit::Config.new }
|
6
|
+
let(:check_data) { {} }
|
7
|
+
|
8
|
+
describe "#new" do
|
9
|
+
context "without a config" do
|
10
|
+
before { configure_callcredit }
|
11
|
+
subject(:new_client) { Callcredit::Client.new }
|
12
|
+
|
13
|
+
its(:config) { should_not == Callcredit.config }
|
14
|
+
it "has the attributes of the global config" do
|
15
|
+
new_client.config[:first_name] == Callcredit.config[:first_name]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "with a config" do
|
20
|
+
before { config[:first_name] = "test" }
|
21
|
+
subject(:new_client) { Callcredit::Client.new(config) }
|
22
|
+
|
23
|
+
its(:config) { should_not == config }
|
24
|
+
it "has the attributes of the passed in config" do
|
25
|
+
new_client.config[:first_name] == config[:first_name]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#id_enhanced_check" do
|
31
|
+
it "delegates to an instance of IDEnhanced" do
|
32
|
+
expect_any_instance_of(Callcredit::Checks::IDEnhanced).
|
33
|
+
to receive(:perform).once
|
34
|
+
client.id_enhanced_check(check_data)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#perform_check" do
|
39
|
+
subject(:perform_check) { client.id_enhanced_check(check_data) }
|
40
|
+
|
41
|
+
it "delegates to an instance of Request" do
|
42
|
+
expect_any_instance_of(Callcredit::Request).to receive(:perform).once
|
43
|
+
client.perform_check(:check_type, check_data)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<callvalidate>
|
3
|
+
<authentication>
|
4
|
+
<company/>
|
5
|
+
<username/>
|
6
|
+
<password/>
|
7
|
+
</authentication>
|
8
|
+
<sessions>
|
9
|
+
<session>
|
10
|
+
<data>
|
11
|
+
<ChecksRequired>
|
12
|
+
<BankStandard>no</BankStandard>
|
13
|
+
<BankEnhanced>no</BankEnhanced>
|
14
|
+
<CardLive>no</CardLive>
|
15
|
+
<CardEnhanced>no</CardEnhanced>
|
16
|
+
<IDEnhanced>yes</IDEnhanced>
|
17
|
+
<NCOAAlert>no</NCOAAlert>
|
18
|
+
<CallValidate3D>no</CallValidate3D>
|
19
|
+
<TheAffordabilityReport>no</TheAffordabilityReport>
|
20
|
+
<DeliveryFraud>no</DeliveryFraud>
|
21
|
+
<EmailValidate>no</EmailValidate>
|
22
|
+
<CreditScore>no</CreditScore>
|
23
|
+
<Zodiac>no</Zodiac>
|
24
|
+
<IPAddress>no</IPAddress>
|
25
|
+
<BankAccountPlus>no</BankAccountPlus>
|
26
|
+
<BankOFA>no</BankOFA>
|
27
|
+
<CardOFA>no</CardOFA>
|
28
|
+
</ChecksRequired>
|
29
|
+
<Personal>
|
30
|
+
<Individual>
|
31
|
+
<Dateofbirth>01/01/2000</Dateofbirth>
|
32
|
+
<Title>Mr</Title>
|
33
|
+
<Firstname/>
|
34
|
+
<Othernames/>
|
35
|
+
<Surname/>
|
36
|
+
<Phonenumber/>
|
37
|
+
<Drivinglicensenumber/>
|
38
|
+
</Individual>
|
39
|
+
<Address>
|
40
|
+
<Buildingnumber/>
|
41
|
+
<Buildingname/>
|
42
|
+
<Address1/>
|
43
|
+
<Postcode/>
|
44
|
+
</Address>
|
45
|
+
</Personal>
|
46
|
+
</data>
|
47
|
+
</session>
|
48
|
+
</sessions>
|
49
|
+
<application/>
|
50
|
+
</callvalidate>
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Callcredit::Request do
|
4
|
+
let(:request) { Callcredit::Request.new(connection, config) }
|
5
|
+
let(:config) { Callcredit::Config.new }
|
6
|
+
let(:connection) { Callcredit::Client.new(config).send(:connection) }
|
7
|
+
|
8
|
+
let(:response_hash) { { status: status, body: body } }
|
9
|
+
let(:status) { 200 }
|
10
|
+
let(:body) { "<Results><Errors/></Results>" }
|
11
|
+
before { stub_request(:get, config[:api_endpoint]).to_return(response_hash) }
|
12
|
+
|
13
|
+
let(:check_data) { { personal_data: { date_of_birth: date_of_birth } } }
|
14
|
+
let(:date_of_birth) { "01/01/2000" }
|
15
|
+
|
16
|
+
describe '#build_request_xml' do
|
17
|
+
subject(:build_request_xml) do
|
18
|
+
request.build_request_xml(:id_enhanced, check_data).to_s
|
19
|
+
end
|
20
|
+
let(:request_xml) do
|
21
|
+
path = File.join(File.dirname(__FILE__), 'fixtures', 'request.xml')
|
22
|
+
File.open(path.to_s).read
|
23
|
+
end
|
24
|
+
|
25
|
+
it { should == request_xml }
|
26
|
+
|
27
|
+
context "with a date object for date_of_birth" do
|
28
|
+
let(:date_of_birth) { Date.parse("01/01/2000") }
|
29
|
+
it { should == request_xml }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#perform" do
|
34
|
+
subject(:perform_check) { request.perform(:id_enhanced, check_data) }
|
35
|
+
|
36
|
+
it "makes a get request" do
|
37
|
+
perform_check
|
38
|
+
a_request(:get, config[:api_endpoint]).should have_been_made
|
39
|
+
end
|
40
|
+
|
41
|
+
context "when the config[:raw] is true" do
|
42
|
+
before { config[:raw] = true }
|
43
|
+
it { should be_a Faraday::Response }
|
44
|
+
its(:body) { should be_a String }
|
45
|
+
end
|
46
|
+
|
47
|
+
context "when the config[:raw] is false" do
|
48
|
+
it { should be_a Hash }
|
49
|
+
it { should include "Results" }
|
50
|
+
|
51
|
+
context "errors" do
|
52
|
+
context "500" do
|
53
|
+
let(:status) { 500 }
|
54
|
+
|
55
|
+
it "wraps the error" do
|
56
|
+
expect { perform_check }.to raise_error Callcredit::APIError
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "400" do
|
61
|
+
let(:status) { 400 }
|
62
|
+
|
63
|
+
it "wraps the error" do
|
64
|
+
expect { perform_check }.to raise_error Callcredit::APIError
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "200 with a single error from Callcredit" do
|
69
|
+
let(:body) do
|
70
|
+
path = File.join(File.dirname(__FILE__),
|
71
|
+
'fixtures', 'bad_request.xml')
|
72
|
+
File.open(path.to_s).read
|
73
|
+
end
|
74
|
+
|
75
|
+
it "wraps the error" do
|
76
|
+
expect { perform_check }.
|
77
|
+
to raise_error(Callcredit::APIError, "Single error")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "200 with multiple errors from Callcredit" do
|
82
|
+
let(:body) do
|
83
|
+
path = File.join(File.dirname(__FILE__),
|
84
|
+
'fixtures', 'access_denied.xml')
|
85
|
+
File.open(path.to_s).read
|
86
|
+
end
|
87
|
+
|
88
|
+
it "wraps the error" do
|
89
|
+
expect { perform_check }.
|
90
|
+
to raise_error(Callcredit::APIError, "Error1 | Error2")
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context "200 with unexpected XML" do
|
95
|
+
let(:body) { "<TopLevel></TopLevel>" }
|
96
|
+
|
97
|
+
it "wraps the error" do
|
98
|
+
expect { perform_check }.to raise_error Callcredit::APIError
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'callcredit'
|
2
|
+
require 'webmock/rspec'
|
3
|
+
RSpec.configure { |config| config.include WebMock::API }
|
4
|
+
|
5
|
+
def configure_callcredit
|
6
|
+
Callcredit.configure { |config| config[:first_name] = "Grey" }
|
7
|
+
end
|
8
|
+
|
9
|
+
shared_examples "it validates presence" do |property|
|
10
|
+
context "with a missing #{property}" do
|
11
|
+
before { check_data.delete(property) }
|
12
|
+
|
13
|
+
it "raises and error" do
|
14
|
+
expect { subject }.to raise_error Callcredit::InvalidRequestError
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: callcredit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Grey Baker
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-02-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faraday
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: faraday_middleware
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: multi_xml
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: nokogiri
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: webmock
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Ruby wrapper for Callcredit's CallValidate API
|
98
|
+
email:
|
99
|
+
- grey@gocardless.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- .gitignore
|
105
|
+
- CHANGELOG.md
|
106
|
+
- Gemfile
|
107
|
+
- LICENSE
|
108
|
+
- README.md
|
109
|
+
- callcredit.gemspec
|
110
|
+
- lib/callcredit.rb
|
111
|
+
- lib/callcredit/checks/id_enhanced.rb
|
112
|
+
- lib/callcredit/client.rb
|
113
|
+
- lib/callcredit/config.rb
|
114
|
+
- lib/callcredit/constants.rb
|
115
|
+
- lib/callcredit/errors/api_error.rb
|
116
|
+
- lib/callcredit/errors/authentication_error.rb
|
117
|
+
- lib/callcredit/errors/callcredit_error.rb
|
118
|
+
- lib/callcredit/errors/invalid_request_error.rb
|
119
|
+
- lib/callcredit/middleware/check_response.rb
|
120
|
+
- lib/callcredit/request.rb
|
121
|
+
- lib/callcredit/response.rb
|
122
|
+
- lib/callcredit/util.rb
|
123
|
+
- lib/callcredit/version.rb
|
124
|
+
- spec/callcredit_spec.rb
|
125
|
+
- spec/checks/id_enhanced_spec.rb
|
126
|
+
- spec/client_spec.rb
|
127
|
+
- spec/fixtures/access_denied.xml
|
128
|
+
- spec/fixtures/bad_request.xml
|
129
|
+
- spec/fixtures/request.xml
|
130
|
+
- spec/request_spec.rb
|
131
|
+
- spec/spec_helper.rb
|
132
|
+
homepage: https://github.com/gocardless/callcredit-ruby
|
133
|
+
licenses: []
|
134
|
+
metadata: {}
|
135
|
+
post_install_message:
|
136
|
+
rdoc_options: []
|
137
|
+
require_paths:
|
138
|
+
- lib
|
139
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - '>='
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
145
|
+
requirements:
|
146
|
+
- - '>='
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: '0'
|
149
|
+
requirements: []
|
150
|
+
rubyforge_project:
|
151
|
+
rubygems_version: 2.2.2
|
152
|
+
signing_key:
|
153
|
+
specification_version: 4
|
154
|
+
summary: Ruby wrapper for Callcredit's CallValidate API
|
155
|
+
test_files:
|
156
|
+
- spec/callcredit_spec.rb
|
157
|
+
- spec/checks/id_enhanced_spec.rb
|
158
|
+
- spec/client_spec.rb
|
159
|
+
- spec/fixtures/access_denied.xml
|
160
|
+
- spec/fixtures/bad_request.xml
|
161
|
+
- spec/fixtures/request.xml
|
162
|
+
- spec/request_spec.rb
|
163
|
+
- spec/spec_helper.rb
|