From 1141bae115e4d7f83e38f92300b0962e15f5cdd9 Mon Sep 17 00:00:00 2001 From: Anup Narkhede Date: Sun, 22 Nov 2009 22:11:15 +0000 Subject: [PATCH] Adding 3d secure support for SagePayGateway --- lib/active_merchant/billing/gateway.rb | 4 + lib/active_merchant/billing/gateways/bogus.rb | 23 ++- lib/active_merchant/billing/gateways/sage_pay.rb | 47 +++- lib/active_merchant/billing/response.rb | 10 +- .../remote_sage_pay_three_d_secure_test.rb | 259 ++++++++++++++++++++ test/unit/gateways/bogus_test.rb | 31 +++ test/unit/gateways/gateway_test.rb | 6 + test/unit/gateways/sage_pay_test.rb | 44 ++++ test/unit/response_test.rb | 16 ++ 9 files changed, 430 insertions(+), 10 deletions(-) create mode 100644 test/remote/gateways/remote_sage_pay_three_d_secure_test.rb diff --git a/lib/active_merchant/billing/gateway.rb b/lib/active_merchant/billing/gateway.rb index 876f466..7e3232e 100644 --- a/lib/active_merchant/billing/gateway.rb +++ b/lib/active_merchant/billing/gateway.rb @@ -91,6 +91,10 @@ module ActiveMerchant #:nodoc: class_inheritable_accessor :homepage_url class_inheritable_accessor :display_name + # Indicates if the gateway supports 3D Secure authentication or not + class_inheritable_accessor :supports_3d_secure + self.supports_3d_secure = false + # The application making the calls to the gateway # Useful for things like the PayPal build notation (BN) id fields superclass_delegating_accessor :application_id diff --git a/lib/active_merchant/billing/gateways/bogus.rb b/lib/active_merchant/billing/gateways/bogus.rb index 739b5ef..64c0ecd 100644 --- a/lib/active_merchant/billing/gateways/bogus.rb +++ b/lib/active_merchant/billing/gateways/bogus.rb @@ -12,8 +12,17 @@ module ActiveMerchant #:nodoc: CAPTURE_ERROR_MESSAGE = "Bogus Gateway: Use authorization number 1 for exception, 2 for error and anything else for success" VOID_ERROR_MESSAGE = "Bogus Gateway: Use authorization number 1 for exception, 2 for error and anything else for success" + THREE_D_SECURE_MESSAGE = "Bogus Gateway: Requires additional 3D secure authentication" + + THREE_D_MD = 'md' + THREE_D_PA_REQ = 'pa_req' + THREE_D_PA_RES = 'pa_res' + THREE_D_ACS_URL = 'https://domain.com/3d_secure_page' + self.supported_countries = ['US'] self.supported_cardtypes = [:bogus] + self.supports_3d_secure = true + self.homepage_url = 'http://example.com' self.display_name = 'Bogus' @@ -23,9 +32,11 @@ module ActiveMerchant #:nodoc: Response.new(true, SUCCESS_MESSAGE, {:authorized_amount => money.to_s}, :test => true, :authorization => AUTHORIZATION ) when '2' Response.new(false, FAILURE_MESSAGE, {:authorized_amount => money.to_s, :error => FAILURE_MESSAGE }, :test => true) + when '4' + Response.new(false, THREE_D_SECURE_MESSAGE, {:authorized_amount => money.to_s}, :three_d_secure => true, :pa_req => THREE_D_PA_REQ, :md => THREE_D_MD, :acs_url => THREE_D_ACS_URL, :test => true) else raise Error, ERROR_MESSAGE - end + end end def purchase(money, creditcard, options = {}) @@ -34,11 +45,21 @@ module ActiveMerchant #:nodoc: Response.new(true, SUCCESS_MESSAGE, {:paid_amount => money.to_s}, :test => true) when '2' Response.new(false, FAILURE_MESSAGE, {:paid_amount => money.to_s, :error => FAILURE_MESSAGE },:test => true) + when '4' + Response.new(false, THREE_D_SECURE_MESSAGE, {:paid_amount => money.to_s}, :three_d_secure => true, :pa_req => THREE_D_PA_REQ, :md => THREE_D_MD, :acs_url => THREE_D_ACS_URL, :test => true) else raise Error, ERROR_MESSAGE end end + def three_d_complete(pa_res, md) + if pa_res == THREE_D_PA_RES && md == THREE_D_MD + Response.new(true, SUCCESS_MESSAGE, {}, :test => true, :authorization => AUTHORIZATION) + else + Response.new(false, FAILURE_MESSAGE, {},:test => true) + end + end + def credit(money, ident, options = {}) case ident when '1' diff --git a/lib/active_merchant/billing/gateways/sage_pay.rb b/lib/active_merchant/billing/gateways/sage_pay.rb index e464c37..8a2103b 100644 --- a/lib/active_merchant/billing/gateways/sage_pay.rb +++ b/lib/active_merchant/billing/gateways/sage_pay.rb @@ -44,10 +44,11 @@ module ActiveMerchant #:nodoc: self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :switch, :solo, :maestro, :diners_club] self.supported_countries = ['GB'] self.default_currency = 'GBP' - + self.supports_3d_secure = true + self.homepage_url = 'http://www.sagepay.com' self.display_name = 'SagePay' - + def initialize(options = {}) requires!(options, :login) @options = options @@ -58,6 +59,10 @@ module ActiveMerchant #:nodoc: @options[:test] || super end + def three_d_secure_enabled? + @options[:enable_3d_secure] + end + def purchase(money, credit_card, options = {}) requires!(options, :order_id) @@ -68,7 +73,8 @@ module ActiveMerchant #:nodoc: add_credit_card(post, credit_card) add_address(post, options) add_customer_data(post, options) - + add_three_d_secure_flag(post, options) + commit(:purchase, post) end @@ -82,7 +88,8 @@ module ActiveMerchant #:nodoc: add_credit_card(post, credit_card) add_address(post, options) add_customer_data(post, options) - + add_three_d_secure_flag(post, options) + commit(:authorization, post) end @@ -118,6 +125,10 @@ module ActiveMerchant #:nodoc: commit(:credit, post) end + def three_d_complete(pa_res, md) + commit(:three_d_complete, 'PARes' => pa_res, 'MD' => md) + end + private def add_reference(post, identification) order_id, transaction_id, authorization, security_key = identification.split(';') @@ -152,7 +163,15 @@ module ActiveMerchant #:nodoc: add_pair(post, :BillingPhone, options[:phone].gsub(/[^0-9+]/, '')[0,20]) unless options[:phone].blank? add_pair(post, :ClientIPAddress, options[:ip]) end - + + def add_three_d_secure_flag(post, options) + if three_d_secure_enabled? && options[:skip_3d_secure] != true + add_pair(post, :Apply3DSecure, '0') + else + add_pair(post, :Apply3DSecure, '2') + end + end + def add_address(post, options) if billing_address = options[:billing_address] || options[:address] first_name, last_name = parse_first_and_last_name(billing_address[:name]) @@ -236,7 +255,11 @@ module ActiveMerchant #:nodoc: :street_match => AVS_CVV_CODE[ response["AddressResult"] ], :postal_match => AVS_CVV_CODE[ response["PostCodeResult"] ], }, - :cvv_result => AVS_CVV_CODE[ response["CV2Result"] ] + :cvv_result => AVS_CVV_CODE[ response["CV2Result"] ], + :three_d_secure => response["Status"] == '3DAUTH', + :pa_req => response["PAReq"], + :md => response["MD"], + :acs_url => response["ACSURL"] ) end @@ -258,12 +281,20 @@ module ActiveMerchant #:nodoc: end def build_url(action) - endpoint = [ :purchase, :authorization ].include?(action) ? "vspdirect-register" : TRANSACTIONS[action].downcase + if action == :three_d_complete + endpoint = 'direct3dcallback' + else + endpoint = [ :purchase, :authorization ].include?(action) ? "vspdirect-register" : TRANSACTIONS[action].downcase + end "#{test? ? TEST_URL : LIVE_URL}/#{endpoint}.vsp" end def build_simulator_url(action) - endpoint = [ :purchase, :authorization ].include?(action) ? "VSPDirectGateway.asp" : "VSPServerGateway.asp?Service=Vendor#{TRANSACTIONS[action].capitalize}Tx" + if action == :three_d_complete + endpoint = 'VSPDirectCallback.asp' + else + endpoint = [ :purchase, :authorization ].include?(action) ? "VSPDirectGateway.asp" : "VSPServerGateway.asp?Service=Vendor#{TRANSACTIONS[action].capitalize}Tx" + end "#{SIMULATOR_URL}/#{endpoint}" end diff --git a/lib/active_merchant/billing/response.rb b/lib/active_merchant/billing/response.rb index 122b72e..ac71123 100644 --- a/lib/active_merchant/billing/response.rb +++ b/lib/active_merchant/billing/response.rb @@ -5,7 +5,7 @@ module ActiveMerchant #:nodoc: end class Response - attr_reader :params, :message, :test, :authorization, :avs_result, :cvv_result + attr_reader :params, :message, :test, :authorization, :avs_result, :cvv_result, :pa_req, :md, :acs_url def success? @success @@ -19,6 +19,10 @@ module ActiveMerchant #:nodoc: @fraud_review end + def three_d_secure? + @three_d_secure + end + def initialize(success, message, params = {}, options = {}) @success, @message, @params = success, message, params.stringify_keys @test = options[:test] || false @@ -26,6 +30,10 @@ module ActiveMerchant #:nodoc: @fraud_review = options[:fraud_review] @avs_result = AVSResult.new(options[:avs_result]).to_hash @cvv_result = CVVResult.new(options[:cvv_result]).to_hash + @three_d_secure = options[:three_d_secure] + @pa_req = options[:pa_req] + @md = options[:md] + @acs_url = options[:acs_url] end end end diff --git a/test/remote/gateways/remote_sage_pay_three_d_secure_test.rb b/test/remote/gateways/remote_sage_pay_three_d_secure_test.rb new file mode 100644 index 0000000..904d406 --- /dev/null +++ b/test/remote/gateways/remote_sage_pay_three_d_secure_test.rb @@ -0,0 +1,259 @@ +require File.dirname(__FILE__) + '/../../test_helper' +require 'mechanize' + +class RemoteSagePayThreeDSecureTest < Test::Unit::TestCase + # Run the tests in the test environment and ensure 3D Secure is enabled on your ProTX account settings + # set to true to run the tests in the simulated environment + SagePayGateway.simulate = false + + TEST_3D_PASSWORD = 'password' + + def setup + @gateway = SagePayGateway.new(fixtures(:protx).merge(:enable_3d_secure => true)) + + @amex = CreditCard.new( + :number => '374200000000004', + :month => 12, + :year => next_year, + :verification_value => 4887, + :first_name => 'Tekin', + :last_name => 'Suleyman', + :type => 'american_express' + ) + + @visa = CreditCard.new( + :number => '4929000000006', + :month => 6, + :year => next_year, + :verification_value => 123, + :first_name => 'Tekin', + :last_name => 'Suleyman', + :type => 'visa' + ) + + @maestro = CreditCard.new( + :number => '300000000000000004', + :month => 12, + :year => next_year, + :start_month => 12, + :start_year => next_year - 2, + :verification_value => 123, + :first_name => 'Tekin', + :last_name => 'Suleyman', + :type => 'maestro' + ) + + @solo = CreditCard.new( + :number => '6334900000000005', + :month => 6, + :year => next_year, + :issue_number => 1, + :start_month => 12, + :start_year => next_year - 2, + :verification_value => 227, + :first_name => 'Tekin', + :last_name => 'Suleyman', + :type => 'solo' + ) + + @mastercard = CreditCard.new( + :number => '5404000000000001', + :month => 12, + :year => next_year, + :verification_value => 419, + :first_name => 'Tekin', + :last_name => 'Suleyman', + :type => 'master' + ) + + @electron = CreditCard.new( + :number => '4917300000000008', + :month => 12, + :year => next_year, + :verification_value => 123, + :first_name => 'Tekin', + :last_name => 'Suleyman', + :type => 'electron' + ) + + @declined_card = CreditCard.new( + :number => '4111111111111111', + :month => 9, + :year => next_year, + :first_name => 'Tekin', + :last_name => 'Suleyman', + :type => 'visa' + ) + + @options = { + :billing_address => { + :name => 'Tekin Suleyman', + :address1 => 'Flat 10 Lapwing Court', + :address2 => 'West Didsbury', + :city => "Manchester", + :county => 'Greater Manchester', + :country => 'GB', + :zip => 'M20 2PS' + }, + :shipping_address => { + :name => 'Tekin Suleyman', + :address1 => '120 Grosvenor St', + :city => "Manchester", + :county => 'Greater Manchester', + :country => 'GB', + :zip => 'M1 7QW' + }, + :order_id => generate_unique_id, + :description => 'Store purchase', + :ip => '86.150.65.37', + :email => 'tekin@tekin.co.uk', + :phone => '0161 123 4567' + } + + @amount = 100 + end + + def test_successful_three_d_secure_mastercard_purchase + response = @gateway.purchase(@amount, @mastercard, @options) + + assert_failure response + assert_3d_secure response + + pa_res, md = retrieve_and_submit_three_d_secure_form(response, TEST_3D_PASSWORD) + three_d_secure_response = @gateway.three_d_complete(pa_res,md) + + assert_success three_d_secure_response + end + + def test_successful_purchase_with_3d_secure_override + @options = @options.merge(:skip_3d_secure => true) + response = @gateway.purchase(@amount, @mastercard, @options) + + assert_success response + assert !response.authorization.blank? + end + + def test_successful_authorization_with_3d_secure_override + @options = @options.merge(:skip_3d_secure => true) + response = @gateway.authorize(@amount, @mastercard, @options) + + assert_success response + assert !response.authorization.blank? + end + + def test_successful_three_d_secure_mastercard_authorization + response = @gateway.authorize(@amount, @mastercard, @options) + + assert_failure response + assert_3d_secure response + + pa_res, md = retrieve_and_submit_three_d_secure_form(response, TEST_3D_PASSWORD) + three_d_secure_response = @gateway.three_d_complete(pa_res,md) + + assert_success three_d_secure_response + end + + def test_successful_three_d_secure_visa_purchase + response = @gateway.purchase(@amount, @visa, @options) + + assert_failure response + assert_3d_secure response + + pa_res, md = retrieve_and_submit_three_d_secure_form(response, TEST_3D_PASSWORD) + three_d_secure_response = @gateway.three_d_complete(pa_res,md) + + assert_success three_d_secure_response + end + + def test_successful_three_d_secure_maestro_purchase + response = @gateway.purchase(@amount, @maestro, @options) + + assert_failure response + assert_3d_secure response + + pa_res, md = retrieve_and_submit_three_d_secure_form(response, TEST_3D_PASSWORD) + three_d_secure_response = @gateway.three_d_complete(pa_res,md) + + assert_success three_d_secure_response + end + + def test_successful_three_d_secure_amex_purchase + response = @gateway.purchase(@amount, @amex, @options) + + assert_failure response + assert_3d_secure response + + pa_res, md = retrieve_and_submit_three_d_secure_form(response, TEST_3D_PASSWORD) + three_d_secure_response = @gateway.three_d_complete(pa_res,md) + + assert_success three_d_secure_response + end + + def test_successful_three_d_secure_solo_purchase + response = @gateway.purchase(@amount, @solo, @options) + + assert_failure response + assert_3d_secure response + + pa_res, md = retrieve_and_submit_three_d_secure_form(response, TEST_3D_PASSWORD) + three_d_secure_response = @gateway.three_d_complete(pa_res,md) + + assert_success three_d_secure_response + end + + + def test_successful_three_d_secure_electron_purchase + response = @gateway.purchase(@amount, @electron, @options) + + assert_failure response + assert_3d_secure response + + pa_res, md = retrieve_and_submit_three_d_secure_form(response, TEST_3D_PASSWORD) + three_d_secure_response = @gateway.three_d_complete(pa_res,md) + + assert_success three_d_secure_response + end + + + def test_failed_three_d_secure_purchase + response = @gateway.purchase(@amount, @mastercard, @options) + + assert_failure response + assert_3d_secure response + + pa_res, md = retrieve_and_submit_three_d_secure_form(response, 'wrong password') + three_d_secure_response = @gateway.three_d_complete(pa_res,md) + + assert_failure three_d_secure_response + end + + def test_invalid_login + message = SagePayGateway.simulate ? 'VSP Simulator cannot find your vendor name. Ensure you have have supplied a Vendor field with your VSP Vendor name assigned to it.' : '3034 : The Vendor or VendorName value is required.' + + gateway = SagePayGateway.new( + :login => '' + ) + assert response = gateway.purchase(@amount, @mastercard, @options) + assert_equal message, response.message + assert_failure response + end + + private + + def assert_3d_secure(response) + assert response.three_d_secure?, "Response not 3D secure: #{response.inspect}" + end + + # Uses mechanize to retrieve 3D secure page, fill in password, submit and retrieve the pa_res and md + def retrieve_and_submit_three_d_secure_form(response, password) + agent = WWW::Mechanize.new + page = agent.post(response.acs_url, :MD => response.md, :PaReq => response.pa_req, :TermUrl => 'http://localhost') + page.forms[0].password = password + result = agent.submit(page.forms[0]) + [result.forms[0].PaRes,result.forms[0].MD] + end + + def next_year + Date.today.year + 1 + end +end diff --git a/test/unit/gateways/bogus_test.rb b/test/unit/gateways/bogus_test.rb index 9b2d048..f8baba4 100644 --- a/test/unit/gateways/bogus_test.rb +++ b/test/unit/gateways/bogus_test.rb @@ -20,6 +20,33 @@ class BogusTest < Test::Unit::TestCase @gateway.purchase(1000, @creditcard) end + def test_3d_secure_authorize + response = @gateway.authorize(1000, credit_card('4')) + assert response.three_d_secure? + assert_equal BogusGateway::THREE_D_PA_REQ, response.pa_req + assert_equal BogusGateway::THREE_D_MD, response.md + assert_equal BogusGateway::THREE_D_ACS_URL, response.acs_url + end + + def test_3d_secure_purchase + response = @gateway.purchase(1000, credit_card('4')) + assert response.three_d_secure? + assert_equal BogusGateway::THREE_D_PA_REQ, response.pa_req + assert_equal BogusGateway::THREE_D_MD, response.md + assert_equal BogusGateway::THREE_D_ACS_URL, response.acs_url + end + + def test_3d_complete + response = @gateway.three_d_complete(BogusGateway::THREE_D_PA_RES, BogusGateway::THREE_D_MD) + assert_equal BogusGateway::SUCCESS_MESSAGE, response.message + + response = @gateway.three_d_complete('incorrect PaRes', BogusGateway::THREE_D_MD) + assert_equal BogusGateway::FAILURE_MESSAGE, response.message + + response = @gateway.three_d_complete(BogusGateway::THREE_D_PA_RES, 'incorrect MD') + assert_equal BogusGateway::FAILURE_MESSAGE, response.message + end + def test_credit @gateway.credit(1000, @response.params["transid"]) end @@ -36,6 +63,10 @@ class BogusTest < Test::Unit::TestCase @gateway.unstore('1') end + def test_supports_3d_secure + assert @gateway.supports_3d_secure + end + def test_supported_countries assert_equal ['US'], BogusGateway.supported_countries end diff --git a/test/unit/gateways/gateway_test.rb b/test/unit/gateways/gateway_test.rb index bc09170..801e274 100644 --- a/test/unit/gateways/gateway_test.rb +++ b/test/unit/gateways/gateway_test.rb @@ -9,6 +9,12 @@ class GatewayTest < Test::Unit::TestCase assert_false [:visa, :bogus].all? { |invalid_cardtype| Gateway.supports?(invalid_cardtype) } end + def test_should_be_able_to_check_for_3d_secure_support + assert !Gateway.supports_3d_secure + Gateway.supports_3d_secure = true + assert Gateway.supports_3d_secure + end + def test_should_gateway_uses_ssl_strict_checking_by_default assert Gateway.ssl_strict end diff --git a/test/unit/gateways/sage_pay_test.rb b/test/unit/gateways/sage_pay_test.rb index 87d7999..c133915 100644 --- a/test/unit/gateways/sage_pay_test.rb +++ b/test/unit/gateways/sage_pay_test.rb @@ -43,6 +43,37 @@ class SagePayTest < Test::Unit::TestCase assert_failure response end + def test_response_requires_three_d_secure_authentication + @gateway.stubs(:ssl_post).returns(three_d_secure_response) + + response = @gateway.purchase(100, @credit_card, @options) + assert_failure response + assert response.three_d_secure? + + assert_equal 'eJxVUttygjAQfe9XMH4AuUCoOGscW9sRx7ZM7UsfmZAWVEBDUPr3TRCqzdOes5uzuyeBWVvsnZNUdV6V0xFx8WjG7+AjU1IuNlI0SnJ4kXWdfEsnT6cjign1mH8fMC8MKSMMeyMO8fxdHjn0OtzIuATQAI2AEllSag6JOD5Er5yRceD7gHoIhVTRgjMcMi8ICb4cQBcayqSQfKOlSspd5ax1CqijQFRNqdUPH9MA0ACgUXueaX2YIHQ+n926v+iKym12gGwa0HWkuLFRbeTaPOWRV7+VpyzX0Xr79bxdr0TytFx9Yr2YTwHZCkgTLTnFOMSU+g4hE8YmXgio4yEp7BycdAv0AA62x/w2c8uAsVnJUgyLDAhke6hKaSoooL8YUlkLHqtKt85LHJm+FgO67vG4tEYLbbwj1uMusmK5sceMHXRqFgCytah/PtQ/tIn+fYBfp7GzSg==', + response.pa_req + assert_equal '2012354765399251503', + response.md + assert_equal 'https://ukvpstest.protx.com/mpitools/accesscontroler?action=pareq', + response.acs_url + end + + def test_supports_3d_secure + assert @gateway.supports_3d_secure + end + + def test_can_enable_3d_secure + assert !@gateway.three_d_secure_enabled? + @gateway2 = ProtxGateway.new(:login => 'X', :enable_3d_secure => true) + assert @gateway2.three_d_secure_enabled? + end + + def test_three_d_complete + @gateway.stubs(:ssl_post).returns(successful_purchase_response) + response = @gateway.three_d_complete('PARes VALUE','MD VALUE') + assert_success response + end + def test_purchase_url assert_equal 'https://test.sagepay.com/gateway/service/vspdirect-register.vsp', @gateway.send(:url_for, :purchase) end @@ -136,4 +167,17 @@ PostCodeResult=MATCHED CV2Result=MATCHED RESP end + + def three_d_secure_response + <<-RESP +Status=3DAUTH +MD=2012354765399251503 +ACSURL=https://ukvpstest.protx.com/mpitools/accesscontroler?action=pareq +PAReq=eJxVUttygjAQfe9XMH4AuUCoOGscW9sRx7ZM7UsfmZAWVEBDUPr3TRCqzdOes5uzuyeBWVvsnZNUdV6V0xFx8WjG7+AjU1IuNlI0SnJ4kXWdfEsnT6cjign1mH8fMC8MKSMMeyMO8fxdHjn0OtzIuATQAI2AEllSag6JOD5Er5yRceD7gHoIhVTRgjMcMi8ICb4cQBcayqSQfKOlSspd5ax1CqijQFRNqdUPH9MA0ACgUXueaX2YIHQ+n926v+iKym12gGwa0HWkuLFRbeTaPOWRV7+VpyzX0Xr79bxdr0TytFx9Yr2YTwHZCkgTLTnFOMSU+g4hE8YmXgio4yEp7BycdAv0AA62x/w2c8uAsVnJUgyLDAhke6hKaSoooL8YUlkLHqtKt85LHJm+FgO67vG4tEYLbbwj1uMusmK5sceMHXRqFgCytah/PtQ/tIn+fYBfp7GzSg== +StatusDetail=2007 : Please redirect your customer to the ACSURL, passing the MD and PaReq. +VPSProtocol=2.22 +3DSecureStatus=OK + RESP + end + end diff --git a/test/unit/response_test.rb b/test/unit/response_test.rb index 753918a..e989e5b 100644 --- a/test/unit/response_test.rb +++ b/test/unit/response_test.rb @@ -6,6 +6,22 @@ class ResponseTest < Test::Unit::TestCase assert !Response.new(false, 'message', :param => 'value').success? end + def test_three_d_secure_required + assert Response.new(false, 'message', {}, :three_d_secure => true).three_d_secure? + assert !Response.new(false, 'message', {}, :three_d_secure => false).three_d_secure? + end + + def test_three_d_secure_params + pa_req ='eJxVUttygjAQfe9XMH4AuUCoOGscW9' + md = '2012354765399251503' + acs_url = 'https://ukvpstest.protx.com/mpitools/accesscontroler?action=pareq' + response = Response.new(false, 'message', {}, :three_d_secure => true, :pa_req => pa_req, :md => md, :acs_url => acs_url) + + assert_equal pa_req, response.pa_req + assert_equal md, response.md + assert_equal acs_url, response.acs_url + end + def test_get_params response = Response.new(true, 'message', :param => 'value') -- 1.6.0.2