gocardless_pro 2.25.0 → 2.27.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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +3 -3
  3. data/lib/gocardless_pro.rb +18 -0
  4. data/lib/gocardless_pro/client.rb +31 -1
  5. data/lib/gocardless_pro/resources/bank_authorisation.rb +87 -0
  6. data/lib/gocardless_pro/resources/billing_request.rb +86 -0
  7. data/lib/gocardless_pro/resources/billing_request_flow.rb +62 -0
  8. data/lib/gocardless_pro/resources/creditor.rb +2 -3
  9. data/lib/gocardless_pro/resources/institution.rb +45 -0
  10. data/lib/gocardless_pro/resources/payer_authorisation.rb +3 -0
  11. data/lib/gocardless_pro/resources/scenario_simulator.rb +42 -0
  12. data/lib/gocardless_pro/resources/webhook.rb +62 -0
  13. data/lib/gocardless_pro/services/bank_authorisations_service.rb +82 -0
  14. data/lib/gocardless_pro/services/billing_request_flows_service.rb +47 -0
  15. data/lib/gocardless_pro/services/billing_requests_service.rb +325 -0
  16. data/lib/gocardless_pro/services/institutions_service.rb +56 -0
  17. data/lib/gocardless_pro/services/payer_authorisations_service.rb +5 -5
  18. data/lib/gocardless_pro/services/scenario_simulators_service.rb +148 -0
  19. data/lib/gocardless_pro/services/subscriptions_service.rb +8 -3
  20. data/lib/gocardless_pro/services/webhooks_service.rb +113 -0
  21. data/lib/gocardless_pro/version.rb +1 -1
  22. data/spec/resources/bank_authorisation_spec.rb +259 -0
  23. data/spec/resources/billing_request_flow_spec.rb +129 -0
  24. data/spec/resources/billing_request_spec.rb +736 -0
  25. data/spec/resources/institution_spec.rb +103 -0
  26. data/spec/resources/scenario_simulator_spec.rb +63 -0
  27. data/spec/resources/webhook_spec.rb +323 -0
  28. data/spec/services/bank_authorisations_service_spec.rb +366 -0
  29. data/spec/services/billing_request_flows_service_spec.rb +152 -0
  30. data/spec/services/billing_requests_service_spec.rb +1042 -0
  31. data/spec/services/institutions_service_spec.rb +223 -0
  32. data/spec/services/scenario_simulators_service_spec.rb +74 -0
  33. data/spec/services/webhooks_service_spec.rb +545 -0
  34. metadata +39 -3
