solidus_six_saferpay 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/solidus_six_saferpay/saferpay_payment.js +4 -1
  3. data/app/controllers/spree/solidus_six_saferpay/checkout_controller.rb +93 -16
  4. data/app/controllers/spree/solidus_six_saferpay/transaction/checkout_controller.rb +1 -1
  5. data/app/models/spree/payment_method/saferpay_payment_method.rb +1 -1
  6. data/app/models/spree/payment_method/saferpay_payment_page.rb +2 -2
  7. data/app/models/spree/payment_method/saferpay_transaction.rb +2 -2
  8. data/app/services/spree/solidus_six_saferpay/cancel_authorized_payment.rb +33 -0
  9. data/app/services/spree/solidus_six_saferpay/initialize_payment.rb +0 -1
  10. data/app/services/spree/solidus_six_saferpay/initialize_transaction.rb +0 -1
  11. data/app/services/spree/solidus_six_saferpay/inquire_payment.rb +26 -1
  12. data/app/views/spree/checkout/payment/_saferpay_payment.html.erb +2 -2
  13. data/app/views/spree/solidus_six_saferpay/checkout/{iframe_breakout_redirect.html.erb → iframe_breakout_redirect.erb} +1 -1
  14. data/config/locales/de.yml +3 -0
  15. data/config/locales/en.yml +3 -0
  16. data/config/locales/fr.yml +3 -0
  17. data/config/routes.rb +2 -2
  18. data/lib/solidus_six_saferpay/configuration.rb +2 -0
  19. data/lib/solidus_six_saferpay/gateway.rb +6 -7
  20. data/lib/solidus_six_saferpay/version.rb +1 -1
  21. data/spec/controllers/spree/solidus_six_saferpay/payment_page/checkout_controller_spec.rb +8 -198
  22. data/spec/controllers/spree/solidus_six_saferpay/transaction/checkout_controller_spec.rb +8 -221
  23. data/spec/services/spree/solidus_six_saferpay/assert_payment_page_spec.rb +2 -128
  24. data/spec/services/spree/solidus_six_saferpay/authorize_transaction_spec.rb +3 -127
  25. data/spec/services/spree/solidus_six_saferpay/cancel_authorized_payment_spec.rb +58 -0
  26. data/spec/services/spree/solidus_six_saferpay/initialize_payment_page_spec.rb +0 -2
  27. data/spec/services/spree/solidus_six_saferpay/initialize_transaction_spec.rb +0 -1
  28. data/spec/services/spree/solidus_six_saferpay/inquire_payment_page_payment_spec.rb +2 -98
  29. data/spec/services/spree/solidus_six_saferpay/inquire_transaction_payment_spec.rb +2 -98
  30. data/spec/services/spree/solidus_six_saferpay/process_payment_page_payment_spec.rb +1 -206
  31. data/spec/services/spree/solidus_six_saferpay/process_transaction_payment_spec.rb +1 -200
  32. data/spec/support/shared_examples/authorize_payment.rb +132 -0
  33. data/spec/support/shared_examples/checkout_controller.rb +342 -0
  34. data/spec/support/shared_examples/inquire_payment.rb +118 -0
  35. data/spec/support/shared_examples/process_authorized_payment.rb +202 -0
  36. metadata +14 -6
  37. data/app/services/spree/solidus_six_saferpay/inquire_transaction.rb +0 -7
  38. data/spec/controllers/spree/solidus_six_saferpay/checkout_controller_spec.rb +0 -41
