gocardless_pro 1.1.0 → 2.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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -4
  3. data/lib/gocardless_pro.rb +1 -0
  4. data/lib/gocardless_pro/api_service.rb +2 -0
  5. data/lib/gocardless_pro/client.rb +4 -3
  6. data/lib/gocardless_pro/error/invalid_state_error.rb +17 -0
  7. data/lib/gocardless_pro/middlewares/raise_gocardless_errors.rb +50 -0
  8. data/lib/gocardless_pro/request.rb +38 -1
  9. data/lib/gocardless_pro/resources/creditor.rb +2 -2
  10. data/lib/gocardless_pro/resources/creditor_bank_account.rb +11 -11
  11. data/lib/gocardless_pro/resources/customer_bank_account.rb +2 -2
  12. data/lib/gocardless_pro/resources/event.rb +2 -2
  13. data/lib/gocardless_pro/resources/mandate.rb +2 -2
  14. data/lib/gocardless_pro/resources/payment.rb +7 -7
  15. data/lib/gocardless_pro/resources/payout.rb +7 -6
  16. data/lib/gocardless_pro/resources/redirect_flow.rb +2 -2
  17. data/lib/gocardless_pro/resources/refund.rb +2 -2
  18. data/lib/gocardless_pro/resources/subscription.rb +2 -2
  19. data/lib/gocardless_pro/response.rb +2 -54
  20. data/lib/gocardless_pro/services/bank_details_lookups_service.rb +3 -0
  21. data/lib/gocardless_pro/services/creditor_bank_accounts_service.rb +31 -2
  22. data/lib/gocardless_pro/services/creditors_service.rb +21 -1
  23. data/lib/gocardless_pro/services/customer_bank_accounts_service.rb +34 -2
  24. data/lib/gocardless_pro/services/customers_service.rb +21 -1
  25. data/lib/gocardless_pro/services/events_service.rb +5 -0
  26. data/lib/gocardless_pro/services/mandate_pdfs_service.rb +3 -0
  27. data/lib/gocardless_pro/services/mandates_service.rb +47 -3
  28. data/lib/gocardless_pro/services/payments_service.rb +47 -3
  29. data/lib/gocardless_pro/services/payouts_service.rb +5 -0
  30. data/lib/gocardless_pro/services/redirect_flows_service.rb +28 -2
  31. data/lib/gocardless_pro/services/refunds_service.rb +21 -1
  32. data/lib/gocardless_pro/services/subscriptions_service.rb +34 -2
  33. data/lib/gocardless_pro/version.rb +1 -1
  34. data/spec/api_service_spec.rb +106 -0
  35. data/spec/middlewares/raise_gocardless_errors_spec.rb +98 -0
  36. data/spec/resources/bank_details_lookup_spec.rb +102 -19
  37. data/spec/resources/creditor_bank_account_spec.rb +416 -40
  38. data/spec/resources/creditor_spec.rb +414 -53
  39. data/spec/resources/customer_bank_account_spec.rb +452 -40
  40. data/spec/resources/customer_spec.rb +457 -45
  41. data/spec/resources/event_spec.rb +171 -72
  42. data/spec/resources/mandate_pdf_spec.rb +100 -17
  43. data/spec/resources/mandate_spec.rb +501 -44
  44. data/spec/resources/payment_spec.rb +531 -48
  45. data/spec/resources/payout_spec.rb +189 -45
  46. data/spec/resources/redirect_flow_spec.rb +277 -43
  47. data/spec/resources/refund_spec.rb +349 -34
  48. data/spec/resources/subscription_spec.rb +531 -53
  49. data/spec/response_spec.rb +12 -79
  50. data/spec/services/bank_details_lookups_service_spec.rb +67 -2
  51. data/spec/services/creditor_bank_accounts_service_spec.rb +309 -31
  52. data/spec/services/creditors_service_spec.rb +343 -33
  53. data/spec/services/customer_bank_accounts_service_spec.rb +335 -32
  54. data/spec/services/customers_service_spec.rb +364 -36
  55. data/spec/services/events_service_spec.rb +185 -24
  56. data/spec/services/mandate_pdfs_service_spec.rb +66 -2
  57. data/spec/services/mandates_service_spec.rb +341 -33
  58. data/spec/services/payments_service_spec.rb +355 -35
  59. data/spec/services/payouts_service_spec.rb +206 -26
  60. data/spec/services/redirect_flows_service_spec.rb +137 -7
  61. data/spec/services/refunds_service_spec.rb +301 -27
  62. data/spec/services/subscriptions_service_spec.rb +377 -38
  63. metadata +6 -3
