Added IPN support, initially only covers e-checks

This commit is contained in:
Brian Quinn 2010-11-21 13:41:06 +00:00
parent 1d4caea500
commit 038ae315b3
5 changed files with 90 additions and 17 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.swp

View File

@ -1,8 +1,50 @@
class PaypalExpressCallbacksController < Admin::BaseController class PaypalExpressCallbacksController < Spree::BaseController
def index include ActiveMerchant::Billing::Integrations
render :text => "index" skip_before_filter :verify_authenticity_token
end
def show def notify
render :text => "text to render..." @notification = Paypal::Notification.new(request.raw_post)
debugger
# we only care about eChecks (for now?)
if @notification.params["payment_type"] == "echeck" && @notification.acknowledge
case @notification.params["payment_status"]
when "Denied"
retrieve_details
create_txn PaypalTxn::TxnType::DENIED
when "Completed"
retrieve_details
create_txn PaypalTxn::TxnType::CAPTURE
end
end
render :nothing => true
end end
private
def retrieve_details
@order = Order.find_by_number(@notification.params["invoice"])
@payment = @order.checkout.payments.find(:first,
:conditions => {"transactions.txn_type" => PaypalTxn::TxnType::AUTHORIZE,
"transactions.payment_type" => @notification.params["payment_type"]},
:joins => :transactions)
end
def create_txn(txn_type)
if @payment.can_finalize?
@payment.finalize!
PaypalTxn.create(:payment => @payment,
:txn_type => txn_type,
:amount => @notification.params["payment_gross"].to_f,
:payment_status => @notification.params["payment_status"],
:transaction_id => @notification.params["txn_id"],
:transaction_type => @notification.params["txn_type"],
:payment_type => @notification.params["payment_type"])
end
end
end end

View File

@ -1,5 +1,5 @@
class PaypalTxn < Transaction class PaypalTxn < Transaction
enumerable_constant :txn_type, :constants => [:authorize, :capture, :purchase, :void, :credit] enumerable_constant :txn_type, :constants => [:authorize, :capture, :purchase, :void, :credit, :denied, :unknown]
def txn_type_name def txn_type_name
TxnType.from_value(txn_type) TxnType.from_value(txn_type)

View File

@ -4,7 +4,7 @@ map.resources :orders do |order|
order.resource :checkout, :member => {:paypal_checkout => :any, :paypal_payment => :any, :paypal_confirm => :any, :paypal_finish => :any} order.resource :checkout, :member => {:paypal_checkout => :any, :paypal_payment => :any, :paypal_confirm => :any, :paypal_finish => :any}
end end
map.resources :paypal_express_callbacks, :only => [:index] map.paypal_notify "/paypal_notify", :controller => :paypal_express_callbacks, :action => :notify, :method => [:post, :get]
map.namespace :admin do |admin| map.namespace :admin do |admin|
admin.resources :orders do |order| admin.resources :orders do |order|

View File

