gocardless_pro 2.25.0 → 2.27.0

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