diff --git a/app/models/spree/billing_integration/paypal_express_base.rb b/app/models/spree/billing_integration/paypal_express_base.rb index 4a81256..d694f4f 100644 --- a/app/models/spree/billing_integration/paypal_express_base.rb +++ b/app/models/spree/billing_integration/paypal_express_base.rb @@ -16,4 +16,46 @@ class Spree::BillingIntegration::PaypalExpressBase < Spree::BillingIntegration def payment_profiles_supported? !!preferred_review end + + def capture(payment_or_amount, account_or_response_code, gateway_options) + if payment_or_amount.is_a?(Spree::Payment) + authorization = find_authorization(payment_or_amount) + provider.capture(amount_in_cents(payment_or_amount.amount), authorization.params["transaction_id"], :currency => preferred_currency) + else + provider.capture(payment_or_amount, account_or_response_code, :currency => preferred_currency) + end + end + + def credit(amount, account, response_code, gateway_options) + provider.credit(amount, response_code, :currency => preferred_currency) + end + + + def find_authorization(payment) + logs = payment.log_entries.all(:order => 'created_at DESC') + logs.each do |log| + details = YAML.load(log.details) # return the transaction details + if (details.params['payment_status'] == 'Pending' && details.params['pending_reason'] == 'authorization') + return details + end + end + return nil + end + + def find_capture(payment) + #find the transaction associated with the original authorization/capture + logs = payment.log_entries.all(:order => 'created_at DESC') + logs.each do |log| + details = YAML.load(log.details) # return the transaction details + if details.params['payment_status'] == 'Completed' + return details + end + end + return nil + end + + def amount_in_cents(amount) + (100 * amount).to_i + end + end \ No newline at end of file diff --git a/app/models/spree/paypal_account.rb b/app/models/spree/paypal_account.rb index cc09d6a..8980ab1 100644 --- a/app/models/spree/paypal_account.rb +++ b/app/models/spree/paypal_account.rb @@ -6,46 +6,15 @@ class Spree::PaypalAccount < ActiveRecord::Base %w{capture credit} end - def capture(payment) - authorization = find_authorization(payment) - - ppx_response = payment.payment_method.provider.capture(amount_in_cents(payment.amount), authorization.params["transaction_id"], :currency => payment.payment_method.preferred_currency) - if ppx_response.success? - record_log payment, ppx_response - payment.complete - else - gateway_error(ppx_response.message) - end - - end - def can_capture?(payment) !echeck?(payment) && payment.state == "pending" end - def credit(payment, amount=nil) - authorization = find_capture(payment) - - amount = payment.credit_allowed >= payment.order.outstanding_balance.abs ? payment.order.outstanding_balance : payment.credit_allowed - amount=amount.abs if amount - - ppx_response = payment.payment_method.provider.credit(amount.nil? ? amount_in_cents(amount) : amount_in_cents(amount), authorization.params['transaction_id'], :currency => payment.payment_method.preferred_currency) - - if ppx_response.success? - record_log payment, ppx_response - payment.update_attribute(:amount, payment.amount - amount) - payment.complete - payment.order.update! - else - gateway_error(ppx_response.message) - end - end - def can_credit?(payment) return false unless payment.state == "completed" return false unless payment.order.payment_state == "credit_owed" payment.credit_allowed > 0 - !find_capture(payment).nil? + !payment.payment_method.find_capture(payment).nil? end # fix for Payment#payment_profiles_supported? @@ -64,43 +33,4 @@ class Spree::PaypalAccount < ActiveRecord::Base return false end - def record_log(payment, response) - payment.log_entries.create(:details => response.to_yaml) - end - - private - def find_authorization(payment) - logs = payment.log_entries.all(:order => 'created_at DESC') - logs.each do |log| - details = YAML.load(log.details) # return the transaction details - if (details.params['payment_status'] == 'Pending' && details.params['pending_reason'] == 'authorization') - return details - end - end - return nil - end - - def find_capture(payment) - #find the transaction associated with the original authorization/capture - logs = payment.log_entries.all(:order => 'created_at DESC') - logs.each do |log| - details = YAML.load(log.details) # return the transaction details - if details.params['payment_status'] == 'Completed' - return details - end - end - return nil - end - - def gateway_error(text) - msg = "#{I18n.t('gateway_error')} ... #{text}" - logger.error(msg) - raise Spree::Core::GatewayError.new(msg) - end - - private - - def amount_in_cents(amount) - (100 * amount).to_i - end end diff --git a/spec/models/billing_integration/paypal_express_base_spec.rb b/spec/models/billing_integration/paypal_express_base_spec.rb new file mode 100644 index 0000000..85aa463 --- /dev/null +++ b/spec/models/billing_integration/paypal_express_base_spec.rb @@ -0,0 +1,108 @@ +require 'spec_helper' + +describe Spree::BillingIntegration::PaypalExpressBase do + let(:order) do + order = Spree::Order.new(:bill_address => Spree::Address.new, + :ship_address => Spree::Address.new) + end + + let(:gateway) do + gateway = Spree::BillingIntegration::PaypalExpressBase.new({:environment => 'test', :active => true, :preferred_currency => "EUR"}, :without_protection => true) + gateway.stub :source_required => true + gateway.stub :provider => mock('paypal provider') + gateway.stub :find_authorization => mock('authorization', :params => authorization_params) + gateway + end + + let(:authorization_params) { {'transaction_id' => '123'} } + let(:provider) { gateway.provider } + + let(:account) do + mock_model(Spree::PaypalAccount) + end + + let(:payment) do + payment = Spree::Payment.new + payment.source = account + payment.order = order + payment.payment_method = gateway + payment.amount = 10.0 + payment + end + + let(:amount_in_cents) { payment.amount.to_f * 100 } + + let!(:success_response) do + mock('success_response', :success? => true, + :authorization => '123', + :avs_result => { 'code' => 'avs-code' }) + end + + let(:failed_response) { mock('gateway_response', :success? => false) } + + before(:each) do + # So it doesn't create log entries every time a processing method is called + payment.log_entries.stub(:create) + end + + describe "#capture" do + before { payment.state = 'pending' } + + context "when payment_profiles_supported = true" do + before { gateway.stub :payment_profiles_supported? => true } + + context "if sucessful" do + before do + provider.should_receive(:capture).with(amount_in_cents, '123', :currency => 'EUR').and_return(success_response) + end + + it "should store the response_code" do + payment.capture! + payment.response_code.should == '123' + end + end + + context "if unsucessful" do + before do + gateway.should_receive(:capture).with(payment, account, anything).and_return(failed_response) + end + + it "should not make payment complete" do + lambda { payment.capture! }.should raise_error(Spree::Core::GatewayError) + payment.state.should == "failed" + end + end + end + + context "when payment_profiles_supported = false" do + before do + payment.stub :response_code => '123' + gateway.stub :payment_profiles_supported? => false + end + + context "if sucessful" do + before do + provider.should_receive(:capture).with(amount_in_cents, '123', anything).and_return(success_response) + end + + it "should store the response_code" do + payment.capture! + payment.response_code.should == '123' + end + end + + context "if unsucessful" do + before do + provider.should_receive(:capture).with(amount_in_cents, '123', anything).and_return(failed_response) + end + + it "should not make payment complete" do + lambda { payment.capture! }.should raise_error(Spree::Core::GatewayError) + payment.state.should == "failed" + end + end + + end + end + +end