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::CustomersService 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.customers.create(params: new_resource) }
12
14
  context 'with a valid request' do
@@ -82,13 +84,36 @@ describe GoCardlessPro::Services::CustomersService do
82
84
  }
83
85
 
84
86
  }.to_json,
85
- headers: { 'Content-Type' => 'application/json' }
87
+ headers: response_headers
86
88
  )
87
89
  end
88
90
 
89
91
  it 'creates and returns the resource' do
90
92
  expect(post_create_response).to be_a(GoCardlessPro::Resources::Customer)
91
93
  end
94
+
95
+ describe 'retry behaviour' do
96
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
97
+
98
+ it 'retries timeouts' do
99
+ stub = stub_request(:post, %r{.*api.gocardless.com/customers})
100
+ .to_timeout.then.to_return(status: 200, headers: response_headers)
101
+
102
+ post_create_response
103
+ expect(stub).to have_been_requested.twice
104
+ end
105
+
106
+ it 'retries 5XX errors' do
107
+ stub = stub_request(:post, %r{.*api.gocardless.com/customers})
108
+ .to_return(status: 502,
109
+ headers: { 'Content-Type' => 'text/html' },
110
+ body: '<html><body>Response from Cloudflare</body></html>')
111
+ .then.to_return(status: 200, headers: response_headers)
112
+
113
+ post_create_response
114
+ expect(stub).to have_been_requested.twice
115
+ end
116
+ end
92
117
  end
93
118
 
94
119
  context 'with a request that returns a validation error' do
@@ -105,7 +130,7 @@ describe GoCardlessPro::Services::CustomersService do
105
130
  ]
106
131
  }
107
132
  }.to_json,
108
- headers: { 'Content-Type' => 'application/json' },
133
+ headers: response_headers,
109
134
  status: 422
110
135
  )
111
136
  end
@@ -114,42 +139,129 @@ describe GoCardlessPro::Services::CustomersService do
114
139
  expect { post_create_response }.to raise_error(GoCardlessPro::ValidationError)
115
140
  end
116
141
  end
142
+
143
+ context 'with a request that returns an idempotent creation conflict error' do
144
+ let(:id) { 'ID123' }
145
+
146
+ let(:new_resource) do
147
+ {
148
+
149
+ 'address_line1' => 'address_line1-input',
150
+ 'address_line2' => 'address_line2-input',
151
+ 'address_line3' => 'address_line3-input',
152
+ 'city' => 'city-input',
153
+ 'company_name' => 'company_name-input',
154
+ 'country_code' => 'country_code-input',
155
+ 'created_at' => 'created_at-input',
156
+ 'email' => 'email-input',
157
+ 'family_name' => 'family_name-input',
158
+ 'given_name' => 'given_name-input',
159
+ 'id' => 'id-input',
160
+ 'language' => 'language-input',
161
+ 'metadata' => 'metadata-input',
162
+ 'postal_code' => 'postal_code-input',
163
+ 'region' => 'region-input',
164
+ 'swedish_identity_number' => 'swedish_identity_number-input'
165
+ }
166
+ end
167
+
168
+ let!(:post_stub) do
169
+ stub_request(:post, %r{.*api.gocardless.com/customers}).to_return(
170
+ body: {
171
+ error: {
172
+ type: 'invalid_state',
173
+ code: 409,
174
+ errors: [
175
+ {
176
+ message: 'A resource has already been created with this idempotency key',
177
+ reason: 'idempotent_creation_conflict',
178
+ links: {
179
+ conflicting_resource_id: id
180
+ }
181
+ }
182
+ ]
183
+ }
184
+ }.to_json,
185
+ headers: response_headers,
186
+ status: 409
187
+ )
188
+ end
189
+
190
+ let!(:get_stub) do
191
+ stub_url = "/customers/#{id}"
192
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/)
193
+ .to_return(
194
+ body: {
195
+ 'customers' => {
196
+
197
+ 'address_line1' => 'address_line1-input',
198
+ 'address_line2' => 'address_line2-input',
199
+ 'address_line3' => 'address_line3-input',
200
+ 'city' => 'city-input',
201
+ 'company_name' => 'company_name-input',
202
+ 'country_code' => 'country_code-input',
203
+ 'created_at' => 'created_at-input',
204
+ 'email' => 'email-input',
205
+ 'family_name' => 'family_name-input',
206
+ 'given_name' => 'given_name-input',
207
+ 'id' => 'id-input',
208
+ 'language' => 'language-input',
209
+ 'metadata' => 'metadata-input',
210
+ 'postal_code' => 'postal_code-input',
211
+ 'region' => 'region-input',
212
+ 'swedish_identity_number' => 'swedish_identity_number-input'
213
+ }
214
+ }.to_json,
215
+ headers: response_headers
216
+ )
217
+ end
218
+
219
+ it 'fetches the already-created resource' do
220
+ post_create_response
221
+ expect(post_stub).to have_been_requested
222
+ expect(get_stub).to have_been_requested
223
+ end
224
+ end
117
225
  end
