spree_paypal_express_mutalis 2.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|