solidus_six_saferpay 0.1.8 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -0
  3. data/README.md +3 -0
  4. data/app/assets/javascripts/solidus_six_saferpay/saferpay_payment.js +4 -1
  5. data/app/controllers/spree/solidus_six_saferpay/checkout_controller.rb +65 -41
  6. data/app/controllers/spree/solidus_six_saferpay/transaction/checkout_controller.rb +1 -1
  7. data/app/models/spree/payment_method/saferpay_payment_method.rb +1 -1
  8. data/app/models/spree/payment_method/saferpay_payment_page.rb +2 -2
  9. data/app/models/spree/payment_method/saferpay_transaction.rb +2 -2
  10. data/app/services/spree/solidus_six_saferpay/cancel_authorized_payment.rb +33 -0
  11. data/app/services/spree/solidus_six_saferpay/initialize_payment.rb +0 -1
  12. data/app/services/spree/solidus_six_saferpay/initialize_transaction.rb +0 -1
  13. data/app/services/spree/solidus_six_saferpay/inquire_payment.rb +27 -1
  14. data/app/services/spree/solidus_six_saferpay/order_not_found_handler.rb +28 -0
  15. data/app/services/spree/solidus_six_saferpay/payment_not_found_handler.rb +28 -0
  16. data/app/services/spree/solidus_six_saferpay/payment_processing_success_handler.rb +26 -0
  17. data/app/views/spree/checkout/payment/_saferpay_payment.html.erb +2 -2
  18. data/app/views/spree/solidus_six_saferpay/checkout/{iframe_breakout_redirect.html.erb → iframe_breakout_redirect.erb} +1 -1
  19. data/config/locales/de.yml +3 -0
  20. data/config/locales/en.yml +3 -0
  21. data/config/locales/fr.yml +3 -0
  22. data/config/routes.rb +6 -6
  23. data/lib/solidus_six_saferpay/configuration.rb +0 -2
  24. data/lib/solidus_six_saferpay/gateway.rb +11 -11
  25. data/lib/solidus_six_saferpay/payment_page_gateway.rb +8 -9
  26. data/lib/solidus_six_saferpay/transaction_gateway.rb +8 -9
  27. data/lib/solidus_six_saferpay/version.rb +1 -1
  28. data/solidus_six_saferpay.gemspec +2 -1
  29. data/spec/controllers/spree/solidus_six_saferpay/payment_page/checkout_controller_spec.rb +8 -197
  30. data/spec/controllers/spree/solidus_six_saferpay/transaction/checkout_controller_spec.rb +8 -220
  31. data/spec/services/spree/solidus_six_saferpay/assert_payment_page_spec.rb +2 -128
  32. data/spec/services/spree/solidus_six_saferpay/authorize_transaction_spec.rb +3 -127
  33. data/spec/services/spree/solidus_six_saferpay/cancel_authorized_payment_spec.rb +58 -0
  34. data/spec/services/spree/solidus_six_saferpay/initialize_payment_page_spec.rb +0 -2
  35. data/spec/services/spree/solidus_six_saferpay/initialize_transaction_spec.rb +0 -1
  36. data/spec/services/spree/solidus_six_saferpay/inquire_payment_page_payment_spec.rb +2 -98
  37. data/spec/services/spree/solidus_six_saferpay/inquire_transaction_payment_spec.rb +2 -98
  38. data/spec/services/spree/solidus_six_saferpay/order_not_found_handler_spec.rb +30 -0
  39. data/spec/services/spree/solidus_six_saferpay/payment_not_found_handler_spec.rb +30 -0
  40. data/spec/services/spree/solidus_six_saferpay/payment_processing_success_handler_spec.rb +34 -0
  41. data/spec/services/spree/solidus_six_saferpay/process_payment_page_payment_spec.rb +1 -206
  42. data/spec/services/spree/solidus_six_saferpay/process_transaction_payment_spec.rb +1 -200
  43. data/spec/solidus_six_saferpay/configuration_spec.rb +0 -3
  44. data/spec/solidus_six_saferpay/gateway_spec.rb +1 -14
  45. data/spec/solidus_six_saferpay/payment_page_gateway_spec.rb +16 -14
  46. data/spec/solidus_six_saferpay/transaction_gateway_spec.rb +17 -14
  47. data/spec/support/shared_examples/authorize_payment.rb +132 -0
  48. data/spec/support/shared_examples/checkout_controller.rb +294 -0
  49. data/spec/support/shared_examples/inquire_payment.rb +118 -0
  50. data/spec/support/shared_examples/process_authorized_payment.rb +202 -0
  51. metadata +41 -10
  52. data/app/services/spree/solidus_six_saferpay/inquire_transaction.rb +0 -7
  53. data/spec/controllers/spree/solidus_six_saferpay/checkout_controller_spec.rb +0 -41