@@ -0,0 +1,132 @@
1
+ RSpec.shared_examples 'authorize_payment' do
2
+
3
+ let(:transaction_status) { "AUTHORIZED" }
4
+ let(:transaction_id) { "723n4MAjMdhjSAhAKEUdA8jtl9jb" }
5
+ let(:transaction_date) { "2015-01-30T12:45:22.258+01:00" }
6
+ let(:amount_value) { "100" }
7
+ let(:amount_currency) { "USD" }
8
+ let(:brand_name) { 'PaymentBrand' }
9
+ let(:display_text) { "xxxx xxxx xxxx 1234" }
10
+ let(:six_transaction_reference) { "0:0:3:723n4MAjMdhjSAhAKEUdA8jtl9jb" }
11
+
12
+ let(:payment_means) do
13
+ SixSaferpay::ResponsePaymentMeans.new(
14
+ brand: SixSaferpay::Brand.new(name: brand_name),
15
+ display_text: display_text
16
+ )
17
+ end
18
+
19
+ # https://saferpay.github.io/jsonapi/#Payment_v1_PaymentPage_Assert
20
+ # https://saferpay.github.io/jsonapi/#Payment_v1_Transaction_Authorize
21
+ let(:api_response) do
22
+ api_response_class.new(
23
+ response_header: SixSaferpay::ResponseHeader.new(request_id: 'test', spec_version: 'test'),
24
+ transaction: SixSaferpay::Transaction.new(
25
+ type: "PAYMENT",
26
+ status: transaction_status,
27
+ id: transaction_id,
28
+ date: transaction_date,
29
+ amount: SixSaferpay::Amount.new(value: amount_value, currency_code: amount_currency),
30
+ six_transaction_reference: six_transaction_reference,
31
+ ),
32
+ payment_means: payment_means
33
+ )
34
+ end
35
+
36
+ let(:gateway_response) do
37
+ ::SolidusSixSaferpay::GatewayResponse.new(
38
+ gateway_success,
39
+ "initialize success: #{gateway_success}",
40
+ api_response
41
+ )
42
+ end
43
+
44
+ # stub gateway to return our mock response
45
+ before do
46
+ allow(subject).to receive(:gateway).
47
+ and_return(double('gateway', authorize: gateway_response))
48
+ end
49
+
50
+ context 'when not successful' do
51
+ let(:gateway_success) { false }
52
+
53
+ it 'indicates failure' do
54
+ subject.call
55
+
56
+ expect(subject).not_to be_success
57
+ end
58
+
59
+ it 'does not update the payment attributes' do
60
+ expect { subject.call }.not_to change { payment.transaction_id }
61
+ expect { subject.call }.not_to change { payment.transaction_status }
62
+ expect { subject.call }.not_to change { payment.transaction_date }
63
+ expect { subject.call }.not_to change { payment.six_transaction_reference }
64
+ expect { subject.call }.not_to change { payment.display_text }
65
+ expect { subject.call }.not_to change { payment.response_hash }
66
+ end
67
+ end
68
+
69
+ context 'when successful' do
70
+ let(:gateway_success) { true }
71
+
72
+ it 'updates the transaction_id' do
73
+ expect { subject.call }.to change { payment.transaction_id }.from(nil).to(transaction_id)
74
+ end
75
+
76
+ it 'updates the transaction status' do
77
+ expect { subject.call }.to change { payment.transaction_status }.from(nil).to(transaction_status)
78
+ end
79
+
80
+ it 'updates the transaction date' do
81
+ expect { subject.call }.to change { payment.transaction_date }.from(nil).to(DateTime.parse(transaction_date))
82
+ end
83
+
84
+ it 'updates the six_transaction_reference' do
85
+ expect { subject.call }.to change { payment.six_transaction_reference }.from(nil).to(six_transaction_reference)
86
+ end
87
+
88
+ it 'updates the display_text' do
89
+ expect { subject.call }.to change { payment.display_text }.from(nil).to(display_text)
90
+ end
91
+
92
+ it 'updates the response hash' do
93
+ expect { subject.call }.to change { payment.response_hash }.from(payment.response_hash).to(api_response.to_h)
94
+ end
95
+
96
+ context 'when the payment was made with a card' do
97
+ let(:masked_number) { "xxxx xxxx xxxx 5555" }
98
+ let(:exp_year) { "19" }
99
+ let(:exp_month) { "5" }
100
+ let(:payment_means) do
101
+ SixSaferpay::ResponsePaymentMeans.new(
102
+ brand: SixSaferpay::Brand.new(name: brand_name),
103
+ display_text: display_text,
104
+ card: SixSaferpay::ResponseCard.new(
105
+ masked_number: masked_number,
106
+ exp_year: exp_year,
107
+ exp_month: exp_month
108
+ )
109
+ )
110
+ end
111
+
112
+ it 'updates the masked number' do
113
+ expect { subject.call }.to change { payment.masked_number }.from(nil).to(masked_number)
114
+ end
115
+
116
+ it 'updates the expiry year' do
117
+ expect { subject.call }.to change { payment.expiration_year }.from(nil).to(exp_year)
118
+ end
119
+
120
+ it 'updates the expiry month' do
121
+ expect { subject.call }.to change { payment.expiration_month }.from(nil).to(exp_month)
122
+ end
123
+ end
124
+
125
+ it 'indicates success' do
126
+ subject.call
127
+
128
+ expect(subject).to be_success
129
+ end
130
+ end
131
+
132
+ end
@@ -0,0 +1,342 @@
1
+ RSpec.shared_examples 'checkout_controller' do
2
+
3
+ routes { Spree::Core::Engine.routes }
4
+
5
+ subject { described_class.new }
6
+
7
+ let(:user) { create(:user) }
8
+ let(:order) { create(:order) }
9
+ let(:order_number) { order.number }
10
+ let(:payment_method) { create(:saferpay_payment_method_payment_page) }
11
+
12
+ before do
13
+ allow(controller).to receive_messages try_spree_current_user: user
14
+ allow(controller).to receive_messages current_order: order
15
+ allow(Spree::Order).to receive(:find_by).with(number: order_number).and_return(order)
16
+ end
17
+
18
+ describe 'GET init' do
19
+ let(:success) { false }
20
+ let(:redirect_url) { '/saferpay/redirect/url' }
21
+ let(:initialized_payment) { instance_double("Spree::SolidusSixSaferpay::InitializePayment", success?: success, redirect_url: redirect_url) }
22
+
23
+ context 'when the current order for this user is not the one for which payment is initialized' do
24
+ it 'returns an error and redirects to the cart page' do
25
+ allow(Spree::Order).to receive(:find_by).with(number: order_number).and_return(nil)
26
+
27
+ get :init, params: { order_number: order_number, payment_method_id: payment_method.id }
28
+
29
+ body = JSON.parse(response.body)
30
+ expect(body['errors']).to match(/modified/)
31
+ expect(body['redirect_url']).to eq('/cart')
32
+ expect(response.status).to eq(422)
33
+ end
34
+ end
35
+
36
+ it 'tries to initialize the saferpay payment' do
37
+ expect(initialize_payment_service_class).to receive(:call).with(order, payment_method).and_return(initialized_payment)
38
+
39
+ get :init, params: { order_number: order_number, payment_method_id: payment_method.id }
40
+ end
41
+
42
+
43
+ context 'when payment initialize succeeds' do
44
+ let(:success) { true }
45
+
46
+ before do
47
+ allow(initialize_payment_service_class).to receive(:call).with(order, payment_method).and_return(initialized_payment)
48
+ end
49
+
50
+ it 'returns the redirect_url' do
51
+ get :init, params: { order_number: order_number, payment_method_id: payment_method.id }
52
+
53
+ body = JSON.parse(response.body)
54
+ expect(body["redirect_url"]).to eq(redirect_url)
55
+ end
56
+ end
57
+
58
+ context 'when payment initialize fails' do
59
+ let(:success) { false }
60
+
61
+ before do
62
+ allow(initialize_payment_service_class).to receive(:call).with(order, payment_method).and_return(initialized_payment)
63
+ end
64
+
65
+
66
+ it 'returns an error and redirects to the cart page' do
67
+ get :init, params: { order_number: order_number, payment_method_id: payment_method.id }
68
+
69
+ body = JSON.parse(response.body)
70
+ expect(body['errors']).to match(/could not be initialized/)
71
+ expect(body['redirect_url']).to eq('/cart')
72
+ expect(response.status).to eq(422)
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+
79
+ describe 'GET success' do
80
+ context 'when the order is not found' do
81
+ let(:order) { nil }
82
+ let(:order_number) { "not_found" }
83
+
84
+ context 'when a custom error handler exists' do
85
+ let(:handler) { double("handler") }
86
+ let(:error_handler) { Proc.new {|context, order_number| handler.exec(order_number) } }
87
+
88
+ before do
89
+ allow(::SolidusSixSaferpay.config).to receive(:payment_processing_order_not_found_handler).and_return(error_handler)
90
+ end
91
+
92
+ it 'calls the custom handler' do
93
+ expect(SolidusSixSaferpay.config.payment_processing_order_not_found_handler).to eq(error_handler)
94
+ expect(handler).to receive(:exec).with(order_number)
95
+
96
+ get :success, params: { order_number: order_number }
97
+ end
98
+ end
99
+
100
+ it 'redirects to the cart page via iframe breakout' do
101
+ get :success, params: { order_number: order_number }
102
+ expect(assigns(:redirect_path)).to eq(routes.cart_path)
103
+ expect(response).to render_template :iframe_breakout_redirect
104
+ end
105
+ end
106
+
107
+ context 'when payment could not be created' do
108
+ # We are not creating a payment so there is none to be found in the
109
+ # controller action
110
+ let!(:payment) { nil }
111
+
112
+ context 'when a custom error handler exists' do
113
+ let(:handler) { double("handler") }
114
+ let(:error_handler) { Proc.new {|context, order_number| handler.exec(order_number) } }
115
+
116
+ before do
117
+ allow(::SolidusSixSaferpay.config).to receive(:payment_processing_payment_not_found_handler).and_return(error_handler)
118
+ end
119
+
120
+ it 'calls the custom success processing handler' do
121
+ expect(SolidusSixSaferpay.config.payment_processing_payment_not_found_handler).to eq(error_handler)
122
+ expect(handler).to receive(:exec).with(order)
123
+
124
+ get :success, params: { order_number: order_number }
125
+ end
126
+ end
127
+
128
+ it 'redirects to the cart page via iframe breakout' do
129
+ get :success, params: { order_number: order_number }
130
+ expect(assigns(:redirect_path)).to eq(routes.cart_path)
131
+ expect(response).to render_template :iframe_breakout_redirect
132
+ end
133
+ end
134
+
135
+
136
+ context 'when the order is already completed' do
137
+ let(:order) { create(:order_ready_to_ship) }
138
+
139
+ it 'redirects to the cart page via iframe breakout' do
140
+ get :success, params: { order_number: order_number }
141
+ expect(assigns(:redirect_path)).to eq(routes.cart_path)
142
+ expect(response).to render_template :iframe_breakout_redirect
143
+ end
144
+ end
145
+
146
+ context 'when payment create was successful' do
147
+ let!(:payment) { create(:six_saferpay_payment, order: order) }
148
+ let(:assert_success) { false }
149
+ let(:payment_assert) { instance_double("Spree::SolidusSixSaferpay::AssertPaymentPage", success?: assert_success) }
150
+ let(:payment_inquiry) { instance_double("Spree::SolidusSixSaferpay::InquirePaymentPagePayment", user_message: "payment inquiry message") }
151
+
152
+ it 'asserts the payment' do
153
+ expect(authorize_payment_service_class).to receive(:call).with(payment).and_return(payment_assert)
154
+ expect(inquire_payment_service_class).to receive(:call).with(payment).and_return(payment_inquiry)
155
+
156
+ get :success, params: { order_number: order_number }
157
+ end
158
+
159
+ context 'when the payment assert is successful' do
160
+ let(:assert_success) { true }
161
+ let(:process_success) { false }
162
+ let(:processed_payment) { instance_double("Spree::SolidusSixSaferpay::ProcessPaymentPagePayment", success?: process_success, user_message: "payment processing message") }
163
+
164
+ before do
165
+ allow(authorize_payment_service_class).to receive(:call).with(payment).and_return(payment_assert)
166
+ end
167
+
168
+ it 'processes the asserted payment' do
169
+ expect(process_authorization_service_class).to receive(:call).with(payment).and_return(processed_payment)
170
+
171
+ get :success, params: { order_number: order_number }
172
+ end
173
+
174
+ context 'when the processing is successful' do
175
+ let(:process_success) { true }
176
+
177
+ before do
178
+ allow(process_authorization_service_class).to receive(:call).with(payment).and_return(processed_payment)
179
+ end
180
+
181
+ context 'when a custom success processing handler exists' do
182
+ let(:success_handler) { Proc.new {|context, order| order.touch } }
183
+
184
+ before do
185
+ allow(::SolidusSixSaferpay.config).to receive(:payment_processing_success_handler).and_return(success_handler)
186
+ end
187
+
188
+ it 'calls the custom success processing handler' do
189
+ expect(SolidusSixSaferpay.config.payment_processing_success_handler).to eq(success_handler)
190
+ expect(order).to receive(:touch)
191
+
192
+ get :success, params: { order_number: order_number }
193
+ end
194
+ end
195
+
196
+
197
+ context 'when order is in payment state' do
198
+ let(:order) { create(:order, state: :payment) }
199
+
200
+ it 'moves order to next state' do
201
+ expect(order).to receive(:next!)
202
+
203
+ get :success, params: { order_number: order_number }
204
+ end
205
+ end
206
+
207
+ context 'when order is already in complete state' do
208
+ let(:order) { create(:order, state: :complete) }
209
+
210
+ it 'does not modify the order state' do
211
+ expect(order).not_to receive(:next!)
212
+
213
+ get :success, params: { order_number: order_number }
214
+ end
215
+ end
216
+ end
217
+
218
+ context 'when the processing fails' do
219
+ let(:process_success) { false }
220
+
221
+ before do
222
+ allow(process_authorization_service_class).to receive(:call).with(payment).and_return(processed_payment)
223
+ end
224
+
225
+ it 'displays an error message' do
226
+ get :success, params: { order_number: order_number }
227
+
228
+ expect(flash[:error]).to eq("payment processing message")
229
+ end
230
+ end
231
+
232
+ end
233
+
234
+ context 'when the payment assert fails' do
235
+ let(:assert_success) { false }
236
+
237
+ before do
238
+ allow(authorize_payment_service_class).to receive(:call).with(payment).and_return(payment_assert)
239
+ end
240
+
241
+ it 'inquires the payment' do
242
+ expect(inquire_payment_service_class).to receive(:call).with(payment).and_return(payment_inquiry)
243
+
244
+ get :success, params: { order_number: order_number }
245
+ end
246
+
247
+ it 'displays an error message' do
248
+ expect(inquire_payment_service_class).to receive(:call).with(payment).and_return(payment_inquiry)
249
+ get :success, params: { order_number: order_number }
250
+
251
+ expect(flash[:error]).to eq("payment inquiry message")
252
+ end
253
+ end
254
+ end
255
+ end
256
+
257
+ describe 'GET fail' do
258
+
259
+ context 'when the order is not found' do
260
+ let(:order) { nil }
261
+ let(:order_number) { "not_found" }
262
+
263
+ context 'when a custom error handler exists' do
264
+ let(:handler) { double("handler") }
265
+ let(:error_handler) { Proc.new {|context, order_number| handler.exec(order_number) } }
266
+
267
+ before do
268
+ allow(::SolidusSixSaferpay.config).to receive(:payment_processing_order_not_found_handler).and_return(error_handler)
269
+ end
270
+
271
+ it 'calls the custom processing handler' do
272
+ expect(SolidusSixSaferpay.config.payment_processing_order_not_found_handler).to eq(error_handler)
273
+ expect(handler).to receive(:exec).with(order_number)
274
+
275
+ get :fail, params: { order_number: order_number }
276
+ end
277
+ end
278
+
279
+ it 'redirects to the cart page via iframe breakout' do
280
+ get :fail, params: { order_number: order_number }
281
+ expect(assigns(:redirect_path)).to eq(routes.cart_path)
282
+ expect(response).to render_template :iframe_breakout_redirect
283
+ end
284
+ end
285
+
286
+ context 'when payment could not be created' do
287
+ # We are not creating a payment so there is none to be found in the
288
+ # controller action
289
+ let!(:payment) { nil }
290
+
291
+ context 'when a custom error handler exists' do
292
+ let(:handler) { double("handler") }
293
+ let(:error_handler) { Proc.new {|context, order_number| handler.exec(order_number) } }
294
+
295
+ before do
296
+ allow(::SolidusSixSaferpay.config).to receive(:payment_processing_payment_not_found_handler).and_return(error_handler)
297
+ end
298
+
299
+ it 'calls the custom success processing handler' do
300
+ expect(SolidusSixSaferpay.config.payment_processing_payment_not_found_handler).to eq(error_handler)
301
+ expect(handler).to receive(:exec).with(order)
302
+
303
+ get :fail, params: { order_number: order_number }
304
+ end
305
+ end
306
+
307
+ it 'redirects to the cart page via iframe breakout' do
308
+ get :fail, params: { order_number: order_number }
309
+ expect(assigns(:redirect_path)).to eq(routes.cart_path)
310
+ expect(response).to render_template :iframe_breakout_redirect
311
+ end
312
+ end
313
+
314
+ context 'when payment create was successful' do
315
+ let!(:payment) { create(:six_saferpay_payment, order: order) }
316
+ let(:payment_inquiry) { instance_double("Spree::SolidusSixSaferpay::InquirePaymentPagePayment", user_message: "payment inquiry message") }
317
+
318
+ it 'inquires the payment' do
319
+ expect(inquire_payment_service_class).to receive(:call).with(payment).and_return(payment_inquiry)
320
+
321
+ get :fail, params: { order_number: order_number }
322
+ end
323
+
324
+ it 'displays an error message' do
325
+ expect(inquire_payment_service_class).to receive(:call).with(payment).and_return(payment_inquiry)
326
+
327
+ get :fail, params: { order_number: order_number }
328
+
329
+ expect(flash[:error]).to eq("payment inquiry message")
330
+ end
331
+
332
+ it 'redirects to the cart page via iframe breakout' do
333
+ expect(inquire_payment_service_class).to receive(:call).with(payment).and_return(payment_inquiry)
334
+
335
+ get :fail, params: { order_number: order_number }
336
+
337
+ expect(assigns(:redirect_path)).to eq(routes.checkout_state_path(:payment))
338
+ expect(response).to render_template :iframe_breakout_redirect
339
+ end
340
+ end
341
+ end
342
+ end