forked from foodcoops/foodsoft
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
42 changed files
with
2,002 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
.bundle/ | ||
log/*.log | ||
pkg/ | ||
test/dummy/db/*.sqlite3 | ||
test/dummy/log/*.log | ||
test/dummy/tmp/ | ||
test/dummy/.sass-cache |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
source "http://rubygems.org" | ||
|
||
# Declare your gem's dependencies in foodsoft_adyen.gemspec. | ||
# Bundler will treat runtime dependencies like base dependencies, and | ||
# development dependencies will be added by default to the :development group. | ||
gemspec |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
= FoodsoftAdyen | ||
|
||
This project adds support for Adyen payments to Foodsoft. | ||
|
||
* Make sure the gem is uncommented in foodsoft's `Gemfile` | ||
* Enter your Adyen account details in `config/environments/production.rb` (or `development.rb`) | ||
|
||
|
||
== Configuration | ||
|
||
The Adyen notifications API is used to credit accounts. That means that you | ||
need to enable notifications the Adyen customer area: | ||
|
||
* Log into https://ca-live.adyen.com/ | ||
* Choose your merchant account | ||
* Go to `Settings` then `Notifications` | ||
* Enter the following settings: | ||
* URL: `https://your.foodsoft.host/path/f/payments/adyen/notify` | ||
* Active: `yes` | ||
* Method: `HTTP POST (parameters)` | ||
* Populate SOAP header: `no` | ||
* In `Authentication`, specify the user and password you set earlier in | ||
your environment configuration in `config.foodsoft_adyen.notify_username` and | ||
`config.foodsoft_adyen.notify_password`. It anyone knows these, they can | ||
credit accounts, so do make sure to use a long password that's impossible to | ||
remember. | ||
* Press `Save Settings` | ||
|
||
Now use the option to test a notification (below in the same Adyen CA screen). | ||
Check your rails log file to see if the notification was received properly. | ||
|
||
|
||
== PIN payment flow | ||
|
||
PIN payments are done using the mobile Adyen app. At the time of writing, this is | ||
somewhat new, and the app may not be fully stabilised yet; but it should be usable. | ||
|
||
The flow is like this: | ||
|
||
1. In `/f/payments/adyen/pin`, an ordergroup is selected. | ||
2. This redirects to the Adyen app on the mobile platform. The query string in the | ||
redirect is used to pass amount and description. | ||
3. The Adyen app processes the payment. | ||
4. The Adyen app redirects to the foodsoft callback page, with a `result` parameter | ||
indicating success or failure. | ||
5. The foodsoft user-interface shows whether it succeeded or not. No financial | ||
transaction is added, however, since that is taken care of by the | ||
Adyen HTTP POST notification. This also makes sure that no url tampering | ||
on the browser can be used to credit accounts without real payments. | ||
6. Shortly after, that might take a couple of minutes, Adyen will call the | ||
notification URL, where the user's account can be updated. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
#!/usr/bin/env rake | ||
begin | ||
require 'bundler/setup' | ||
rescue LoadError | ||
puts 'You must `gem install bundler` and `bundle install` to run rake tasks' | ||
end | ||
begin | ||
require 'rdoc/task' | ||
rescue LoadError | ||
require 'rdoc/rdoc' | ||
require 'rake/rdoctask' | ||
RDoc::Task = Rake::RDocTask | ||
end | ||
|
||
RDoc::Task.new(:rdoc) do |rdoc| | ||
rdoc.rdoc_dir = 'rdoc' | ||
rdoc.title = 'FoodsoftAdyen' | ||
rdoc.options << '--line-numbers' | ||
rdoc.rdoc_files.include('README.rdoc') | ||
rdoc.rdoc_files.include('lib/**/*.rb') | ||
end | ||
|
||
#APP_RAKEFILE = File.expand_path("../../../Rakefile", __FILE__) | ||
#load 'rails/tasks/engine.rake' | ||
|
||
Bundler::GemHelper.install_tasks | ||
|
8 changes: 8 additions & 0 deletions
8
lib/foodsoft_adyen/app/assets/javascripts/payments/adyen_mobile.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
/* | ||
* Javascript specific to mobile devices | ||
*/ | ||
//= require jquery.mobile | ||
|
||
$(function() { | ||
$('[data-role="popup"][data-immediate="true"]').popup('open'); | ||
}); |
3 changes: 3 additions & 0 deletions
3
lib/foodsoft_adyen/app/assets/stylesheets/payments/adyen_mobile.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
/* | ||
*= require jquery.mobile | ||
*/ |
68 changes: 68 additions & 0 deletions
68
lib/foodsoft_adyen/app/controllers/payments/adyen_notifications_controller.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
require 'base64' | ||
|
||
class Payments::AdyenNotificationsController < ApplicationController | ||
skip_before_filter :verify_authenticity_token, :only => [:notify] | ||
skip_before_filter :authenticate, :only => [:notify] | ||
before_filter :authenticate_adyen, :only => [:notify] | ||
|
||
class WrongCurrencyException < Exception; end | ||
class UserNotFoundException < Exception; end | ||
class NotificationDataException < Exception; end | ||
|
||
def notify | ||
notification = AdyenNotification.log(params) | ||
if notification.successful_authorisation? | ||
data = decode_notification_data(notification.merchant_reference) | ||
(user = User.find(data[:g])) rescue raise UserNotFoundException | ||
notification.currency == Rails.configuration.foodsoft_adyen.currency or raise WrongCurrencyException | ||
notice = "#{notification.payment_method} payment (Adyen #{notification.psp_reference})" | ||
amount = notification.value/100.0 | ||
@transaction = FinancialTransaction.new(:user=>user, :ordergroup=>user.ordergroup, :amount=>amount, :note=>notice) | ||
@transaction.add_transaction! | ||
logger.debug 'foodsoft_adyen: handled authorisation notification' | ||
else | ||
logger.debug 'foodsoft_adyen: nothing to do' | ||
end | ||
ws_return :accepted | ||
rescue NotificationDataException => e | ||
ws_return :rejected, "merchant_reference #{e}" | ||
rescue UserNotFoundException | ||
ws_return :rejected, 'merchant_reference does not contain a valid user' | ||
rescue WrongCurrencyException | ||
ws_return :rejected, "foodsoft_adyen configuration only accepts currency #{Rails.configuration.foodsoft_adyen.currency}" | ||
rescue ActiveRecord::RecordNotUnique, ActiveRecord::RecordInvalid => e | ||
# Validation failed, because of the duplicate check. | ||
# So ignore this notification, it is already stored and handled. | ||
logger.debug 'foodsoft_adyen: notification already handled, ignoring' | ||
ws_return :accepted | ||
rescue Exception => e | ||
ws_return :error, e | ||
end | ||
|
||
protected | ||
def authenticate_adyen | ||
authenticate_or_request_with_http_basic do |username, password| | ||
username == Rails.configuration.foodsoft_adyen.notify_username && password == Rails.configuration.foodsoft_adyen.notify_password | ||
end | ||
end | ||
|
||
def ws_return(status, msg=nil) | ||
if status == :rejected | ||
logger.warn "foodsoft_adyen: #{msg}" | ||
elsif status == :error | ||
logger.error msg | ||
logger.error(Rails.backtrace_cleaner.clean(msg.backtrace).map{|x| " #{x}"}.join("\n")) if msg.is_a? Exception | ||
end | ||
render :text => ("[#{status}]" + (msg.nil? ? '' : " #{msg}")) | ||
end | ||
|
||
# returns hash of foodsoft data for transaction | ||
def decode_notification_data(data) | ||
ActiveSupport::JSON.decode Base64.urlsafe_decode64(data.gsub(/^.*\((.*)\)\s*$/,'\1')), {symbolize_names: true} | ||
rescue ActiveSupport::JSON.parse_error | ||
raise NotificationDataException.new('does not contain valid JSON') | ||
rescue ArgumentError | ||
raise NotificationDataException.new('is not a URL-safe base64 encoded string (RFC 4648)') | ||
end | ||
|
||
end |
62 changes: 62 additions & 0 deletions
62
lib/foodsoft_adyen/app/controllers/payments/adyen_pin_controller.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
require 'base64' | ||
|
||
class Payments::AdyenPinController < ApplicationController | ||
before_filter :find_ordergroup | ||
layout 'adyen_mobile' | ||
|
||
# show list of ordergroups | ||
def index | ||
@ordergroups = Ordergroup.undeleted | ||
@ordergroups = @ordergroups.where('name LIKE ?', "%#{params[:query]}%") unless params[:query].nil? | ||
@ordergroups = @ordergroups.page(params[:page]).per(@per_page) | ||
end | ||
|
||
# show form for initiating a new payment | ||
def new | ||
#@adyen_pin_url = adyen_pin_url(@ordergroup.id, 4.99, 'hi there') | ||
create | ||
end | ||
|
||
# initiate pin payment using Adyen app | ||
def create | ||
redirect_to adyen_pin_url(@ordergroup, @ordergroup.get_available_funds) | ||
end | ||
|
||
# callback url after payment | ||
def created | ||
index | ||
render :index | ||
end | ||
|
||
|
||
protected | ||
|
||
def find_ordergroup | ||
@ordergroup = Ordergroup.find(params[:ordergroup_id]) rescue nil | ||
end | ||
|
||
|
||
private | ||
|
||
def adyen_pin_url(ordergroup, amount) | ||
opts = { | ||
currency: Rails.configuration.foodsoft_adyen.currency, | ||
amount: (amount * 100).to_i, | ||
description: encode_notification_data({g: ordergroup.id}, ordergroup.name), | ||
callback: created_payments_adyen_pin_url(:ordergroup_id => ordergroup.id), # or use opt sessionId | ||
callbackAutomatic: 0, | ||
start_immediately: true | ||
} | ||
if request.user_agent.match '\bAndroid\b' | ||
return "http://www.adyen.com/android-app/payment?#{opts.to_query}" | ||
else #elsif request.user_agent.match '\b(iPod|iPhone|iPad)\b' | ||
return "adyen://payment?#{opts.to_query}" | ||
end | ||
end | ||
|
||
def encode_notification_data(data, title=nil) | ||
d = Base64.urlsafe_encode64 data.to_json | ||
return [title, "(#{d})"].compact.join(' ') | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# The +AdyenNotification+ class handles notifications sent by Adyen to your servers. | ||
# | ||
# Because notifications contain important payment status information, you should store | ||
# these notifications in your database. For this reason, +AdyenNotification+ inherits | ||
# from +ActiveRecord::Base+, and a migration is included to simply create a suitable table | ||
# to store the notifications in. | ||
# | ||
# Adyen can either send notifications to you via HTTP POST requests, or SOAP requests. | ||
# Because SOAP is not really well supported in Rails and setting up a SOAP server is | ||
# not trivial, only handling HTTP POST notifications is currently supported. | ||
# | ||
# @example | ||
# @notification = AdyenNotification.log(request) | ||
# if @notification.successful_authorisation? | ||
# @invoice = Invoice.find(@notification.merchant_reference) | ||
# @invoice.set_paid! | ||
# end | ||
class AdyenNotification < ActiveRecord::Base | ||
|
||
# A notification should always include an event_code | ||
validates_presence_of :event_code | ||
|
||
# A notification should always include a psp_reference | ||
validates_presence_of :psp_reference | ||
|
||
# A notification should be unique using the composed key of | ||
# [:psp_reference, :event_code, :success] | ||
validates_uniqueness_of :success, :scope => [:psp_reference, :event_code] | ||
|
||
# Make sure we don't end up with an original_reference with an empty string | ||
before_validation { |notification| notification.original_reference = nil if notification.original_reference.blank? } | ||
|
||
# Logs an incoming notification into the database. | ||
# | ||
# @param [Hash] params The notification parameters that should be stored in the database. | ||
# @return [Adyen::Notification] The initiated and persisted notification instance. | ||
# @raise This method will raise an exception if the notification cannot be stored. | ||
# @see Adyen::Notification::HttpPost.log | ||
def self.log(params) | ||
converted_params = {} | ||
|
||
# Assign explicit each attribute from CamelCase notation to notification | ||
# For example, merchantReference will be converted to merchant_reference | ||
self.new.tap do |notification| | ||
params.each do |key, value| | ||
setter = "#{key.to_s.underscore}=" | ||
notification.send(setter, value) if notification.respond_to?(setter) | ||
end | ||
notification.save! | ||
end | ||
end | ||
|
||
# Returns true if this notification is an AUTHORISATION notification | ||
# @return [true, false] true iff event_code == 'AUTHORISATION' | ||
# @see Adyen.notification#successful_authorisation? | ||
def authorisation? | ||
event_code == 'AUTHORISATION' | ||
end | ||
|
||
alias_method :authorization?, :authorisation? | ||
|
||
# Returns true if this notification is an AUTHORISATION notification and | ||
# the success status indicates that the authorization was successfull. | ||
# @return [true, false] true iff the notification is an authorization | ||
# and the authorization was successful according to the success field. | ||
def successful_authorisation? | ||
authorisation? && success? | ||
end | ||
|
||
alias_method :successful_authorization?, :successful_authorisation? | ||
end |
18 changes: 18 additions & 0 deletions
18
lib/foodsoft_adyen/app/views/layouts/adyen_mobile.html.haml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
- content_for :head do | ||
= stylesheet_link_tag "payments/adyen_mobile", :media => "all" | ||
|
||
- content_for :javascript do | ||
= javascript_include_tag "payments/adyen_mobile" | ||
|
||
= render layout: 'layouts/header' do | ||
%div{:data => {:role => :page}} | ||
|
||
- if show_title? | ||
%div{:data => {:role => :header, :position => :fixed}} | ||
%h1= yield(:title) | ||
|
||
%div#messages | ||
= bootstrap_flash | ||
|
||
%div{:data => {:role => :content}} | ||
= yield |
21 changes: 21 additions & 0 deletions
21
lib/foodsoft_adyen/app/views/payments/adyen_pin/_created.html.haml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
- if params[:result] == 'APPROVED' | ||
%h3.ui-title Payment done | ||
%p Thanks! Your account will be updated in a couple of minutes. | ||
|
||
- elsif params[:result] == 'CANCELLED' | ||
%h3.ui-title Payment cancelled | ||
%p The transaction was cancelled. #{params[:cancelMessage]} | ||
|
||
- elsif params[:result] == 'DECLINED' | ||
%h3.ui-title Payment declined | ||
%p I'm sorry, it didn't work out. #{params[:declineMessage]} | ||
|
||
- else | ||
%h3.ui-title Payment failed | ||
%p Something went wrong, sorry! #{params[:errorMessage]} | ||
|
||
|
||
= link_to t('ui.close'), '#', :data => { :role => :button, :rel => :back, :theme => 'b' } | ||
- if params[:result] != 'APPROVED' and @ordergroup | ||
= link_to 'Retry', new_payments_adyen_pin_path(:ordergroup_id => @ordergroup.id), :data => {:role => :button, :ajax => :false} | ||
|
6 changes: 6 additions & 0 deletions
6
lib/foodsoft_adyen/app/views/payments/adyen_pin/_ordergroups.html.haml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
%ul{:data => {:role => :listview, :filter => :true}} | ||
- for ordergroup in @ordergroups | ||
%li | ||
= link_to new_payments_adyen_pin_path(:ordergroup_id => ordergroup.id), :data => { :ajax => :false } do | ||
= ordergroup.name | ||
%p.ui-li-aside= ordergroup.users.map(&:name).join ',' |
18 changes: 18 additions & 0 deletions
18
lib/foodsoft_adyen/app/views/payments/adyen_pin/index.html.haml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
- title t('.title') | ||
|
||
-# payment response dialog | ||
- unless params[:result].blank? | ||
%div#paymentResult{:data => {:role => :popup, 'overlay-theme' => 'a', :shadow => :true, :immediate => :true, :transition => 'pop'}} | ||
%div{:data => {:role => :content}} | ||
= render :partial => "created" | ||
|
||
- content_for :javascript do | ||
:javascript | ||
// auto-close dialog so next member can use it | ||
setTimeout(function() { | ||
$('#paymentResult').popup('close'); | ||
}, 8*1000); | ||
|
||
#orderGroupsTable | ||
= render :partial => "ordergroups" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
$('#ordergroupsTable').html('#{escape_javascript(render("ordergroups"))}'); |
4 changes: 4 additions & 0 deletions
4
lib/foodsoft_adyen/app/views/payments/adyen_pin/new.html.haml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
- title t('.title') | ||
|
||
= link_to "do the payment", @adyen_pin_url | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
en: | ||
payments: | ||
navigation: | ||
pin: PIN terminal | ||
adyen_pin: | ||
index: | ||
title: PIN payment |
Oops, something went wrong.