@@ -13,206 +13,7 @@ module Spree
13
13
  end
14
14
 
15
15
  describe '#call' do
16
- context 'liability_shift check' do
17
-
18
- before do
19
- allow(subject).to receive(:gateway).and_return(double('gateway'))
20
-
21
-
22
- # ensure other methods don't modify outcome
23
- allow(subject).to receive(:validate_payment!)
24
- allow(subject).to receive(:cancel_old_solidus_payments)
25
- allow(payment).to receive(:create_solidus_payment!)
26
- end
27
-
28
- context 'when liability shift is required' do
29
- context 'and liability shift is not granted' do
30
-
31
- let(:payment) { create(:six_saferpay_payment, :authorized, :without_liability_shift) }
32
-
33
- it 'cancels the payment' do
34
- expect(payment.payment_method.preferred_require_liability_shift).to be true
35
- expect(payment.liability.liability_shift).to be false
36
-
37
- expect(subject.gateway).to receive(:void).with(payment.transaction_id)
38
-
39
- subject.call
40
- end
41
-
42
- it 'indicates failure' do
43
- expect(payment.payment_method.preferred_require_liability_shift).to be true
44
- expect(payment.liability.liability_shift).to be false
45
-
46
- expect(subject.gateway).to receive(:void).with(payment.transaction_id)
47
-
48
- subject.call
49
-
50
- expect(subject).not_to be_success
51
- end
52
-
53
- end
54
-
55
- context 'and liability shift is granted' do
56
- it "doesn't cancel the payment" do
57
- expect(payment.payment_method.preferred_require_liability_shift).to be true
58
- expect(payment.liability.liability_shift).to be true
59
-
60
- expect(subject.gateway).not_to receive(:void)
61
-
62
- subject.call
63
- end
64
-
65
- it 'passes the liability shift check' do
66
- expect(payment.payment_method.preferred_require_liability_shift).to be true
67
- expect(payment.liability.liability_shift).to be true
68
-
69
- subject.call
70
-
71
- expect(subject).to be_success
72
- end
73
- end
74
- end
75
-
76
- context 'when liability shift is not required' do
77
- let(:payment_method) { create(:saferpay_payment_method, :no_require_liability_shift) }
78
-
79
- context 'and liability shift is not granted' do
80
- let(:payment) { create(:six_saferpay_payment, :authorized, :without_liability_shift, payment_method: payment_method) }
81
-
82
- it "doesn't cancel the payment" do
83
- expect(payment.payment_method.preferred_require_liability_shift).to be false
84
- expect(payment.liability.liability_shift).to be false
85
-
86
- expect(subject.gateway).not_to receive(:void)
87
-
88
- subject.call
89
- end
90
-
91
- it 'passes the liability shift check' do
92
- expect(payment.payment_method.preferred_require_liability_shift).to be false
93
- expect(payment.liability.liability_shift).to be false
94
- subject.call
95
-
96
- expect(subject).to be_success
97
- end
98
- end
99
-
100
- context 'and liability shift is granted' do
101
- let(:payment) { create(:six_saferpay_payment, :authorized, payment_method: payment_method) }
102
- it "doesn't cancel the payment" do
103
- expect(payment.payment_method.preferred_require_liability_shift).to be false
104
- expect(payment.liability.liability_shift).to be true
105
-
106
- expect(subject.gateway).not_to receive(:void)
107
-
108
- subject.call
109
- end
110
-
111
- it 'passes the liability shift check' do
112
- expect(payment.payment_method.preferred_require_liability_shift).to be false
113
- expect(payment.liability.liability_shift).to be true
114
- subject.call
115
-
116
- expect(subject).to be_success
117
- end
118
- end
119
- end
120
- end
121
-
122
- context 'payment validation' do
123
- before do
124
- allow(subject).to receive(:gateway).and_return(double('gateway'))
125
-
126
-
127
- # ensure other methods don't modify outcome
128
- allow(subject).to receive(:check_liability_shift_requirements!)
129
- allow(subject).to receive(:cancel_old_solidus_payments)
130
- allow(payment).to receive(:create_solidus_payment!)
131
- end
132
-
133
- it 'validates the payment' do
134
- expect(PaymentValidator).to receive(:call).with(payment)
135
- subject.call
136
- end
137
-
138
- context 'when the payment is invalid' do
139
- it 'cancels the payment' do
140
- expect(subject.gateway).to receive(:void).with(payment.transaction_id)
141
-
142
- subject.call
143
- end
144
-
145
- it 'indicates failure' do
146
- allow(subject.gateway).to receive(:void).with(payment.transaction_id)
147
-
148
- subject.call
149
-
150
- expect(subject).not_to be_success
151
- end
152
- end
153
-
154
- context 'when the payment is valid' do
155
- before do
156
- allow(PaymentValidator).to receive(:call).with(payment).and_return(true)
157
- end
158
-
159
- it "doesn't cancel the payment" do
160
- expect(subject.gateway).not_to receive(:void)
161
-
162
- subject.call
163
- end
164
-
165
- it 'indicates success' do
166
- subject.call
167
-
168
- expect(subject).to be_success
169
- end
170
- end
171
- end
172
-
173
- context 'when the payment has passed all validations' do
174
- before do
175
- allow(subject).to receive(:check_liability_shift_requirements!).and_return(true)
176
- allow(subject).to receive(:validate_payment!).and_return(true)
177
- end
178
-
179
- context 'when previous solidus payments exist for this order' do
180
- let(:order) { payment.order }
181
- let!(:previous_payment_invalid) { create(:payment_using_saferpay, order: order) }
182
- let!(:previous_payment_checkout) { create(:payment_using_saferpay, order: order) }
183
-
184
- before do
185
- # This is bad practice because we mock which payments are invalidated here.
186
- # The reason is that you can't stub methods on AR objects that
187
- # are loaded from the DB and because #solidus_payments_to_cancel
188
- # is just AR scopes, I prefer this test over using stuff like
189
- # #expect_any_instance_of
190
- allow(subject).to receive(:solidus_payments_to_cancel).and_return([previous_payment_checkout])
191
- end
192
-
193
- it 'cancels old solidus payments' do
194
- expect(previous_payment_invalid).not_to receive(:cancel!)
195
- expect(previous_payment_checkout).to receive(:cancel!)
196
-
197
- subject.call
198
- end
199
- end
200
-
201
- it 'creates a new solidus payment' do
202
- expect(payment).to receive(:create_solidus_payment!)
203
-
204
- subject.call
205
- end
206
-
207
- it 'indicates success' do
208
- allow(payment).to receive(:create_solidus_payment!)
209
-
210
- subject.call
211
-
212
- expect(subject).to be_success
213
- end
214
- end
215
-
16
+ it_behaves_like 'process_authorized_payment'
216
17
  end
