gocardless_pro 2.19.0 → 2.23.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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +25 -43
  3. data/Gemfile +7 -0
  4. data/gocardless_pro.gemspec +2 -2
  5. data/lib/gocardless_pro.rb +3 -0
  6. data/lib/gocardless_pro/api_service.rb +1 -1
  7. data/lib/gocardless_pro/client.rb +6 -1
  8. data/lib/gocardless_pro/resources/payout.rb +2 -0
  9. data/lib/gocardless_pro/resources/payout_item.rb +2 -0
  10. data/lib/gocardless_pro/resources/tax_rate.rb +48 -0
  11. data/lib/gocardless_pro/services/bank_details_lookups_service.rb +0 -10
  12. data/lib/gocardless_pro/services/base_service.rb +11 -0
  13. data/lib/gocardless_pro/services/creditor_bank_accounts_service.rb +0 -10
  14. data/lib/gocardless_pro/services/creditors_service.rb +0 -10
  15. data/lib/gocardless_pro/services/currency_exchange_rates_service.rb +0 -10
  16. data/lib/gocardless_pro/services/customer_bank_accounts_service.rb +0 -10
  17. data/lib/gocardless_pro/services/customer_notifications_service.rb +0 -10
  18. data/lib/gocardless_pro/services/customers_service.rb +0 -10
  19. data/lib/gocardless_pro/services/events_service.rb +0 -10
  20. data/lib/gocardless_pro/services/instalment_schedules_service.rb +21 -10
  21. data/lib/gocardless_pro/services/mandate_import_entries_service.rb +0 -10
  22. data/lib/gocardless_pro/services/mandate_imports_service.rb +0 -10
  23. data/lib/gocardless_pro/services/mandate_pdfs_service.rb +0 -10
  24. data/lib/gocardless_pro/services/mandates_service.rb +0 -10
  25. data/lib/gocardless_pro/services/payments_service.rb +0 -10
  26. data/lib/gocardless_pro/services/payout_items_service.rb +0 -10
  27. data/lib/gocardless_pro/services/payouts_service.rb +0 -10
  28. data/lib/gocardless_pro/services/redirect_flows_service.rb +0 -10
  29. data/lib/gocardless_pro/services/refunds_service.rb +6 -11
  30. data/lib/gocardless_pro/services/subscriptions_service.rb +0 -10
  31. data/lib/gocardless_pro/services/tax_rates_service.rb +74 -0
  32. data/lib/gocardless_pro/version.rb +1 -1
  33. data/spec/resources/instalment_schedule_spec.rb +35 -0
  34. data/spec/resources/payout_item_spec.rb +5 -0
  35. data/spec/resources/payout_spec.rb +8 -0
  36. data/spec/resources/tax_rate_spec.rb +198 -0
  37. data/spec/services/creditor_bank_accounts_service_spec.rb +1 -1
  38. data/spec/services/customer_bank_accounts_service_spec.rb +1 -1
  39. data/spec/services/customer_notifications_service_spec.rb +1 -1
  40. data/spec/services/customers_service_spec.rb +1 -1
  41. data/spec/services/instalment_schedules_service_spec.rb +61 -1
  42. data/spec/services/mandate_imports_service_spec.rb +2 -2
  43. data/spec/services/mandates_service_spec.rb +2 -2
  44. data/spec/services/payments_service_spec.rb +2 -2
  45. data/spec/services/payout_items_service_spec.rb +9 -0
  46. data/spec/services/payouts_service_spec.rb +12 -0
  47. data/spec/services/redirect_flows_service_spec.rb +1 -1
  48. data/spec/services/subscriptions_service_spec.rb +3 -3
  49. data/spec/services/tax_rates_service_spec.rb +381 -0
  50. metadata +17 -11
@@ -411,7 +411,7 @@ describe GoCardlessPro::Services::RedirectFlowsService do
411
411
  stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
412
412
  to_timeout
413
413
 
414
- expect { post_response }.to raise_error(Faraday::TimeoutError)
414
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
415
415
  expect(stub).to have_been_requested
416
416
  end
417
417
  end
@@ -916,7 +916,7 @@ describe GoCardlessPro::Services::SubscriptionsService do
916
916
  stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
917
917
  to_timeout
918
918
 
919
- expect { post_response }.to raise_error(Faraday::TimeoutError)
919
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
920
920
  expect(stub).to have_been_requested
921
921
  end
922
922
  end
@@ -1017,7 +1017,7 @@ describe GoCardlessPro::Services::SubscriptionsService do
1017
1017
  stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
