2011-12-07 22:00:27 +00:00
module Spree
CheckoutController . class_eval do
before_filter :redirect_to_paypal_express_form_if_needed , :only = > [ :update ]
def paypal_checkout
load_order
opts = all_opts ( @order , params [ :payment_method_id ] , 'checkout' )
opts . merge! ( address_options ( @order ) )
@gateway = paypal_gateway
if Spree :: Config [ :auto_capture ]
@ppx_response = @gateway . setup_purchase ( opts [ :money ] , opts )
else
@ppx_response = @gateway . setup_authorization ( opts [ :money ] , opts )
end
2010-11-21 13:41:06 +00:00
2011-12-07 22:00:27 +00:00
unless @ppx_response . success?
gateway_error ( @ppx_response )
redirect_to edit_order_url ( @order )
return
end
2011-12-16 23:28:20 +00:00
redirect_to ( @gateway . redirect_url_for ( response . token , :review = > payment_method . preferred_review ) )
2011-12-07 22:00:27 +00:00
rescue ActiveMerchant :: ConnectionError = > e
gateway_error I18n . t ( :unable_to_connect_to_gateway )
redirect_to :back
2010-01-22 16:29:55 +00:00
end
2009-07-15 09:26:43 +00:00
2011-12-07 22:00:27 +00:00
def paypal_payment
load_order
opts = all_opts ( @order , params [ :payment_method_id ] , 'payment' )
opts . merge! ( address_options ( @order ) )
@gateway = paypal_gateway
if Spree :: Config [ :auto_capture ]
@ppx_response = @gateway . setup_purchase ( opts [ :money ] , opts )
else
@ppx_response = @gateway . setup_authorization ( opts [ :money ] , opts )
end
2009-07-06 10:57:58 +00:00
2011-12-07 22:00:27 +00:00
unless @ppx_response . success?
gateway_error ( @ppx_response )
redirect_to edit_order_checkout_url ( @order , :state = > " payment " )
return
end
2009-05-14 10:11:59 +00:00
2011-12-16 23:28:20 +00:00
redirect_to ( @gateway . redirect_url_for ( @ppx_response . token , :review = > payment_method . preferred_review ) )
2011-12-07 22:00:27 +00:00
rescue ActiveMerchant :: ConnectionError = > e
gateway_error I18n . t ( :unable_to_connect_to_gateway )
redirect_to :back
2010-11-21 13:41:06 +00:00
end
2011-12-07 22:00:27 +00:00
def paypal_confirm
load_order
2009-07-15 09:26:43 +00:00
2011-12-07 22:00:27 +00:00
opts = { :token = > params [ :token ] , :payer_id = > params [ :PayerID ] } . merge all_opts ( @order , params [ :payment_method_id ] , 'payment' )
gateway = paypal_gateway
2009-05-14 10:11:59 +00:00
2011-12-07 22:00:27 +00:00
@ppx_details = gateway . details_for params [ :token ]
2009-09-07 11:10:32 +00:00
2011-12-07 22:00:27 +00:00
if @ppx_details . success?
# now save the updated order info
2009-05-14 10:11:59 +00:00
2011-12-07 22:00:27 +00:00
Spree :: PaypalAccount . create ( :email = > @ppx_details . params [ " payer " ] ,
2011-12-16 23:30:15 +00:00
:payer_id = > @ppx_details . params [ " payer_id " ] ,
:payer_country = > @ppx_details . params [ " payer_country " ] ,
:payer_status = > @ppx_details . params [ " payer_status " ] )
2010-01-28 16:48:01 +00:00
2011-12-07 22:00:27 +00:00
@order . special_instructions = @ppx_details . params [ " note " ]
2010-02-24 20:43:18 +00:00
2011-12-07 22:00:27 +00:00
unless payment_method . preferred_no_shipping
ship_address = @ppx_details . address
order_ship_address = Spree :: Address . new :firstname = > @ppx_details . params [ " first_name " ] ,
2011-12-16 23:30:15 +00:00
:lastname = > @ppx_details . params [ " last_name " ] ,
:address1 = > ship_address [ " address1 " ] ,
:address2 = > ship_address [ " address2 " ] ,
:city = > ship_address [ " city " ] ,
:country = > Spree :: Country . find_by_iso ( ship_address [ " country " ] ) ,
:zipcode = > ship_address [ " zip " ] ,
# phone is currently blanked in AM's PPX response lib
:phone = > @ppx_details . params [ " phone " ] || " (not given) "
2010-02-24 20:43:18 +00:00
2011-12-07 22:00:27 +00:00
if ( state = Spree :: State . find_by_abbr ( ship_address [ " state " ] ) )
order_ship_address . state = state
else
order_ship_address . state_name = ship_address [ " state " ]
end
order_ship_address . save!
2010-03-05 11:59:20 +00:00
2011-12-07 22:00:27 +00:00
@order . ship_address = order_ship_address
2011-12-16 23:30:47 +00:00
@order . bill_address || = order_ship_address
2011-12-07 22:00:27 +00:00
end
2012-05-24 21:02:15 +00:00
@order . state = " payment "
2011-12-07 22:00:27 +00:00
@order . save
if payment_method . preferred_review
2012-05-24 21:02:15 +00:00
@order . next
2012-04-08 20:58:26 +00:00
render 'spree/shared/paypal_express_confirm'
2010-03-05 11:59:20 +00:00
else
2011-12-07 22:00:27 +00:00
paypal_finish
2010-03-05 11:59:20 +00:00
end
2011-12-07 22:00:27 +00:00
else
gateway_error ( @ppx_details )
2010-03-05 11:59:20 +00:00
2011-12-07 22:00:27 +00:00
#Failed trying to get payment details from PPX
redirect_to edit_order_checkout_url ( @order , :state = > " payment " )
2010-01-28 16:48:01 +00:00
end
2011-12-07 22:00:27 +00:00
rescue ActiveMerchant :: ConnectionError = > e
gateway_error I18n . t ( :unable_to_connect_to_gateway )
redirect_to edit_order_url ( @order )
end
2010-02-24 20:43:18 +00:00
2011-12-07 22:00:27 +00:00
def paypal_finish
load_order
2010-12-17 11:20:09 +00:00
2011-12-07 22:00:27 +00:00
opts = { :token = > params [ :token ] , :payer_id = > params [ :PayerID ] } . merge all_opts ( @order , params [ :payment_method_id ] , 'payment' )
gateway = paypal_gateway
2010-11-26 14:46:24 +00:00
2011-12-16 23:32:00 +00:00
method = Spree :: Config [ :auto_capture ] ? :purchase : :authorize
ppx_auth_response = gateway . send ( method , ( @order . total * 100 ) . to_i , opts )
2010-01-22 16:29:55 +00:00
2011-12-07 22:00:27 +00:00
paypal_account = Spree :: PaypalAccount . find_by_payer_id ( params [ :PayerID ] )
2009-09-07 09:24:55 +00:00
2012-05-02 12:32:52 +00:00
payment = @order . payments . create (
2011-12-07 22:00:27 +00:00
:amount = > ppx_auth_response . params [ " gross_amount " ] . to_f ,
:source = > paypal_account ,
2011-12-20 00:13:27 +00:00
:source_type = > 'Spree::PaypalAccount' ,
2011-12-07 22:00:27 +00:00
:payment_method_id = > params [ :payment_method_id ] ,
:response_code = > ppx_auth_response . params [ " ack " ] ,
2012-05-02 12:32:52 +00:00
:avs_response = > ppx_auth_response . avs_result [ " code " ] )
2009-09-07 09:24:55 +00:00
2011-12-07 22:00:27 +00:00
payment . started_processing!
2009-09-07 09:24:55 +00:00
2011-12-07 22:00:27 +00:00
record_log payment , ppx_auth_response
2011-01-18 16:11:33 +00:00
2011-12-07 22:00:27 +00:00
if ppx_auth_response . success?
#confirm status
case ppx_auth_response . params [ " payment_status " ]
when " Completed "
payment . complete!
when " Pending "
payment . pend!
else
payment . pend!
Rails . logger . error " Unexpected response from PayPal Express "
Rails . logger . error ppx_auth_response . to_yaml
end
2011-01-18 16:11:33 +00:00
2012-05-02 15:26:01 +00:00
@order . update_attribute ( :state , " complete " )
2012-05-02 15:38:51 +00:00
state_callback ( :after ) # So that after_complete is called, setting session[:order_id] to nil
2012-05-25 17:50:05 +00:00
@order . send ( :consume_users_credit ) if @order . respond_to? ( :consume_users_credit , true ) #since we dont rely on state machine callback, we just explicitly call this method for spree_store_credits
2012-05-22 21:26:11 +00:00
2012-05-02 15:26:01 +00:00
@order . finalize!
2011-12-07 22:00:27 +00:00
flash [ :notice ] = I18n . t ( :order_processed_successfully )
redirect_to completion_route
2011-01-18 16:11:33 +00:00
2010-11-28 14:31:31 +00:00
else
2012-04-08 20:58:26 +00:00
payment . failure!
2011-12-07 22:00:27 +00:00
order_params = { }
gateway_error ( ppx_auth_response )
2010-11-21 13:41:06 +00:00
2011-12-07 22:00:27 +00:00
#Failed trying to complete pending payment!
redirect_to edit_order_checkout_url ( @order , :state = > " payment " )
2010-01-28 16:48:01 +00:00
end
2011-12-07 22:00:27 +00:00
rescue ActiveMerchant :: ConnectionError = > e
gateway_error I18n . t ( :unable_to_connect_to_gateway )
redirect_to edit_order_url ( @order )
end
2010-01-22 16:29:55 +00:00
2011-12-07 22:00:27 +00:00
private
2010-11-21 13:41:06 +00:00
2012-04-09 16:39:27 +00:00
def asset_url ( _path )
URI :: HTTP . build ( :path = > ActionController :: Base . helpers . asset_path ( _path ) , :host = > Spree :: Config [ :site_url ] ) . to_s
end
2011-12-07 22:00:27 +00:00
def record_log ( payment , response )
2012-05-02 12:32:52 +00:00
payment . log_entries . create ( :details = > response . to_yaml )
2010-01-28 16:48:01 +00:00
end
2010-11-17 16:26:19 +00:00
2011-12-07 22:00:27 +00:00
def redirect_to_paypal_express_form_if_needed
return unless ( params [ :state ] == " payment " )
return unless params [ :order ] [ :payments_attributes ]
2012-05-16 20:02:48 +00:00
2012-05-21 21:01:52 +00:00
if @order . update_attributes ( object_params )
if params [ :order ] [ :coupon_code ] and ! params [ :order ] [ :coupon_code ] . blank? and @order . coupon_code . present?
2012-05-16 20:02:48 +00:00
fire_event ( 'spree.checkout.coupon_code_added' , :coupon_code = > @order . coupon_code )
end
2011-12-07 22:00:27 +00:00
end
2012-05-21 21:01:52 +00:00
2011-12-07 22:00:27 +00:00
load_order
payment_method = Spree :: PaymentMethod . find ( params [ :order ] [ :payments_attributes ] . first [ :payment_method_id ] )
2010-11-17 16:26:19 +00:00
2011-12-07 22:00:27 +00:00
if payment_method . kind_of? ( Spree :: BillingIntegration :: PaypalExpress ) || payment_method . kind_of? ( Spree :: BillingIntegration :: PaypalExpressUk )
2012-05-21 21:01:52 +00:00
redirect_to ( paypal_payment_order_checkout_url ( @order , :payment_method_id = > payment_method . id ) ) and return
2011-12-07 22:00:27 +00:00
end
2010-11-28 14:31:31 +00:00
end
2010-11-17 16:26:19 +00:00
2011-12-07 22:00:27 +00:00
def fixed_opts
2012-02-21 15:06:22 +00:00
if Spree :: PaypalExpress :: Config [ :paypal_express_local_confirm ] . nil?
2011-12-07 22:00:27 +00:00
user_action = " continue "
else
2012-02-21 15:06:22 +00:00
user_action = Spree :: PaypalExpress :: Config [ :paypal_express_local_confirm ] == " t " ? " continue " : " commit "
2011-12-07 22:00:27 +00:00
end
2012-05-21 21:01:52 +00:00
2012-05-12 10:00:57 +00:00
#asset_url doesn't like Spree::Config[:logo] being an absolute url
#if statement didn't work within hash
if URI . parse ( Spree :: Config [ :logo ] ) . absolute?
chosen_image = Spree :: Config [ :logo ]
else
chosen_image = asset_url ( Spree :: Config [ :logo ] )
end
2011-12-07 22:00:27 +00:00
2012-04-09 16:39:27 +00:00
2011-12-07 22:00:27 +00:00
{ :description = > " Goods from #{ Spree :: Config [ :site_name ] } " , # site details...
#:page_style => "foobar", # merchant account can set named config
:background_color = > " ffffff " , # must be hex only, six chars
:header_background_color = > " ffffff " ,
:header_border_color = > " ffffff " ,
2012-05-21 21:01:52 +00:00
:header_image = > chosen_image ,
2011-12-07 22:00:27 +00:00
:allow_note = > true ,
2012-04-09 16:39:27 +00:00
:locale = > user_locale ,
2011-12-07 22:00:27 +00:00
:req_confirm_shipping = > false , # for security, might make an option later
:user_action = > user_action
# WARNING -- don't use :ship_discount, :insurance_offered, :insurance since
# they've not been tested and may trigger some paypal bugs, eg not showing order
# see http://www.pdncommunity.com/t5/PayPal-Developer-Blog/Displaying-Order-Details-in-Express-Checkout/bc-p/92902#C851
}
2010-06-09 17:48:01 +00:00
end
2010-11-17 16:26:19 +00:00
2012-04-09 16:39:27 +00:00
def user_locale
I18n . locale . to_s
end
2011-12-07 22:00:27 +00:00
# hook to override paypal site options
def paypal_site_opts
2012-04-09 17:53:31 +00:00
{ :currency = > payment_method . preferred_currency , :allow_guest_checkout = > payment_method . preferred_allow_guest_checkout }
2010-02-24 20:43:18 +00:00
end
2011-12-07 22:00:27 +00:00
def order_opts ( order , payment_method , stage )
items = order . line_items . map do | item |
price = ( item . price * 100 ) . to_i # convert for gateway
{ :name = > item . variant . product . name ,
:description = > ( item . variant . product . description [ 0 .. 120 ] if item . variant . product . description ) ,
:sku = > item . variant . sku ,
:quantity = > item . quantity ,
:amount = > price ,
:weight = > item . variant . weight ,
:height = > item . variant . height ,
:width = > item . variant . width ,
:depth = > item . variant . weight }
end
2010-04-30 09:12:54 +00:00
2012-04-11 15:17:19 +00:00
credits = order . adjustments . eligible . map do | credit |
2011-12-07 22:00:27 +00:00
if credit . amount < 0 . 00
{ :name = > credit . label ,
:description = > credit . label ,
:sku = > credit . id ,
:quantity = > 1 ,
:amount = > ( credit . amount * 100 ) . to_i }
end
end
2010-01-22 16:29:55 +00:00
2011-12-07 22:00:27 +00:00
credits_total = 0
credits . compact!
if credits . present?
items . concat credits
credits_total = credits . map { | i | i [ :amount ] * i [ :quantity ] } . sum
2010-11-28 14:31:31 +00:00
end
2010-04-30 09:12:54 +00:00
2012-04-08 21:35:26 +00:00
opts = { :return_url = > paypal_confirm_order_checkout_url ( order , :payment_method_id = > payment_method ) ,
2012-04-09 01:10:00 +00:00
:cancel_return_url = > edit_order_checkout_url ( order , :state = > :payment ) ,
2011-12-07 22:00:27 +00:00
:order_id = > order . number ,
:custom = > order . number ,
:items = > items ,
:subtotal = > ( ( order . item_total * 100 ) + credits_total ) . to_i ,
2012-04-11 15:17:19 +00:00
:tax = > ( order . tax_total * 100 ) . to_i ,
:shipping = > ( order . ship_total * 100 ) . to_i ,
2011-12-07 22:00:27 +00:00
:money = > ( order . total * 100 ) . to_i }
if stage == " checkout "
opts [ :handling ] = 0
2012-02-16 17:38:51 +00:00
opts [ :callback_url ] = spree_root_url + " paypal_express_callbacks/ #{ order . number } "
2011-12-07 22:00:27 +00:00
opts [ :callback_timeout ] = 3
elsif stage == " payment "
#hack to add float rounding difference in as handling fee - prevents PayPal from rejecting orders
#because the integer totals are different from the float based total. This is temporary and will be
#removed once Spree's currency values are persisted as integers (normally only 1c)
opts [ :handling ] = ( order . total * 100 ) . to_i - opts . slice ( :subtotal , :tax , :shipping ) . values . sum
2010-11-28 14:31:31 +00:00
end
2010-01-22 16:29:55 +00:00
2011-12-07 22:00:27 +00:00
opts
2011-01-24 19:56:04 +00:00
end
2010-01-22 16:29:55 +00:00
2011-12-07 22:00:27 +00:00
def address_options ( order )
if payment_method . preferred_no_shipping
{ :no_shipping = > true }
else
{
:no_shipping = > false ,
:address_override = > true ,
:address = > {
:name = > " #{ order . ship_address . firstname } #{ order . ship_address . lastname } " ,
:address1 = > order . ship_address . address1 ,
:address2 = > order . ship_address . address2 ,
:city = > order . ship_address . city ,
:state = > order . ship_address . state . nil? ? order . ship_address . state_name . to_s : order . ship_address . state . abbr ,
:country = > order . ship_address . country . iso ,
:zip = > order . ship_address . zipcode ,
:phone = > order . ship_address . phone
}
}
end
2010-01-22 16:29:55 +00:00
end
2011-12-07 22:00:27 +00:00
def all_opts ( order , payment_method , stage = nil )
opts = fixed_opts . merge ( order_opts ( order , payment_method , stage ) ) . merge ( paypal_site_opts )
2010-01-22 16:29:55 +00:00
2011-12-07 22:00:27 +00:00
if stage == " payment "
opts . merge! flat_rate_shipping_and_handling_options ( order , stage )
end
2010-01-22 16:29:55 +00:00
2011-12-07 22:00:27 +00:00
# suggest current user's email or any email stored in the order
opts [ :email ] = current_user ? current_user . email : order . email
2010-01-22 16:29:55 +00:00
2011-12-07 22:00:27 +00:00
opts
2010-01-22 16:29:55 +00:00
end
2011-12-07 22:00:27 +00:00
# hook to allow applications to load in their own shipping and handling costs
def flat_rate_shipping_and_handling_options ( order , stage )
# max_fallback = 0.0
# shipping_options = ShippingMethod.all.map do |shipping_method|
# max_fallback = shipping_method.fallback_amount if shipping_method.fallback_amount > max_fallback
# { :name => "#{shipping_method.id}",
# :label => "#{shipping_method.name} - #{shipping_method.zone.name}",
# :amount => (shipping_method.fallback_amount*100) + 1,
# :default => shipping_method.is_default }
# end
#
#
# default_shipping_method = ShippingMethod.find(:first, :conditions => {:is_default => true})
#
# opts = { :shipping_options => shipping_options,
# :max_amount => (order.total + max_fallback)*100
# }
#
# opts[:shipping] = (default_shipping_method.nil? ? 0 : default_shipping_method.fallback_amount) if stage == "checkout"
#
# opts
{ }
end
2010-01-22 16:29:55 +00:00
2011-12-07 22:00:27 +00:00
def gateway_error ( response )
if response . is_a? ActiveMerchant :: Billing :: Response
text = response . params [ 'message' ] ||
response . params [ 'response_reason_text' ] ||
response . message
else
text = response . to_s
end
2010-01-22 16:29:55 +00:00
2011-12-07 22:00:27 +00:00
msg = " #{ I18n . t ( 'gateway_error' ) } : #{ text } "
logger . error ( msg )
flash [ :error ] = msg
2010-12-17 11:20:09 +00:00
end
2011-12-07 22:00:27 +00:00
# create the gateway from the supplied options
def payment_method
2012-04-09 17:53:31 +00:00
@payment_method || = Spree :: PaymentMethod . find ( params [ :payment_method_id ] )
2011-12-07 22:00:27 +00:00
end
2009-05-14 10:11:59 +00:00
2011-12-07 22:00:27 +00:00
def paypal_gateway
payment_method . provider
end
2010-02-24 20:43:18 +00:00
end
2009-05-14 10:11:59 +00:00
end