Added PayPal Instant Update feature
This commit is contained in:
parent
56930b93aa
commit
ca60407d5d
|
@ -13,9 +13,10 @@ This extension allows the store to use PayPal Express from two locations:
|
|||
sent to the PayPal review page (along with detailed order information).
|
||||
|
||||
|
||||
2. Cart Checkout (THIS FEATURE IS NOT YET COMPLETE) - Presents the PayPal checkout button on the users Cart page and redirects the user to complete
|
||||
2. Cart Checkout* - Presents the PayPal checkout button on the users Cart page and redirects the user to complete
|
||||
all shipping / addressing information on PaypPal's site. This also supports PayPal's Instant Update feature to retrieve shipping options live from
|
||||
Spree when the user selects / changes their shipping address on PayPal's site.
|
||||
* Check "Checkout from cart" in admin for feature to work
|
||||
|
||||
This extension follows the documented flow for a PayPal Express Checkout, where a user is forwarded to PayPal to allow them to login and review
|
||||
the order (possibly select / change shipping address and method), then the user is redirected back to Spree to confirm the order. The user
|
||||
|
|
|
@ -29,7 +29,12 @@ module Spree
|
|||
def paypal_payment
|
||||
load_order
|
||||
opts = all_opts(@order,params[:payment_method_id], 'payment')
|
||||
opts.merge!(address_options(@order))
|
||||
unless payment_method.preferred_cart_checkout
|
||||
opts.merge!(address_options(@order))
|
||||
else
|
||||
opts.merge!(shipping_options)
|
||||
end
|
||||
|
||||
@gateway = paypal_gateway
|
||||
|
||||
if Spree::Config[:auto_capture]
|
||||
|
@ -61,6 +66,8 @@ module Spree
|
|||
if @ppx_details.success?
|
||||
# now save the updated order info
|
||||
|
||||
#TODO Search for existing records
|
||||
|
||||
Spree::PaypalAccount.create(:email => @ppx_details.params["payer"],
|
||||
:payer_id => @ppx_details.params["payer_id"],
|
||||
:payer_country => @ppx_details.params["payer_country"],
|
||||
|
@ -90,11 +97,18 @@ module Spree
|
|||
|
||||
@order.ship_address = order_ship_address
|
||||
@order.bill_address ||= order_ship_address
|
||||
|
||||
#Add Instant Update Shipping
|
||||
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
|
||||
|
@ -231,7 +245,7 @@ module Spree
|
|||
:background_color => "ffffff", # must be hex only, six chars
|
||||
:header_background_color => "ffffff",
|
||||
:header_border_color => "ffffff",
|
||||
:header_image => chosen_image,
|
||||
:header_image => chosen_image,
|
||||
:allow_note => true,
|
||||
:locale => user_locale,
|
||||
:req_confirm_shipping => false, # for security, might make an option later
|
||||
|
@ -283,6 +297,15 @@ module Spree
|
|||
credits_total = credits.map {|i| i[:amount] * i[:quantity] }.sum
|
||||
end
|
||||
|
||||
unless @order.payment_method.preferred_cart_checkout
|
||||
order_total = (order.total * 100).to_i
|
||||
shipping_total = (order.ship_total*100).to_i
|
||||
else
|
||||
shipping_cost = shipping_options[:shipping_options].first[:amount]
|
||||
order_total = (order.total * 100 + (shipping_cost)).to_i
|
||||
shipping_total = (shipping_cost).to_i
|
||||
end
|
||||
|
||||
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,
|
||||
|
@ -290,8 +313,9 @@ module Spree
|
|||
: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 }
|
||||
:shipping => shipping_total,
|
||||
:money => order_total,
|
||||
:max_amount => (order.total * 300).to_i}
|
||||
|
||||
if stage == "checkout"
|
||||
opts[:handling] = 0
|
||||
|
@ -302,12 +326,40 @@ module Spree
|
|||
#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
|
||||
if @order.payment_method.preferred_cart_checkout
|
||||
opts[:handling] = 0
|
||||
else
|
||||
opts[:handling] = (order.total*100).to_i - opts.slice(:subtotal, :tax, :shipping).values.sum
|
||||
end
|
||||
end
|
||||
|
||||
opts
|
||||
end
|
||||
|
||||
def shipping_options
|
||||
#Uses users address if exists, if not uses first shipping method
|
||||
if (current_user.present? && current_user.addresses.present?)
|
||||
estimate_shipping_for_user
|
||||
shipping_default = @rate_hash_user.map.with_index do |shipping_method, idx|
|
||||
{ :default => (idx == 0 ? true : false),
|
||||
:name => shipping_method.name,
|
||||
:amount => (shipping_method.cost*100).to_i }
|
||||
end
|
||||
else
|
||||
shipping_method = ShippingMethod.all.first
|
||||
shipping_default = [{ :default => true,
|
||||
:name => shipping_method.name,
|
||||
:amount => ((shipping_method.calculator.compute(self).to_f) * 100).to_i }]
|
||||
end
|
||||
|
||||
{
|
||||
:callback_url => spree_root_url + "paypal_shipping_update",
|
||||
:callback_timeout => 6,
|
||||
:callback_version => '61.0',
|
||||
:shipping_options => shipping_default
|
||||
}
|
||||
end
|
||||
|
||||
def address_options(order)
|
||||
if payment_method.preferred_no_shipping
|
||||
{ :no_shipping => true }
|
||||
|
@ -346,22 +398,19 @@ module Spree
|
|||
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,
|
||||
# { :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,
|
||||
# :max_amount => (order.total + max_fallback)*100
|
||||
# }
|
||||
#
|
||||
# opts[:shipping] = (default_shipping_method.nil? ? 0 : default_shipping_method.fallback_amount) if stage == "checkout"
|
||||
#
|
||||
|
||||
# #opts[:shipping] = (default_shipping_method.nil? ? 0 : default_shipping_method.fallback_amount) if stage == "checkout"
|
||||
|
||||
# opts
|
||||
{}
|
||||
end
|
||||
|
@ -389,5 +438,35 @@ module Spree
|
|||
payment_method.provider
|
||||
end
|
||||
|
||||
def add_shipping_charge
|
||||
# Replace with these changes once Active_Merchant pushes pending pull request
|
||||
# shipment_name = @ppx_details.shipping['amount'].chomp(" Shipping")
|
||||
# shipment_cost = @ppx_details.shipping['name'].to_f
|
||||
|
||||
shipment_name = @ppx_details.params['UserSelectedOptions']['ShippingOptionName'].chomp(" Shipping")
|
||||
shipment_cost = @ppx_details.params['UserSelectedOptions']['ShippingOptionAmount'].to_f
|
||||
if @order.shipping_method_id.blank? && @order.rate_hash.present?
|
||||
selected_shipping = @order.rate_hash.detect { |v| v['name'] == shipment_name && v['cost'] == shipment_cost }
|
||||
@order.shipping_method_id = selected_shipping.id
|
||||
end
|
||||
@order.shipments.each { |s| s.destroy unless s.shipping_method.available_to_order?(@order) }
|
||||
@order.create_shipment!
|
||||
@order.update!
|
||||
end
|
||||
|
||||
def estimate_shipping_for_user
|
||||
zipcode = current_user.addresses.first.zipcode
|
||||
country = current_user.addresses.first.country.iso
|
||||
shipping_methods = Spree::ShippingMethod.all
|
||||
#TODO remove hard coded shipping
|
||||
#Make a deep copy of the order object then stub out the parts required to get a shipping quote
|
||||
@shipping_order = Marshal::load(Marshal.dump(@order)) #Make a deep copy of the order object
|
||||
@shipping_order.ship_address = Spree::Address.new(:country => Spree::Country.find_by_iso(country), :zipcode => zipcode)
|
||||
shipment = Spree::Shipment.new(:address => @shipping_order.ship_address)
|
||||
@shipping_order.ship_address.shipments<<shipment
|
||||
@shipping_order.shipments<<shipment
|
||||
@rate_hash_user = @shipping_order.rate_hash
|
||||
#TODO
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,7 +8,6 @@ module Spree
|
|||
def notify
|
||||
retrieve_details #need to retreive details first to ensure ActiveMerchant gets configured correctly.
|
||||
|
||||
|
||||
@notification = Paypal::Notification.new(request.raw_post)
|
||||
|
||||
# we only care about eChecks (for now?)
|
||||
|
@ -29,6 +28,39 @@ module Spree
|
|||
render :nothing => true
|
||||
end
|
||||
|
||||
def shipping_estimate
|
||||
#details from Paypal
|
||||
if request.post?
|
||||
@method = params[:METHOD]
|
||||
@version = params[:CALLBACKVERSION]
|
||||
@token = params[:TOKEN]
|
||||
@currency = params[:CURRENCYCODE]
|
||||
@locale = params[:LOCALECODE]
|
||||
@street = params[:SHIPTOSTREET]
|
||||
@street2 = params[:SHIPTOSTREET2]
|
||||
@city = params[:SHIPTOCITY]
|
||||
@state = params[:SHIPTOSTATE]
|
||||
@country = params[:SHIPTOCOUNTRY]
|
||||
@zip = params[:SHIPTOZIP]
|
||||
end
|
||||
#available shipping based on paypal details
|
||||
estimate_shipping_and_taxes
|
||||
|
||||
payment_methods_atts2 = {}
|
||||
@rate_hash.each_with_index do |shipping_method, idx|
|
||||
payment_methods_atts2["L_TAXAMT#{idx}"] = @order.tax_total #TODO need to calculate based on shipping method
|
||||
payment_methods_atts2["L_SHIPPINGOPTIONAMOUNT#{idx}"] = shipping_method.cost
|
||||
payment_methods_atts2["L_SHIPPINGOPTIONNAME#{idx}"] = shipping_method.name
|
||||
payment_methods_atts2["L_SHIPPINGOPTIONLABEL#{idx}"] = "Shipping" #Do not change, required field
|
||||
payment_methods_atts2["L_SHIPPINGOPTIONISDEFAULT#{idx}"] = (idx == 0 ? true : false)
|
||||
end
|
||||
|
||||
#compiles NVP query used by paypal callback
|
||||
query = payment_methods_atts2.inject('METHOD=CallbackResponse&CALLBACKVERSION=61&OFFERINSURANCEOPTION=false') { |string, pair| string + '&' + pair[0].to_s + '=' + pair[1].to_s }
|
||||
|
||||
render :text => query #query read by PayPal
|
||||
end
|
||||
|
||||
private
|
||||
def retrieve_details
|
||||
@order = Spree::Order.find_by_number(params["invoice"])
|
||||
|
@ -40,5 +72,19 @@ module Spree
|
|||
end
|
||||
end
|
||||
|
||||
def estimate_shipping_and_taxes
|
||||
@order = Spree::Order.find_by_number(current_order(true).number)
|
||||
zipcode = @zip
|
||||
shipping_methods = Spree::ShippingMethod.all
|
||||
#TODO remove hard coded shipping
|
||||
#Make a deep copy of the order object then stub out the parts required to get a shipping quote
|
||||
@shipping_order = Marshal::load(Marshal.dump(@order)) #Make a deep copy of the order object
|
||||
@shipping_order.ship_address = Spree::Address.new(:country => Spree::Country.find_by_iso("#{@country}"), :zipcode => zipcode)
|
||||
shipment = Spree::Shipment.new(:address => @shipping_order.ship_address)
|
||||
@shipping_order.ship_address.shipments<<shipment
|
||||
@shipping_order.shipments<<shipment
|
||||
@rate_hash = @shipping_order.rate_hash
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,10 +4,11 @@ class Spree::BillingIntegration::PaypalExpressBase < Spree::BillingIntegration
|
|||
preference :signature, :string
|
||||
preference :review, :boolean, :default => false
|
||||
preference :no_shipping, :boolean, :default => false
|
||||
preference :cart_checkout, :boolean, :default => false
|
||||
preference :currency, :string, :default => 'USD'
|
||||
preference :allow_guest_checkout, :boolean, :default => false
|
||||
|
||||
attr_accessible :preferred_login, :preferred_password, :preferred_signature, :preferred_review, :preferred_no_shipping, :preferred_currency, :preferred_allow_guest_checkout, :preferred_server, :preferred_test_mode
|
||||
attr_accessible :preferred_login, :preferred_password, :preferred_signature, :preferred_review, :preferred_no_shipping, :preferred_currency, :preferred_allow_guest_checkout, :preferred_server, :preferred_test_mode, :preferred_cart_checkout
|
||||
|
||||
def provider_class
|
||||
ActiveMerchant::Billing::PaypalExpressGateway
|
||||
|
|
|
@ -12,6 +12,7 @@ en-GB:
|
|||
result: Result
|
||||
review: Review
|
||||
no_shipping: No Shipping
|
||||
cart_checkout: Checkout From Cart
|
||||
paypal_account: PayPal Account
|
||||
payer_id: Payer ID
|
||||
payer_country: Country
|
||||
|
|
|
@ -14,6 +14,7 @@ en:
|
|||
result: Result
|
||||
review: Review
|
||||
no_shipping: No Shipping
|
||||
cart_checkout: Checkout From Cart
|
||||
paypal_account: PayPal Account
|
||||
payer_id: Payer ID
|
||||
payer_country: Country
|
||||
|
|
|
@ -12,6 +12,8 @@ Spree::Core::Engine.routes.draw do
|
|||
|
||||
match '/paypal_notify' => 'paypal_express_callbacks#notify', :via => [:get, :post]
|
||||
|
||||
match '/paypal_shipping_update' => 'paypal_express_callbacks#shipping_estimate', :via => :post
|
||||
|
||||
namespace :admin do
|
||||
resources :orders do
|
||||
resources :paypal_payments do
|
||||
|
|
Loading…
Reference in New Issue