166 lines
6.2 KiB
Ruby
166 lines
6.2 KiB
Ruby
# Mollie payment page
|
|
class Payments::MollieIdealController < ApplicationController
|
|
before_action -> { require_plugin_enabled FoodsoftMollie }
|
|
skip_before_action :authenticate, :only => [:check]
|
|
skip_before_action :verify_authenticity_token, :only => [:check]
|
|
before_action :accept_return_to, only: [:new]
|
|
before_action :ordergroup, only: [:new, :create, :result]
|
|
before_action :transaction, only: [:result]
|
|
before_action :configure_api_key
|
|
|
|
def new
|
|
@amount = (params[:amount] or [0, -@ordergroup.get_available_funds].max)
|
|
@amount = [params[:min], params[:amount]].max if params[:min]
|
|
end
|
|
|
|
def create
|
|
# store parameters so we can redirect to original form on problems
|
|
session[:mollie_params] = params.select { |k, _| %w[amount label title fixed min text].include?(k) }.to_h
|
|
|
|
amount = params[:amount].to_f
|
|
amount = [params[:min].to_f, amount].max if params[:min]
|
|
|
|
# @todo check amount > transaction feex
|
|
|
|
ft_type = FinancialTransactionType.find_by_id(FoodsoftConfig[:mollie][:financial_transaction_type]) || FinancialTransactionType.first
|
|
|
|
@transaction = FinancialTransaction.create!(
|
|
amount: nil,
|
|
ordergroup: @ordergroup,
|
|
user: @current_user,
|
|
payment_plugin: 'mollie',
|
|
payment_amount: amount,
|
|
# @todo payment_currency
|
|
payment_state: 'created',
|
|
financial_transaction_type_id: ft_type.id,
|
|
note: I18n.t('payments.mollie_ideal.controller.transaction_note', method: nil),
|
|
|
|
)
|
|
|
|
payment = Mollie::Payment.create(
|
|
amount: {
|
|
value: '%.2f' % [amount], # @todo use format over %
|
|
currency: 'EUR' # @todo payment_currency
|
|
},
|
|
description: "#{@transaction.id}, #{@ordergroup.id}, #{FoodsoftConfig[:name]}",
|
|
redirectUrl: result_payments_mollie_url(id: @transaction.id),
|
|
webhookUrl: (request.local? ? check_payments_mollie_url : 'https://example.com'), # Mollie doesn't accept localhost here
|
|
metadata: {
|
|
scope: FoodsoftConfig.scope,
|
|
transaction_id: @transaction.id,
|
|
user: @current_user.id,
|
|
ordergroup: @ordergroup.id
|
|
}
|
|
)
|
|
@transaction.update payment_id: payment.id, payment_state: 'pending'
|
|
|
|
logger.info "Mollie start: #{amount} for ##{@current_user.id} (#{@current_user.display})"
|
|
|
|
redirect_to payment.checkout_url, allow_other_host: true
|
|
rescue Mollie::Exception => e
|
|
Rails.logger.info "Mollie create warning: #{e}"
|
|
redirect_to new_payments_mollie_path(session[:mollie_params]), :alert => I18n.t('errors.general_msg', msg: e.message)
|
|
end
|
|
|
|
# Endpoint that Mollie calls when a payment status changes.
|
|
def check
|
|
logger.info "Mollie check: #{params[:id]}"
|
|
@transaction = FinancialTransaction.find_by_payment_plugin_and_payment_id!('mollie', params[:id])
|
|
logger.debug " financial transaction: #{@transaction.inspect}"
|
|
render plain: update_transaction(@transaction)
|
|
rescue StandardError => e
|
|
Rails.logger.error "Mollie check error: #{e}"
|
|
render plain: "Error: #{e.message}"
|
|
end
|
|
|
|
# User is redirect here after payment
|
|
def result
|
|
update_transaction @transaction # @todo if request.local? # so localhost works too
|
|
logger.info "Mollie result: transaction #{@transaction.id} (#{@transaction.payment_id}) is #{@transaction.payment_state}"
|
|
case @transaction.payment_state
|
|
when 'paid'
|
|
redirect_to_return_or root_path, :notice => I18n.t('payments.mollie_ideal.controller.result.notice', amount: @transaction.amount, fee: @transaction.payment_fee)
|
|
when 'pending'
|
|
redirect_to_return_or root_path, :notice => I18n.t('payments.mollie_ideal.controller.result.wait')
|
|
else
|
|
payment_is_failed
|
|
end
|
|
end
|
|
|
|
def payment_is_failed
|
|
# redirect to form with same parameters as original page
|
|
pms = { foodcoop: FoodsoftConfig.scope }.merge((session[:mollie_params] or {}))
|
|
session[:mollie_params] = nil
|
|
redirect_to new_payments_mollie_path(pms), :alert => I18n.t('payments.mollie_ideal.controller.result.failed') # TODO: recall check's response.message
|
|
end
|
|
|
|
def cancel
|
|
redirect_to_return_or root_path
|
|
end
|
|
|
|
protected
|
|
|
|
def ordergroup
|
|
# @todo what if the current user doesn't have one?
|
|
@ordergroup = current_user.ordergroup
|
|
end
|
|
|
|
def transaction
|
|
@transaction = @ordergroup.financial_transactions.find(params[:id])
|
|
end
|
|
|
|
# @todo move this to ApplicationController, use it in SessionController too
|
|
# @todo use a stack of return_to urls
|
|
def accept_return_to
|
|
session[:return_to] = nil # or else an unfollowed previous return_to may interfere
|
|
return if params[:return_to].blank?
|
|
|
|
return unless params[:return_to].starts_with?(root_path) || params[:return_to].starts_with?(root_url)
|
|
|
|
session[:return_to] = params[:return_to]
|
|
end
|
|
|
|
def redirect_to_return_or(fallback_url, options = {})
|
|
if session[:return_to].present?
|
|
redirect_to_url = session[:return_to]
|
|
session[:return_to] = nil
|
|
else
|
|
redirect_to_url = fallback_url
|
|
end
|
|
redirect_to redirect_to_url, options
|
|
end
|
|
|
|
# Query Mollie status and update financial transaction
|
|
def update_transaction(transaction)
|
|
payment = Mollie::Payment.get transaction.payment_id
|
|
logger.debug "Mollie update_transaction: #{payment.inspect}"
|
|
update_transaction_details(transaction, payment)
|
|
update_transaction_amount(transaction, payment)
|
|
payment.status
|
|
end
|
|
|
|
def update_transaction_amount(transaction, payment)
|
|
if payment.status == 'paid'
|
|
payment_fee = FoodsoftMollie.payment_fee payment.amount.value, transaction.payment_method
|
|
amount = payment.amount.value.to_f - payment_fee.to_f
|
|
end
|
|
transaction.update payment_state: payment.status, amount: amount || 0, payment_fee: payment_fee || nil
|
|
end
|
|
|
|
def update_transaction_details(transaction, payment)
|
|
if payment.details
|
|
transaction.payment_acct_number = payment.details.consumerAccount
|
|
transaction.payment_acct_name = payment.details.consumerName
|
|
end
|
|
transaction.payment_method = payment.method
|
|
transaction.note = I18n.t('payments.mollie_ideal.controller.transaction_note', method: payment.method) # @todo add amount + fee to note
|
|
end
|
|
|
|
private
|
|
|
|
def configure_api_key
|
|
Mollie::Client.configure do |config|
|
|
config.api_key = FoodsoftConfig[:mollie]['api_key']
|
|
end
|
|
end
|
|
end
|