callcredit 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,2 @@
1
+ Gemfile.lock
2
+ .bundle
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
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
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
+ ```
@@ -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,4 @@
1
+ module Callcredit
2
+ class APIError < CallcreditError
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Callcredit
2
+ class AuthenticationError < CallcreditError
3
+ end
4
+ 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,10 @@
1
+ module Callcredit
2
+ class InvalidRequestError < CallcreditError
3
+ attr_accessor :param
4
+
5
+ def initialize(message, param, status=nil, response_body=nil)
6
+ super(message, status, response_body)
7
+ @param = param
8
+ end
9
+ end
10
+ 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,3 @@
1
+ module Callcredit
2
+ VERSION = '0.3.0'.freeze
3
+ 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
@@ -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,10 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Results>
3
+ <Result PID="" RID="">
4
+ <Displays/>
5
+ </Result>
6
+ <Errors>
7
+ <Error module="auth">Error1</Error>
8
+ <Error module="default">Error2</Error>
9
+ </Errors>
10
+ </Results>
@@ -0,0 +1,5 @@
1
+ <Results>
2
+ <Errors>
3
+ <Error module="default">Single error</Error>
4
+ </Errors>
5
+ </Results>
@@ -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
@@ -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