217
18
  end
218
19
  end
@@ -4,9 +4,6 @@ module SolidusSixSaferpay
4
4
  RSpec.describe Configuration do
5
5
 
6
6
  describe '.config' do
7
- it 'exposes a configurable payment success handler' do
8
- expect(described_class).to respond_to(:payment_processing_success_handler)
9
- end
10
7
  it 'exposes a configurable list of error handlers' do
11
8
  expect(described_class).to respond_to(:error_handlers)
12
9
  end
@@ -7,15 +7,11 @@ module SolidusSixSaferpay
7
7
  let(:terminal_id) { 'TERMINAL_ID' }
8
8
  let(:username) { 'USERNAME' }
9
9
  let(:password) { 'PASSWORD' }
10
- let(:success_url) { '/api/endpoints/success' }
11
- let(:fail_url) { '/api/endpoints/fail' }
12
10
  let(:base_url) { 'https://test.saferpay-api-host.test' }
13
11
  let(:css_url) { '/custom/css/url' }
14
12
 
15
13
  let(:gateway) do
16
14
  described_class.new(
17
- success_url: success_url,
18
- fail_url: fail_url,
19
15
  customer_id: customer_id,
20
16
  terminal_id: terminal_id,
21
17
  username: username,
@@ -29,8 +25,6 @@ module SolidusSixSaferpay
29
25
 
30
26
  it 'configures the API client' do
31
27
  gateway = described_class.new(
32
- success_url: success_url,
33
- fail_url: fail_url,
34
28
  customer_id: customer_id,
35
29
  terminal_id: terminal_id,
36
30
  username: username,
@@ -45,8 +39,6 @@ module SolidusSixSaferpay
45
39
  expect(config.terminal_id).to eq(terminal_id)
46
40
  expect(config.username).to eq(username)
47
41
  expect(config.password).to eq(password)
48
- expect(config.success_url).to eq(success_url)
49
- expect(config.fail_url).to eq(fail_url)
50
42
  expect(config.base_url).to eq(base_url)
51
43
  expect(config.css_url).to eq(css_url)
52
44
  end
@@ -63,10 +55,7 @@ module SolidusSixSaferpay
63
55
  end
64
56
 
65
57
  it 'falls back to ENV vars' do
66
- gateway = described_class.new(
67
- success_url: success_url,
68
- fail_url: fail_url
69
- )
58
+ gateway = described_class.new
70
59
 
71
60
  config = SixSaferpay.config
72
61
 
@@ -74,8 +63,6 @@ module SolidusSixSaferpay
74
63
  expect(config.terminal_id).to eq(terminal_id)
75
64
  expect(config.username).to eq(username)
76
65
  expect(config.password).to eq(password)
77
- expect(config.success_url).to eq(success_url)
78
- expect(config.fail_url).to eq(fail_url)
79
66
  expect(config.base_url).to eq(base_url)
80
67
  expect(config.css_url).to eq(css_url)
81
68
  end
@@ -8,8 +8,6 @@ module SolidusSixSaferpay
8
8
  let(:terminal_id) { 'TERMINAL_ID' }
9
9
  let(:username) { 'USERNAME' }
10
10
  let(:password) { 'PASSWORD' }
11
- let(:success_url) { '/api/endpoints/success' }
12
- let(:fail_url) { '/api/endpoints/fail' }
13
11
  let(:base_url) { 'https://test.saferpay-api-host.test' }
14
12
  let(:css_url) { '/custom/css/url' }
15
13
 
@@ -38,17 +36,6 @@ module SolidusSixSaferpay
38
36
  allow(ENV).to receive(:fetch).with('SIX_SAFERPAY_CSS_URL').and_return(css_url)
39
37
  end
40
38
 
41
- describe '#initialize' do
42
- it 'configures the API client with correct urls by default' do
43
- described_class.new
44
-
45
- config = SixSaferpay.config
46
-
47
- expect(config.success_url).to eq(solidus_six_saferpay_payment_page_success_url)
48
- expect(config.fail_url).to eq(solidus_six_saferpay_payment_page_fail_url)
49
- end
50
- end
51
-
52
39
  describe '#initialize_payment' do
53
40
 
54
41
  let(:saferpay_billing_address) do
@@ -111,11 +98,19 @@ module SolidusSixSaferpay
111
98
  delivery_address: saferpay_shipping_address
112
99
  )
113
100
  end
101
+ let(:return_urls) do
102
+ instance_double("SixSaferpay::ReturnUrls",
103
+ success: solidus_six_saferpay_payment_page_success_url(order),
104
+ fd_fail: solidus_six_saferpay_payment_page_fail_url(order),
105
+ fd_abort: solidus_six_saferpay_payment_page_fail_url(order),
106
+ )
107
+ end
114
108
 
115
109
  let(:initialize_params) do
116
110
  {
117
111
  payment: saferpay_payment,
118
- payer: saferpay_payer
112
+ payer: saferpay_payer,
113
+ return_urls: return_urls
119
114
  }
120
115
  end
121
116
 
@@ -185,6 +180,13 @@ module SolidusSixSaferpay
185
180
  delivery_address: saferpay_shipping_address
186
181
  ).and_return(saferpay_payer)
