diff --git a/app/models/spree/payment/processing.rb b/app/models/spree/payment/processing.rb index 98e2cf4c1e..ad7eb87aca 100644 --- a/app/models/spree/payment/processing.rb +++ b/app/models/spree/payment/processing.rb @@ -107,6 +107,33 @@ module Spree end end + def refund!(refund_amount = nil) + protect_from_connection_error do + check_environment + + refund_amount = calculate_refund_amount(refund_amount) + + if payment_method.payment_profiles_supported? + response = payment_method.refund((refund_amount * 100).round, source, response_code, gateway_options) + else + response = payment_method.refund((refund_amount * 100).round, response_code, gateway_options) + end + + record_response(response) + + if response.success? + self.class.create(order: order, + source: self, + payment_method: payment_method, + amount: refund_amount.abs * -1, + response_code: response.authorization, + state: 'completed') + else + gateway_error(response) + end + end + end + def partial_credit(amount) return if amount > credit_allowed started_processing! @@ -137,6 +164,11 @@ module Spree private + def calculate_refund_amount(refund_amount = nil) + refund_amount ||= credit_allowed >= order.outstanding_balance.abs ? order.outstanding_balance.abs : credit_allowed.abs + refund_amount.to_f + end + def gateway_action(source, action, success_state) protect_from_connection_error do check_environment diff --git a/app/models/spree/payment_decorator.rb b/app/models/spree/payment_decorator.rb index c333fc531d..065b5ca99d 100644 --- a/app/models/spree/payment_decorator.rb +++ b/app/models/spree/payment_decorator.rb @@ -33,40 +33,8 @@ module Spree I18n.t('payment_method_fee') end - def refund!(refund_amount = nil) - protect_from_connection_error do - check_environment - - refund_amount = calculate_refund_amount(refund_amount) - - if payment_method.payment_profiles_supported? - response = payment_method.refund((refund_amount * 100).round, source, response_code, gateway_options) - else - response = payment_method.refund((refund_amount * 100).round, response_code, gateway_options) - end - - record_response(response) - - if response.success? - self.class.create(order: order, - source: self, - payment_method: payment_method, - amount: refund_amount.abs * -1, - response_code: response.authorization, - state: 'completed') - else - gateway_error(response) - end - end - end - private - def calculate_refund_amount(refund_amount = nil) - refund_amount ||= credit_allowed >= order.outstanding_balance.abs ? order.outstanding_balance.abs : credit_allowed.abs - refund_amount.to_f - end - def create_payment_profile return unless source.is_a?(CreditCard) return unless source.try(:save_requested_by_customer?) diff --git a/spec/models/spree/payment_original_spec.rb b/spec/models/spree/payment_original_spec.rb index 80b9379eab..a29d8ccee6 100644 --- a/spec/models/spree/payment_original_spec.rb +++ b/spec/models/spree/payment_original_spec.rb @@ -703,4 +703,84 @@ describe Spree::Payment do 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(: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 } + payment.refund! + end + + it "performs the refund with payment profiles" do + allow(payment.payment_method).to receive(:payment_profiles_supported?) { true } + 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 + + 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 end diff --git a/spec/models/spree/payment_spec.rb b/spec/models/spree/payment_spec.rb index ecd5d7b3b6..55586d368b 100644 --- a/spec/models/spree/payment_spec.rb +++ b/spec/models/spree/payment_spec.rb @@ -3,86 +3,6 @@ require 'spec_helper' module Spree describe Payment do - 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(: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 } - payment.refund! - end - - it "performs the refund with payment profiles" do - allow(payment.payment_method).to receive(:payment_profiles_supported?) { true } - 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 - - 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(Payment, :count).by(1) - - p = 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) }