gocardless_pro 1.1.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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