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::CreditorsService 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.creditors.create(params: new_resource) }
12
14
  context 'with a valid request' do
@@ -73,13 +75,36 @@ describe GoCardlessPro::Services::CreditorsService do
73
75
  }
74
76
 
75
77
  }.to_json,
76
- headers: { 'Content-Type' => 'application/json' }
78
+ headers: response_headers
77
79
  )
78
80
  end
79
81
 
80
82
  it 'creates and returns the resource' do
81
83
  expect(post_create_response).to be_a(GoCardlessPro::Resources::Creditor)
82
84
  end
85
+
86
+ describe 'retry behaviour' do
87
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
88
+
89
+ it 'retries timeouts' do
90
+ stub = stub_request(:post, %r{.*api.gocardless.com/creditors})
91
+ .to_timeout.then.to_return(status: 200, headers: response_headers)
92
+
93
+ post_create_response
94
+ expect(stub).to have_been_requested.twice
95
+ end
96
+
97
+ it 'retries 5XX errors' do
98
+ stub = stub_request(:post, %r{.*api.gocardless.com/creditors})
99
+ .to_return(status: 502,
100
+ headers: { 'Content-Type' => 'text/html' },
101
+ body: '<html><body>Response from Cloudflare</body></html>')
102
+ .then.to_return(status: 200, headers: response_headers)
103
+
104
+ post_create_response
105
+ expect(stub).to have_been_requested.twice
106
+ end
107
+ end
83
108
  end
84
109
 
85
110
  context 'with a request that returns a validation error' do
@@ -96,7 +121,7 @@ describe GoCardlessPro::Services::CreditorsService do
96
121
  ]
97
122
  }
98
123
  }.to_json,
99
- headers: { 'Content-Type' => 'application/json' },
124
+ headers: response_headers,
100
125
  status: 422
101
126
  )
102
127
  end
@@ -105,39 +130,120 @@ describe GoCardlessPro::Services::CreditorsService do
105
130
  expect { post_create_response }.to raise_error(GoCardlessPro::ValidationError)
106
131
  end
107
132
  end
133
+
134
+ context 'with a request that returns an idempotent creation conflict error' do
135
+ let(:id) { 'ID123' }
136
+
137
+ let(:new_resource) do
138
+ {
139
+
140
+ 'address_line1' => 'address_line1-input',
141
+ 'address_line2' => 'address_line2-input',
142
+ 'address_line3' => 'address_line3-input',
143
+ 'city' => 'city-input',
144
+ 'country_code' => 'country_code-input',
145
+ 'created_at' => 'created_at-input',
146
+ 'id' => 'id-input',
147
+ 'links' => 'links-input',
148
+ 'logo_url' => 'logo_url-input',
149
+ 'name' => 'name-input',
150
+ 'postal_code' => 'postal_code-input',
151
+ 'region' => 'region-input',
152
+ 'scheme_identifiers' => 'scheme_identifiers-input'
153
+ }
154
+ end
155
+
156
+ let!(:post_stub) do
157
+ stub_request(:post, %r{.*api.gocardless.com/creditors}).to_return(
158
+ body: {
159
+ error: {
160
+ type: 'invalid_state',
161
+ code: 409,
162
+ errors: [
163
+ {
164
+ message: 'A resource has already been created with this idempotency key',
165
+ reason: 'idempotent_creation_conflict',
166
+ links: {
167
+ conflicting_resource_id: id
168
+ }
169
+ }
170
+ ]
171
+ }
172
+ }.to_json,
173
+ headers: response_headers,
174
+ status: 409
175
+ )
176
+ end
177
+
178
+ let!(:get_stub) do
179
+ stub_url = "/creditors/#{id}"
180
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/)
181
+ .to_return(
182
+ body: {
183
+ 'creditors' => {
184
+
185
+ 'address_line1' => 'address_line1-input',
186
+ 'address_line2' => 'address_line2-input',
187
+ 'address_line3' => 'address_line3-input',
188
+ 'city' => 'city-input',
189
+ 'country_code' => 'country_code-input',
190
+ 'created_at' => 'created_at-input',
191
+ 'id' => 'id-input',
192
+ 'links' => 'links-input',
193
+ 'logo_url' => 'logo_url-input',
194
+ 'name' => 'name-input',
195
+ 'postal_code' => 'postal_code-input',
196
+ 'region' => 'region-input',
197
+ 'scheme_identifiers' => 'scheme_identifiers-input'
198
+ }
199
+ }.to_json,
200
+ headers: response_headers
201
+ )
202
+ end
203
+
204
+ it 'fetches the already-created resource' do
205
+ post_create_response
206
+ expect(post_stub).to have_been_requested
207
+ expect(get_stub).to have_been_requested
208
+ end
209
+ end
108
210
  end
