spree_paypal_express 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/.travis.yml +14 -0
- data/Gemfile +3 -0
- data/LICENSE +23 -0
- data/README.markdown +146 -0
- data/Rakefile +13 -0
- data/Versionfile +5 -0
- data/app/assets/images/paypal.png +0 -0
- data/app/assets/javascripts/admin/spree_paypal_express.js +0 -0
- data/app/assets/javascripts/store/spree_paypal_express.js +0 -0
- data/app/assets/stylesheets/admin/spree_paypal_express.css +0 -0
- data/app/assets/stylesheets/store/spree_paypal_express.css +0 -0
- data/app/controllers/spree/checkout_controller_decorator.rb +384 -0
- data/app/controllers/spree/paypal_express_callbacks_controller.rb +44 -0
- data/app/helpers/spree/checkout_helper_decorator.rb +7 -0
- data/app/models/spree/billing_integration/paypal_express.rb +3 -0
- data/app/models/spree/billing_integration/paypal_express_base.rb +62 -0
- data/app/models/spree/billing_integration/paypal_express_uk.rb +3 -0
- data/app/models/spree/log_entry_decorator.rb +3 -0
- data/app/models/spree/payment_decorator.rb +3 -0
- data/app/models/spree/paypal_account.rb +36 -0
- data/app/overrides/spree/shared/_order_details/add_paypal_details.html.erb.deface +16 -0
- data/app/views/spree/admin/payments/source_forms/_paypalexpress.html.erb +9 -0
- data/app/views/spree/admin/payments/source_forms/_paypalexpressuk.html.erb +9 -0
- data/app/views/spree/admin/payments/source_views/_paypalexpress.html.erb +110 -0
- data/app/views/spree/admin/payments/source_views/_paypalexpressuk.html.erb +110 -0
- data/app/views/spree/admin/paypal_payments/refund.html.erb +15 -0
- data/app/views/spree/checkout/payment/_paypalexpress.html.erb +3 -0
- data/app/views/spree/checkout/payment/_paypalexpressuk.html.erb +3 -0
- data/app/views/spree/shared/_paypal_express_checkout.html.erb +3 -0
- data/app/views/spree/shared/paypal_express_confirm.html.erb +23 -0
- data/capture-notes +28 -0
- data/config/initializers/paypal_express.rb +1 -0
- data/config/locales/en-GB.yml +30 -0
- data/config/locales/en.yml +32 -0
- data/config/routes.rb +25 -0
- data/db/migrate/20100224133156_create_paypal_accounts.rb +14 -0
- data/db/migrate/20120117182027_namespace_paypal_accounts.rb +5 -0
- data/lib/generators/spree_paypal_express/install/install_generator.rb +18 -0
- data/lib/spree/paypal_express_configuration.rb +5 -0
- data/lib/spree_paypal_express.rb +3 -0
- data/lib/spree_paypal_express/engine.rb +38 -0
- data/response-example-one +55 -0
- data/response-xml-one +137 -0
- data/spec/controllers/checkout_controller_spec.rb +323 -0
- data/spec/factories/address_factory.rb +13 -0
- data/spec/factories/order_factory.rb +11 -0
- data/spec/factories/ppx_factory.rb +5 -0
- data/spec/factories/state_factory.rb +5 -0
- data/spec/models/billing_integration/paypal_express_base_spec.rb +135 -0
- data/spec/requests/paypal_express_spec.rb +40 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/support/authentication_helpers.rb +13 -0
- data/spec/support/controller_hacks.rb +33 -0
- data/spec/support/shared_connection.rb +12 -0
- data/spec/support/url_helpers.rb +7 -0
- data/spree_paypal_express.gemspec +24 -0
- metadata +224 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
before_script:
|
2
|
+
- "export DISPLAY=:99.0"
|
3
|
+
- "sh -e /etc/init.d/xvfb start"
|
4
|
+
- "DISPLAY=:99.0 bundle exec rake test_app"
|
5
|
+
script: "DISPLAY=:99.0 bundle exec rspec spec"
|
6
|
+
notifications:
|
7
|
+
email:
|
8
|
+
- briandquinn@gmail.com
|
9
|
+
branches:
|
10
|
+
only:
|
11
|
+
- master
|
12
|
+
- 1-1-stable
|
13
|
+
rvm:
|
14
|
+
- 1.9.3
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Redistribution and use in source and binary forms, with or without modification,
|
2
|
+
are permitted provided that the following conditions are met:
|
3
|
+
|
4
|
+
* Redistributions of source code must retain the above copyright notice,
|
5
|
+
this list of conditions and the following disclaimer.
|
6
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
7
|
+
this list of conditions and the following disclaimer in the documentation
|
8
|
+
and/or other materials provided with the distribution.
|
9
|
+
* Neither the name of the Rails Dog LLC nor the names of its
|
10
|
+
contributors may be used to endorse or promote products derived from this
|
11
|
+
software without specific prior written permission.
|
12
|
+
|
13
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
14
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
15
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
16
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
17
|
+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
18
|
+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
19
|
+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
20
|
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
21
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
22
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
23
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.markdown
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
# Official PayPal Express for Spree
|
2
|
+
|
3
|
+
[![Build Status](https://secure.travis-ci.org/spree/spree_paypal_express.png?branch=master)](http://travis-ci.org/spree/spree_paypal_express)
|
4
|
+
|
5
|
+
This is the official PayPal Express extension for Spree, based on the extension by PaulCC it has been extended to support Spree's
|
6
|
+
Billing Integrations which allows users to configure the PayPal Express gateway including API login / password and signatures fields
|
7
|
+
via the Admin UI.
|
8
|
+
|
9
|
+
This extension allows the store to use PayPal Express from two locations:
|
10
|
+
|
11
|
+
1. Checkout Payment - When configured the PayPal Express checkout button will appear alongside the standard credit card payment
|
12
|
+
options on the payment stage of the standard checkout. The selected shipping address and shipping method / costs are automatically
|
13
|
+
sent to the PayPal review page (along with detailed order information).
|
14
|
+
|
15
|
+
|
16
|
+
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
|
17
|
+
all shipping / addressing information on PaypPal's site. This also supports PayPal's Instant Update feature to retrieve shipping options live from
|
18
|
+
Spree when the user selects / changes their shipping address on PayPal's site.
|
19
|
+
|
20
|
+
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
|
21
|
+
the order (possibly select / change shipping address and method), then the user is redirected back to Spree to confirm the order. The user
|
22
|
+
MUST confirm the order on the Spree site before the payment is authorized / captured from PayPal (and the order is transitioned to the New state).
|
23
|
+
|
24
|
+
|
25
|
+
Installation
|
26
|
+
============
|
27
|
+
|
28
|
+
1. Add the following line to your application's Gemfile
|
29
|
+
|
30
|
+
gem "spree_paypal_express", :git => "git://github.com/spree/spree_paypal_express.git"
|
31
|
+
|
32
|
+
**Note:** The :git option is only required for the edge version, and can be removed to used the released gem.
|
33
|
+
|
34
|
+
2. Install the gem using Bundler:
|
35
|
+
|
36
|
+
bundle install
|
37
|
+
|
38
|
+
3. Copy & run migrations
|
39
|
+
|
40
|
+
bundle exec rails g spree_paypal_express:install
|
41
|
+
|
42
|
+
Versions
|
43
|
+
========
|
44
|
+
|
45
|
+
To determine the correct version of this extension, please refer to the Versionfile.
|
46
|
+
|
47
|
+
IPN & eCheck Support
|
48
|
+
===================
|
49
|
+
eCheck payments are now fully supported and PayPal's Instant Payment Notification service is also supported for receiving updates relating to eCheck payments only. To configure eCheck payments you'll need to:
|
50
|
+
|
51
|
+
1. Configure your PayPal account to accept eCheck payments (under Profile on PayPal's website).
|
52
|
+
|
53
|
+
2. Set the IPN URL on your PayPal account (under Profile on PayPal's website) to:
|
54
|
+
|
55
|
+
https://www.yourstore.com/paypal_notify
|
56
|
+
|
57
|
+
3. Enable auto_capture within Spree (as eCheck payments are only supported for purchase and not authorize requests).
|
58
|
+
|
59
|
+
Spree::Config.set(:auto_capture => true)
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
Configuration
|
64
|
+
=============
|
65
|
+
|
66
|
+
1. Before you begin
|
67
|
+
|
68
|
+
You'll need to have a Paypal developer account (developer.paypal.com) and both buyer and seller test accounts.
|
69
|
+
|
70
|
+
**Tip:** these are sandbox only, so use email addresses and passwords that are easy to remember, e.g. buyer@example.com and seller@example.com.
|
71
|
+
|
72
|
+
Your sandbox credentials are available from the API Credentials link.
|
73
|
+
|
74
|
+
2. Setup the Payment Method
|
75
|
+
|
76
|
+
Log in as an admin and add a new **Payment Method** (under Configuration), using following details:
|
77
|
+
|
78
|
+
**Name:** Paypal Express
|
79
|
+
|
80
|
+
**Environment:** Development (or what ever environment you prefer)
|
81
|
+
|
82
|
+
**Active:** Yes
|
83
|
+
|
84
|
+
**Provider:** Spree::BillingIntegration::PaypalExpress
|
85
|
+
|
86
|
+
Click **Create* , and now add your credentials in the screen that follows:
|
87
|
+
|
88
|
+
**Review:** unchecked [1]
|
89
|
+
|
90
|
+
**Signature:** API signature from your paypal seller test account
|
91
|
+
|
92
|
+
**Server:** test (for Development or live for Production)
|
93
|
+
|
94
|
+
**Test Mode:** checked (or unchecked for Production)
|
95
|
+
|
96
|
+
**Password:** API Password from your paypal seller test account
|
97
|
+
|
98
|
+
**Login:** API Username from your paypal seller test account (care to use the API Username and not the Test Account address)
|
99
|
+
|
100
|
+
Click **Update**
|
101
|
+
|
102
|
+
Test Drive
|
103
|
+
==========
|
104
|
+
|
105
|
+
While testing PayPal Express checkout locally make sure you're logged into your PayPal **developer** account in another browser window before attempting a PayPal payment, as you'll be redirected and forced to sign in to your developer account.
|
106
|
+
|
107
|
+
1. Add an item to cart
|
108
|
+
|
109
|
+
2. Check out
|
110
|
+
|
111
|
+
3. Address step: complete it using a valid US address.
|
112
|
+
|
113
|
+
4. Delivery step: pick anything
|
114
|
+
|
115
|
+
5. On the Payment Step, you should see a PayPal button. You can select it directly or just click "Continue"
|
116
|
+
|
117
|
+
6. You will get redirected to PayPals sandbox site, be sure to log in as a **Buyer** / **Personal** test account and not the account you use to configure the Payment Method with.
|
118
|
+
|
119
|
+
7. You should now see the paypal order details screen with a Pay Now button.
|
120
|
+
|
121
|
+
8. Click Pay Now, and you should now be redirected back to Spree's order thank you page.
|
122
|
+
|
123
|
+
9. Log into the Admin UI and review the Order and Payment details to confirm the successful checkout.
|
124
|
+
|
125
|
+
|
126
|
+
Running Specs
|
127
|
+
=============
|
128
|
+
|
129
|
+
1. Create Test App
|
130
|
+
|
131
|
+
rake test_app
|
132
|
+
|
133
|
+
2. Run Specs
|
134
|
+
|
135
|
+
rake spec
|
136
|
+
|
137
|
+
NOTES
|
138
|
+
=====
|
139
|
+
|
140
|
+
To automatically capture funds or enable accepting eCheck payments, add this to you site extension's activate method:
|
141
|
+
|
142
|
+
if Spree::Config.instance
|
143
|
+
Spree::Config[:auto_capture] = true
|
144
|
+
end
|
145
|
+
|
146
|
+
[1] If you check the review checkbox in the admin section for Payment Methods/Paypal Express, the flow is slightly different. Instead of Pay Now on Paypal's order details page, it now says Continue. And the user is directed back to the spree app's Confirmation page showing a place order button. Use whichever suits your needs best. Personally, I leave review unchecked to cut down on the steps in the checkout flow.
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'spree/core/testing_support/common_rake'
|
4
|
+
|
5
|
+
RSpec::Core::RakeTask.new
|
6
|
+
|
7
|
+
task :default => [:spec ]
|
8
|
+
|
9
|
+
desc "Generates a dummy app for testing"
|
10
|
+
task :test_app do
|
11
|
+
ENV['LIB_NAME'] = 'spree_paypal_express'
|
12
|
+
Rake::Task['common:test_app'].invoke
|
13
|
+
end
|
data/Versionfile
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
"1.1.0" => { :branch => "1-1-stable" }
|
2
|
+
"1.0.0" => { :branch => "1-0-stable" }
|
3
|
+
"0.70.x" => { :ref => "bea1aa48e0089083546bec4b19565a40e9a50a20" }
|
4
|
+
"0.60.x" => { :ref => "073f2f814dd8f3ad2e66ddde2c7079d8c76e4d27" }
|
5
|
+
"0.50.x" => { :ref => "39a3b00602d591e5c27bf13941aa5c13e4b95579" }
|
Binary file
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,384 @@
|
|
1
|
+
module Spree
|
2
|
+
CheckoutController.class_eval do
|
3
|
+
before_filter :redirect_to_paypal_express_form_if_needed, :only => [:update]
|
4
|
+
|
5
|
+
def paypal_checkout
|
6
|
+
load_order
|
7
|
+
opts = all_opts(@order, params[:payment_method_id], 'checkout')
|
8
|
+
opts.merge!(address_options(@order))
|
9
|
+
@gateway = paypal_gateway
|
10
|
+
|
11
|
+
if Spree::Config[:auto_capture]
|
12
|
+
@ppx_response = @gateway.setup_purchase(opts[:money], opts)
|
13
|
+
else
|
14
|
+
@ppx_response = @gateway.setup_authorization(opts[:money], opts)
|
15
|
+
end
|
16
|
+
|
17
|
+
unless @ppx_response.success?
|
18
|
+
gateway_error(@ppx_response)
|
19
|
+
redirect_to edit_order_url(@order)
|
20
|
+
return
|
21
|
+
end
|
22
|
+
|
23
|
+
redirect_to(@gateway.redirect_url_for(response.token, :review => payment_method.preferred_review))
|
24
|
+
rescue ActiveMerchant::ConnectionError => e
|
25
|
+
gateway_error I18n.t(:unable_to_connect_to_gateway)
|
26
|
+
redirect_to :back
|
27
|
+
end
|
28
|
+
|
29
|
+
def paypal_payment
|
30
|
+
load_order
|
31
|
+
opts = all_opts(@order,params[:payment_method_id], 'payment')
|
32
|
+
opts.merge!(address_options(@order))
|
33
|
+
@gateway = paypal_gateway
|
34
|
+
|
35
|
+
if Spree::Config[:auto_capture]
|
36
|
+
@ppx_response = @gateway.setup_purchase(opts[:money], opts)
|
37
|
+
else
|
38
|
+
@ppx_response = @gateway.setup_authorization(opts[:money], opts)
|
39
|
+
end
|
40
|
+
|
41
|
+
unless @ppx_response.success?
|
42
|
+
gateway_error(@ppx_response)
|
43
|
+
redirect_to edit_order_checkout_url(@order, :state => "payment")
|
44
|
+
return
|
45
|
+
end
|
46
|
+
|
47
|
+
redirect_to(@gateway.redirect_url_for(@ppx_response.token, :review => payment_method.preferred_review))
|
48
|
+
rescue ActiveMerchant::ConnectionError => e
|
49
|
+
gateway_error I18n.t(:unable_to_connect_to_gateway)
|
50
|
+
redirect_to :back
|
51
|
+
end
|
52
|
+
|
53
|
+
def paypal_confirm
|
54
|
+
load_order
|
55
|
+
|
56
|
+
opts = { :token => params[:token], :payer_id => params[:PayerID] }.merge all_opts(@order, params[:payment_method_id], 'payment')
|
57
|
+
gateway = paypal_gateway
|
58
|
+
|
59
|
+
@ppx_details = gateway.details_for params[:token]
|
60
|
+
|
61
|
+
if @ppx_details.success?
|
62
|
+
# now save the updated order info
|
63
|
+
|
64
|
+
Spree::PaypalAccount.create(:email => @ppx_details.params["payer"],
|
65
|
+
:payer_id => @ppx_details.params["payer_id"],
|
66
|
+
:payer_country => @ppx_details.params["payer_country"],
|
67
|
+
:payer_status => @ppx_details.params["payer_status"])
|
68
|
+
|
69
|
+
@order.special_instructions = @ppx_details.params["note"]
|
70
|
+
|
71
|
+
unless payment_method.preferred_no_shipping
|
72
|
+
ship_address = @ppx_details.address
|
73
|
+
order_ship_address = Spree::Address.new :firstname => @ppx_details.params["first_name"],
|
74
|
+
:lastname => @ppx_details.params["last_name"],
|
75
|
+
:address1 => ship_address["address1"],
|
76
|
+
:address2 => ship_address["address2"],
|
77
|
+
:city => ship_address["city"],
|
78
|
+
:country => Spree::Country.find_by_iso(ship_address["country"]),
|
79
|
+
:zipcode => ship_address["zip"],
|
80
|
+
# phone is currently blanked in AM's PPX response lib
|
81
|
+
:phone => @ppx_details.params["phone"] || "(not given)"
|
82
|
+
|
83
|
+
if (state = Spree::State.find_by_abbr(ship_address["state"].upcase))
|
84
|
+
order_ship_address.state = state
|
85
|
+
else
|
86
|
+
order_ship_address.state_name = ship_address["state"]
|
87
|
+
end
|
88
|
+
order_ship_address.save!
|
89
|
+
|
90
|
+
@order.ship_address = order_ship_address
|
91
|
+
@order.bill_address ||= order_ship_address
|
92
|
+
end
|
93
|
+
@order.state = "payment"
|
94
|
+
@order.save
|
95
|
+
|
96
|
+
if payment_method.preferred_review
|
97
|
+
@order.next
|
98
|
+
render 'spree/shared/paypal_express_confirm'
|
99
|
+
else
|
100
|
+
paypal_finish
|
101
|
+
end
|
102
|
+
|
103
|
+
else
|
104
|
+
gateway_error(@ppx_details)
|
105
|
+
|
106
|
+
#Failed trying to get payment details from PPX
|
107
|
+
redirect_to edit_order_checkout_url(@order, :state => "payment")
|
108
|
+
end
|
109
|
+
rescue ActiveMerchant::ConnectionError => e
|
110
|
+
gateway_error I18n.t(:unable_to_connect_to_gateway)
|
111
|
+
redirect_to edit_order_url(@order)
|
112
|
+
end
|
113
|
+
|
114
|
+
def paypal_finish
|
115
|
+
load_order
|
116
|
+
|
117
|
+
opts = { :token => params[:token], :payer_id => params[:PayerID] }.merge all_opts(@order, params[:payment_method_id], 'payment' )
|
118
|
+
gateway = paypal_gateway
|
119
|
+
|
120
|
+
method = Spree::Config[:auto_capture] ? :purchase : :authorize
|
121
|
+
ppx_auth_response = gateway.send(method, (@order.total*100).to_i, opts)
|
122
|
+
|
123
|
+
paypal_account = Spree::PaypalAccount.find_by_payer_id(params[:PayerID])
|
124
|
+
|
125
|
+
payment = @order.payments.create(
|
126
|
+
:amount => ppx_auth_response.params["gross_amount"].to_f,
|
127
|
+
:source => paypal_account,
|
128
|
+
:source_type => 'Spree::PaypalAccount',
|
129
|
+
:payment_method_id => params[:payment_method_id],
|
130
|
+
:response_code => ppx_auth_response.authorization,
|
131
|
+
:avs_response => ppx_auth_response.avs_result["code"])
|
132
|
+
|
133
|
+
payment.started_processing!
|
134
|
+
|
135
|
+
record_log payment, ppx_auth_response
|
136
|
+
|
137
|
+
if ppx_auth_response.success?
|
138
|
+
#confirm status
|
139
|
+
case ppx_auth_response.params["payment_status"]
|
140
|
+
when "Completed"
|
141
|
+
payment.complete!
|
142
|
+
when "Pending"
|
143
|
+
payment.pend!
|
144
|
+
else
|
145
|
+
payment.pend!
|
146
|
+
Rails.logger.error "Unexpected response from PayPal Express"
|
147
|
+
Rails.logger.error ppx_auth_response.to_yaml
|
148
|
+
end
|
149
|
+
|
150
|
+
@order.update_attributes({:state => "complete", :completed_at => Time.now}, :without_protection => true)
|
151
|
+
|
152
|
+
state_callback(:after) # So that after_complete is called, setting session[:order_id] to nil
|
153
|
+
|
154
|
+
# Since we dont rely on state machine callback, we just explicitly call this method for spree_store_credits
|
155
|
+
if @order.respond_to?(:consume_users_credit, true)
|
156
|
+
@order.send(:consume_users_credit)
|
157
|
+
end
|
158
|
+
|
159
|
+
@order.finalize!
|
160
|
+
flash[:notice] = I18n.t(:order_processed_successfully)
|
161
|
+
redirect_to completion_route
|
162
|
+
|
163
|
+
else
|
164
|
+
payment.failure!
|
165
|
+
order_params = {}
|
166
|
+
gateway_error(ppx_auth_response)
|
167
|
+
|
168
|
+
#Failed trying to complete pending payment!
|
169
|
+
redirect_to edit_order_checkout_url(@order, :state => "payment")
|
170
|
+
end
|
171
|
+
rescue ActiveMerchant::ConnectionError => e
|
172
|
+
gateway_error I18n.t(:unable_to_connect_to_gateway)
|
173
|
+
redirect_to edit_order_url(@order)
|
174
|
+
end
|
175
|
+
|
176
|
+
private
|
177
|
+
|
178
|
+
def asset_url(_path)
|
179
|
+
URI::HTTP.build(:path => ActionController::Base.helpers.asset_path(_path), :host => Spree::Config[:site_url]).to_s
|
180
|
+
end
|
181
|
+
|
182
|
+
def record_log(payment, response)
|
183
|
+
payment.log_entries.create(:details => response.to_yaml)
|
184
|
+
end
|
185
|
+
|
186
|
+
def redirect_to_paypal_express_form_if_needed
|
187
|
+
return unless (params[:state] == "payment")
|
188
|
+
return unless params[:order][:payments_attributes]
|
189
|
+
|
190
|
+
if @order.update_attributes(object_params)
|
191
|
+
if params[:order][:coupon_code] and !params[:order][:coupon_code].blank? and @order.coupon_code.present?
|
192
|
+
fire_event('spree.checkout.coupon_code_added', :coupon_code => @order.coupon_code)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
load_order
|
197
|
+
payment_method = Spree::PaymentMethod.find(params[:order][:payments_attributes].first[:payment_method_id])
|
198
|
+
|
199
|
+
if payment_method.kind_of?(Spree::BillingIntegration::PaypalExpress) || payment_method.kind_of?(Spree::BillingIntegration::PaypalExpressUk)
|
200
|
+
redirect_to(paypal_payment_order_checkout_url(@order, :payment_method_id => payment_method.id)) and return
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def fixed_opts
|
205
|
+
if Spree::PaypalExpress::Config[:paypal_express_local_confirm].nil?
|
206
|
+
user_action = "continue"
|
207
|
+
else
|
208
|
+
user_action = Spree::PaypalExpress::Config[:paypal_express_local_confirm] == "t" ? "continue" : "commit"
|
209
|
+
end
|
210
|
+
|
211
|
+
#asset_url doesn't like Spree::Config[:logo] being an absolute url
|
212
|
+
#if statement didn't work within hash
|
213
|
+
if URI.parse(Spree::Config[:logo]).absolute?
|
214
|
+
chosen_image = Spree::Config[:logo]
|
215
|
+
else
|
216
|
+
chosen_image = asset_url(Spree::Config[:logo])
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
{ :description => "Goods from #{Spree::Config[:site_name]}", # site details...
|
221
|
+
#:page_style => "foobar", # merchant account can set named config
|
222
|
+
:background_color => "ffffff", # must be hex only, six chars
|
223
|
+
:header_background_color => "ffffff",
|
224
|
+
:header_border_color => "ffffff",
|
225
|
+
:header_image => chosen_image,
|
226
|
+
:allow_note => true,
|
227
|
+
:locale => user_locale,
|
228
|
+
:req_confirm_shipping => false, # for security, might make an option later
|
229
|
+
:user_action => user_action
|
230
|
+
|
231
|
+
# WARNING -- don't use :ship_discount, :insurance_offered, :insurance since
|
232
|
+
# they've not been tested and may trigger some paypal bugs, eg not showing order
|
233
|
+
# see http://www.pdncommunity.com/t5/PayPal-Developer-Blog/Displaying-Order-Details-in-Express-Checkout/bc-p/92902#C851
|
234
|
+
}
|
235
|
+
end
|
236
|
+
|
237
|
+
def user_locale
|
238
|
+
I18n.locale.to_s
|
239
|
+
end
|
240
|
+
|
241
|
+
# hook to override paypal site options
|
242
|
+
def paypal_site_opts
|
243
|
+
{:currency => payment_method.preferred_currency, :allow_guest_checkout => payment_method.preferred_allow_guest_checkout }
|
244
|
+
end
|
245
|
+
|
246
|
+
def order_opts(order, payment_method, stage)
|
247
|
+
items = order.line_items.map do |item|
|
248
|
+
price = (item.price * 100).to_i # convert for gateway
|
249
|
+
{ :name => item.variant.product.name,
|
250
|
+
:description => (item.variant.product.description[0..120] if item.variant.product.description),
|
251
|
+
:number => item.variant.sku,
|
252
|
+
:quantity => item.quantity,
|
253
|
+
:amount => price,
|
254
|
+
:weight => item.variant.weight,
|
255
|
+
:height => item.variant.height,
|
256
|
+
:width => item.variant.width,
|
257
|
+
:depth => item.variant.weight }
|
258
|
+
end
|
259
|
+
|
260
|
+
credits = order.adjustments.eligible.map do |credit|
|
261
|
+
if credit.amount < 0.00
|
262
|
+
{ :name => credit.label,
|
263
|
+
:description => credit.label,
|
264
|
+
:sku => credit.id,
|
265
|
+
:quantity => 1,
|
266
|
+
:amount => (credit.amount*100).to_i }
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
credits_total = 0
|
271
|
+
credits.compact!
|
272
|
+
if credits.present?
|
273
|
+
items.concat credits
|
274
|
+
credits_total = credits.map {|i| i[:amount] * i[:quantity] }.sum
|
275
|
+
end
|
276
|
+
|
277
|
+
opts = { :return_url => paypal_confirm_order_checkout_url(order, :payment_method_id => payment_method),
|
278
|
+
:cancel_return_url => edit_order_checkout_url(order, :state => :payment),
|
279
|
+
:order_id => order.number,
|
280
|
+
:custom => order.number,
|
281
|
+
:items => items,
|
282
|
+
:subtotal => ((order.item_total * 100) + credits_total).to_i,
|
283
|
+
:tax => (order.tax_total*100).to_i,
|
284
|
+
:shipping => (order.ship_total*100).to_i,
|
285
|
+
:money => (order.total * 100 ).to_i }
|
286
|
+
|
287
|
+
if stage == "checkout"
|
288
|
+
opts[:handling] = 0
|
289
|
+
|
290
|
+
opts[:callback_url] = spree_root_url + "paypal_express_callbacks/#{order.number}"
|
291
|
+
opts[:callback_timeout] = 3
|
292
|
+
elsif stage == "payment"
|
293
|
+
#hack to add float rounding difference in as handling fee - prevents PayPal from rejecting orders
|
294
|
+
#because the integer totals are different from the float based total. This is temporary and will be
|
295
|
+
#removed once Spree's currency values are persisted as integers (normally only 1c)
|
296
|
+
opts[:handling] = (order.total*100).to_i - opts.slice(:subtotal, :tax, :shipping).values.sum
|
297
|
+
end
|
298
|
+
|
299
|
+
opts
|
300
|
+
end
|
301
|
+
|
302
|
+
def address_options(order)
|
303
|
+
if payment_method.preferred_no_shipping
|
304
|
+
{ :no_shipping => true }
|
305
|
+
else
|
306
|
+
{
|
307
|
+
:no_shipping => false,
|
308
|
+
:address_override => true,
|
309
|
+
:address => {
|
310
|
+
:name => "#{order.ship_address.firstname} #{order.ship_address.lastname}",
|
311
|
+
:address1 => order.ship_address.address1,
|
312
|
+
:address2 => order.ship_address.address2,
|
313
|
+
:city => order.ship_address.city,
|
314
|
+
:state => order.ship_address.state.nil? ? order.ship_address.state_name.to_s : order.ship_address.state.abbr,
|
315
|
+
:country => order.ship_address.country.iso,
|
316
|
+
:zip => order.ship_address.zipcode,
|
317
|
+
:phone => order.ship_address.phone
|
318
|
+
}
|
319
|
+
}
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
def all_opts(order, payment_method, stage=nil)
|
324
|
+
opts = fixed_opts.merge(order_opts(order, payment_method, stage)).merge(paypal_site_opts)
|
325
|
+
|
326
|
+
if stage == "payment"
|
327
|
+
opts.merge! flat_rate_shipping_and_handling_options(order, stage)
|
328
|
+
end
|
329
|
+
|
330
|
+
# suggest current user's email or any email stored in the order
|
331
|
+
opts[:email] = current_user ? current_user.email : order.email
|
332
|
+
|
333
|
+
opts
|
334
|
+
end
|
335
|
+
|
336
|
+
# hook to allow applications to load in their own shipping and handling costs
|
337
|
+
def flat_rate_shipping_and_handling_options(order, stage)
|
338
|
+
# max_fallback = 0.0
|
339
|
+
# shipping_options = ShippingMethod.all.map do |shipping_method|
|
340
|
+
# max_fallback = shipping_method.fallback_amount if shipping_method.fallback_amount > max_fallback
|
341
|
+
# { :name => "#{shipping_method.id}",
|
342
|
+
# :label => "#{shipping_method.name} - #{shipping_method.zone.name}",
|
343
|
+
# :amount => (shipping_method.fallback_amount*100) + 1,
|
344
|
+
# :default => shipping_method.is_default }
|
345
|
+
# end
|
346
|
+
#
|
347
|
+
#
|
348
|
+
# default_shipping_method = ShippingMethod.find(:first, :conditions => {:is_default => true})
|
349
|
+
#
|
350
|
+
# opts = { :shipping_options => shipping_options,
|
351
|
+
# :max_amount => (order.total + max_fallback)*100
|
352
|
+
# }
|
353
|
+
#
|
354
|
+
# opts[:shipping] = (default_shipping_method.nil? ? 0 : default_shipping_method.fallback_amount) if stage == "checkout"
|
355
|
+
#
|
356
|
+
# opts
|
357
|
+
{}
|
358
|
+
end
|
359
|
+
|
360
|
+
def gateway_error(response)
|
361
|
+
if response.is_a? ActiveMerchant::Billing::Response
|
362
|
+
text = response.params['message'] ||
|
363
|
+
response.params['response_reason_text'] ||
|
364
|
+
response.message
|
365
|
+
else
|
366
|
+
text = response.to_s
|
367
|
+
end
|
368
|
+
|
369
|
+
msg = "#{I18n.t('gateway_error')}: #{text}"
|
370
|
+
logger.error(msg)
|
371
|
+
flash[:error] = msg
|
372
|
+
end
|
373
|
+
|
374
|
+
# create the gateway from the supplied options
|
375
|
+
def payment_method
|
376
|
+
@payment_method ||= Spree::PaymentMethod.find(params[:payment_method_id])
|
377
|
+
end
|
378
|
+
|
379
|
+
def paypal_gateway
|
380
|
+
payment_method.provider
|
381
|
+
end
|
382
|
+
|
383
|
+
end
|
384
|
+
end
|