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