spree_paypal_express_mutalis 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/.gitignore +10 -0
- data/.rspec +1 -0
- data/.travis.yml +16 -0
- data/CONTRIBUTING.md +81 -0
- data/Gemfile +8 -0
- data/LICENSE.md +26 -0
- data/README.md +86 -0
- data/Rakefile +23 -0
- data/app/assets/javascripts/admin/spree_paypal_express.js +25 -0
- data/app/assets/javascripts/store/spree_paypal_express.js +19 -0
- data/app/assets/stylesheets/admin/spree_paypal_express.css +3 -0
- data/app/assets/stylesheets/store/spree_paypal_express.css +3 -0
- data/app/controllers/spree/admin/payments_controller_decorator.rb +19 -0
- data/app/controllers/spree/admin/paypal_payments_controller.rb +15 -0
- data/app/controllers/spree/paypal_controller.rb +144 -0
- data/app/models/spree/gateway/pay_pal_express.rb +100 -0
- data/app/models/spree/paypal_express_checkout.rb +4 -0
- data/app/views/spree/admin/payments/_paypal_complete.html.erb +20 -0
- data/app/views/spree/admin/payments/paypal_refund.html.erb +31 -0
- data/app/views/spree/admin/payments/source_forms/_paypal.html.erb +6 -0
- data/app/views/spree/admin/payments/source_views/_paypal.html.erb +35 -0
- data/app/views/spree/checkout/payment/_paypal.html.erb +6 -0
- data/config/locales/en.yml +16 -0
- data/config/routes.rb +18 -0
- data/db/migrate/20130723042610_create_spree_paypal_express_checkouts.rb +8 -0
- data/db/migrate/20130808030836_add_transaction_id_to_spree_paypal_express_checkouts.rb +6 -0
- data/db/migrate/20130809013846_add_state_to_spree_paypal_express_checkouts.rb +5 -0
- data/db/migrate/20130809014319_add_refunded_fields_to_spree_paypal_express_checkouts.rb +8 -0
- data/lib/generators/spree_paypal_express/install/install_generator.rb +31 -0
- data/lib/spree_paypal_express.rb +3 -0
- data/lib/spree_paypal_express/engine.rb +26 -0
- data/lib/spree_paypal_express/factories.rb +6 -0
- data/lib/spree_paypal_express/version.rb +3 -0
- data/script/rails +7 -0
- data/spec/features/paypal_spec.rb +244 -0
- data/spec/models/pay_pal_express_spec.rb +50 -0
- data/spec/spec_helper.rb +45 -0
- data/spree_paypal_express.gemspec +39 -0
- metadata +253 -0
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'paypal-sdk-merchant'
|
2
|
+
module Spree
|
3
|
+
class Gateway::PayPalExpress < Gateway
|
4
|
+
preference :login, :string
|
5
|
+
preference :password, :string
|
6
|
+
preference :signature, :string
|
7
|
+
preference :server, :string, default: 'sandbox'
|
8
|
+
|
9
|
+
def supports?(source)
|
10
|
+
true
|
11
|
+
end
|
12
|
+
|
13
|
+
def provider_class
|
14
|
+
::PayPal::SDK::Merchant::API
|
15
|
+
end
|
16
|
+
|
17
|
+
def provider
|
18
|
+
::PayPal::SDK.configure(
|
19
|
+
:mode => preferred_server.present? ? preferred_server : "sandbox",
|
20
|
+
:username => preferred_login,
|
21
|
+
:password => preferred_password,
|
22
|
+
:signature => preferred_signature)
|
23
|
+
provider_class.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def auto_capture?
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
def method_type
|
31
|
+
'paypal'
|
32
|
+
end
|
33
|
+
|
34
|
+
def purchase(amount, express_checkout, gateway_options={})
|
35
|
+
pp_request = provider.build_do_express_checkout_payment({
|
36
|
+
:DoExpressCheckoutPaymentRequestDetails => {
|
37
|
+
:PaymentAction => "Sale",
|
38
|
+
:Token => express_checkout.token,
|
39
|
+
:PayerID => express_checkout.payer_id,
|
40
|
+
:PaymentDetails => [{
|
41
|
+
:OrderTotal => {
|
42
|
+
:currencyID => Spree::Config[:currency],
|
43
|
+
# gsub is here because PayPal fails to acknowledge
|
44
|
+
# that some people like their currencies written as:
|
45
|
+
# 21,99
|
46
|
+
# As opposed to:
|
47
|
+
# 21.99
|
48
|
+
# The international payments company fails to handle
|
49
|
+
# international payment amounts. SMH.
|
50
|
+
:value => ::Money.new(amount, Spree::Config[:currency]).to_s.gsub(',', '.') }
|
51
|
+
}]
|
52
|
+
}
|
53
|
+
})
|
54
|
+
|
55
|
+
pp_response = provider.do_express_checkout_payment(pp_request)
|
56
|
+
if pp_response.success?
|
57
|
+
# We need to store the transaction id for the future.
|
58
|
+
# This is mainly so we can use it later on to refund the payment if the user wishes.
|
59
|
+
transaction_id = pp_response.do_express_checkout_payment_response_details.payment_info.first.transaction_id
|
60
|
+
express_checkout.update_column(:transaction_id, transaction_id)
|
61
|
+
# This is rather hackish, required for payment/processing handle_response code.
|
62
|
+
Class.new do
|
63
|
+
def success?; true; end
|
64
|
+
def authorization; nil; end
|
65
|
+
end.new
|
66
|
+
else
|
67
|
+
class << pp_response
|
68
|
+
def to_s
|
69
|
+
errors.map(&:long_message).join(" ")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
pp_response
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def refund(payment, amount)
|
77
|
+
refund_type = payment.amount == amount.to_f ? "Full" : "Partial"
|
78
|
+
refund_transaction = provider.build_refund_transaction({
|
79
|
+
:TransactionID => payment.source.transaction_id,
|
80
|
+
:RefundType => refund_type,
|
81
|
+
:Amount => {
|
82
|
+
:currencyID => payment.currency,
|
83
|
+
:value => amount },
|
84
|
+
:RefundSource => "any" })
|
85
|
+
refund_transaction_response = provider.refund_transaction(refund_transaction)
|
86
|
+
if refund_transaction_response.success?
|
87
|
+
payment.source.update_attributes({
|
88
|
+
:refunded_at => Time.now,
|
89
|
+
:refund_transaction_id => refund_transaction_response.RefundTransactionID,
|
90
|
+
:state => "refunded",
|
91
|
+
:refund_type => refund_type
|
92
|
+
} )
|
93
|
+
end
|
94
|
+
refund_transaction_response
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# payment.state = 'completed'
|
100
|
+
# current_order.state = 'complete'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<%= form_tag paypal_refund_admin_order_payment_path(@order, @payment) do %>
|
2
|
+
<div class="label-block left five columns alpha">
|
3
|
+
<div>
|
4
|
+
<fieldset data-hook="admin_variant_new_form">
|
5
|
+
<legend><%= Spree.t('refund', :scope => :paypal) %></legend>
|
6
|
+
<div class='field'>
|
7
|
+
<%= label_tag 'refund_amount', Spree.t(:refund_amount, :scope => 'paypal') %>
|
8
|
+
<small><em><%= Spree.t(:original_amount, :scope => 'paypal', :amount => @payment.display_amount) %></em></small><br>
|
9
|
+
<% symbol = ::Money.new(1, Spree::Config[:currency]).symbol %>
|
10
|
+
<% if Spree::Config[:currency_symbol_position] == "before" %>
|
11
|
+
<%= symbol %><%= text_field_tag 'refund_amount', @payment.amount %>
|
12
|
+
<% else %>
|
13
|
+
<%= text_field_tag 'refund_amount', @payment.amount %><%= symbol %>
|
14
|
+
<% end %>
|
15
|
+
</div>
|
16
|
+
<%= button Spree.t(:refund, :scope => 'paypal'), 'icon-dollar' %>
|
17
|
+
</fieldset>
|
18
|
+
</div>
|
19
|
+
</div>
|
20
|
+
<% end %>
|
@@ -0,0 +1,31 @@
|
|
1
|
+
<%= render :partial => 'spree/admin/shared/order_tabs', :locals => { :current => 'Payments' } %>
|
2
|
+
|
3
|
+
<% content_for :page_title do %>
|
4
|
+
<i class="icon-arrow-right"></i>
|
5
|
+
<%= link_to Spree.t(:payments), admin_order_payments_path(@order) %>
|
6
|
+
<i class="icon-arrow-right"></i>
|
7
|
+
<%= payment_method_name(@payment) %>
|
8
|
+
<i class="icon-arrow-right"></i>
|
9
|
+
<%= Spree.t('refund', :scope => :paypal) %>
|
10
|
+
<% end %>
|
11
|
+
|
12
|
+
<%= form_tag paypal_refund_admin_order_payment_path(@order, @payment) do %>
|
13
|
+
<div class="label-block left five columns alpha">
|
14
|
+
<div>
|
15
|
+
<fieldset data-hook="admin_variant_new_form">
|
16
|
+
<legend><%= Spree.t('refund', :scope => :paypal) %></legend>
|
17
|
+
<div class='field'>
|
18
|
+
<%= label_tag 'refund_amount', Spree.t(:refund_amount, :scope => 'paypal') %>
|
19
|
+
<small><em><%= Spree.t(:original_amount, :scope => 'paypal', :amount => @payment.display_amount) %></em></small><br>
|
20
|
+
<% symbol = ::Money.new(1, Spree::Config[:currency]).symbol %>
|
21
|
+
<% if Spree::Config[:currency_symbol_position] == "before" %>
|
22
|
+
<%= symbol %><%= text_field_tag 'refund_amount', @payment.amount %>
|
23
|
+
<% else %>
|
24
|
+
<%= text_field_tag 'refund_amount', @payment.amount %><%= symbol %>
|
25
|
+
<% end %>
|
26
|
+
</div>
|
27
|
+
<%= button Spree.t(:refund, :scope => 'paypal'), 'icon-dollar' %>
|
28
|
+
</fieldset>
|
29
|
+
</div>
|
30
|
+
</div>
|
31
|
+
<% end %>
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<fieldset data-hook="paypal">
|
2
|
+
<legend align="center"><%= Spree.t(:transaction, :scope => :paypal) %></legend>
|
3
|
+
|
4
|
+
<div class="row">
|
5
|
+
<div class="alpha six columns">
|
6
|
+
<dl>
|
7
|
+
<dt><%= Spree.t(:payer_id, :scope => :paypal) %>:</dt>
|
8
|
+
<dd><%= payment.source.payer_id %></dd>
|
9
|
+
|
10
|
+
<dt><%= Spree.t(:token, :scope => :paypal) %>:</dt>
|
11
|
+
<dd><%= payment.source.token %></dd>
|
12
|
+
|
13
|
+
<dt><%= Spree.t(:transaction_id) %>:</dt>
|
14
|
+
<dd><%= payment.source.transaction_id %></dd>
|
15
|
+
</dl>
|
16
|
+
</div>
|
17
|
+
|
18
|
+
<% if payment.source.state != 'refunded' %>
|
19
|
+
<%= button_link_to Spree.t('actions.refund', :scope => :paypal), spree.paypal_refund_admin_order_payment_path(@order, payment), :icon => 'icon-dollar' %>
|
20
|
+
<% else %>
|
21
|
+
<div class="alpha six columns">
|
22
|
+
<dl>
|
23
|
+
<dt><%= Spree.t(:state, :scope => :paypal) %>:</dt>
|
24
|
+
<dd><%= payment.source.state.titleize %></dd>
|
25
|
+
|
26
|
+
<dt><%= Spree.t(:refunded_at, :scope => :paypal) %>:</dt>
|
27
|
+
<dd><%= pretty_time(payment.source.refunded_at) %></dd>
|
28
|
+
|
29
|
+
<dt><%= Spree.t(:refund_transaction_id, :scope => :paypal) %>:</dt>
|
30
|
+
<dd><%= payment.source.refund_transaction_id %></dd>
|
31
|
+
</dl>
|
32
|
+
</div>
|
33
|
+
<% end %>
|
34
|
+
</div>
|
35
|
+
</fieldset>
|
@@ -0,0 +1,6 @@
|
|
1
|
+
|
2
|
+
<%= link_to(image_tag("https://www.paypal.com/en_US/i/btn/btn_xpressCheckout.gif"), paypal_express_url(:payment_method_id => payment_method.id), :method => :post, :id => "paypal_button") %>
|
3
|
+
|
4
|
+
<script>
|
5
|
+
SpreePaypalExpress.paymentMethodID = "<%= payment_method.id %>"
|
6
|
+
</script>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
---
|
2
|
+
en:
|
3
|
+
spree:
|
4
|
+
paypal:
|
5
|
+
already_refunded: "This payment has been refunded and no further action can be taken on it."
|
6
|
+
transaction: "PayPal Transaction"
|
7
|
+
payer_id: "Payer ID"
|
8
|
+
transaction_id: "Transaction ID"
|
9
|
+
token: "Token"
|
10
|
+
refund: "Refund"
|
11
|
+
refund_amount: "Amount"
|
12
|
+
original_amount: "Original amount: %{amount}"
|
13
|
+
refund_successful: "PayPal refund successful"
|
14
|
+
refund_unsuccessful: "PayPal refund unsuccessful"
|
15
|
+
actions:
|
16
|
+
refund: "Refund"
|
data/config/routes.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Spree::Core::Engine.routes.draw do
|
2
|
+
post '/paypal', :to => "paypal#express", :as => :paypal_express
|
3
|
+
get '/paypal/confirm', :to => "paypal#confirm", :as => :confirm_paypal
|
4
|
+
get '/paypal/cancel', :to => "paypal#cancel", :as => :cancel_paypal
|
5
|
+
get '/paypal/notify', :to => "paypal#notify", :as => :notify_paypal
|
6
|
+
|
7
|
+
namespace :admin do
|
8
|
+
# Using :only here so it doesn't redraw those routes
|
9
|
+
resources :orders, :only => [] do
|
10
|
+
resources :payments, :only => [] do
|
11
|
+
member do
|
12
|
+
get 'paypal_refund'
|
13
|
+
post 'paypal_refund'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
class AddRefundedFieldsToSpreePaypalExpressCheckouts < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
add_column :spree_paypal_express_checkouts, :refund_transaction_id, :string
|
4
|
+
add_column :spree_paypal_express_checkouts, :refunded_at, :datetime
|
5
|
+
add_column :spree_paypal_express_checkouts, :refund_type, :string
|
6
|
+
add_column :spree_paypal_express_checkouts, :created_at, :datetime
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module SpreePaypalExpress
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
|
5
|
+
class_option :auto_run_migrations, :type => :boolean, :default => false
|
6
|
+
|
7
|
+
def add_javascripts
|
8
|
+
append_file 'app/assets/javascripts/store/all.js', "//= require store/spree_paypal_express\n"
|
9
|
+
append_file 'app/assets/javascripts/admin/all.js', "//= require admin/spree_paypal_express\n"
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_stylesheets
|
13
|
+
inject_into_file 'app/assets/stylesheets/store/all.css', " *= require store/spree_paypal_express\n", :before => /\*\//, :verbose => true
|
14
|
+
inject_into_file 'app/assets/stylesheets/admin/all.css', " *= require admin/spree_paypal_express\n", :before => /\*\//, :verbose => true
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_migrations
|
18
|
+
run 'bundle exec rake railties:install:migrations FROM=spree_paypal_express'
|
19
|
+
end
|
20
|
+
|
21
|
+
def run_migrations
|
22
|
+
run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask 'Would you like to run the migrations now? [Y/n]')
|
23
|
+
if run_migrations
|
24
|
+
run 'bundle exec rake db:migrate'
|
25
|
+
else
|
26
|
+
puts 'Skipping rake db:migrate, don\'t forget to run it!'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module SpreePaypalExpress
|
2
|
+
class Engine < Rails::Engine
|
3
|
+
require 'spree/core'
|
4
|
+
isolate_namespace Spree
|
5
|
+
engine_name 'spree_paypal_express'
|
6
|
+
|
7
|
+
config.autoload_paths += %W(#{config.root}/lib)
|
8
|
+
|
9
|
+
# use rspec for tests
|
10
|
+
config.generators do |g|
|
11
|
+
g.test_framework :rspec
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.activate
|
15
|
+
Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')) do |c|
|
16
|
+
Rails.configuration.cache_classes ? require(c) : load(c)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
config.to_prepare &method(:activate).to_proc
|
21
|
+
|
22
|
+
initializer "spree.paypal_express.payment_methods", :after => "spree.register.payment_methods" do |app|
|
23
|
+
app.config.spree.payment_methods << Spree::Gateway::PayPalExpress
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
FactoryGirl.define do
|
2
|
+
# Define your Spree extensions Factories within this file to enable applications, and other extensions to use and override them.
|
3
|
+
#
|
4
|
+
# Example adding this to your spec_helper will load these Factories for use:
|
5
|
+
# require 'spree_paypal_express/factories'
|
6
|
+
end
|
data/script/rails
ADDED
@@ -0,0 +1,244 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "PayPal", :js => true do
|
4
|
+
let!(:product) { FactoryGirl.create(:product, :name => 'iPad') }
|
5
|
+
before do
|
6
|
+
@gateway = Spree::Gateway::PayPalExpress.create!({
|
7
|
+
:preferred_login => "paypal_api1.ryanbigg.com",
|
8
|
+
:preferred_password => "1373587879",
|
9
|
+
:preferred_signature => "ACOYQHq-aXKftiD4jURhihawsVSsAsaMr4qH4Tz4K17mJoa3K4M0Dvop",
|
10
|
+
:name => "PayPal",
|
11
|
+
:active => true,
|
12
|
+
:environment => Rails.env
|
13
|
+
})
|
14
|
+
FactoryGirl.create(:shipping_method)
|
15
|
+
end
|
16
|
+
def fill_in_billing
|
17
|
+
within("#billing") do
|
18
|
+
fill_in "First Name", :with => "Test"
|
19
|
+
fill_in "Last Name", :with => "User"
|
20
|
+
fill_in "Street Address", :with => "1 User Lane"
|
21
|
+
# City, State and ZIP must all match for PayPal to be happy
|
22
|
+
fill_in "City", :with => "Adamsville"
|
23
|
+
select "United States of America", :from => "order_bill_address_attributes_country_id"
|
24
|
+
select "Alabama", :from => "order_bill_address_attributes_state_id"
|
25
|
+
fill_in "Zip", :with => "35005"
|
26
|
+
fill_in "Phone", :with => "555-AME-RICA"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def switch_to_paypal_login
|
31
|
+
# If you go through a payment once in the sandbox, it remembers your preferred setting.
|
32
|
+
# It defaults to the *wrong* setting for the first time, so we need to have this method.
|
33
|
+
unless page.has_selector?("#login_email")
|
34
|
+
find("#loadLogin").click
|
35
|
+
end
|
36
|
+
end
|
37
|
+
it "pays for an order successfully" do
|
38
|
+
visit spree.root_path
|
39
|
+
click_link 'iPad'
|
40
|
+
click_button 'Add To Cart'
|
41
|
+
click_button 'Checkout'
|
42
|
+
within("#guest_checkout") do
|
43
|
+
fill_in "Email", :with => "test@example.com"
|
44
|
+
click_button 'Continue'
|
45
|
+
end
|
46
|
+
fill_in_billing
|
47
|
+
click_button "Save and Continue"
|
48
|
+
# Delivery step doesn't require any action
|
49
|
+
click_button "Save and Continue"
|
50
|
+
find("#paypal_button").click
|
51
|
+
switch_to_paypal_login
|
52
|
+
fill_in "login_email", :with => "pp@spreecommerce.com"
|
53
|
+
fill_in "login_password", :with => "thequickbrownfox"
|
54
|
+
click_button "Log In"
|
55
|
+
find("#continue_abovefold").click # Because there's TWO continue buttons.
|
56
|
+
page.should have_content("Your order has been processed successfully")
|
57
|
+
|
58
|
+
Spree::Payment.last.source.transaction_id.should_not be_blank
|
59
|
+
end
|
60
|
+
|
61
|
+
it "includes adjustments in PayPal summary" do
|
62
|
+
visit spree.root_path
|
63
|
+
click_link 'iPad'
|
64
|
+
click_button 'Add To Cart'
|
65
|
+
# TODO: Is there a better way to find this current order?
|
66
|
+
order = Spree::Order.last
|
67
|
+
order.adjustments.create!(:amount => -5, :label => "$5 off")
|
68
|
+
order.adjustments.create!(:amount => 10, :label => "$10 on")
|
69
|
+
visit '/cart'
|
70
|
+
within("#cart_adjustments") do
|
71
|
+
page.should have_content("$5 off")
|
72
|
+
page.should have_content("$10 on")
|
73
|
+
end
|
74
|
+
click_button 'Checkout'
|
75
|
+
within("#guest_checkout") do
|
76
|
+
fill_in "Email", :with => "test@example.com"
|
77
|
+
click_button 'Continue'
|
78
|
+
end
|
79
|
+
fill_in_billing
|
80
|
+
click_button "Save and Continue"
|
81
|
+
# Delivery step doesn't require any action
|
82
|
+
click_button "Save and Continue"
|
83
|
+
find("#paypal_button").click
|
84
|
+
within("#miniCart") do
|
85
|
+
page.should have_content("$5 off")
|
86
|
+
page.should have_content("$10 on")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Regression test for #10
|
91
|
+
context "will skip $0 items" do
|
92
|
+
let!(:product2) { FactoryGirl.create(:product, :name => 'iPod') }
|
93
|
+
|
94
|
+
specify do
|
95
|
+
visit spree.root_path
|
96
|
+
click_link 'iPad'
|
97
|
+
click_button 'Add To Cart'
|
98
|
+
|
99
|
+
visit spree.root_path
|
100
|
+
click_link 'iPod'
|
101
|
+
click_button 'Add To Cart'
|
102
|
+
|
103
|
+
# TODO: Is there a better way to find this current order?
|
104
|
+
order = Spree::Order.last
|
105
|
+
order.line_items.last.update_attribute(:price, 0)
|
106
|
+
click_button 'Checkout'
|
107
|
+
within("#guest_checkout") do
|
108
|
+
fill_in "Email", :with => "test@example.com"
|
109
|
+
click_button 'Continue'
|
110
|
+
end
|
111
|
+
fill_in_billing
|
112
|
+
click_button "Save and Continue"
|
113
|
+
# Delivery step doesn't require any action
|
114
|
+
click_button "Save and Continue"
|
115
|
+
find("#paypal_button").click
|
116
|
+
within("#miniCart") do
|
117
|
+
page.should have_content('iPad')
|
118
|
+
page.should_not have_content('iPod')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context "can process an order with $0 item total" do
|
124
|
+
before do
|
125
|
+
# If we didn't do this then the order would be free and skip payment altogether
|
126
|
+
calculator = Spree::ShippingMethod.first.calculator
|
127
|
+
calculator.preferred_amount = 10
|
128
|
+
calculator.save
|
129
|
+
end
|
130
|
+
|
131
|
+
specify do
|
132
|
+
visit spree.root_path
|
133
|
+
click_link 'iPad'
|
134
|
+
click_button 'Add To Cart'
|
135
|
+
# TODO: Is there a better way to find this current order?
|
136
|
+
order = Spree::Order.last
|
137
|
+
order.adjustments.create!(:amount => -order.line_items.last.price, :label => "FREE iPad ZOMG!")
|
138
|
+
click_button 'Checkout'
|
139
|
+
within("#guest_checkout") do
|
140
|
+
fill_in "Email", :with => "test@example.com"
|
141
|
+
click_button 'Continue'
|
142
|
+
end
|
143
|
+
fill_in_billing
|
144
|
+
click_button "Save and Continue"
|
145
|
+
# Delivery step doesn't require any action
|
146
|
+
click_button "Save and Continue"
|
147
|
+
find("#paypal_button").click
|
148
|
+
within("#miniCart") do
|
149
|
+
page.should have_content('Current purchase')
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
context "cannot process a payment with invalid gateway details" do
|
155
|
+
before do
|
156
|
+
@gateway.preferred_login = nil
|
157
|
+
@gateway.save
|
158
|
+
end
|
159
|
+
|
160
|
+
specify do
|
161
|
+
visit spree.root_path
|
162
|
+
click_link 'iPad'
|
163
|
+
click_button 'Add To Cart'
|
164
|
+
click_button 'Checkout'
|
165
|
+
within("#guest_checkout") do
|
166
|
+
fill_in "Email", :with => "test@example.com"
|
167
|
+
click_button 'Continue'
|
168
|
+
end
|
169
|
+
fill_in_billing
|
170
|
+
click_button "Save and Continue"
|
171
|
+
# Delivery step doesn't require any action
|
172
|
+
click_button "Save and Continue"
|
173
|
+
find("#paypal_button").click
|
174
|
+
page.should have_content("PayPal failed. Security header is not valid")
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
context "as an admin" do
|
179
|
+
stub_authorization!
|
180
|
+
|
181
|
+
context "refunding payments" do
|
182
|
+
before do
|
183
|
+
visit spree.root_path
|
184
|
+
click_link 'iPad'
|
185
|
+
click_button 'Add To Cart'
|
186
|
+
click_button 'Checkout'
|
187
|
+
within("#guest_checkout") do
|
188
|
+
fill_in "Email", :with => "test@example.com"
|
189
|
+
click_button 'Continue'
|
190
|
+
end
|
191
|
+
fill_in_billing
|
192
|
+
click_button "Save and Continue"
|
193
|
+
# Delivery step doesn't require any action
|
194
|
+
click_button "Save and Continue"
|
195
|
+
find("#paypal_button").click
|
196
|
+
switch_to_paypal_login
|
197
|
+
fill_in "login_email", :with => "pp@spreecommerce.com"
|
198
|
+
fill_in "login_password", :with => "thequickbrownfox"
|
199
|
+
click_button "Log In"
|
200
|
+
find("#continue_abovefold").click # Because there's TWO continue buttons.
|
201
|
+
page.should have_content("Your order has been processed successfully")
|
202
|
+
|
203
|
+
visit '/admin'
|
204
|
+
click_link Spree::Order.last.number
|
205
|
+
click_link "Payments"
|
206
|
+
click_link "PayPal"
|
207
|
+
click_link "Refund"
|
208
|
+
end
|
209
|
+
|
210
|
+
it "can refund payments fully" do
|
211
|
+
click_button "Refund"
|
212
|
+
page.should have_content("PayPal refund successful")
|
213
|
+
|
214
|
+
payment = Spree::Payment.last
|
215
|
+
source = payment.source
|
216
|
+
source.refund_transaction_id.should_not be_blank
|
217
|
+
source.refunded_at.should_not be_blank
|
218
|
+
source.state.should eql("refunded")
|
219
|
+
source.refund_type.should eql("Full")
|
220
|
+
end
|
221
|
+
|
222
|
+
it "can refund payments partially" do
|
223
|
+
payment = Spree::Payment.last
|
224
|
+
# Take a dollar off, which should cause refund type to be...
|
225
|
+
fill_in "Amount", :with => payment.amount - 1
|
226
|
+
click_button "Refund"
|
227
|
+
page.should have_content("PayPal refund successful")
|
228
|
+
|
229
|
+
source = payment.source
|
230
|
+
source.refund_transaction_id.should_not be_blank
|
231
|
+
source.refunded_at.should_not be_blank
|
232
|
+
source.state.should eql("refunded")
|
233
|
+
# ... a partial refund
|
234
|
+
source.refund_type.should eql("Partial")
|
235
|
+
end
|
236
|
+
|
237
|
+
it "errors when given an invalid refund amount" do
|
238
|
+
fill_in "Amount", :with => "lol"
|
239
|
+
click_button "Refund"
|
240
|
+
page.should have_content("PayPal refund unsuccessful (The partial refund amount is not valid)")
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|