Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ SolidusStripe.PaymentIntents.prototype.onIntentsPayment = function(payment) {
'Content-Type': 'application/json'
},
body: JSON.stringify({
form_data: this.form.serialize(),
spree_payment_method_id: this.config.id,
stripe_payment_method_id: payment.paymentMethod.id,
authenticity_token: this.authToken
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
form_data: this.form.serialize(),
spree_payment_method_id: this.config.id,
stripe_payment_intent_id: result.paymentIntent.id,
authenticity_token: this.authToken
Expand Down
49 changes: 29 additions & 20 deletions app/controllers/solidus_stripe/intents_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,19 @@ class IntentsController < Spree::BaseController

def confirm
begin
if params[:stripe_payment_method_id].present?
intent = stripe.create_intent(
(current_order.total * 100).to_i,
params[:stripe_payment_method_id],
currency: current_order.currency,
confirmation_method: 'manual',
confirm: true,
setup_future_usage: 'on_session',
metadata: { order_id: current_order.id }
)
elsif params[:stripe_payment_intent_id].present?
intent = stripe.confirm_intent(params[:stripe_payment_intent_id], nil)
@intent = begin
if params[:stripe_payment_method_id].present?
create_intent
elsif params[:stripe_payment_intent_id].present?
stripe.confirm_intent(params[:stripe_payment_intent_id], nil)
end
end
rescue Stripe::CardError => e
render json: { error: e.message }, status: 500
return
end

generate_payment_response(intent)
generate_payment_response
end

private
Expand All @@ -33,20 +27,35 @@ def stripe
@stripe ||= Spree::PaymentMethod::StripeCreditCard.find(params[:spree_payment_method_id])
end

def generate_payment_response(intent)
response = intent.params
def generate_payment_response
response = @intent.params
# Note that if your API version is before 2019-02-11, 'requires_action'
# appears as 'requires_source_action'.
if %w[requires_source_action requires_action].include?(response['status']) && response['next_action']['type'] == 'use_stripe_sdk'
render json: {
requires_action: true,
stripe_payment_intent_client_secret: response['client_secret']
}
elsif response['status'] == 'succeeded'
render json: {
requires_action: true,
stripe_payment_intent_client_secret: response['client_secret']
}
elsif response['status'] == 'requires_capture'
SolidusStripe::CreateIntentsOrderService.new(@intent, stripe, self).call
render json: { success: true }
else
render json: { error: response['error']['message'] }, status: 500
end
end

def create_intent
stripe.create_intent(
(current_order.total * 100).to_i,
params[:stripe_payment_method_id],
description: "Solidus Order ID: #{current_order.number} (pending)",
currency: current_order.currency,
confirmation_method: 'manual',
capture_method: 'manual',
confirm: true,
setup_future_usage: 'off_session',
metadata: { order_id: current_order.id }
)
end
end
end
39 changes: 39 additions & 0 deletions app/decorators/models/spree/order_update_attributes_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# frozen_string_literal: true

module Spree
module OrderUpdateAttributesDecorator
def assign_payments_attributes
return if payments_attributes.empty?
return if adding_new_stripe_payment_intents_card?

stripe_intents_pending_payments.each(&:void_transaction!)

super
end

private

def adding_new_stripe_payment_intents_card?
paying_with_stripe_intents? && stripe_intents_pending_payments.any?
end

def stripe_intents_pending_payments
@stripe_intents_pending_payments ||= order.payments.valid.select do |payment|
payment_method = payment.payment_method
payment.pending? && stripe_intents?(payment_method)
end
end

def paying_with_stripe_intents?
if id = payments_attributes.first&.dig(:payment_method_id)
stripe_intents?(Spree::PaymentMethod.find(id))
end
end

def stripe_intents?(payment_method)
payment_method.respond_to?(:v3_intents?) && payment_method.v3_intents?
end

::Spree::OrderUpdateAttributes.prepend(self)
end
end
11 changes: 11 additions & 0 deletions app/decorators/models/spree/payment_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

module Spree
module PaymentDecorator
def gateway_order_identifier
gateway_order_id
end

::Spree::Payment.prepend(self)
end
end
70 changes: 70 additions & 0 deletions app/models/solidus_stripe/create_intents_order_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# frozen_string_literal: true

module SolidusStripe
class CreateIntentsOrderService
attr_reader :intent, :stripe, :controller

delegate :request, :current_order, :params, to: :controller

def initialize(intent, stripe, controller)
@intent, @stripe, @controller = intent, stripe, controller
end

def call
invalidate_previous_payment_intents_payments
payment = create_payment
description = "Solidus Order ID: #{payment.gateway_order_identifier}"
stripe.update_intent(nil, response['id'], nil, description: description)
end

private

def invalidate_previous_payment_intents_payments
if stripe.v3_intents?
current_order.payments.pending.where(payment_method: stripe).each(&:void_transaction!)
end
end

def create_payment
Spree::OrderUpdateAttributes.new(
current_order,
payment_params,
request_env: request.headers.env
).apply

Spree::Payment.find_by(response_code: response['id']).tap do |payment|
payment.update!(state: :pending)
end
end

def payment_params
card = response['charges']['data'][0]['payment_method_details']['card']
address_attributes = form_data['payment_source'][stripe.id.to_s]['address_attributes']

{
payments_attributes: [{
payment_method_id: stripe.id,
amount: current_order.total,
response_code: response['id'],
source_attributes: {
month: card['exp_month'],
year: card['exp_year'],
cc_type: card['brand'],
gateway_payment_profile_id: response['payment_method'],
last_digits: card['last4'],
name: current_order.bill_address.full_name,
address_attributes: address_attributes
}
}]
}
end

def response
intent.params
end

def form_data
Rack::Utils.parse_nested_query(params[:form_data])
end
end
end
11 changes: 3 additions & 8 deletions app/models/spree/payment_method/stripe_credit_card.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ class StripeCreditCard < Spree::PaymentMethod::CreditCard
'Visa' => 'visa'
}

delegate :create_intent, :update_intent, :confirm_intent, to: :gateway

def stripe_config(order)
{
id: id,
Expand Down Expand Up @@ -59,14 +61,6 @@ def payment_profiles_supported?
true
end

def create_intent(*args)
gateway.create_intent(*args)
end

def confirm_intent(*args)
gateway.confirm_intent(*args)
end

def purchase(money, creditcard, transaction_options)
gateway.purchase(*options_for_purchase_or_auth(money, creditcard, transaction_options))
end
Expand Down Expand Up @@ -142,6 +136,7 @@ def options_for_purchase_or_auth(money, creditcard, transaction_options)
options = {}
options[:description] = "Solidus Order ID: #{transaction_options[:order_id]}"
options[:currency] = transaction_options[:currency]
options[:off_session] = true if v3_intents?

if customer = creditcard.gateway_customer_profile_id
options[:customer] = customer
Expand Down
Loading