diff --git a/.gitignore b/.gitignore
index c1e0daf..4b729f0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,5 +17,8 @@ tmtags
coverage
rdoc
pkg
+vendor
+.bundle
+.ideal
## PROJECT::SPECIFIC
diff --git a/Gemfile b/Gemfile
index 855cf03..b82fe25 100644
--- a/Gemfile
+++ b/Gemfile
@@ -3,3 +3,8 @@ source "http://rubygems.org"
gem 'rake'
gemspec
+
+
+group :test do
+ gem 'activesupport'
+end
\ No newline at end of file
diff --git a/Gemfile.lock b/Gemfile.lock
index 4c1eb88..b73992a 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,24 +1,26 @@
PATH
remote: .
specs:
- ideal (0.2.0)
- builder
+ ideal (0.9.0)
nap
+ nokogiri
GEM
remote: http://rubygems.org/
specs:
- builder (3.0.0)
+ activesupport (3.0.0)
metaclass (0.0.1)
mocha (0.10.0)
metaclass (~> 0.0.1)
- nap (0.4)
+ nap (0.5.1)
+ nokogiri (1.5.5)
rake (0.9.2.2)
PLATFORMS
ruby
DEPENDENCIES
+ activesupport
ideal!
mocha
rake
diff --git a/ideal.gemspec b/ideal.gemspec
index f7b6f01..61bd9eb 100644
--- a/ideal.gemspec
+++ b/ideal.gemspec
@@ -22,7 +22,7 @@ Gem::Specification.new do |s|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]
- s.add_dependency "builder"
+ s.add_dependency "nokogiri"
s.add_dependency "nap"
s.add_development_dependency "mocha"
end
diff --git a/lib/ideal.rb b/lib/ideal.rb
index 56fce30..46e7d52 100644
--- a/lib/ideal.rb
+++ b/lib/ideal.rb
@@ -1,6 +1,6 @@
# encoding: utf-8
-require 'builder'
+require 'nokogiri'
require 'rest'
require 'ideal/acquirers'
diff --git a/lib/ideal/acquirers.rb b/lib/ideal/acquirers.rb
index 9a8ad84..d6d7570 100644
--- a/lib/ideal/acquirers.rb
+++ b/lib/ideal/acquirers.rb
@@ -7,12 +7,12 @@ module Ideal
'test_url' => 'https://idealtest.secure-ing.com/ideal/iDeal'
},
'rabobank' => {
- 'live_url' => 'https://ideal.rabobank.nl/ideal/iDeal',
- 'test_url' => 'https://idealtest.rabobank.nl/ideal/iDeal'
+ 'live_url' => 'https://ideal.rabobank.nl/ideal/iDEALv3',
+ 'test_url' => 'https://idealtest.rabobank.nl/ideal/iDEALv3'
},
'abnamro' => {
'live_url' => 'https://abnamro.ideal-payment.de/ideal/iDeal',
'test_url' => 'https://abnamro-test.ideal-payment.de/ideal/iDeal'
}
}
-end
\ No newline at end of file
+end
diff --git a/lib/ideal/gateway.rb b/lib/ideal/gateway.rb
index 996972f..e937f86 100644
--- a/lib/ideal/gateway.rb
+++ b/lib/ideal/gateway.rb
@@ -3,7 +3,7 @@
require 'openssl'
require 'net/https'
require 'base64'
-require 'digest/sha1'
+require 'digest/sha2'
module Ideal
# === Response classes
@@ -15,11 +15,10 @@ module Ideal
#
# See the Response class for more information on errors.
class Gateway
- AUTHENTICATION_TYPE = 'SHA1_RSA'
LANGUAGE = 'nl'
CURRENCY = 'EUR'
- API_VERSION = '1.1.0'
- XML_NAMESPACE = 'http://www.idealdesk.com/Message'
+ API_VERSION = '3.3.1'
+ XML_NAMESPACE = 'http://www.idealdesk.com/ideal/messages/mer-acq/3.3.1'
def self.acquirers
Ideal::ACQUIRERS
@@ -138,7 +137,7 @@ def request_url
#
# gateway.issuers.list # => [{ :id => '1006', :name => 'ABN AMRO Bank' }, …]
def issuers
- post_data request_url, build_directory_request_body, DirectoryResponse
+ post_data request_url, build_directory_request, DirectoryResponse
end
# Starts a purchase by sending an acquirer transaction request for the
@@ -186,7 +185,7 @@ def issuers
#
# See the Gateway class description for a more elaborate example.
def setup_purchase(money, options)
- post_data request_url, build_transaction_request_body(money, options), TransactionResponse
+ post_data request_url, build_transaction_request(money, options), TransactionResponse
end
# Sends a acquirer status request for the specified +transaction_id+ and
@@ -206,7 +205,7 @@ def setup_purchase(money, options)
#
# See the Gateway class description for a more elaborate example.
def capture(transaction_id)
- post_data request_url, build_status_request_body(:transaction_id => transaction_id), StatusResponse
+ post_data request_url, build_status_request(:transaction_id => transaction_id), StatusResponse
end
private
@@ -240,22 +239,48 @@ def enforce_maximum_length(key, string, max_length)
raise ArgumentError, "The value for `#{key}' contains diacritical characters `#{string}'." if string =~ DIACRITICAL_CHARACTERS
end
- # Returns the +token+ as specified in section 2.8.4 of the iDeal specs.
- #
- # This is the params['AcquirerStatusRes']['Signature']['fingerprint'] in
- # a StatusResponse instance.
- def token
- Digest::SHA1.hexdigest(self.class.private_certificate.to_der).upcase
- end
-
def strip_whitespace(str)
str.gsub(/\s/m,'')
end
+
+ #signs the xml
+ def sign!(xml)
+ digest_val = digest_value(xml)
+ xml.Signature(xmlns: 'http://www.w3.org/2000/09/xmldsig#') do |xml|
+ xml.SignedInfo do |xml|
+ xml.CanonicalizationMethod(Algorithm: 'http://www.w3.org/2001/10/xml-exc-c14n#')
+ xml.SignatureMethod(Algorithm: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256')
+ xml.Reference(URI: '') do |xml|
+ xml.Transforms do |xml|
+ xml.Transform(Algorithm: 'http://www.w3.org/2000/09/xmldsig#enveloped-signature')
+ end
+ xml.DigestMethod(Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256')
+ xml.DigestValue digest_val
+ end
+ end
+ xml.SignatureValue signature_value(digest_val)
+ xml.KeyInfo do |xml|
+ xml.KeyName fingerprint
+ end
+ end
+ end
- # Creates a +tokenCode+ from the specified +message+.
- def token_code(message)
- signature = self.class.private_key.sign(OpenSSL::Digest::SHA1.new, strip_whitespace(message))
- strip_whitespace(Base64.encode64(signature))
+ # Creates a +signatureValue+ from the xml+.
+ def signature_value(digest_value)
+ signature = Ideal::Gateway.private_key.sign(OpenSSL::Digest::SHA256.new, digest_value)
+ strip_whitespace(Base64.encode64(strip_whitespace(signature)))
+ end
+
+ # Creates a +digestValue+ from the xml+.
+ def digest_value(xml)
+ canonical = xml.doc.canonicalize(Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0)
+ digest = OpenSSL::Digest::SHA256.new.digest canonical
+ strip_whitespace(Base64.encode64(strip_whitespace(digest)))
+ end
+
+ # Creates a keyName value for the XML signature
+ def fingerprint
+ Digest::SHA1.hexdigest(Ideal::Gateway.private_certificate.to_der).upcase
end
# Returns a string containing the current UTC time, formatted as per the
@@ -264,62 +289,6 @@ def created_at_timestamp
Time.now.gmtime.strftime("%Y-%m-%dT%H:%M:%S.000Z")
end
- def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
- if first_letter_in_uppercase
- lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
- else
- lower_case_and_underscored_word.to_s[0].chr.downcase + camelize(lower_case_and_underscored_word)[1..-1]
- end
- end
-
- # iDeal doesn't really seem to care about nice looking keys in their XML.
- # Probably some Java XML class, hence the method name.
- def javaize_key(key)
- key = key.to_s
- case key
- when 'acquirer_transaction_request'
- 'AcquirerTrxReq'
- when 'acquirer_status_request'
- 'AcquirerStatusReq'
- when 'directory_request'
- 'DirectoryReq'
- when 'issuer', 'merchant', 'transaction'
- key.capitalize
- when 'created_at'
- 'createDateTimeStamp'
- when 'merchant_return_url'
- 'merchantReturnURL'
- when 'token_code', 'expiration_period', 'entrance_code'
- key[0,1] + camelize(key)[1..-1]
- when /^(\w+)_id$/
- "#{$1}ID"
- else
- key
- end
- end
-
- # Creates xml with a given hash of tag-value pairs according to the iDeal
- # requirements.
- def xml_for(name, tags_and_values)
- xml = Builder::XmlMarkup.new
- xml.instruct!
- xml.tag!(javaize_key(name), 'xmlns' => XML_NAMESPACE, 'version' => API_VERSION) { xml_from_array(xml, tags_and_values) }
- xml.target!
- end
-
- # Recursively creates xml for a given hash of tag-value pair. Uses
- # javaize_key on the tags to create the tags needed by iDeal.
- def xml_from_array(builder, tags_and_values)
- tags_and_values.each do |tag, value|
- tag = javaize_key(tag)
- if value.is_a?(Array)
- builder.tag!(tag) { xml_from_array(builder, value) }
- else
- builder.tag!(tag, value)
- end
- end
- end
-
def requires!(options, *keys)
missing = keys - options.keys
unless missing.empty?
@@ -327,45 +296,40 @@ def requires!(options, *keys)
end
end
- def build_status_request_body(options)
+ def build_status_request(options)
requires!(options, :transaction_id)
timestamp = created_at_timestamp
- message = "#{timestamp}#{self.class.merchant_id}#{@sub_id}#{options[:transaction_id]}"
-
- xml_for(:acquirer_status_request, [
- [:created_at, timestamp],
- [:merchant, [
- [:merchant_id, self.class.merchant_id],
- [:sub_id, @sub_id],
- [:authentication, AUTHENTICATION_TYPE],
- [:token, token],
- [:token_code, token_code(message)]
- ]],
-
- [:transaction, [
- [:transaction_id, options[:transaction_id]]
- ]]
- ])
+ Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
+ xml.AcquirerStatusReq(xmlns: XML_NAMESPACE, version: API_VERSION) do |xml|
+ xml.createDateTimestamp created_at_timestamp
+ xml.Merchant do |xml|
+ xml.merchantID self.class.merchant_id
+ xml.subID @sub_id
+ end
+ xml.Transaction do |xml|
+ xml.transactionID options[:transaction_id]
+ end
+ sign!(xml)
+ end
+ end.to_xml
end
- def build_directory_request_body
+ def build_directory_request
timestamp = created_at_timestamp
- message = "#{timestamp}#{self.class.merchant_id}#{@sub_id}"
-
- xml_for(:directory_request, [
- [:created_at, timestamp],
- [:merchant, [
- [:merchant_id, self.class.merchant_id],
- [:sub_id, @sub_id],
- [:authentication, AUTHENTICATION_TYPE],
- [:token, token],
- [:token_code, token_code(message)]
- ]]
- ])
+ Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
+ xml.DirectoryReq(xmlns: XML_NAMESPACE, version: API_VERSION) do |xml|
+ xml.createDateTimestamp created_at_timestamp
+ xml.Merchant do |xml|
+ xml.merchantID self.class.merchant_id
+ xml.subID @sub_id
+ end
+ sign!(xml)
+ end
+ end.to_xml
end
- def build_transaction_request_body(money, options)
+ def build_transaction_request(money, options)
requires!(options, :issuer_id, :expiration_period, :return_url, :order_id, :description, :entrance_code)
enforce_maximum_length(:money, money.to_s, 12)
@@ -374,41 +338,30 @@ def build_transaction_request_body(money, options)
enforce_maximum_length(:entrance_code, options[:entrance_code], 40)
timestamp = created_at_timestamp
- message = timestamp +
- options[:issuer_id] +
- self.class.merchant_id +
- @sub_id.to_s +
- options[:return_url] +
- options[:order_id] +
- money.to_s +
- CURRENCY +
- LANGUAGE +
- options[:description] +
- options[:entrance_code]
-
- xml_for(:acquirer_transaction_request, [
- [:created_at, timestamp],
- [:issuer, [[:issuer_id, options[:issuer_id]]]],
-
- [:merchant, [
- [:merchant_id, self.class.merchant_id],
- [:sub_id, @sub_id],
- [:authentication, AUTHENTICATION_TYPE],
- [:token, token],
- [:token_code, token_code(message)],
- [:merchant_return_url, options[:return_url]]
- ]],
-
- [:transaction, [
- [:purchase_id, options[:order_id]],
- [:amount, money],
- [:currency, CURRENCY],
- [:expiration_period, options[:expiration_period]],
- [:language, LANGUAGE],
- [:description, options[:description]],
- [:entrance_code, options[:entrance_code]]
- ]]
- ])
+
+ Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
+ xml.AcquirerTrxReq(xmlns: XML_NAMESPACE, version: API_VERSION) do |xml|
+ xml.createDateTimestamp created_at_timestamp
+ xml.Issuer do |xml|
+ xml.issuerID options[:issuer_id]
+ end
+ xml.Merchant do |xml|
+ xml.merchantID self.class.merchant_id
+ xml.subID 0
+ xml.merchantReturnURL options[:return_url]
+ end
+ xml.Transaction do |xml|
+ xml.purchaseID options[:order_id]
+ xml.amount money
+ xml.currency CURRENCY
+ xml.expirationPeriod options[:expiration_period]
+ xml.language LANGUAGE
+ xml.description options[:description]
+ xml.entranceCode options[:entrance_code]
+ end
+ sign!(xml)
+ end
+ end.to_xml
end
def log(thing, contents)
diff --git a/test/gateway_test.rb b/test/gateway_test.rb
index db29aaa..dfdcfa7 100644
--- a/test/gateway_test.rb
+++ b/test/gateway_test.rb
@@ -6,7 +6,7 @@ module IdealTestCases
# This method is called at the end of the file when all fixture data has been loaded.
def self.setup_ideal_gateway!
Ideal::Gateway.class_eval do
- self.acquirer = :ing
+ self.acquirer = :rabobank
self.merchant_id = '123456789'
@@ -18,13 +18,14 @@ def self.setup_ideal_gateway!
end
VALID_PURCHASE_OPTIONS = {
- :issuer_id => '0001',
+ :issuer_id => '99999IBAN',
:expiration_period => 'PT10M',
:return_url => 'http://return_to.example.com',
:order_id => '12345678901',
:description => 'A classic Dutch windmill',
:entrance_code => '1234'
}
+
class ClassMethodsTest < Test::Unit::TestCase
def test_merchant_id
@@ -38,7 +39,7 @@ def test_verify_live_url_for_ing
def test_verify_live_url_for_rabobank
Ideal::Gateway.acquirer = :rabobank
- assert_equal 'https://ideal.rabobank.nl/ideal/iDeal', Ideal::Gateway.live_url
+ assert_equal 'https://ideal.rabobank.nl/ideal/iDEALv3', Ideal::Gateway.live_url
end
def test_verify_live_urls_for_abnamro
@@ -53,7 +54,7 @@ def test_does_not_allow_configuration_of_unknown_acquirers
end
def test_acquirers
- assert_equal 'https://ideal.rabobank.nl/ideal/iDeal', Ideal::Gateway.acquirers['rabobank']['live_url']
+ assert_equal 'https://ideal.rabobank.nl/ideal/iDEALv3', Ideal::Gateway.acquirers['rabobank']['live_url']
assert_equal 'https://ideal.secure-ing.com/ideal/iDeal', Ideal::Gateway.acquirers['ing']['live_url']
assert_equal 'https://abnamro.ideal-payment.de/ideal/iDeal', Ideal::Gateway.acquirers['abnamro']['live_url']
end
@@ -103,52 +104,52 @@ def test_returns_created_at_timestamp
assert_equal timestamp, @gateway.send(:created_at_timestamp)
end
- def test_ruby_to_java_keys_conversion
- keys = [
- [:acquirer_transaction_request, 'AcquirerTrxReq'],
- [:acquirer_status_request, 'AcquirerStatusReq'],
- [:directory_request, 'DirectoryReq'],
- [:created_at, 'createDateTimeStamp'],
- [:issuer, 'Issuer'],
- [:merchant, 'Merchant'],
- [:transaction, 'Transaction'],
- [:issuer_id, 'issuerID'],
- [:merchant_id, 'merchantID'],
- [:sub_id, 'subID'],
- [:token_code, 'tokenCode'],
- [:merchant_return_url, 'merchantReturnURL'],
- [:purchase_id, 'purchaseID'],
- [:expiration_period, 'expirationPeriod'],
- [:entrance_code, 'entranceCode']
- ]
-
- keys.each do |key, expected_key|
- assert_equal expected_key, @gateway.send(:javaize_key, key)
+ def test_digest_value_generation
+ sha256 = OpenSSL::Digest::SHA256.new
+ OpenSSL::Digest::SHA256.stubs(:new).returns(sha256)
+ xml = Nokogiri::XML::Builder.new do |xml|
+ xml.request do |xml|
+ xml.content 'digest test'
+ @gateway.send(:sign!, xml)
+ end
end
+ digest_value = xml.doc.at_xpath('//xmlns:DigestValue', 'xmlns' => 'http://www.w3.org/2000/09/xmldsig#').text
+ xml.doc.at_xpath('//xmlns:Signature', 'xmlns' => 'http://www.w3.org/2000/09/xmldsig#').remove
+ canonical = xml.doc.canonicalize(Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0)
+ digest = sha256.digest canonical
+ expected_digest_value = strip_whitespace(Base64.encode64(strip_whitespace(digest)))
+ assert_equal expected_digest_value, digest_value
end
- def test_does_not_convert_unknown_key_to_java_key
- assert_equal 'not_a_registered_key', @gateway.send(:javaize_key, :not_a_registered_key)
+
+ def test_signature_value_generation
+ sha256 = OpenSSL::Digest::SHA256.new
+ OpenSSL::Digest::SHA256.stubs(:new).returns(sha256)
+ signature_value = @gateway.send(:signature_value, 'foo')
+ signature = Ideal::Gateway.private_key.sign(sha256, 'foo')
+ expected_signature_value = strip_whitespace(Base64.encode64(strip_whitespace(signature)))
+ assert_equal expected_signature_value, signature_value
end
- def test_token_generation
+ def test_key_name_generation
expected_token = Digest::SHA1.hexdigest(OpenSSL::X509::Certificate.new(PRIVATE_CERTIFICATE).to_der).upcase
- assert_equal expected_token, @gateway.send(:token)
+ assert_equal expected_token, @gateway.send(:fingerprint)
end
- def test_token_code_generation
- Ideal::Gateway.acquirer = :ing
- message = "Top\tsecret\tman.\nI could tell you, but then I'd have to kill you…"
- stripped_message = message.gsub(/\s/m, '')
-
- sha1 = OpenSSL::Digest::SHA1.new
- OpenSSL::Digest::SHA1.stubs(:new).returns(sha1)
- signature = Ideal::Gateway.private_key.sign(sha1, stripped_message)
- encoded_signature = Base64.encode64(signature).strip.gsub(/\n/, '')
-
- assert_equal encoded_signature, @gateway.send(:token_code, message)
- end
+ # def test_token_code_generation
+ # Ideal::Gateway.acquirer = :ing
+ # message = "Top\tsecret\tman.\nI could tell you, but then I'd have to kill you…"
+ # stripped_message = message.gsub(/\s/m, '')
+ #
+ # sha1 = OpenSSL::Digest::SHA1.new
+ # OpenSSL::Digest::SHA1.stubs(:new).returns(sha1)
+ #
+ # signature = Ideal::Gateway.private_key.sign(sha1, stripped_message)
+ # encoded_signature = Base64.encode64(signature).strip.gsub(/\n/, '')
+ #
+ # assert_equal encoded_signature, @gateway.send(:token_code, message)
+ # end
def test_posts_data_with_ssl_to_request_url_and_return_the_correct_response_for_test
Ideal::Gateway.environment = :test
@@ -168,60 +169,63 @@ def test_posts_data_with_ssl_to_request_url_and_return_the_correct_response_for_
class XMLBuildingTest < Test::Unit::TestCase
def setup
@gateway = Ideal::Gateway.new
- end
-
- def test_contains_correct_info_in_root_node
- expected_xml = Builder::XmlMarkup.new
- expected_xml.instruct!
- expected_xml.tag!('AcquirerTrxReq', 'xmlns' => Ideal::Gateway::XML_NAMESPACE, 'version' => Ideal::Gateway::API_VERSION) {}
-
- assert_equal expected_xml.target!, @gateway.send(:xml_for, :acquirer_transaction_request, [])
- end
-
- def test_creates_correct_xml_with_java_keys_from_array_with_ruby_keys
- expected_xml = Builder::XmlMarkup.new
- expected_xml.instruct!
- expected_xml.tag!('AcquirerTrxReq', 'xmlns' => Ideal::Gateway::XML_NAMESPACE, 'version' => Ideal::Gateway::API_VERSION) do
- expected_xml.tag!('a_parent') do
- expected_xml.tag!('createDateTimeStamp', '2009-01-26')
- end
- end
-
- assert_equal expected_xml.target!, @gateway.send(:xml_for, :acquirer_transaction_request, [[:a_parent, [[:created_at, '2009-01-26']]]])
- end
+ @gateway.stubs(:created_at_timestamp).returns('created_at_timestamp')
+ @gateway.stubs(:digest_value).returns('digest_value')
+ @gateway.stubs(:signature_value).returns('signature_value')
+ @gateway.stubs(:fingerprint).returns('fingerprint')
+ end
+
+ def test_transaction_request_xml
+ options = {
+ issuer_id: 'issuer_id',
+ return_url: 'return_url',
+ order_id: 'purchase_id',
+ expiration_period: 'expiration_period',
+ description: 'description',
+ entrance_code: 'entrance_code'
+ }
+ xml = @gateway.send(:build_transaction_request, 'amount', options)
+ assert_equal xml, TRANSACTION_REQUEST
+ end
+
+ def test_status_request_xml
+ options = {
+ transaction_id: 'transaction_id',
+ }
+ xml = @gateway.send(:build_status_request, options)
+ assert_equal xml, STATUS_REQUEST
+ end
+
+ def test_directory_request_xml
+ xml = @gateway.send(:build_directory_request)
+ assert_equal xml, DIRECTORY_REQUEST
+ end
+
+
end
- class RequestBodyBuildingTest < Test::Unit::TestCase
+ class ErroneousInputTest < Test::Unit::TestCase
+
def setup
@gateway = Ideal::Gateway.new
-
@gateway.stubs(:created_at_timestamp).returns('created_at_timestamp')
- @gateway.stubs(:token).returns('the_token')
- @gateway.stubs(:token_code)
-
+ @gateway.stubs(:digest_value).returns('digest_value')
+ @gateway.stubs(:signature_value).returns('signature_value')
+ @gateway.stubs(:fingerprint).returns('fingerprint')
+
@transaction_id = '0001023456789112'
- end
- def test_build_transaction_request_body_raises_ArgumentError_with_missing_required_options
- options = VALID_PURCHASE_OPTIONS.dup
- options.keys.each do |key|
- options.delete(key)
-
- assert_raise(ArgumentError) do
- @gateway.send(:build_transaction_request_body, 100, options)
- end
- end
end
-
+
def test_valid_with_valid_options
- assert_not_nil @gateway.send(:build_transaction_request_body, 4321, VALID_PURCHASE_OPTIONS)
+ assert_not_nil @gateway.send(:build_transaction_request, 4321, VALID_PURCHASE_OPTIONS)
end
-
+
def test_checks_that_fields_are_not_too_long
assert_raise ArgumentError do
- @gateway.send(:build_transaction_request_body, 1234567890123, VALID_PURCHASE_OPTIONS) # 13 chars
+ @gateway.send(:build_transaction_request, 1234567890123, VALID_PURCHASE_OPTIONS) # 13 chars
end
-
+
[
[:order_id, '12345678901234567'], # 17 chars,
[:description, '123456789012345678901234567890123'], # 33 chars
@@ -229,119 +233,45 @@ def test_checks_that_fields_are_not_too_long
].each do |key, value|
options = VALID_PURCHASE_OPTIONS.dup
options[key] = value
-
+
assert_raise ArgumentError do
- @gateway.send(:build_transaction_request_body, 4321, options)
+ @gateway.send(:build_transaction_request, 4321, options)
end
end
end
-
+
+ def test_build_transaction_request_body_raises_ArgumentError_with_missing_required_options
+ options = VALID_PURCHASE_OPTIONS.dup
+ options.keys.each do |key|
+ options.delete(key)
+
+ assert_raise(ArgumentError) do
+ @gateway.send(:build_transaction_request, 100, options)
+ end
+ end
+ end
+
def test_checks_that_fields_do_not_contain_diacritical_characters
assert_raise ArgumentError do
- @gateway.send(:build_transaction_request_body, 'graphème', VALID_PURCHASE_OPTIONS)
+ @gateway.send(:build_transaction_request, 'graphème', VALID_PURCHASE_OPTIONS)
end
-
+
[:order_id, :description, :entrance_code].each do |key, value|
options = VALID_PURCHASE_OPTIONS.dup
options[key] = 'graphème'
-
+
assert_raise ArgumentError do
- @gateway.send(:build_transaction_request_body, 4321, options)
+ @gateway.send(:build_transaction_request, 4321, options)
end
end
end
-
- def test_builds_a_transaction_request_body
- money = 4321
-
- message = 'created_at_timestamp' +
- VALID_PURCHASE_OPTIONS[:issuer_id] +
- Ideal::Gateway.merchant_id +
- @gateway.sub_id.to_s +
- VALID_PURCHASE_OPTIONS[:return_url] +
- VALID_PURCHASE_OPTIONS[:order_id] +
- money.to_s +
- Ideal::Gateway::CURRENCY +
- Ideal::Gateway::LANGUAGE +
- VALID_PURCHASE_OPTIONS[:description] +
- VALID_PURCHASE_OPTIONS[:entrance_code]
-
- @gateway.expects(:token_code).with(message).returns('the_token_code')
-
- @gateway.expects(:xml_for).with(:acquirer_transaction_request, [
- [:created_at, 'created_at_timestamp'],
- [:issuer, [[:issuer_id, VALID_PURCHASE_OPTIONS[:issuer_id]]]],
-
- [:merchant, [
- [:merchant_id, Ideal::Gateway.merchant_id],
- [:sub_id, @gateway.sub_id],
- [:authentication, Ideal::Gateway::AUTHENTICATION_TYPE],
- [:token, 'the_token'],
- [:token_code, 'the_token_code'],
- [:merchant_return_url, VALID_PURCHASE_OPTIONS[:return_url]]
- ]],
-
- [:transaction, [
- [:purchase_id, VALID_PURCHASE_OPTIONS[:order_id]],
- [:amount, money],
- [:currency, Ideal::Gateway::CURRENCY],
- [:expiration_period, VALID_PURCHASE_OPTIONS[:expiration_period]],
- [:language, Ideal::Gateway::LANGUAGE],
- [:description, VALID_PURCHASE_OPTIONS[:description]],
- [:entrance_code, VALID_PURCHASE_OPTIONS[:entrance_code]]
- ]]
- ])
-
- @gateway.send(:build_transaction_request_body, money, VALID_PURCHASE_OPTIONS)
- end
-
- def test_builds_a_directory_request_body
- message = 'created_at_timestamp' + Ideal::Gateway.merchant_id + @gateway.sub_id.to_s
- @gateway.expects(:token_code).with(message).returns('the_token_code')
-
- @gateway.expects(:xml_for).with(:directory_request, [
- [:created_at, 'created_at_timestamp'],
- [:merchant, [
- [:merchant_id, Ideal::Gateway.merchant_id],
- [:sub_id, @gateway.sub_id],
- [:authentication, Ideal::Gateway::AUTHENTICATION_TYPE],
- [:token, 'the_token'],
- [:token_code, 'the_token_code']
- ]]
- ])
-
- @gateway.send(:build_directory_request_body)
- end
-
+
def test_builds_a_status_request_body_raises_ArgumentError_with_missing_required_options
assert_raise(ArgumentError) do
- @gateway.send(:build_status_request_body, {})
+ @gateway.send(:build_status_request, {})
end
end
-
- def test_builds_a_status_request_body
- options = { :transaction_id => @transaction_id }
-
- message = 'created_at_timestamp' + Ideal::Gateway.merchant_id + @gateway.sub_id.to_s + options[:transaction_id]
- @gateway.expects(:token_code).with(message).returns('the_token_code')
-
- @gateway.expects(:xml_for).with(:acquirer_status_request, [
- [:created_at, 'created_at_timestamp'],
- [:merchant, [
- [:merchant_id, Ideal::Gateway.merchant_id],
- [:sub_id, @gateway.sub_id],
- [:authentication, Ideal::Gateway::AUTHENTICATION_TYPE],
- [:token, 'the_token'],
- [:token_code, 'the_token_code']
- ]],
-
- [:transaction, [
- [:transaction_id, options[:transaction_id]]
- ]],
- ])
-
- @gateway.send(:build_status_request_body, options)
- end
+
end
class GeneralResponseTest < Test::Unit::TestCase
@@ -415,7 +345,7 @@ def setup
end
def test_returns_a_list_with_only_one_issuer
- @gateway.stubs(:build_directory_request_body).returns('the request body')
+ @gateway.stubs(:build_directory_request).returns('the request body')
@gateway.expects(:ssl_post).with(@gateway.request_url, 'the request body').returns(DIRECTORY_RESPONSE_WITH_ONE_ISSUER)
expected_issuers = [{ :id => '1006', :name => 'ABN AMRO Bank' }]
@@ -426,7 +356,7 @@ def test_returns_a_list_with_only_one_issuer
end
def test_returns_list_of_issuers_from_response
- @gateway.stubs(:build_directory_request_body).returns('the request body')
+ @gateway.stubs(:build_directory_request).returns('the request body')
@gateway.expects(:ssl_post).with(@gateway.request_url, 'the request body').returns(DIRECTORY_RESPONSE_WITH_MULTIPLE_ISSUERS)
expected_issuers = [
@@ -447,7 +377,7 @@ class SetupPurchaseTest < Test::Unit::TestCase
def setup
@gateway = Ideal::Gateway.new
- @gateway.stubs(:build_transaction_request_body).with(4321, VALID_PURCHASE_OPTIONS).returns('the request body')
+ @gateway.stubs(:build_transaction_request).with(4321, VALID_PURCHASE_OPTIONS).returns('the request body')
@gateway.expects(:ssl_post).with(@gateway.request_url, 'the request body').returns(ACQUIRER_TRANSACTION_RESPONSE)
@setup_purchase_response = @gateway.setup_purchase(4321, VALID_PURCHASE_OPTIONS)
@@ -471,7 +401,7 @@ class CapturePurchaseTest < Test::Unit::TestCase
def setup
@gateway = Ideal::Gateway.new
- @gateway.stubs(:build_status_request_body).
+ @gateway.stubs(:build_status_request).
with(:transaction_id => '0001023456789112').returns('the request body')
end
@@ -738,5 +668,101 @@ def expects_request_and_returns(str)
}
+ TRANSACTION_REQUEST = @transaction_xml = %{
+
+ created_at_timestamp
+
+ issuer_id
+
+
+ 123456789
+ 0
+ return_url
+
+
+ purchase_id
+ amount
+ EUR
+ expiration_period
+ nl
+ description
+ entrance_code
+
+
+
+
+
+
+
+
+
+
+ digest_value
+
+
+ signature_value
+
+ fingerprint
+
+
+
+}
+
+ DIRECTORY_REQUEST = %{
+
+ created_at_timestamp
+
+ 123456789
+ 0
+
+
+
+
+
+
+
+
+
+
+ digest_value
+
+
+ signature_value
+
+ fingerprint
+
+
+
+}
+
+ STATUS_REQUEST = %{
+
+ created_at_timestamp
+
+ 123456789
+ 0
+
+
+ transaction_id
+
+
+
+
+
+
+
+
+
+
+ digest_value
+
+
+ signature_value
+
+ fingerprint
+
+
+
+}
setup_ideal_gateway!
end
\ No newline at end of file
diff --git a/test/helper.rb b/test/helper.rb
index b5f8bf1..4897d0a 100644
--- a/test/helper.rb
+++ b/test/helper.rb
@@ -1,6 +1,7 @@
require 'rubygems'
require 'test/unit'
require 'mocha'
+require 'active_support/test_case'
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
$LOAD_PATH.unshift(File.dirname(__FILE__))
@@ -9,13 +10,21 @@
$stdout.sync = true
+unless defined?(Test::Unit::AssertionFailedError)
+ Test::Unit::AssertionFailedError = ActiveSupport::TestCase::Assertion
+end
+
module Test
module Unit
class TestCase
HOME_DIR = RUBY_PLATFORM =~ /mswin32/ ? ENV['HOMEPATH'] : ENV['HOME'] unless defined?(HOME_DIR)
LOCAL_CREDENTIALS = File.join(HOME_DIR.to_s, '.ideal/fixtures.yml') unless defined?(LOCAL_CREDENTIALS)
DEFAULT_CREDENTIALS = File.join(File.dirname(__FILE__), 'fixtures.yml') unless defined?(DEFAULT_CREDENTIALS)
-
+
+ def strip_whitespace(str)
+ str.gsub(/\s/m,'')
+ end
+
private
def all_fixtures
diff --git a/test/remote_test.rb b/test/remote_test.rb
index 4a0200d..e79c4cc 100644
--- a/test/remote_test.rb
+++ b/test/remote_test.rb
@@ -9,7 +9,7 @@ def setup
@gateway = Ideal::Gateway.new
- @@issuer ||= @gateway.issuers.list[0]
+ @@issuer ||= {id: 'RABONL2UXXX'}
@valid_options = {
:issuer_id => @@issuer[:id],