187
182
 
183
+ # mock return_urls
184
+ expect(SixSaferpay::ReturnUrls).to receive(:new).with(
185
+ success: solidus_six_saferpay_payment_page_success_url(order),
186
+ fd_fail: solidus_six_saferpay_payment_page_fail_url(order),
187
+ fd_abort: solidus_six_saferpay_payment_page_fail_url(order),
188
+ ).and_return(return_urls)
189
+
188
190
  expect(SixSaferpay::SixPaymentPage::Initialize).to receive(:new).with(initialize_params).and_return(saferpay_initialize)
189
191
 
190
192
  expect(SixSaferpay::Client).to receive(:post).with(saferpay_initialize).and_return(api_initialize_response)
@@ -8,8 +8,6 @@ module SolidusSixSaferpay
8
8
  let(:terminal_id) { 'TERMINAL_ID' }
9
9
  let(:username) { 'USERNAME' }
10
10
  let(:password) { 'PASSWORD' }
11
- let(:success_url) { '/api/endpoints/success' }
12
- let(:fail_url) { '/api/endpoints/fail' }
13
11
  let(:base_url) { 'https://test.saferpay-api-host.test' }
14
12
  let(:css_url) { '/custom/css/url' }
15
13
 
@@ -38,17 +36,6 @@ module SolidusSixSaferpay
38
36
  allow(ENV).to receive(:fetch).with('SIX_SAFERPAY_CSS_URL').and_return(css_url)