1018
1018
  to_timeout
1019
1019
 
1020
- expect { post_response }.to raise_error(Faraday::TimeoutError)
1020
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
1021
1021
  expect(stub).to have_been_requested
1022
1022
  end
1023
1023
  end
@@ -1118,7 +1118,7 @@ describe GoCardlessPro::Services::SubscriptionsService do
1118
1118
  stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
1119
1119
  to_timeout
1120
1120
 
1121
- expect { post_response }.to raise_error(Faraday::TimeoutError)
1121
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
1122
1122
  expect(stub).to have_been_requested
1123
1123
  end
1124
1124
  end
@@ -0,0 +1,381 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoCardlessPro::Services::TaxRatesService do
4
+ let(:client) do
5
+ GoCardlessPro::Client.new(
6
+ access_token: 'SECRET_TOKEN'
7
+ )
8
+ end
9
+
10
+ let(:response_headers) { { 'Content-Type' => 'application/json' } }
11
+
12
+ describe '#list' do
13
+ describe 'with no filters' do
14
+ subject(:get_list_response) { client.tax_rates.list }
15
+
16
+ let(:body) do
17
+ {
18
+ 'tax_rates' => [{
19
+
20
+ 'end_date' => 'end_date-input',
21
+ 'id' => 'id-input',
22
+ 'jurisdiction' => 'jurisdiction-input',
23
+ 'percentage' => 'percentage-input',
24
+ 'start_date' => 'start_date-input',
25
+ 'type' => 'type-input',
26
+ }],
27
+ meta: {
28
+ cursors: {
29
+ before: nil,
30
+ after: 'ABC123',
31
+ },
32
+ },
33
+ }.to_json
34
+ end
35
+
36
+ before do
37
+ stub_request(:get, %r{.*api.gocardless.com/tax_rates}).to_return(
38
+ body: body,
39
+ headers: response_headers
40
+ )
41
+ end
42
+
43
+ it 'wraps each item in the resource class' do
44
+ expect(get_list_response.records.map(&:class).uniq.first).to eq(GoCardlessPro::Resources::TaxRate)
45
+
46
+ expect(get_list_response.records.first.end_date).to eq('end_date-input')
47
+
48
+ expect(get_list_response.records.first.id).to eq('id-input')
49
+
50
+ expect(get_list_response.records.first.jurisdiction).to eq('jurisdiction-input')
51
+
52
+ expect(get_list_response.records.first.percentage).to eq('percentage-input')
53
+
54
+ expect(get_list_response.records.first.start_date).to eq('start_date-input')
55
+
56
+ expect(get_list_response.records.first.type).to eq('type-input')
57
+ end
58
+
59
+ it 'exposes the cursors for before and after' do
60
+ expect(get_list_response.before).to eq(nil)
61
+ expect(get_list_response.after).to eq('ABC123')
62
+ end
63
+
64
+ specify { expect(get_list_response.api_response.headers).to eql('content-type' => 'application/json') }
65
+
66
+ describe 'retry behaviour' do
67
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
68
+
69
+ it 'retries timeouts' do
70
+ stub = stub_request(:get, %r{.*api.gocardless.com/tax_rates}).
71
+ to_timeout.then.to_return(status: 200, headers: response_headers, body: body)
72
+
73
+ get_list_response
74
+ expect(stub).to have_been_requested.twice
75
+ end
76
+
77
+ it 'retries 5XX errors' do
78
+ stub = stub_request(:get, %r{.*api.gocardless.com/tax_rates}).
79
+ to_return(status: 502,
80
+ headers: { 'Content-Type' => 'text/html' },
81
+ body: '<html><body>Response from Cloudflare</body></html>').
82
+ then.to_return(status: 200, headers: response_headers, body: body)
83
+
84
+ get_list_response
85
+ expect(stub).to have_been_requested.twice
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ describe '#all' do
92
+ let!(:first_response_stub) do
93
+ stub_request(:get, %r{.*api.gocardless.com/tax_rates$}).to_return(
94
+ body: {
95
+ 'tax_rates' => [{
96
+
97
+ 'end_date' => 'end_date-input',
98
+ 'id' => 'id-input',
99
+ 'jurisdiction' => 'jurisdiction-input',
100
+ 'percentage' => 'percentage-input',
101
+ 'start_date' => 'start_date-input',
102
+ 'type' => 'type-input',
103
+ }],
104
+ meta: {
105
+ cursors: { after: 'AB345' },
106
+ limit: 1,
107
+ },
108
+ }.to_json,
109
+ headers: response_headers
110
+ )
111
+ end
112
+
113
+ let!(:second_response_stub) do
114
+ stub_request(:get, %r{.*api.gocardless.com/tax_rates\?after=AB345}).to_return(
115
+ body: {
116
+ 'tax_rates' => [{
117
+
118
+ 'end_date' => 'end_date-input',
119
+ 'id' => 'id-input',
120
+ 'jurisdiction' => 'jurisdiction-input',
121
+ 'percentage' => 'percentage-input',
122
+ 'start_date' => 'start_date-input',
123
+ 'type' => 'type-input',
124
+ }],
125
+ meta: {
126
+ limit: 2,
127
+ cursors: {},
128
+ },
129
+ }.to_json,
130
+ headers: response_headers
131
+ )
132
+ end
133
+
134
+ it 'automatically makes the extra requests' do
135
+ expect(client.tax_rates.all.to_a.length).to eq(2)
136
+ expect(first_response_stub).to have_been_requested
137
+ expect(second_response_stub).to have_been_requested
138
+ end
139
+
140
+ describe 'retry behaviour' do
141
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
142
+
143
+ it 'retries timeouts' do
144
+ first_response_stub = stub_request(:get, %r{.*api.gocardless.com/tax_rates$}).to_return(
145
+ body: {
146
+ 'tax_rates' => [{
147
+
148
+ 'end_date' => 'end_date-input',
149
+ 'id' => 'id-input',
150
+ 'jurisdiction' => 'jurisdiction-input',
151
+ 'percentage' => 'percentage-input',
152
+ 'start_date' => 'start_date-input',
153
+ 'type' => 'type-input',
154
+ }],
155
+ meta: {
156
+ cursors: { after: 'AB345' },
157
+ limit: 1,
158
+ },
159
+ }.to_json,
160
+ headers: response_headers
161
+ )
162
+
163
+ second_response_stub = stub_request(:get, %r{.*api.gocardless.com/tax_rates\?after=AB345}).
164
+ to_timeout.then.
165
+ to_return(
166
+ body: {
167
+ 'tax_rates' => [{
168
+
169
+ 'end_date' => 'end_date-input',
170
+ 'id' => 'id-input',
171
+ 'jurisdiction' => 'jurisdiction-input',
172
+ 'percentage' => 'percentage-input',
173
+ 'start_date' => 'start_date-input',
174
+ 'type' => 'type-input',
175
+ }],
176
+ meta: {
177
+ limit: 2,
178
+ cursors: {},
179
+ },
180
+ }.to_json,
181
+ headers: response_headers
182
+ )
183
+
184
+ client.tax_rates.all.to_a
185
+
186
+ expect(first_response_stub).to have_been_requested
187
+ expect(second_response_stub).to have_been_requested.twice
188
+ end
189
+
190
+ it 'retries 5XX errors' do
191
+ first_response_stub = stub_request(:get, %r{.*api.gocardless.com/tax_rates$}).to_return(
192
+ body: {
193
+ 'tax_rates' => [{
194
+
195
+ 'end_date' => 'end_date-input',
196
+ 'id' => 'id-input',
197
+ 'jurisdiction' => 'jurisdiction-input',
198
+ 'percentage' => 'percentage-input',
199
+ 'start_date' => 'start_date-input',
200
+ 'type' => 'type-input',
201
+ }],
202
+ meta: {
203
+ cursors: { after: 'AB345' },
204
+ limit: 1,
205
+ },
206
+ }.to_json,
207
+ headers: response_headers
208
+ )
209
+
210
+ second_response_stub = stub_request(:get, %r{.*api.gocardless.com/tax_rates\?after=AB345}).
211
+ to_return(
212
+ status: 502,
213
+ body: '<html><body>Response from Cloudflare</body></html>',
214
+ headers: { 'Content-Type' => 'text/html' }
215
+ ).then.to_return(
216
+ body: {
217
+ 'tax_rates' => [{
218
+
219
+ 'end_date' => 'end_date-input',
220
+ 'id' => 'id-input',
221
+ 'jurisdiction' => 'jurisdiction-input',
222
+ 'percentage' => 'percentage-input',
223
+ 'start_date' => 'start_date-input',
224
+ 'type' => 'type-input',
225
+ }],
226
+ meta: {
227
+ limit: 2,
228
+ cursors: {},
229
+ },
230
+ }.to_json,
231
+ headers: response_headers
232
+ )
233
+
234
+ client.tax_rates.all.to_a
235
+
236
+ expect(first_response_stub).to have_been_requested
237
+ expect(second_response_stub).to have_been_requested.twice
238
+ end
239
+ end
240
+ end
241
+
242
+ describe '#get' do
243
+ let(:id) { 'ID123' }
244
+
245
+ subject(:get_response) { client.tax_rates.get(id) }
246
+
247
+ context 'passing in a custom header' do
248
+ let!(:stub) do
249
+ stub_url = '/tax_rates/:identity'.gsub(':identity', id)
250
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).
251
+ with(headers: { 'Foo' => 'Bar' }).
252
+ to_return(
253
+ body: {
254
+ 'tax_rates' => {
255
+
256
+ 'end_date' => 'end_date-input',
257
+ 'id' => 'id-input',
258
+ 'jurisdiction' => 'jurisdiction-input',
259
+ 'percentage' => 'percentage-input',
260
+ 'start_date' => 'start_date-input',
261
+ 'type' => 'type-input',
262
+ },
263
+ }.to_json,
264
+ headers: response_headers
265
+ )
266
+ end
267
+
268
+ subject(:get_response) do
269
+ client.tax_rates.get(id, headers: {
270
+ 'Foo' => 'Bar',
271
+ })
272
+ end
273
+
274
+ it 'includes the header' do
275
+ get_response
276
+ expect(stub).to have_been_requested
277
+ end
278
+ end
279
+
280
+ context 'when there is a tax_rate to return' do
281
+ before do
282
+ stub_url = '/tax_rates/:identity'.gsub(':identity', id)
283
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
284
+ body: {
285
+ 'tax_rates' => {
286
+
287
+ 'end_date' => 'end_date-input',
288
+ 'id' => 'id-input',
289
+ 'jurisdiction' => 'jurisdiction-input',
290
+ 'percentage' => 'percentage-input',
291
+ 'start_date' => 'start_date-input',
292
+ 'type' => 'type-input',
293
+ },
294
+ }.to_json,
295
+ headers: response_headers
296
+ )
297
+ end
298
+
299
+ it 'wraps the response in a resource' do
300
+ expect(get_response).to be_a(GoCardlessPro::Resources::TaxRate)
301
+ end
302
+ end
303
+
304
+ context 'when nothing is returned' do
305
+ before do
306
+ stub_url = '/tax_rates/:identity'.gsub(':identity', id)
307
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
308
+ body: '',
309
+ headers: response_headers
310
+ )
311
+ end
312
+
313
+ it 'returns nil' do
314
+ expect(get_response).to be_nil
315
+ end
316
+ end
317
+
318
+ context "when an ID is specified which can't be included in a valid URI" do
319
+ let(:id) { '`' }
320
+
321
+ it "doesn't raise an error" do
322
+ expect { get_response }.to_not raise_error(/bad URI/)
323
+ end
324
+ end
325
+
326
+ describe 'retry behaviour' do
327
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
328
+
329
+ it 'retries timeouts' do
330
+ stub_url = '/tax_rates/:identity'.gsub(':identity', id)
331
+
332
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/).
333
+ to_timeout.then.to_return(status: 200, headers: response_headers)
334
+
335
+ get_response
336
+ expect(stub).to have_been_requested.twice
337
+ end
338
+
339
+ it 'retries 5XX errors, other than 500s' do
340
+ stub_url = '/tax_rates/:identity'.gsub(':identity', id)
341
+
342
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/).
343
+ to_return(status: 502,
344
+ headers: { 'Content-Type' => 'text/html' },
345
+ body: '<html><body>Response from Cloudflare</body></html>').
346
+ then.to_return(status: 200, headers: response_headers)
347
+
348
+ get_response
349
+ expect(stub).to have_been_requested.twice
350
+ end
351
+
352
+ it 'retries 500 errors returned by the API' do
353
+ stub_url = '/tax_rates/:identity'.gsub(':identity', id)
354
+
355
+ gocardless_error = {
356
+ 'error' => {
357
+ 'message' => 'Internal server error',
358
+ 'documentation_url' => 'https://developer.gocardless.com/#gocardless',
359
+ 'errors' => [{
360
+ 'message' => 'Internal server error',
361
+ 'reason' => 'internal_server_error',
362
+ }],
363
+ 'type' => 'gocardless',
364
+ 'code' => 500,
365
+ 'request_id' => 'dummy_request_id',
366
+ 'id' => 'dummy_exception_id',
367
+ },
368
+ }
369
+
370
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/).
371
+ to_return(status: 500,
372
+ headers: response_headers,
373
+ body: gocardless_error.to_json).
374
+ then.to_return(status: 200, headers: response_headers)
375
+
376
+ get_response
377
+ expect(stub).to have_been_requested.twice
378
+ end
379
+ end
380
+ end
381
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gocardless_pro
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.19.0
4
+ version: 2.23.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GoCardless
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-20 00:00:00.000000000 Z
11
+ date: 2020-07-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: 0.9.2
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '1.0'
22
+ version: '2'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,7 @@ dependencies:
29
29
  version: 0.9.2
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '1.0'
32
+ version: '2'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: rspec
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -50,14 +50,14 @@ dependencies:
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '1.18'
53
+ version: 3.8.3
54
54
  type: :development
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: '1.18'
60
+ version: 3.8.3
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: rubocop
63
63
  requirement: !ruby/object:Gem::Requirement