@@ -7,6 +7,8 @@ describe GoCardlessPro::Services::CustomerBankAccountsService do
7
7
  )
8
8
  end
9
9
 
10
+ let(:response_headers) { { 'Content-Type' => 'application/json' } }
11
+
10
12
  describe '#create' do
11
13
  subject(:post_create_response) { client.customer_bank_accounts.create(params: new_resource) }
12
14
  context 'with a valid request' do
@@ -64,13 +66,36 @@ describe GoCardlessPro::Services::CustomerBankAccountsService do
64
66
  }
65
67
 
66
68
  }.to_json,
67
- headers: { 'Content-Type' => 'application/json' }
69
+ headers: response_headers
68
70
  )
69
71
  end
70
72
 
71
73
  it 'creates and returns the resource' do
72
74
  expect(post_create_response).to be_a(GoCardlessPro::Resources::CustomerBankAccount)
73
75
  end
76
+
77
+ describe 'retry behaviour' do
78
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
79
+
80
+ it 'retries timeouts' do
81
+ stub = stub_request(:post, %r{.*api.gocardless.com/customer_bank_accounts})
82
+ .to_timeout.then.to_return(status: 200, headers: response_headers)
83
+
84
+ post_create_response
85
+ expect(stub).to have_been_requested.twice
86
+ end
87
+
88
+ it 'retries 5XX errors' do
89
+ stub = stub_request(:post, %r{.*api.gocardless.com/customer_bank_accounts})
90
+ .to_return(status: 502,
91
+ headers: { 'Content-Type' => 'text/html' },
92
+ body: '<html><body>Response from Cloudflare</body></html>')
93
+ .then.to_return(status: 200, headers: response_headers)
94
+
95
+ post_create_response
96
+ expect(stub).to have_been_requested.twice
97
+ end
98
+ end
74
99
  end
75
100
 
76
101
  context 'with a request that returns a validation error' do
@@ -87,7 +112,7 @@ describe GoCardlessPro::Services::CustomerBankAccountsService do
87
112
  ]
88
113
  }
89
114
  }.to_json,
90
- headers: { 'Content-Type' => 'application/json' },
115
+ headers: response_headers,
91
116
  status: 422
92
117
  )
93
118
  end
@@ -96,36 +121,111 @@ describe GoCardlessPro::Services::CustomerBankAccountsService do
96
121
  expect { post_create_response }.to raise_error(GoCardlessPro::ValidationError)
97
122
  end
98
123
  end
