Merge pull request #6808 from andrewpbrett/authorize-payment-links

Allow a customer to perform further action for a Stripe payment if needed (#4181)
This commit is contained in:
Andy Brett
2021-02-17 10:17:51 -08:00
committed by GitHub
8 changed files with 111 additions and 3 deletions

View File

@@ -27,6 +27,7 @@ module Spree
def show
@order = Spree::Order.find_by!(number: params[:id])
ProcessPaymentIntent.new(params["payment_intent"], @order).call!
@order.reload
end
def empty

View File

@@ -16,7 +16,7 @@ module Api
end
def payments
object.payments.joins(:payment_method).completed
object.payments.joins(:payment_method).where('state IN (?)', %w(completed pending))
end
def shop_id

View File

@@ -1,6 +1,6 @@
module Api
class PaymentSerializer < ActiveModel::Serializer
attributes :amount, :updated_at, :payment_method, :state
attributes :amount, :updated_at, :payment_method, :state, :cvv_response_message
def payment_method
object.payment_method.try(:name)

View File

@@ -1,5 +1,13 @@
# frozen_string_literal: true
# When directing a customer to Stripe to authorize the payment, we specify a
# redirect_url that Stripe should return them to. When checking out, it's
# /checkout; for admin payments and subscription payemnts it's the order url.
# This class confirms that the payment intent matches what's in our database,
# marks the payment as complete, and removes the cvv_response_message field,
# which we use to indicate that authorization is required. It also completes the
# Order, if appropriate.
class ProcessPaymentIntent
def initialize(payment_intent, order)
@payment_intent = payment_intent
@@ -11,7 +19,8 @@ class ProcessPaymentIntent
return unless valid?
last_payment.update_attribute(:cvv_response_message, nil)
last_payment.complete!
OrderWorkflow.new(@order).next
last_payment.complete! if !last_payment.completed?
end
private

View File

@@ -16,6 +16,8 @@
%td.order3.show-for-large-up
%i{"ng-class" => "{'ofn-i_012-warning': payment.state == 'invalid' || payment.state == 'void' || payment.state == 'failed'}"}
%span{"ng-bind" => "::'spree.payment_states.' + payment.state | t | capitalize"}
%span{"ng-if" => "payment.cvv_response_message.length > 0" }
%a{"ng-href" => "{{payment.cvv_response_message}}", "ng-bind" => "::'spree.payment_states.authorise' | t | capitalize" }
%td.order4.show-for-large-up
%td.order5.text-right{"ng-class" => "{'credit' : payment.amount > 0, 'debit' : payment.amount < 0, 'paid' : payment.amount == 0}","ng-bind" => "::payment.amount | localizeCurrency"}
%td.order6.show-for-large-up

View File

@@ -3621,6 +3621,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using
processing: processing
void: void
invalid: invalid
authorise: authorise
order_mailer:
cancel_email:
customer_greeting: "Dear %{name},"

View File

@@ -79,6 +79,50 @@ describe Spree::OrdersController, type: :controller do
end
end
describe "confirming a payment intent" do
let(:customer) { create(:customer) }
let(:order) { create(:order, customer: customer, distributor: customer.enterprise) }
let!(:payment) { create(
:payment,
cvv_response_message: "https://stripe.com/redirect",
response_code: "pi_123",
order: order,
state: "pending")
}
before do
allow(controller).to receive(:spree_current_user) { current_user }
end
context "after returning from Stripe to authorize a payment" do
let(:current_user) { order.user }
context "with a valid payment intent" do
let(:payment_intent) { "pi_123" }
it "completes the payment" do
get :show, id: order.number, payment_intent: payment_intent
expect(response).to be_success
payment.reload
expect(payment.cvv_response_message).to be nil
expect(payment.state).to eq("completed")
end
end
context "with an invalid payment intent" do
let(:payment_intent) { "invalid" }
it "does not complete the payment" do
get :show, id: order.number, payment_intent: payment_intent
expect(response).to be_success
payment.reload
expect(payment.cvv_response_message).to eq("https://stripe.com/redirect")
expect(payment.state).to eq("pending")
end
end
end
end
describe "viewing cart" do
it "redirects home when no distributor is selected" do
get :edit

View File

@@ -0,0 +1,51 @@
# frozen_string_literal: true
require 'spec_helper'
describe ProcessPaymentIntent do
let(:service) { described_class.new }
describe "processing a payment intent" do
let(:customer) { create(:customer) }
let(:order) { create(:order, customer: customer, distributor: customer.enterprise, state: "payment") }
let!(:payment) { create(
:payment,
cvv_response_message: "https://stripe.com/redirect",
response_code: "pi_123",
order: order,
state: "pending")
}
context "an invalid intent" do
let(:invalid_intent) { "invalid" }
let(:service) { ProcessPaymentIntent.new(invalid_intent, order) }
it "does not complete the payment" do
service.call!
expect(payment.reload.state).to eq("pending")
end
end
context "a valid intent" do
let(:valid_intent) { "pi_123" }
let(:service) { ProcessPaymentIntent.new(valid_intent, order) }
before do
allow(order).to receive(:deliver_order_confirmation_email)
end
it "completes the payment" do
service.call!
payment.reload
expect(payment.state).to eq("completed")
expect(payment.cvv_response_message).to be nil
end
it "completes the order" do
service.call!
expect(order.state).to eq("complete")
expect(order).to have_received(:deliver_order_confirmation_email)
end
end
end
end