@ -1,4 +1,3 @@
# aim to unpick this later
module Spree::PaypalExpress module Spree::PaypalExpress
include ERB::Util include ERB::Util
include ActiveMerchant::RequiresParameters include ActiveMerchant::RequiresParameters
@ -7,13 +6,21 @@ module Spree::PaypalExpress
target.before_filter :redirect_to_paypal_express_form_if_needed, :only => [:update] target.before_filter :redirect_to_paypal_express_form_if_needed, :only => [:update]
end end
# Outbound redirect to PayPal from Cart Page
# Not fully implmented or tested.
#
def paypal_checkout def paypal_checkout
load_object load_object
opts = all_opts(@order, params[:payment_method_id], 'checkout') opts = all_opts(@order, params[:payment_method_id], 'checkout')
opts.merge!(address_options(@order)) opts.merge!(address_options(@order))
gateway = paypal_gateway gateway = paypal_gateway
response = gateway.setup_authorization(opts[:money], opts) if Spree::Config[:auto_capture]
response = gateway.setup_purchase(opts[:money], opts)
else
response = gateway.setup_authorization(opts[:money], opts)
end
unless response.success? unless response.success?
gateway_error(response) gateway_error(response)
redirect_to edit_order_url(@order) redirect_to edit_order_url(@order)
@ -23,13 +30,20 @@ module Spree::PaypalExpress
redirect_to (gateway.redirect_url_for response.token, :review => payment_method.preferred_review) redirect_to (gateway.redirect_url_for response.token, :review => payment_method.preferred_review)
end end
# Outbound redirect to PayPal from checkout payments step
#
def paypal_payment def paypal_payment
load_object load_object
opts = all_opts(@order,params[:payment_method_id], 'payment') opts = all_opts(@order,params[:payment_method_id], 'payment')
opts.merge!(address_options(@order)) opts.merge!(address_options(@order))
gateway = paypal_gateway gateway = paypal_gateway
response = gateway.setup_authorization(opts[:money], opts) if Spree::Config[:auto_capture]
response = gateway.setup_purchase(opts[:money], opts)
else
response = gateway.setup_authorization(opts[:money], opts)
end
unless response.success? unless response.success?
gateway_error(response) gateway_error(response)
redirect_to edit_order_checkout_url(@order, :step => "payment") redirect_to edit_order_checkout_url(@order, :step => "payment")
@ -39,6 +53,8 @@ module Spree::PaypalExpress
redirect_to (gateway.redirect_url_for response.token, :review => payment_method.preferred_review) redirect_to (gateway.redirect_url_for response.token, :review => payment_method.preferred_review)
end end
# Inbound post from PayPal after (possible) successful completion
#
def paypal_confirm def paypal_confirm
load_object load_object
@ -92,6 +108,9 @@ module Spree::PaypalExpress
end end
end end
# Local call from A) Order Review Screen, or B) Automatically after paypal_confirm (no review).
# Completes checkout & order
#
def paypal_finish def paypal_finish
load_object load_object
@ -100,13 +119,23 @@ module Spree::PaypalExpress
if Spree::Config[:auto_capture] if Spree::Config[:auto_capture]
ppx_auth_response = gateway.purchase((@order.total*100).to_i, opts) ppx_auth_response = gateway.purchase((@order.total*100).to_i, opts)
txn_type = PaypalTxn::TxnType::CAPTURE
else else
ppx_auth_response = gateway.authorize((@order.total*100).to_i, opts) ppx_auth_response = gateway.authorize((@order.total*100).to_i, opts)
txn_type = PaypalTxn::TxnType::AUTHORIZE
end end
if ppx_auth_response.success? if ppx_auth_response.success?
#confirm status
case ppx_auth_response.params["payment_status"]
when "Completed"
txn_type = PaypalTxn::TxnType::CAPTURE
when "Pending"
txn_type = PaypalTxn::TxnType::AUTHORIZE
else
txn_type = PaypalTxn::TxnType::UNKNOWN
Rails.logger.error "Unexpected response from PayPal Express"
Rails.logger.error ppx_auth_response.to_yaml
end
paypal_account = PaypalAccount.find_by_payer_id(params[:PayerID]) paypal_account = PaypalAccount.find_by_payer_id(params[:PayerID])
payment = @order.checkout.payments.create(:amount => ppx_auth_response.params["gross_amount"].to_f, payment = @order.checkout.payments.create(:amount => ppx_auth_response.params["gross_amount"].to_f,
@ -136,13 +165,17 @@ module Spree::PaypalExpress
end end
complete_checkout complete_checkout
if Spree::Config[:auto_capture] # even with auto_capture , an Auth might be returned / forced by PPX
if Spree::Config[:auto_capture] && txn_type == PaypalTxn::TxnType::CAPTURE
payment.finalize! payment.finalize!
end end
else else
order_params = {} order_params = {}
gateway_error(ppx_auth_response) gateway_error(ppx_auth_response)
#Failed trying to complete pending payment!
redirect_to edit_order_checkout_url(@order, :step => "payment")
end end
end end
@ -173,11 +206,8 @@ module Spree::PaypalExpress
:background_color => "ffffff", # must be hex only, six chars :background_color => "ffffff", # must be hex only, six chars
:header_background_color => "ffffff", :header_background_color => "ffffff",
:header_border_color => "ffffff", :header_border_color => "ffffff",
:allow_note => true, :allow_note => true,
:locale => Spree::Config[:default_locale], :locale => Spree::Config[:default_locale],
:notify_url => 'to be done', # this is a callback, not tried it yet
:req_confirm_shipping => false, # for security, might make an option later :req_confirm_shipping => false, # for security, might make an option later
:user_action => user_action :user_action => user_action