Created
November 8, 2012 14:56
-
-
Save v-fedorov/4039266 to your computer and use it in GitHub Desktop.
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
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 | |
unless @ppx_response.success? | |
gateway_error(@ppx_response) | |
redirect_to edit_order_url(@order) | |
return | |
end | |
redirect_to(@gateway.redirect_url_for(response.token, :review => payment_method.preferred_review)) | |
rescue ActiveMerchant::ConnectionError => e | |
gateway_error I18n.t(:unable_to_connect_to_gateway) | |
redirect_to :back | |
end | |
def paypal_payment | |
load_order | |
opts = all_opts(@order,params[:payment_method_id], 'payment') | |
unless payment_method.preferred_cart_checkout | |
opts.merge!(address_options(@order)) | |
end | |
@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 | |
unless @ppx_response.success? | |
gateway_error(@ppx_response) | |
redirect_to edit_order_checkout_url(@order, :state => "payment") | |
return | |
end | |
redirect_to(@gateway.redirect_url_for(@ppx_response.token, :review => payment_method.preferred_review)) | |
Rails.logger.debug "works" | |
rescue ActiveMerchant::ConnectionError => e | |
gateway_error I18n.t(:unable_to_connect_to_gateway) | |
redirect_to :back | |
end | |
def paypal_confirm | |
load_order | |
opts = { :token => params[:token], :payer_id => params[:PayerID] }.merge all_opts(@order, params[:payment_method_id], 'payment') | |
gateway = paypal_gateway | |
@ppx_details = gateway.details_for params[:token] | |
if @ppx_details.success? | |
# now save the updated order info | |
Spree::PaypalAccount.create(:email => @ppx_details.params["payer"], | |
:payer_id => @ppx_details.params["payer_id"], | |
:payer_country => @ppx_details.params["payer_country"], | |
:payer_status => @ppx_details.params["payer_status"]) | |
@order.special_instructions = @ppx_details.params["note"] | |
unless payment_method.preferred_no_shipping | |
ship_address = @ppx_details.address | |
order_ship_address = Spree::Address.new :firstname => @ppx_details.params["first_name"], | |
: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)" | |
state = Spree::State.find_by_abbr(ship_address["state"].upcase) if ship_address["state"].present? | |
if state | |
order_ship_address.state = state | |
else | |
order_ship_address.state_name = ship_address["state"] | |
end | |
order_ship_address.save! | |
@order.ship_address = order_ship_address | |
@order.bill_address ||= order_ship_address | |
# if payment_method.preferred_cart_checkout | |
# add_shipping_charge | |
# end | |
end | |
@order.state = "payment" | |
@order.save | |
if payment_method.preferred_review | |
@order.next | |
render 'spree/shared/paypal_express_confirm' | |
else | |
paypal_finish | |
end | |
else | |
gateway_error(@ppx_details) | |
#Failed trying to get payment details from PPX | |
redirect_to edit_order_checkout_url(@order, :state => "payment") | |
end | |
rescue ActiveMerchant::ConnectionError => e | |
gateway_error I18n.t(:unable_to_connect_to_gateway) | |
redirect_to edit_order_url(@order) | |
end | |
def paypal_finish | |
load_order | |
opts = { :token => params[:token], :payer_id => params[:PayerID] }.merge all_opts(@order, params[:payment_method_id], 'payment' ) | |
gateway = paypal_gateway | |
method = Spree::Config[:auto_capture] ? :purchase : :authorize | |
ppx_auth_response = gateway.send(method, (@order.total*100).to_i, opts) | |
paypal_account = Spree::PaypalAccount.find_by_payer_id(params[:PayerID]) | |
payment = @order.payments.create( | |
:amount => ppx_auth_response.params["gross_amount"].to_f, | |
:source => paypal_account, | |
:source_type => 'Spree::PaypalAccount', | |
:payment_method_id => params[:payment_method_id], | |
:response_code => ppx_auth_response.authorization, | |
:avs_response => ppx_auth_response.avs_result["code"]) | |
payment.started_processing! | |
record_log payment, ppx_auth_response | |
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 | |
@order.update_attributes({:state => "complete", :completed_at => Time.now}, :without_protection => true) | |
state_callback(:after) # So that after_complete is called, setting session[:order_id] to nil | |
# Since we dont rely on state machine callback, we just explicitly call this method for spree_store_credits | |
if @order.respond_to?(:consume_users_credit, true) | |
@order.send(:consume_users_credit) | |
end | |
@order.finalize! | |
flash[:notice] = I18n.t(:order_processed_successfully) | |
redirect_to completion_route | |
else | |
payment.failure! | |
order_params = {} | |
gateway_error(ppx_auth_response) | |
#Failed trying to complete pending payment! | |
redirect_to edit_order_checkout_url(@order, :state => "payment") | |
end | |
rescue ActiveMerchant::ConnectionError => e | |
gateway_error I18n.t(:unable_to_connect_to_gateway) | |
redirect_to edit_order_url(@order) | |
end | |
private | |
def asset_url(_path) | |
URI::HTTP.build(:path => ActionController::Base.helpers.asset_path(_path), :host => Spree::Config[:site_url]).to_s | |
end | |
def record_log(payment, response) | |
payment.log_entries.create(:details => response.to_yaml) | |
end | |
def redirect_to_paypal_express_form_if_needed | |
return unless (params[:state] == "payment") | |
return unless params[:order][:payments_attributes] | |
if @order.update_attributes(object_params) | |
if params[:order][:coupon_code] and !params[:order][:coupon_code].blank? and @order.coupon_code.present? | |
fire_event('spree.checkout.coupon_code_added', :coupon_code => @order.coupon_code) | |
end | |
end | |
load_order | |
payment_method = Spree::PaymentMethod.find(params[:order][:payments_attributes].first[:payment_method_id]) | |
if payment_method.kind_of?(Spree::BillingIntegration::PaypalExpress) || payment_method.kind_of?(Spree::BillingIntegration::PaypalExpressUk) | |
redirect_to(paypal_payment_order_checkout_url(@order, :payment_method_id => payment_method.id)) and return | |
end | |
end | |
def fixed_opts | |
if Spree::PaypalExpress::Config[:paypal_express_local_confirm].nil? | |
user_action = "continue" | |
else | |
user_action = Spree::PaypalExpress::Config[:paypal_express_local_confirm] == "t" ? "continue" : "commit" | |
end | |
#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 | |
{ :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", | |
:header_image => chosen_image, | |
:allow_note => true, | |
:locale => user_locale, | |
: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 | |
} | |
end | |
def user_locale | |
I18n.locale.to_s | |
end | |
# hook to override paypal site options | |
def paypal_site_opts | |
{:currency => payment_method.preferred_currency, :allow_guest_checkout => payment_method.preferred_allow_guest_checkout } | |
end | |
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), | |
:number => 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 | |
credits = order.adjustments.eligible.map do |credit| | |
if credit.amount < 0.00 | |
{ :name => credit.label, | |
:description => credit.label, | |
:sku => credit.id, | |
:quantity => 1, | |
:amount => (credit.amount*100).to_i } | |
end | |
end | |
credits_total = 0 | |
credits.compact! | |
if credits.present? | |
items.concat credits | |
credits_total = credits.map {|i| i[:amount] * i[:quantity] }.sum | |
end | |
shipping_options = [{:default => true, :name => "first one", :amount => 0}] | |
opts = { :return_url => paypal_confirm_order_checkout_url(order, :payment_method_id => payment_method), | |
:cancel_return_url => edit_order_checkout_url(order, :state => :payment), | |
:order_id => order.number, | |
:custom => order.number, | |
:items => items, | |
:subtotal => ((order.item_total * 100) + credits_total).to_i, | |
:tax => (order.tax_total*100).to_i, | |
:shipping => (order.ship_total*100).to_i, | |
:money => (order.total * 100 ).to_i, | |
:max_amount => (order.total * 300 ).to_i, | |
:callback_url => "http://4dig.localtunnel.com/paypal_shipping_update", | |
:callback_timeout => 6, | |
:callback_version => 87, | |
:shipping_options => shipping_options} | |
if stage == "checkout" | |
opts[:handling] = 0 | |
opts[:callback_url] = spree_root_url + "paypal_express_callbacks/#{order.number}" | |
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] = 0 | |
end | |
opts | |
end | |
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 | |
end | |
def all_opts(order, payment_method, stage=nil) | |
opts = fixed_opts.merge(order_opts(order, payment_method, stage)).merge(paypal_site_opts) | |
if stage == "payment" | |
opts.merge! flat_rate_shipping_and_handling_options(order, stage) | |
end | |
# suggest current user's email or any email stored in the order | |
opts[:email] = current_user ? current_user.email : order.email | |
opts | |
end | |
# 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| | |
# { :name => "#{shipping_method.name}", | |
# :amount => (shipping_method.rate), | |
# :default => shipping_method.is_default } | |
# end | |
# default_shipping_method = ShippingMethod.find(:first, :conditions => {:is_default => true}) | |
# opts = { :shipping_options => shipping_options, | |
# } | |
# #opts[:shipping] = (default_shipping_method.nil? ? 0 : default_shipping_method.fallback_amount) if stage == "checkout" | |
# opts | |
{} | |
end | |
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 | |
msg = "#{I18n.t('gateway_error')}: #{text}" | |
logger.error(msg) | |
flash[:error] = msg | |
end | |
# create the gateway from the supplied options | |
def payment_method | |
@payment_method ||= Spree::PaymentMethod.find(params[:payment_method_id]) | |
end | |
def paypal_gateway | |
payment_method.provider | |
end | |
def add_shipping_charge | |
if @order.shipping_method_id.blank? && @order.rate_hash.present? | |
@order.shipping_method_id = @order.rate_hash.first[:id] | |
end | |
@order.shipments.each { |s| s.destroy unless s.shipping_method.available_to_order?(@order) } | |
@order.create_shipment! | |
@order.update! | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment