diff --git a/.gitignore b/.gitignore index 29e06144180..ac8b43785f2 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,9 @@ pkg .rvmrc vendor/cache +vendor/bundle .bundle +.rbenv-version # ignore Gemfile.lock because we support multiple versions of Rails and don't want to ship locked version requirements Gemfile.lock diff --git a/CHANGELOG b/CHANGELOG index fb8733f206e..af398321fe9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ = ActiveMerchant CHANGELOG * PayPal gateway: Support for incomplete captures [mbulat] +* Moneris gateway: Add support for vault [kenzie] == Version 1.23.0 (May 23, 2012) diff --git a/lib/active_merchant/billing/gateways/moneris.rb b/lib/active_merchant/billing/gateways/moneris.rb index 8e46a383155..09799f2dce4 100644 --- a/lib/active_merchant/billing/gateways/moneris.rb +++ b/lib/active_merchant/billing/gateways/moneris.rb @@ -31,18 +31,34 @@ def initialize(options = {}) # captured at a later date. # # Pass in +order_id+ and optionally a +customer+ parameter. - def authorize(money, creditcard, options = {}) - debit_commit 'preauth', money, creditcard, options + def authorize(money, creditcard_or_datakey, options = {}) + requires!(options, :order_id) + post = {} + add_payment_source(post, creditcard_or_datakey) + post[:amount] = amount(money) + post[:order_id] = options[:order_id] + post[:cust_id] = options[:customer] + post[:crypt_type] = options[:crypt_type] || @options[:crypt_type] + action = (post[:data_key].blank?) ? 'preauth' : 'res_preauth_cc' + commit(action, post) end - # This action verifies funding on a customer's card, and readies them for + # This action verifies funding on a customer's card and readies them for # deposit in a merchant's account. # # Pass in order_id and optionally a customer parameter - def purchase(money, creditcard, options = {}) - debit_commit 'purchase', money, creditcard, options + def purchase(money, creditcard_or_datakey, options = {}) + requires!(options, :order_id) + post = {} + add_payment_source(post, creditcard_or_datakey) + post[:amount] = amount(money) + post[:order_id] = options[:order_id] + post[:cust_id] = options[:customer] + post[:crypt_type] = options[:crypt_type] || @options[:crypt_type] + action = (post[:data_key].blank?) ? 'purchase' : 'res_purchase_cc' + commit(action, post) end - + # This method retrieves locked funds from a customer's account (from a # PreAuth) and prepares them for deposit in a merchant's account. # @@ -79,30 +95,46 @@ def refund(money, authorization, options = {}) commit 'refund', crediting_params(authorization, :amount => amount(money)) end + def store(credit_card, options = {}) + post = {} + post[:pan] = credit_card.number + post[:expdate] = expdate(credit_card) + post[:crypt_type] = options[:crypt_type] || @options[:crypt_type] + commit('res_add_cc', post) + end + + def unstore(data_key) + post = {} + post[:data_key] = data_key + commit('res_delete', post) + end + + def update(data_key, credit_card, options = {}) + post = {} + post[:pan] = credit_card.number + post[:expdate] = expdate(credit_card) + post[:data_key] = data_key + post[:crypt_type] = options[:crypt_type] || @options[:crypt_type] + commit('res_update_cc', post) + end + def test? @options[:test] || super end + private # :nodoc: all def expdate(creditcard) sprintf("%.4i", creditcard.year)[-2..-1] + sprintf("%.2i", creditcard.month) end - - def debit_commit(commit_type, money, creditcard, options) - requires!(options, :order_id) - commit(commit_type, debit_params(money, creditcard, options)) - end - - # Common params used amongst the +purchase+ and +authorization+ methods - def debit_params(money, creditcard, options = {}) - { - :order_id => options[:order_id], - :cust_id => options[:customer], - :amount => amount(money), - :pan => creditcard.number, - :expdate => expdate(creditcard), - :crypt_type => options[:crypt_type] || @options[:crypt_type] - } + + def add_payment_source(post, source) + if source.is_a?(String) + post[:data_key] = source + else + post[:pan] = source.number + post[:expdate] = expdate(source) + end end # Common params used amongst the +credit+, +void+ and +capture+ methods @@ -205,7 +237,12 @@ def actions "transact" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type], "Batchcloseall" => [], "opentotals" => [:ecr_number], - "batchclose" => [:ecr_number] + "batchclose" => [:ecr_number], + "res_add_cc" => [:pan, :expdate, :crypt_type], + "res_delete" => [:data_key], + "res_update_cc" => [:data_key, :pan, :expdate, :crypt_type], + "res_purchase_cc" => [:data_key, :order_id, :cust_id, :amount, :crypt_type], + "res_preauth_cc" => [:data_key, :order_id, :cust_id, :amount, :crypt_type] } end end diff --git a/test/remote/gateways/remote_moneris_test.rb b/test/remote/gateways/remote_moneris_test.rb index 18a5a5cbcb1..61a24492fa4 100644 --- a/test/remote/gateways/remote_moneris_test.rb +++ b/test/remote/gateways/remote_moneris_test.rb @@ -9,8 +9,7 @@ def setup @credit_card = credit_card('4242424242424242') @options = { :order_id => generate_unique_id, - :billing_address => address, - :description => 'Store Purchase' + :customer => generate_unique_id } end @@ -63,7 +62,7 @@ def test_successful_purchase_and_void def test_failed_purchase_and_void purchase = @gateway.purchase(101, @credit_card, @options) assert_failure purchase - + void = @gateway.void(purchase.authorization) assert_failure void end @@ -81,4 +80,50 @@ def test_failed_purchase_from_error assert_failure response assert_equal 'Declined', response.message end + + def test_successful_store + assert response = @gateway.store(@credit_card) + assert_success response + assert_equal "Successfully registered cc details", response.message + assert response.params["data_key"].present? + @data_key = response.params["data_key"] + end + + def test_successful_unstore + test_successful_store + assert response = @gateway.unstore(@data_key) + assert_success response + assert_equal "Successfully deleted cc details", response.message + assert response.params["data_key"].present? + end + + def test_update + test_successful_store + assert response = @gateway.update(@data_key, @credit_card) + assert_success response + assert_equal "Successfully updated cc details", response.message + assert response.params["data_key"].present? + end + + def test_successful_purchase_with_vault + test_successful_store + assert response = @gateway.purchase(@amount, @data_key, @options) + assert_success response + assert_equal "Approved", response.message + assert_false response.authorization.blank? + end + + def test_successful_authorization_with_vault + test_successful_store + assert response = @gateway.authorize(@amount, @data_key, @options) + assert_success response + assert_false response.authorization.blank? + end + + def test_failed_authorization_with_vault + test_successful_store + response = @gateway.authorize(105, @data_key, @options) + assert_failure response + end + end diff --git a/test/unit/gateways/moneris_test.rb b/test/unit/gateways/moneris_test.rb index c59bd88f63d..438c27cf3ab 100644 --- a/test/unit/gateways/moneris_test.rb +++ b/test/unit/gateways/moneris_test.rb @@ -11,7 +11,7 @@ def setup @amount = 100 @credit_card = credit_card('4242424242424242') - @options = { :order_id => '1', :billing_address => address } + @options = { :order_id => '1', :customer => '1' } end def test_successful_purchase @@ -52,7 +52,6 @@ def test_amount_style end def test_purchase_is_valid_xml - params = { :order_id => "order1", :amount => "1.01", @@ -67,7 +66,6 @@ def test_purchase_is_valid_xml end def test_purchase_is_valid_xml - params = { :order_id => "order1", :amount => "1.01", @@ -82,7 +80,6 @@ def test_purchase_is_valid_xml end def test_capture_is_valid_xml - params = { :order_id => "order1", :amount => "1.01", @@ -109,8 +106,62 @@ def test_should_raise_error_if_transaction_param_empty_on_credit_request assert_raise(ArgumentError) { @gateway.void(invalid_transaction_param) } end end - - private + + + def test_successful_store + @gateway.expects(:ssl_post).returns(successful_store_response) + assert response = @gateway.store(@credit_card) + assert_success response + assert_equal "Successfully registered cc details", response.message + assert response.params["data_key"].present? + @data_key = response.params["data_key"] + end + + def test_successful_unstore + @gateway.expects(:ssl_post).returns(successful_unstore_response) + test_successful_store + assert response = @gateway.unstore(@data_key) + assert_success response + assert_equal "Successfully deleted cc details", response.message + assert response.params["data_key"].present? + end + + def test_update + @gateway.expects(:ssl_post).returns(successful_update_response) + test_successful_store + assert response = @gateway.update(@data_key, @credit_card) + assert_success response + assert_equal "Successfully updated cc details", response.message + assert response.params["data_key"].present? + end + + def test_successful_purchase_with_vault + @gateway.expects(:ssl_post).returns(successful_purchase_response) + test_successful_store + assert response = @gateway.purchase(100, @data_key, {:order_id => generate_unique_id, :customer => generate_unique_id}) + assert_success response + assert_equal "Approved", response.message + assert response.authorization.present? + end + + def test_successful_authorization_with_vault + @gateway.expects(:ssl_post).returns(successful_purchase_response) + test_successful_store + assert response = @gateway.authorize(100, @data_key, {:order_id => generate_unique_id, :customer => generate_unique_id}) + assert_success response + assert_equal "Approved", response.message + assert response.authorization.present? + end + + def test_failed_authorization_with_vault + @gateway.expects(:ssl_post).returns(failed_purchase_response) + test_successful_store + assert response = @gateway.authorize(100, @data_key, @options) + assert_failure response + end + + private + def successful_purchase_response <<-RESPONSE @@ -161,6 +212,50 @@ def failed_purchase_response RESPONSE end + + def successful_store_response + <<-RESPONSE + + + + 1234567890 + 027 + true + Successfully registered cc details * = + + + RESPONSE + end + + def successful_unstore_response + <<-RESPONSE + + + + 1234567890 + 027 + true + Successfully deleted cc details * = + + + RESPONSE + end + + def successful_update_response + <<-RESPONSE + + + + 1234567890 + 027 + true + Successfully updated cc details * = + + + RESPONSE + end + + def xml_purchase_fixture 'store1yesguy1.01424242424242424203037order1' end