mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-27 01:43:22 +00:00
Merge pull request #5446 from luisramos0/sca_refunds
Make StripeSCA void action work
This commit is contained in:
@@ -57,8 +57,11 @@ module Spree
|
||||
|
||||
# NOTE: the name of this method is determined by Spree::Payment::Processing
|
||||
def void(response_code, _creditcard, gateway_options)
|
||||
payment_intent_id = response_code
|
||||
payment_intent_response = Stripe::PaymentIntent.retrieve(payment_intent_id,
|
||||
stripe_account: stripe_account_id)
|
||||
gateway_options[:stripe_account] = stripe_account_id
|
||||
provider.void(response_code, gateway_options)
|
||||
provider.refund(payment_intent_response.amount_received, response_code, gateway_options)
|
||||
end
|
||||
|
||||
# NOTE: the name of this method is determined by Spree::Payment::Processing
|
||||
|
||||
@@ -36,7 +36,6 @@ module OpenFoodNetwork
|
||||
end
|
||||
|
||||
# Using the `in_stock?` method allows overrides by distributors.
|
||||
# It also allows the upgrade to Spree 2.0.
|
||||
def filter_on_hand(variants)
|
||||
if params[:report_type] == 'inventory'
|
||||
variants.select(&:in_stock?)
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Spree::Admin::PaymentsController, type: :controller do
|
||||
let!(:shop) { create(:enterprise) }
|
||||
let!(:user) { shop.owner }
|
||||
let!(:order) { create(:order, distributor: shop, state: 'complete') }
|
||||
let!(:line_item) { create(:line_item, order: order, price: 5.0) }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:spree_current_user) { user }
|
||||
order.reload.update_totals
|
||||
end
|
||||
|
||||
context "Stripe Connect" do
|
||||
context "requesting a refund on a payment" do
|
||||
let(:params) { { id: payment.id, order_id: order.number, e: :void } }
|
||||
|
||||
# Required for the respond override in the controller decorator to work
|
||||
before { @request.env['HTTP_REFERER'] = spree.admin_order_payments_url(payment) }
|
||||
|
||||
context "that was processed by stripe" do
|
||||
let!(:payment_method) { create(:stripe_payment_method, distributors: [shop]) }
|
||||
let!(:payment) do
|
||||
create(:payment, order: order, state: 'completed', payment_method: payment_method,
|
||||
response_code: 'ch_1a2b3c', amount: order.total)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(Stripe).to receive(:api_key) { "sk_test_12345" }
|
||||
end
|
||||
|
||||
context "where the request succeeds" do
|
||||
before do
|
||||
stub_request(:post, "https://api.stripe.com/v1/charges/ch_1a2b3c/refunds").
|
||||
with(basic_auth: ["sk_test_12345", ""]).
|
||||
to_return(status: 200,
|
||||
body: JSON.generate(id: 're_123', object: 'refund', status: 'succeeded') )
|
||||
end
|
||||
|
||||
it "voids the payment" do
|
||||
order.reload
|
||||
expect(order.payment_total).to_not eq 0
|
||||
expect(order.outstanding_balance).to eq 0
|
||||
spree_put :fire, params
|
||||
expect(payment.reload.state).to eq 'void'
|
||||
order.reload
|
||||
expect(order.payment_total).to eq 0
|
||||
expect(order.outstanding_balance).to_not eq 0
|
||||
end
|
||||
end
|
||||
|
||||
context "where the request fails" do
|
||||
before do
|
||||
stub_request(:post, "https://api.stripe.com/v1/charges/ch_1a2b3c/refunds").
|
||||
with(basic_auth: ["sk_test_12345", ""]).
|
||||
to_return(status: 200, body: JSON.generate(error: { message: "Bup-bow!" }) )
|
||||
end
|
||||
|
||||
it "does not void the payment" do
|
||||
order.reload
|
||||
expect(order.payment_total).to_not eq 0
|
||||
expect(order.outstanding_balance).to eq 0
|
||||
spree_put :fire, params
|
||||
expect(payment.reload.state).to eq 'completed'
|
||||
order.reload
|
||||
expect(order.payment_total).to_not eq 0
|
||||
expect(order.outstanding_balance).to eq 0
|
||||
expect(flash[:error]).to eq "Bup-bow!"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "requesting a partial credit on a payment" do
|
||||
let(:params) { { id: payment.id, order_id: order.number, e: :credit } }
|
||||
|
||||
# Required for the respond override in the controller decorator to work
|
||||
before { @request.env['HTTP_REFERER'] = spree.admin_order_payments_url(payment) }
|
||||
|
||||
context "that was processed by stripe" do
|
||||
let!(:payment_method) { create(:stripe_payment_method, distributors: [shop]) }
|
||||
let!(:payment) do
|
||||
create(:payment, order: order, state: 'completed', payment_method: payment_method,
|
||||
response_code: 'ch_1a2b3c', amount: order.total + 5)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(Stripe).to receive(:api_key) { "sk_test_12345" }
|
||||
end
|
||||
|
||||
context "where the request succeeds" do
|
||||
before do
|
||||
stub_request(:post, "https://api.stripe.com/v1/charges/ch_1a2b3c/refunds").
|
||||
with(basic_auth: ["sk_test_12345", ""]).
|
||||
to_return(status: 200,
|
||||
body: JSON.generate(id: 're_123', object: 'refund', status: 'succeeded') )
|
||||
end
|
||||
|
||||
it "partially refunds the payment" do
|
||||
order.reload
|
||||
expect(order.payment_total).to eq order.total + 5
|
||||
expect(order.outstanding_balance).to eq(-5)
|
||||
spree_put :fire, params
|
||||
expect(payment.reload.state).to eq 'completed'
|
||||
order.reload
|
||||
expect(order.payment_total).to eq order.total
|
||||
expect(order.outstanding_balance).to eq 0
|
||||
end
|
||||
end
|
||||
|
||||
context "where the request fails" do
|
||||
before do
|
||||
stub_request(:post, "https://api.stripe.com/v1/charges/ch_1a2b3c/refunds").
|
||||
with(basic_auth: ["sk_test_12345", ""]).
|
||||
to_return(status: 200, body: JSON.generate(error: { message: "Bup-bow!" }) )
|
||||
end
|
||||
|
||||
it "does not void the payment" do
|
||||
order.reload
|
||||
expect(order.payment_total).to eq order.total + 5
|
||||
expect(order.outstanding_balance).to eq(-5)
|
||||
spree_put :fire, params
|
||||
expect(payment.reload.state).to eq 'completed'
|
||||
order.reload
|
||||
expect(order.payment_total).to eq order.total + 5
|
||||
expect(order.outstanding_balance).to eq(-5)
|
||||
expect(flash[:error]).to eq "Bup-bow!"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "StripeSCA" do
|
||||
context "requesting a refund on a payment" do
|
||||
let(:params) { { id: payment.id, order_id: order.number, e: :void } }
|
||||
|
||||
# Required for the respond override in the controller decorator to work
|
||||
before { @request.env['HTTP_REFERER'] = spree.admin_order_payments_url(payment) }
|
||||
|
||||
context "that was processed by stripe" do
|
||||
let!(:payment_method) { create(:stripe_sca_payment_method, distributors: [shop]) }
|
||||
let!(:payment) do
|
||||
create(:payment, order: order, state: 'completed', payment_method: payment_method,
|
||||
response_code: 'pi_123', amount: order.total)
|
||||
end
|
||||
let(:stripe_account) { create(:stripe_account, enterprise: shop) }
|
||||
|
||||
before do
|
||||
allow(Stripe).to receive(:api_key) { "sk_test_12345" }
|
||||
allow(StripeAccount).to receive(:find_by) { stripe_account }
|
||||
|
||||
# Retrieves payment intent info
|
||||
stub_request(:get, "https://api.stripe.com/v1/payment_intents/pi_123")
|
||||
.with(headers: { 'Stripe-Account' => 'abc123' })
|
||||
.to_return({ status: 200, body: JSON.generate(
|
||||
amount_received: 2000,
|
||||
charges: { data: [{ id: "ch_1a2b3c" }] }
|
||||
) })
|
||||
end
|
||||
|
||||
context "where the request succeeds" do
|
||||
before do
|
||||
# Issues the refund
|
||||
stub_request(:post, "https://api.stripe.com/v1/charges/ch_1a2b3c/refunds").
|
||||
with(basic_auth: ["sk_test_12345", ""]).
|
||||
to_return(status: 200,
|
||||
body: JSON.generate(id: 're_123', object: 'refund', status: 'succeeded') )
|
||||
end
|
||||
|
||||
it "voids the payment" do
|
||||
order.reload
|
||||
expect(order.payment_total).to_not eq 0
|
||||
expect(order.outstanding_balance).to eq 0
|
||||
spree_put :fire, params
|
||||
expect(payment.reload.state).to eq 'void'
|
||||
order.reload
|
||||
expect(order.payment_total).to eq 0
|
||||
expect(order.outstanding_balance).to_not eq 0
|
||||
end
|
||||
end
|
||||
|
||||
context "where the request fails" do
|
||||
before do
|
||||
stub_request(:post, "https://api.stripe.com/v1/charges/ch_1a2b3c/refunds").
|
||||
with(basic_auth: ["sk_test_12345", ""]).
|
||||
to_return(status: 200, body: JSON.generate(error: { message: "Bup-bow!" }) )
|
||||
end
|
||||
|
||||
it "does not void the payment" do
|
||||
order.reload
|
||||
expect(order.payment_total).to_not eq 0
|
||||
expect(order.outstanding_balance).to eq 0
|
||||
spree_put :fire, params
|
||||
expect(payment.reload.state).to eq 'completed'
|
||||
order.reload
|
||||
expect(order.payment_total).to_not eq 0
|
||||
expect(order.outstanding_balance).to eq 0
|
||||
expect(flash[:error]).to eq "Bup-bow!"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "requesting a partial credit on a payment" do
|
||||
let(:params) { { id: payment.id, order_id: order.number, e: :credit } }
|
||||
|
||||
# Required for the respond override in the controller decorator to work
|
||||
before { @request.env['HTTP_REFERER'] = spree.admin_order_payments_url(payment) }
|
||||
|
||||
context "that was processed by stripe" do
|
||||
let!(:payment_method) { create(:stripe_sca_payment_method, distributors: [shop]) }
|
||||
let!(:payment) do
|
||||
create(:payment, order: order, state: 'completed', payment_method: payment_method,
|
||||
response_code: 'pi_123', amount: order.total + 5)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(Stripe).to receive(:api_key) { "sk_test_12345" }
|
||||
|
||||
# Retrieves payment intent info
|
||||
stub_request(:get, "https://api.stripe.com/v1/payment_intents/pi_123")
|
||||
.to_return({ status: 200, body: JSON.generate(
|
||||
amount_received: 2000,
|
||||
charges: { data: [{ id: "ch_1a2b3c" }] }
|
||||
) })
|
||||
end
|
||||
|
||||
context "where the request succeeds" do
|
||||
before do
|
||||
stub_request(:post, "https://api.stripe.com/v1/charges/ch_1a2b3c/refunds").
|
||||
with(basic_auth: ["sk_test_12345", ""]).
|
||||
to_return(status: 200,
|
||||
body: JSON.generate(id: 're_123', object: 'refund', status: 'succeeded') )
|
||||
end
|
||||
|
||||
it "partially refunds the payment" do
|
||||
order.reload
|
||||
expect(order.payment_total).to eq order.total + 5
|
||||
expect(order.outstanding_balance).to eq(-5)
|
||||
spree_put :fire, params
|
||||
expect(payment.reload.state).to eq 'completed'
|
||||
order.reload
|
||||
expect(order.payment_total).to eq order.total
|
||||
expect(order.outstanding_balance).to eq 0
|
||||
end
|
||||
end
|
||||
|
||||
context "where the request fails" do
|
||||
before do
|
||||
stub_request(:post, "https://api.stripe.com/v1/charges/ch_1a2b3c/refunds").
|
||||
with(basic_auth: ["sk_test_12345", ""]).
|
||||
to_return(status: 200, body: JSON.generate(error: { message: "Bup-bow!" }) )
|
||||
end
|
||||
|
||||
it "does not void the payment" do
|
||||
order.reload
|
||||
expect(order.payment_total).to eq order.total + 5
|
||||
expect(order.outstanding_balance).to eq(-5)
|
||||
spree_put :fire, params
|
||||
expect(payment.reload.state).to eq 'completed'
|
||||
order.reload
|
||||
expect(order.payment_total).to eq order.total + 5
|
||||
expect(order.outstanding_balance).to eq(-5)
|
||||
expect(flash[:error]).to eq "Bup-bow!"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -138,128 +138,4 @@ describe Spree::Admin::PaymentsController, type: :controller do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "as an enterprise user" do
|
||||
before do
|
||||
order.reload.update_totals
|
||||
end
|
||||
|
||||
context "requesting a refund on a payment" do
|
||||
let(:params) { { id: payment.id, order_id: order.number, e: :void } }
|
||||
|
||||
# Required for the respond override in the controller decorator to work
|
||||
before { @request.env['HTTP_REFERER'] = spree.admin_order_payments_url(payment) }
|
||||
|
||||
context "that was processed by stripe" do
|
||||
let!(:payment_method) { create(:stripe_payment_method, distributors: [shop]) }
|
||||
let!(:payment) do
|
||||
create(:payment, order: order, state: 'completed', payment_method: payment_method,
|
||||
response_code: 'ch_1a2b3c', amount: order.total)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(Stripe).to receive(:api_key) { "sk_test_12345" }
|
||||
end
|
||||
|
||||
context "where the request succeeds" do
|
||||
before do
|
||||
stub_request(:post, "https://api.stripe.com/v1/charges/ch_1a2b3c/refunds").
|
||||
with(basic_auth: ["sk_test_12345", ""]).
|
||||
to_return(status: 200,
|
||||
body: JSON.generate(id: 're_123', object: 'refund', status: 'succeeded') )
|
||||
end
|
||||
|
||||
it "voids the payment" do
|
||||
order.reload
|
||||
expect(order.payment_total).to_not eq 0
|
||||
expect(order.outstanding_balance).to eq 0
|
||||
spree_put :fire, params
|
||||
expect(payment.reload.state).to eq 'void'
|
||||
order.reload
|
||||
expect(order.payment_total).to eq 0
|
||||
expect(order.outstanding_balance).to_not eq 0
|
||||
end
|
||||
end
|
||||
|
||||
context "where the request fails" do
|
||||
before do
|
||||
stub_request(:post, "https://api.stripe.com/v1/charges/ch_1a2b3c/refunds").
|
||||
with(basic_auth: ["sk_test_12345", ""]).
|
||||
to_return(status: 200, body: JSON.generate(error: { message: "Bup-bow!" }) )
|
||||
end
|
||||
|
||||
it "does not void the payment" do
|
||||
order.reload
|
||||
expect(order.payment_total).to_not eq 0
|
||||
expect(order.outstanding_balance).to eq 0
|
||||
spree_put :fire, params
|
||||
expect(payment.reload.state).to eq 'completed'
|
||||
order.reload
|
||||
expect(order.payment_total).to_not eq 0
|
||||
expect(order.outstanding_balance).to eq 0
|
||||
expect(flash[:error]).to eq "Bup-bow!"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "requesting a partial credit on a payment" do
|
||||
let(:params) { { id: payment.id, order_id: order.number, e: :credit } }
|
||||
|
||||
# Required for the respond override in the controller decorator to work
|
||||
before { @request.env['HTTP_REFERER'] = spree.admin_order_payments_url(payment) }
|
||||
|
||||
context "that was processed by stripe" do
|
||||
let!(:payment_method) { create(:stripe_payment_method, distributors: [shop]) }
|
||||
let!(:payment) do
|
||||
create(:payment, order: order, state: 'completed', payment_method: payment_method,
|
||||
response_code: 'ch_1a2b3c', amount: order.total + 5)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(Stripe).to receive(:api_key) { "sk_test_12345" }
|
||||
end
|
||||
|
||||
context "where the request succeeds" do
|
||||
before do
|
||||
stub_request(:post, "https://api.stripe.com/v1/charges/ch_1a2b3c/refunds").
|
||||
with(basic_auth: ["sk_test_12345", ""]).
|
||||
to_return(status: 200,
|
||||
body: JSON.generate(id: 're_123', object: 'refund', status: 'succeeded') )
|
||||
end
|
||||
|
||||
it "partially refunds the payment" do
|
||||
order.reload
|
||||
expect(order.payment_total).to eq order.total + 5
|
||||
expect(order.outstanding_balance).to eq(-5)
|
||||
spree_put :fire, params
|
||||
expect(payment.reload.state).to eq 'completed'
|
||||
order.reload
|
||||
expect(order.payment_total).to eq order.total
|
||||
expect(order.outstanding_balance).to eq 0
|
||||
end
|
||||
end
|
||||
|
||||
context "where the request fails" do
|
||||
before do
|
||||
stub_request(:post, "https://api.stripe.com/v1/charges/ch_1a2b3c/refunds").
|
||||
with(basic_auth: ["sk_test_12345", ""]).
|
||||
to_return(status: 200, body: JSON.generate(error: { message: "Bup-bow!" }) )
|
||||
end
|
||||
|
||||
it "does not void the payment" do
|
||||
order.reload
|
||||
expect(order.payment_total).to eq order.total + 5
|
||||
expect(order.outstanding_balance).to eq(-5)
|
||||
spree_put :fire, params
|
||||
expect(payment.reload.state).to eq 'completed'
|
||||
order.reload
|
||||
expect(order.payment_total).to eq order.total + 5
|
||||
expect(order.outstanding_balance).to eq(-5)
|
||||
expect(flash[:error]).to eq "Bup-bow!"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -97,18 +97,7 @@ module OpenFoodNetwork
|
||||
|
||||
describe "Filtering variants" do
|
||||
let(:variants) { Spree::Variant.where(nil).joins(:product).where(is_master: false) }
|
||||
it "should return unfiltered variants sans-params" do
|
||||
product1 = create(:simple_product, supplier: supplier)
|
||||
product2 = create(:simple_product, supplier: supplier)
|
||||
|
||||
expect(subject.filter(Spree::Variant.where(nil))).to match_array [product1.master, product1.variants.first, product2.master, product2.variants.first]
|
||||
end
|
||||
it "should filter deleted products" do
|
||||
product1 = create(:simple_product, supplier: supplier)
|
||||
product2 = create(:simple_product, supplier: supplier)
|
||||
product2.destroy
|
||||
expect(subject.filter(Spree::Variant.where(nil))).to match_array [product1.master, product1.variants.first]
|
||||
end
|
||||
describe "based on report type" do
|
||||
it "returns only variants on hand" do
|
||||
product1 = create(:simple_product, supplier: supplier, on_hand: 99)
|
||||
|
||||
@@ -64,12 +64,20 @@ describe "checking out an order with a Stripe SCA payment method", type: :reques
|
||||
}
|
||||
end
|
||||
let(:payment_intent_response_mock) do
|
||||
{ status: 200, body: JSON.generate(object: "payment_intent", amount: 2000, charges: { data: [{ id: "ch_1234", amount: 2000 }] }) }
|
||||
{
|
||||
status: 200, body: JSON.generate(object: "payment_intent",
|
||||
amount: 2000,
|
||||
charges: { data: [{ id: "ch_1234", amount: 2000 }] })
|
||||
}
|
||||
end
|
||||
let(:payment_intent_authorize_response_mock) do
|
||||
{ status: 200, body: JSON.generate(id: payment_intent_id, object: "payment_intent", amount: 2000,
|
||||
{
|
||||
status: 200, body: JSON.generate(id: payment_intent_id,
|
||||
object: "payment_intent",
|
||||
amount: 2000,
|
||||
status: "requires_capture", last_payment_error: nil,
|
||||
charges: { data: [{ id: "ch_1234", amount: 2000 }] }) }
|
||||
charges: { data: [{ id: "ch_1234", amount: 2000 }] })
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
@@ -166,13 +174,15 @@ describe "checking out an order with a Stripe SCA payment method", type: :reques
|
||||
headers: { 'Stripe-Account' => 'abc123' })
|
||||
.to_return(hubs_payment_method_response_mock)
|
||||
|
||||
# Creates a customer (this stubs the customers call to the main stripe account and also the call to the connected account)
|
||||
# Creates a customer
|
||||
# This stubs the customers call to both the main stripe account and the connected account
|
||||
stub_request(:post, "https://api.stripe.com/v1/customers")
|
||||
.with(body: { email: order.email })
|
||||
.to_return(customer_response_mock)
|
||||
|
||||
# Attaches the payment method to the customer in the hub's stripe account
|
||||
stub_request(:post, "https://api.stripe.com/v1/payment_methods/#{hubs_stripe_payment_method}/attach")
|
||||
stub_request(:post,
|
||||
"https://api.stripe.com/v1/payment_methods/#{hubs_stripe_payment_method}/attach")
|
||||
.with(body: { customer: customer_id },
|
||||
headers: { 'Stripe-Account' => 'abc123' })
|
||||
.to_return(hubs_payment_method_response_mock)
|
||||
@@ -191,7 +201,8 @@ describe "checking out an order with a Stripe SCA payment method", type: :reques
|
||||
source_attributes[:save_requested_by_customer] = '1'
|
||||
|
||||
# Attaches the payment method to the customer
|
||||
stub_request(:post, "https://api.stripe.com/v1/payment_methods/#{stripe_payment_method}/attach")
|
||||
stub_request(:post,
|
||||
"https://api.stripe.com/v1/payment_methods/#{stripe_payment_method}/attach")
|
||||
.with(body: { customer: customer_id })
|
||||
.to_return(payment_method_attach_response_mock)
|
||||
end
|
||||
@@ -316,9 +327,13 @@ describe "checking out an order with a Stripe SCA payment method", type: :reques
|
||||
|
||||
context "when the stripe API sends a url for the authorization of the transaction" do
|
||||
let(:payment_intent_authorize_response_mock) do
|
||||
{ status: 200, body: JSON.generate(id: payment_intent_id, object: "payment_intent",
|
||||
next_source_action: { type: "authorize_with_url", authorize_with_url: { url: stripe_redirect_url } },
|
||||
status: "requires_source_action" ) }
|
||||
{ status: 200, body: JSON.generate(id: payment_intent_id,
|
||||
object: "payment_intent",
|
||||
next_source_action: {
|
||||
type: "authorize_with_url",
|
||||
authorize_with_url: { url: stripe_redirect_url }
|
||||
},
|
||||
status: "requires_source_action") }
|
||||
end
|
||||
|
||||
it "redirects the user to the authorization stripe url" do
|
||||
|
||||
Reference in New Issue
Block a user