callcredit 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|