Files
openfoodnetwork/app/controllers/spree/admin/payment_methods_controller.rb
Maikel Linke 93f2af7e7d Lazy load payment method classes
This avoids any class reloading issues in dev, simplifies most of the
code path and opens up for refactoring.

The only reason we still load the class is to call `clean_name` on it
for the translation key. We can probably do better.
2026-01-09 16:54:02 +11:00

215 lines
6.8 KiB
Ruby

# frozen_string_literal: true
module Spree
module Admin
class PaymentMethodsController < ::Admin::ResourceController
skip_before_action :load_resource, only: [:create, :show_provider_preferences]
before_action :load_data
before_action :validate_payment_method_provider, only: [:create]
before_action :load_hubs, only: [:new, :edit, :update]
respond_to :html
PAYMENT_METHODS = %w{
Spree::PaymentMethod::Check
Spree::Gateway::PayPalExpress
Spree::Gateway::StripeSCA
}.freeze
def create
force_environment
@payment_method = payment_method_class.constantize.new(base_params)
@object = @payment_method
if !base_params["calculator_type"] && gateway_params["calculator_type"]
@payment_method.calculator_type = gateway_params["calculator_type"]
end
load_hubs
if @payment_method.save
flash[:success] = Spree.t(:successfully_created, resource: Spree.t(:payment_method))
redirect_to spree.edit_admin_payment_method_path(@payment_method)
else
respond_with(@payment_method)
end
end
def update
restrict_stripe_account_change
force_environment
if @payment_method.type.to_s != payment_method_class
@payment_method.update_columns(
type: payment_method_class,
updated_at: Time.zone.now
)
@payment_method = PaymentMethod.find(params[:id])
end
if @payment_method.update(update_params)
flash[:success] = Spree.t(:successfully_updated, resource: Spree.t(:payment_method))
redirect_to spree.edit_admin_payment_method_path(@payment_method)
else
respond_with(@payment_method)
clear_preference_cache
end
end
# Only show payment methods that user has access to and sort by distributor name
# ! Redundant code copied from Admin::ResourceController with modifications marked
def collection
return parent.public_send(controller_name) if parent_data.present?
collection = if model_class.respond_to?(:accessible_by) &&
!current_ability.has_block?(params[:action], model_class)
model_class.accessible_by(current_ability, action)
else
model_class.where(nil)
end
collection = collection.managed_by(spree_current_user).by_name # This line added
# This block added
if params.key? :enterprise_id
distributor = Enterprise.find params[:enterprise_id]
collection = collection.for_distributor(distributor)
end
collection
end
def show_provider_preferences
if params[:pm_id].present?
@payment_method = PaymentMethod.find(params[:pm_id])
authorize! :show_provider_preferences, @payment_method
payment_method_type = params[:provider_type]
if @payment_method['type'].to_s != payment_method_type
@payment_method.update_columns(
type: payment_method_type,
updated_at: Time.zone.now
)
@payment_method = PaymentMethod.find(params[:pm_id])
end
else
@payment_method = PaymentMethod.new(type: params[:provider_type])
end
render partial: 'provider_settings'
end
private
def payment_method_class
@payment_method_class ||= base_params.delete(:type)
end
def force_environment
base_params[:environment] = Rails.env unless spree_current_user.admin?
end
def load_data
@providers = load_providers
@calculators = PaymentMethod.calculators.sort_by(&:name)
end
def validate_payment_method_provider
valid_payment_methods = PAYMENT_METHODS
return if valid_payment_methods.include?(params[:payment_method][:type])
flash[:error] = Spree.t(:invalid_payment_provider)
redirect_to spree.new_admin_payment_method_path
end
def load_hubs
# rubocop:disable Style/TernaryParentheses
@hubs = Enterprise.managed_by(spree_current_user).is_distributor.to_a.sort_by! do |d|
[(@payment_method.has_distributor? d) ? 0 : 1, d.name]
end
# rubocop:enable Style/TernaryParentheses
end
def load_providers
providers = PAYMENT_METHODS.sort
unless show_stripe?
providers.reject! { |provider| stripe_provider?(provider) }
end
providers.map(&:constantize)
end
# Show Stripe as an option if enabled, or if the
# current payment_method is already a Stripe method
def show_stripe?
Spree::Config.stripe_connect_enabled ||
stripe_payment_method?
end
def restrict_stripe_account_change
return unless @payment_method
return unless stripe_payment_method?
return unless @payment_method.preferred_enterprise_id&.positive?
@stripe_account_holder = Enterprise.find(@payment_method.preferred_enterprise_id)
return if spree_current_user.enterprises.include? @stripe_account_holder
update_params[:preferred_enterprise_id] = @stripe_account_holder.id
end
def stripe_payment_method?
@payment_method.try(:type) == "Spree::Gateway::StripeSCA"
end
def stripe_provider?(provider)
provider.ends_with?("StripeSCA")
end
def base_params
@base_params ||= PermittedAttributes::PaymentMethod.new(params[:payment_method]).
call.to_h.with_indifferent_access
end
def gateway_params
raw_params[ActiveModel::Naming.param_key(@payment_method)] || {}
end
# Merge payment method params with gateway params like :gateway_stripe_sca
# Also, remove password if present and blank
def update_params
@update_params ||= begin
params_for_update = base_params.merge(gateway_params)
params_for_update.each do |key, value|
if key.include?("password") && value.blank?
params_for_update.delete(key)
end
end
# Ensure the calculator to be updated is the correct type
if params_for_update["calculator_type"] && params_for_update["calculator_attributes"]
add_type_to_calculator_attributes(params_for_update)
end
params_for_update
end
end
def clear_preference_cache
@payment_method.calculator.preferences.each_key do |key|
# Only persisted models have cache keys.
cache_key = @payment_method.calculator.preference_cache_key(key)
Rails.cache.delete(cache_key) if cache_key
end
end
def add_type_to_calculator_attributes(hash)
hash["calculator_attributes"]["type"] = hash["calculator_type"]
end
end
end
end