@@ -128,7 +128,7 @@ dependencies:
128
128
  - - "<"
129
129
  - !ruby/object:Gem::Version
130
130
  version: '13'
131
- description:
131
+ description:
132
132
  email:
133
133
  - engineering@gocardless.com
134
134
  executables: []
@@ -178,6 +178,7 @@ files:
178
178
  - lib/gocardless_pro/resources/redirect_flow.rb
179
179
  - lib/gocardless_pro/resources/refund.rb
180
180
  - lib/gocardless_pro/resources/subscription.rb
181
+ - lib/gocardless_pro/resources/tax_rate.rb
181
182
  - lib/gocardless_pro/response.rb
182
183
  - lib/gocardless_pro/services/bank_details_lookups_service.rb
183
184
  - lib/gocardless_pro/services/base_service.rb
@@ -199,6 +200,7 @@ files:
199
200
  - lib/gocardless_pro/services/redirect_flows_service.rb
200
201
  - lib/gocardless_pro/services/refunds_service.rb
201
202
  - lib/gocardless_pro/services/subscriptions_service.rb
203
+ - lib/gocardless_pro/services/tax_rates_service.rb
202
204
  - lib/gocardless_pro/version.rb
203
205
  - lib/gocardless_pro/webhook.rb
204
206
  - spec/api_response_spec.rb
