spree_unified_payment 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/.gitignore +9 -0
  2. data/.travis.yml +6 -0
  3. data/Gemfile +20 -0
  4. data/LICENSE +26 -0
  5. data/README.md +61 -0
  6. data/Versionfile +12 -0
  7. data/app/assets/javascripts/admin/spree_unified_payment.js +39 -0
  8. data/app/assets/stylesheets/store/spree_unified_payment.css +7 -0
  9. data/app/controllers/application_controller.rb +2 -0
  10. data/app/controllers/spree/admin/unified_payments_controller.rb +34 -0
  11. data/app/controllers/spree/checkout_controller_decorator.rb +19 -0
  12. data/app/controllers/spree/unified_payments_controller.rb +151 -0
  13. data/app/helpers/transaction_notification_mail_helper.rb +12 -0
  14. data/app/helpers/unified_transaction_helper.rb +18 -0
  15. data/app/mailers/spree/transaction_notification_mailer.rb +16 -0
  16. data/app/models/spree/order_decorator.rb +67 -0
  17. data/app/models/spree/payment_method/unified_payment_method.rb +34 -0
  18. data/app/models/spree/store_credit_decorator.rb +4 -0
  19. data/app/models/spree/user_decorator.rb +7 -0
  20. data/app/models/unified_payment/transaction_decorator.rb +102 -0
  21. data/app/overrides/add_unified_tabs_to_admin_menu.rb +6 -0
  22. data/app/views/spree/admin/unified_payments/index.html.erb +98 -0
  23. data/app/views/spree/admin/unified_payments/query_gateway.js.erb +10 -0
  24. data/app/views/spree/admin/unified_payments/receipt.html.erb +20 -0
  25. data/app/views/spree/checkout/payment/_unifiedpaymentmethod.html.erb +24 -0
  26. data/app/views/spree/transaction_notification_mailer/send_mail.html.erb +20 -0
  27. data/app/views/spree/unified_payments/approved.html.erb +25 -0
  28. data/app/views/spree/unified_payments/canceled.html.erb +1 -0
  29. data/app/views/spree/unified_payments/create.html.erb +8 -0
  30. data/app/views/spree/unified_payments/create.js.erb +3 -0
  31. data/app/views/spree/unified_payments/declined.html.erb +4 -0
  32. data/app/views/spree/unified_payments/index.html.erb +28 -0
  33. data/app/views/spree/unified_payments/new.html.erb +35 -0
  34. data/app/views/spree/unified_payments/new.js.erb +3 -0
  35. data/config/initializers/constants.rb +8 -0
  36. data/config/routes.rb +15 -0
  37. data/db/migrate/20140120075553_add_transaction_fields_to_unified_payment_transactions.rb +14 -0
  38. data/db/migrate/20140120081453_add_unified_transaction_id_to_spree_store_credits.rb +6 -0
  39. data/lib/generators/spree_unified_payment/install/install_generator.rb +27 -0
  40. data/lib/spree_unified_payment/engine.rb +26 -0
  41. data/lib/spree_unified_payment.rb +2 -0
  42. data/lib/transaction_expiration.rb +9 -0
  43. data/spec/constants_spec.rb +11 -0
  44. data/spec/controllers/spree/admin/unified_payments_controller_spec.rb +155 -0
  45. data/spec/controllers/spree/checkout_controller_decorator_spec.rb +114 -0
  46. data/spec/controllers/spree/unified_payments_controller_spec.rb +509 -0
  47. data/spec/mailers/transaction_notification_mailer_spec.rb +48 -0
  48. data/spec/models/spree/order_decorator_spec.rb +206 -0
  49. data/spec/models/spree/payment_method/unified_payment_spec.rb +25 -0
  50. data/spec/models/spree/store_credit_decorator_spec.rb +11 -0
  51. data/spec/models/spree/user_decorator_spec.rb +12 -0
  52. data/spec/models/unified_payment/transaction_decorator_spec.rb +483 -0
  53. data/spec/spec_helper.rb +66 -0
  54. data/spree_unified_payment.gemspec +23 -0
  55. 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