add mollie plugin
This commit is contained in:
parent
55234b4e27
commit
c59eefd219
21 changed files with 1117 additions and 0 deletions
|
|
@ -0,0 +1,160 @@
|
|||
# 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 [10, -@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]
|
||||
|
||||
redirect_to new_payments_mollie_path(session[:mollie_params]), alert: t('.invalid_amount') and return if amount <= 0
|
||||
|
||||
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: t('.controller.transaction_note', method: nil)
|
||||
)
|
||||
|
||||
payment = Mollie::Payment.create(
|
||||
amount: {
|
||||
value: format('%.2f', amount),
|
||||
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 => 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 => t('.controller.result.notice', amount: @transaction.amount, fee: @transaction.payment_fee)
|
||||
when 'pending'
|
||||
redirect_to_return_or root_path, :notice => t('.controller.result.wait')
|
||||
else
|
||||
pms = { foodcoop: FoodsoftConfig.scope }.merge((session[:mollie_params] or {}))
|
||||
session[:mollie_params] = nil
|
||||
redirect_to new_payments_mollie_path(pms), :alert => t('.controller.result.failed')
|
||||
end
|
||||
end
|
||||
|
||||
def cancel
|
||||
redirect_to_return_or root_path
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def ordergroup
|
||||
@ordergroup = current_user.ordergroup
|
||||
redirect_to root_path, alert: t('.no_ordergroup') and return if @ordergroup.nil?
|
||||
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 = t('.controller.transaction_note', method: payment.method)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def configure_api_key
|
||||
Mollie::Client.configure do |config|
|
||||
config.api_key = FoodsoftConfig[:mollie]['api_key']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
/ insert_after 'erb:contains(":webstats_tracking_code")'
|
||||
%h4= 'Mollie'
|
||||
= config_input form, :use_mollie, as: :boolean
|
||||
= form.fields_for :mollie do |fields|
|
||||
= config_input fields, :api_key, input_html: {class: 'input-xlarge'}
|
||||
= config_input fields, :financial_transaction_type, :as => :select, :collection => FinancialTransactionType.order(:name).map { |t| [ t.name, t.id ] }
|
||||
%h5= 'Fee'
|
||||
= fields.fields_for :fee do |fields|
|
||||
= config_input fields, :ideal, input_html: {class: 'input-xlarge'}
|
||||
= config_input fields, :banktransfer, input_html: {class: 'input-xlarge'}
|
||||
= config_input fields, :creditcard, input_html: {class: 'input-xlarge'}
|
||||
= config_input fields, :paypal, input_html: {class: 'input-xlarge'}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
/ insert_after 'erb[silent]:contains("<dashboard_ordergroup_mark>")'
|
||||
= link_to new_payments_mollie_path do
|
||||
= t '.credit_your_account'
|
||||
%i.icon.icon-chevron-right
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
/ insert_after 'erb[silent]:contains("<home_ordergroup_well_mark>")'
|
||||
= link_to t('.credit_your_account'), new_payments_mollie_path, class: 'btn btn-secondary'
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
- content_for :javascript do
|
||||
:javascript
|
||||
// @todo code duplication of foodsoft_mollie.rb
|
||||
function paymentFee(amount, feeSpec) {
|
||||
if (typeof feeSpec === 'number') {
|
||||
return feeSpec;
|
||||
}
|
||||
|
||||
return feeSpec.split('+').reduce((sum, c) => {
|
||||
const parsed = c.trim().match(/^(.*)\s*%\s*$/);
|
||||
if (parsed) {
|
||||
return sum + (parseFloat(parsed[1]) / 100 * parseFloat(amount));
|
||||
} else {
|
||||
return sum + parseFloat(c);
|
||||
}
|
||||
}, 0).toFixed(2);
|
||||
}
|
||||
|
||||
function handleInputAmount(){
|
||||
var amount = parseFloat($('#amount').val());
|
||||
var isError = false;
|
||||
$('#fee_list').children('#fee').each(function(){
|
||||
var fee = $(this).data('fee');
|
||||
if (amount){
|
||||
$(this).text(I18n.l("currency", paymentFee(amount, fee)));
|
||||
} else {
|
||||
$(this).text(fee);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$('#amount').on('keyup', handleInputAmount);
|
||||
$(document).ready(handleInputAmount);
|
||||
|
||||
%h5= t '.fee'
|
||||
%dl.dl-horizontal#fee_list
|
||||
- FoodsoftConfig[:mollie][:fee].each do |k, v|
|
||||
%dt= k
|
||||
%dd{id: "fee", data: { fee: v}}"
|
||||
= form_tag payments_mollie_path, method: :post do
|
||||
- if params[:text]
|
||||
.well= params[:text]
|
||||
.control-group
|
||||
.control-label
|
||||
= label_tag 'amount', ((params[:label] or t('.amount_pay')))
|
||||
.controls
|
||||
.input-prepend
|
||||
%span.add-on= t 'number.currency.format.unit'
|
||||
= text_field_tag 'amount', @amount, readonly: (params[:fixed]=='true'), class: 'input-mini'
|
||||
- if params[:min]
|
||||
.help-inline{style: 'margin-bottom: 10px'}
|
||||
= "(min #{number_to_currency params[:min], precision: 0})"
|
||||
.control-group
|
||||
.controls
|
||||
= submit_tag t('.submit')
|
||||
= link_to t('ui.or_cancel'), cancel_payments_mollie_path
|
||||
|
||||
-# pass through options to allow reusing on error
|
||||
- %w(label title fixed min text).each do |k|
|
||||
= hidden_field_tag k, params[k] if params[k]
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
- title (params[:title] or t('.title'))
|
||||
= render :partial => 'form'
|
||||
Loading…
Add table
Add a link
Reference in a new issue