@@ -225,6 +227,7 @@ files:
225
227
  - spec/resources/redirect_flow_spec.rb
226
228
  - spec/resources/refund_spec.rb
227
229
  - spec/resources/subscription_spec.rb
230
+ - spec/resources/tax_rate_spec.rb
228
231
  - spec/response_spec.rb
229
232
  - spec/services/bank_details_lookups_service_spec.rb
230
233
  - spec/services/creditor_bank_accounts_service_spec.rb
@@ -245,13 +248,14 @@ files:
245
248
  - spec/services/redirect_flows_service_spec.rb
246
249
  - spec/services/refunds_service_spec.rb
247
250
  - spec/services/subscriptions_service_spec.rb
251
+ - spec/services/tax_rates_service_spec.rb
248
252
  - spec/spec_helper.rb
249
253
  - spec/webhook_spec.rb
250
254
  homepage: https://github.com/gocardless/gocardless-pro-ruby
251
255
  licenses:
252
256
  - MIT
253
257
  metadata: {}
254
- post_install_message:
258
+ post_install_message:
255
259
  rdoc_options: []
256
260
  require_paths:
257
261
  - lib
@@ -266,8 +270,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
266
270
  - !ruby/object:Gem::Version
267
271
  version: '0'
268
272
  requirements: []
269
- rubygems_version: 3.0.6
270
- signing_key:
273
+ rubygems_version: 3.1.2
274
+ signing_key:
271
275
  specification_version: 4
272
276
  summary: A gem for calling the GoCardless Pro API
273
277
  test_files:
@@ -295,6 +299,7 @@ test_files:
295
299
  - spec/resources/redirect_flow_spec.rb
296
300
  - spec/resources/refund_spec.rb
297
301
  - spec/resources/subscription_spec.rb
302
+ - spec/resources/tax_rate_spec.rb
298
303
  - spec/response_spec.rb
299
304
  - spec/services/bank_details_lookups_service_spec.rb
300
305
  - spec/services/creditor_bank_accounts_service_spec.rb
@@ -315,5 +320,6 @@ test_files:
315
320
  - spec/services/redirect_flows_service_spec.rb
316
321
  - spec/services/refunds_service_spec.rb
317
322
  - spec/services/subscriptions_service_spec.rb
323
+ - spec/services/tax_rates_service_spec.rb
318
324
  - spec/spec_helper.rb
319
325
  - spec/webhook_spec.rb