124
+
125
+ context 'with a request that returns an idempotent creation conflict error' do
126
+ let(:id) { 'ID123' }
127
+
128
+ let(:new_resource) do
129
+ {
130
+
131
+ 'account_holder_name' => 'account_holder_name-input',
132
+ 'account_number_ending' => 'account_number_ending-input',
133
+ 'bank_name' => 'bank_name-input',
134
+ 'country_code' => 'country_code-input',
135
+ 'created_at' => 'created_at-input',
136
+ 'currency' => 'currency-input',
137
+ 'enabled' => 'enabled-input',
138
+ 'id' => 'id-input',
139
+ 'links' => 'links-input',
140
+ 'metadata' => 'metadata-input'
141
+ }
142
+ end
143
+
144
+ let!(:post_stub) do
145
+ stub_request(:post, %r{.*api.gocardless.com/customer_bank_accounts}).to_return(
146
+ body: {
147
+ error: {
148
+ type: 'invalid_state',
149
+ code: 409,
150
+ errors: [
151
+ {
152
+ message: 'A resource has already been created with this idempotency key',
153
+ reason: 'idempotent_creation_conflict',
154
+ links: {
155
+ conflicting_resource_id: id
156
+ }
157
+ }
158
+ ]
159
+ }
160
+ }.to_json,
161
+ headers: response_headers,
162
+ status: 409
163
+ )
164
+ end
165
+
166
+ let!(:get_stub) do
167
+ stub_url = "/customer_bank_accounts/#{id}"
168
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/)
169
+ .to_return(
170
+ body: {
171
+ 'customer_bank_accounts' => {
172
+
173
+ 'account_holder_name' => 'account_holder_name-input',
174
+ 'account_number_ending' => 'account_number_ending-input',
175
+ 'bank_name' => 'bank_name-input',
176
+ 'country_code' => 'country_code-input',
177
+ 'created_at' => 'created_at-input',
178
+ 'currency' => 'currency-input',
179
+ 'enabled' => 'enabled-input',
180
+ 'id' => 'id-input',
181
+ 'links' => 'links-input',
182
+ 'metadata' => 'metadata-input'
183
+ }
184
+ }.to_json,
185
+ headers: response_headers
186
+ )
187
+ end
188
+
189
+ it 'fetches the already-created resource' do
190
+ post_create_response
191
+ expect(post_stub).to have_been_requested
192
+ expect(get_stub).to have_been_requested
193
+ end
194
+ end
99
195
  end
100
196
 
101
197
  describe '#list' do
102
198
  describe 'with no filters' do
103
199
  subject(:get_list_response) { client.customer_bank_accounts.list }
104
200
 
105
- before do
106
- stub_request(:get, %r{.*api.gocardless.com/customer_bank_accounts}).to_return(
107
- body: {
108
- 'customer_bank_accounts' => [{
201
+ let(:body) do
202
+ {
203
+ 'customer_bank_accounts' => [{
109
204
 
110
- 'account_holder_name' => 'account_holder_name-input',
111
- 'account_number_ending' => 'account_number_ending-input',
112
- 'bank_name' => 'bank_name-input',
113
- 'country_code' => 'country_code-input',
114
- 'created_at' => 'created_at-input',
115
- 'currency' => 'currency-input',
116
- 'enabled' => 'enabled-input',
117
- 'id' => 'id-input',
118
- 'links' => 'links-input',
119
- 'metadata' => 'metadata-input'
120
- }],
121
- meta: {
122
- cursors: {
123
- before: nil,
124
- after: 'ABC123'
125
- }
205
+ 'account_holder_name' => 'account_holder_name-input',
206
+ 'account_number_ending' => 'account_number_ending-input',
207
+ 'bank_name' => 'bank_name-input',
208
+ 'country_code' => 'country_code-input',
209
+ 'created_at' => 'created_at-input',
210
+ 'currency' => 'currency-input',
211
+ 'enabled' => 'enabled-input',
212
+ 'id' => 'id-input',
213
+ 'links' => 'links-input',
214
+ 'metadata' => 'metadata-input'
215
+ }],
216
+ meta: {
217
+ cursors: {
218
+ before: nil,
219
+ after: 'ABC123'
126
220
  }
127
- }.to_json,
128
- headers: { 'Content-Type' => 'application/json' }
221
+ }
222
+ }.to_json
223
+ end
224
+
225
+ before do
226
+ stub_request(:get, %r{.*api.gocardless.com/customer_bank_accounts}).to_return(
227
+ body: body,
228
+ headers: response_headers
129
229
  )
130
230
  end
131
231
 
@@ -157,6 +257,29 @@ describe GoCardlessPro::Services::CustomerBankAccountsService do
157
257
  end
158
258
 
159
259
  specify { expect(get_list_response.api_response.headers).to eql('content-type' => 'application/json') }
260
+
261
+ describe 'retry behaviour' do
262
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
263
+
264
+ it 'retries timeouts' do
265
+ stub = stub_request(:get, %r{.*api.gocardless.com/customer_bank_accounts})
266
+ .to_timeout.then.to_return(status: 200, headers: response_headers, body: body)
267
+
268
+ get_list_response
269
+ expect(stub).to have_been_requested.twice
270
+ end
271
+
272
+ it 'retries 5XX errors' do
273
+ stub = stub_request(:get, %r{.*api.gocardless.com/customer_bank_accounts})
274
+ .to_return(status: 502,
275
+ headers: { 'Content-Type' => 'text/html' },
276
+ body: '<html><body>Response from Cloudflare</body></html>')
277
+ .then.to_return(status: 200, headers: response_headers, body: body)
278
+
279
+ get_list_response
280
+ expect(stub).to have_been_requested.twice
281
+ end
282
+ end
160
283
  end
161
284
  end
162
285
 
@@ -182,7 +305,7 @@ describe GoCardlessPro::Services::CustomerBankAccountsService do
182
305
  limit: 1
183
306
  }
184
307
  }.to_json,
