sharetribe/sharetribe

View on GitHub
app/controllers/concerns/payments.rb

Summary

Maintainability
D
1 day
Test Coverage
module Payments
  extend ActiveSupport::Concern

  def payment_index
    render 'index', locals: form_locals
  end

  def form_locals
    more_locals = {}

    if @paypal_enabled
      more_locals.merge!(paypal_index)
    end

    if @stripe_enabled
      more_locals.merge!(stripe_index)
    end

    more_locals.merge!(build_prefs_form)
    view_locals = build_view_locals.merge(more_locals)

    stripe_connected =  view_locals[:stripe_enabled] && view_locals[:stripe_account] && view_locals[:stripe_account][:api_verified]
    paypal_connected =  view_locals[:paypal_enabled] && view_locals[:paypal_account].present?

    stripe_connect_onboarding = FeatureFlagHelper.feature_enabled?(:stripe_connect_onboarding)
    buyer_commission = stripe_tx_settings[:active] && (stripe_tx_settings[:commission_from_buyer].to_i > 0 || stripe_tx_settings[:minimum_buyer_transaction_fee_cents].to_i > 0)
    payment_locals = {
      stripe_connected: stripe_connected,
      paypal_connected: paypal_connected,
      payments_connected: stripe_connected || paypal_connected,
      stripe_allowed: stripe_allowed,
      paypal_allowed: paypal_allowed,
      stripe_ready: StripeHelper.community_ready_for_payments?(@current_community.id),
      paypal_ready: PaypalHelper.community_ready_for_payments?(@current_community.id),
      paypal_enabled_by_admin: !!paypal_tx_settings[:active],
      stripe_enabled_by_admin: !!stripe_tx_settings[:active],
      buyer_commission: buyer_commission,
      stripe_connect_onboarding: stripe_connect_onboarding
    }
    view_locals.merge(payment_locals)
  end

  def stripe_allowed
    stripe_mode = stripe_api.charges_mode(@current_community.id)
    TransactionService::AvailableCurrencies.stripe_allows_country_and_currency?(@current_community.country, @current_community.currency, stripe_mode)
  end

  def paypal_allowed
    TransactionService::AvailableCurrencies.paypal_allows_country_and_currency?(@current_community.country, @current_community.currency)
  end

  def enable
    if can_enable_gateway?
      result = tx_settings_api.activate(community_id: @current_community.id, payment_gateway: params[:payment_gateway], payment_process: :preauthorize)
      raise t('admin2.stripe.cannot_enable_gateway', gateway: params[:payment_gateway]) unless result[:success]
    else
      raise t('admin2.stripe.cannot_enable_gateway_because_of_buyer_commission', gateway: params[:payment_gateway])
    end
  rescue StandardError => e
    flash[:error] = e.message
  ensure
    redirect_to action: :index
  end

  def disable
    result = tx_settings_api.disable(community_id: @current_community.id, payment_gateway: params[:payment_gateway], payment_process: :preauthorize)
    raise t('admin2.stripe.cannot_disable_gateway', gateway: params[:payment_gateway]) unless result[:success]
  rescue StandardError => e
    flash[:error] = e.message
  ensure
    redirect_to action: :index
  end

  private

  MIN_COMMISSION_PERCENTAGE = 0
  MAX_COMMISSION_PERCENTAGE = 100

  def ensure_payments_enabled
    @paypal_enabled = PaypalHelper.paypal_provisioned?(@current_community.id)
    @stripe_enabled = StripeHelper.stripe_provisioned?(@current_community.id)
    unless @paypal_enabled || @stripe_enabled
      flash[:error] = t('admin.payment_preferences.index.payments_not_enabled')
      redirect_to admin2_path
    end
  end

  def paypal_index
    paypal_account = paypal_accounts_api.get(community_id: @current_community.id).data
    { order_permission_action: admin_paypal_preferences_account_create_path,
      paypal_account: paypal_account }
  end

  def stripe_index
    stripe_account = stripe_tx_settings
    { stripe_account: stripe_account,
      stripe_api_form: StripeApiKeysForm.new }
  end

  def tx_settings_by_gateway(gateway)
    tx_settings_api.get(community_id: @current_community.id, payment_gateway: gateway, payment_process: :preauthorize)
  end

  def paypal_tx_settings
    Maybe(tx_settings_by_gateway(:paypal))
      .select { |result| result[:success] }
      .map { |result| result[:data] }
      .or_else({})
  end

  def stripe_tx_settings
    Maybe(tx_settings_by_gateway(:stripe))
      .select { |result| result[:success] }
      .map { |result| result[:data] }
      .or_else({})
  end

  def active_tx_setttings
    @paypal_enabled ? paypal_tx_settings : stripe_tx_settings
  end

  def build_prefs_form(params = nil)
    currency = @current_community.currency
    data = { paypal_prefs_form: nil, stripe_prefs_form: nil }

    if @paypal_enabled
      data[:paypal_prefs_form] = prefs_form_from_settings(paypal_tx_settings, paypal_minimum_commissions_api.get(currency), currency)
    end
    if @stripe_enabled
      data[:stripe_prefs_form] = prefs_form_from_settings(stripe_tx_settings, 0, currency)
    end

    form = data[:paypal_prefs_form] || data[:stripe_prefs_form]
    data[:payment_prefs_form] = form
    data[:payment_prefs_valid] = form.valid?
    data
  end

  def prefs_form_from_settings(tx_settings, minimum_commission, currency)
    PaymentPreferencesForm.new(
      minimum_commission: minimum_commission,
      commission_from_seller: tx_settings[:commission_from_seller],
      minimum_listing_price: Money.new(@current_community.minimum_price_cents, currency),
      minimum_transaction_fee: Money.new(tx_settings[:minimum_transaction_fee_cents], currency),
      marketplace_currency: currency,
      commission_from_buyer: tx_settings[:commission_from_buyer],
      minimum_buyer_transaction_fee: Money.new(tx_settings[:minimum_buyer_transaction_fee_cents], currency)
    )
  end

  def build_view_locals
    @selected_left_navi_link = "payment_preferences"
    make_onboarding_popup

    {
      min_commission_percentage: MIN_COMMISSION_PERCENTAGE,
      max_commission_percentage: MAX_COMMISSION_PERCENTAGE,
      available_currencies: TransactionService::AvailableCurrencies::CURRENCIES,
      currency: @current_community.currency,
      display_knowledge_base_articles: APP_CONFIG.display_knowledge_base_articles,
      support_email: APP_CONFIG.support_email,
      stripe_enabled: @stripe_enabled,
      paypal_enabled: @paypal_enabled,
      stripe_account: nil,
      paypal_account: nil,
      country_name: CountryI18nHelper.translate_country(@current_community.country)
    }
  end

  PaymentPreferencesForm = FormUtils.define_form("PaymentPreferencesForm",
                                                 :commission_from_seller,
                                                 :minimum_listing_price,
                                                 :minimum_commission,
                                                 :minimum_transaction_fee,
                                                 :marketplace_currency,
                                                 :mode,
                                                 :commission_from_buyer,
                                                 :minimum_buyer_transaction_fee
  ).with_validations do
    validates_numericality_of(
      :commission_from_seller,
      only_integer: true,
      allow_nil: false,
      greater_than_or_equal_to: MIN_COMMISSION_PERCENTAGE,
      less_than_or_equal_to: MAX_COMMISSION_PERCENTAGE,
      if: proc { mode == 'transaction_fee' || mode == 'paypal' }
    )

    available_currencies = TransactionService::AvailableCurrencies::CURRENCIES
    validates_inclusion_of(:marketplace_currency, in: available_currencies)

    validate do |prefs|
      if minimum_listing_price.nil? || minimum_listing_price < (minimum_commission || 0)
        prefs.errors[:base] << I18n.t("admin.paypal_accounts.minimum_listing_price_below_min",
                                      minimum_commission: minimum_commission)
      elsif minimum_transaction_fee && minimum_listing_price < minimum_transaction_fee
        prefs.errors[:base] << I18n.t("admin.paypal_accounts.minimum_listing_price_below_tx_fee",
                                      minimum_transaction_fee: minimum_transaction_fee)
      end
    end
  end

  def update_payment_preferences
    currency = params[:payment_preferences_form]["marketplace_currency"] || @current_community.currency

    minimum_commission = @paypal_enabled ? (paypal_minimum_commissions_api.get(currency) || 0) : 0

    form = PaymentPreferencesForm.new(parse_preferences(params[:payment_preferences_form], currency).merge(minimum_commission: minimum_commission))
    if form.valid?
      ActiveRecord::Base.transaction do
        @current_community.currency = currency
        @current_community.save!

        base_params = if form.mode == 'transaction_fee'
                        {
                          community_id: @current_community.id,
                          payment_process: :preauthorize,
                          commission_from_seller: form.commission_from_seller,
                          minimum_transaction_fee_cents: form.minimum_transaction_fee.try(:cents),
                          minimum_transaction_fee_currency: currency,
                          commission_from_buyer: form.commission_from_buyer,
                          minimum_buyer_transaction_fee_cents: form.minimum_buyer_transaction_fee.try(:cents),
                          minimum_buyer_transaction_fee_currency: currency
                        }.compact
                      elsif form.mode == 'paypal'
                        {
                          community_id: @current_community.id,
                          payment_process: :preauthorize,
                          commission_from_seller: form.commission_from_seller,
                          minimum_transaction_fee_cents: form.minimum_transaction_fee.try(:cents),
                          minimum_transaction_fee_currency: currency,
                          minimum_price_cents: form.minimum_listing_price.try(:cents),
                          minimum_price_currency: currency

                        }.compact
                      else
                        {
                          community_id: @current_community.id,
                          payment_process: :preauthorize,
                          minimum_price_cents: form.minimum_listing_price.try(:cents),
                          minimum_price_currency: currency
                        }.compact
                      end

        if paypal_tx_settings.present? && (params[:gateway] == 'paypal' || form.mode == 'general')
          tx_settings_api.update(base_params.merge(payment_gateway: :paypal))
        end
        if stripe_tx_settings.present? && (params[:gateway] == 'stripe'|| form.mode == 'general')
          tx_settings_api.update(base_params.merge(payment_gateway: :stripe))
        end
      end

      if form.mode == 'transaction_fee' || form.mode == 'paypal'
        # Onboarding wizard step recording
        state_changed = Admin::OnboardingWizard.new(@current_community.id)
                                               .update_from_event(:payment_preferences_updated, @current_community)
        if state_changed
          record_event(flash, "km_record", { km_event: "Onboarding payments setup" })
          record_event(flash, "km_record", { km_event: "Onboarding paypal connected" })

          flash[:show_onboarding_popup] = true
        end
        t("admin.payment_preferences.transaction_fee_settings_updated")
      else
        t("admin.payment_preferences.general_settings_updated")
      end
    else
      raise form.errors.full_messages.join(", ")
    end
  end

  def paypal_minimum_commissions_api
    PaypalService::API::Api.minimum_commissions
  end

  def tx_settings_api
    TransactionService::API::Api.settings
  end

  def paypal_accounts_api
    PaypalService::API::Api.accounts
  end

  def parse_money_with_default(str_value, default, currency)
    if str_value.present?
      MoneyUtil.parse_str_to_money(str_value, currency)
    elsif default.present?
      Money.new(default.to_i, currency)
    end
  end

  def parse_preferences(params, currency)
    tx_settings = active_tx_setttings
    minimum_transaction_fee_cents = PaymentSettings.max_minimum_transaction_fee(@current_community)
    tx_fee =  parse_money_with_default(params[:minimum_transaction_fee], minimum_transaction_fee_cents, currency)
    tx_commission = params[:commission_from_seller] || tx_settings[:commission_from_seller]
    tx_commission = tx_commission.present? ? tx_commission.to_i : nil
    tx_min_price = parse_money_with_default(params[:minimum_listing_price], @current_community.minimum_price_cents, currency)

    commission_from_buyer = params[:commission_from_buyer] || tx_settings[:commission_from_buyer]
    commission_from_buyer = commission_from_buyer.present? ? commission_from_buyer.to_i : nil
    minimum_buyer_transaction_fee = parse_money_with_default(params[:minimum_buyer_transaction_fee], tx_settings[:minimum_buyer_transaction_fee], currency)
    {
      minimum_listing_price: tx_min_price,
      minimum_transaction_fee: tx_fee,
      commission_from_seller: tx_commission,
      marketplace_currency: currency,
      mode: params[:mode],
      commission_from_buyer: commission_from_buyer,
      minimum_buyer_transaction_fee: minimum_buyer_transaction_fee
    }
  end

  StripeApiKeysForm = FormUtils.define_form("StripeApiKeysForm",
                                            :api_private_key,
                                            :api_publishable_key).with_validations do
    validates_format_of :api_private_key, with: Regexp.new(APP_CONFIG.stripe_private_key_pattern)
    validates_format_of :api_publishable_key, with: Regexp.new(APP_CONFIG.stripe_publishable_key_pattern)
  end

  def process_update_stripe_keys
    api_form = StripeApiKeysForm.new(params[:stripe_api_keys_form])
    if api_form.valid? && api_form.api_private_key.present?
      if !@stripe_enabled
        tx_settings_api.provision({ community_id: @current_community.id,
                                    payment_process: :preauthorize,
                                    payment_gateway: :stripe,
                                    api_private_key: api_form.api_private_key,
                                    api_publishable_key: api_form.api_publishable_key
                                  })
      else
        tx_settings_api.update({ community_id: @current_community.id,
                                 payment_process: :preauthorize,
                                 payment_gateway: :stripe,
                                 api_private_key: api_form.api_private_key,
                                 api_publishable_key: api_form.api_publishable_key
                               })
      end
      if stripe_api.check_balance(community: @current_community.id)
        tx_settings_api.api_verified(community_id: @current_community.id, payment_gateway: :stripe, payment_process: :preauthorize)
        tx_settings_api.activate(community_id: @current_community.id, payment_gateway: :stripe, payment_process: :preauthorize)
      else
        tx_settings_api.disable(community_id: @current_community.id, payment_gateway: :stripe, payment_process: :preauthorize)
        raise t("admin.payment_preferences.invalid_api_keys")
      end
    else
      raise t("admin.payment_preferences.missing_api_keys")
    end
  end

  def stripe_api
    StripeService::API::Api.wrapper
  end

  def ensure_params_payment_gateway
    %w[stripe paypal].include?(params[:payment_gateway])
  end

  def can_enable_gateway?
    if params[:payment_gateway] == 'paypal'
      commission_from_buyer = stripe_tx_settings[:commission_from_buyer]
      minimum_buyer_transaction_fee_cents = stripe_tx_settings[:minimum_buyer_transaction_fee_cents]
      !((commission_from_buyer.present? && commission_from_buyer >0) ||
        (minimum_buyer_transaction_fee_cents.present? && minimum_buyer_transaction_fee_cents > 0))
    else
      true
    end
  end
end