@@ -0,0 +1,1042 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoCardlessPro::Services::BillingRequestsService 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.billing_requests.list }
15
+
16
+ let(:body) do
17
+ {
18
+ 'billing_requests' => [{
19
+
20
+ 'actions' => 'actions-input',
21
+ 'auto_fulfil' => 'auto_fulfil-input',
22
+ 'created_at' => 'created_at-input',
23
+ 'id' => 'id-input',
24
+ 'links' => 'links-input',
25
+ 'mandate_request' => 'mandate_request-input',
26
+ 'metadata' => 'metadata-input',
27
+ 'payment_request' => 'payment_request-input',
28
+ 'resources' => 'resources-input',
29
+ 'status' => 'status-input',
30
+ }],
31
+ meta: {
32
+ cursors: {
33
+ before: nil,
34
+ after: 'ABC123',
35
+ },
36
+ },
37
+ }.to_json
38
+ end
39
+
40
+ before do
41
+ stub_request(:get, %r{.*api.gocardless.com/billing_requests}).to_return(
42
+ body: body,
43
+ headers: response_headers
44
+ )
45
+ end
46
+
47
+ it 'wraps each item in the resource class' do
48
+ expect(get_list_response.records.map(&:class).uniq.first).to eq(GoCardlessPro::Resources::BillingRequest)
49
+
50
+ expect(get_list_response.records.first.actions).to eq('actions-input')
51
+
52
+ expect(get_list_response.records.first.auto_fulfil).to eq('auto_fulfil-input')
53
+
54
+ expect(get_list_response.records.first.created_at).to eq('created_at-input')
55
+
56
+ expect(get_list_response.records.first.id).to eq('id-input')
57
+
58
+ expect(get_list_response.records.first.mandate_request).to eq('mandate_request-input')
59
+
60
+ expect(get_list_response.records.first.metadata).to eq('metadata-input')
61
+
62
+ expect(get_list_response.records.first.payment_request).to eq('payment_request-input')
63
+
64
+ expect(get_list_response.records.first.resources).to eq('resources-input')
65
+
66
+ expect(get_list_response.records.first.status).to eq('status-input')
67
+ end
68
+
69
+ it 'exposes the cursors for before and after' do
70
+ expect(get_list_response.before).to eq(nil)
71
+ expect(get_list_response.after).to eq('ABC123')
72
+ end
73
+
74
+ specify { expect(get_list_response.api_response.headers).to eql('content-type' => 'application/json') }
75
+
76
+ describe 'retry behaviour' do
77
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
78
+
79
+ it 'retries timeouts' do
80
+ stub = stub_request(:get, %r{.*api.gocardless.com/billing_requests}).
81
+ to_timeout.then.to_return(status: 200, headers: response_headers, body: body)
82
+
83
+ get_list_response
84
+ expect(stub).to have_been_requested.twice
85
+ end
86
+
87
+ it 'retries 5XX errors' do
88
+ stub = stub_request(:get, %r{.*api.gocardless.com/billing_requests}).
89
+ to_return(status: 502,
90
+ headers: { 'Content-Type' => 'text/html' },
91
+ body: '<html><body>Response from Cloudflare</body></html>').
92
+ then.to_return(status: 200, headers: response_headers, body: body)
93
+
94
+ get_list_response
95
+ expect(stub).to have_been_requested.twice
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ describe '#all' do
102
+ let!(:first_response_stub) do
103
+ stub_request(:get, %r{.*api.gocardless.com/billing_requests$}).to_return(
104
+ body: {
105
+ 'billing_requests' => [{
106
+
107
+ 'actions' => 'actions-input',
108
+ 'auto_fulfil' => 'auto_fulfil-input',
109
+ 'created_at' => 'created_at-input',
110
+ 'id' => 'id-input',
111
+ 'links' => 'links-input',
112
+ 'mandate_request' => 'mandate_request-input',
113
+ 'metadata' => 'metadata-input',
114
+ 'payment_request' => 'payment_request-input',
115
+ 'resources' => 'resources-input',
116
+ 'status' => 'status-input',
117
+ }],
118
+ meta: {
119
+ cursors: { after: 'AB345' },
120
+ limit: 1,
121
+ },
122
+ }.to_json,
123
+ headers: response_headers
124
+ )
125
+ end
126
+
127
+ let!(:second_response_stub) do
128
+ stub_request(:get, %r{.*api.gocardless.com/billing_requests\?after=AB345}).to_return(
129
+ body: {
130
+ 'billing_requests' => [{
131
+
132
+ 'actions' => 'actions-input',
133
+ 'auto_fulfil' => 'auto_fulfil-input',
134
+ 'created_at' => 'created_at-input',
135
+ 'id' => 'id-input',
136
+ 'links' => 'links-input',
137
+ 'mandate_request' => 'mandate_request-input',
138
+ 'metadata' => 'metadata-input',
139
+ 'payment_request' => 'payment_request-input',
140
+ 'resources' => 'resources-input',
141
+ 'status' => 'status-input',
142
+ }],
143
+ meta: {
144
+ limit: 2,
145
+ cursors: {},
146
+ },
147
+ }.to_json,
148
+ headers: response_headers
149
+ )
150
+ end
151
+
152
+ it 'automatically makes the extra requests' do
153
+ expect(client.billing_requests.all.to_a.length).to eq(2)
154
+ expect(first_response_stub).to have_been_requested
155
+ expect(second_response_stub).to have_been_requested
156
+ end
157
+
158
+ describe 'retry behaviour' do
159
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
160
+
161
+ it 'retries timeouts' do
162
+ first_response_stub = stub_request(:get, %r{.*api.gocardless.com/billing_requests$}).to_return(
163
+ body: {
164
+ 'billing_requests' => [{
165
+
166
+ 'actions' => 'actions-input',
167
+ 'auto_fulfil' => 'auto_fulfil-input',
168
+ 'created_at' => 'created_at-input',
169
+ 'id' => 'id-input',
170
+ 'links' => 'links-input',
171
+ 'mandate_request' => 'mandate_request-input',
172
+ 'metadata' => 'metadata-input',
173
+ 'payment_request' => 'payment_request-input',
174
+ 'resources' => 'resources-input',
175
+ 'status' => 'status-input',
176
+ }],
177
+ meta: {
178
+ cursors: { after: 'AB345' },
179
+ limit: 1,
180
+ },
181
+ }.to_json,
182
+ headers: response_headers
183
+ )
184
+
185
+ second_response_stub = stub_request(:get, %r{.*api.gocardless.com/billing_requests\?after=AB345}).
186
+ to_timeout.then.
187
+ to_return(
188
+ body: {
189
+ 'billing_requests' => [{
190
+
191
+ 'actions' => 'actions-input',
192
+ 'auto_fulfil' => 'auto_fulfil-input',
193
+ 'created_at' => 'created_at-input',
194
+ 'id' => 'id-input',
195
+ 'links' => 'links-input',
196
+ 'mandate_request' => 'mandate_request-input',
197
+ 'metadata' => 'metadata-input',
198
+ 'payment_request' => 'payment_request-input',
199
+ 'resources' => 'resources-input',
200
+ 'status' => 'status-input',
201
+ }],
202
+ meta: {
203
+ limit: 2,
204
+ cursors: {},
205
+ },
206
+ }.to_json,
207
+ headers: response_headers
208
+ )
209
+
210
+ client.billing_requests.all.to_a
211
+
212
+ expect(first_response_stub).to have_been_requested
213
+ expect(second_response_stub).to have_been_requested.twice
214
+ end
215
+
216
+ it 'retries 5XX errors' do
217
+ first_response_stub = stub_request(:get, %r{.*api.gocardless.com/billing_requests$}).to_return(
218
+ body: {
219
+ 'billing_requests' => [{
220
+
221
+ 'actions' => 'actions-input',
222
+ 'auto_fulfil' => 'auto_fulfil-input',
223
+ 'created_at' => 'created_at-input',
224
+ 'id' => 'id-input',
225
+ 'links' => 'links-input',
226
+ 'mandate_request' => 'mandate_request-input',
227
+ 'metadata' => 'metadata-input',
228
+ 'payment_request' => 'payment_request-input',
229
+ 'resources' => 'resources-input',
230
+ 'status' => 'status-input',
231
+ }],
232
+ meta: {
233
+ cursors: { after: 'AB345' },
234
+ limit: 1,
235
+ },
236
+ }.to_json,
237
+ headers: response_headers
238
+ )
239
+
240
+ second_response_stub = stub_request(:get, %r{.*api.gocardless.com/billing_requests\?after=AB345}).
241
+ to_return(
242
+ status: 502,
243
+ body: '<html><body>Response from Cloudflare</body></html>',
244
+ headers: { 'Content-Type' => 'text/html' }
245
+ ).then.to_return(
246
+ body: {
247
+ 'billing_requests' => [{
248
+
249
+ 'actions' => 'actions-input',
250
+ 'auto_fulfil' => 'auto_fulfil-input',
251
+ 'created_at' => 'created_at-input',
252
+ 'id' => 'id-input',
253
+ 'links' => 'links-input',
254
+ 'mandate_request' => 'mandate_request-input',
255
+ 'metadata' => 'metadata-input',
256
+ 'payment_request' => 'payment_request-input',
257
+ 'resources' => 'resources-input',
258
+ 'status' => 'status-input',
259
+ }],
260
+ meta: {
261
+ limit: 2,
262
+ cursors: {},
263
+ },
264
+ }.to_json,
265
+ headers: response_headers
266
+ )
267
+
268
+ client.billing_requests.all.to_a
269
+
270
+ expect(first_response_stub).to have_been_requested
271
+ expect(second_response_stub).to have_been_requested.twice
272
+ end
273
+ end
274
+ end
275
+
276
+ describe '#create' do
277
+ subject(:post_create_response) { client.billing_requests.create(params: new_resource) }
278
+ context 'with a valid request' do
279
+ let(:new_resource) do
280
+ {
281
+
282
+ 'actions' => 'actions-input',
283
+ 'auto_fulfil' => 'auto_fulfil-input',
284
+ 'created_at' => 'created_at-input',
285
+ 'id' => 'id-input',
286
+ 'links' => 'links-input',
287
+ 'mandate_request' => 'mandate_request-input',
288
+ 'metadata' => 'metadata-input',
289
+ 'payment_request' => 'payment_request-input',
290
+ 'resources' => 'resources-input',
291
+ 'status' => 'status-input',
292
+ }
293
+ end
294
+
295
+ before do
296
+ stub_request(:post, %r{.*api.gocardless.com/billing_requests}).
297
+ with(
298
+ body: {
299
+ 'billing_requests' => {
300
+
301
+ 'actions' => 'actions-input',
302
+ 'auto_fulfil' => 'auto_fulfil-input',
303
+ 'created_at' => 'created_at-input',
304
+ 'id' => 'id-input',
305
+ 'links' => 'links-input',
306
+ 'mandate_request' => 'mandate_request-input',
307
+ 'metadata' => 'metadata-input',
308
+ 'payment_request' => 'payment_request-input',
309
+ 'resources' => 'resources-input',
310
+ 'status' => 'status-input',
311
+ },
312
+ }
313
+ ).
314
+ to_return(
315
+ body: {
316
+ 'billing_requests' =>
317
+
318
+ {
319
+
320
+ 'actions' => 'actions-input',
321
+ 'auto_fulfil' => 'auto_fulfil-input',
322
+ 'created_at' => 'created_at-input',
323
+ 'id' => 'id-input',
324
+ 'links' => 'links-input',
325
+ 'mandate_request' => 'mandate_request-input',
326
+ 'metadata' => 'metadata-input',
327
+ 'payment_request' => 'payment_request-input',
328
+ 'resources' => 'resources-input',
329
+ 'status' => 'status-input',
330
+ },
331
+
332
+ }.to_json,
333
+ headers: response_headers
334
+ )
335
+ end
336
+
337
+ it 'creates and returns the resource' do
338
+ expect(post_create_response).to be_a(GoCardlessPro::Resources::BillingRequest)
339
+ end
340
+
341
+ describe 'retry behaviour' do
342
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
343
+
344
+ it 'retries timeouts' do
345
+ stub = stub_request(:post, %r{.*api.gocardless.com/billing_requests}).
346
+ to_timeout.then.to_return(status: 200, headers: response_headers)
347
+
348
+ post_create_response
349
+ expect(stub).to have_been_requested.twice
350
+ end
351
+
352
+ it 'retries 5XX errors' do
353
+ stub = stub_request(:post, %r{.*api.gocardless.com/billing_requests}).
354
+ to_return(status: 502,
355
+ headers: { 'Content-Type' => 'text/html' },
356
+ body: '<html><body>Response from Cloudflare</body></html>').
357
+ then.to_return(status: 200, headers: response_headers)
358
+
359
+ post_create_response
360
+ expect(stub).to have_been_requested.twice
361
+ end
362
+ end
363
+ end
364
+
365
+ context 'with a request that returns a validation error' do
366
+ let(:new_resource) { {} }
367
+
368
+ before do
369
+ stub_request(:post, %r{.*api.gocardless.com/billing_requests}).to_return(
370
+ body: {
371
+ error: {
372
+ type: 'validation_failed',
373
+ code: 422,
374
+ errors: [
375
+ { message: 'test error message', field: 'test_field' },
376
+ ],
377
+ },
378
+ }.to_json,
379
+ headers: response_headers,
380
+ status: 422
381
+ )
382
+ end
383
+
384
+ it 'throws the correct error' do
385
+ expect { post_create_response }.to raise_error(GoCardlessPro::ValidationError)
386
+ end
387
+ end
388
+
389
+ context 'with a request that returns an idempotent creation conflict error' do
390
+ let(:id) { 'ID123' }
391
+
392
+ let(:new_resource) do
393
+ {
394
+
395
+ 'actions' => 'actions-input',
396
+ 'auto_fulfil' => 'auto_fulfil-input',
397
+ 'created_at' => 'created_at-input',
398
+ 'id' => 'id-input',
399
+ 'links' => 'links-input',
400
+ 'mandate_request' => 'mandate_request-input',
401
+ 'metadata' => 'metadata-input',
402
+ 'payment_request' => 'payment_request-input',
403
+ 'resources' => 'resources-input',
404
+ 'status' => 'status-input',
405
+ }
406
+ end
407
+
408
+ let!(:post_stub) do
409
+ stub_request(:post, %r{.*api.gocardless.com/billing_requests}).to_return(
410
+ body: {
411
+ error: {
412
+ type: 'invalid_state',
413
+ code: 409,
414
+ errors: [
415
+ {
416
+ message: 'A resource has already been created with this idempotency key',
417
+ reason: 'idempotent_creation_conflict',
418
+ links: {
419
+ conflicting_resource_id: id,
420
+ },
421
+ },
422
+ ],
423
+ },
424
+ }.to_json,
425
+ headers: response_headers,
426
+ status: 409
427
+ )
428
+ end
429
+
430
+ let!(:get_stub) do
431
+ stub_url = "/billing_requests/#{id}"
432
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).
433
+ to_return(
434
+ body: {
435
+ 'billing_requests' => {
436
+
437
+ 'actions' => 'actions-input',
438
+ 'auto_fulfil' => 'auto_fulfil-input',
439
+ 'created_at' => 'created_at-input',
440
+ 'id' => 'id-input',
441
+ 'links' => 'links-input',
442
+ 'mandate_request' => 'mandate_request-input',
443
+ 'metadata' => 'metadata-input',
444
+ 'payment_request' => 'payment_request-input',
445
+ 'resources' => 'resources-input',
446
+ 'status' => 'status-input',
447
+ },
448
+ }.to_json,
449
+ headers: response_headers
450
+ )
451
+ end
452
+
453
+ context 'with default behaviour' do
454
+ it 'fetches the already-created resource' do
455
+ post_create_response
456
+ expect(post_stub).to have_been_requested
457
+ expect(get_stub).to have_been_requested
458
+ end
459
+ end
460
+
461
+ context 'with on_idempotency_conflict: :raise' do
462
+ let(:client) do
463
+ GoCardlessPro::Client.new(
464
+ access_token: 'SECRET_TOKEN',
465
+ on_idempotency_conflict: :raise
466
+ )
467
+ end
468
+
469
+ it 'raises an IdempotencyConflict error' do
470
+ expect { post_create_response }.
471
+ to raise_error(GoCardlessPro::IdempotencyConflict)
472
+ end
473
+ end
474
+
475
+ context 'with on_idempotency_conflict: :unknown' do
476
+ let(:client) do
477
+ GoCardlessPro::Client.new(
478
+ access_token: 'SECRET_TOKEN',
479
+ on_idempotency_conflict: :unknown
480
+ )
481
+ end
482
+
483
+ it 'raises an ArgumentError' do
484
+ expect { post_create_response }.to raise_error(ArgumentError)
485
+ end
486
+ end
487
+ end
488
+ end
489
+
490
+ describe '#get' do
491
+ let(:id) { 'ID123' }
492
+
493
+ subject(:get_response) { client.billing_requests.get(id) }
494
+
495
+ context 'passing in a custom header' do
496
+ let!(:stub) do
497
+ stub_url = '/billing_requests/:identity'.gsub(':identity', id)
498
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).
499
+ with(headers: { 'Foo' => 'Bar' }).
500
+ to_return(
501
+ body: {
502
+ 'billing_requests' => {
503
+
504
+ 'actions' => 'actions-input',
505
+ 'auto_fulfil' => 'auto_fulfil-input',
506
+ 'created_at' => 'created_at-input',
507
+ 'id' => 'id-input',
508
+ 'links' => 'links-input',
509
+ 'mandate_request' => 'mandate_request-input',
510
+ 'metadata' => 'metadata-input',
511
+ 'payment_request' => 'payment_request-input',
512
+ 'resources' => 'resources-input',
513
+ 'status' => 'status-input',
514
+ },
515
+ }.to_json,
516
+ headers: response_headers
517
+ )
518
+ end
519
+
520
+ subject(:get_response) do
521
+ client.billing_requests.get(id, headers: {
522
+ 'Foo' => 'Bar',
523
+ })
524
+ end
525
+
526
+ it 'includes the header' do
527
+ get_response
528
+ expect(stub).to have_been_requested
529
+ end
530
+ end
531
+
532
+ context 'when there is a billing_request to return' do
533
+ before do
534
+ stub_url = '/billing_requests/:identity'.gsub(':identity', id)
535
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
536
+ body: {
537
+ 'billing_requests' => {
538
+
539
+ 'actions' => 'actions-input',
540
+ 'auto_fulfil' => 'auto_fulfil-input',
541
+ 'created_at' => 'created_at-input',
542
+ 'id' => 'id-input',
543
+ 'links' => 'links-input',
544
+ 'mandate_request' => 'mandate_request-input',
545
+ 'metadata' => 'metadata-input',
546
+ 'payment_request' => 'payment_request-input',
547
+ 'resources' => 'resources-input',
548
+ 'status' => 'status-input',
549
+ },
550
+ }.to_json,
551
+ headers: response_headers
552
+ )
553
+ end
554
+
555
+ it 'wraps the response in a resource' do
556
+ expect(get_response).to be_a(GoCardlessPro::Resources::BillingRequest)
557
+ end
558
+ end
559
+
560
+ context 'when nothing is returned' do
561
+ before do
562
+ stub_url = '/billing_requests/:identity'.gsub(':identity', id)
563
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
564
+ body: '',
565
+ headers: response_headers
566
+ )
567
+ end
568
+
569
+ it 'returns nil' do
570
+ expect(get_response).to be_nil
571
+ end
572
+ end
573
+
574
+ context "when an ID is specified which can't be included in a valid URI" do
575
+ let(:id) { '`' }
576
+
577
+ it "doesn't raise an error" do
578
+ expect { get_response }.to_not raise_error(/bad URI/)
579
+ end
580
+ end
581
+
582
+ describe 'retry behaviour' do
583
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
584
+
585
+ it 'retries timeouts' do
586
+ stub_url = '/billing_requests/:identity'.gsub(':identity', id)
587
+
588
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/).
589
+ to_timeout.then.to_return(status: 200, headers: response_headers)
590
+
591
+ get_response
592
+ expect(stub).to have_been_requested.twice
593
+ end
594
+
595
+ it 'retries 5XX errors, other than 500s' do
596
+ stub_url = '/billing_requests/:identity'.gsub(':identity', id)
597
+
598
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/).
599
+ to_return(status: 502,
600
+ headers: { 'Content-Type' => 'text/html' },
601
+ body: '<html><body>Response from Cloudflare</body></html>').
602
+ then.to_return(status: 200, headers: response_headers)
603
+
604
+ get_response
605
+ expect(stub).to have_been_requested.twice
606
+ end
607
+
608
+ it 'retries 500 errors returned by the API' do
609
+ stub_url = '/billing_requests/:identity'.gsub(':identity', id)
610
+
611
+ gocardless_error = {
612
+ 'error' => {
613
+ 'message' => 'Internal server error',
614
+ 'documentation_url' => 'https://developer.gocardless.com/#gocardless',
615
+ 'errors' => [{
616
+ 'message' => 'Internal server error',
617
+ 'reason' => 'internal_server_error',
618
+ }],
619
+ 'type' => 'gocardless',
620
+ 'code' => 500,
621
+ 'request_id' => 'dummy_request_id',
622
+ 'id' => 'dummy_exception_id',
623
+ },
624
+ }
625
+
626
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/).
627
+ to_return(status: 500,
628
+ headers: response_headers,
629
+ body: gocardless_error.to_json).
630
+ then.to_return(status: 200, headers: response_headers)
631
+
632
+ get_response
633
+ expect(stub).to have_been_requested.twice
634
+ end
635
+ end
636
+ end
637
+
638
+ describe '#collect_customer_details' do
639
+ subject(:post_response) { client.billing_requests.collect_customer_details(resource_id) }
640
+
641
+ let(:resource_id) { 'ABC123' }
642
+
643
+ let!(:stub) do
644
+ # /billing_requests/%v/actions/collect_customer_details
645
+ stub_url = '/billing_requests/:identity/actions/collect_customer_details'.gsub(':identity', resource_id)
646
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).to_return(
647
+ body: {
648
+ 'billing_requests' => {
649
+
650
+ 'actions' => 'actions-input',
651
+ 'auto_fulfil' => 'auto_fulfil-input',
652
+ 'created_at' => 'created_at-input',
653
+ 'id' => 'id-input',
654
+ 'links' => 'links-input',
655
+ 'mandate_request' => 'mandate_request-input',
656
+ 'metadata' => 'metadata-input',
657
+ 'payment_request' => 'payment_request-input',
658
+ 'resources' => 'resources-input',
659
+ 'status' => 'status-input',
660
+ },
661
+ }.to_json,
662
+ headers: response_headers
663
+ )
664
+ end
665
+
666
+ it 'wraps the response and calls the right endpoint' do
667
+ expect(post_response).to be_a(GoCardlessPro::Resources::BillingRequest)
668
+
669
+ expect(stub).to have_been_requested
670
+ end
671
+
672
+ describe 'retry behaviour' do
673
+ it "doesn't retry errors" do
674
+ stub_url = '/billing_requests/:identity/actions/collect_customer_details'.gsub(':identity', resource_id)
675
+ stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
676
+ to_timeout
677
+
678
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
679
+ expect(stub).to have_been_requested
680
+ end
681
+ end
682
+
683
+ context 'when the request needs a body and custom header' do
684
+ let(:body) { { foo: 'bar' } }
685
+ let(:headers) { { 'Foo' => 'Bar' } }
686
+ subject(:post_response) { client.billing_requests.collect_customer_details(resource_id, body, headers) }
687
+
688
+ let(:resource_id) { 'ABC123' }
689
+
690
+ let!(:stub) do
691
+ # /billing_requests/%v/actions/collect_customer_details
692
+ stub_url = '/billing_requests/:identity/actions/collect_customer_details'.gsub(':identity', resource_id)
693
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).
694
+ with(
695
+ body: { foo: 'bar' },
696
+ headers: { 'Foo' => 'Bar' }
697
+ ).to_return(
698
+ body: {
699
+ 'billing_requests' => {
700
+
701
+ 'actions' => 'actions-input',
702
+ 'auto_fulfil' => 'auto_fulfil-input',
703
+ 'created_at' => 'created_at-input',
704
+ 'id' => 'id-input',
705
+ 'links' => 'links-input',
706
+ 'mandate_request' => 'mandate_request-input',
707
+ 'metadata' => 'metadata-input',
708
+ 'payment_request' => 'payment_request-input',
709
+ 'resources' => 'resources-input',
710
+ 'status' => 'status-input',
711
+ },
712
+ }.to_json,
713
+ headers: response_headers
714
+ )
715
+ end
716
+ end
717
+ end
718
+
719
+ describe '#collect_bank_account_details' do
720
+ subject(:post_response) { client.billing_requests.collect_bank_account_details(resource_id) }
721
+
722
+ let(:resource_id) { 'ABC123' }
723
+
724
+ let!(:stub) do
725
+ # /billing_requests/%v/actions/collect_bank_account_details
726
+ stub_url = '/billing_requests/:identity/actions/collect_bank_account_details'.gsub(':identity', resource_id)
727
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).to_return(
728
+ body: {
729
+ 'billing_requests' => {
730
+
731
+ 'actions' => 'actions-input',
732
+ 'auto_fulfil' => 'auto_fulfil-input',
733
+ 'created_at' => 'created_at-input',
734
+ 'id' => 'id-input',
735
+ 'links' => 'links-input',
736
+ 'mandate_request' => 'mandate_request-input',
737
+ 'metadata' => 'metadata-input',
738
+ 'payment_request' => 'payment_request-input',
739
+ 'resources' => 'resources-input',
740
+ 'status' => 'status-input',
741
+ },
742
+ }.to_json,
743
+ headers: response_headers
744
+ )
745
+ end
746
+
747
+ it 'wraps the response and calls the right endpoint' do
748
+ expect(post_response).to be_a(GoCardlessPro::Resources::BillingRequest)
749
+
750
+ expect(stub).to have_been_requested
751
+ end
752
+
753
+ describe 'retry behaviour' do
754
+ it "doesn't retry errors" do
755
+ stub_url = '/billing_requests/:identity/actions/collect_bank_account_details'.gsub(':identity', resource_id)
756
+ stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
757
+ to_timeout
758
+
759
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
760
+ expect(stub).to have_been_requested
761
+ end
762
+ end
763
+
764
+ context 'when the request needs a body and custom header' do
765
+ let(:body) { { foo: 'bar' } }
766
+ let(:headers) { { 'Foo' => 'Bar' } }
767
+ subject(:post_response) { client.billing_requests.collect_bank_account_details(resource_id, body, headers) }
768
+
769
+ let(:resource_id) { 'ABC123' }
770
+
771
+ let!(:stub) do
772
+ # /billing_requests/%v/actions/collect_bank_account_details
773
+ stub_url = '/billing_requests/:identity/actions/collect_bank_account_details'.gsub(':identity', resource_id)
774
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).
775
+ with(
776
+ body: { foo: 'bar' },
777
+ headers: { 'Foo' => 'Bar' }
778
+ ).to_return(
779
+ body: {
780
+ 'billing_requests' => {
781
+
782
+ 'actions' => 'actions-input',
783
+ 'auto_fulfil' => 'auto_fulfil-input',
784
+ 'created_at' => 'created_at-input',
785
+ 'id' => 'id-input',
786
+ 'links' => 'links-input',
787
+ 'mandate_request' => 'mandate_request-input',
788
+ 'metadata' => 'metadata-input',
789
+ 'payment_request' => 'payment_request-input',
790
+ 'resources' => 'resources-input',
791
+ 'status' => 'status-input',
792
+ },
793
+ }.to_json,
794
+ headers: response_headers
795
+ )
796
+ end
797
+ end
798
+ end
799
+
800
+ describe '#fulfil' do
801
+ subject(:post_response) { client.billing_requests.fulfil(resource_id) }
802
+
803
+ let(:resource_id) { 'ABC123' }
804
+
805
+ let!(:stub) do
806
+ # /billing_requests/%v/actions/fulfil
807
+ stub_url = '/billing_requests/:identity/actions/fulfil'.gsub(':identity', resource_id)
808
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).to_return(
809
+ body: {
810
+ 'billing_requests' => {
811
+
812
+ 'actions' => 'actions-input',
813
+ 'auto_fulfil' => 'auto_fulfil-input',
814
+ 'created_at' => 'created_at-input',
815
+ 'id' => 'id-input',
816
+ 'links' => 'links-input',
817
+ 'mandate_request' => 'mandate_request-input',
818
+ 'metadata' => 'metadata-input',
819
+ 'payment_request' => 'payment_request-input',
820
+ 'resources' => 'resources-input',
821
+ 'status' => 'status-input',
822
+ },
823
+ }.to_json,
824
+ headers: response_headers
825
+ )
826
+ end
827
+
828
+ it 'wraps the response and calls the right endpoint' do
829
+ expect(post_response).to be_a(GoCardlessPro::Resources::BillingRequest)
830
+
831
+ expect(stub).to have_been_requested
832
+ end
833
+
834
+ describe 'retry behaviour' do
835
+ it "doesn't retry errors" do
836
+ stub_url = '/billing_requests/:identity/actions/fulfil'.gsub(':identity', resource_id)
837
+ stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
838
+ to_timeout
839
+
840
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
841
+ expect(stub).to have_been_requested
842
+ end
843
+ end
844
+
845
+ context 'when the request needs a body and custom header' do
846
+ let(:body) { { foo: 'bar' } }
847
+ let(:headers) { { 'Foo' => 'Bar' } }
848
+ subject(:post_response) { client.billing_requests.fulfil(resource_id, body, headers) }
849
+
850
+ let(:resource_id) { 'ABC123' }
851
+
852
+ let!(:stub) do
853
+ # /billing_requests/%v/actions/fulfil
854
+ stub_url = '/billing_requests/:identity/actions/fulfil'.gsub(':identity', resource_id)
855
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).
856
+ with(
857
+ body: { foo: 'bar' },
858
+ headers: { 'Foo' => 'Bar' }
859
+ ).to_return(
860
+ body: {
861
+ 'billing_requests' => {
862
+
863
+ 'actions' => 'actions-input',
864
+ 'auto_fulfil' => 'auto_fulfil-input',
865
+ 'created_at' => 'created_at-input',
866
+ 'id' => 'id-input',
867
+ 'links' => 'links-input',
868
+ 'mandate_request' => 'mandate_request-input',
869
+ 'metadata' => 'metadata-input',
870
+ 'payment_request' => 'payment_request-input',
871
+ 'resources' => 'resources-input',
872
+ 'status' => 'status-input',
873
+ },
874
+ }.to_json,
875
+ headers: response_headers
876
+ )
877
+ end
878
+ end
879
+ end
880
+
881
+ describe '#cancel' do
882
+ subject(:post_response) { client.billing_requests.cancel(resource_id) }
883
+
884
+ let(:resource_id) { 'ABC123' }
885
+
886
+ let!(:stub) do
887
+ # /billing_requests/%v/actions/cancel
888
+ stub_url = '/billing_requests/:identity/actions/cancel'.gsub(':identity', resource_id)
889
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).to_return(
890
+ body: {
891
+ 'billing_requests' => {
892
+
893
+ 'actions' => 'actions-input',
894
+ 'auto_fulfil' => 'auto_fulfil-input',
895
+ 'created_at' => 'created_at-input',
896
+ 'id' => 'id-input',
897
+ 'links' => 'links-input',
898
+ 'mandate_request' => 'mandate_request-input',
899
+ 'metadata' => 'metadata-input',
900
+ 'payment_request' => 'payment_request-input',
901
+ 'resources' => 'resources-input',
902
+ 'status' => 'status-input',
903
+ },
904
+ }.to_json,
905
+ headers: response_headers
906
+ )
907
+ end
908
+
909
+ it 'wraps the response and calls the right endpoint' do
910
+ expect(post_response).to be_a(GoCardlessPro::Resources::BillingRequest)
911
+
912
+ expect(stub).to have_been_requested
913
+ end
914
+
915
+ describe 'retry behaviour' do
916
+ it "doesn't retry errors" do
917
+ stub_url = '/billing_requests/:identity/actions/cancel'.gsub(':identity', resource_id)
918
+ stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
919
+ to_timeout
920
+
921
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
922
+ expect(stub).to have_been_requested
923
+ end
924
+ end
925
+
926
+ context 'when the request needs a body and custom header' do
927
+ let(:body) { { foo: 'bar' } }
928
+ let(:headers) { { 'Foo' => 'Bar' } }
929
+ subject(:post_response) { client.billing_requests.cancel(resource_id, body, headers) }
930
+
931
+ let(:resource_id) { 'ABC123' }
932
+
933
+ let!(:stub) do
934
+ # /billing_requests/%v/actions/cancel
935
+ stub_url = '/billing_requests/:identity/actions/cancel'.gsub(':identity', resource_id)
936
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).
937
+ with(
938
+ body: { foo: 'bar' },
939
+ headers: { 'Foo' => 'Bar' }
940
+ ).to_return(
941
+ body: {
942
+ 'billing_requests' => {
943
+
944
+ 'actions' => 'actions-input',
945
+ 'auto_fulfil' => 'auto_fulfil-input',
946
+ 'created_at' => 'created_at-input',
947
+ 'id' => 'id-input',
948
+ 'links' => 'links-input',
949
+ 'mandate_request' => 'mandate_request-input',
950
+ 'metadata' => 'metadata-input',
951
+ 'payment_request' => 'payment_request-input',
952
+ 'resources' => 'resources-input',
953
+ 'status' => 'status-input',
954
+ },
955
+ }.to_json,
956
+ headers: response_headers
957
+ )
958
+ end
959
+ end
960
+ end
961
+
962
+ describe '#notify' do
963
+ subject(:post_response) { client.billing_requests.notify(resource_id) }
964
+
965
+ let(:resource_id) { 'ABC123' }
966
+
967
+ let!(:stub) do
968
+ # /billing_requests/%v/actions/notify
969
+ stub_url = '/billing_requests/:identity/actions/notify'.gsub(':identity', resource_id)
970
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).to_return(
971
+ body: {
972
+ 'billing_requests' => {
973
+
974
+ 'actions' => 'actions-input',
975
+ 'auto_fulfil' => 'auto_fulfil-input',
976
+ 'created_at' => 'created_at-input',
977
+ 'id' => 'id-input',
978
+ 'links' => 'links-input',
979
+ 'mandate_request' => 'mandate_request-input',
980
+ 'metadata' => 'metadata-input',
981
+ 'payment_request' => 'payment_request-input',
982
+ 'resources' => 'resources-input',
983
+ 'status' => 'status-input',
984
+ },
985
+ }.to_json,
986
+ headers: response_headers
987
+ )
988
+ end
989
+
990
+ it 'wraps the response and calls the right endpoint' do
991
+ expect(post_response).to be_a(GoCardlessPro::Resources::BillingRequest)
992
+
993
+ expect(stub).to have_been_requested
994
+ end
995
+
996
+ describe 'retry behaviour' do
997
+ it "doesn't retry errors" do
998
+ stub_url = '/billing_requests/:identity/actions/notify'.gsub(':identity', resource_id)
999
+ stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
1000
+ to_timeout
1001
+
1002
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
1003
+ expect(stub).to have_been_requested
1004
+ end
1005
+ end
1006
+
1007
+ context 'when the request needs a body and custom header' do
1008
+ let(:body) { { foo: 'bar' } }
1009
+ let(:headers) { { 'Foo' => 'Bar' } }
1010
+ subject(:post_response) { client.billing_requests.notify(resource_id, body, headers) }
1011
+
1012
+ let(:resource_id) { 'ABC123' }
1013
+
1014
+ let!(:stub) do
1015
+ # /billing_requests/%v/actions/notify
1016
+ stub_url = '/billing_requests/:identity/actions/notify'.gsub(':identity', resource_id)
1017
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).
1018
+ with(
1019
+ body: { foo: 'bar' },
1020
+ headers: { 'Foo' => 'Bar' }
1021
+ ).to_return(
1022
+ body: {
1023
+ 'billing_requests' => {
1024
+
1025
+ 'actions' => 'actions-input',
1026
+ 'auto_fulfil' => 'auto_fulfil-input',
1027
+ 'created_at' => 'created_at-input',
1028
+ 'id' => 'id-input',
1029
+ 'links' => 'links-input',
1030
+ 'mandate_request' => 'mandate_request-input',
1031
+ 'metadata' => 'metadata-input',
1032
+ 'payment_request' => 'payment_request-input',
1033
+ 'resources' => 'resources-input',
1034
+ 'status' => 'status-input',
1035
+ },
1036
+ }.to_json,
1037
+ headers: response_headers
1038
+ )
1039
+ end
1040
+ end
1041
+ end
1042
+ end