118
226
 
119
227
  describe '#list' do
120
228
  describe 'with no filters' do
121
229
  subject(:get_list_response) { client.customers.list }
122
230
 
123
- before do
124
- stub_request(:get, %r{.*api.gocardless.com/customers}).to_return(
125
- body: {
126
- 'customers' => [{
231
+ let(:body) do
232
+ {
233
+ 'customers' => [{
127
234
 
128
- 'address_line1' => 'address_line1-input',
129
- 'address_line2' => 'address_line2-input',
130
- 'address_line3' => 'address_line3-input',
131
- 'city' => 'city-input',
132
- 'company_name' => 'company_name-input',
133
- 'country_code' => 'country_code-input',
134
- 'created_at' => 'created_at-input',
135
- 'email' => 'email-input',
136
- 'family_name' => 'family_name-input',
137
- 'given_name' => 'given_name-input',
138
- 'id' => 'id-input',
139
- 'language' => 'language-input',
140
- 'metadata' => 'metadata-input',
141
- 'postal_code' => 'postal_code-input',
142
- 'region' => 'region-input',
143
- 'swedish_identity_number' => 'swedish_identity_number-input'
144
- }],
145
- meta: {
146
- cursors: {
147
- before: nil,
148
- after: 'ABC123'
149
- }
235
+ 'address_line1' => 'address_line1-input',
236
+ 'address_line2' => 'address_line2-input',
237
+ 'address_line3' => 'address_line3-input',
238
+ 'city' => 'city-input',
239
+ 'company_name' => 'company_name-input',
240
+ 'country_code' => 'country_code-input',
241
+ 'created_at' => 'created_at-input',
242
+ 'email' => 'email-input',
243
+ 'family_name' => 'family_name-input',
244
+ 'given_name' => 'given_name-input',
245
+ 'id' => 'id-input',
246
+ 'language' => 'language-input',
247
+ 'metadata' => 'metadata-input',
248
+ 'postal_code' => 'postal_code-input',
249
+ 'region' => 'region-input',
250
+ 'swedish_identity_number' => 'swedish_identity_number-input'
251
+ }],
252
+ meta: {
253
+ cursors: {
254
+ before: nil,
255
+ after: 'ABC123'
150
256
  }
151
- }.to_json,
152
- headers: { 'Content-Type' => 'application/json' }
257
+ }
258
+ }.to_json
259
+ end
260
+
261
+ before do
262
+ stub_request(:get, %r{.*api.gocardless.com/customers}).to_return(
263
+ body: body,
264
+ headers: response_headers
153
265
  )
154
266
  end
155
267
 
@@ -195,6 +307,29 @@ describe GoCardlessPro::Services::CustomersService do
195
307
  end
196
308
 
197
309
  specify { expect(get_list_response.api_response.headers).to eql('content-type' => 'application/json') }
310
+
311
+ describe 'retry behaviour' do
312
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
313
+
314
+ it 'retries timeouts' do
315
+ stub = stub_request(:get, %r{.*api.gocardless.com/customers})
316
+ .to_timeout.then.to_return(status: 200, headers: response_headers, body: body)
317
+
318
+ get_list_response
319
+ expect(stub).to have_been_requested.twice
320
+ end
321
+
322
+ it 'retries 5XX errors' do
323
+ stub = stub_request(:get, %r{.*api.gocardless.com/customers})
324
+ .to_return(status: 502,
325
+ headers: { 'Content-Type' => 'text/html' },
326
+ body: '<html><body>Response from Cloudflare</body></html>')
327
+ .then.to_return(status: 200, headers: response_headers, body: body)
328
+
329
+ get_list_response
330
+ expect(stub).to have_been_requested.twice
331
+ end
332
+ end
198
333
  end
199
334
  end
200
335
 
@@ -226,7 +361,7 @@ describe GoCardlessPro::Services::CustomersService do
226
361
  limit: 1
227
362
  }
228
363
  }.to_json,
229
- headers: { 'Content-Type' => 'application/json' }
364
+ headers: response_headers
230
365
  )
231
366
  end
232
367
 
@@ -257,7 +392,7 @@ describe GoCardlessPro::Services::CustomersService do
257
392
  cursors: {}
258
393
  }
259
394
  }.to_json,
260
- headers: { 'Content-Type' => 'application/json' }
395
+ headers: response_headers
261
396
  )
262
397
  end
263
398
 
@@ -266,6 +401,147 @@ describe GoCardlessPro::Services::CustomersService do
266
401
  expect(first_response_stub).to have_been_requested
