diff --git a/spec/models/spree/payment_original_spec.rb b/spec/models/spree/payment_original_spec.rb index 70bce9a2b5..418c09882c 100644 --- a/spec/models/spree/payment_original_spec.rb +++ b/spec/models/spree/payment_original_spec.rb @@ -1,554 +1,576 @@ require 'spec_helper' describe Spree::Payment do - let(:order) do - order = Spree::Order.new(:bill_address => Spree::Address.new, - :ship_address => Spree::Address.new) - end - - let(:gateway) do - gateway = Spree::Gateway::Bogus.new(:environment => 'test', :active => true) - gateway.stub :source_required => true - gateway - end - - let(:card) { create(:credit_card) } - - before { allow(card).to receive(:has_payment_profile?).and_return(true) } - - let(:payment) do - payment = create(:payment) - payment.source = card - payment.order = order - payment.payment_method = gateway - payment - end - - let(:amount_in_cents) { payment.amount.to_f * 100 } - - let!(:success_response) do - double('success_response', :success? => true, - :authorization => '123', - :avs_result => { 'code' => 'avs-code' }, - :cvv_result => { 'code' => 'cvv-code', 'message' => "CVV Result"}) - end - - let(:failed_response) { double('gateway_response', :success? => false) } - - before(:each) do - # So it doesn't create log entries every time a processing method is called - allow(payment).to receive(:record_response) - end - - context "extends LocalizedNumber" do - it_behaves_like "a model using the LocalizedNumber module", [:amount] - end - - context 'validations' do - it "returns useful error messages when source is invalid" do - payment.source = Spree::CreditCard.new - expect(payment).not_to be_valid - cc_errors = payment.errors['Credit Card'] - expect(cc_errors).to include("Number can't be blank") - expect(cc_errors).to include("Month is not a number") - expect(cc_errors).to include("Year is not a number") - expect(cc_errors).to include("Verification Value can't be blank") - end - end - - # Regression test for https://github.com/spree/spree/pull/2224 - context 'failure' do - it 'should transition to failed from pending state' do - payment.state = 'pending' - payment.failure - expect(payment.state).to eql('failed') + context 'original specs from Spree' do + let(:order) do + order = Spree::Order.new(:bill_address => Spree::Address.new, + :ship_address => Spree::Address.new) end - it 'should transition to failed from processing state' do - payment.state = 'processing' - payment.failure - expect(payment.state).to eql('failed') + let(:gateway) do + gateway = Spree::Gateway::Bogus.new(:environment => 'test', :active => true) + gateway.stub :source_required => true + gateway end - end + let(:card) { create(:credit_card) } - context 'invalidate' do - it 'should transition from checkout to invalid' do - payment.state = 'checkout' - payment.invalidate - expect(payment.state).to eq('invalid') - end - end + before { allow(card).to receive(:has_payment_profile?).and_return(true) } - context "processing" do - before do - payment.stub(:update_order) - payment.stub(:create_payment_profile) + let(:payment) do + payment = create(:payment) + payment.source = card + payment.order = order + payment.payment_method = gateway + payment end - context "#process!" do - it "should purchase if with auto_capture" do - payment.payment_method.should_receive(:auto_capture?).and_return(true) - payment.should_receive(:purchase!) - payment.process! + let(:amount_in_cents) { payment.amount.to_f * 100 } + + let!(:success_response) do + double('success_response', :success? => true, + :authorization => '123', + :avs_result => { 'code' => 'avs-code' }, + :cvv_result => { 'code' => 'cvv-code', 'message' => "CVV Result"}) + end + + let(:failed_response) { double('gateway_response', :success? => false) } + + before(:each) do + # So it doesn't create log entries every time a processing method is called + allow(payment).to receive(:record_response) + end + + context "extends LocalizedNumber" do + it_behaves_like "a model using the LocalizedNumber module", [:amount] + end + + context 'validations' do + it "returns useful error messages when source is invalid" do + payment.source = Spree::CreditCard.new + expect(payment).not_to be_valid + cc_errors = payment.errors['Credit Card'] + expect(cc_errors).to include("Number can't be blank") + expect(cc_errors).to include("Month is not a number") + expect(cc_errors).to include("Year is not a number") + expect(cc_errors).to include("Verification Value can't be blank") + end + end + + # Regression test for https://github.com/spree/spree/pull/2224 + context 'failure' do + it 'should transition to failed from pending state' do + payment.state = 'pending' + payment.failure + expect(payment.state).to eql('failed') end - it "should authorize without auto_capture" do - payment.payment_method.should_receive(:auto_capture?).and_return(false) - payment.should_receive(:authorize!) - payment.process! + it 'should transition to failed from processing state' do + payment.state = 'processing' + payment.failure + expect(payment.state).to eql('failed') end - it "should make the state 'processing'" do - payment.should_receive(:started_processing!) - payment.process! - end + end - it "should invalidate if payment method doesnt support source" do - payment.payment_method.should_receive(:supports?).with(payment.source).and_return(false) - expect { payment.process!}.to raise_error(Spree::Core::GatewayError) + context 'invalidate' do + it 'should transition from checkout to invalid' do + payment.state = 'checkout' + payment.invalidate expect(payment.state).to eq('invalid') end - end - context "#authorize" do - it "should call authorize on the gateway with the payment amount" do - payment.payment_method.should_receive(:authorize).with(amount_in_cents, - card, - anything).and_return(success_response) - payment.authorize! + context "processing" do + before do + payment.stub(:update_order) + payment.stub(:create_payment_profile) end - it "should call authorize on the gateway with the currency code" do - payment.stub :currency => 'GBP' - payment.payment_method.should_receive(:authorize).with(amount_in_cents, - card, - hash_including({:currency => "GBP"})).and_return(success_response) - payment.authorize! - end - - it "should log the response" do - payment.authorize! - expect(payment).to have_received(:record_response) - end - - context "when gateway does not match the environment" do - it "should raise an exception" do - gateway.stub :environment => "foo" - expect { payment.authorize! }.to raise_error(Spree::Core::GatewayError) + context "#process!" do + it "should purchase if with auto_capture" do + payment.payment_method.should_receive(:auto_capture?).and_return(true) + payment.should_receive(:purchase!) + payment.process! end + + it "should authorize without auto_capture" do + payment.payment_method.should_receive(:auto_capture?).and_return(false) + payment.should_receive(:authorize!) + payment.process! + end + + it "should make the state 'processing'" do + payment.should_receive(:started_processing!) + payment.process! + end + + it "should invalidate if payment method doesnt support source" do + payment.payment_method.should_receive(:supports?).with(payment.source).and_return(false) + expect { payment.process!}.to raise_error(Spree::Core::GatewayError) + expect(payment.state).to eq('invalid') + end + end - context "if successful" do - before do + context "#authorize" do + it "should call authorize on the gateway with the payment amount" do payment.payment_method.should_receive(:authorize).with(amount_in_cents, card, anything).and_return(success_response) - end - - it "should store the response_code, avs_response and cvv_response fields" do - payment.authorize! - expect(payment.response_code).to eq('123') - expect(payment.avs_response).to eq('avs-code') - expect(payment.cvv_response_code).to eq('cvv-code') - expect(payment.cvv_response_message).to eq('CVV Result') - end - - it "should make payment pending" do - payment.should_receive(:pend!) payment.authorize! end - end - context "if unsuccessful" do - it "should mark payment as failed" do - gateway.stub(:authorize).and_return(failed_response) - payment.should_receive(:failure) - payment.should_not_receive(:pend) - expect { - payment.authorize! - }.to raise_error(Spree::Core::GatewayError) - end - end - end - - context "purchase" do - it "should call purchase on the gateway with the payment amount" do - gateway.should_receive(:purchase).with(amount_in_cents, card, anything).and_return(success_response) - payment.purchase! - end - - it "should log the response" do - payment.purchase! - expect(payment).to have_received(:record_response) - end - - context "when gateway does not match the environment" do - it "should raise an exception" do - gateway.stub :environment => "foo" - expect { payment.purchase! }.to raise_error(Spree::Core::GatewayError) - end - end - - context "if successful" do - before do - payment.payment_method.should_receive(:purchase).with(amount_in_cents, - card, - anything).and_return(success_response) + it "should call authorize on the gateway with the currency code" do + payment.stub :currency => 'GBP' + payment.payment_method.should_receive(:authorize).with(amount_in_cents, + card, + hash_including({:currency => "GBP"})).and_return(success_response) + payment.authorize! end - it "should store the response_code and avs_response" do - payment.purchase! - expect(payment.response_code).to eq('123') - expect(payment.avs_response).to eq('avs-code') + it "should log the response" do + payment.authorize! + expect(payment).to have_received(:record_response) end - it "should make payment complete" do - payment.should_receive(:complete!) - payment.purchase! - end - end - - context "if unsuccessful" do - it "should make payment failed" do - gateway.stub(:purchase).and_return(failed_response) - payment.should_receive(:failure) - payment.should_not_receive(:pend) - expect { payment.purchase! }.to raise_error(Spree::Core::GatewayError) - end - end - end - - context "#capture" do - before do - payment.stub(:complete).and_return(true) - end - - context "when payment is pending" do - before do - payment.state = 'pending' + context "when gateway does not match the environment" do + it "should raise an exception" do + gateway.stub :environment => "foo" + expect { payment.authorize! }.to raise_error(Spree::Core::GatewayError) + end end context "if successful" do before do - payment.payment_method.should_receive(:capture).with(payment, card, anything).and_return(success_response) + payment.payment_method.should_receive(:authorize).with(amount_in_cents, + card, + anything).and_return(success_response) end - it "should make payment complete" do - payment.should_receive(:complete) - payment.capture! - end - - it "should store the response_code" do - gateway.stub :capture => success_response - payment.capture! + it "should store the response_code, avs_response and cvv_response fields" do + payment.authorize! expect(payment.response_code).to eq('123') + expect(payment.avs_response).to eq('avs-code') + expect(payment.cvv_response_code).to eq('cvv-code') + expect(payment.cvv_response_message).to eq('CVV Result') + end + + it "should make payment pending" do + payment.should_receive(:pend!) + payment.authorize! end end context "if unsuccessful" do - it "should not make payment complete" do - gateway.stub :capture => failed_response + it "should mark payment as failed" do + gateway.stub(:authorize).and_return(failed_response) payment.should_receive(:failure) - payment.should_not_receive(:complete) - expect { payment.capture! }.to raise_error(Spree::Core::GatewayError) + payment.should_not_receive(:pend) + expect { + payment.authorize! + }.to raise_error(Spree::Core::GatewayError) end end end - # Regression test for #2119 - context "when payment is completed" do - before do - payment.state = 'completed' + context "purchase" do + it "should call purchase on the gateway with the payment amount" do + gateway.should_receive(:purchase).with(amount_in_cents, card, anything).and_return(success_response) + payment.purchase! end - it "should do nothing" do - payment.should_not_receive(:complete) - payment.payment_method.should_not_receive(:capture) - payment.log_entries.should_not_receive(:create) - payment.capture! + it "should log the response" do + payment.purchase! + expect(payment).to have_received(:record_response) + end + + context "when gateway does not match the environment" do + it "should raise an exception" do + gateway.stub :environment => "foo" + expect { payment.purchase! }.to raise_error(Spree::Core::GatewayError) + end + end + + context "if successful" do + before do + payment.payment_method.should_receive(:purchase).with(amount_in_cents, + card, + anything).and_return(success_response) + end + + it "should store the response_code and avs_response" do + payment.purchase! + expect(payment.response_code).to eq('123') + expect(payment.avs_response).to eq('avs-code') + end + + it "should make payment complete" do + payment.should_receive(:complete!) + payment.purchase! + end + end + + context "if unsuccessful" do + it "should make payment failed" do + gateway.stub(:purchase).and_return(failed_response) + payment.should_receive(:failure) + payment.should_not_receive(:pend) + expect { payment.purchase! }.to raise_error(Spree::Core::GatewayError) + end + end + end + + context "#capture" do + before do + payment.stub(:complete).and_return(true) + end + + context "when payment is pending" do + before do + payment.state = 'pending' + end + + context "if successful" do + before do + payment.payment_method.should_receive(:capture).with(payment, card, anything).and_return(success_response) + end + + it "should make payment complete" do + payment.should_receive(:complete) + payment.capture! + end + + it "should store the response_code" do + gateway.stub :capture => success_response + payment.capture! + expect(payment.response_code).to eq('123') + end + end + + context "if unsuccessful" do + it "should not make payment complete" do + gateway.stub :capture => failed_response + payment.should_receive(:failure) + payment.should_not_receive(:complete) + expect { payment.capture! }.to raise_error(Spree::Core::GatewayError) + end + end + end + + # Regression test for #2119 + context "when payment is completed" do + before do + payment.state = 'completed' + end + + it "should do nothing" do + payment.should_not_receive(:complete) + payment.payment_method.should_not_receive(:capture) + payment.log_entries.should_not_receive(:create) + payment.capture! + end + end + end + + context "#void" do + before do + payment.response_code = '123' + payment.state = 'pending' + end + + context "when profiles are supported" do + it "should call payment_gateway.void with the payment's response_code" do + gateway.stub :payment_profiles_supported? => true + gateway.should_receive(:void).with('123', card, anything).and_return(success_response) + payment.void_transaction! + end + end + + context "when profiles are not supported" do + it "should call payment_gateway.void with the payment's response_code" do + gateway.stub :payment_profiles_supported? => false + gateway.should_receive(:void).with('123', anything).and_return(success_response) + payment.void_transaction! + end + end + + it "should log the response" do + payment.void_transaction! + expect(payment).to have_received(:record_response) + end + + context "when gateway does not match the environment" do + it "should raise an exception" do + gateway.stub :environment => "foo" + expect { payment.void_transaction! }.to raise_error(Spree::Core::GatewayError) + end + end + + context "if successful" do + it "should update the response_code with the authorization from the gateway" do + # Change it to something different + payment.response_code = 'abc' + payment.void_transaction! + expect(payment.response_code).to eq('12345') + end + end + + context "if unsuccessful" do + it "should not void the payment" do + gateway.stub :void => failed_response + payment.should_not_receive(:void) + expect { payment.void_transaction! }.to raise_error(Spree::Core::GatewayError) + end + end + + # Regression test for #2119 + context "if payment is already voided" do + before do + payment.state = 'void' + end + + it "should not void the payment" do + payment.payment_method.should_not_receive(:void) + payment.void_transaction! + end + end + end + + context "#credit" do + before do + payment.state = 'complete' + payment.response_code = '123' + end + + context "when outstanding_balance is less than payment amount" do + before do + payment.order.stub :outstanding_balance => 10 + payment.stub :credit_allowed => 1000 + end + + it "should call credit on the gateway with the credit amount and response_code" do + gateway.should_receive(:credit).with(1000, card, '123', anything).and_return(success_response) + payment.credit! + end + end + + context "when outstanding_balance is equal to payment amount" do + before do + payment.order.stub :outstanding_balance => payment.amount + end + + it "should call credit on the gateway with the credit amount and response_code" do + gateway.should_receive(:credit).with(amount_in_cents, card, '123', anything).and_return(success_response) + payment.credit! + end + end + + context "when outstanding_balance is greater than payment amount" do + before do + payment.order.stub :outstanding_balance => 101 + end + + it "should call credit on the gateway with the original payment amount and response_code" do + gateway.should_receive(:credit).with(amount_in_cents.to_f, card, '123', anything).and_return(success_response) + payment.credit! + end + end + + it "should log the response" do + payment.credit! + expect(payment).to have_received(:record_response) + end + + context "when gateway does not match the environment" do + it "should raise an exception" do + gateway.stub :environment => "foo" + expect { payment.credit! }.to raise_error(Spree::Core::GatewayError) + end + end + + context "when response is successful" do + it "should create an offsetting payment" do + Spree::Payment.should_receive(:create) + payment.credit! + end + + it "resulting payment should have correct values" do + payment.order.stub :outstanding_balance => 100 + payment.stub :credit_allowed => 10 + + offsetting_payment = payment.credit! + expect(offsetting_payment.amount.to_f).to eq(-10) + expect(offsetting_payment).to be_completed + expect(offsetting_payment.response_code).to eq('12345') + expect(offsetting_payment.source).to eq(payment) + end end end end - context "#void" do - before do - payment.response_code = '123' - payment.state = 'pending' + context "when response is unsuccessful" do + it "should not create a payment" do + gateway.stub :credit => failed_response + Spree::Payment.should_not_receive(:create) + expect { payment.credit! }.to raise_error(Spree::Core::GatewayError) end + end - context "when profiles are supported" do - it "should call payment_gateway.void with the payment's response_code" do - gateway.stub :payment_profiles_supported? => true - gateway.should_receive(:void).with('123', card, anything).and_return(success_response) - payment.void_transaction! - end + context "when already processing" do + it "should return nil without trying to process the source" do + payment.state = 'processing' + + payment.should_not_receive(:authorize!) + payment.should_not_receive(:purchase!) + expect(payment.process!).to be_nil end + end - context "when profiles are not supported" do - it "should call payment_gateway.void with the payment's response_code" do - gateway.stub :payment_profiles_supported? => false - gateway.should_receive(:void).with('123', anything).and_return(success_response) - payment.void_transaction! - end - end - - it "should log the response" do - payment.void_transaction! - expect(payment).to have_received(:record_response) - end - - context "when gateway does not match the environment" do - it "should raise an exception" do - gateway.stub :environment => "foo" - expect { payment.void_transaction! }.to raise_error(Spree::Core::GatewayError) - end - end - - context "if successful" do - it "should update the response_code with the authorization from the gateway" do - # Change it to something different - payment.response_code = 'abc' - payment.void_transaction! - expect(payment.response_code).to eq('12345') - end - end - - context "if unsuccessful" do - it "should not void the payment" do - gateway.stub :void => failed_response - payment.should_not_receive(:void) - expect { payment.void_transaction! }.to raise_error(Spree::Core::GatewayError) - end - end - - # Regression test for #2119 - context "if payment is already voided" do + context "with source required" do + context "raises an error if no source is specified" do before do - payment.state = 'void' + payment.source = nil end - it "should not void the payment" do - payment.payment_method.should_not_receive(:void) - payment.void_transaction! + specify do + expect { payment.process! }.to raise_error(Spree::Core::GatewayError, Spree.t(:payment_processing_failed)) end end end + context "with source optional" do + context "raises no error if source is not specified" do + before do + payment.source = nil + payment.payment_method.stub(:source_required? => false) + end + + specify do + expect { payment.process! }.not_to raise_error + end + end + end + + context "#credit_allowed" do + it "is the difference between offsets total and payment amount" do + payment.amount = 100 + payment.stub(:offsets_total).and_return(0) + expect(payment.credit_allowed).to eq(100) + payment.stub(:offsets_total).and_return(80) + expect(payment.credit_allowed).to eq(20) + end + end + + context "#can_credit?" do + it "is true if credit_allowed > 0" do + payment.stub(:credit_allowed).and_return(100) + expect(payment.can_credit?).to be true + end + it "is false if credit_allowed is 0" do + payment.stub(:credit_allowed).and_return(0) + expect(payment.can_credit?).to be false + end + end + context "#credit" do - before do - payment.state = 'complete' - payment.response_code = '123' + context "when amount <= credit_allowed" do + it "makes the state processing" do + payment.payment_method.name = 'Gateway' + payment.payment_method.distributors << create(:distributor_enterprise) + payment.payment_method.save! + + payment.order = create(:order) + + payment.state = 'completed' + payment.stub(:credit_allowed).and_return(10) + payment.partial_credit(10) + expect(payment).to be_processing + end + it "calls credit on the source with the payment and amount" do + payment.state = 'completed' + payment.stub(:credit_allowed).and_return(10) + payment.should_receive(:credit!).with(10) + payment.partial_credit(10) + end + end + context "when amount > credit_allowed" do + it "should not call credit on the source" do + payment.state = 'completed' + payment.stub(:credit_allowed).and_return(10) + payment.partial_credit(20) + expect(payment).to be_completed + end + end + end + + context "#save" do + it "should call order#update!" do + gateway.name = 'Gateway' + gateway.distributors << create(:distributor_enterprise) + gateway.save! + + order = create(:order) + payment = Spree::Payment.create(:amount => 100, :order => order, :payment_method => gateway) + order.should_receive(:update!) + payment.save end - context "when outstanding_balance is less than payment amount" do + context "when profiles are supported" do before do - payment.order.stub :outstanding_balance => 10 - payment.stub :credit_allowed => 1000 + gateway.stub :payment_profiles_supported? => true + payment.source.stub :has_payment_profile? => false end - it "should call credit on the gateway with the credit amount and response_code" do - gateway.should_receive(:credit).with(1000, card, '123', anything).and_return(success_response) - payment.credit! - end - end - context "when outstanding_balance is equal to payment amount" do - before do - payment.order.stub :outstanding_balance => payment.amount + context "when there is an error connecting to the gateway" do + it "should call gateway_error " do + pending '[Spree build] Failing spec' + message = double("gateway_error") + connection_error = ActiveMerchant::ConnectionError.new(message, nil) + expect(gateway).to receive(:create_profile).and_raise(connection_error) + expect do + Spree::Payment.create( + :amount => 100, + :order => order, + :source => card, + :payment_method => gateway + ) + end.should raise_error(Spree::Core::GatewayError) + end end - it "should call credit on the gateway with the credit amount and response_code" do - gateway.should_receive(:credit).with(amount_in_cents, card, '123', anything).and_return(success_response) - payment.credit! - end - end + context "when successfully connecting to the gateway" do + xit "should create a payment profile" do + gateway.name = 'Gateway' + gateway.distributors << create(:distributor_enterprise) + gateway.save! - context "when outstanding_balance is greater than payment amount" do - before do - payment.order.stub :outstanding_balance => 101 - end + payment.payment_method = gateway - it "should call credit on the gateway with the original payment amount and response_code" do - gateway.should_receive(:credit).with(amount_in_cents.to_f, card, '123', anything).and_return(success_response) - payment.credit! - end - end + expect(gateway).to receive(:create_profile) - it "should log the response" do - payment.credit! - expect(payment).to have_received(:record_response) - end - - context "when gateway does not match the environment" do - it "should raise an exception" do - gateway.stub :environment => "foo" - expect { payment.credit! }.to raise_error(Spree::Core::GatewayError) - end - end - - context "when response is successful" do - it "should create an offsetting payment" do - Spree::Payment.should_receive(:create) - payment.credit! - end - - it "resulting payment should have correct values" do - payment.order.stub :outstanding_balance => 100 - payment.stub :credit_allowed => 10 - - offsetting_payment = payment.credit! - expect(offsetting_payment.amount.to_f).to eq(-10) - expect(offsetting_payment).to be_completed - expect(offsetting_payment.response_code).to eq('12345') - expect(offsetting_payment.source).to eq(payment) - end - end - end - end - - context "when response is unsuccessful" do - it "should not create a payment" do - gateway.stub :credit => failed_response - Spree::Payment.should_not_receive(:create) - expect { payment.credit! }.to raise_error(Spree::Core::GatewayError) - end - end - - context "when already processing" do - it "should return nil without trying to process the source" do - payment.state = 'processing' - - payment.should_not_receive(:authorize!) - payment.should_not_receive(:purchase!) - expect(payment.process!).to be_nil - end - end - - context "with source required" do - context "raises an error if no source is specified" do - before do - payment.source = nil - end - - specify do - expect { payment.process! }.to raise_error(Spree::Core::GatewayError, Spree.t(:payment_processing_failed)) - end - end - end - - context "with source optional" do - context "raises no error if source is not specified" do - before do - payment.source = nil - payment.payment_method.stub(:source_required? => false) - end - - specify do - expect { payment.process! }.not_to raise_error - end - end - end - - context "#credit_allowed" do - it "is the difference between offsets total and payment amount" do - payment.amount = 100 - payment.stub(:offsets_total).and_return(0) - expect(payment.credit_allowed).to eq(100) - payment.stub(:offsets_total).and_return(80) - expect(payment.credit_allowed).to eq(20) - end - end - - context "#can_credit?" do - it "is true if credit_allowed > 0" do - payment.stub(:credit_allowed).and_return(100) - expect(payment.can_credit?).to be true - end - it "is false if credit_allowed is 0" do - payment.stub(:credit_allowed).and_return(0) - expect(payment.can_credit?).to be false - end - end - - context "#credit" do - context "when amount <= credit_allowed" do - it "makes the state processing" do - payment.payment_method.name = 'Gateway' - payment.payment_method.distributors << create(:distributor_enterprise) - payment.payment_method.save! - - payment.order = create(:order) - - payment.state = 'completed' - payment.stub(:credit_allowed).and_return(10) - payment.partial_credit(10) - expect(payment).to be_processing - end - it "calls credit on the source with the payment and amount" do - payment.state = 'completed' - payment.stub(:credit_allowed).and_return(10) - payment.should_receive(:credit!).with(10) - payment.partial_credit(10) - end - end - context "when amount > credit_allowed" do - it "should not call credit on the source" do - payment.state = 'completed' - payment.stub(:credit_allowed).and_return(10) - payment.partial_credit(20) - expect(payment).to be_completed - end - end - end - - context "#save" do - it "should call order#update!" do - gateway.name = 'Gateway' - gateway.distributors << create(:distributor_enterprise) - gateway.save! - - order = create(:order) - payment = Spree::Payment.create(:amount => 100, :order => order, :payment_method => gateway) - order.should_receive(:update!) - payment.save - end - - context "when profiles are supported" do - before do - gateway.stub :payment_profiles_supported? => true - payment.source.stub :has_payment_profile? => false - end - - - context "when there is an error connecting to the gateway" do - it "should call gateway_error " do - pending '[Spree build] Failing spec' - message = double("gateway_error") - connection_error = ActiveMerchant::ConnectionError.new(message, nil) - expect(gateway).to receive(:create_profile).and_raise(connection_error) - expect do - Spree::Payment.create( + payment = Spree::Payment.create( :amount => 100, - :order => order, + :order => create(:order), :source => card, :payment_method => gateway ) - end.should raise_error(Spree::Core::GatewayError) + end end + + end - context "when successfully connecting to the gateway" do - xit "should create a payment profile" do + context "when profiles are not supported" do + before { gateway.stub :payment_profiles_supported? => false } + + it "should not create a payment profile" do gateway.name = 'Gateway' gateway.distributors << create(:distributor_enterprise) gateway.save! - payment.payment_method = gateway - - expect(gateway).to receive(:create_profile) - + gateway.should_not_receive :create_profile payment = Spree::Payment.create( :amount => 100, :order => create(:order), @@ -557,263 +579,243 @@ describe Spree::Payment do ) end end - - end - context "when profiles are not supported" do - before { gateway.stub :payment_profiles_supported? => false } + context "#build_source" do + it "should build the payment's source" do + params = { :amount => 100, :payment_method => gateway, + :source_attributes => { + :expiry =>"1 / 99", + :number => '1234567890123', + :verification_value => '123' + } + } - it "should not create a payment profile" do - gateway.name = 'Gateway' - gateway.distributors << create(:distributor_enterprise) - gateway.save! + payment = Spree::Payment.new(params) + expect(payment).to be_valid + expect(payment.source).not_to be_nil + end - gateway.should_not_receive :create_profile - payment = Spree::Payment.create( - :amount => 100, - :order => create(:order), - :source => card, - :payment_method => gateway - ) + it "errors when payment source not valid" do + params = { :amount => 100, :payment_method => gateway, + :source_attributes => {:expiry => "1 / 12" }} + + payment = Spree::Payment.new(params) + expect(payment).not_to be_valid + expect(payment.source).not_to be_nil + expect(payment.source.errors[:number]).not_to be_empty + expect(payment.source.errors[:verification_value]).not_to be_empty end end - end - context "#build_source" do - it "should build the payment's source" do - params = { :amount => 100, :payment_method => gateway, - :source_attributes => { - :expiry =>"1 / 99", - :number => '1234567890123', - :verification_value => '123' - } - } - - payment = Spree::Payment.new(params) - expect(payment).to be_valid - expect(payment.source).not_to be_nil + context "#currency" do + before { order.stub(:currency) { "ABC" } } + it "returns the order currency" do + expect(payment.currency).to eq("ABC") + end end - it "errors when payment source not valid" do - params = { :amount => 100, :payment_method => gateway, - :source_attributes => {:expiry => "1 / 12" }} - - payment = Spree::Payment.new(params) - expect(payment).not_to be_valid - expect(payment.source).not_to be_nil - expect(payment.source.errors[:number]).not_to be_empty - expect(payment.source.errors[:verification_value]).not_to be_empty - end - end - - context "#currency" do - before { order.stub(:currency) { "ABC" } } - it "returns the order currency" do - expect(payment.currency).to eq("ABC") - end - end - - context "#display_amount" do - it "returns a Spree::Money for this amount" do - expect(payment.display_amount).to eq(Spree::Money.new(payment.amount)) - end - end - - # Regression test for #2216 - context "#gateway_options" do - before { order.stub(:last_ip_address => "192.168.1.1") } - - it "contains an IP" do - expect(payment.gateway_options[:ip]).to eq(order.last_ip_address) - end - end - - context "#set_unique_identifier" do - # Regression test for #1998 - it "sets a unique identifier on create" do - payment.run_callbacks(:save) - expect(payment.identifier).not_to be_blank - expect(payment.identifier.size).to eq(8) - expect(payment.identifier).to be_a(String) + context "#display_amount" do + it "returns a Spree::Money for this amount" do + expect(payment.display_amount).to eq(Spree::Money.new(payment.amount)) + end end - context "other payment exists" do - let(:other_payment) { - gateway.name = 'Gateway' - gateway.distributors << create(:distributor_enterprise) - gateway.save! + # Regression test for #2216 + context "#gateway_options" do + before { order.stub(:last_ip_address => "192.168.1.1") } - payment = Spree::Payment.new - payment.source = card - payment.order = create(:order) - payment.payment_method = gateway - payment - } - - before { other_payment.save! } - - it "doesn't set duplicate identifier" do - payment.should_receive(:generate_identifier).and_return(other_payment.identifier) - payment.should_receive(:generate_identifier).and_call_original + it "contains an IP" do + expect(payment.gateway_options[:ip]).to eq(order.last_ip_address) + end + end + context "#set_unique_identifier" do + # Regression test for #1998 + it "sets a unique identifier on create" do payment.run_callbacks(:save) - expect(payment.identifier).not_to be_blank - expect(payment.identifier).not_to eq(other_payment.identifier) + expect(payment.identifier.size).to eq(8) + expect(payment.identifier).to be_a(String) + end + + context "other payment exists" do + let(:other_payment) { + gateway.name = 'Gateway' + gateway.distributors << create(:distributor_enterprise) + gateway.save! + + payment = Spree::Payment.new + payment.source = card + payment.order = create(:order) + payment.payment_method = gateway + payment + } + + before { other_payment.save! } + + it "doesn't set duplicate identifier" do + payment.should_receive(:generate_identifier).and_return(other_payment.identifier) + payment.should_receive(:generate_identifier).and_call_original + + payment.run_callbacks(:save) + + expect(payment.identifier).not_to be_blank + expect(payment.identifier).not_to eq(other_payment.identifier) + end end end - end - describe "available actions" do - context "for most gateways" do - let(:payment) { create(:payment, source: create(:credit_card)) } + describe "available actions" do + context "for most gateways" do + let(:payment) { create(:payment, source: create(:credit_card)) } - it "can capture and void" do - expect(payment.actions).to match_array %w(capture void) + it "can capture and void" do + expect(payment.actions).to match_array %w(capture void) + end + + describe "when a payment has been taken" do + before do + allow(payment).to receive(:state) { 'completed' } + allow(payment).to receive(:order) { double(:order, payment_state: 'credit_owed') } + end + + it "can void and credit" do + expect(payment.actions).to match_array %w(void credit) + end + end end - describe "when a payment has been taken" do + context "for Pin Payments" do + let(:d) { create(:distributor_enterprise) } + let(:pin) { Spree::Gateway::Pin.create! name: 'pin', distributor_ids: [d.id] } + let(:payment) { create(:payment, source: create(:credit_card), payment_method: pin) } + + it "does not void" do + expect(payment.actions).not_to include 'void' + end + + describe "when a payment has been taken" do + before do + allow(payment).to receive(:state) { 'completed' } + allow(payment).to receive(:order) { double(:order, payment_state: 'credit_owed') } + end + + it "can refund instead of crediting" do + expect(payment.actions).not_to include 'credit' + expect(payment.actions).to include 'refund' + end + end + end + end + + describe "refunding" do + let(:payment) { create(:payment) } + let(:success) { double(success?: true, authorization: 'abc123') } + let(:failure) { double(success?: false) } + + it "always checks the environment" do + allow(payment.payment_method).to receive(:refund) { success } + expect(payment).to receive(:check_environment) + payment.refund! + end + + describe "calculating refund amount" do + it "returns the parameter amount when given" do + expect(payment.send(:calculate_refund_amount, 123)).to be === 123.0 + end + + it "refunds up to the value of the payment when the outstanding balance is larger" do + allow(payment).to receive(:credit_allowed) { 123 } + allow(payment).to receive(:order) { double(:order, outstanding_balance: 1000) } + expect(payment.send(:calculate_refund_amount)).to eq(123) + end + + it "refunds up to the outstanding balance of the order when the payment is larger" do + allow(payment).to receive(:credit_allowed) { 1000 } + allow(payment).to receive(:order) { double(:order, outstanding_balance: 123) } + expect(payment.send(:calculate_refund_amount)).to eq(123) + end + end + + describe "performing refunds" do before do - allow(payment).to receive(:state) { 'completed' } - allow(payment).to receive(:order) { double(:order, payment_state: 'credit_owed') } + allow(payment).to receive(:calculate_refund_amount) { 123 } + expect(payment.payment_method).to receive(:refund).and_return(success) end - it "can void and credit" do - expect(payment.actions).to match_array %w(void credit) - end - end - end - - context "for Pin Payments" do - let(:d) { create(:distributor_enterprise) } - let(:pin) { Spree::Gateway::Pin.create! name: 'pin', distributor_ids: [d.id] } - let(:payment) { create(:payment, source: create(:credit_card), payment_method: pin) } - - it "does not void" do - expect(payment.actions).not_to include 'void' - end - - describe "when a payment has been taken" do - before do - allow(payment).to receive(:state) { 'completed' } - allow(payment).to receive(:order) { double(:order, payment_state: 'credit_owed') } + it "performs the refund without payment profiles" do + allow(payment.payment_method).to receive(:payment_profiles_supported?) { false } + payment.refund! end - it "can refund instead of crediting" do - expect(payment.actions).not_to include 'credit' - expect(payment.actions).to include 'refund' + it "performs the refund with payment profiles" do + allow(payment.payment_method).to receive(:payment_profiles_supported?) { true } + payment.refund! end end - end - end - describe "refunding" do - let(:payment) { create(:payment) } - let(:success) { double(success?: true, authorization: 'abc123') } - let(:failure) { double(success?: false) } - - it "always checks the environment" do - allow(payment.payment_method).to receive(:refund) { success } - expect(payment).to receive(:check_environment) - payment.refund! - end - - describe "calculating refund amount" do - it "returns the parameter amount when given" do - expect(payment.send(:calculate_refund_amount, 123)).to be === 123.0 - end - - it "refunds up to the value of the payment when the outstanding balance is larger" do - allow(payment).to receive(:credit_allowed) { 123 } - allow(payment).to receive(:order) { double(:order, outstanding_balance: 1000) } - expect(payment.send(:calculate_refund_amount)).to eq(123) - end - - it "refunds up to the outstanding balance of the order when the payment is larger" do - allow(payment).to receive(:credit_allowed) { 1000 } - allow(payment).to receive(:order) { double(:order, outstanding_balance: 123) } - expect(payment.send(:calculate_refund_amount)).to eq(123) - end - end - - describe "performing refunds" do - before do + it "records the response" do allow(payment).to receive(:calculate_refund_amount) { 123 } - expect(payment.payment_method).to receive(:refund).and_return(success) - end - - it "performs the refund without payment profiles" do - allow(payment.payment_method).to receive(:payment_profiles_supported?) { false } + allow(payment.payment_method).to receive(:refund).and_return(success) + expect(payment).to receive(:record_response).with(success) payment.refund! end - it "performs the refund with payment profiles" do - allow(payment.payment_method).to receive(:payment_profiles_supported?) { true } + it "records a payment on success" do + allow(payment).to receive(:calculate_refund_amount) { 123 } + allow(payment.payment_method).to receive(:refund).and_return(success) + allow(payment).to receive(:record_response) + + expect do + payment.refund! + end.to change(Spree::Payment, :count).by(1) + + p = Spree::Payment.last + expect(p.order).to eq(payment.order) + expect(p.source).to eq(payment) + expect(p.payment_method).to eq(payment.payment_method) + expect(p.amount).to eq(-123) + expect(p.response_code).to eq(success.authorization) + expect(p.state).to eq('completed') + end + + it "logs the error on failure" do + allow(payment).to receive(:calculate_refund_amount) { 123 } + allow(payment.payment_method).to receive(:refund).and_return(failure) + allow(payment).to receive(:record_response) + expect(payment).to receive(:gateway_error).with(failure) payment.refund! end end - it "records the response" do - allow(payment).to receive(:calculate_refund_amount) { 123 } - allow(payment.payment_method).to receive(:refund).and_return(success) - expect(payment).to receive(:record_response).with(success) - payment.refund! - end + describe "applying transaction fees" do + let!(:order) { create(:order) } + let!(:line_item) { create(:line_item, order: order, quantity: 3, price: 5.00) } - it "records a payment on success" do - allow(payment).to receive(:calculate_refund_amount) { 123 } - allow(payment.payment_method).to receive(:refund).and_return(success) - allow(payment).to receive(:record_response) - - expect do - payment.refund! - end.to change(Spree::Payment, :count).by(1) - - p = Spree::Payment.last - expect(p.order).to eq(payment.order) - expect(p.source).to eq(payment) - expect(p.payment_method).to eq(payment.payment_method) - expect(p.amount).to eq(-123) - expect(p.response_code).to eq(success.authorization) - expect(p.state).to eq('completed') - end - - it "logs the error on failure" do - allow(payment).to receive(:calculate_refund_amount) { 123 } - allow(payment.payment_method).to receive(:refund).and_return(failure) - allow(payment).to receive(:record_response) - expect(payment).to receive(:gateway_error).with(failure) - payment.refund! - end - end - - describe "applying transaction fees" do - let!(:order) { create(:order) } - let!(:line_item) { create(:line_item, order: order, quantity: 3, price: 5.00) } - - before do - order.reload.update! - end - - context "when order-based calculator" do - let!(:shop) { create(:enterprise) } - let!(:payment_method) { create(:payment_method, calculator: calculator) } - - let!(:calculator) do - Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10) + before do + order.reload.update! end - context "when order complete and inventory tracking enabled" do - let!(:order) { create(:completed_order_with_totals, distributor: shop) } - let!(:variant) { order.line_items.first.variant } - let!(:inventory_item) { create(:inventory_item, enterprise: shop, variant: variant) } + context "when order-based calculator" do + let!(:shop) { create(:enterprise) } + let!(:payment_method) { create(:payment_method, calculator: calculator) } - it "creates adjustment" do - payment = create(:payment, order: order, payment_method: payment_method, - amount: order.total) - expect(payment.adjustment).to be_present - expect(payment.adjustment.amount).not_to eq(0) + let!(:calculator) do + Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10) + end + + context "when order complete and inventory tracking enabled" do + let!(:order) { create(:completed_order_with_totals, distributor: shop) } + let!(:variant) { order.line_items.first.variant } + let!(:inventory_item) { create(:inventory_item, enterprise: shop, variant: variant) } + + it "creates adjustment" do + payment = create(:payment, order: order, payment_method: payment_method, + amount: order.total) + expect(payment.adjustment).to be_present + expect(payment.adjustment.amount).not_to eq(0) + end end end end