185
- headers: { 'Content-Type' => 'application/json' }
308
+ headers: response_headers
186
309
  )
187
310
  end
188
311
 
@@ -207,7 +330,7 @@ describe GoCardlessPro::Services::CustomerBankAccountsService do
207
330
  cursors: {}
208
331
  }
209
332
  }.to_json,
210
- headers: { 'Content-Type' => 'application/json' }
333
+ headers: response_headers
211
334
  )
212
335
  end
213
336
 
@@ -216,6 +339,123 @@ describe GoCardlessPro::Services::CustomerBankAccountsService do
216
339
  expect(first_response_stub).to have_been_requested
217
340
  expect(second_response_stub).to have_been_requested
218
341
  end
342
+
343
+ describe 'retry behaviour' do
344
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
345
+
346
+ it 'retries timeouts' do
347
+ first_response_stub = stub_request(:get, %r{.*api.gocardless.com/customer_bank_accounts$}).to_return(
348
+ body: {
349
+ 'customer_bank_accounts' => [{
350
+
351
+ 'account_holder_name' => 'account_holder_name-input',
352
+ 'account_number_ending' => 'account_number_ending-input',
353
+ 'bank_name' => 'bank_name-input',
354
+ 'country_code' => 'country_code-input',
355
+ 'created_at' => 'created_at-input',
356
+ 'currency' => 'currency-input',
357
+ 'enabled' => 'enabled-input',
358
+ 'id' => 'id-input',
359
+ 'links' => 'links-input',
360
+ 'metadata' => 'metadata-input'
361
+ }],
362
+ meta: {
363
+ cursors: { after: 'AB345' },
364
+ limit: 1
365
+ }
366
+ }.to_json,
367
+ headers: response_headers
368
+ )
369
+
370
+ second_response_stub = stub_request(:get, %r{.*api.gocardless.com/customer_bank_accounts\?after=AB345})
371
+ .to_timeout.then
372
+ .to_return(
373
+ body: {
374
+ 'customer_bank_accounts' => [{
375
+
376
+ 'account_holder_name' => 'account_holder_name-input',
377
+ 'account_number_ending' => 'account_number_ending-input',
378
+ 'bank_name' => 'bank_name-input',
379
+ 'country_code' => 'country_code-input',
380
+ 'created_at' => 'created_at-input',
381
+ 'currency' => 'currency-input',
382
+ 'enabled' => 'enabled-input',
383
+ 'id' => 'id-input',
384
+ 'links' => 'links-input',
385
+ 'metadata' => 'metadata-input'
386
+ }],
387
+ meta: {
388
+ limit: 2,
389
+ cursors: {}
390
+ }
391
+ }.to_json,
392
+ headers: response_headers
393
+ )
394
+
395
+ client.customer_bank_accounts.all.to_a
396
+
397
+ expect(first_response_stub).to have_been_requested
398
+ expect(second_response_stub).to have_been_requested.twice
399
+ end
400
+
401
+ it 'retries 5XX errors' do
402
+ first_response_stub = stub_request(:get, %r{.*api.gocardless.com/customer_bank_accounts$}).to_return(
403
+ body: {
404
+ 'customer_bank_accounts' => [{
405
+
406
+ 'account_holder_name' => 'account_holder_name-input',
407
+ 'account_number_ending' => 'account_number_ending-input',
408
+ 'bank_name' => 'bank_name-input',
409
+ 'country_code' => 'country_code-input',
410
+ 'created_at' => 'created_at-input',
411
+ 'currency' => 'currency-input',
412
+ 'enabled' => 'enabled-input',
413
+ 'id' => 'id-input',
414
+ 'links' => 'links-input',
415
+ 'metadata' => 'metadata-input'
416
+ }],
417
+ meta: {
418
+ cursors: { after: 'AB345' },
419
+ limit: 1
420
+ }
421
+ }.to_json,
422
+ headers: response_headers
423
+ )
424
+
425
+ second_response_stub = stub_request(:get, %r{.*api.gocardless.com/customer_bank_accounts\?after=AB345})
426
+ .to_return(
427
+ status: 502,
428
+ body: '<html><body>Response from Cloudflare</body></html>',
429
+ headers: { 'Content-Type' => 'text/html' }
430
+ ).then.to_return(
431
+ body: {
432
+ 'customer_bank_accounts' => [{
433
+
434
+ 'account_holder_name' => 'account_holder_name-input',
435
+ 'account_number_ending' => 'account_number_ending-input',
436
+ 'bank_name' => 'bank_name-input',
437
+ 'country_code' => 'country_code-input',
438
+ 'created_at' => 'created_at-input',
439
+ 'currency' => 'currency-input',
440
+ 'enabled' => 'enabled-input',
441
+ 'id' => 'id-input',
442
+ 'links' => 'links-input',
443
+ 'metadata' => 'metadata-input'
444
+ }],
445
+ meta: {
446
+ limit: 2,
447
+ cursors: {}
448
+ }
449
+ }.to_json,
450
+ headers: response_headers
451
+ )
452
+
453
+ client.customer_bank_accounts.all.to_a
454
+
455
+ expect(first_response_stub).to have_been_requested
456
+ expect(second_response_stub).to have_been_requested.twice
457
+ end
458
+ end
219
459
  end
