From 12fae36f73ac9bf0a84e965a5ad66006ecc7078d Mon Sep 17 00:00:00 2001 From: Caleb Simpson Date: Wed, 21 Aug 2013 11:13:47 -0400 Subject: [PATCH 01/17] Firstdata E4: Include missing address information for AVS and CVV results. --- .../billing/gateways/firstdata_e4.rb | 17 ++++++++++++++--- .../remote/gateways/remote_firstdata_e4_test.rb | 8 ++++++++ test/unit/gateways/firstdata_e4_test.rb | 10 ++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/lib/active_merchant/billing/gateways/firstdata_e4.rb b/lib/active_merchant/billing/gateways/firstdata_e4.rb index ee9f6b4b445..1915ca758cc 100644 --- a/lib/active_merchant/billing/gateways/firstdata_e4.rb +++ b/lib/active_merchant/billing/gateways/firstdata_e4.rb @@ -116,7 +116,7 @@ def build_sale_or_authorization_request(money, credit_card_or_store_authorizatio if credit_card_or_store_authorization.is_a? String add_credit_card_token(xml, credit_card_or_store_authorization) else - add_credit_card(xml, credit_card_or_store_authorization) + add_credit_card(xml, credit_card_or_store_authorization, options) end add_customer_data(xml, options) @@ -138,7 +138,7 @@ def build_capture_or_credit_request(money, identification, options) def build_store_request(credit_card, options) xml = Builder::XmlMarkup.new - add_credit_card(xml, credit_card) + add_credit_card(xml, credit_card, options) add_customer_data(xml, options) xml.target! @@ -164,12 +164,23 @@ def add_amount(xml, money) xml.tag! "DollarAmount", amount(money) end - def add_credit_card(xml, credit_card) + def add_credit_card(xml, credit_card, options) xml.tag! "Card_Number", credit_card.number xml.tag! "Expiry_Date", expdate(credit_card) xml.tag! "CardHoldersName", credit_card.name xml.tag! "CardType", credit_card.brand + add_credit_card_verification_strings(xml, credit_card, options) + end + + def add_credit_card_verification_strings(xml, credit_card, options) + address = options[:billing_address] || options[:address] + if address + address_values = [] + [:address1, :zip, :city, :state, :country].each { |part| address_values << address[part].to_s } + xml.tag! "VerificationStr1", address_values.join("|") + end + if credit_card.verification_value? xml.tag! "CVD_Presence_Ind", "1" xml.tag! "VerificationStr2", credit_card.verification_value diff --git a/test/remote/gateways/remote_firstdata_e4_test.rb b/test/remote/gateways/remote_firstdata_e4_test.rb index 026e480fa65..6b402bb2968 100644 --- a/test/remote/gateways/remote_firstdata_e4_test.rb +++ b/test/remote/gateways/remote_firstdata_e4_test.rb @@ -78,4 +78,12 @@ def test_invalid_login assert_match /Unauthorized Request/, response.message assert_failure response end + + def test_response_contains_cvv_and_avs_results + response = @gateway.purchase(@amount, @credit_card, @options) + assert_success response + assert_equal 'M', response.cvv_result["code"] + assert_equal '1', response.avs_result["code"] + end + end diff --git a/test/unit/gateways/firstdata_e4_test.rb b/test/unit/gateways/firstdata_e4_test.rb index 622ea5c00a1..c6d387f3a4c 100644 --- a/test/unit/gateways/firstdata_e4_test.rb +++ b/test/unit/gateways/firstdata_e4_test.rb @@ -2,6 +2,8 @@ require 'yaml' class FirstdataE4Test < Test::Unit::TestCase + include CommStub + def setup @gateway = FirstdataE4Gateway.new( :login => "A00427-01", @@ -107,6 +109,14 @@ def test_cvv_result assert_equal 'M', response.cvv_result['code'] end + def test_requests_include_verification_string + stub_comms(:ssl_post) do + @gateway.purchase(@amount, @credit_card, @options) + end.check_request do |endpoint, data, headers| + assert_match "1234 My Street|K1C2N6|Ottawa|ON|CA", data + end.respond_with(successful_purchase_response) + end + private def successful_purchase_response From 42817143c5e4d6fcdc9ae723a784204ee1ec87cc Mon Sep 17 00:00:00 2001 From: Caleb Simpson Date: Wed, 21 Aug 2013 15:48:47 -0400 Subject: [PATCH 02/17] Allow changing the primary response of a MultiResponse to the first one run. --- lib/active_merchant/billing/response.rb | 12 +++++- test/unit/gateways/stripe_test.rb | 11 +++++ test/unit/multi_response_test.rb | 57 +++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/lib/active_merchant/billing/response.rb b/lib/active_merchant/billing/response.rb index c02a22e7cd7..7a714e76318 100644 --- a/lib/active_merchant/billing/response.rb +++ b/lib/active_merchant/billing/response.rb @@ -47,6 +47,7 @@ def self.run(&block) def initialize @responses = [] + @primary_response = :last end def process @@ -65,10 +66,19 @@ def success? @responses.all?{|r| r.success?} end + def primary_response + success? && @primary_response == :first ? @responses.first : @responses.last + end + + def make_first_response_primary + @primary_response = :first + self + end + %w(params message test authorization avs_result cvv_result test? fraud_review?).each do |m| class_eval %( def #{m} - (@responses.empty? ? nil : @responses.last.#{m}) + (@responses.empty? ? nil : primary_response.#{m}) end ) end diff --git a/test/unit/gateways/stripe_test.rb b/test/unit/gateways/stripe_test.rb index 4e7cfacfbcb..bf579bd4883 100644 --- a/test/unit/gateways/stripe_test.rb +++ b/test/unit/gateways/stripe_test.rb @@ -87,6 +87,17 @@ def test_successful_refund_with_refund_fee_amount assert_success response end + def test_refund_with_fee_response_gives_a_charge_authorization + s = sequence("request") + @gateway.expects(:ssl_request).returns(successful_partially_refunded_response).in_sequence(s) + @gateway.expects(:ssl_request).returns(successful_application_fee_list_response).in_sequence(s) + @gateway.expects(:ssl_request).returns(successful_refunded_application_fee_response).in_sequence(s) + + assert response = @gateway.refund(@refund_amount, 'ch_test_charge', :refund_fee_amount => 100) + assert_success response + assert_equal 'ch_test_charge', response.authorization + end + def test_unsuccessful_refund_with_refund_fee_amount_when_application_fee_id_not_found s = sequence("request") @gateway.expects(:ssl_request).returns(successful_partially_refunded_response).in_sequence(s) diff --git a/test/unit/multi_response_test.rb b/test/unit/multi_response_test.rb index 93542d0acef..979b451d551 100644 --- a/test/unit/multi_response_test.rb +++ b/test/unit/multi_response_test.rb @@ -72,6 +72,63 @@ def test_proxies_last_request assert !m.fraud_review? end + def test_proxies_first_request_if_marked + m = MultiResponse.new.make_first_response_primary + + r1 = Response.new( + true, + "1", + {"one" => 1}, + :test => true, + :authorization => "auth1", + :avs_result => {:code => "AVS1"}, + :cvv_result => "CVV1", + :fraud_review => true + ) + m.process{r1} + assert_equal({"one" => 1}, m.params) + assert_equal "1", m.message + assert m.test + assert_equal "auth1", m.authorization + assert_equal "AVS1", m.avs_result["code"] + assert_equal "CVV1", m.cvv_result["code"] + assert m.test? + assert m.fraud_review? + + r2 = Response.new( + true, + "2", + {"two" => 2}, + :test => false, + :authorization => "auth2", + :avs_result => {:code => "AVS2"}, + :cvv_result => "CVV2", + :fraud_review => false + ) + m.process{r2} + assert_equal({"one" => 1}, m.params) + assert_equal "1", m.message + assert m.test + assert_equal "auth1", m.authorization + assert_equal "AVS1", m.avs_result["code"] + assert_equal "CVV1", m.cvv_result["code"] + assert m.test? + assert m.fraud_review? + end + + def test_primary_response_always_returns_the_last_response_on_failure + m = MultiResponse.new.make_first_response_primary + + r1 = Response.new(true, "1", {}, {}) + r2 = Response.new(false, "2", {}, {}) + r3 = Response.new(false, "3", {}, {}) + m.process{r1} + m.process{r2} + m.process{r3} + assert_equal r2, m.primary_response + assert_equal '2', m.message + end + def test_stops_processing_upon_failure r1 = Response.new(false, "1", {}) r2 = Response.new(true, "2", {}) From de47a87de8153b499313b7d6c1aaac8337021933 Mon Sep 17 00:00:00 2001 From: Caleb Simpson Date: Wed, 21 Aug 2013 15:49:35 -0400 Subject: [PATCH 03/17] Stripe: Make the actual refund response primary rather than the fee refund. --- lib/active_merchant/billing/gateways/stripe.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_merchant/billing/gateways/stripe.rb b/lib/active_merchant/billing/gateways/stripe.rb index 883dcf7d7f2..307f2473860 100644 --- a/lib/active_merchant/billing/gateways/stripe.rb +++ b/lib/active_merchant/billing/gateways/stripe.rb @@ -78,7 +78,7 @@ def refund(money, identification, options = {}) r.process { fetch_application_fees(identification, commit_options) } r.process { refund_application_fee(options[:refund_fee_amount], application_fee_from_response(r), commit_options) } - end + end.make_first_response_primary end def application_fee_from_response(response) From eaf0168f6f98046c2793741f4020530851b8975a Mon Sep 17 00:00:00 2001 From: Caleb Simpson Date: Thu, 22 Aug 2013 09:53:41 -0400 Subject: [PATCH 04/17] Pass the primary response into the run method instead of tacking it on the end. --- lib/active_merchant/billing/gateways/stripe.rb | 4 ++-- lib/active_merchant/billing/response.rb | 12 +++++------- test/unit/multi_response_test.rb | 6 ++++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/active_merchant/billing/gateways/stripe.rb b/lib/active_merchant/billing/gateways/stripe.rb index 307f2473860..5924d9aa791 100644 --- a/lib/active_merchant/billing/gateways/stripe.rb +++ b/lib/active_merchant/billing/gateways/stripe.rb @@ -71,14 +71,14 @@ def refund(money, identification, options = {}) post = {:amount => amount(money)} commit_options = generate_meta(options) - MultiResponse.run do |r| + MultiResponse.run(:first) do |r| r.process { commit(:post, "charges/#{CGI.escape(identification)}/refund", post, commit_options) } return r unless options[:refund_fee_amount] r.process { fetch_application_fees(identification, commit_options) } r.process { refund_application_fee(options[:refund_fee_amount], application_fee_from_response(r), commit_options) } - end.make_first_response_primary + end end def application_fee_from_response(response) diff --git a/lib/active_merchant/billing/response.rb b/lib/active_merchant/billing/response.rb index 7a714e76318..715855511bc 100644 --- a/lib/active_merchant/billing/response.rb +++ b/lib/active_merchant/billing/response.rb @@ -39,11 +39,14 @@ def initialize(success, message, params = {}, options = {}) end class MultiResponse < Response - def self.run(&block) - new.tap(&block) + def self.run(primary_response = :last, &block) + response = new.tap(&block) + response.primary_response = primary_response + response end attr_reader :responses + attr_writer :primary_response def initialize @responses = [] @@ -70,11 +73,6 @@ def primary_response success? && @primary_response == :first ? @responses.first : @responses.last end - def make_first_response_primary - @primary_response = :first - self - end - %w(params message test authorization avs_result cvv_result test? fraud_review?).each do |m| class_eval %( def #{m} diff --git a/test/unit/multi_response_test.rb b/test/unit/multi_response_test.rb index 979b451d551..18a3b34dd88 100644 --- a/test/unit/multi_response_test.rb +++ b/test/unit/multi_response_test.rb @@ -73,7 +73,8 @@ def test_proxies_last_request end def test_proxies_first_request_if_marked - m = MultiResponse.new.make_first_response_primary + m = MultiResponse.new + m.primary_response = :first r1 = Response.new( true, @@ -117,7 +118,8 @@ def test_proxies_first_request_if_marked end def test_primary_response_always_returns_the_last_response_on_failure - m = MultiResponse.new.make_first_response_primary + m = MultiResponse.new + m.primary_response = :first r1 = Response.new(true, "1", {}, {}) r2 = Response.new(false, "2", {}, {}) From b624e3763186db8073183b3d9532af64619157dc Mon Sep 17 00:00:00 2001 From: Caleb Simpson Date: Thu, 22 Aug 2013 14:26:55 -0400 Subject: [PATCH 05/17] Litle: Deprecate credit method in favor of refund. --- lib/active_merchant/billing/gateways/litle.rb | 9 +++++++-- test/remote/gateways/remote_litle_test.rb | 8 ++++---- test/unit/gateways/litle_test.rb | 12 ++++++------ 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/lib/active_merchant/billing/gateways/litle.rb b/lib/active_merchant/billing/gateways/litle.rb index f666266e639..81a389bfcc0 100755 --- a/lib/active_merchant/billing/gateways/litle.rb +++ b/lib/active_merchant/billing/gateways/litle.rb @@ -112,11 +112,16 @@ def void(identification, options = {}) end end - def credit(money, identification_or_token, options = {}) - to_pass = build_credit_request(money, identification_or_token, options) + def refund(money, authorization, options = {}) + to_pass = build_credit_request(money, authorization, options) build_response(:credit, @litle.credit(to_pass)) end + def credit(money, authorization, options = {}) + deprecated CREDIT_DEPCRECATION_MESSAGE + refund(money, authorization, options) + end + def store(creditcard_or_paypage_registration_id, options = {}) to_pass = create_token_hash(creditcard_or_paypage_registration_id, options) build_response(:registerToken, @litle.register_token_request(to_pass), %w(000 801 802)) diff --git a/test/remote/gateways/remote_litle_test.rb b/test/remote/gateways/remote_litle_test.rb index ef4a146017a..44bcdca4542 100755 --- a/test/remote/gateways/remote_litle_test.rb +++ b/test/remote/gateways/remote_litle_test.rb @@ -88,7 +88,7 @@ def test_unsuccessful_purchase assert_equal 'Insufficient Funds', response.message end - def test_authorization_capture_credit_void + def test_authorization_capture_refund_void #Auth assert auth_response = @gateway.authorize(10010, @credit_card1, @options) @@ -102,7 +102,7 @@ def test_authorization_capture_credit_void #Credit against the Capture capture_litle_txn_id = capture_response.params['litleOnlineResponse']['captureResponse']['litleTxnId'] - assert credit_response = @gateway.credit(10010, capture_litle_txn_id) + assert credit_response = @gateway.refund(10010, capture_litle_txn_id) assert_success credit_response assert_equal 'Approved', credit_response.message @@ -119,8 +119,8 @@ def test_capture_unsuccessful assert_equal 'No transaction found with specified litleTxnId', capture_response.message end - def test_credit_unsuccessful - assert credit_response = @gateway.credit(10010, 123456789012345360) + def test_refund_unsuccessful + assert credit_response = @gateway.refund(10010, 123456789012345360) assert_failure credit_response assert_equal 'No transaction found with specified litleTxnId', credit_response.message end diff --git a/test/unit/gateways/litle_test.rb b/test/unit/gateways/litle_test.rb index d43e295c744..49c1ef8d231 100755 --- a/test/unit/gateways/litle_test.rb +++ b/test/unit/gateways/litle_test.rb @@ -593,31 +593,31 @@ def test_void_authorization_fail_schema assert_equal 'Error validating xml data against the schema', responseFrom.message end - def test_credit_pass + def test_refund_pass creditResponseObj = {'response' => '000', 'message' => 'pass', 'litleTxnId'=>'123456789012345678'} retObj = {'response'=>'0','creditResponse'=>creditResponseObj} LitleOnline::Communications.expects(:http_post => retObj.to_xml(:root => 'litleOnlineResponse')) identification = "1234;credit" - responseFrom = @gateway.credit(0, identification) + responseFrom = @gateway.refund(0, identification) assert_equal true, responseFrom.success? assert_equal '123456789012345678;credit', responseFrom.authorization end - def test_credit_fail + def test_refund_fail creditResponseObj = {'response' => '111', 'message' => 'fail', 'litleTxnId'=>'123456789012345678'} retObj = {'response'=>'0','creditResponse'=>creditResponseObj} LitleOnline::Communications.expects(:http_post => retObj.to_xml(:root => 'litleOnlineResponse')) identification = "1234;credit" - responseFrom = @gateway.credit(0, identification) + responseFrom = @gateway.refund(0, identification) assert_equal false, responseFrom.success? assert_equal '123456789012345678;credit', responseFrom.authorization end - def test_credit_fail_schema + def test_refund_fail_schema retObj = {'response'=>'1','message'=>'Error validating xml data against the schema'} LitleOnline::Communications.expects(:http_post => retObj.to_xml(:root => 'litleOnlineResponse')) identification = '1234;credit' - responseFrom = @gateway.credit(0, identification) + responseFrom = @gateway.refund(0, identification) assert_equal false, responseFrom.success? assert_equal 'Error validating xml data against the schema', responseFrom.message end From 77a459e2f8ecf6edac5009a70055d2addadfafc1 Mon Sep 17 00:00:00 2001 From: Caleb Simpson Date: Thu, 22 Aug 2013 15:31:38 -0400 Subject: [PATCH 06/17] Credit deprecation test for litle. --- lib/active_merchant/billing/gateways/litle.rb | 2 +- test/unit/gateways/litle_test.rb | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/active_merchant/billing/gateways/litle.rb b/lib/active_merchant/billing/gateways/litle.rb index 81a389bfcc0..83ce2bc28dc 100755 --- a/lib/active_merchant/billing/gateways/litle.rb +++ b/lib/active_merchant/billing/gateways/litle.rb @@ -118,7 +118,7 @@ def refund(money, authorization, options = {}) end def credit(money, authorization, options = {}) - deprecated CREDIT_DEPCRECATION_MESSAGE + deprecated CREDIT_DEPRECATION_MESSAGE refund(money, authorization, options) end diff --git a/test/unit/gateways/litle_test.rb b/test/unit/gateways/litle_test.rb index 49c1ef8d231..e3166870e8f 100755 --- a/test/unit/gateways/litle_test.rb +++ b/test/unit/gateways/litle_test.rb @@ -736,6 +736,13 @@ def test_that_setting_a_wiredump_device_on_the_gateway_sets_the_little_logger_up end end + def test_deprecated_credit + @gateway.expects(:refund).returns true + assert_deprecation_warning(Gateway::CREDIT_DEPRECATION_MESSAGE, @gateway) do + assert response = @gateway.credit(0, '123') + end + end + private def with_litle_configuration_restoration(&block) From 97dc152fefafc9d5333709d992d04ac3b172bdbd Mon Sep 17 00:00:00 2001 From: Duff OMelia Date: Thu, 22 Aug 2013 14:05:12 -0400 Subject: [PATCH 07/17] Moneris: Add verification_value support We're now actually sending the CVV to Moneris and parsing the CVV result from the response. Closes #817. --- CHANGELOG | 2 +- .../billing/gateways/moneris.rb | 33 ++++++++++++++++--- test/remote/gateways/remote_moneris_test.rb | 18 ++++++++-- test/unit/gateways/moneris_test.rb | 26 +++++++++++++-- 4 files changed, 68 insertions(+), 11 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index bae438126d3..28f4bdec65e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,7 +7,7 @@ * PayU: Fix a major bug with status types [melari] * SecureNet: Allow production transactions [duff] * Stripe: Allow a card_not_present_fee to be specified [melari] - +* Moneris: Add verification_value support [duff] == Version 1.36.0 (August 2, 2013) diff --git a/lib/active_merchant/billing/gateways/moneris.rb b/lib/active_merchant/billing/gateways/moneris.rb index f6bc8bec1fd..74b3732d14f 100644 --- a/lib/active_merchant/billing/gateways/moneris.rb +++ b/lib/active_merchant/billing/gateways/moneris.rb @@ -128,6 +128,7 @@ def add_payment_source(post, source) else post[:pan] = source.number post[:expdate] = expdate(source) + post[:cvd_value] = source.verification_value if source.verification_value? end end @@ -155,6 +156,7 @@ def commit(action, parameters = {}) Response.new(successful?(response), message_from(response[:message]), response, :test => test?, + :cvv_result => response[:cvd_result_code].try(:last), :authorization => authorization_from(response) ) end @@ -192,14 +194,35 @@ def post_data(action, parameters = {}) root = xml.add_element("request") root.add_element("store_id").text = options[:login] root.add_element("api_token").text = options[:password] - transaction = root.add_element(action) + root.add_element(transaction_element(action, parameters)) + + xml.to_s + end + + def transaction_element(action, parameters) + transaction = REXML::Element.new(action) # Must add the elements in the correct order actions[action].each do |key| - transaction.add_element(key.to_s).text = parameters[key] unless parameters[key].blank? + if key == :cvd_info + transaction.add_element(cvd_element(parameters[:cvd_value])) + else + transaction.add_element(key.to_s).text = parameters[key] unless parameters[key].blank? + end end - xml.to_s + transaction + end + + def cvd_element(cvd_value) + element = REXML::Element.new('cvd_info') + if cvd_value + element.add_element('cvd_indicator').text = "1" + element.add_element('cvd_value').text = cvd_value + else + element.add_element('cvd_indicator').text = "0" + end + element end def message_from(message) @@ -219,8 +242,8 @@ def normalize(field) def actions { - "purchase" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type], - "preauth" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type], + "purchase" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type, :cvd_info], + "preauth" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type, :cvd_info], "command" => [:order_id], "refund" => [:order_id, :amount, :txn_number, :crypt_type], "indrefund" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type], diff --git a/test/remote/gateways/remote_moneris_test.rb b/test/remote/gateways/remote_moneris_test.rb index 45525b337e6..760a0e18b34 100644 --- a/test/remote/gateways/remote_moneris_test.rb +++ b/test/remote/gateways/remote_moneris_test.rb @@ -49,12 +49,12 @@ def test_successful_authorization_and_void assert_success void end - def test_successful_purchase_and_credit + def test_successful_purchase_and_refund purchase = @gateway.purchase(@amount, @credit_card, @options) assert_success purchase - credit = @gateway.credit(@amount, purchase.authorization) - assert_success credit + refund = @gateway.refund(@amount, purchase.authorization) + assert_success refund end def test_failed_purchase_from_error @@ -108,4 +108,16 @@ def test_failed_authorization_with_vault assert_failure response end + def test_cvv_match + assert response = @gateway.purchase(1039, @credit_card, @options) + assert_success response + assert_equal({'code' => 'M', 'message' => 'Match'}, response.cvv_result) + end + + def test_cvv_no_match + assert response = @gateway.purchase(1053, @credit_card, @options) + assert_success response + assert_equal({'code' => 'N', 'message' => 'No Match'}, response.cvv_result) + end + end diff --git a/test/unit/gateways/moneris_test.rb b/test/unit/gateways/moneris_test.rb index 74e263948fa..fbd0ef4f366 100644 --- a/test/unit/gateways/moneris_test.rb +++ b/test/unit/gateways/moneris_test.rb @@ -1,6 +1,8 @@ require 'test_helper' class MonerisTest < Test::Unit::TestCase + include CommStub + def setup Base.mode = :test @@ -165,6 +167,26 @@ def test_failed_authorization_with_vault assert_failure response end + def test_passing_cvv + @credit_card.verification_value = "452" + stub_comms do + @gateway.purchase(@amount, @credit_card, @options) + end.check_request do |endpoint, data, headers| + assert_match(%r{cvd_indicator>1<}, data) + assert_match(%r{cvd_value>452<}, data) + end.respond_with(successful_purchase_response) + end + + def test_no_cvv_specified + @credit_card.verification_value = "" + stub_comms do + @gateway.purchase(@amount, @credit_card, @options) + end.check_request do |endpoint, data, headers| + assert_match(%r{cvd_indicator>0<}, data) + assert_no_match(%r{cvd_value>}, data) + end.respond_with(successful_purchase_response) + end + private def successful_purchase_response @@ -262,10 +284,10 @@ def successful_update_response def xml_purchase_fixture - 'store1yesguy1.01424242424242424203037order1' + 'store1yesguy1.01424242424242424203037order10' end def xml_capture_fixture - 'store1yesguy1.01424242424242424203037order1' + 'store1yesguy1.01424242424242424203037order10' end end From a6aeddcc998678615ace2a809fae413c736cfe06 Mon Sep 17 00:00:00 2001 From: Luis Lopez Date: Fri, 23 Aug 2013 10:43:26 -0300 Subject: [PATCH 08/17] added missing authorization_code param to response --- test/remote/gateways/remote_authorize_net_test.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/remote/gateways/remote_authorize_net_test.rb b/test/remote/gateways/remote_authorize_net_test.rb index 7676e10286d..a18b04e3d36 100644 --- a/test/remote/gateways/remote_authorize_net_test.rb +++ b/test/remote/gateways/remote_authorize_net_test.rb @@ -106,6 +106,7 @@ def test_bad_login assert_equal Response, response.class assert_equal ["action", + "authorization_code", "avs_result_code", "card_code", "response_code", @@ -128,6 +129,7 @@ def test_using_test_request assert_equal Response, response.class assert_equal ["action", + "authorization_code", "avs_result_code", "card_code", "response_code", From e748a8611a7c556de76be642949fee0282402bf8 Mon Sep 17 00:00:00 2001 From: Paul Simpson Date: Fri, 23 Aug 2013 12:45:39 -0400 Subject: [PATCH 09/17] Update Stripe gateway to reflect current currency support * Add GB to the countries supported by Stripe * Update the README to reflect CA and GB support --- README.md | 2 +- lib/active_merchant/billing/gateways/stripe.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 27c7805bc95..52409cbfb2e 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta * [SecurePayTech](http://www.securepaytech.com/) - NZ * [SkipJack](http://www.skipjack.com/) - US, CA * [Spreedly Core](https://spreedlycore.com/) - AD, AE, AT, AU, BD, BE, BG, BN, CA, CH, CY, CZ, DE, DK, EE, EG, ES, FI, FR, GB, GI, GR, HK, HU, ID, IE, IL, IM, IN, IS, IT, JO, KW, LB, LI, LK, LT, LU, LV, MC, MT, MU, MV, MX, MY, NL, NO, NZ, OM, PH, PL, PT, QA, RO, SA, SE, SG, SI, SK, SM, TR, TT, UM, US, VA, VN, ZA -* [Stripe](https://stripe.com/) - US +* [Stripe](https://stripe.com/) - US, CA, GB * [TransFirst](http://www.transfirst.com/) - US * [Transnational](http://www.tnbci.com/) - US * [TrustCommerce](http://www.trustcommerce.com/) - US diff --git a/lib/active_merchant/billing/gateways/stripe.rb b/lib/active_merchant/billing/gateways/stripe.rb index 95f97ad7ba6..8dfdcb03feb 100644 --- a/lib/active_merchant/billing/gateways/stripe.rb +++ b/lib/active_merchant/billing/gateways/stripe.rb @@ -21,7 +21,7 @@ class StripeGateway < Gateway 'unchecked' => 'P' } - self.supported_countries = ['US', 'CA'] + self.supported_countries = ['US', 'CA', 'GB'] self.default_currency = 'USD' self.money_format = :cents self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club] From 9789eb0b2e8fa6dff0a3a94958bcb0b3b0667b6e Mon Sep 17 00:00:00 2001 From: Kei Kubo Date: Mon, 26 Aug 2013 01:01:23 +0900 Subject: [PATCH 10/17] WebPay: modify the currency unit in JPY from yen (1) to sen (1/100). --- lib/active_merchant/billing/gateways/webpay.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/active_merchant/billing/gateways/webpay.rb b/lib/active_merchant/billing/gateways/webpay.rb index c291e94c222..fe2a883ec5e 100644 --- a/lib/active_merchant/billing/gateways/webpay.rb +++ b/lib/active_merchant/billing/gateways/webpay.rb @@ -25,6 +25,15 @@ def refund_fee(identification, options, meta) raise NotImplementedError.new end + def localized_amount(money, currency = self.default_currency) + non_fractional_currency?(currency) ? (amount(money).to_f / 100).floor : amount(money) + end + + def add_amount(post, money, options) + post[:currency] = (options[:currency] || currency(money)).downcase + post[:amount] = localized_amount(money, post[:currency].upcase) + end + def json_error(raw_response) msg = 'Invalid response received from the WebPay API. Please contact support@webpay.jp if you continue to receive this message.' msg += " (The raw response returned by the API was #{raw_response.inspect})" From 3e2c2c580d3b3af1510f48bf8703e3b8789f98a6 Mon Sep 17 00:00:00 2001 From: Kei Kubo Date: Mon, 26 Aug 2013 01:04:03 +0900 Subject: [PATCH 11/17] WebPay: update a test API key. --- test/fixtures.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fixtures.yml b/test/fixtures.yml index 14bdee08288..814be2c77f9 100644 --- a/test/fixtures.yml +++ b/test/fixtures.yml @@ -626,7 +626,7 @@ vindicia: password: PASSWORD webpay: - login: "vtUQeOtUnYr7PGCLQ96Ul4zqpDUO4sOE" + login: "test_secret_eHn4TTgsGguBcW764a2KA8Yd" # Working test credentials, no need to replace wirecard: From ca7442713e6038b58a4ed0e47b2441d0e4db22c5 Mon Sep 17 00:00:00 2001 From: Kei Kubo Date: Mon, 26 Aug 2013 01:04:37 +0900 Subject: [PATCH 12/17] WebPay: add unit tests to check the currency unit. --- test/remote/gateways/remote_webpay_test.rb | 6 +++--- test/unit/gateways/webpay_test.rb | 11 +++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/test/remote/gateways/remote_webpay_test.rb b/test/remote/gateways/remote_webpay_test.rb index 53295eccec4..e9b3a78349a 100644 --- a/test/remote/gateways/remote_webpay_test.rb +++ b/test/remote/gateways/remote_webpay_test.rb @@ -6,7 +6,7 @@ class RemoteWebpayTest < Test::Unit::TestCase def setup @gateway = WebpayGateway.new(fixtures(:webpay)) - @amount = 100 + @amount = 10000 @credit_card = credit_card('4242424242424242') @declined_card = credit_card('4000') @new_credit_card = credit_card('5105105105105100') @@ -59,7 +59,7 @@ def test_successful_refund assert response = @gateway.purchase(@amount, @credit_card, @options) assert_success response assert response.authorization - assert void = @gateway.refund(@amount - 20, response.authorization) + assert void = @gateway.refund(@amount - 2000, response.authorization) assert_success void end @@ -97,7 +97,7 @@ def test_invalid_login gateway = WebpayGateway.new(:login => 'active_merchant_test') assert response = gateway.purchase(@amount, @credit_card, @options) assert_failure response - assert_equal "Invalid API key provided: active_merchant_test", response.message + assert_equal "Invalid API key provided. Check your API key is correct.", response.message end end diff --git a/test/unit/gateways/webpay_test.rb b/test/unit/gateways/webpay_test.rb index 885691e0083..7e08ed24e57 100644 --- a/test/unit/gateways/webpay_test.rb +++ b/test/unit/gateways/webpay_test.rb @@ -26,6 +26,17 @@ def test_successful_purchase assert response.test? end + def test_appropiate_purchase_amount + @gateway.expects(:ssl_request).returns(successful_purchase_response) + + response = @gateway.purchase(@amount, @credit_card, @options) + assert_instance_of Response, response + assert_success response + + assert_equal @amount, response.params["amount"] + end + + def test_successful_void @gateway.expects(:ssl_request).returns(successful_purchase_response(true)) From 178d07afdea977b67d1f8e87f262c08baf67d893 Mon Sep 17 00:00:00 2001 From: Kei Kubo Date: Mon, 26 Aug 2013 01:05:07 +0900 Subject: [PATCH 13/17] WebPay: modify refund method to support the new currency unit in JPY. --- lib/active_merchant/billing/gateways/webpay.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/active_merchant/billing/gateways/webpay.rb b/lib/active_merchant/billing/gateways/webpay.rb index fe2a883ec5e..07f8a82f500 100644 --- a/lib/active_merchant/billing/gateways/webpay.rb +++ b/lib/active_merchant/billing/gateways/webpay.rb @@ -21,6 +21,20 @@ def capture(money, credit_card, options = {}) raise NotImplementedError.new end + def refund(money, identification, options = {}) + post = {:amount => localized_amount(money)} + commit_options = generate_meta(options) + + MultiResponse.run do |r| + r.process { commit(:post, "charges/#{CGI.escape(identification)}/refund", post, commit_options) } + + return r unless options[:refund_fee_amount] + + r.process { fetch_application_fees(identification, commit_options) } + r.process { refund_application_fee(options[:refund_fee_amount], application_fee_from_response(r), commit_options) } + end + end + def refund_fee(identification, options, meta) raise NotImplementedError.new end From 4fc2f17ac6f68c9e3a0a7d22a7cc5eef8e7aadad Mon Sep 17 00:00:00 2001 From: Kei Kubo Date: Mon, 26 Aug 2013 01:13:14 +0900 Subject: [PATCH 14/17] WebPay: add tests to modify currency unit in JPY from yen to sen. --- test/remote/gateways/remote_webpay_test.rb | 18 +++++++++++++++++- test/unit/gateways/webpay_test.rb | 6 +++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/test/remote/gateways/remote_webpay_test.rb b/test/remote/gateways/remote_webpay_test.rb index e9b3a78349a..3aa0edf27ea 100644 --- a/test/remote/gateways/remote_webpay_test.rb +++ b/test/remote/gateways/remote_webpay_test.rb @@ -7,6 +7,7 @@ def setup @gateway = WebpayGateway.new(fixtures(:webpay)) @amount = 10000 + @refund_amount = 2000 @credit_card = credit_card('4242424242424242') @declined_card = credit_card('4000') @new_credit_card = credit_card('5105105105105100') @@ -24,6 +25,12 @@ def test_successful_purchase assert response.params["paid"] end + def test_appropriate_purchase_amount + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_success response + assert_equal @amount / 100, response.params["amount"] + end + def test_purchase_description assert response = @gateway.purchase(@amount, @credit_card, { :description => "TheDescription", :email => "email@example.com" }) assert_equal "TheDescription", response.params["description"], "Use the description if it's specified." @@ -59,8 +66,17 @@ def test_successful_refund assert response = @gateway.purchase(@amount, @credit_card, @options) assert_success response assert response.authorization - assert void = @gateway.refund(@amount - 2000, response.authorization) + assert void = @gateway.refund(@refund_amount, response.authorization) + assert_success void + end + + def test_appropriate_refund_amount + assert response = @gateway.purchase(@amount, @credit_card, @options) + assert_success response + assert response.authorization + assert void = @gateway.refund(@refund_amount, response.authorization) assert_success void + assert_equal @refund_amount / 100, void.params["amount_refunded"] end def test_unsuccessful_refund diff --git a/test/unit/gateways/webpay_test.rb b/test/unit/gateways/webpay_test.rb index 7e08ed24e57..1e6d9406c9c 100644 --- a/test/unit/gateways/webpay_test.rb +++ b/test/unit/gateways/webpay_test.rb @@ -5,8 +5,8 @@ def setup @gateway = WebpayGateway.new(:login => 'login') @credit_card = credit_card() - @amount = 400 - @refund_amount = 200 + @amount = 40000 + @refund_amount = 20000 @options = { :billing_address => address(), @@ -33,7 +33,7 @@ def test_appropiate_purchase_amount assert_instance_of Response, response assert_success response - assert_equal @amount, response.params["amount"] + assert_equal @amount / 100, response.params["amount"] end From a01a611f8f73eaf35ac1c31916bf9dc126cd33ea Mon Sep 17 00:00:00 2001 From: Duff OMelia Date: Mon, 26 Aug 2013 15:57:55 -0400 Subject: [PATCH 15/17] SecureNet: Allow some optional fields Now :invoice_description and :invoice_number can be specified in the options for a purchase or an authorization and we map :description to the much larger NOTES field. http://cl.ly/image/2e360n1g1T21/content.png Closes #825. --- CHANGELOG | 1 + .../billing/gateways/secure_net.rb | 5 ++-- test/unit/gateways/secure_net_test.rb | 23 +++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 28f4bdec65e..af36d7cf0f8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -8,6 +8,7 @@ * SecureNet: Allow production transactions [duff] * Stripe: Allow a card_not_present_fee to be specified [melari] * Moneris: Add verification_value support [duff] +* SecureNet: Add INVOICENUM and INVOICEDESC optional fields [duff] == Version 1.36.0 (August 2, 2013) diff --git a/lib/active_merchant/billing/gateways/secure_net.rb b/lib/active_merchant/billing/gateways/secure_net.rb index 4ee4a23a669..fd2aad4e282 100644 --- a/lib/active_merchant/billing/gateways/secure_net.rb +++ b/lib/active_merchant/billing/gateways/secure_net.rb @@ -182,8 +182,9 @@ def add_address(xml, creditcard, options) end def add_invoice(xml, options) - xml.tag! 'INVOICEDESC', options[:description] - xml.tag! 'INVOICENUM', 'inv-8' + xml.tag! 'NOTE', options[:description] if options[:description] + xml.tag! 'INVOICEDESC', options[:invoice_description] if options[:invoice_description] + xml.tag! 'INVOICENUM', options[:invoice_number] if options[:invoice_number] end def add_merchant_key(xml, options) diff --git a/test/unit/gateways/secure_net_test.rb b/test/unit/gateways/secure_net_test.rb index 1a14fc1a4bd..4221f602f19 100644 --- a/test/unit/gateways/secure_net_test.rb +++ b/test/unit/gateways/secure_net_test.rb @@ -1,6 +1,8 @@ require 'test_helper' class SecureNetTest < Test::Unit::TestCase + include CommStub + def setup @gateway = SecureNetGateway.new( :login => 'X', @@ -126,6 +128,27 @@ def test_failure_without_response_reason_text end end + def test_passes_optional_fields + options = { description: "Good Stuff", invoice_description: "Sweet Invoice", invoice_number: "48" } + stub_comms do + @gateway.purchase(@amount, @credit_card, options) + end.check_request do |endpoint, data, headers| + assert_match(%r{NOTE>Good Stuff<}, data) + assert_match(%r{INVOICEDESC>Sweet Invoice<}, data) + assert_match(%r{INVOICENUM>48<}, data) + end.respond_with(successful_purchase_response) + end + + def test_only_passes_optional_fields_if_specified + stub_comms do + @gateway.purchase(@amount, @credit_card, {}) + end.check_request do |endpoint, data, headers| + assert_no_match(%r{NOTE}, data) + assert_no_match(%r{INVOICEDESC}, data) + assert_no_match(%r{INVOICENUM}, data) + end.respond_with(successful_purchase_response) + end + private From f79a3d9bee48d5dcf9df91e5ca62356d0fec73d2 Mon Sep 17 00:00:00 2001 From: Duff OMelia Date: Mon, 26 Aug 2013 16:37:49 -0400 Subject: [PATCH 16/17] Balanced: Adjust BalancedGateway::Error parent It's now ActiveMerchant::ActiveMerchantError rather than StandardError. We want to allow users of ActiveMerchant to generically rescue errors without having to have special gateway specific handling. Closes #826. --- CHANGELOG | 1 + lib/active_merchant/billing/gateways/balanced.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index af36d7cf0f8..acc08d5578f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,6 +9,7 @@ * Stripe: Allow a card_not_present_fee to be specified [melari] * Moneris: Add verification_value support [duff] * SecureNet: Add INVOICENUM and INVOICEDESC optional fields [duff] +* Balanced: Make BalancedGateway::Error inherit from ActiveMerchantError [duff] == Version 1.36.0 (August 2, 2013) diff --git a/lib/active_merchant/billing/gateways/balanced.rb b/lib/active_merchant/billing/gateways/balanced.rb index 5ff114a607d..49ea3019d78 100644 --- a/lib/active_merchant/billing/gateways/balanced.rb +++ b/lib/active_merchant/billing/gateways/balanced.rb @@ -69,7 +69,7 @@ class BalancedGateway < Gateway self.display_name = 'Balanced' self.money_format = :cents - class Error < StandardError + class Error < ActiveMerchant::ActiveMerchantError attr_reader :response def initialize(response, msg=nil) From 068b85243e8e5e8c306dad544f81182fe45a71f4 Mon Sep 17 00:00:00 2001 From: Caleb Simpson Date: Wed, 28 Aug 2013 10:38:33 -0400 Subject: [PATCH 17/17] Allow nokogiri as low as 1.4.7 --- activemerchant.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activemerchant.gemspec b/activemerchant.gemspec index 1bf3b895da7..4ada5eed037 100644 --- a/activemerchant.gemspec +++ b/activemerchant.gemspec @@ -24,7 +24,7 @@ Gem::Specification.new do |s| s.add_dependency('builder', '>= 2.0.0') s.add_dependency('json', '>= 1.5.1') s.add_dependency('active_utils', '>= 1.0.2') - s.add_dependency('nokogiri', ">= 1.5.10") + s.add_dependency('nokogiri', ">= 1.4.7") s.add_development_dependency('rake') s.add_development_dependency('mocha', '~> 0.13.0')