solidus_six_saferpay 0.2.0 → 0.3.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 (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