109
211
 
110
212
  describe '#list' do
111
213
  describe 'with no filters' do
112
214
  subject(:get_list_response) { client.creditors.list }
113
215
 
114
- before do
115
- stub_request(:get, %r{.*api.gocardless.com/creditors}).to_return(
116
- body: {
117
- 'creditors' => [{
216
+ let(:body) do
217
+ {
218
+ 'creditors' => [{
118
219
 
119
- 'address_line1' => 'address_line1-input',
120
- 'address_line2' => 'address_line2-input',
121
- 'address_line3' => 'address_line3-input',
122
- 'city' => 'city-input',
123
- 'country_code' => 'country_code-input',
124
- 'created_at' => 'created_at-input',
125
- 'id' => 'id-input',
126
- 'links' => 'links-input',
127
- 'logo_url' => 'logo_url-input',
128
- 'name' => 'name-input',
129
- 'postal_code' => 'postal_code-input',
130
- 'region' => 'region-input',
131
- 'scheme_identifiers' => 'scheme_identifiers-input'
132
- }],
133
- meta: {
134
- cursors: {
135
- before: nil,
136
- after: 'ABC123'
137
- }
220
+ 'address_line1' => 'address_line1-input',
221
+ 'address_line2' => 'address_line2-input',
222
+ 'address_line3' => 'address_line3-input',
223
+ 'city' => 'city-input',
224
+ 'country_code' => 'country_code-input',
225
+ 'created_at' => 'created_at-input',
226
+ 'id' => 'id-input',
227
+ 'links' => 'links-input',
228
+ 'logo_url' => 'logo_url-input',
229
+ 'name' => 'name-input',
230
+ 'postal_code' => 'postal_code-input',
231
+ 'region' => 'region-input',
232
+ 'scheme_identifiers' => 'scheme_identifiers-input'
233
+ }],
234
+ meta: {
235
+ cursors: {
236
+ before: nil,
237
+ after: 'ABC123'
138
238
  }
139
- }.to_json,
140
- headers: { 'Content-Type' => 'application/json' }
239
+ }
240
+ }.to_json
241
+ end
242
+
243
+ before do
244
+ stub_request(:get, %r{.*api.gocardless.com/creditors}).to_return(
245
+ body: body,
246
+ headers: response_headers
141
247
  )
142
248
  end
143
249
 
@@ -175,6 +281,29 @@ describe GoCardlessPro::Services::CreditorsService do
175
281
  end
176
282
 
177
283
  specify { expect(get_list_response.api_response.headers).to eql('content-type' => 'application/json') }
284
+
285
+ describe 'retry behaviour' do
286
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
287
+
288
+ it 'retries timeouts' do
289
+ stub = stub_request(:get, %r{.*api.gocardless.com/creditors})
290
+ .to_timeout.then.to_return(status: 200, headers: response_headers, body: body)
291
+
292
+ get_list_response
293
+ expect(stub).to have_been_requested.twice
294
+ end
295
+
296
+ it 'retries 5XX errors' do
297
+ stub = stub_request(:get, %r{.*api.gocardless.com/creditors})
298
+ .to_return(status: 502,
299
+ headers: { 'Content-Type' => 'text/html' },
300
+ body: '<html><body>Response from Cloudflare</body></html>')
301
+ .then.to_return(status: 200, headers: response_headers, body: body)
302
+
303
+ get_list_response
304
+ expect(stub).to have_been_requested.twice
305
+ end
306
+ end
178
307
  end
179
308
  end
180
309
 
@@ -203,7 +332,7 @@ describe GoCardlessPro::Services::CreditorsService do
203
332
  limit: 1
204
333
  }
205
334
  }.to_json,
206
- headers: { 'Content-Type' => 'application/json' }
335
+ headers: response_headers
207
336
  )
208
337
  end
209
338
 
@@ -231,7 +360,7 @@ describe GoCardlessPro::Services::CreditorsService do
231
360
  cursors: {}
232
361
  }
233
362
  }.to_json,
234
- headers: { 'Content-Type' => 'application/json' }
363
+ headers: response_headers
235
364
  )
236
365
  end
237
366
 
@@ -240,6 +369,135 @@ describe GoCardlessPro::Services::CreditorsService do
240
369
  expect(first_response_stub).to have_been_requested
