spree_unified_payment 1.0.0

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.
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