spree_unified_payment 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +9 -0
- data/.travis.yml +6 -0
- data/Gemfile +20 -0
- data/LICENSE +26 -0
- data/README.md +61 -0
- data/Versionfile +12 -0
- data/app/assets/javascripts/admin/spree_unified_payment.js +39 -0
- data/app/assets/stylesheets/store/spree_unified_payment.css +7 -0
- data/app/controllers/application_controller.rb +2 -0
- data/app/controllers/spree/admin/unified_payments_controller.rb +34 -0
- data/app/controllers/spree/checkout_controller_decorator.rb +19 -0
- data/app/controllers/spree/unified_payments_controller.rb +151 -0
- data/app/helpers/transaction_notification_mail_helper.rb +12 -0
- data/app/helpers/unified_transaction_helper.rb +18 -0
- data/app/mailers/spree/transaction_notification_mailer.rb +16 -0
- data/app/models/spree/order_decorator.rb +67 -0
- data/app/models/spree/payment_method/unified_payment_method.rb +34 -0
- data/app/models/spree/store_credit_decorator.rb +4 -0
- data/app/models/spree/user_decorator.rb +7 -0
- data/app/models/unified_payment/transaction_decorator.rb +102 -0
- data/app/overrides/add_unified_tabs_to_admin_menu.rb +6 -0
- data/app/views/spree/admin/unified_payments/index.html.erb +98 -0
- data/app/views/spree/admin/unified_payments/query_gateway.js.erb +10 -0
- data/app/views/spree/admin/unified_payments/receipt.html.erb +20 -0
- data/app/views/spree/checkout/payment/_unifiedpaymentmethod.html.erb +24 -0
- data/app/views/spree/transaction_notification_mailer/send_mail.html.erb +20 -0
- data/app/views/spree/unified_payments/approved.html.erb +25 -0
- data/app/views/spree/unified_payments/canceled.html.erb +1 -0
- data/app/views/spree/unified_payments/create.html.erb +8 -0
- data/app/views/spree/unified_payments/create.js.erb +3 -0
- data/app/views/spree/unified_payments/declined.html.erb +4 -0
- data/app/views/spree/unified_payments/index.html.erb +28 -0
- data/app/views/spree/unified_payments/new.html.erb +35 -0
- data/app/views/spree/unified_payments/new.js.erb +3 -0
- data/config/initializers/constants.rb +8 -0
- data/config/routes.rb +15 -0
- data/db/migrate/20140120075553_add_transaction_fields_to_unified_payment_transactions.rb +14 -0
- data/db/migrate/20140120081453_add_unified_transaction_id_to_spree_store_credits.rb +6 -0
- data/lib/generators/spree_unified_payment/install/install_generator.rb +27 -0
- data/lib/spree_unified_payment/engine.rb +26 -0
- data/lib/spree_unified_payment.rb +2 -0
- data/lib/transaction_expiration.rb +9 -0
- data/spec/constants_spec.rb +11 -0
- data/spec/controllers/spree/admin/unified_payments_controller_spec.rb +155 -0
- data/spec/controllers/spree/checkout_controller_decorator_spec.rb +114 -0
- data/spec/controllers/spree/unified_payments_controller_spec.rb +509 -0
- data/spec/mailers/transaction_notification_mailer_spec.rb +48 -0
- data/spec/models/spree/order_decorator_spec.rb +206 -0
- data/spec/models/spree/payment_method/unified_payment_spec.rb +25 -0
- data/spec/models/spree/store_credit_decorator_spec.rb +11 -0
- data/spec/models/spree/user_decorator_spec.rb +12 -0
- data/spec/models/unified_payment/transaction_decorator_spec.rb +483 -0
- data/spec/spec_helper.rb +66 -0
- data/spree_unified_payment.gemspec +23 -0
- metadata +184 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Spree::TransactionNotificationMailer do
|
4
|
+
let(:card_transaction) { mock_model(UnifiedPayment::Transaction, :status => 'successful', :xml_response => '') }
|
5
|
+
let(:order) { mock_model(Spree::Order) }
|
6
|
+
let(:user) { mock_model(Spree::User) }
|
7
|
+
|
8
|
+
before do
|
9
|
+
@email = "test_user@westaycute.com"
|
10
|
+
order.stub(:line_items).and_return([])
|
11
|
+
card_transaction.stub(:order).and_return(order)
|
12
|
+
card_transaction.stub(:user).and_return(user)
|
13
|
+
user.stub(:email).and_return(@email)
|
14
|
+
end
|
15
|
+
describe 'fetches info from card_transaction' do
|
16
|
+
it { card_transaction.should_receive(:order).and_return(order) }
|
17
|
+
it { card_transaction.should_receive(:user).and_return(order) }
|
18
|
+
it { user.should_receive(:email).and_return(@email) }
|
19
|
+
it { card_transaction.should_receive(:status).and_return(card_transaction.status) }
|
20
|
+
it { card_transaction.should_receive(:xml_response).and_return('') }
|
21
|
+
|
22
|
+
after do
|
23
|
+
Spree::TransactionNotificationMailer.send_mail(card_transaction)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'when xml response is a message' do
|
28
|
+
before do
|
29
|
+
card_transaction.stub(:xml_response).and_return("<Message><OrderStatus>Approved</OrderStatus></Message>")
|
30
|
+
end
|
31
|
+
|
32
|
+
it { Hash.should_receive(:from_xml).with(card_transaction.xml_response).and_return({'Message' => { 'OrderStatus' => 'Approved'} } ) }
|
33
|
+
after do
|
34
|
+
Spree::TransactionNotificationMailer.send_mail(card_transaction)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'when xml response is not a message' do
|
39
|
+
before do
|
40
|
+
card_transaction.stub(:xml_response).and_return("<NoMessage></NoMessage>")
|
41
|
+
end
|
42
|
+
|
43
|
+
it { Hash.should_not_receive(:from_xml) }
|
44
|
+
after do
|
45
|
+
Spree::TransactionNotificationMailer.send_mail(card_transaction)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Spree::Order do
|
4
|
+
let(:user) { mock_model(Spree::User) }
|
5
|
+
before(:each) do
|
6
|
+
@order = Spree::Order.create!
|
7
|
+
end
|
8
|
+
|
9
|
+
it { should have_many :unified_transactions }
|
10
|
+
|
11
|
+
describe "#pending_card_transaction" do
|
12
|
+
before do
|
13
|
+
UnifiedPayment::Transaction.any_instance.stub(:wallet_transaction).and_return(true)
|
14
|
+
UnifiedPayment::Transaction.any_instance.stub(:enqueue_expiration_task).and_return(true)
|
15
|
+
UnifiedPayment::Transaction.any_instance.stub(:assign_attributes_using_xml).and_return(true)
|
16
|
+
UnifiedPayment::Transaction.any_instance.stub(:complete_order).and_return(true)
|
17
|
+
UnifiedPayment::Transaction.any_instance.stub(:notify_user_on_transaction_status).and_return(true)
|
18
|
+
@successful_card_transaction = @order.unified_transactions.create!(:status => 'successful', :payment_transaction_id => '1234')
|
19
|
+
@pending_card_transaction = @order.unified_transactions.create!(:status => 'pending', :payment_transaction_id => '1234')
|
20
|
+
end
|
21
|
+
|
22
|
+
it { @order.pending_card_transaction.should eq(@pending_card_transaction) }
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#reserve_stock' do
|
26
|
+
before do
|
27
|
+
@pending_inventory_unit = mock_model(Spree::InventoryUnit, :pending => true)
|
28
|
+
@order_shipment_with_pending_units = mock_model(Spree::Shipment)
|
29
|
+
@order_shipment_with_pending_units.stub(:finalize!).and_return(true)
|
30
|
+
@order_shipment_with_pending_units.stub(:update!).with(@order).and_return(true)
|
31
|
+
@order_shipment_with_pending_units.stub(:inventory_units).and_return([@pending_inventory_unit])
|
32
|
+
|
33
|
+
@unpending_inventory_unit = mock_model(Spree::InventoryUnit, :pending => false)
|
34
|
+
@order_shipment_without_pending_units = mock_model(Spree::Shipment)
|
35
|
+
@order_shipment_without_pending_units.stub(:inventory_units).and_return([@unpending_inventory_unit])
|
36
|
+
@order.stub(:shipments).and_return([@order_shipment_with_pending_units, @order_shipment_without_pending_units])
|
37
|
+
end
|
38
|
+
|
39
|
+
it { @order.should_receive(:shipments).and_return([@order_shipment_with_pending_units, @order_shipment_without_pending_units]) }
|
40
|
+
it { @order_shipment_with_pending_units.should_receive(:update!).with(@order).and_return(true) }
|
41
|
+
it { @order_shipment_with_pending_units.should_receive(:finalize!).and_return(true) }
|
42
|
+
it { @order_shipment_without_pending_units.should_not_receive(:update!).with(@order) }
|
43
|
+
it { @order_shipment_without_pending_units.should_not_receive(:finalize!) }
|
44
|
+
|
45
|
+
after do
|
46
|
+
@order.reserve_stock
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#create_proposed_shipments' do
|
51
|
+
before do
|
52
|
+
@order_shipment = mock_model(Spree::Shipment)
|
53
|
+
inventory_unit = mock_model(Spree::InventoryUnit, :pending => false)
|
54
|
+
@order_shipment.stub(:inventory_units).and_return([inventory_unit])
|
55
|
+
@order_shipment.stub(:cancel).and_return(true)
|
56
|
+
|
57
|
+
@pending_order_shipment = mock_model(Spree::Shipment)
|
58
|
+
pending_inventory_unit = mock_model(Spree::InventoryUnit, :pending => true)
|
59
|
+
@pending_order_shipment.stub(:inventory_units).and_return([pending_inventory_unit])
|
60
|
+
|
61
|
+
@shipments = [@order_shipment, @pending_order_shipment]
|
62
|
+
@shipments.stub(:destroy_all).and_return(true)
|
63
|
+
@order.stub(:shipments).and_return(@shipments)
|
64
|
+
end
|
65
|
+
|
66
|
+
it { @order_shipment.should_receive(:cancel).and_return(true) }
|
67
|
+
it { @pending_order_shipment.should_not_receive(:cancel) }
|
68
|
+
it { @shipments.should_receive(:destroy_all).and_return(true) }
|
69
|
+
|
70
|
+
after do
|
71
|
+
@order.create_proposed_shipments
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
describe '#release_inventory' do
|
77
|
+
before do
|
78
|
+
@pending_inventory_unit = mock_model(Spree::InventoryUnit, :pending => true)
|
79
|
+
@order_shipment_with_pending_units = mock_model(Spree::Shipment)
|
80
|
+
@order_shipment_with_pending_units.stub(:finalize!).and_return(true)
|
81
|
+
@order_shipment_with_pending_units.stub(:update!).with(@order).and_return(true)
|
82
|
+
@order_shipment_with_pending_units.stub(:inventory_units).and_return([@pending_inventory_unit])
|
83
|
+
|
84
|
+
@unpending_inventory_unit = mock_model(Spree::InventoryUnit, :pending => false)
|
85
|
+
@order_shipment_without_pending_units = mock_model(Spree::Shipment)
|
86
|
+
@order_shipment_without_pending_units.stub(:inventory_units).and_return([@unpending_inventory_unit])
|
87
|
+
@order_shipment_without_pending_units.stub(:cancel).and_return(true)
|
88
|
+
@order.stub(:shipments).and_return([@order_shipment_with_pending_units, @order_shipment_without_pending_units])
|
89
|
+
end
|
90
|
+
|
91
|
+
it { @order_shipment_without_pending_units.should_receive(:cancel).and_return(true) }
|
92
|
+
it { @order_shipment_with_pending_units.should_not_receive(:cancel) }
|
93
|
+
|
94
|
+
after do
|
95
|
+
@order.release_inventory
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe '#reason_if_cant_pay_by_card' do
|
100
|
+
before do
|
101
|
+
@order.total = 100
|
102
|
+
# @order.stub(:completed?).and_return(false)
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'total is 0' do
|
106
|
+
before { @order.total = 0 }
|
107
|
+
it { @order.reason_if_cant_pay_by_card.should eq('Order Total is invalid') }
|
108
|
+
end
|
109
|
+
|
110
|
+
context 'order is completed' do
|
111
|
+
before { @order.stub(:completed?).and_return(true) }
|
112
|
+
it { @order.reason_if_cant_pay_by_card.should eq('Order already completed') }
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'order has insufficient stock lines' do
|
116
|
+
before { @order.stub(:insufficient_stock_lines).and_return([0]) }
|
117
|
+
it { @order.reason_if_cant_pay_by_card.should eq('An item in your cart has become unavailable.') }
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe 'finalize!' do
|
122
|
+
before do
|
123
|
+
@order.stub(:user_id).and_return(user.id)
|
124
|
+
@adjustment = mock_model(Spree::Adjustment)
|
125
|
+
@order.stub(:adjustments).and_return([@adjustment])
|
126
|
+
@adjustment.stub(:update_column).with("state", "closed").and_return(true)
|
127
|
+
@order.stub(:save).and_return(true)
|
128
|
+
@order_updater = Spree::OrderUpdater.new(@order)
|
129
|
+
@order.stub(:updater).and_return(@order_updater)
|
130
|
+
@order_updater.stub(:update_shipment_state).and_return(true)
|
131
|
+
@order_updater.stub(:update_payment_state).and_return(true)
|
132
|
+
@order_updater.stub(:run_hooks).and_return(true)
|
133
|
+
@state_changes = []
|
134
|
+
@state_changes.stub(:create).with({:previous_state=>'confirm', :next_state=>"complete", :name=>"order", :user_id=>user.id}, {:without_protection=>true}).and_return(true)
|
135
|
+
|
136
|
+
@order.stub(:state_changes).and_return(@state_changes)
|
137
|
+
@order.stub(:previous_states).and_return([:delivery, :payment, :confirm])
|
138
|
+
@order.stub(:reserve_stock).and_return(true)
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'updates completed at' do
|
142
|
+
@order.completed_at.should be_nil
|
143
|
+
@order.finalize!
|
144
|
+
@order.reload.completed_at.should_not be_nil
|
145
|
+
@order.stub(:deliver_order_confirmation_email).and_return(true)
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'udpates adjustments' do
|
149
|
+
@adjustment.should_receive(:update_column).with("state", "closed").and_return(true)
|
150
|
+
@order.finalize!
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'saves self' do
|
154
|
+
@order.should_receive(:save).and_return(true)
|
155
|
+
@order.finalize!
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'updates shipment states' do
|
159
|
+
@order_updater.should_receive(:update_shipment_state).and_return(true)
|
160
|
+
@order.finalize!
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'updates payment states' do
|
164
|
+
@order_updater.should_receive(:update_payment_state).and_return(true)
|
165
|
+
@order.finalize!
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'run hooks through updater' do
|
169
|
+
@order_updater.should_receive(:run_hooks).and_return(true)
|
170
|
+
@order.finalize!
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'sends email' do
|
174
|
+
@order.should_receive(:deliver_order_confirmation_email).and_return(true)
|
175
|
+
@order.finalize!
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'stores state changes' do
|
179
|
+
@state_changes.should_receive(:create).with({:previous_state=>'confirm', :next_state=>"complete", :name=>"order", :user_id=>user.id}, {:without_protection=>true}).and_return(true)
|
180
|
+
@order.finalize!
|
181
|
+
end
|
182
|
+
|
183
|
+
context 'when orders last state was confirm' do
|
184
|
+
before do
|
185
|
+
@order.stub(:previous_states).and_return([:delivery, :payment, :confirm])
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'does not reserve stock' do
|
189
|
+
@order.should_not_receive(:reserve_stock)
|
190
|
+
@order.finalize!
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
context 'when orders last state was not confirm' do
|
195
|
+
before do
|
196
|
+
@order.stub(:previous_states).and_return([:delivery, :payment])
|
197
|
+
@state_changes.stub(:create).with({:previous_state=>"payment", :next_state=>"complete", :name=>"order", :user_id=>user.id}, {:without_protection=>true}).and_return(true)
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'reserves stock' do
|
201
|
+
@order.should_receive(:reserve_stock)
|
202
|
+
@order.finalize!
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Spree::PaymentMethod::UnifiedPaymentMethod do
|
4
|
+
let(:pending_payment) { mock_model(Spree::Payment, :state => 'pending') }
|
5
|
+
let(:complete_payment) { mock_model(Spree::Payment, :state => 'complete') }
|
6
|
+
let(:void_payment) { mock_model(Spree::Payment, :state => 'void') }
|
7
|
+
before { @unified_payment = Spree::PaymentMethod::UnifiedPaymentMethod.new }
|
8
|
+
it { @unified_payment.actions.should eq(["capture", "void"]) }
|
9
|
+
it { @unified_payment.can_capture?(pending_payment).should be_true }
|
10
|
+
it { @unified_payment.can_capture?(complete_payment).should be_false }
|
11
|
+
it { @unified_payment.can_void?(pending_payment).should be_true }
|
12
|
+
it { @unified_payment.can_void?(void_payment).should be_false }
|
13
|
+
it { @unified_payment.source_required?.should be_false }
|
14
|
+
it { @unified_payment.payment_profiles_supported?.should be_true }
|
15
|
+
|
16
|
+
it 'voids a payment' do
|
17
|
+
ActiveMerchant::Billing::Response.should_receive(:new).with(true, "", {}, {}).and_return(true)
|
18
|
+
@unified_payment.void
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'captures a payment' do
|
22
|
+
ActiveMerchant::Billing::Response.should_receive(:new).with(true, "", {}, {}).and_return(true)
|
23
|
+
@unified_payment.capture
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Spree::StoreCredit do
|
4
|
+
describe 'attr-accessible' do
|
5
|
+
[:balance, :user, :transactioner, :type].each do |attribute|
|
6
|
+
it { should allow_mass_assignment_of attribute }
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
it { should belong_to(:unified_transaction).class_name('UnifiedPayment::Transaction').with_foreign_key('unified_transaction_id') }
|
11
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Spree::User do
|
4
|
+
it { should have_many(:unified_payments).class_name('UnifiedPayment::Transaction') }
|
5
|
+
|
6
|
+
describe 'create_unified_transaction_user' do
|
7
|
+
it 'creates a new user' do
|
8
|
+
Spree::User.where(:email => 'new_user@unified.com').should be_blank
|
9
|
+
Spree::User.create_unified_transaction_user('new_user@unified.com').should eq(Spree::User.where(:email => 'new_user@unified.com').first )
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,483 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe UnifiedPayment::Transaction do
|
4
|
+
|
5
|
+
[:gateway_order_status, :amount, :currency, :response_status, :pan, :response_description, :order_description, :xml_response, :status].each do |attribute|
|
6
|
+
it { should allow_mass_assignment_of attribute }
|
7
|
+
end
|
8
|
+
|
9
|
+
it { should belong_to(:user).class_name('Spree::User') }
|
10
|
+
it { should belong_to(:order).class_name('Spree::Order') }
|
11
|
+
|
12
|
+
it { should have_one(:store_credit).class_name('Spree::StoreCredit') }
|
13
|
+
let(:order) { mock_model(Spree::Order) }
|
14
|
+
let(:user) { mock_model(Spree::User) }
|
15
|
+
|
16
|
+
before do
|
17
|
+
UnifiedPayment::Transaction.any_instance.stub(:assign_attributes_using_xml).and_return(true)
|
18
|
+
UnifiedPayment::Transaction.any_instance.stub(:notify_user_on_transaction_status).and_return(true)
|
19
|
+
UnifiedPayment::Transaction.any_instance.stub(:complete_order).and_return(true)
|
20
|
+
UnifiedPayment::Transaction.any_instance.stub(:cancel_order).and_return(true)
|
21
|
+
UnifiedPayment::Transaction.any_instance.stub(:wallet_transaction).and_return(true)
|
22
|
+
UnifiedPayment::Transaction.any_instance.stub(:enqueue_expiration_task).and_return(true)
|
23
|
+
UnifiedPayment::Transaction.any_instance.stub(:release_order_inventory).and_return(true)
|
24
|
+
UnifiedPayment::Transaction.any_instance.stub(:payment_valid_for_order?).and_return(true)
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'callbacks' do
|
28
|
+
context 'after and before save' do
|
29
|
+
context 'pending transaction on creation' do
|
30
|
+
before do
|
31
|
+
@pending_card_transaction = UnifiedPayment::Transaction.new(:status => 'pending', :payment_transaction_id => '1234')
|
32
|
+
end
|
33
|
+
|
34
|
+
it { @pending_card_transaction.should_not_receive(:notify_user_on_transaction_status) }
|
35
|
+
it { @pending_card_transaction.should_not_receive(:assign_attributes_using_xml) }
|
36
|
+
it { @pending_card_transaction.should_not_receive(:complete_order) }
|
37
|
+
it { @pending_card_transaction.should_not_receive(:cancel_order) }
|
38
|
+
it { @pending_card_transaction.should_not_receive(:wallet_transaction) }
|
39
|
+
it { @pending_card_transaction.should_not_receive(:release_order_inventory) }
|
40
|
+
|
41
|
+
after do
|
42
|
+
@pending_card_transaction.save!
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'pending to successful transaction' do
|
47
|
+
before do
|
48
|
+
@successful_card_transaction = UnifiedPayment::Transaction.new(:status => 'pending', :payment_transaction_id => '1234')
|
49
|
+
@successful_card_transaction.save!
|
50
|
+
@successful_card_transaction.status = 'successful'
|
51
|
+
end
|
52
|
+
|
53
|
+
it { @successful_card_transaction.should_receive(:notify_user_on_transaction_status).and_return(true) }
|
54
|
+
it { @successful_card_transaction.should_receive(:assign_attributes_using_xml).and_return(true) }
|
55
|
+
it { @successful_card_transaction.should_not_receive(:release_order_inventory) }
|
56
|
+
|
57
|
+
context 'order inventory released' do
|
58
|
+
before do
|
59
|
+
@successful_card_transaction.stub(:order_inventory_released?).and_return(true)
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'payment valid for order' do
|
63
|
+
it { @successful_card_transaction.should_receive(:wallet_transaction).and_return(true) }
|
64
|
+
it { @successful_card_transaction.should_not_receive(:complete_order) }
|
65
|
+
it { @successful_card_transaction.should_not_receive(:cancel_order) }
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'payment not valid for order' do
|
69
|
+
before { @successful_card_transaction.stub(:payment_valid_for_order?).and_return(false) }
|
70
|
+
|
71
|
+
it { @successful_card_transaction.should_receive(:wallet_transaction).and_return(true) }
|
72
|
+
it { @successful_card_transaction.should_not_receive(:complete_order) }
|
73
|
+
it { @successful_card_transaction.should_not_receive(:cancel_order) }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'order inventory not released and' do
|
78
|
+
before { @successful_card_transaction.stub(:order_inventory_released?).and_return(false) }
|
79
|
+
context 'payment valid for order' do
|
80
|
+
it { @successful_card_transaction.should_receive(:complete_order).and_return(true) }
|
81
|
+
it { @successful_card_transaction.should_not_receive(:wallet_transaction) }
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'payment not valid for order' do
|
85
|
+
before { @successful_card_transaction.stub(:payment_valid_for_order?).and_return(false) }
|
86
|
+
it { @successful_card_transaction.should_receive(:wallet_transaction).and_return(true) }
|
87
|
+
it { @successful_card_transaction.should_not_receive(:complete_order) }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
after do
|
92
|
+
@successful_card_transaction.save!
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'pending to unsuccessful transaction' do
|
97
|
+
before do
|
98
|
+
@unsuccessful_card_transaction = UnifiedPayment::Transaction.new(:status => 'pending', :payment_transaction_id => '1234')
|
99
|
+
@unsuccessful_card_transaction.save!
|
100
|
+
@unsuccessful_card_transaction.status = 'unsuccessful'
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'order inventory released' do
|
104
|
+
before do
|
105
|
+
@unsuccessful_card_transaction.stub(:order_inventory_released?).and_return(true)
|
106
|
+
end
|
107
|
+
it { @unsuccessful_card_transaction.should_not_receive(:complete_order) }
|
108
|
+
end
|
109
|
+
|
110
|
+
it { @unsuccessful_card_transaction.should_receive(:notify_user_on_transaction_status).and_return(true) }
|
111
|
+
it { @unsuccessful_card_transaction.should_receive(:assign_attributes_using_xml).and_return(true) }
|
112
|
+
it { @unsuccessful_card_transaction.should_not_receive(:complete_order) }
|
113
|
+
it { @unsuccessful_card_transaction.should_receive(:cancel_order).and_return(true) }
|
114
|
+
it { @unsuccessful_card_transaction.should_not_receive(:release_order_inventory) }
|
115
|
+
|
116
|
+
after do
|
117
|
+
@unsuccessful_card_transaction.save!
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'expire transaction' do
|
122
|
+
before do
|
123
|
+
@pending_card_transaction = UnifiedPayment::Transaction.create!(:status => 'pending', :payment_transaction_id => '1234')
|
124
|
+
@expired_card_transaction = UnifiedPayment::Transaction.create!(:status => 'unsuccessful', :payment_transaction_id => '1234')
|
125
|
+
@expired_card_transaction.expired_at = Time.current
|
126
|
+
@expired_card_transaction.save!
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'should not call release_order_inventory when not expiring a card transaction' do
|
130
|
+
@pending_card_transaction.should_not_receive(:release_order_inventory)
|
131
|
+
@pending_card_transaction.update_attribute(:status, :successful)
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'should not call release_order_inventory for expiring expired card transaction' do
|
135
|
+
@expired_card_transaction.should_not_receive(:release_order_inventory)
|
136
|
+
@expired_card_transaction.update_attribute(:expired_at, Time.now)
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'should call release_order_inventory for expiring pending card transaction' do
|
140
|
+
@pending_card_transaction.should_receive(:release_order_inventory).and_return(true)
|
141
|
+
@pending_card_transaction.update_attribute(:expired_at, Time.now)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
context 'scopes' do
|
148
|
+
describe 'pending' do
|
149
|
+
before do
|
150
|
+
@successful_card_transaction = UnifiedPayment::Transaction.create!(:status => 'successful', :payment_transaction_id => '1234')
|
151
|
+
@pending_card_transaction = UnifiedPayment::Transaction.create!(:status => 'pending', :payment_transaction_id => '1234')
|
152
|
+
end
|
153
|
+
|
154
|
+
it { UnifiedPayment::Transaction.pending.should eq([@pending_card_transaction]) }
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
describe '#order_inventory_released?' do
|
159
|
+
before(:all) do
|
160
|
+
@expired_card_transaction = UnifiedPayment::Transaction.new
|
161
|
+
@expired_card_transaction.expired_at = Time.now
|
162
|
+
@pending_card_transaction = UnifiedPayment::Transaction.new
|
163
|
+
end
|
164
|
+
|
165
|
+
it { @expired_card_transaction.order_inventory_released?.should be_true }
|
166
|
+
it { @pending_card_transaction.order_inventory_released?.should be_false }
|
167
|
+
end
|
168
|
+
|
169
|
+
describe '#assign_attributes_using_xml' do
|
170
|
+
before do
|
171
|
+
UnifiedPayment::Transaction.any_instance.unstub(:assign_attributes_using_xml)
|
172
|
+
@card_transaction_with_message = UnifiedPayment::Transaction.create!(:payment_transaction_id => '123321')
|
173
|
+
@xml_response = '<Message><PAN>123XXX123</PAN><PurchaseAmountScr>200</PurchaseAmountScr><Currency>NGN</Currency><ResponseDescription>TestDescription</ResponseDescription><OrderStatus>OnTest</OrderStatus><OrderDescription>TestOrder</OrderDescription><Status>00</Status><MerchantTranID>12345654321</MerchantTranID><ApprovalCode>123ABC</ApprovalCode></Message>'
|
174
|
+
@card_transaction_with_message.stub(:xml_response).and_return(@xml_response)
|
175
|
+
@gateway_transaction = UnifiedPayment::Transaction.new
|
176
|
+
@card_transaction_with_message.stub(:gateway_transaction).and_return(@gateway_transaction)
|
177
|
+
@card_transaction_without_message = UnifiedPayment::Transaction.new()
|
178
|
+
end
|
179
|
+
|
180
|
+
describe 'method calls' do
|
181
|
+
it { @card_transaction_with_message.should_receive(:xml_response).and_return(@xml_response) }
|
182
|
+
it { @xml_response.should_receive(:include?).with('<Message').and_return(true) }
|
183
|
+
|
184
|
+
after do
|
185
|
+
@card_transaction_with_message.send(:assign_attributes_using_xml)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe 'assigns' do
|
190
|
+
before { @card_transaction_with_message.send(:assign_attributes_using_xml) }
|
191
|
+
it { @card_transaction_with_message.pan.should eq('123XXX123') }
|
192
|
+
it { @card_transaction_with_message.response_description.should eq('TestDescription') }
|
193
|
+
it { @card_transaction_with_message.gateway_order_status.should eq('OnTest') }
|
194
|
+
it { @card_transaction_with_message.order_description.should eq('TestOrder') }
|
195
|
+
it { @card_transaction_with_message.response_status.should eq('00') }
|
196
|
+
it { @card_transaction_with_message.approval_code.should eq('123ABC') }
|
197
|
+
it { @card_transaction_with_message.merchant_id.should eq('12345654321') }
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
describe '#notify_user_on_transaction_status' do
|
202
|
+
before do
|
203
|
+
UnifiedPayment::Transaction.any_instance.unstub(:notify_user_on_transaction_status)
|
204
|
+
@card_transaction = UnifiedPayment::Transaction.new(:status => 'pending', :payment_transaction_id => '1234')
|
205
|
+
@mailer_object = Object.new
|
206
|
+
@mailer_object.stub(:deliver!).and_return(true)
|
207
|
+
Spree::TransactionNotificationMailer.stub(:delay).and_return(Spree::TransactionNotificationMailer)
|
208
|
+
Spree::TransactionNotificationMailer.stub(:send_mail).with(@card_transaction).and_return(@mailer_object)
|
209
|
+
end
|
210
|
+
|
211
|
+
context 'when previous state was not pending' do
|
212
|
+
it { Spree::TransactionNotificationMailer.should_not_receive(:send_mail) }
|
213
|
+
end
|
214
|
+
|
215
|
+
context 'when previous state was pending' do
|
216
|
+
before do
|
217
|
+
@card_transaction.save!
|
218
|
+
@card_transaction.status = 'successful'
|
219
|
+
end
|
220
|
+
|
221
|
+
it { Spree::TransactionNotificationMailer.should_receive(:delay).and_return(Spree::TransactionNotificationMailer) }
|
222
|
+
it { Spree::TransactionNotificationMailer.should_receive(:send_mail).with(@card_transaction).and_return(@mailer_object) }
|
223
|
+
end
|
224
|
+
|
225
|
+
after do
|
226
|
+
@card_transaction.save!
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
describe '#complete_order' do
|
231
|
+
before do
|
232
|
+
UnifiedPayment::Transaction.any_instance.unstub(:complete_order)
|
233
|
+
@card_transaction = UnifiedPayment::Transaction.new(:status => 'successful', :payment_transaction_id => '1234')
|
234
|
+
@card_transaction.stub(:order).and_return(order)
|
235
|
+
order.stub(:next!).and_return(true)
|
236
|
+
@payment = mock_model(Spree::Payment)
|
237
|
+
@payment.stub(:complete).and_return(true)
|
238
|
+
order.stub(:pending_payments).and_return([@payment])
|
239
|
+
end
|
240
|
+
|
241
|
+
it { order.should_receive(:next!).and_return(true) }
|
242
|
+
it { order.should_receive(:pending_payments).and_return([@payment]) }
|
243
|
+
it { @payment.should_receive(:complete).and_return(true) }
|
244
|
+
|
245
|
+
after do
|
246
|
+
@card_transaction.send(:complete_order)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
describe '#cancel_order' do
|
251
|
+
before do
|
252
|
+
UnifiedPayment::Transaction.any_instance.unstub(:cancel_order)
|
253
|
+
@card_transaction = UnifiedPayment::Transaction.new(:status => 'unsuccessful', :payment_transaction_id => '1234')
|
254
|
+
@card_transaction.stub(:order).and_return(order)
|
255
|
+
order.stub(:release_inventory).and_return(true)
|
256
|
+
@payment = mock_model(Spree::Payment)
|
257
|
+
@payment.stub(:update_attribute).with(:state, 'failed').and_return(true)
|
258
|
+
order.stub(:pending_payments).and_return([@payment])
|
259
|
+
end
|
260
|
+
|
261
|
+
context 'when order is completed' do
|
262
|
+
before do
|
263
|
+
order.stub(:completed?).and_return(true)
|
264
|
+
end
|
265
|
+
|
266
|
+
it { order.should_not_receive(:release_inventory) }
|
267
|
+
it { order.should_not_receive(:pending_payments) }
|
268
|
+
|
269
|
+
after do
|
270
|
+
@card_transaction.send(:cancel_order)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
context 'when order is not completed' do
|
275
|
+
before do
|
276
|
+
order.stub(:completed?).and_return(false)
|
277
|
+
end
|
278
|
+
|
279
|
+
context 'inventory released' do
|
280
|
+
before { @card_transaction.stub(:order_inventory_released?).and_return(true) }
|
281
|
+
it { order.should_not_receive(:release_inventory) }
|
282
|
+
end
|
283
|
+
|
284
|
+
context 'inventory not released' do
|
285
|
+
it { order.should_receive(:release_inventory).and_return(true) }
|
286
|
+
end
|
287
|
+
|
288
|
+
it { order.should_receive(:pending_payments).and_return([@payment]) }
|
289
|
+
it { @payment.should_receive(:update_attribute).with(:state, 'failed').and_return(true) }
|
290
|
+
|
291
|
+
after do
|
292
|
+
@card_transaction.send(:cancel_order)
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
describe '#release_order_inventory' do
|
298
|
+
before do
|
299
|
+
UnifiedPayment::Transaction.any_instance.unstub(:release_order_inventory)
|
300
|
+
@order = mock_model(Spree::Order)
|
301
|
+
@order.stub(:release_inventory).and_return(true)
|
302
|
+
@card_transaction = UnifiedPayment::Transaction.new(:status => 'somestatus')
|
303
|
+
@card_transaction.stub(:order).and_return(@order)
|
304
|
+
end
|
305
|
+
|
306
|
+
context 'when order is already completed' do
|
307
|
+
before { @order.stub(:completed?).and_return(true) }
|
308
|
+
it { @order.should_not_receive(:release_inventory) }
|
309
|
+
end
|
310
|
+
|
311
|
+
context 'when order is not completed' do
|
312
|
+
before { @order.stub(:completed?).and_return(false) }
|
313
|
+
it { @order.should_receive(:release_inventory) }
|
314
|
+
end
|
315
|
+
|
316
|
+
after do
|
317
|
+
@card_transaction.send(:release_order_inventory)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
describe 'abort!' do
|
322
|
+
before do
|
323
|
+
@time_now = Time.now
|
324
|
+
Time.stub(:now).and_return(@time_now)
|
325
|
+
@card_transaction = UnifiedPayment::Transaction.new(:status => 'somestatus', :payment_transaction_id => '1234')
|
326
|
+
@card_transaction.stub(:release_order_inventory).and_return(true)
|
327
|
+
end
|
328
|
+
|
329
|
+
it 'release inventory' do
|
330
|
+
@card_transaction.should_receive(:release_order_inventory).and_return(true)
|
331
|
+
@card_transaction.abort!
|
332
|
+
end
|
333
|
+
|
334
|
+
it 'assigns expired_at' do
|
335
|
+
@card_transaction.abort!
|
336
|
+
@card_transaction.reload.expired_at.should eq(@time_now.to_s)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
describe 'status checks for pening, unsuccessful and successful' do
|
341
|
+
before do
|
342
|
+
@successful_card_transaction = UnifiedPayment::Transaction.create!(:status => 'successful', :payment_transaction_id => '1234')
|
343
|
+
@pending_card_transaction = UnifiedPayment::Transaction.create!(:status => 'pending', :payment_transaction_id => '1234')
|
344
|
+
@unsuccessful_card_transaction = UnifiedPayment::Transaction.create!(:status => 'unsuccessful', :payment_transaction_id => '1234')
|
345
|
+
end
|
346
|
+
|
347
|
+
it { @successful_card_transaction.pending?.should be_false }
|
348
|
+
it { @successful_card_transaction.successful?.should be_true }
|
349
|
+
it { @successful_card_transaction.unsuccessful?.should be_false }
|
350
|
+
|
351
|
+
it { @unsuccessful_card_transaction.successful?.should be_false }
|
352
|
+
it { @unsuccessful_card_transaction.pending?.should be_false }
|
353
|
+
it { @unsuccessful_card_transaction.unsuccessful?.should be_true }
|
354
|
+
|
355
|
+
it { @pending_card_transaction.pending?.should be_true }
|
356
|
+
it { @pending_card_transaction.successful?.should be_false }
|
357
|
+
it { @pending_card_transaction.unsuccessful?.should be_false }
|
358
|
+
end
|
359
|
+
|
360
|
+
describe '#enqueue_expiration_task' do
|
361
|
+
before do
|
362
|
+
UnifiedPayment::Transaction.any_instance.unstub(:enqueue_expiration_task)
|
363
|
+
@last_id = UnifiedPayment::Transaction.last.try(:id) || 0
|
364
|
+
current_time = Time.current
|
365
|
+
Time.stub(:current).and_return(current_time)
|
366
|
+
@new_card_transaction = UnifiedPayment::Transaction.new(:status => 'pending')
|
367
|
+
@new_card_transaction.stub(:id).and_return(123)
|
368
|
+
end
|
369
|
+
|
370
|
+
context 'when transaction id is present' do
|
371
|
+
before { @new_card_transaction.payment_transaction_id = '1234' }
|
372
|
+
it 'enqueue delayed job' do
|
373
|
+
Delayed::Job.should_receive(:enqueue).with(TransactionExpiration.new(@new_card_transaction.id), { :run_at => TRANSACTION_LIFETIME.minutes.from_now }).and_return(true)
|
374
|
+
end
|
375
|
+
|
376
|
+
after do
|
377
|
+
@new_card_transaction.save!
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
context 'when transaction id is not present' do
|
382
|
+
it 'does not enqueue delayed job' do
|
383
|
+
Delayed::Job.should_not_receive(:enqueue).with(TransactionExpiration.new(@new_card_transaction.id), { :run_at => TRANSACTION_LIFETIME.minutes.from_now })
|
384
|
+
end
|
385
|
+
|
386
|
+
after do
|
387
|
+
@new_card_transaction.save!
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
describe '#wallet_transaction' do
|
393
|
+
before do
|
394
|
+
UnifiedPayment::Transaction.any_instance.unstub(:wallet_transaction)
|
395
|
+
@card_transaction = UnifiedPayment::Transaction.create!(:payment_transaction_id => '123454321', :amount => 200)
|
396
|
+
@card_transaction.stub(:user).and_return(user)
|
397
|
+
@card_transaction.stub(:order).and_return(order)
|
398
|
+
user.stub(:store_credits_total).and_return(100)
|
399
|
+
@store_credit_balance = user.store_credits_total + @card_transaction.amount.to_f
|
400
|
+
@store_credit = Object.new
|
401
|
+
@card_transaction.stub(:build_store_credit).with(:balance => @store_credit_balance, :user => user, :transactioner => user, :amount => @card_transaction.amount.to_f, :reason => "transferred from transaction:#{@card_transaction.payment_transaction_id}", :payment_mode => Spree::Credit::PAYMENT_MODE['Payment Refund'], :type => "Spree::Credit").and_return(@store_credit)
|
402
|
+
@store_credit.stub(:save!).and_return(true)
|
403
|
+
end
|
404
|
+
|
405
|
+
it { @card_transaction.should_receive(:build_store_credit).with(:balance => @store_credit_balance, :user => user, :transactioner => user, :amount => @card_transaction.amount.to_f, :reason => "transferred from transaction:#{@card_transaction.payment_transaction_id}", :payment_mode => Spree::Credit::PAYMENT_MODE['Payment Refund'], :type => "Spree::Credit").and_return(@store_credit) }
|
406
|
+
it { user.should_receive(:store_credits_total).and_return(100) }
|
407
|
+
it { @store_credit.should_receive(:save!).and_return(true) }
|
408
|
+
it { @card_transaction.should_not_receive(:associate_user) }
|
409
|
+
|
410
|
+
context 'when user is nil' do
|
411
|
+
before { user.stub(:nil?).and_return(true) }
|
412
|
+
|
413
|
+
it { @card_transaction.should_receive(:associate_user).and_return(true) }
|
414
|
+
end
|
415
|
+
|
416
|
+
after do
|
417
|
+
@card_transaction.wallet_transaction
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
describe '#associate_user' do
|
422
|
+
before do
|
423
|
+
@card_transaction = UnifiedPayment::Transaction.create!(:payment_transaction_id => '123454321')
|
424
|
+
@card_transaction.stub(:order).and_return(order)
|
425
|
+
order.stub(:email).and_return('test_user@baloo.com')
|
426
|
+
@card_transaction.stub(:save!).and_return(true)
|
427
|
+
@new_user = mock_model(Spree::User)
|
428
|
+
end
|
429
|
+
|
430
|
+
context 'when user with the order email exists' do
|
431
|
+
before do
|
432
|
+
Spree::User.stub(:where).with(:email => order.email).and_return([user])
|
433
|
+
@card_transaction.stub(:user=).with(user).and_return(true)
|
434
|
+
end
|
435
|
+
|
436
|
+
describe 'method calls' do
|
437
|
+
it { Spree::User.should_receive(:where).with(:email => order.email).and_return([user]) }
|
438
|
+
it { @card_transaction.should_receive(:user=).with(user).and_return(true) }
|
439
|
+
after do
|
440
|
+
@card_transaction.send(:associate_user)
|
441
|
+
end
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
context 'when user with the order email does not exist' do
|
446
|
+
before do
|
447
|
+
Spree::User.stub(:where).with(:email => order.email).and_return([])
|
448
|
+
Spree::User.stub(:create_unified_transaction_user).with(order.email).and_return(user)
|
449
|
+
end
|
450
|
+
|
451
|
+
describe 'method calls' do
|
452
|
+
it { Spree::User.should_receive(:where).with(:email => order.email).and_return([]) }
|
453
|
+
it { Spree::User.should_receive(:create_unified_transaction_user).with(order.email).and_return(user) }
|
454
|
+
|
455
|
+
after do
|
456
|
+
@card_transaction.send(:associate_user)
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
it 'associates a new user' do
|
461
|
+
@card_transaction.user.should be_nil
|
462
|
+
@card_transaction.send(:associate_user)
|
463
|
+
@card_transaction.user.should eq(user)
|
464
|
+
end
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
describe '#update_transaction_on_query' do
|
469
|
+
before { @card_transaction = UnifiedPayment::Transaction.create!(:payment_transaction_id => '123454321') }
|
470
|
+
|
471
|
+
context 'status is APPROVED' do
|
472
|
+
it { @card_transaction.should_receive(:assign_attributes).with(:gateway_order_status => 'APPROVED', :status => 'successful').and_return(true) }
|
473
|
+
it { @card_transaction.should_receive(:save).with(:validate => false).and_return(true) }
|
474
|
+
after { @card_transaction.update_transaction_on_query('APPROVED') }
|
475
|
+
end
|
476
|
+
|
477
|
+
context 'status is APPROVED' do
|
478
|
+
it { @card_transaction.should_receive(:assign_attributes).with(:gateway_order_status => 'MyStatus').and_return(true) }
|
479
|
+
it { @card_transaction.should_receive(:save).with(:validate => false).and_return(true) }
|
480
|
+
after { @card_transaction.update_transaction_on_query('MyStatus') }
|
481
|
+
end
|
482
|
+
end
|
483
|
+
end
|