241
370
  expect(second_response_stub).to have_been_requested
242
371
  end
372
+
373
+ describe 'retry behaviour' do
374
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
375
+
376
+ it 'retries timeouts' do
377
+ first_response_stub = stub_request(:get, %r{.*api.gocardless.com/creditors$}).to_return(
378
+ body: {
379
+ 'creditors' => [{
380
+
381
+ 'address_line1' => 'address_line1-input',
382
+ 'address_line2' => 'address_line2-input',
383
+ 'address_line3' => 'address_line3-input',
384
+ 'city' => 'city-input',
385
+ 'country_code' => 'country_code-input',
386
+ 'created_at' => 'created_at-input',
387
+ 'id' => 'id-input',
388
+ 'links' => 'links-input',
389
+ 'logo_url' => 'logo_url-input',
390
+ 'name' => 'name-input',
391
+ 'postal_code' => 'postal_code-input',
392
+ 'region' => 'region-input',
393
+ 'scheme_identifiers' => 'scheme_identifiers-input'
394
+ }],
395
+ meta: {
396
+ cursors: { after: 'AB345' },
397
+ limit: 1
398
+ }
399
+ }.to_json,
400
+ headers: response_headers
401
+ )
402
+
403
+ second_response_stub = stub_request(:get, %r{.*api.gocardless.com/creditors\?after=AB345})
404
+ .to_timeout.then
405
+ .to_return(
406
+ body: {
407
+ 'creditors' => [{
408
+
409
+ 'address_line1' => 'address_line1-input',
410
+ 'address_line2' => 'address_line2-input',
411
+ 'address_line3' => 'address_line3-input',
412
+ 'city' => 'city-input',
413
+ 'country_code' => 'country_code-input',
414
+ 'created_at' => 'created_at-input',
415
+ 'id' => 'id-input',
416
+ 'links' => 'links-input',
417
+ 'logo_url' => 'logo_url-input',
418
+ 'name' => 'name-input',
419
+ 'postal_code' => 'postal_code-input',
420
+ 'region' => 'region-input',
421
+ 'scheme_identifiers' => 'scheme_identifiers-input'
422
+ }],
423
+ meta: {
424
+ limit: 2,
425
+ cursors: {}
426
+ }
427
+ }.to_json,
428
+ headers: response_headers
429
+ )
430
+
431
+ client.creditors.all.to_a
432
+
433
+ expect(first_response_stub).to have_been_requested
434
+ expect(second_response_stub).to have_been_requested.twice
435
+ end
436
+
437
+ it 'retries 5XX errors' do
438
+ first_response_stub = stub_request(:get, %r{.*api.gocardless.com/creditors$}).to_return(
439
+ body: {
440
+ 'creditors' => [{
441
+
442
+ 'address_line1' => 'address_line1-input',
443
+ 'address_line2' => 'address_line2-input',
444
+ 'address_line3' => 'address_line3-input',
445
+ 'city' => 'city-input',
446
+ 'country_code' => 'country_code-input',
447
+ 'created_at' => 'created_at-input',
448
+ 'id' => 'id-input',
449
+ 'links' => 'links-input',
450
+ 'logo_url' => 'logo_url-input',
451
+ 'name' => 'name-input',
452
+ 'postal_code' => 'postal_code-input',
453
+ 'region' => 'region-input',
454
+ 'scheme_identifiers' => 'scheme_identifiers-input'
455
+ }],
456
+ meta: {
457
+ cursors: { after: 'AB345' },
458
+ limit: 1
459
+ }
460
+ }.to_json,
461
+ headers: response_headers
462
+ )
463
+
464
+ second_response_stub = stub_request(:get, %r{.*api.gocardless.com/creditors\?after=AB345})
465
+ .to_return(
466
+ status: 502,
467
+ body: '<html><body>Response from Cloudflare</body></html>',
468
+ headers: { 'Content-Type' => 'text/html' }
469
+ ).then.to_return(
470
+ body: {
471
+ 'creditors' => [{
472
+
473
+ 'address_line1' => 'address_line1-input',
474
+ 'address_line2' => 'address_line2-input',
475
+ 'address_line3' => 'address_line3-input',
476
+ 'city' => 'city-input',
477
+ 'country_code' => 'country_code-input',
478
+ 'created_at' => 'created_at-input',
479
+ 'id' => 'id-input',
480
+ 'links' => 'links-input',
481
+ 'logo_url' => 'logo_url-input',
482
+ 'name' => 'name-input',
483
+ 'postal_code' => 'postal_code-input',
484
+ 'region' => 'region-input',
485
+ 'scheme_identifiers' => 'scheme_identifiers-input'
486
+ }],
487
+ meta: {
488
+ limit: 2,
489
+ cursors: {}
490
+ }
491
+ }.to_json,
492
+ headers: response_headers
493
+ )
494
+
495
+ client.creditors.all.to_a
496
+
497
+ expect(first_response_stub).to have_been_requested
498
+ expect(second_response_stub).to have_been_requested.twice
499
+ end
500
+ end
243
501
  end
