solidus_core 1.0.2 → 1.0.3
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 +4 -4
- data/CHANGELOG.md +1 -0
- data/Gemfile +3 -0
- data/Rakefile +16 -0
- data/script/rails +9 -0
- data/solidus_core.gemspec +48 -0
- data/spec/fixtures/thinking-cat.jpg +0 -0
- data/spec/helpers/base_helper_spec.rb +173 -0
- data/spec/helpers/order_helper_spec.rb +12 -0
- data/spec/helpers/products_helper_spec.rb +220 -0
- data/spec/helpers/taxons_helper_spec.rb +17 -0
- data/spec/lib/calculated_adjustments_spec.rb +7 -0
- data/spec/lib/i18n_spec.rb +123 -0
- data/spec/lib/search/base_spec.rb +86 -0
- data/spec/lib/search/variant_spec.rb +92 -0
- data/spec/lib/spree/core/controller_helpers/auth_spec.rb +66 -0
- data/spec/lib/spree/core/controller_helpers/order_spec.rb +92 -0
- data/spec/lib/spree/core/controller_helpers/search_spec.rb +17 -0
- data/spec/lib/spree/core/controller_helpers/store_spec.rb +16 -0
- data/spec/lib/spree/core/controller_helpers/strong_parameters_spec.rb +39 -0
- data/spec/lib/spree/core/current_store_spec.rb +36 -0
- data/spec/lib/spree/core/delegate_belongs_to_spec.rb +22 -0
- data/spec/lib/spree/core/importer/order_spec.rb +431 -0
- data/spec/lib/spree/core/role_configuration_spec.rb +138 -0
- data/spec/lib/spree/core/validators/email_spec.rb +48 -0
- data/spec/lib/spree/localized_number_spec.rb +38 -0
- data/spec/lib/spree/migrations_spec.rb +36 -0
- data/spec/lib/spree/money_spec.rb +127 -0
- data/spec/lib/tasks/exchanges_spec.rb +231 -0
- data/spec/lib/tasks/migrations/copy_shipped_shipments_to_cartons_spec.rb +115 -0
- data/spec/lib/tasks/order_capturing_spec.rb +56 -0
- data/spec/mailers/carton_mailer_spec.rb +43 -0
- data/spec/mailers/order_mailer_spec.rb +122 -0
- data/spec/mailers/reimbursement_mailer_spec.rb +40 -0
- data/spec/mailers/test_mailer_spec.rb +15 -0
- data/spec/models/spree/ability_spec.rb +276 -0
- data/spec/models/spree/address_spec.rb +250 -0
- data/spec/models/spree/adjustment_reason_spec.rb +13 -0
- data/spec/models/spree/adjustment_spec.rb +177 -0
- data/spec/models/spree/app_configuration_spec.rb +20 -0
- data/spec/models/spree/asset_spec.rb +24 -0
- data/spec/models/spree/calculator/default_tax_spec.rb +127 -0
- data/spec/models/spree/calculator/flat_percent_item_total_spec.rb +25 -0
- data/spec/models/spree/calculator/flat_rate_spec.rb +47 -0
- data/spec/models/spree/calculator/flexi_rate_spec.rb +41 -0
- data/spec/models/spree/calculator/percent_on_line_item_spec.rb +15 -0
- data/spec/models/spree/calculator/price_sack_spec.rb +30 -0
- data/spec/models/spree/calculator/refunds/default_refund_amount_spec.rb +51 -0
- data/spec/models/spree/calculator/shipping/flat_percent_item_total_spec.rb +23 -0
- data/spec/models/spree/calculator/shipping/flat_rate_spec.rb +13 -0
- data/spec/models/spree/calculator/shipping/flexi_rate_spec.rb +52 -0
- data/spec/models/spree/calculator/shipping/per_item_spec.rb +20 -0
- data/spec/models/spree/calculator/shipping/price_sack_spec.rb +30 -0
- data/spec/models/spree/calculator/tiered_flat_rate_spec.rb +36 -0
- data/spec/models/spree/calculator/tiered_percent_spec.rb +47 -0
- data/spec/models/spree/calculator_spec.rb +36 -0
- data/spec/models/spree/carton_spec.rb +133 -0
- data/spec/models/spree/classification_spec.rb +15 -0
- data/spec/models/spree/concerns/display_money_spec.rb +43 -0
- data/spec/models/spree/concerns/user_methods_spec.rb +41 -0
- data/spec/models/spree/credit_card_spec.rb +334 -0
- data/spec/models/spree/customer_return_spec.rb +276 -0
- data/spec/models/spree/exchange_spec.rb +79 -0
- data/spec/models/spree/gateway/bogus_simple.rb +20 -0
- data/spec/models/spree/gateway/bogus_spec.rb +13 -0
- data/spec/models/spree/gateway_spec.rb +82 -0
- data/spec/models/spree/inventory_unit_spec.rb +307 -0
- data/spec/models/spree/item_adjustments_spec.rb +256 -0
- data/spec/models/spree/line_item_spec.rb +191 -0
- data/spec/models/spree/option_type_spec.rb +14 -0
- data/spec/models/spree/option_value_spec.rb +22 -0
- data/spec/models/spree/order/address_spec.rb +50 -0
- data/spec/models/spree/order/adjustments_spec.rb +39 -0
- data/spec/models/spree/order/callbacks_spec.rb +42 -0
- data/spec/models/spree/order/checkout_spec.rb +902 -0
- data/spec/models/spree/order/currency_updater_spec.rb +32 -0
- data/spec/models/spree/order/finalizing_spec.rb +111 -0
- data/spec/models/spree/order/payment_spec.rb +210 -0
- data/spec/models/spree/order/risk_assessment_spec.rb +68 -0
- data/spec/models/spree/order/state_machine_spec.rb +221 -0
- data/spec/models/spree/order/tax_spec.rb +84 -0
- data/spec/models/spree/order/totals_spec.rb +24 -0
- data/spec/models/spree/order/updating_spec.rb +18 -0
- data/spec/models/spree/order/validations_spec.rb +15 -0
- data/spec/models/spree/order_cancellations_spec.rb +120 -0
- data/spec/models/spree/order_capturing_spec.rb +116 -0
- data/spec/models/spree/order_contents_spec.rb +265 -0
- data/spec/models/spree/order_inventory_spec.rb +228 -0
- data/spec/models/spree/order_mutex_spec.rb +85 -0
- data/spec/models/spree/order_promotion_spec.rb +31 -0
- data/spec/models/spree/order_shipping_spec.rb +247 -0
- data/spec/models/spree/order_spec.rb +1412 -0
- data/spec/models/spree/order_stock_location_spec.rb +18 -0
- data/spec/models/spree/order_updater_spec.rb +299 -0
- data/spec/models/spree/payment_method/store_credit_spec.rb +294 -0
- data/spec/models/spree/payment_method_spec.rb +96 -0
- data/spec/models/spree/payment_spec.rb +1044 -0
- data/spec/models/spree/permission_sets/base_spec.rb +12 -0
- data/spec/models/spree/permission_sets/configuration_display.rb +82 -0
- data/spec/models/spree/permission_sets/configuration_management_spec.rb +50 -0
- data/spec/models/spree/permission_sets/dashboard_display_spec.rb +22 -0
- data/spec/models/spree/permission_sets/order_display_spec.rb +49 -0
- data/spec/models/spree/permission_sets/order_management_spec.rb +36 -0
- data/spec/models/spree/permission_sets/product_display_spec.rb +60 -0
- data/spec/models/spree/permission_sets/product_management_spec.rb +40 -0
- data/spec/models/spree/permission_sets/promotion_display_spec.rb +34 -0
- data/spec/models/spree/permission_sets/promotion_management_spec.rb +26 -0
- data/spec/models/spree/permission_sets/report_display_spec.rb +24 -0
- data/spec/models/spree/permission_sets/restricted_transfer_management_spec.rb +132 -0
- data/spec/models/spree/permission_sets/stock_display_spec.rb +26 -0
- data/spec/models/spree/permission_sets/stock_management_spec.rb +24 -0
- data/spec/models/spree/permission_sets/user_display_spec.rb +36 -0
- data/spec/models/spree/permission_sets/user_management_spec.rb +28 -0
- data/spec/models/spree/preference_spec.rb +80 -0
- data/spec/models/spree/preferences/configuration_spec.rb +30 -0
- data/spec/models/spree/preferences/preferable_spec.rb +294 -0
- data/spec/models/spree/preferences/scoped_store_spec.rb +58 -0
- data/spec/models/spree/preferences/static_model_preferences_spec.rb +78 -0
- data/spec/models/spree/preferences/statically_configurable_spec.rb +60 -0
- data/spec/models/spree/preferences/store_spec.rb +39 -0
- data/spec/models/spree/price_spec.rb +42 -0
- data/spec/models/spree/product/scopes_spec.rb +148 -0
- data/spec/models/spree/product_duplicator_spec.rb +103 -0
- data/spec/models/spree/product_filter_spec.rb +26 -0
- data/spec/models/spree/product_property_spec.rb +20 -0
- data/spec/models/spree/product_spec.rb +437 -0
- data/spec/models/spree/promotion/actions/create_adjustment_spec.rb +96 -0
- data/spec/models/spree/promotion/actions/create_item_adjustments_spec.rb +165 -0
- data/spec/models/spree/promotion/actions/create_quantity_adjustments_spec.rb +115 -0
- data/spec/models/spree/promotion/actions/free_shipping_spec.rb +40 -0
- data/spec/models/spree/promotion/rules/first_order_spec.rb +75 -0
- data/spec/models/spree/promotion/rules/item_total_spec.rb +67 -0
- data/spec/models/spree/promotion/rules/nth_order_spec.rb +70 -0
- data/spec/models/spree/promotion/rules/one_use_per_user_spec.rb +42 -0
- data/spec/models/spree/promotion/rules/option_value_spec.rb +94 -0
- data/spec/models/spree/promotion/rules/product_spec.rb +143 -0
- data/spec/models/spree/promotion/rules/taxon_spec.rb +102 -0
- data/spec/models/spree/promotion/rules/user_logged_in_spec.rb +27 -0
- data/spec/models/spree/promotion/rules/user_spec.rb +37 -0
- data/spec/models/spree/promotion_builder_spec.rb +118 -0
- data/spec/models/spree/promotion_category_spec.rb +17 -0
- data/spec/models/spree/promotion_code/code_builder_spec.rb +79 -0
- data/spec/models/spree/promotion_code_spec.rb +187 -0
- data/spec/models/spree/promotion_handler/cart_spec.rb +114 -0
- data/spec/models/spree/promotion_handler/coupon_spec.rb +335 -0
- data/spec/models/spree/promotion_handler/free_shipping_spec.rb +47 -0
- data/spec/models/spree/promotion_handler/page_spec.rb +44 -0
- data/spec/models/spree/promotion_rule_spec.rb +28 -0
- data/spec/models/spree/promotion_spec.rb +767 -0
- data/spec/models/spree/refund_spec.rb +204 -0
- data/spec/models/spree/reimbursement/credit_spec.rb +36 -0
- data/spec/models/spree/reimbursement/reimbursement_type_engine_spec.rb +140 -0
- data/spec/models/spree/reimbursement/reimbursement_type_validator_spec.rb +83 -0
- data/spec/models/spree/reimbursement_performer_spec.rb +30 -0
- data/spec/models/spree/reimbursement_spec.rb +231 -0
- data/spec/models/spree/reimbursement_tax_calculator_spec.rb +51 -0
- data/spec/models/spree/reimbursement_type/credit_spec.rb +53 -0
- data/spec/models/spree/reimbursement_type/exchange_spec.rb +46 -0
- data/spec/models/spree/reimbursement_type/original_payment_spec.rb +107 -0
- data/spec/models/spree/reimbursement_type/store_credit_spec.rb +97 -0
- data/spec/models/spree/return_authorization_spec.rb +290 -0
- data/spec/models/spree/return_item/eligibility_validator/default_spec.rb +77 -0
- data/spec/models/spree/return_item/eligibility_validator/inventory_shipped_spec.rb +58 -0
- data/spec/models/spree/return_item/eligibility_validator/no_reimbursements_spec.rb +85 -0
- data/spec/models/spree/return_item/eligibility_validator/order_completed_spec.rb +32 -0
- data/spec/models/spree/return_item/eligibility_validator/rma_required_spec.rb +29 -0
- data/spec/models/spree/return_item/eligibility_validator/time_since_purchase_spec.rb +35 -0
- data/spec/models/spree/return_item/exchange_variant_eligibility/same_option_value_spec.rb +65 -0
- data/spec/models/spree/return_item/exchange_variant_eligibility/same_product_spec.rb +43 -0
- data/spec/models/spree/return_item_spec.rb +775 -0
- data/spec/models/spree/returns_calculator_spec.rb +14 -0
- data/spec/models/spree/shipment_spec.rb +709 -0
- data/spec/models/spree/shipping_calculator_spec.rb +45 -0
- data/spec/models/spree/shipping_method_spec.rb +88 -0
- data/spec/models/spree/shipping_rate_spec.rb +142 -0
- data/spec/models/spree/state_spec.rb +14 -0
- data/spec/models/spree/stock/availability_validator_spec.rb +83 -0
- data/spec/models/spree/stock/coordinator_spec.rb +116 -0
- data/spec/models/spree/stock/differentiator_spec.rb +39 -0
- data/spec/models/spree/stock/estimator_spec.rb +146 -0
- data/spec/models/spree/stock/inventory_unit_builder_spec.rb +38 -0
- data/spec/models/spree/stock/package_spec.rb +163 -0
- data/spec/models/spree/stock/packer_spec.rb +91 -0
- data/spec/models/spree/stock/prioritizer_spec.rb +125 -0
- data/spec/models/spree/stock/quantifier_spec.rb +115 -0
- data/spec/models/spree/stock/splitter/backordered_spec.rb +29 -0
- data/spec/models/spree/stock/splitter/base_spec.rb +21 -0
- data/spec/models/spree/stock/splitter/shipping_category_spec.rb +50 -0
- data/spec/models/spree/stock/splitter/weight_spec.rb +29 -0
- data/spec/models/spree/stock_item_spec.rb +426 -0
- data/spec/models/spree/stock_location_spec.rb +279 -0
- data/spec/models/spree/stock_movement_spec.rb +56 -0
- data/spec/models/spree/stock_transfer_spec.rb +290 -0
- data/spec/models/spree/store_credit_category_spec.rb +17 -0
- data/spec/models/spree/store_credit_event_spec.rb +314 -0
- data/spec/models/spree/store_credit_spec.rb +876 -0
- data/spec/models/spree/store_spec.rb +55 -0
- data/spec/models/spree/tax_category_spec.rb +27 -0
- data/spec/models/spree/tax_rate_spec.rb +378 -0
- data/spec/models/spree/taxon_spec.rb +74 -0
- data/spec/models/spree/taxonomy_spec.rb +18 -0
- data/spec/models/spree/tracker_spec.rb +21 -0
- data/spec/models/spree/transfer_item_spec.rb +264 -0
- data/spec/models/spree/unit_cancel_spec.rb +148 -0
- data/spec/models/spree/user_spec.rb +223 -0
- data/spec/models/spree/validations/db_maximum_length_validator_spec.rb +23 -0
- data/spec/models/spree/variant/scopes_spec.rb +55 -0
- data/spec/models/spree/variant_spec.rb +546 -0
- data/spec/models/spree/zone_spec.rb +305 -0
- data/spec/spec_helper.rb +78 -0
- data/spec/support/big_decimal.rb +5 -0
- data/spec/support/concerns/default_price.rb +34 -0
- data/spec/support/dummy_ability.rb +4 -0
- data/spec/support/test_gateway.rb +2 -0
- metadata +229 -3
- data/lib/spree/testing_support/rspec-activemodel-mocks_patch.rb +0 -8
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Spree::PaymentMethod, :type => :model do
|
|
4
|
+
describe "#available" do
|
|
5
|
+
before do
|
|
6
|
+
[nil, 'both', 'front_end', 'back_end'].each do |display_on|
|
|
7
|
+
Spree::Gateway::Test.create(
|
|
8
|
+
:name => 'Display Both',
|
|
9
|
+
:display_on => display_on,
|
|
10
|
+
:active => true,
|
|
11
|
+
:environment => 'test',
|
|
12
|
+
:description => 'foofah'
|
|
13
|
+
)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "should have 4 total methods" do
|
|
18
|
+
expect(Spree::PaymentMethod.all.size).to eq(4)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should return all methods available to front-end/back-end when no parameter is passed" do
|
|
22
|
+
expect(Spree::PaymentMethod.available.size).to eq(2)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "should return all methods available to front-end/back-end when display_on = :both" do
|
|
26
|
+
expect(Spree::PaymentMethod.available(:both).size).to eq(2)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "should return all methods available to front-end when display_on = :front_end" do
|
|
30
|
+
expect(Spree::PaymentMethod.available(:front_end).size).to eq(2)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "should return all methods available to back-end when display_on = :back_end" do
|
|
34
|
+
expect(Spree::PaymentMethod.available(:back_end).size).to eq(2)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe '#auto_capture?' do
|
|
39
|
+
class TestGateway < Spree::Gateway
|
|
40
|
+
def provider_class
|
|
41
|
+
Provider
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
let(:gateway) { TestGateway.new }
|
|
46
|
+
|
|
47
|
+
subject { gateway.auto_capture? }
|
|
48
|
+
|
|
49
|
+
context 'when auto_capture is nil' do
|
|
50
|
+
before(:each) do
|
|
51
|
+
expect(Spree::Config).to receive('[]').with(:auto_capture).and_return(auto_capture)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
context 'and when Spree::Config[:auto_capture] is false' do
|
|
55
|
+
let(:auto_capture) { false }
|
|
56
|
+
|
|
57
|
+
it 'should be false' do
|
|
58
|
+
expect(gateway.auto_capture).to be_nil
|
|
59
|
+
expect(subject).to be false
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
context 'and when Spree::Config[:auto_capture] is true' do
|
|
64
|
+
let(:auto_capture) { true }
|
|
65
|
+
|
|
66
|
+
it 'should be true' do
|
|
67
|
+
expect(gateway.auto_capture).to be_nil
|
|
68
|
+
expect(subject).to be true
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
context 'when auto_capture is not nil' do
|
|
74
|
+
before(:each) do
|
|
75
|
+
gateway.auto_capture = auto_capture
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
context 'and is true' do
|
|
79
|
+
let(:auto_capture) { true }
|
|
80
|
+
|
|
81
|
+
it 'should be true' do
|
|
82
|
+
expect(subject).to be true
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
context 'and is false' do
|
|
87
|
+
let(:auto_capture) { false }
|
|
88
|
+
|
|
89
|
+
it 'should be true' do
|
|
90
|
+
expect(subject).to be false
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
end
|
|
@@ -0,0 +1,1044 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Spree::Payment, :type => :model do
|
|
4
|
+
let(:order) { Spree::Order.create }
|
|
5
|
+
let(:refund_reason) { create(:refund_reason) }
|
|
6
|
+
|
|
7
|
+
let(:gateway) do
|
|
8
|
+
gateway = Spree::Gateway::Bogus.new(:environment => 'test', :active => true, :name => 'Bogus gateway')
|
|
9
|
+
allow(gateway).to receive_messages :source_required => true
|
|
10
|
+
gateway
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
let(:avs_code) { 'D' }
|
|
14
|
+
let(:cvv_code) { 'M' }
|
|
15
|
+
|
|
16
|
+
let(:card) { create :credit_card }
|
|
17
|
+
|
|
18
|
+
let(:payment) do
|
|
19
|
+
payment = Spree::Payment.new
|
|
20
|
+
payment.source = card
|
|
21
|
+
payment.order = order
|
|
22
|
+
payment.payment_method = gateway
|
|
23
|
+
payment.amount = 5
|
|
24
|
+
payment
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
let(:amount_in_cents) { (payment.amount * 100).round }
|
|
28
|
+
|
|
29
|
+
let!(:success_response) do
|
|
30
|
+
ActiveMerchant::Billing::Response.new(true, '', {}, {
|
|
31
|
+
authorization: '123',
|
|
32
|
+
cvv_result: cvv_code,
|
|
33
|
+
avs_result: { code: avs_code }
|
|
34
|
+
})
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
let(:failed_response) do
|
|
38
|
+
ActiveMerchant::Billing::Response.new(false, '', {}, {})
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
before(:each) do
|
|
42
|
+
# So it doesn't create log entries every time a processing method is called
|
|
43
|
+
allow(payment.log_entries).to receive(:create!)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
context '.risky' do
|
|
47
|
+
|
|
48
|
+
let!(:payment_1) { create(:payment, avs_response: 'Y', cvv_response_code: 'M', cvv_response_message: 'Match') }
|
|
49
|
+
let!(:payment_2) { create(:payment, avs_response: 'Y', cvv_response_code: 'M', cvv_response_message: '') }
|
|
50
|
+
let!(:payment_3) { create(:payment, avs_response: 'A', cvv_response_code: 'M', cvv_response_message: 'Match') }
|
|
51
|
+
let!(:payment_4) { create(:payment, avs_response: 'Y', cvv_response_code: 'N', cvv_response_message: 'No Match') }
|
|
52
|
+
|
|
53
|
+
it 'should not return successful responses' do
|
|
54
|
+
expect(subject.class.risky.to_a).to match_array([payment_3, payment_4])
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
context "#captured_amount" do
|
|
60
|
+
context "calculates based on capture events" do
|
|
61
|
+
it "with 0 capture events" do
|
|
62
|
+
expect(payment.captured_amount).to eq(0)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "with some capture events" do
|
|
66
|
+
payment.save
|
|
67
|
+
payment.capture_events.create!(amount: 2.0)
|
|
68
|
+
payment.capture_events.create!(amount: 3.0)
|
|
69
|
+
expect(payment.captured_amount).to eq(5)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
context '#uncaptured_amount' do
|
|
75
|
+
context "calculates based on capture events" do
|
|
76
|
+
it "with 0 capture events" do
|
|
77
|
+
expect(payment.uncaptured_amount).to eq(5.0)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "with some capture events" do
|
|
81
|
+
payment.save
|
|
82
|
+
payment.capture_events.create!(amount: 2.0)
|
|
83
|
+
payment.capture_events.create!(amount: 3.0)
|
|
84
|
+
expect(payment.uncaptured_amount).to eq(0)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
context 'validations' do
|
|
90
|
+
it "returns useful error messages when source is invalid" do
|
|
91
|
+
payment.source = Spree::CreditCard.new
|
|
92
|
+
expect(payment).not_to be_valid
|
|
93
|
+
cc_errors = payment.errors['Credit Card']
|
|
94
|
+
expect(cc_errors).to include("Number can't be blank")
|
|
95
|
+
expect(cc_errors).to include("Month is not a number")
|
|
96
|
+
expect(cc_errors).to include("Year is not a number")
|
|
97
|
+
expect(cc_errors).to include("Verification Value can't be blank")
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Regression test for https://github.com/spree/spree/pull/2224
|
|
102
|
+
context 'failure' do
|
|
103
|
+
it 'should transition to failed from pending state' do
|
|
104
|
+
payment.state = 'pending'
|
|
105
|
+
payment.failure
|
|
106
|
+
expect(payment.state).to eql('failed')
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it 'should transition to failed from processing state' do
|
|
110
|
+
payment.state = 'processing'
|
|
111
|
+
payment.failure
|
|
112
|
+
expect(payment.state).to eql('failed')
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
context 'invalidate' do
|
|
118
|
+
it 'should transition from checkout to invalid' do
|
|
119
|
+
payment.state = 'checkout'
|
|
120
|
+
payment.invalidate
|
|
121
|
+
expect(payment.state).to eq('invalid')
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
context "the payment's source is invalid" do
|
|
125
|
+
|
|
126
|
+
before(:each) do
|
|
127
|
+
card.year = 2014
|
|
128
|
+
payment.source = card
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it "transitions to invalid" do
|
|
132
|
+
payment.state = 'checkout'
|
|
133
|
+
payment.invalidate
|
|
134
|
+
expect(payment.state).to eq ('invalid')
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
context "processing" do
|
|
140
|
+
describe "#process!" do
|
|
141
|
+
context 'with autocapture' do
|
|
142
|
+
before do
|
|
143
|
+
payment.payment_method.update_attributes!(auto_capture: true)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it "should purchase" do
|
|
147
|
+
payment.process!
|
|
148
|
+
expect(payment).to be_completed
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
context 'without autocapture' do
|
|
153
|
+
before do
|
|
154
|
+
payment.payment_method.update_attributes!(auto_capture: false)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
context 'when in the checkout state' do
|
|
158
|
+
before { payment.update_attributes!(state: 'checkout') }
|
|
159
|
+
|
|
160
|
+
it "authorizes" do
|
|
161
|
+
payment.process!
|
|
162
|
+
expect(payment).to be_pending
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
context 'when in the processing state' do
|
|
167
|
+
before { payment.update_attributes!(state: 'processing') }
|
|
168
|
+
|
|
169
|
+
it "does not authorize" do
|
|
170
|
+
payment.process!
|
|
171
|
+
expect(payment).to be_processing
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
context 'when in the pending state' do
|
|
176
|
+
before { payment.update_attributes!(state: 'pending') }
|
|
177
|
+
|
|
178
|
+
it "does not re-authorize" do
|
|
179
|
+
expect(payment).to_not receive(:authorize!)
|
|
180
|
+
payment.process!
|
|
181
|
+
expect(payment).to be_pending
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
context 'when in a failed state' do
|
|
186
|
+
before { payment.update_attributes!(state: 'failed') }
|
|
187
|
+
|
|
188
|
+
it "raises an exception" do
|
|
189
|
+
expect {
|
|
190
|
+
payment.process!
|
|
191
|
+
}.to raise_error(StateMachines::InvalidTransition, /Cannot transition/)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
context 'when in the completed state' do
|
|
196
|
+
before { payment.update_attributes!(state: 'completed') }
|
|
197
|
+
|
|
198
|
+
it "authorizes" do
|
|
199
|
+
payment.process!
|
|
200
|
+
# TODO: Is this really what we want to happen in this case?
|
|
201
|
+
expect(payment).to be_pending
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
it "should make the state 'processing'" do
|
|
207
|
+
expect(payment).to receive(:started_processing!)
|
|
208
|
+
payment.process!
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
it "should invalidate if payment method doesnt support source" do
|
|
212
|
+
expect(payment.payment_method).to receive(:supports?).with(payment.source).and_return(false)
|
|
213
|
+
expect { payment.process!}.to raise_error(Spree::Core::GatewayError)
|
|
214
|
+
expect(payment.state).to eq('invalid')
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Regression test for #4598
|
|
218
|
+
it "should allow payments with a gateway_customer_profile_id" do
|
|
219
|
+
allow(payment.source).to receive_messages :gateway_customer_profile_id => "customer_1"
|
|
220
|
+
expect(payment.payment_method).to receive(:supports?).with(payment.source).and_return(false)
|
|
221
|
+
expect(payment).to receive(:started_processing!)
|
|
222
|
+
payment.process!
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Another regression test for #4598
|
|
226
|
+
it "should allow payments with a gateway_payment_profile_id" do
|
|
227
|
+
allow(payment.source).to receive_messages :gateway_payment_profile_id => "customer_1"
|
|
228
|
+
expect(payment.payment_method).to receive(:supports?).with(payment.source).and_return(false)
|
|
229
|
+
expect(payment).to receive(:started_processing!)
|
|
230
|
+
payment.process!
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
describe "#authorize!" do
|
|
235
|
+
it "should call authorize on the gateway with the payment amount" do
|
|
236
|
+
expect(payment.payment_method).to receive(:authorize).with(amount_in_cents,
|
|
237
|
+
card,
|
|
238
|
+
anything).and_return(success_response)
|
|
239
|
+
payment.authorize!
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
it "should call authorize on the gateway with the currency code" do
|
|
243
|
+
allow(payment).to receive_messages :currency => 'GBP'
|
|
244
|
+
expect(payment.payment_method).to receive(:authorize).with(amount_in_cents,
|
|
245
|
+
card,
|
|
246
|
+
hash_including({:currency => "GBP"})).and_return(success_response)
|
|
247
|
+
payment.authorize!
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
it "should log the response" do
|
|
251
|
+
payment.save!
|
|
252
|
+
expect(payment.log_entries).to receive(:create!).with(details: anything)
|
|
253
|
+
payment.authorize!
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
context "when gateway does not match the environment" do
|
|
257
|
+
it "should raise an exception" do
|
|
258
|
+
allow(gateway).to receive_messages :environment => "foo"
|
|
259
|
+
expect { payment.authorize! }.to raise_error(Spree::Core::GatewayError)
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
context "if successful" do
|
|
264
|
+
before do
|
|
265
|
+
expect(payment.payment_method).to receive(:authorize).with(amount_in_cents,
|
|
266
|
+
card,
|
|
267
|
+
anything).and_return(success_response)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
it "should store the response_code, avs_response and cvv_response fields" do
|
|
271
|
+
payment.authorize!
|
|
272
|
+
expect(payment.response_code).to eq('123')
|
|
273
|
+
expect(payment.avs_response).to eq(avs_code)
|
|
274
|
+
expect(payment.cvv_response_code).to eq(cvv_code)
|
|
275
|
+
expect(payment.cvv_response_message).to eq(ActiveMerchant::Billing::CVVResult::MESSAGES[cvv_code])
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
it "should make payment pending" do
|
|
279
|
+
expect(payment).to receive(:pend!)
|
|
280
|
+
payment.authorize!
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
context "if unsuccessful" do
|
|
285
|
+
it "should mark payment as failed" do
|
|
286
|
+
allow(gateway).to receive(:authorize).and_return(failed_response)
|
|
287
|
+
expect(payment).to receive(:failure)
|
|
288
|
+
expect(payment).not_to receive(:pend)
|
|
289
|
+
expect {
|
|
290
|
+
payment.authorize!
|
|
291
|
+
}.to raise_error(Spree::Core::GatewayError)
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
describe "#purchase!" do
|
|
297
|
+
it "should call purchase on the gateway with the payment amount" do
|
|
298
|
+
expect(gateway).to receive(:purchase).with(amount_in_cents, card, anything).and_return(success_response)
|
|
299
|
+
payment.purchase!
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
it "should log the response" do
|
|
303
|
+
payment.save!
|
|
304
|
+
expect(payment.log_entries).to receive(:create!).with(details: anything)
|
|
305
|
+
payment.purchase!
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
context "when gateway does not match the environment" do
|
|
309
|
+
it "should raise an exception" do
|
|
310
|
+
allow(gateway).to receive_messages :environment => "foo"
|
|
311
|
+
expect { payment.purchase! }.to raise_error(Spree::Core::GatewayError)
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
context "if successful" do
|
|
316
|
+
before do
|
|
317
|
+
expect(payment.payment_method).to receive(:purchase).with(amount_in_cents,
|
|
318
|
+
card,
|
|
319
|
+
anything).and_return(success_response)
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
it "should store the response_code and avs_response" do
|
|
323
|
+
payment.purchase!
|
|
324
|
+
expect(payment.response_code).to eq('123')
|
|
325
|
+
expect(payment.avs_response).to eq(avs_code)
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
it "should make payment complete" do
|
|
329
|
+
expect(payment).to receive(:complete!)
|
|
330
|
+
payment.purchase!
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
it "should log a capture event" do
|
|
334
|
+
payment.purchase!
|
|
335
|
+
expect(payment.capture_events.count).to eq(1)
|
|
336
|
+
expect(payment.capture_events.first.amount).to eq(payment.amount)
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
it "should set the uncaptured amount to 0" do
|
|
340
|
+
payment.purchase!
|
|
341
|
+
expect(payment.uncaptured_amount).to eq(0)
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
context "if unsuccessful" do
|
|
346
|
+
before do
|
|
347
|
+
allow(gateway).to receive(:purchase).and_return(failed_response)
|
|
348
|
+
expect(payment).to receive(:failure)
|
|
349
|
+
expect(payment).not_to receive(:pend)
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
it "should make payment failed" do
|
|
353
|
+
expect { payment.purchase! }.to raise_error(Spree::Core::GatewayError)
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
it "should not log a capture event" do
|
|
357
|
+
expect { payment.purchase! }.to raise_error(Spree::Core::GatewayError)
|
|
358
|
+
expect(payment.capture_events.count).to eq(0)
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
describe "#capture!" do
|
|
364
|
+
context "when payment is pending" do
|
|
365
|
+
before do
|
|
366
|
+
payment.amount = 100
|
|
367
|
+
payment.state = 'pending'
|
|
368
|
+
payment.response_code = '12345'
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
context "if successful" do
|
|
372
|
+
context 'for entire amount' do
|
|
373
|
+
before do
|
|
374
|
+
expect(payment.payment_method).to receive(:capture).with(payment.display_amount.money.cents, payment.response_code, anything).and_return(success_response)
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
it "should make payment complete" do
|
|
378
|
+
expect(payment).to receive(:complete!)
|
|
379
|
+
payment.capture!
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
it "logs capture events" do
|
|
383
|
+
payment.capture!
|
|
384
|
+
expect(payment.capture_events.count).to eq(1)
|
|
385
|
+
expect(payment.capture_events.first.amount).to eq(payment.amount)
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
it "logs capture events" do
|
|
390
|
+
payment.capture!
|
|
391
|
+
expect(payment.capture_events.count).to eq(1)
|
|
392
|
+
expect(payment.capture_events.first.amount).to eq(payment.amount)
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
context "capturing a partial amount" do
|
|
397
|
+
it "logs capture events" do
|
|
398
|
+
payment.capture!(5000)
|
|
399
|
+
expect(payment.capture_events.count).to eq(1)
|
|
400
|
+
expect(payment.capture_events.first.amount).to eq(50)
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
it "stores the captured amount on the payment" do
|
|
404
|
+
payment.capture!(6000)
|
|
405
|
+
expect(payment.captured_amount).to eq(60)
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
it "updates the amount of the payment" do
|
|
409
|
+
payment.capture!(6000)
|
|
410
|
+
expect(payment.reload.amount).to eq(60)
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
context "if unsuccessful" do
|
|
415
|
+
it "should not make payment complete" do
|
|
416
|
+
allow(gateway).to receive_messages :capture => failed_response
|
|
417
|
+
expect(payment).to receive(:failure)
|
|
418
|
+
expect(payment).not_to receive(:complete)
|
|
419
|
+
expect { payment.capture! }.to raise_error(Spree::Core::GatewayError)
|
|
420
|
+
end
|
|
421
|
+
end
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
# Regression test for #2119
|
|
425
|
+
context "when payment is completed" do
|
|
426
|
+
before do
|
|
427
|
+
payment.state = 'completed'
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
it "should do nothing" do
|
|
431
|
+
expect(payment).not_to receive(:complete)
|
|
432
|
+
expect(payment.payment_method).not_to receive(:capture)
|
|
433
|
+
expect(payment.log_entries).not_to receive(:create!)
|
|
434
|
+
payment.capture!
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
describe "#cancel!" do
|
|
440
|
+
before do
|
|
441
|
+
payment.response_code = 'abc'
|
|
442
|
+
payment.state = 'pending'
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
context "if successful" do
|
|
446
|
+
it "should update the response_code with the authorization from the gateway" do
|
|
447
|
+
# Change it to something different
|
|
448
|
+
allow(gateway).to receive_messages :cancel => success_response
|
|
449
|
+
payment.cancel!
|
|
450
|
+
expect(payment.state).to eq('void')
|
|
451
|
+
expect(payment.response_code).to eq('123')
|
|
452
|
+
end
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
context "if unsuccessful" do
|
|
456
|
+
it "should not void the payment" do
|
|
457
|
+
allow(gateway).to receive_messages :cancel => failed_response
|
|
458
|
+
expect { payment.cancel! }.to raise_error(Spree::Core::GatewayError)
|
|
459
|
+
expect(payment.state).to eq('pending')
|
|
460
|
+
expect(payment.response_code).to eq('abc')
|
|
461
|
+
end
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
describe "#void_transaction!" do
|
|
468
|
+
before do
|
|
469
|
+
payment.response_code = '123'
|
|
470
|
+
payment.state = 'pending'
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
context "when profiles are supported" do
|
|
474
|
+
it "should call payment_gateway.void with the payment's response_code" do
|
|
475
|
+
allow(gateway).to receive_messages :payment_profiles_supported? => true
|
|
476
|
+
expect(gateway).to receive(:void).with('123', card, anything).and_return(success_response)
|
|
477
|
+
payment.void_transaction!
|
|
478
|
+
end
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
context "when profiles are not supported" do
|
|
482
|
+
it "should call payment_gateway.void with the payment's response_code" do
|
|
483
|
+
allow(gateway).to receive_messages :payment_profiles_supported? => false
|
|
484
|
+
expect(gateway).to receive(:void).with('123', anything).and_return(success_response)
|
|
485
|
+
payment.void_transaction!
|
|
486
|
+
end
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
it "should log the response" do
|
|
490
|
+
expect(payment.log_entries).to receive(:create!).with(:details => anything)
|
|
491
|
+
payment.void_transaction!
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
context "when gateway does not match the environment" do
|
|
495
|
+
it "should raise an exception" do
|
|
496
|
+
allow(gateway).to receive_messages :environment => "foo"
|
|
497
|
+
expect { payment.void_transaction! }.to raise_error(Spree::Core::GatewayError)
|
|
498
|
+
end
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
context "if successful" do
|
|
502
|
+
it "should update the response_code with the authorization from the gateway" do
|
|
503
|
+
# Change it to something different
|
|
504
|
+
payment.response_code = 'abc'
|
|
505
|
+
payment.void_transaction!
|
|
506
|
+
expect(payment.response_code).to eq('12345')
|
|
507
|
+
end
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
context "if unsuccessful" do
|
|
511
|
+
it "should not void the payment" do
|
|
512
|
+
allow(gateway).to receive_messages :void => failed_response
|
|
513
|
+
expect(payment).not_to receive(:void)
|
|
514
|
+
expect { payment.void_transaction! }.to raise_error(Spree::Core::GatewayError)
|
|
515
|
+
end
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
# Regression test for #2119
|
|
519
|
+
context "if payment is already voided" do
|
|
520
|
+
before do
|
|
521
|
+
payment.state = 'void'
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
it "should not void the payment" do
|
|
525
|
+
expect(payment.payment_method).not_to receive(:void)
|
|
526
|
+
payment.void_transaction!
|
|
527
|
+
end
|
|
528
|
+
end
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
context "when already processing" do
|
|
534
|
+
it "should return nil without trying to process the source" do
|
|
535
|
+
payment.state = 'processing'
|
|
536
|
+
|
|
537
|
+
expect(payment.process!).to be_nil
|
|
538
|
+
end
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
context "with source required" do
|
|
542
|
+
context "raises an error if no source is specified" do
|
|
543
|
+
before do
|
|
544
|
+
payment.source = nil
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
specify do
|
|
548
|
+
expect { payment.process! }.to raise_error(Spree::Core::GatewayError, Spree.t(:payment_processing_failed))
|
|
549
|
+
end
|
|
550
|
+
end
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
context "with source optional" do
|
|
554
|
+
context "raises no error if source is not specified" do
|
|
555
|
+
before do
|
|
556
|
+
payment.source = nil
|
|
557
|
+
allow(payment.payment_method).to receive_messages(:source_required? => false)
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
specify do
|
|
561
|
+
expect { payment.process! }.not_to raise_error
|
|
562
|
+
end
|
|
563
|
+
end
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
describe "#credit_allowed" do
|
|
567
|
+
# Regression test for #4403 & #4407
|
|
568
|
+
it "is the difference between offsets total and payment amount" do
|
|
569
|
+
payment.amount = 100
|
|
570
|
+
allow(payment).to receive(:offsets_total).and_return(0)
|
|
571
|
+
expect(payment.credit_allowed).to eq(100)
|
|
572
|
+
allow(payment).to receive(:offsets_total).and_return(-80)
|
|
573
|
+
expect(payment.credit_allowed).to eq(20)
|
|
574
|
+
end
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
describe "#can_credit?" do
|
|
578
|
+
it "is true if credit_allowed > 0" do
|
|
579
|
+
allow(payment).to receive(:credit_allowed).and_return(100)
|
|
580
|
+
expect(payment.can_credit?).to be true
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
it "is false if credit_allowed is 0" do
|
|
584
|
+
allow(payment).to receive(:credit_allowed).and_return(0)
|
|
585
|
+
expect(payment.can_credit?).to be false
|
|
586
|
+
end
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
describe "#save" do
|
|
590
|
+
context "captured payments" do
|
|
591
|
+
it "update order payment total" do
|
|
592
|
+
payment = create(:payment, order: order, state: 'completed')
|
|
593
|
+
expect(order.payment_total).to eq payment.amount
|
|
594
|
+
end
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
context "not completed payments" do
|
|
598
|
+
it "doesn't update order payment total" do
|
|
599
|
+
expect {
|
|
600
|
+
Spree::Payment.create(:amount => 100, :order => order)
|
|
601
|
+
}.not_to change { order.payment_total }
|
|
602
|
+
end
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
context 'when the payment was completed but now void' do
|
|
606
|
+
let(:payment) do
|
|
607
|
+
Spree::Payment.create(
|
|
608
|
+
amount: 100,
|
|
609
|
+
order: order,
|
|
610
|
+
state: 'completed'
|
|
611
|
+
)
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
it 'updates order payment total' do
|
|
615
|
+
payment.void
|
|
616
|
+
expect(order.payment_total).to eq 0
|
|
617
|
+
end
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
context "completed orders" do
|
|
621
|
+
before { allow(order).to receive_messages completed?: true }
|
|
622
|
+
|
|
623
|
+
it "updates payment_state and shipments" do
|
|
624
|
+
expect(order.updater).to receive(:update_payment_state)
|
|
625
|
+
expect(order.updater).to receive(:update_shipment_state)
|
|
626
|
+
Spree::Payment.create(:amount => 100, :order => order)
|
|
627
|
+
end
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
context "when profiles are supported" do
|
|
631
|
+
before do
|
|
632
|
+
allow(gateway).to receive_messages :payment_profiles_supported? => true
|
|
633
|
+
allow(payment.source).to receive_messages :has_payment_profile? => false
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
context "when there is an error connecting to the gateway" do
|
|
637
|
+
it "should call gateway_error " do
|
|
638
|
+
expect(gateway).to receive(:create_profile).and_raise(ActiveMerchant::ConnectionError.new("foo", nil))
|
|
639
|
+
expect do
|
|
640
|
+
Spree::Payment.create(
|
|
641
|
+
:amount => 100,
|
|
642
|
+
:order => order,
|
|
643
|
+
:source => card,
|
|
644
|
+
:payment_method => gateway
|
|
645
|
+
)
|
|
646
|
+
end.to raise_error(Spree::Core::GatewayError)
|
|
647
|
+
end
|
|
648
|
+
end
|
|
649
|
+
|
|
650
|
+
context "with multiple payment attempts" do
|
|
651
|
+
let(:attributes) { attributes_for(:credit_card) }
|
|
652
|
+
it "should not try to create profiles on old failed payment attempts" do
|
|
653
|
+
allow_any_instance_of(Spree::Payment).to receive(:payment_method) { gateway }
|
|
654
|
+
|
|
655
|
+
order.payments.create!(
|
|
656
|
+
source_attributes: attributes,
|
|
657
|
+
payment_method: gateway,
|
|
658
|
+
amount: 100
|
|
659
|
+
)
|
|
660
|
+
expect(gateway).to receive(:create_profile).exactly :once
|
|
661
|
+
expect(order.payments.count).to eq(1)
|
|
662
|
+
order.payments.create!(
|
|
663
|
+
source_attributes: attributes,
|
|
664
|
+
payment_method: gateway,
|
|
665
|
+
amount: 100
|
|
666
|
+
)
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
end
|
|
670
|
+
|
|
671
|
+
context "when successfully connecting to the gateway" do
|
|
672
|
+
it "should create a payment profile" do
|
|
673
|
+
expect(payment.payment_method).to receive :create_profile
|
|
674
|
+
payment = Spree::Payment.create(
|
|
675
|
+
:amount => 100,
|
|
676
|
+
:order => order,
|
|
677
|
+
:source => card,
|
|
678
|
+
:payment_method => gateway
|
|
679
|
+
)
|
|
680
|
+
end
|
|
681
|
+
end
|
|
682
|
+
end
|
|
683
|
+
|
|
684
|
+
context "when profiles are not supported" do
|
|
685
|
+
before { allow(gateway).to receive_messages :payment_profiles_supported? => false }
|
|
686
|
+
|
|
687
|
+
it "should not create a payment profile" do
|
|
688
|
+
expect(gateway).not_to receive :create_profile
|
|
689
|
+
payment = Spree::Payment.create(
|
|
690
|
+
:amount => 100,
|
|
691
|
+
:order => order,
|
|
692
|
+
:source => card,
|
|
693
|
+
:payment_method => gateway
|
|
694
|
+
)
|
|
695
|
+
end
|
|
696
|
+
end
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
describe '#invalidate_old_payments' do
|
|
700
|
+
it 'should not invalidate other payments if not valid' do
|
|
701
|
+
payment.save
|
|
702
|
+
invalid_payment = Spree::Payment.new(:amount => 100, :order => order, :state => 'invalid', :payment_method => gateway)
|
|
703
|
+
invalid_payment.save
|
|
704
|
+
expect(payment.reload.state).to eq('checkout')
|
|
705
|
+
end
|
|
706
|
+
end
|
|
707
|
+
|
|
708
|
+
describe "#build_source" do
|
|
709
|
+
let(:params) do
|
|
710
|
+
{
|
|
711
|
+
:amount => 100,
|
|
712
|
+
:payment_method => gateway,
|
|
713
|
+
:source_attributes => {
|
|
714
|
+
:expiry =>"01 / 99",
|
|
715
|
+
:number => '1234567890123',
|
|
716
|
+
:verification_value => '123',
|
|
717
|
+
:name => 'Spree Commerce'
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
end
|
|
721
|
+
|
|
722
|
+
it "should build the payment's source" do
|
|
723
|
+
payment = Spree::Payment.new(params)
|
|
724
|
+
expect(payment).to be_valid
|
|
725
|
+
expect(payment.source).not_to be_nil
|
|
726
|
+
end
|
|
727
|
+
|
|
728
|
+
it "assigns user and gateway to payment source" do
|
|
729
|
+
order = create(:order)
|
|
730
|
+
source = order.payments.new(params).source
|
|
731
|
+
|
|
732
|
+
expect(source.user_id).to eq order.user_id
|
|
733
|
+
expect(source.payment_method_id).to eq gateway.id
|
|
734
|
+
end
|
|
735
|
+
|
|
736
|
+
it "errors when payment source not valid" do
|
|
737
|
+
params = { :amount => 100, :payment_method => gateway,
|
|
738
|
+
:source_attributes => {:expiry => "1 / 12" }}
|
|
739
|
+
|
|
740
|
+
payment = Spree::Payment.new(params)
|
|
741
|
+
expect(payment).not_to be_valid
|
|
742
|
+
expect(payment.source).not_to be_nil
|
|
743
|
+
expect(payment.source.error_on(:number).size).to eq(1)
|
|
744
|
+
expect(payment.source.error_on(:verification_value).size).to eq(1)
|
|
745
|
+
end
|
|
746
|
+
|
|
747
|
+
it "does not build a new source when duplicating the model with source_attributes set" do
|
|
748
|
+
payment = create(:payment)
|
|
749
|
+
payment.source_attributes = params[:source_attributes]
|
|
750
|
+
expect { payment.dup }.to_not change { payment.source }
|
|
751
|
+
end
|
|
752
|
+
end
|
|
753
|
+
|
|
754
|
+
describe "#currency" do
|
|
755
|
+
before { allow(order).to receive(:currency) { "ABC" } }
|
|
756
|
+
it "returns the order currency" do
|
|
757
|
+
expect(payment.currency).to eq("ABC")
|
|
758
|
+
end
|
|
759
|
+
end
|
|
760
|
+
|
|
761
|
+
describe "#display_amount" do
|
|
762
|
+
it "returns a Spree::Money for this amount" do
|
|
763
|
+
expect(payment.display_amount).to eq(Spree::Money.new(payment.amount))
|
|
764
|
+
end
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
# Regression test for #2216
|
|
768
|
+
describe "#gateway_options" do
|
|
769
|
+
before { allow(order).to receive_messages(:last_ip_address => "192.168.1.1") }
|
|
770
|
+
|
|
771
|
+
it "contains an IP" do
|
|
772
|
+
expect(payment.gateway_options[:ip]).to eq(order.last_ip_address)
|
|
773
|
+
end
|
|
774
|
+
|
|
775
|
+
it "contains the email address from a persisted order" do
|
|
776
|
+
# Sets the payment's order to a different Ruby object entirely
|
|
777
|
+
payment.order = Spree::Order.find(payment.order_id)
|
|
778
|
+
email = 'foo@example.com'
|
|
779
|
+
order.update_attributes(:email => email)
|
|
780
|
+
expect(payment.gateway_options[:email]).to eq(email)
|
|
781
|
+
end
|
|
782
|
+
end
|
|
783
|
+
|
|
784
|
+
describe "#set_unique_identifier" do
|
|
785
|
+
# Regression test for #1998
|
|
786
|
+
it "sets a unique identifier on create" do
|
|
787
|
+
payment.run_callbacks(:create)
|
|
788
|
+
expect(payment.identifier).not_to be_blank
|
|
789
|
+
expect(payment.identifier.size).to eq(8)
|
|
790
|
+
expect(payment.identifier).to be_a(String)
|
|
791
|
+
end
|
|
792
|
+
|
|
793
|
+
# Regression test for #3733
|
|
794
|
+
it "does not regenerate the identifier on re-save" do
|
|
795
|
+
payment.save
|
|
796
|
+
old_identifier = payment.identifier
|
|
797
|
+
payment.save
|
|
798
|
+
expect(payment.identifier).to eq(old_identifier)
|
|
799
|
+
end
|
|
800
|
+
|
|
801
|
+
context "other payment exists" do
|
|
802
|
+
let(:other_payment) {
|
|
803
|
+
payment = Spree::Payment.new
|
|
804
|
+
payment.source = card
|
|
805
|
+
payment.order = order
|
|
806
|
+
payment.payment_method = gateway
|
|
807
|
+
payment
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
before { other_payment.save! }
|
|
811
|
+
|
|
812
|
+
it "doesn't set duplicate identifier" do
|
|
813
|
+
expect(payment).to receive(:generate_identifier).and_return(other_payment.identifier)
|
|
814
|
+
expect(payment).to receive(:generate_identifier).and_call_original
|
|
815
|
+
|
|
816
|
+
payment.run_callbacks(:create)
|
|
817
|
+
|
|
818
|
+
expect(payment.identifier).not_to be_blank
|
|
819
|
+
expect(payment.identifier).not_to eq(other_payment.identifier)
|
|
820
|
+
end
|
|
821
|
+
end
|
|
822
|
+
end
|
|
823
|
+
|
|
824
|
+
describe "#amount=" do
|
|
825
|
+
before do
|
|
826
|
+
subject.amount = amount
|
|
827
|
+
end
|
|
828
|
+
|
|
829
|
+
context "when the amount is a string" do
|
|
830
|
+
context "amount is a decimal" do
|
|
831
|
+
let(:amount) { '2.99' }
|
|
832
|
+
|
|
833
|
+
it '#amount' do
|
|
834
|
+
expect(subject.amount).to eql(BigDecimal('2.99'))
|
|
835
|
+
end
|
|
836
|
+
end
|
|
837
|
+
|
|
838
|
+
context "amount is an integer" do
|
|
839
|
+
let(:amount) { '2' }
|
|
840
|
+
|
|
841
|
+
it '#amount' do
|
|
842
|
+
expect(subject.amount).to eql(BigDecimal('2.0'))
|
|
843
|
+
end
|
|
844
|
+
end
|
|
845
|
+
|
|
846
|
+
context "amount contains a dollar sign" do
|
|
847
|
+
let(:amount) { '$2.99' }
|
|
848
|
+
|
|
849
|
+
it '#amount' do
|
|
850
|
+
expect(subject.amount).to eql(BigDecimal('2.99'))
|
|
851
|
+
end
|
|
852
|
+
end
|
|
853
|
+
|
|
854
|
+
context "amount contains a comma" do
|
|
855
|
+
let(:amount) { '$2,999.99' }
|
|
856
|
+
|
|
857
|
+
it '#amount' do
|
|
858
|
+
expect(subject.amount).to eql(BigDecimal('2999.99'))
|
|
859
|
+
end
|
|
860
|
+
end
|
|
861
|
+
|
|
862
|
+
context "amount contains a negative sign" do
|
|
863
|
+
let(:amount) { '-2.99' }
|
|
864
|
+
|
|
865
|
+
it '#amount' do
|
|
866
|
+
expect(subject.amount).to eql(BigDecimal('-2.99'))
|
|
867
|
+
end
|
|
868
|
+
end
|
|
869
|
+
|
|
870
|
+
context "amount is invalid" do
|
|
871
|
+
let(:amount) { 'invalid' }
|
|
872
|
+
|
|
873
|
+
# this is a strange default for ActiveRecord
|
|
874
|
+
|
|
875
|
+
it '#amount' do
|
|
876
|
+
expect(subject.amount).to eql(BigDecimal('0'))
|
|
877
|
+
end
|
|
878
|
+
end
|
|
879
|
+
|
|
880
|
+
context "amount is an empty string" do
|
|
881
|
+
let(:amount) { '' }
|
|
882
|
+
|
|
883
|
+
it '#amount' do
|
|
884
|
+
expect(subject.amount).to be_nil
|
|
885
|
+
end
|
|
886
|
+
end
|
|
887
|
+
end
|
|
888
|
+
|
|
889
|
+
context "when the amount is a number" do
|
|
890
|
+
let(:amount) { 1.55 }
|
|
891
|
+
|
|
892
|
+
it '#amount' do
|
|
893
|
+
expect(subject.amount).to eql(BigDecimal('1.55'))
|
|
894
|
+
end
|
|
895
|
+
end
|
|
896
|
+
|
|
897
|
+
context "when the locale uses a coma as a decimal separator" do
|
|
898
|
+
before(:each) do
|
|
899
|
+
I18n.backend.store_translations(:fr, { :number => { :currency => { :format => { :delimiter => ' ', :separator => ',' } } } })
|
|
900
|
+
I18n.locale = :fr
|
|
901
|
+
subject.amount = amount
|
|
902
|
+
end
|
|
903
|
+
|
|
904
|
+
after do
|
|
905
|
+
I18n.locale = I18n.default_locale
|
|
906
|
+
end
|
|
907
|
+
|
|
908
|
+
context "amount is a decimal" do
|
|
909
|
+
let(:amount) { '2,99' }
|
|
910
|
+
|
|
911
|
+
it '#amount' do
|
|
912
|
+
expect(subject.amount).to eql(BigDecimal('2.99'))
|
|
913
|
+
end
|
|
914
|
+
end
|
|
915
|
+
|
|
916
|
+
context "amount contains a $ sign" do
|
|
917
|
+
let(:amount) { '2,99 $' }
|
|
918
|
+
|
|
919
|
+
it '#amount' do
|
|
920
|
+
expect(subject.amount).to eql(BigDecimal('2.99'))
|
|
921
|
+
end
|
|
922
|
+
end
|
|
923
|
+
|
|
924
|
+
context "amount is a number" do
|
|
925
|
+
let(:amount) { 2.99 }
|
|
926
|
+
|
|
927
|
+
it '#amount' do
|
|
928
|
+
expect(subject.amount).to eql(BigDecimal('2.99'))
|
|
929
|
+
end
|
|
930
|
+
end
|
|
931
|
+
|
|
932
|
+
context "amount contains a negative sign" do
|
|
933
|
+
let(:amount) { '-2,99 $' }
|
|
934
|
+
|
|
935
|
+
it '#amount' do
|
|
936
|
+
expect(subject.amount).to eql(BigDecimal('-2.99'))
|
|
937
|
+
end
|
|
938
|
+
end
|
|
939
|
+
|
|
940
|
+
context "amount uses a dot as a decimal separator" do
|
|
941
|
+
let(:amount) { '2.99' }
|
|
942
|
+
|
|
943
|
+
it '#amount' do
|
|
944
|
+
expect(subject.amount).to eql(BigDecimal('2.99'))
|
|
945
|
+
end
|
|
946
|
+
end
|
|
947
|
+
end
|
|
948
|
+
end
|
|
949
|
+
|
|
950
|
+
describe "is_avs_risky?" do
|
|
951
|
+
it "returns false if avs_response included in NON_RISKY_AVS_CODES" do
|
|
952
|
+
('A'..'Z').reject{ |x| subject.class::RISKY_AVS_CODES.include?(x) }.to_a.each do |char|
|
|
953
|
+
payment.update_attribute(:avs_response, char)
|
|
954
|
+
expect(payment.is_avs_risky?).to eq false
|
|
955
|
+
end
|
|
956
|
+
end
|
|
957
|
+
|
|
958
|
+
it "returns false if avs_response.blank?" do
|
|
959
|
+
payment.update_attribute(:avs_response, nil)
|
|
960
|
+
expect(payment.is_avs_risky?).to eq false
|
|
961
|
+
payment.update_attribute(:avs_response, '')
|
|
962
|
+
expect(payment.is_avs_risky?).to eq false
|
|
963
|
+
end
|
|
964
|
+
|
|
965
|
+
it "returns true if avs_response in RISKY_AVS_CODES" do
|
|
966
|
+
# should use avs_response_code helper
|
|
967
|
+
('A'..'Z').reject{ |x| subject.class::NON_RISKY_AVS_CODES.include?(x) }.to_a.each do |char|
|
|
968
|
+
payment.update_attribute(:avs_response, char)
|
|
969
|
+
expect(payment.is_avs_risky?).to eq true
|
|
970
|
+
end
|
|
971
|
+
end
|
|
972
|
+
end
|
|
973
|
+
|
|
974
|
+
describe "is_cvv_risky?" do
|
|
975
|
+
it "returns false if cvv_response_code == 'M'" do
|
|
976
|
+
payment.update_attribute(:cvv_response_code, "M")
|
|
977
|
+
expect(payment.is_cvv_risky?).to eq(false)
|
|
978
|
+
end
|
|
979
|
+
|
|
980
|
+
it "returns false if cvv_response_code == nil" do
|
|
981
|
+
payment.update_attribute(:cvv_response_code, nil)
|
|
982
|
+
expect(payment.is_cvv_risky?).to eq(false)
|
|
983
|
+
end
|
|
984
|
+
|
|
985
|
+
it "returns false if cvv_response_message == ''" do
|
|
986
|
+
payment.update_attribute(:cvv_response_message, '')
|
|
987
|
+
expect(payment.is_cvv_risky?).to eq(false)
|
|
988
|
+
end
|
|
989
|
+
|
|
990
|
+
it "returns true if cvv_response_code == [A-Z], omitting D" do
|
|
991
|
+
# should use cvv_response_code helper
|
|
992
|
+
(%w{N P S U} << "").each do |char|
|
|
993
|
+
payment.update_attribute(:cvv_response_code, char)
|
|
994
|
+
expect(payment.is_cvv_risky?).to eq(true)
|
|
995
|
+
end
|
|
996
|
+
end
|
|
997
|
+
end
|
|
998
|
+
|
|
999
|
+
# Regression test for #4072 (kinda)
|
|
1000
|
+
# The need for this was discovered in the research for #4072
|
|
1001
|
+
context "state changes" do
|
|
1002
|
+
it "are logged to the database" do
|
|
1003
|
+
expect(payment.state_changes).to be_empty
|
|
1004
|
+
expect(payment.process!).to be true
|
|
1005
|
+
expect(payment.state_changes.count).to eq(2)
|
|
1006
|
+
changes = payment.state_changes.map { |change| { change.previous_state => change.next_state} }
|
|
1007
|
+
expect(changes).to match_array([
|
|
1008
|
+
{"checkout" => "processing"},
|
|
1009
|
+
{ "processing" => "pending"}
|
|
1010
|
+
])
|
|
1011
|
+
end
|
|
1012
|
+
end
|
|
1013
|
+
|
|
1014
|
+
describe "#actions" do
|
|
1015
|
+
let(:source) { Spree::CreditCard.new }
|
|
1016
|
+
before { allow(subject).to receive(:payment_source) { source } }
|
|
1017
|
+
|
|
1018
|
+
it "includes the actions that the source can take" do
|
|
1019
|
+
allow(source).to receive(:can_capture?) { true }
|
|
1020
|
+
expect(subject.actions).to include "capture"
|
|
1021
|
+
end
|
|
1022
|
+
|
|
1023
|
+
it "excludes actions that the source cannot take" do
|
|
1024
|
+
allow(source).to receive(:can_capture?) { false }
|
|
1025
|
+
expect(subject.actions).not_to include "capture"
|
|
1026
|
+
end
|
|
1027
|
+
|
|
1028
|
+
it "does not include 'failure' by default" do
|
|
1029
|
+
expect(subject.actions).not_to include "failure"
|
|
1030
|
+
end
|
|
1031
|
+
|
|
1032
|
+
context "payment state is processing" do
|
|
1033
|
+
it "includes the 'failure' action" do
|
|
1034
|
+
# because the processing state does not provide
|
|
1035
|
+
# clarity about what has happened with an external
|
|
1036
|
+
# payment processor, so we want to allow the ability
|
|
1037
|
+
# to have someone look at the what happened and determine
|
|
1038
|
+
# to mark the payment as having failed
|
|
1039
|
+
subject.state = 'processing'
|
|
1040
|
+
expect(subject.actions).to include "failure"
|
|
1041
|
+
end
|
|
1042
|
+
end
|
|
1043
|
+
end
|
|
1044
|
+
end
|