220
460
 
221
461
  describe '#get' do
@@ -244,7 +484,7 @@ describe GoCardlessPro::Services::CustomerBankAccountsService do
244
484
  'metadata' => 'metadata-input'
245
485
  }
246
486
  }.to_json,
247
- headers: { 'Content-Type' => 'application/json' }
487
+ headers: response_headers
248
488
  )
249
489
  end
250
490
 
@@ -279,7 +519,7 @@ describe GoCardlessPro::Services::CustomerBankAccountsService do
279
519
  'metadata' => 'metadata-input'
280
520
  }
281
521
  }.to_json,
282
- headers: { 'Content-Type' => 'application/json' }
522
+ headers: response_headers
283
523
  )
284
524
  end
285
525
 
@@ -293,7 +533,7 @@ describe GoCardlessPro::Services::CustomerBankAccountsService do
293
533
  stub_url = '/customer_bank_accounts/:identity'.gsub(':identity', id)
294
534
  stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
295
535
  body: '',
296
- headers: { 'Content-Type' => 'application/json' }
536
+ headers: response_headers
297
537
  )
298
538
  end
299
539
 
@@ -309,6 +549,33 @@ describe GoCardlessPro::Services::CustomerBankAccountsService do
309
549
  expect { get_response }.to_not raise_error(/bad URI/)
310
550
  end
311
551
  end