244
502
 
245
503
  describe '#get' do
@@ -271,7 +529,7 @@ describe GoCardlessPro::Services::CreditorsService do
271
529
  'scheme_identifiers' => 'scheme_identifiers-input'
272
530
  }
273
531
  }.to_json,
274
- headers: { 'Content-Type' => 'application/json' }
532
+ headers: response_headers
275
533
  )
276
534
  end
277
535
 
@@ -309,7 +567,7 @@ describe GoCardlessPro::Services::CreditorsService do
309
567
  'scheme_identifiers' => 'scheme_identifiers-input'
310
568
  }
311
569
  }.to_json,
312
- headers: { 'Content-Type' => 'application/json' }
570
+ headers: response_headers
313
571
  )
314
572
  end
315
573
 
@@ -323,7 +581,7 @@ describe GoCardlessPro::Services::CreditorsService do
323
581
  stub_url = '/creditors/:identity'.gsub(':identity', id)
324
582
  stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
325
583
  body: '',
326
- headers: { 'Content-Type' => 'application/json' }
584
+ headers: response_headers
327
585
  )
328
586
  end
329
587
 
@@ -339,6 +597,33 @@ describe GoCardlessPro::Services::CreditorsService do
339
597
  expect { get_response }.to_not raise_error(/bad URI/)
340
598
  end
341
599
  end
600
+
601
+ describe 'retry behaviour' do
602
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
603
+
604
+ it 'retries timeouts' do
605
+ stub_url = '/creditors/:identity'.gsub(':identity', id)
606
+
607
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/)
608
+ .to_timeout.then.to_return(status: 200, headers: response_headers)
609
+
610
+ get_response
611
+ expect(stub).to have_been_requested.twice
612
+ end
613
+
614
+ it 'retries 5XX errors' do
615
+ stub_url = '/creditors/:identity'.gsub(':identity', id)
616
+
617
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/)
618
+ .to_return(status: 502,
619
+ headers: { 'Content-Type' => 'text/html' },
620
+ body: '<html><body>Response from Cloudflare</body></html>')
621
+ .then.to_return(status: 200, headers: response_headers)
622
+
623
+ get_response
624
+ expect(stub).to have_been_requested.twice
625
+ end
626
+ end
342
627
  end
343
628
 
344
629
  describe '#update' do
@@ -369,7 +654,7 @@ describe GoCardlessPro::Services::CreditorsService do
369
654
  'scheme_identifiers' => 'scheme_identifiers-input'
370
655
  }
371
656
  }.to_json,
372
- headers: { 'Content-Type' => 'application/json' }
657
+ headers: response_headers
373
658
  )
374
659
  end
375
660
 
@@ -377,6 +662,31 @@ describe GoCardlessPro::Services::CreditorsService do
377
662
  expect(put_update_response).to be_a(GoCardlessPro::Resources::Creditor)
378
663
  expect(stub).to have_been_requested
379
664
  end
665
+
666
+ describe 'retry behaviour' do
667
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
668
+
669
+ it 'retries timeouts' do
670
+ stub_url = '/creditors/:identity'.gsub(':identity', id)
671
+ stub = stub_request(:put, /.*api.gocardless.com#{stub_url}/)
672
+ .to_timeout.then.to_return(status: 200, headers: response_headers)
673
+
674
+ put_update_response
675
+ expect(stub).to have_been_requested.twice
676
+ end
677
+
678
+ it 'retries 5XX errors' do
679
+ stub_url = '/creditors/:identity'.gsub(':identity', id)
680
+ stub = stub_request(:put, /.*api.gocardless.com#{stub_url}/)
681
+ .to_return(status: 502,
682
+ headers: { 'Content-Type' => 'text/html' },
683
+ body: '<html><body>Response from Cloudflare</body></html>')
684
+ .then.to_return(status: 200, headers: response_headers)
685
+
686
+ put_update_response
687
+ expect(stub).to have_been_requested.twice
688
+ end
689
+ end
380
690
  end
381
691
  end
382
692
  end