267
402
  expect(second_response_stub).to have_been_requested
268
403
  end
404
+
405
+ describe 'retry behaviour' do
406
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
407
+
408
+ it 'retries timeouts' do
409
+ first_response_stub = stub_request(:get, %r{.*api.gocardless.com/customers$}).to_return(
410
+ body: {
411
+ 'customers' => [{
412
+
413
+ 'address_line1' => 'address_line1-input',
414
+ 'address_line2' => 'address_line2-input',
415
+ 'address_line3' => 'address_line3-input',
416
+ 'city' => 'city-input',
417
+ 'company_name' => 'company_name-input',
418
+ 'country_code' => 'country_code-input',
419
+ 'created_at' => 'created_at-input',
420
+ 'email' => 'email-input',
421
+ 'family_name' => 'family_name-input',
422
+ 'given_name' => 'given_name-input',
423
+ 'id' => 'id-input',
424
+ 'language' => 'language-input',
425
+ 'metadata' => 'metadata-input',
426
+ 'postal_code' => 'postal_code-input',
427
+ 'region' => 'region-input',
428
+ 'swedish_identity_number' => 'swedish_identity_number-input'
429
+ }],
430
+ meta: {
431
+ cursors: { after: 'AB345' },
432
+ limit: 1
433
+ }
434
+ }.to_json,
435
+ headers: response_headers
436
+ )
437
+
438
+ second_response_stub = stub_request(:get, %r{.*api.gocardless.com/customers\?after=AB345})
439
+ .to_timeout.then
440
+ .to_return(
441
+ body: {
442
+ 'customers' => [{
443
+
444
+ 'address_line1' => 'address_line1-input',
445
+ 'address_line2' => 'address_line2-input',
446
+ 'address_line3' => 'address_line3-input',
447
+ 'city' => 'city-input',
448
+ 'company_name' => 'company_name-input',
449
+ 'country_code' => 'country_code-input',
450
+ 'created_at' => 'created_at-input',
451
+ 'email' => 'email-input',
452
+ 'family_name' => 'family_name-input',
453
+ 'given_name' => 'given_name-input',
454
+ 'id' => 'id-input',
455
+ 'language' => 'language-input',
456
+ 'metadata' => 'metadata-input',
457
+ 'postal_code' => 'postal_code-input',
458
+ 'region' => 'region-input',
459
+ 'swedish_identity_number' => 'swedish_identity_number-input'
460
+ }],
461
+ meta: {
462
+ limit: 2,
463
+ cursors: {}
464
+ }
465
+ }.to_json,
466
+ headers: response_headers
467
+ )
468
+
469
+ client.customers.all.to_a
470
+
471
+ expect(first_response_stub).to have_been_requested
472
+ expect(second_response_stub).to have_been_requested.twice
473
+ end
474
+
475
+ it 'retries 5XX errors' do
476
+ first_response_stub = stub_request(:get, %r{.*api.gocardless.com/customers$}).to_return(
477
+ body: {
478
+ 'customers' => [{
479
+
480
+ 'address_line1' => 'address_line1-input',
481
+ 'address_line2' => 'address_line2-input',
482
+ 'address_line3' => 'address_line3-input',
483
+ 'city' => 'city-input',
484
+ 'company_name' => 'company_name-input',
485
+ 'country_code' => 'country_code-input',
486
+ 'created_at' => 'created_at-input',
487
+ 'email' => 'email-input',
488
+ 'family_name' => 'family_name-input',
489
+ 'given_name' => 'given_name-input',
490
+ 'id' => 'id-input',
491
+ 'language' => 'language-input',
492
+ 'metadata' => 'metadata-input',
493
+ 'postal_code' => 'postal_code-input',
494
+ 'region' => 'region-input',
495
+ 'swedish_identity_number' => 'swedish_identity_number-input'
496
+ }],
497
+ meta: {
498
+ cursors: { after: 'AB345' },
499
+ limit: 1
500
+ }
501
+ }.to_json,
502
+ headers: response_headers
503
+ )
504
+
505
+ second_response_stub = stub_request(:get, %r{.*api.gocardless.com/customers\?after=AB345})
506
+ .to_return(
507
+ status: 502,
508
+ body: '<html><body>Response from Cloudflare</body></html>',
509
+ headers: { 'Content-Type' => 'text/html' }
510
+ ).then.to_return(
511
+ body: {
512
+ 'customers' => [{
513
+
514
+ 'address_line1' => 'address_line1-input',
515
+ 'address_line2' => 'address_line2-input',
516
+ 'address_line3' => 'address_line3-input',
517
+ 'city' => 'city-input',
518
+ 'company_name' => 'company_name-input',
519
+ 'country_code' => 'country_code-input',
520
+ 'created_at' => 'created_at-input',
521
+ 'email' => 'email-input',
522
+ 'family_name' => 'family_name-input',
523
+ 'given_name' => 'given_name-input',
524
+ 'id' => 'id-input',
525
+ 'language' => 'language-input',
526
+ 'metadata' => 'metadata-input',
527
+ 'postal_code' => 'postal_code-input',
528
+ 'region' => 'region-input',
529
+ 'swedish_identity_number' => 'swedish_identity_number-input'
530
+ }],
531
+ meta: {
532
+ limit: 2,
533
+ cursors: {}
534
+ }
535
+ }.to_json,
536
+ headers: response_headers
537
+ )
538
+
539
+ client.customers.all.to_a
540
+
541
+ expect(first_response_stub).to have_been_requested
542
+ expect(second_response_stub).to have_been_requested.twice
543
+ end
544
+ end
269
545
  end
