diff --git a/app/models/paypal_account.rb b/app/models/paypal_account.rb new file mode 100644 index 0000000..5b6ffd4 --- /dev/null +++ b/app/models/paypal_account.rb @@ -0,0 +1,53 @@ +class PaypalAccount < ActiveRecord::Base + has_many :payments, :as => :source + + def finalize!(payment) + authorization = find_authorization + + ppx_response = p.payment_method.capture((100 * payment.amount).to_i, authorization.transaction_id) + if ppx_response.success? + payment = authorization.paypal_payment + + transaction = PaypalTxn.new(:payment => payment, + :amount => ppx_response.params["gross_amount"].to_f, + :message => ppx_response.params["message"], + :payment_status => ppx_response.params["payment_status"], + :pending_reason => ppx_response.params["pending_reason"], + :transaction_id => ppx_response.params["transaction_id"], + :transaction_type => ppx_response.params["transaction_type"], + :payment_type => ppx_response.params["payment_type"], + :ack => ppx_response.params["ack"], + :token => ppx_response.params["token"], + :avs_response => ppx_response.avs_result["code"], + :cvv_response => ppx_response.cvv_result["code"]) + + payment.paypal_txns << transaction + + payment.save + else + gateway_error(ppx_response) + end + + + end + + + private + def find_authorization(payment) + #find the transaction associated with the original authorization/capture + txns.find(:first, + :conditions => {:pending_reason => "authorization", :payment_status => "Pending"}, + :order => 'created_at DESC') + end + + def find_capture(payment) + #find the transaction associated with the original authorization/capture + txns.find(:first, + :conditions => {:payment_status => "Completed"}, + :order => 'created_at DESC') + end + + def can_capture?(payment) + find_capture.nil? + end +end diff --git a/app/models/paypal_payment.rb b/app/models/paypal_payment.rb deleted file mode 100644 index 7b76c2e..0000000 --- a/app/models/paypal_payment.rb +++ /dev/null @@ -1,23 +0,0 @@ -class PaypalPayment < Payment - has_many :paypal_txns - - alias :txns :paypal_txns - - def find_authorization - #find the transaction associated with the original authorization/capture - txns.find(:first, - :conditions => {:pending_reason => "authorization", :payment_status => "Pending"}, - :order => 'created_at DESC') - end - - def find_capture - #find the transaction associated with the original authorization/capture - txns.find(:first, - :conditions => {:payment_status => "Completed"}, - :order => 'created_at DESC') - end - - def can_capture? - find_capture.nil? - end -end diff --git a/app/models/paypal_txn.rb b/app/models/paypal_txn.rb index 885fe4a..d5f83d3 100644 --- a/app/models/paypal_txn.rb +++ b/app/models/paypal_txn.rb @@ -1,3 +1,7 @@ -class PaypalTxn < ActiveRecord::Base - belongs_to :paypal_payment +class PaypalTxn < Transaction + enumerable_constant :txn_type, :constants => [:authorize, :capture, :purchase, :void, :credit] + + def txn_type_name + TxnType.from_value(txn_type) + end end diff --git a/app/views/checkouts/payment/_paypalexpress.html.erb b/app/views/checkouts/payment/_paypalexpress.html.erb new file mode 100644 index 0000000..f511a03 --- /dev/null +++ b/app/views/checkouts/payment/_paypalexpress.html.erb @@ -0,0 +1,3 @@ + + + diff --git a/app/views/shared/_paypal_express_payment.html.erb b/app/views/shared/_paypal_express_payment.html.erb deleted file mode 100644 index 53e789a..0000000 --- a/app/views/shared/_paypal_express_payment.html.erb +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/db/migrate/20100122120551_create_paypal_txns.rb b/db/migrate/20100122120551_create_paypal_txns.rb index a0dbbb5..3f6c00c 100644 --- a/db/migrate/20100122120551_create_paypal_txns.rb +++ b/db/migrate/20100122120551_create_paypal_txns.rb @@ -1,6 +1,6 @@ class CreatePaypalTxns < ActiveRecord::Migration def self.up - create_table :paypal_txns do |t| + create_table :paypal_txns do |t| t.references :paypal_payment t.decimal :gross_amount, :precision => 8, :scale => 2 t.string :payment_status diff --git a/db/migrate/20100224133156_create_paypal_accounts.rb b/db/migrate/20100224133156_create_paypal_accounts.rb new file mode 100644 index 0000000..cd8dd60 --- /dev/null +++ b/db/migrate/20100224133156_create_paypal_accounts.rb @@ -0,0 +1,14 @@ +class CreatePaypalAccounts < ActiveRecord::Migration + def self.up + create_table :paypal_accounts do |t| + t.string :email + t.string :payer_id + t.string :payer_country + t.string :payer_status + end + end + + def self.down + drop_table :paypal_accounts + end +end diff --git a/db/migrate/20100224181116_make_paypal_txn_sti.rb b/db/migrate/20100224181116_make_paypal_txn_sti.rb new file mode 100644 index 0000000..929df28 --- /dev/null +++ b/db/migrate/20100224181116_make_paypal_txn_sti.rb @@ -0,0 +1,26 @@ +class MakePaypalTxnSti < ActiveRecord::Migration + def self.up + add_column :transactions, :payment_status, :string + add_column :transactions, :message, :string + add_column :transactions, :pending_reason, :string + add_column :transactions, :transaction_id, :string + add_column :transactions, :transaction_type, :string + add_column :transactions, :payment_type, :string + add_column :transactions, :token, :string + + #to-do migrate existing ppx_txns + + drop_table :paypal_txns + + end + + def self.down + remove_column :transactions, :payment_status + remove_column :transactions, :message + remove_column :transactions, :pending_reason + remove_column :transactions, :transaction_id + remove_column :transactions, :transaction_type + remove_column :transactions, :payment_type + remove_column :transactions, :token + end +end \ No newline at end of file diff --git a/lib/spree/paypal_express.rb b/lib/spree/paypal_express.rb index 107072c..b88a07d 100644 --- a/lib/spree/paypal_express.rb +++ b/lib/spree/paypal_express.rb @@ -5,7 +5,7 @@ module Spree::PaypalExpress def paypal_checkout load_object - opts = all_opts(@order, 'checkout') + opts = all_opts(@order, params[:payment_method_id], 'checkout') opts.merge!(address_options(@order)) gateway = paypal_gateway @@ -16,12 +16,12 @@ module Spree::PaypalExpress return end - redirect_to (gateway.redirect_url_for response.token) + redirect_to (gateway.redirect_url_for response.token, :review => review) end def paypal_payment load_object - opts = all_opts(@order, 'payment') + opts = all_opts(@order,params[:payment_method_id], 'payment') opts.merge!(address_options(@order)) gateway = paypal_gateway @@ -32,23 +32,28 @@ module Spree::PaypalExpress return end - redirect_to (gateway.redirect_url_for response.token) + redirect_to (gateway.redirect_url_for response.token, :review => review) end def paypal_confirm load_object - opts = { :token => params[:token], :payer_id => params[:PayerID] }.merge all_opts(@order) + opts = { :token => params[:token], :payer_id => params[:PayerID] }.merge all_opts(@order, params[:payment_method_id]) gateway = paypal_gateway @ppx_details = gateway.details_for params[:token] if @ppx_details.success? # now save the updated order info - @order.checkout.email = @ppx_details.email + + 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.checkout.special_instructions = @ppx_details.params["note"] - @order.update_attribute(:user, current_user) + #@order.update_attribute(:user, current_user) ship_address = @ppx_details.address order_ship_address = Address.new :firstname => @ppx_details.params["first_name"], @@ -71,7 +76,12 @@ module Spree::PaypalExpress @order.checkout.ship_address = order_ship_address @order.checkout.save - render :partial => "shared/paypal_express_confirm", :layout => true + + if review + render :partial => "shared/paypal_express_confirm", :layout => true + else + paypal_finish + end else gateway_error(@ppx_details) end @@ -79,9 +89,8 @@ module Spree::PaypalExpress def paypal_finish load_object - #order = Order.find_by_number(params[:id]) - opts = { :token => params[:token], :payer_id => params[:PayerID] }.merge all_opts(@order) + opts = { :token => params[:token], :payer_id => params[:PayerID] }.merge all_opts(@order, params[:payment_method_id]) gateway = paypal_gateway if Spree::Config[:auto_capture] @@ -91,23 +100,26 @@ module Spree::PaypalExpress end if ppx_auth_response.success? + paypal_account = PaypalAccount.find_by_payer_id(params[:PayerID]) - payment = @order.paypal_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, + :source => paypal_account, + :payment_method_id => params[:payment_method_id]) - transaction = PaypalTxn.new(:paypal_payment => payment, - :gross_amount => ppx_auth_response.params["gross_amount"].to_f, - :message => ppx_auth_response.params["message"], - :payment_status => ppx_auth_response.params["payment_status"], - :pending_reason => ppx_auth_response.params["pending_reason"], - :transaction_id => ppx_auth_response.params["transaction_id"], - :transaction_type => ppx_auth_response.params["transaction_type"], - :payment_type => ppx_auth_response.params["payment_type"], - :ack => ppx_auth_response.params["ack"], - :token => ppx_auth_response.params["token"], - :avs_response => ppx_auth_response.avs_result["code"], - :cvv_response => ppx_auth_response.cvv_result["code"]) + PaypalTxn.create(:payment => payment, + :txn_type => PaypalTxn::TxnType::AUTHORIZE, + :amount => ppx_auth_response.params["gross_amount"].to_f, + :message => ppx_auth_response.params["message"], + :payment_status => ppx_auth_response.params["payment_status"], + :pending_reason => ppx_auth_response.params["pending_reason"], + :transaction_id => ppx_auth_response.params["transaction_id"], + :transaction_type => ppx_auth_response.params["transaction_type"], + :payment_type => ppx_auth_response.params["payment_type"], + :response_code => ppx_auth_response.params["ack"], + :token => ppx_auth_response.params["token"], + :avs_response => ppx_auth_response.avs_result["code"], + :cvv_response => ppx_auth_response.cvv_result["code"]) - payment.paypal_txns << transaction @order.save! @checkout.reload @@ -135,15 +147,16 @@ module Spree::PaypalExpress if ppx_response.success? payment = authorization.paypal_payment - transaction = PaypalTxn.new(:paypal_payment => payment, - :gross_amount => ppx_response.params["gross_amount"].to_f, + transaction = PaypalTxn.new(:payment => payment, + :txn_type => PaypalTxn::TxnType::CAPTURE, + :amount => ppx_response.params["gross_amount"].to_f, :message => ppx_response.params["message"], :payment_status => ppx_response.params["payment_status"], :pending_reason => ppx_response.params["pending_reason"], :transaction_id => ppx_response.params["transaction_id"], :transaction_type => ppx_response.params["transaction_type"], :payment_type => ppx_response.params["payment_type"], - :ack => ppx_response.params["ack"], + :response_code => ppx_response.params["ack"], :token => ppx_response.params["token"], :avs_response => ppx_response.avs_result["code"], :cvv_response => ppx_response.cvv_result["code"]) @@ -185,6 +198,12 @@ module Spree::PaypalExpress private def fixed_opts + if Spree::Config[:paypal_express_local_confirm].nil? + user_action = "continue" + else + user_action = Spree::Config[:paypal_express_local_confirm] == "t" ? "continue" : "commit" + end + { :description => "Goods from #{Spree::Config[:site_name]}", # site details... #:page_style => "foobar", # merchant account can set named config @@ -198,6 +217,7 @@ module Spree::PaypalExpress :notify_url => 'to be done', # this is a callback, not tried it yet :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 @@ -205,7 +225,7 @@ module Spree::PaypalExpress } end - def order_opts(order, stage) + def order_opts(order, payment_method, stage) items = order.line_items.map do |item| tax = paypal_variant_tax(item.price, item.variant) price = (item.price * 100).to_i # convert for gateway @@ -223,7 +243,7 @@ module Spree::PaypalExpress end - opts = { :return_url => request.protocol + request.host_with_port + "/orders/#{order.number}/checkout/paypal_confirm", + opts = { :return_url => request.protocol + request.host_with_port + "/orders/#{order.number}/checkout/paypal_confirm?payment_method_id=#{payment_method}", :cancel_return_url => "http://" + request.host_with_port + "/orders/#{order.number}/edit", :order_id => order.number, :custom => order.number, @@ -285,9 +305,8 @@ module Spree::PaypalExpress } end - def all_opts(order, stage=nil) - opts = fixed_opts.merge(order_opts(order, stage))#. - # merge(paypal_site_options order) BQ + def all_opts(order, payment_method, stage=nil) + opts = fixed_opts.merge(order_opts(order, payment_method, stage)) if stage == "payment" opts.merge! flat_rate_shipping_and_handling_options(order, stage) @@ -334,9 +353,16 @@ module Spree::PaypalExpress # create the gateway from the supplied options def paypal_gateway - integration = BillingIntegration.find(params[:integration_id]) if params.key? :integration_id - integration ||= BillingIntegration.current - + integration = PaymentMethod.find(params[:payment_method_id]) gateway = integration.provider end + + # if you want to confirm the order on Paypal's site, then this needs to return false + def review + if Spree::Config[:paypal_express_review].nil? + true + else + Spree::Config[:paypal_express_review] == "t" ? true : false + end + end end