552
+
553
+ describe 'retry behaviour' do
554
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
555
+
556
+ it 'retries timeouts' do
557
+ stub_url = '/customer_bank_accounts/:identity'.gsub(':identity', id)
558
+
559
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/)
560
+ .to_timeout.then.to_return(status: 200, headers: response_headers)
561
+
562
+ get_response
563
+ expect(stub).to have_been_requested.twice
564
+ end
565
+
566
+ it 'retries 5XX errors' do
567
+ stub_url = '/customer_bank_accounts/:identity'.gsub(':identity', id)
568
+
569
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/)
570
+ .to_return(status: 502,
571
+ headers: { 'Content-Type' => 'text/html' },
572
+ body: '<html><body>Response from Cloudflare</body></html>')
573
+ .then.to_return(status: 200, headers: response_headers)
574
+
575
+ get_response
576
+ expect(stub).to have_been_requested.twice
577
+ end
578
+ end
312
579
  end
313
580
 
314
581
  describe '#update' do
@@ -336,7 +603,7 @@ describe GoCardlessPro::Services::CustomerBankAccountsService do
336
603
  'metadata' => 'metadata-input'
337
604
  }
338
605
  }.to_json,
339
- headers: { 'Content-Type' => 'application/json' }
606
+ headers: response_headers
340
607
  )
341
608
  end
342
609
 
@@ -344,6 +611,31 @@ describe GoCardlessPro::Services::CustomerBankAccountsService do
344
611
  expect(put_update_response).to be_a(GoCardlessPro::Resources::CustomerBankAccount)
345
612
  expect(stub).to have_been_requested
346
613
  end
614
+
615
+ describe 'retry behaviour' do
616
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
617
+
618
+ it 'retries timeouts' do
619
+ stub_url = '/customer_bank_accounts/:identity'.gsub(':identity', id)
620
+ stub = stub_request(:put, /.*api.gocardless.com#{stub_url}/)
621
+ .to_timeout.then.to_return(status: 200, headers: response_headers)
622
+
623
+ put_update_response
624
+ expect(stub).to have_been_requested.twice
625
+ end
626
+
627
+ it 'retries 5XX errors' do
628
+ stub_url = '/customer_bank_accounts/:identity'.gsub(':identity', id)
629
+ stub = stub_request(:put, /.*api.gocardless.com#{stub_url}/)
630
+ .to_return(status: 502,
631
+ headers: { 'Content-Type' => 'text/html' },
632
+ body: '<html><body>Response from Cloudflare</body></html>')
633
+ .then.to_return(status: 200, headers: response_headers)
634
+
635
+ put_update_response
636
+ expect(stub).to have_been_requested.twice
637
+ end
638
+ end
347
639
  end
348
640
  end
349
641
 
@@ -371,7 +663,7 @@ describe GoCardlessPro::Services::CustomerBankAccountsService do
371
663
  'metadata' => 'metadata-input'
372
664
  }
373
665
  }.to_json,
374
- headers: { 'Content-Type' => 'application/json' }
666
+ headers: response_headers
375
667
  )
376
668
  end
377
669
 
@@ -381,6 +673,17 @@ describe GoCardlessPro::Services::CustomerBankAccountsService do
381
673
  expect(stub).to have_been_requested
382
674
  end
383
675
 
676
+ describe 'retry behaviour' do
677
+ it "doesn't retry errors" do
678
+ stub_url = '/customer_bank_accounts/:identity/actions/disable'.gsub(':identity', resource_id)
679
+ stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/)
680
+ .to_timeout
681
+
682
+ expect { post_response }.to raise_error(Faraday::TimeoutError)
683
+ expect(stub).to have_been_requested
684
+ end
685
+ end
686
+
384
687
  context 'when the request needs a body and custom header' do
385
688
  let(:body) { { foo: 'bar' } }
386
689
  let(:headers) { { 'Foo' => 'Bar' } }
@@ -411,7 +714,7 @@ describe GoCardlessPro::Services::CustomerBankAccountsService do
411
714
  'metadata' => 'metadata-input'
412
715
  }
413
716
  }.to_json,
414
- headers: { 'Content-Type' => 'application/json' }
717
+ headers: response_headers
415
718
  )
416
719
  end
417
720
  end