270
546
 
271
547
  describe '#get' do
@@ -300,7 +576,7 @@ describe GoCardlessPro::Services::CustomersService do
300
576
  'swedish_identity_number' => 'swedish_identity_number-input'
301
577
  }
302
578
  }.to_json,
303
- headers: { 'Content-Type' => 'application/json' }
579
+ headers: response_headers
304
580
  )
305
581
  end
306
582
 
@@ -341,7 +617,7 @@ describe GoCardlessPro::Services::CustomersService do
341
617
  'swedish_identity_number' => 'swedish_identity_number-input'
342
618
  }
343
619
  }.to_json,
344
- headers: { 'Content-Type' => 'application/json' }
620
+ headers: response_headers
345
621
  )
346
622
  end
347
623
 
@@ -355,7 +631,7 @@ describe GoCardlessPro::Services::CustomersService do
355
631
  stub_url = '/customers/:identity'.gsub(':identity', id)
356
632
  stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
357
633
  body: '',
358
- headers: { 'Content-Type' => 'application/json' }
634
+ headers: response_headers
359
635
  )
360
636
  end
361
637
 
@@ -371,6 +647,33 @@ describe GoCardlessPro::Services::CustomersService do
371
647
  expect { get_response }.to_not raise_error(/bad URI/)
372
648
  end
373
649
  end
650
+
651
+ describe 'retry behaviour' do
652
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
653
+
654
+ it 'retries timeouts' do
655
+ stub_url = '/customers/:identity'.gsub(':identity', id)
656
+
657
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/)
658
+ .to_timeout.then.to_return(status: 200, headers: response_headers)
659
+
660
+ get_response
661
+ expect(stub).to have_been_requested.twice
662
+ end
663
+
664
+ it 'retries 5XX errors' do
665
+ stub_url = '/customers/:identity'.gsub(':identity', id)
666
+
667
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/)
668
+ .to_return(status: 502,
669
+ headers: { 'Content-Type' => 'text/html' },
670
+ body: '<html><body>Response from Cloudflare</body></html>')
671
+ .then.to_return(status: 200, headers: response_headers)
672
+
673
+ get_response
674
+ expect(stub).to have_been_requested.twice
675
+ end
676
+ end
374
677
  end
375
678
 
376
679
  describe '#update' do
@@ -404,7 +707,7 @@ describe GoCardlessPro::Services::CustomersService do
404
707
  'swedish_identity_number' => 'swedish_identity_number-input'
405
708
  }
406
709
  }.to_json,
407
- headers: { 'Content-Type' => 'application/json' }
710
+ headers: response_headers
408
711
  )
409
712
  end
410
713
 
@@ -412,6 +715,31 @@ describe GoCardlessPro::Services::CustomersService do
412
715
  expect(put_update_response).to be_a(GoCardlessPro::Resources::Customer)
413
716
  expect(stub).to have_been_requested
414
717
  end
718
+
719
+ describe 'retry behaviour' do
720
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
721
+
722
+ it 'retries timeouts' do
723
+ stub_url = '/customers/:identity'.gsub(':identity', id)
724
+ stub = stub_request(:put, /.*api.gocardless.com#{stub_url}/)
725
+ .to_timeout.then.to_return(status: 200, headers: response_headers)
726
+
727
+ put_update_response
728
+ expect(stub).to have_been_requested.twice
729
+ end
730
+
731
+ it 'retries 5XX errors' do
732
+ stub_url = '/customers/:identity'.gsub(':identity', id)
733
+ stub = stub_request(:put, /.*api.gocardless.com#{stub_url}/)
734
+ .to_return(status: 502,
735
+ headers: { 'Content-Type' => 'text/html' },
736
+ body: '<html><body>Response from Cloudflare</body></html>')
737
+ .then.to_return(status: 200, headers: response_headers)
738
+
739
+ put_update_response
740
+ expect(stub).to have_been_requested.twice
741
+ end
742
+ end
415
743
  end
416
744
  end
417
745
  end