39
37
  end
40
38
 
41
- describe '#initialize' do
42
- it 'configures the API client with correct urls by default' do
43
- described_class.new
44
-
45
- config = SixSaferpay.config
46
-
47
- expect(config.success_url).to eq(solidus_six_saferpay_transaction_success_url)
48
- expect(config.fail_url).to eq(solidus_six_saferpay_transaction_fail_url)
49
- end
50
- end
51
-
52
39
  describe '#initialize_payment' do
53
40
 
54
41
  let(:saferpay_billing_address) do
@@ -112,10 +99,19 @@ module SolidusSixSaferpay
112
99
  )
113
100
  end
114
101
 
102
+ let(:return_urls) do
103
+ instance_double("SixSaferpay::ReturnUrls",
104
+ success: solidus_six_saferpay_transaction_success_url(order),
105
+ fd_fail: solidus_six_saferpay_transaction_fail_url(order),
106
+ fd_abort: solidus_six_saferpay_transaction_fail_url(order),
107
+ )
108
+ end
109
+
115
110
  let(:initialize_params) do
116
111
  {
117
112
  payment: saferpay_payment,
118
- payer: saferpay_payer
113
+ payer: saferpay_payer,
114
+ return_urls: return_urls
119
115
  }
120
116
  end
121
117
 
@@ -189,6 +185,13 @@ module SolidusSixSaferpay
189
185
  delivery_address: saferpay_shipping_address
190
186
  ).and_return(saferpay_payer)
191
187
 
188
+ # mock return_urls
189
+ expect(SixSaferpay::ReturnUrls).to receive(:new).with(
190
+ success: solidus_six_saferpay_transaction_success_url(order),
191
+ fd_fail: solidus_six_saferpay_transaction_fail_url(order),
192
+ fd_abort: solidus_six_saferpay_transaction_fail_url(order),
193
+ ).and_return(return_urls)
194
+
192
195
  expect(SixSaferpay::SixTransaction::Initialize).to receive(:new).with(initialize_params).and_return(saferpay_initialize)
193
196
 
194
197
  expect(SixSaferpay::Client).to receive(:post).with(saferpay_initialize).and_return(api_initialize_response)
@@ -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