From f691d1aafd2c826ec97ebf85e0696e30c9a5d7c1 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Mon, 13 Jan 2020 17:27:17 +0000 Subject: [PATCH] Add new payment method StripeSCA that will use the Stripe Payment Intents API instead of the Stripe Charges API that the current StripeConnect gatreway uses --- .../darkswarm/services/checkout.js.coffee | 4 +- .../services/stripe_elements.js.coffee | 22 ++++- .../spree/admin/payment_methods_controller.rb | 16 +++- app/models/spree/gateway/stripe_sca.rb | 88 +++++++++++++++++++ .../_provider_settings.html.haml | 3 + config/application.rb | 1 + 6 files changed, 127 insertions(+), 7 deletions(-) create mode 100644 app/models/spree/gateway/stripe_sca.rb diff --git a/app/assets/javascripts/darkswarm/services/checkout.js.coffee b/app/assets/javascripts/darkswarm/services/checkout.js.coffee index 204c35ac66..83cf018365 100644 --- a/app/assets/javascripts/darkswarm/services/checkout.js.coffee +++ b/app/assets/javascripts/darkswarm/services/checkout.js.coffee @@ -7,6 +7,8 @@ Darkswarm.factory 'Checkout', ($injector, CurrentOrder, ShippingMethods, StripeE purchase: -> if @paymentMethod()?.method_type == 'stripe' && !@secrets.selected_card StripeElements.requestToken(@secrets, @submit) + else if @paymentMethod()?.method_type == 'stripe_sca' && !@secrets.selected_card + StripeElements.createPaymentMethod(@secrets, @submit) else @submit() @@ -59,7 +61,7 @@ Darkswarm.factory 'Checkout', ($injector, CurrentOrder, ShippingMethods, StripeE last_name: @order.bill_address.lastname } - if @paymentMethod()?.method_type == 'stripe' + if @paymentMethod()?.method_type == 'stripe' || @paymentMethod()?.method_type == 'stripe_sca' if @secrets.selected_card angular.extend munged_order, { existing_card_id: @secrets.selected_card diff --git a/app/assets/javascripts/darkswarm/services/stripe_elements.js.coffee b/app/assets/javascripts/darkswarm/services/stripe_elements.js.coffee index 32b0535251..89b79e78fd 100644 --- a/app/assets/javascripts/darkswarm/services/stripe_elements.js.coffee +++ b/app/assets/javascripts/darkswarm/services/stripe_elements.js.coffee @@ -1,12 +1,10 @@ Darkswarm.factory 'StripeElements', ($rootScope, Loading, RailsFlashLoader) -> new class StripeElements - # TODO: add locale here for translations of error messages etc. from Stripe - # These are both set from the StripeElements directive stripe: null card: null - # New Stripe Elements method + # Create Token to be used with the Stripe Charges API requestToken: (secrets, submit, loading_message = t("processing_payment")) -> return unless @stripe? && @card? @@ -23,6 +21,24 @@ Darkswarm.factory 'StripeElements', ($rootScope, Loading, RailsFlashLoader) -> secrets.card = response.token.card submit() + # Create Payment Method to be used with the Stripe Payment Intents API + createPaymentMethod: (secrets, submit, loading_message = t("processing_payment")) -> + return unless @stripe? && @card? + + Loading.message = loading_message + cardData = @makeCardData(secrets) + + @stripe.createPaymentMethod({ type: 'card', card: @card } + @card, cardData).then (response) => + if(response.error) + Loading.clear() + RailsFlashLoader.loadFlash({error: t("error") + ": #{response.error.message}"}) + else + secrets.token = response.paymentMethod.id + secrets.cc_type = response.paymentMethod.card.brand + secrets.card = response.paymentMethod.card + submit() + # Maps the brand returned by Stripe to that required by activemerchant mapCC: (ccType) -> switch ccType diff --git a/app/controllers/spree/admin/payment_methods_controller.rb b/app/controllers/spree/admin/payment_methods_controller.rb index 53bf22d06a..ad0bff9a3b 100644 --- a/app/controllers/spree/admin/payment_methods_controller.rb +++ b/app/controllers/spree/admin/payment_methods_controller.rb @@ -110,7 +110,7 @@ module Spree else Gateway.providers.reject{ |p| p.name.include? "Bogus" }.sort_by(&:name) end - @providers.reject!{ |p| p.name.ends_with? "StripeConnect" } unless show_stripe? + @providers.reject!{ |provider| stripe_provider?(provider) } unless show_stripe? @calculators = PaymentMethod.calculators.sort_by(&:name) end @@ -134,12 +134,12 @@ module Spree # current payment_method is already a Stripe method def show_stripe? Spree::Config.stripe_connect_enabled || - @payment_method.try(:type) == "Spree::Gateway::StripeConnect" + stripe_payment_method? end def restrict_stripe_account_change return unless @payment_method - return unless @payment_method.type == "Spree::Gateway::StripeConnect" + return unless stripe_payment_method? return unless @payment_method.preferred_enterprise_id.andand > 0 @stripe_account_holder = Enterprise.find(@payment_method.preferred_enterprise_id) @@ -147,6 +147,16 @@ module Spree params[:payment_method][:preferred_enterprise_id] = @stripe_account_holder.id end + + def stripe_payment_method? + @payment_method.try(:type) == "Spree::Gateway::StripeConnect" || + @payment_method.try(:type) == "Spree::Gateway::StripeSCA" + end + + def stripe_provider?(provider) + provider.name.ends_with?("StripeConnect") || + provider.name.ends_with?("StripeSCA") + end end end end diff --git a/app/models/spree/gateway/stripe_sca.rb b/app/models/spree/gateway/stripe_sca.rb new file mode 100644 index 0000000000..c8f4d56637 --- /dev/null +++ b/app/models/spree/gateway/stripe_sca.rb @@ -0,0 +1,88 @@ +require 'stripe/profile_storer' +require 'active_merchant/billing/gateways/stripe_payment_intents' +require 'active_merchant/billing/gateways/stripe_decorator' + +module Spree + class Gateway + class StripeSCA < Gateway + preference :enterprise_id, :integer + + validate :ensure_enterprise_selected + + attr_accessible :preferred_enterprise_id + + def method_type + 'stripe_sca' + end + + def provider_class + ActiveMerchant::Billing::StripePaymentIntentsGateway + end + + def payment_profiles_supported? + true + end + + def stripe_account_id + StripeAccount.find_by_enterprise_id(preferred_enterprise_id).andand.stripe_user_id + end + + # 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)) + rescue Stripe::StripeError => e + # This will be an error caused by generating a stripe token + failed_activemerchant_billing_response(e.message) + end + + # NOTE: the name of this method is determined by Spree::Payment::Processing + def void(response_code, _creditcard, gateway_options) + gateway_options[:stripe_account] = stripe_account_id + provider.void(response_code, gateway_options) + end + + # NOTE: the name of this method is determined by Spree::Payment::Processing + def credit(money, _creditcard, response_code, gateway_options) + gateway_options[:stripe_account] = stripe_account_id + provider.refund(money, response_code, gateway_options) + end + + def create_profile(payment) + return unless payment.source.gateway_customer_profile_id.nil? + + profile_storer = Stripe::ProfileStorer.new(payment, provider, stripe_account_id) + profile_storer.create_customer_from_token + end + + private + + # In this gateway, what we call 'secret_key' is the 'login' + def options + options = super + options.merge(login: Stripe.api_key) + end + + def options_for_purchase_or_auth(money, creditcard, gateway_options) + options = {} + options[:description] = "Spree Order ID: #{gateway_options[:order_id]}" + options[:currency] = gateway_options[:currency] + options[:stripe_account] = stripe_account_id + + options[:customer] = creditcard.gateway_customer_profile_id + creditcard = creditcard.gateway_payment_profile_id + + [money, creditcard, options] + end + + def failed_activemerchant_billing_response(error_message) + ActiveMerchant::Billing::Response.new(false, error_message) + end + + def ensure_enterprise_selected + return if preferred_enterprise_id.andand > 0 + + errors.add(:stripe_account_owner, I18n.t(:error_required)) + end + end + end +end diff --git a/app/views/spree/admin/payment_methods/_provider_settings.html.haml b/app/views/spree/admin/payment_methods/_provider_settings.html.haml index c64ad9f1d2..38f2ff06e9 100644 --- a/app/views/spree/admin/payment_methods/_provider_settings.html.haml +++ b/app/views/spree/admin/payment_methods/_provider_settings.html.haml @@ -1,6 +1,9 @@ += @payment_method - case @payment_method - when Spree::Gateway::StripeConnect = render 'stripe_connect' +- when Spree::Gateway::StripeSCA + = render 'stripe_connect' - else - if @payment_method.preferences.present? %fieldset.alpha.eleven.columns.no-border-bottom#gateway_fields diff --git a/config/application.rb b/config/application.rb index 057d50ff2f..2799e52d2f 100644 --- a/config/application.rb +++ b/config/application.rb @@ -92,6 +92,7 @@ module Openfoodnetwork app.config.spree.payment_methods << Spree::Gateway::Migs app.config.spree.payment_methods << Spree::Gateway::Pin app.config.spree.payment_methods << Spree::Gateway::StripeConnect + app.config.spree.payment_methods << Spree::Gateway::StripeSCA end # Settings in config/environments/* take precedence over those specified here.