lib/foodsoft_mollie/app/controllers/payments/mollie_ideal_controller.rb
#
# This is a quick hack to get iDEAL payments working without modifying
# foodsoft's database model. Transactions are not stored while in process,
# only on success. Failed transactions leave no trace in the database,
# but they are logged in the server log.
#
# Mollie's check url that is used contains the userid as the last path
# component, so that a financial transaction can be created on success
# for that user and ordergroup.
#
# Perhaps a cleaner approach would be to create a financial transaction
# without amount zero when the payment process starts, and keep track
# of the state using that. Then the transaction id would be enough to
# process it, and also an error message could be given.
#
# Or start using activemerchant - e.g.
# https://github.com/moneybird/active_merchant_mollie
#
class Payments::MollieIdealController < ApplicationController
before_filter -> { require_plugin_enabled FoodsoftMollie }
skip_before_filter :authenticate, :only => [:check]
before_filter :get_ordergroup, only: [:new, :create]
before_filter :accept_return_to, only: [:new]
def new
set_mollie_cfg
@banks = IdealMollie.banks
@amount = (params[:amount] or [0, -@ordergroup.get_available_funds].min)
@amount = [params[:min], @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,v| %w(amount label title fixed min text).include?(k)}
bank_id = params[:bank_id]
amount = params[:amount].to_f
amount = [params[:min].to_f, amount].max if params[:min]
set_mollie_cfg
IdealMollie::Config.return_url = result_payments_mollie_url
IdealMollie::Config.report_url = check_payments_mollie_url(:id => @current_user.id)
request = IdealMollie.new_order(
amount: (amount*100.0).to_i,
description: "#{@current_user.ordergroup.id}, #{FoodsoftConfig[:name]}",
bank_id: bank_id
)
transaction_id = request.transaction_id
logger.info "Mollie start: #{amount} for \##{@current_user.id} (#{@current_user.display}) with bank #{bank_id}"
redirect_to request.url
end
def check
set_mollie_cfg
transaction_id = params[:transaction_id]
response = IdealMollie.check_order(transaction_id)
logger.info "Mollie check: #{response.inspect}"
if response.paid
user = User.find(params[:id])
notice = self.ideal_note(transaction_id)
amount = response.amount/100.0
@transaction = FinancialTransaction.new(:user=>user, :ordergroup=>user.ordergroup, :amount=>amount, :note=>notice)
@transaction.add_transaction!
end
render :nothing => true
end
def result
transaction_id = params[:transaction_id]
@transaction = FinancialTransaction.where(:note => self.ideal_note(transaction_id)).first
if @transaction
logger.info "Mollie result: transaction #{transaction_id} succeeded"
redirect_to_return_or root_path, :notice => I18n.t('payments.mollie_ideal.controller.result.notice')
else
logger.info "Mollie result: transaction #{transaction_id} 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
end
def cancel
redirect_to_return_or root_path
end
protected
def ideal_note(transaction_id)
# this is _not_ translated, because this exact string is used to find the transaction
"iDEAL payment (Mollie #{transaction_id})"
end
def get_ordergroup
# TODO what if the current user doesn't have one?
@ordergroup = current_user.ordergroup
end
def set_mollie_cfg
if mcfg = FoodsoftConfig[:mollie].try(&:symbolize_keys)
IdealMollie::Config.partner_id = mcfg[:partner_id] if mcfg[:partner_id]
IdealMollie::Config.profile_key = mcfg[:profile_key] if mcfg[:profile_key]
IdealMollie::Config.test_mode = mcfg[:test_mode] if mcfg[:test_mode]
end
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 unless params[:return_to].present?
if params[:return_to].starts_with?(root_path) or params[:return_to].starts_with?(root_url)
session[:return_to] = params[:return_to]
end
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
end