From 09a2633e00ef0f99c6fd9b7b143dc02386c6d8d8 Mon Sep 17 00:00:00 2001 From: andrea longhi Date: Fri, 15 May 2020 11:46:50 +0200 Subject: [PATCH 1/3] Make @response ivar accessible in Spree::Refund model A decorator is added in order to be able to properly access the ivar `@response` that is set when the refund is performed. That variable points to a `ActiveMerchant::Billing::Response` object. --- app/decorators/models/spree/refund_decorator.rb | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 app/decorators/models/spree/refund_decorator.rb diff --git a/app/decorators/models/spree/refund_decorator.rb b/app/decorators/models/spree/refund_decorator.rb new file mode 100644 index 00000000..1351837b --- /dev/null +++ b/app/decorators/models/spree/refund_decorator.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Spree + module RefundDecorator + attr_reader :response + + Spree::Refund.prepend(self) + end +end From b0b1a8d78dae96a8632a27dd176ad409d65d566c Mon Sep 17 00:00:00 2001 From: andrea longhi Date: Fri, 15 May 2020 12:20:39 +0200 Subject: [PATCH 2/3] Add StripeCreditCard#try_void This method is now the preferred way in Solidus for canceling a payment and supersedes the old #cancel method. If a payment for a Payment Intent was captured, it cannot be cancelled anymore, but needs to be refunded. For this refund we're using the default order cancel reason named after `Spree::Payment::Cancellation::DEFAULT_REASON`. --- .../payment_method/stripe_credit_card.rb | 15 +++++++ .../payment_method/stripe_credit_card_spec.rb | 45 ++++++++++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/app/models/spree/payment_method/stripe_credit_card.rb b/app/models/spree/payment_method/stripe_credit_card.rb index eadc346b..b6887131 100644 --- a/app/models/spree/payment_method/stripe_credit_card.rb +++ b/app/models/spree/payment_method/stripe_credit_card.rb @@ -81,6 +81,21 @@ def void(response_code, _creditcard, _transaction_options) gateway.void(response_code, {}) end + def payment_intents_refund_reason + Spree::RefundReason.where(name: Spree::Payment::Cancellation::DEFAULT_REASON).first_or_create + end + + def try_void(payment) + if v3_intents? && payment.completed? + payment.refunds.create!( + amount: payment.credit_allowed, + reason: payment_intents_refund_reason + ).response + else + payment.void_transaction! + end + end + def cancel(response_code) gateway.void(response_code, {}) end diff --git a/spec/models/spree/payment_method/stripe_credit_card_spec.rb b/spec/models/spree/payment_method/stripe_credit_card_spec.rb index 250b9165..95cc4962 100644 --- a/spec/models/spree/payment_method/stripe_credit_card_spec.rb +++ b/spec/models/spree/payment_method/stripe_credit_card_spec.rb @@ -22,7 +22,8 @@ let(:payment) { double('Spree::Payment', source: source, - order: order + order: order, + amount: order.total ) } @@ -241,4 +242,46 @@ expect(gateway).to receive(:capture).with(9855, '12345', anything).and_return(success_response) end end + + describe '#try_void' do + let(:payment) { create :payment, amount: order.total } + + shared_examples 'voids the payment transaction' do + it 'voids the payment transaction' do + expect(payment).to receive(:void_transaction!) + + subject.try_void(payment) + end + end + + context 'when using Payment Intents' do + before { subject.preferred_v3_intents = true } + + context 'when the payment is completed' do + before do + allow(payment).to receive(:completed?) { true } + end + + it 'creates a refund' do + expect { subject.try_void(payment) }.to change { Spree::Refund.count }.by(1) + end + end + + context 'when the payment is not completed' do + it_behaves_like 'voids the payment transaction' + end + end + + context 'when not using Payment Intents' do + before { subject.preferred_v3_intents = false } + + context 'when the payment is completed' do + it_behaves_like 'voids the payment transaction' + end + + context 'when the payment is not completed' do + it_behaves_like 'voids the payment transaction' + end + end + end end From b9f2e7b5fe3e969d056b4ea44d552f50b2d49669 Mon Sep 17 00:00:00 2001 From: andrea longhi Date: Fri, 15 May 2020 15:03:26 +0200 Subject: [PATCH 3/3] Improve Payment Intents test coverage with order cancels Due to Payment Intents customizations, we need to verify that order cancels work properly with both a newly generated Payment Intent payment and with one created from an already saved credit card, when the payment has already been captured. --- spec/features/stripe_checkout_spec.rb | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/spec/features/stripe_checkout_spec.rb b/spec/features/stripe_checkout_spec.rb index a3adaedb..dd1adbf0 100644 --- a/spec/features/stripe_checkout_spec.rb +++ b/spec/features/stripe_checkout_spec.rb @@ -400,8 +400,9 @@ click_button "Place Order" expect(page).to have_content("Your order has been processed successfully") - # Capture in backend Spree::Order.complete.each do |order| + # Capture in backend + visit spree.admin_path expect(page).to have_selector("#listing_orders tbody tr", count: 2) @@ -413,6 +414,21 @@ expect(page).to have_content "Payment Updated" expect(find("table#payments")).to have_content "Completed" + + # Order cancel, after capture + click_link "Cart" + + within "#sidebar" do + expect(page).to have_content "Completed" + end + + find('input[value="Cancel"]').click + + expect(page).to have_content "Order canceled" + + within "#sidebar" do + expect(page).to have_content "Canceled" + end end end end