diff --git a/app/models/spree/gateway/stripe_sca.rb b/app/models/spree/gateway/stripe_sca.rb index d5ac331b89..dcc585ff5a 100644 --- a/app/models/spree/gateway/stripe_sca.rb +++ b/app/models/spree/gateway/stripe_sca.rb @@ -32,9 +32,41 @@ module Spree # NOTE: the name of this method is determined by Spree::Payment::Processing def purchase(money, creditcard, gateway_options) - provider.purchase(*options_for_purchase_or_auth(money, creditcard, gateway_options)) + payment_intent_id = payment_intent(creditcard, gateway_options) + unless payment_intent_id + return failed_activemerchant_billing_response("No Pending Payment Found") + end + + payment_intent_response = Stripe::PaymentIntent.retrieve(payment_intent_id, + stripe_account: stripe_account_id) + if payment_intent_response.last_payment_error.present? + return failed_activemerchant_billing_response(payment_intent_response. + last_payment_error. + message) + end + if payment_intent_response.status != 'requires_capture' + return failed_activemerchant_billing_response("Invalid PaymentIntent status.") + end + + options = basic_options(gateway_options) + options[:customer] = creditcard.gateway_customer_profile_id + provider.capture(money, payment_intent_id, options) + rescue Stripe::StripeError => e + failed_activemerchant_billing_response(e.message) + end + + # NOTE: the name of this method is determined by Spree::Payment::Processing + def authorize(money, creditcard, gateway_options) + authorize_response = provider.authorize(*options_for_authorize(money, + creditcard, + gateway_options)) + + if url = url_for_authorization(authorize_response) + authorize_response.cvv_result['message'] = url if authorize_response.cvv_result.present? + end + + authorize_response rescue Stripe::StripeError => e - # This will be an error caused by generating a stripe token failed_activemerchant_billing_response(e.message) end @@ -65,11 +97,17 @@ module Spree options.merge(login: Stripe.api_key) end - def options_for_purchase_or_auth(money, creditcard, gateway_options) + def basic_options(gateway_options) options = {} options[:description] = "Spree Order ID: #{gateway_options[:order_id]}" options[:currency] = gateway_options[:currency] options[:stripe_account] = stripe_account_id + options + end + + def options_for_authorize(money, creditcard, gateway_options) + options = basic_options(gateway_options) + options[:return_url] = full_checkout_path customer_id, payment_method_id = Stripe::CreditCardCloner.new.clone(creditcard, stripe_account_id) @@ -77,6 +115,23 @@ module Spree [money, payment_method_id, options] end + def url_for_authorization(response) + next_action = response.params["next_source_action"] + return unless response.params["status"] == "requires_source_action" && + next_action.present? && + next_action["type"] == "authorize_with_url" + + next_action["authorize_with_url"]["url"] + end + + def payment_intent(creditcard, gateway_options) + order_number = gateway_options[:order_id].split('-').first + payment = Spree::Order.find_by_number(order_number).payments.merge(creditcard.payments).last + return unless payment + + payment.response_code + end + def failed_activemerchant_billing_response(error_message) ActiveMerchant::Billing::Response.new(false, error_message) end @@ -86,6 +141,16 @@ module Spree errors.add(:stripe_account_owner, I18n.t(:error_required)) end + + def full_checkout_path + URI.join(url_helpers.root_url, url_helpers.checkout_path).to_s + end + + def url_helpers + # This is how we can get the helpers with a usable root_url outside the controllers + Rails.application.routes.default_url_options = ActionMailer::Base.default_url_options + Rails.application.routes.url_helpers + end end end end