gocardless_pro 2.27.0 → 2.28.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +20 -1
  3. data/lib/gocardless_pro.rb +6 -0
  4. data/lib/gocardless_pro/api_service.rb +4 -0
  5. data/lib/gocardless_pro/client.rb +6 -1
  6. data/lib/gocardless_pro/error/authentication_error.rb +4 -0
  7. data/lib/gocardless_pro/error/permission_error.rb +4 -0
  8. data/lib/gocardless_pro/error/rate_limit_error.rb +4 -0
  9. data/lib/gocardless_pro/middlewares/raise_gocardless_errors.rb +12 -1
  10. data/lib/gocardless_pro/resources/bank_authorisation.rb +3 -5
  11. data/lib/gocardless_pro/resources/billing_request.rb +29 -7
  12. data/lib/gocardless_pro/resources/billing_request_flow.rb +10 -0
  13. data/lib/gocardless_pro/resources/billing_request_template.rb +68 -0
  14. data/lib/gocardless_pro/resources/payer_authorisation.rb +9 -0
  15. data/lib/gocardless_pro/services/bank_authorisations_service.rb +0 -2
  16. data/lib/gocardless_pro/services/billing_request_flows_service.rb +23 -0
  17. data/lib/gocardless_pro/services/billing_request_templates_service.rb +131 -0
  18. data/lib/gocardless_pro/services/billing_requests_service.rb +49 -22
  19. data/lib/gocardless_pro/services/creditor_bank_accounts_service.rb +0 -4
  20. data/lib/gocardless_pro/services/creditors_service.rb +0 -2
  21. data/lib/gocardless_pro/services/customer_bank_accounts_service.rb +0 -4
  22. data/lib/gocardless_pro/services/customers_service.rb +0 -2
  23. data/lib/gocardless_pro/services/instalment_schedules_service.rb +0 -6
  24. data/lib/gocardless_pro/services/mandate_imports_service.rb +0 -6
  25. data/lib/gocardless_pro/services/mandates_service.rb +0 -6
  26. data/lib/gocardless_pro/services/payer_authorisations_service.rb +0 -6
  27. data/lib/gocardless_pro/services/payments_service.rb +0 -6
  28. data/lib/gocardless_pro/services/redirect_flows_service.rb +0 -4
  29. data/lib/gocardless_pro/services/refunds_service.rb +0 -2
  30. data/lib/gocardless_pro/services/scenario_simulators_service.rb +28 -6
  31. data/lib/gocardless_pro/services/subscriptions_service.rb +2 -10
  32. data/lib/gocardless_pro/services/webhooks_service.rb +0 -2
  33. data/lib/gocardless_pro/version.rb +1 -1
  34. data/spec/api_service_spec.rb +12 -1
  35. data/spec/middlewares/raise_gocardless_errors_spec.rb +30 -0
  36. data/spec/resources/bank_authorisation_spec.rb +7 -7
  37. data/spec/resources/billing_request_flow_spec.rb +90 -0
  38. data/spec/resources/billing_request_spec.rb +75 -29
  39. data/spec/resources/billing_request_template_spec.rb +502 -0
  40. data/spec/services/bank_authorisations_service_spec.rb +7 -20
  41. data/spec/services/billing_request_flows_service_spec.rb +101 -0
  42. data/spec/services/billing_request_templates_service_spec.rb +789 -0
  43. data/spec/services/billing_requests_service_spec.rb +87 -47
  44. data/spec/services/creditor_bank_accounts_service_spec.rb +0 -13
  45. data/spec/services/creditors_service_spec.rb +0 -13
  46. data/spec/services/customer_bank_accounts_service_spec.rb +0 -13
  47. data/spec/services/customers_service_spec.rb +0 -13
  48. data/spec/services/instalment_schedules_service_spec.rb +0 -26
  49. data/spec/services/mandate_imports_service_spec.rb +0 -13
  50. data/spec/services/mandates_service_spec.rb +0 -13
  51. data/spec/services/payer_authorisations_service_spec.rb +0 -13
  52. data/spec/services/payments_service_spec.rb +0 -13
  53. data/spec/services/redirect_flows_service_spec.rb +0 -13
  54. data/spec/services/refunds_service_spec.rb +0 -13
  55. data/spec/services/subscriptions_service_spec.rb +0 -13
  56. metadata +16 -7
@@ -24,13 +24,13 @@ describe GoCardlessPro::Services::BankAuthorisationsService do
24
24
  'bank_authorisations' => {
25
25
 
26
26
  'authorisation_type' => 'authorisation_type-input',
27
+ 'authorised_at' => 'authorised_at-input',
27
28
  'created_at' => 'created_at-input',
28
29
  'expires_at' => 'expires_at-input',
29
30
  'id' => 'id-input',
30
31
  'last_visited_at' => 'last_visited_at-input',
31
32
  'links' => 'links-input',
32
33
  'redirect_uri' => 'redirect_uri-input',
33
- 'short_url' => 'short_url-input',
34
34
  'url' => 'url-input',
35
35
  },
36
36
  }.to_json,
@@ -58,13 +58,13 @@ describe GoCardlessPro::Services::BankAuthorisationsService do
58
58
  'bank_authorisations' => {
59
59
 
60
60
  'authorisation_type' => 'authorisation_type-input',
61
+ 'authorised_at' => 'authorised_at-input',
61
62
  'created_at' => 'created_at-input',
62
63
  'expires_at' => 'expires_at-input',
63
64
  'id' => 'id-input',
64
65
  'last_visited_at' => 'last_visited_at-input',
65
66
  'links' => 'links-input',
66
67
  'redirect_uri' => 'redirect_uri-input',
67
- 'short_url' => 'short_url-input',
68
68
  'url' => 'url-input',
69
69
  },
70
70
  }.to_json,
@@ -162,13 +162,13 @@ describe GoCardlessPro::Services::BankAuthorisationsService do
162
162
  {
163
163
 
164
164
  'authorisation_type' => 'authorisation_type-input',
165
+ 'authorised_at' => 'authorised_at-input',
165
166
  'created_at' => 'created_at-input',
166
167
  'expires_at' => 'expires_at-input',
167
168
  'id' => 'id-input',
168
169
  'last_visited_at' => 'last_visited_at-input',
169
170
  'links' => 'links-input',
170
171
  'redirect_uri' => 'redirect_uri-input',
171
- 'short_url' => 'short_url-input',
172
172
  'url' => 'url-input',
173
173
  }
174
174
  end
@@ -180,13 +180,13 @@ describe GoCardlessPro::Services::BankAuthorisationsService do
180
180
  'bank_authorisations' => {
181
181
 
182
182
  'authorisation_type' => 'authorisation_type-input',
183
+ 'authorised_at' => 'authorised_at-input',
183
184
  'created_at' => 'created_at-input',
184
185
  'expires_at' => 'expires_at-input',
185
186
  'id' => 'id-input',
186
187
  'last_visited_at' => 'last_visited_at-input',
187
188
  'links' => 'links-input',
188
189
  'redirect_uri' => 'redirect_uri-input',
189
- 'short_url' => 'short_url-input',
190
190
  'url' => 'url-input',
191
191
  },
192
192
  }
@@ -198,13 +198,13 @@ describe GoCardlessPro::Services::BankAuthorisationsService do
198
198
  {
199
199
 
200
200
  'authorisation_type' => 'authorisation_type-input',
201
+ 'authorised_at' => 'authorised_at-input',
201
202
  'created_at' => 'created_at-input',
202
203
  'expires_at' => 'expires_at-input',
203
204
  'id' => 'id-input',
204
205
  'last_visited_at' => 'last_visited_at-input',
205
206
  'links' => 'links-input',
206
207
  'redirect_uri' => 'redirect_uri-input',
207
- 'short_url' => 'short_url-input',
208
208
  'url' => 'url-input',
209
209
  },
210
210
 
@@ -272,13 +272,13 @@ describe GoCardlessPro::Services::BankAuthorisationsService do
272
272
  {
273
273
 
274
274
  'authorisation_type' => 'authorisation_type-input',
275
+ 'authorised_at' => 'authorised_at-input',
275
276
  'created_at' => 'created_at-input',
276
277
  'expires_at' => 'expires_at-input',
277
278
  'id' => 'id-input',
278
279
  'last_visited_at' => 'last_visited_at-input',
279
280
  'links' => 'links-input',
280
281
  'redirect_uri' => 'redirect_uri-input',
281
- 'short_url' => 'short_url-input',
282
282
  'url' => 'url-input',
283
283
  }
284
284
  end
@@ -313,13 +313,13 @@ describe GoCardlessPro::Services::BankAuthorisationsService do
313
313
  'bank_authorisations' => {
314
314
 
315
315
  'authorisation_type' => 'authorisation_type-input',
316
+ 'authorised_at' => 'authorised_at-input',
316
317
  'created_at' => 'created_at-input',
317
318
  'expires_at' => 'expires_at-input',
318
319
  'id' => 'id-input',
319
320
  'last_visited_at' => 'last_visited_at-input',
320
321
  'links' => 'links-input',
321
322
  'redirect_uri' => 'redirect_uri-input',
322
- 'short_url' => 'short_url-input',
323
323
  'url' => 'url-input',
324
324
  },
325
325
  }.to_json,
@@ -348,19 +348,6 @@ describe GoCardlessPro::Services::BankAuthorisationsService do
348
348
  to raise_error(GoCardlessPro::IdempotencyConflict)
349
349
  end
350
350
  end
351
-
352
- context 'with on_idempotency_conflict: :unknown' do
353
- let(:client) do
354
- GoCardlessPro::Client.new(
355
- access_token: 'SECRET_TOKEN',
356
- on_idempotency_conflict: :unknown
357
- )
358
- end
359
-
360
- it 'raises an ArgumentError' do
361
- expect { post_create_response }.to raise_error(ArgumentError)
362
- end
363
- end
364
351
  end
365
352
  end
366
353
  end
@@ -16,10 +16,15 @@ describe GoCardlessPro::Services::BillingRequestFlowsService do
16
16
  {
17
17
 
18
18
  'authorisation_url' => 'authorisation_url-input',
19
+ 'auto_fulfil' => 'auto_fulfil-input',
19
20
  'created_at' => 'created_at-input',
20
21
  'expires_at' => 'expires_at-input',
22
+ 'id' => 'id-input',
21
23
  'links' => 'links-input',
24
+ 'lock_bank_account' => 'lock_bank_account-input',
25
+ 'lock_customer_details' => 'lock_customer_details-input',
22
26
  'redirect_uri' => 'redirect_uri-input',
27
+ 'session_token' => 'session_token-input',
23
28
  }
24
29
  end
25
30
 
@@ -30,10 +35,15 @@ describe GoCardlessPro::Services::BillingRequestFlowsService do
30
35
  'billing_request_flows' => {
31
36
 
32
37
  'authorisation_url' => 'authorisation_url-input',
38
+ 'auto_fulfil' => 'auto_fulfil-input',
33
39
  'created_at' => 'created_at-input',
34
40
  'expires_at' => 'expires_at-input',
41
+ 'id' => 'id-input',
35
42
  'links' => 'links-input',
43
+ 'lock_bank_account' => 'lock_bank_account-input',
44
+ 'lock_customer_details' => 'lock_customer_details-input',
36
45
  'redirect_uri' => 'redirect_uri-input',
46
+ 'session_token' => 'session_token-input',
37
47
  },
38
48
  }
39
49
  ).
@@ -44,10 +54,15 @@ describe GoCardlessPro::Services::BillingRequestFlowsService do
44
54
  {
45
55
 
46
56
  'authorisation_url' => 'authorisation_url-input',
57
+ 'auto_fulfil' => 'auto_fulfil-input',
47
58
  'created_at' => 'created_at-input',
48
59
  'expires_at' => 'expires_at-input',
60
+ 'id' => 'id-input',
49
61
  'links' => 'links-input',
62
+ 'lock_bank_account' => 'lock_bank_account-input',
63
+ 'lock_customer_details' => 'lock_customer_details-input',
50
64
  'redirect_uri' => 'redirect_uri-input',
65
+ 'session_token' => 'session_token-input',
51
66
  },
52
67
 
53
68
  }.to_json,
@@ -114,10 +129,15 @@ describe GoCardlessPro::Services::BillingRequestFlowsService do
114
129
  {
115
130
 
116
131
  'authorisation_url' => 'authorisation_url-input',
132
+ 'auto_fulfil' => 'auto_fulfil-input',
117
133
  'created_at' => 'created_at-input',
118
134
  'expires_at' => 'expires_at-input',
135
+ 'id' => 'id-input',
119
136
  'links' => 'links-input',
137
+ 'lock_bank_account' => 'lock_bank_account-input',
138
+ 'lock_customer_details' => 'lock_customer_details-input',
120
139
  'redirect_uri' => 'redirect_uri-input',
140
+ 'session_token' => 'session_token-input',
121
141
  }
122
142
  end
123
143
 
@@ -149,4 +169,85 @@ describe GoCardlessPro::Services::BillingRequestFlowsService do
149
169
  end
150
170
  end
151
171
  end
172
+
173
+ describe '#initialise' do
174
+ subject(:post_response) { client.billing_request_flows.initialise(resource_id) }
175
+
176
+ let(:resource_id) { 'ABC123' }
177
+
178
+ let!(:stub) do
179
+ # /billing_request_flows/%v/actions/initialise
180
+ stub_url = '/billing_request_flows/:identity/actions/initialise'.gsub(':identity', resource_id)
181
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).to_return(
182
+ body: {
183
+ 'billing_request_flows' => {
184
+
185
+ 'authorisation_url' => 'authorisation_url-input',
186
+ 'auto_fulfil' => 'auto_fulfil-input',
187
+ 'created_at' => 'created_at-input',
188
+ 'expires_at' => 'expires_at-input',
189
+ 'id' => 'id-input',
190
+ 'links' => 'links-input',
191
+ 'lock_bank_account' => 'lock_bank_account-input',
192
+ 'lock_customer_details' => 'lock_customer_details-input',
193
+ 'redirect_uri' => 'redirect_uri-input',
194
+ 'session_token' => 'session_token-input',
195
+ },
196
+ }.to_json,
197
+ headers: response_headers
198
+ )
199
+ end
200
+
201
+ it 'wraps the response and calls the right endpoint' do
202
+ expect(post_response).to be_a(GoCardlessPro::Resources::BillingRequestFlow)
203
+
204
+ expect(stub).to have_been_requested
205
+ end
206
+
207
+ describe 'retry behaviour' do
208
+ it "doesn't retry errors" do
209
+ stub_url = '/billing_request_flows/:identity/actions/initialise'.gsub(':identity', resource_id)
210
+ stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
211
+ to_timeout
212
+
213
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
214
+ expect(stub).to have_been_requested
215
+ end
216
+ end
217
+
218
+ context 'when the request needs a body and custom header' do
219
+ let(:body) { { foo: 'bar' } }
220
+ let(:headers) { { 'Foo' => 'Bar' } }
221
+ subject(:post_response) { client.billing_request_flows.initialise(resource_id, body, headers) }
222
+
223
+ let(:resource_id) { 'ABC123' }
224
+
225
+ let!(:stub) do
226
+ # /billing_request_flows/%v/actions/initialise
227
+ stub_url = '/billing_request_flows/:identity/actions/initialise'.gsub(':identity', resource_id)
228
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).
229
+ with(
230
+ body: { foo: 'bar' },
231
+ headers: { 'Foo' => 'Bar' }
232
+ ).to_return(
233
+ body: {
234
+ 'billing_request_flows' => {
235
+
236
+ 'authorisation_url' => 'authorisation_url-input',
237
+ 'auto_fulfil' => 'auto_fulfil-input',
238
+ 'created_at' => 'created_at-input',
239
+ 'expires_at' => 'expires_at-input',
240
+ 'id' => 'id-input',
241
+ 'links' => 'links-input',
242
+ 'lock_bank_account' => 'lock_bank_account-input',
243
+ 'lock_customer_details' => 'lock_customer_details-input',
244
+ 'redirect_uri' => 'redirect_uri-input',
245
+ 'session_token' => 'session_token-input',
246
+ },
247
+ }.to_json,
248
+ headers: response_headers
249
+ )
250
+ end
251
+ end
252
+ end
152
253
  end
@@ -0,0 +1,789 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoCardlessPro::Services::BillingRequestTemplatesService 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_request_templates.list }
15
+
16
+ let(:body) do
17
+ {
18
+ 'billing_request_templates' => [{
19
+
20
+ 'authorisation_url' => 'authorisation_url-input',
21
+ 'created_at' => 'created_at-input',
22
+ 'id' => 'id-input',
23
+ 'mandate_request_currency' => 'mandate_request_currency-input',
24
+ 'mandate_request_metadata' => 'mandate_request_metadata-input',
25
+ 'mandate_request_scheme' => 'mandate_request_scheme-input',
26
+ 'mandate_request_verify' => 'mandate_request_verify-input',
27
+ 'metadata' => 'metadata-input',
28
+ 'name' => 'name-input',
29
+ 'payment_request_amount' => 'payment_request_amount-input',
30
+ 'payment_request_currency' => 'payment_request_currency-input',
31
+ 'payment_request_description' => 'payment_request_description-input',
32
+ 'payment_request_metadata' => 'payment_request_metadata-input',
33
+ 'payment_request_scheme' => 'payment_request_scheme-input',
34
+ 'redirect_uri' => 'redirect_uri-input',
35
+ 'updated_at' => 'updated_at-input',
36
+ }],
37
+ meta: {
38
+ cursors: {
39
+ before: nil,
40
+ after: 'ABC123',
41
+ },
42
+ },
43
+ }.to_json
44
+ end
45
+
46
+ before do
47
+ stub_request(:get, %r{.*api.gocardless.com/billing_request_templates}).to_return(
48
+ body: body,
49
+ headers: response_headers
50
+ )
51
+ end
52
+
53
+ it 'wraps each item in the resource class' do
54
+ expect(get_list_response.records.map(&:class).uniq.first).to eq(GoCardlessPro::Resources::BillingRequestTemplate)
55
+
56
+ expect(get_list_response.records.first.authorisation_url).to eq('authorisation_url-input')
57
+
58
+ expect(get_list_response.records.first.created_at).to eq('created_at-input')
59
+
60
+ expect(get_list_response.records.first.id).to eq('id-input')
61
+
62
+ expect(get_list_response.records.first.mandate_request_currency).to eq('mandate_request_currency-input')
63
+
64
+ expect(get_list_response.records.first.mandate_request_metadata).to eq('mandate_request_metadata-input')
65
+
66
+ expect(get_list_response.records.first.mandate_request_scheme).to eq('mandate_request_scheme-input')
67
+
68
+ expect(get_list_response.records.first.mandate_request_verify).to eq('mandate_request_verify-input')
69
+
70
+ expect(get_list_response.records.first.metadata).to eq('metadata-input')
71
+
72
+ expect(get_list_response.records.first.name).to eq('name-input')
73
+
74
+ expect(get_list_response.records.first.payment_request_amount).to eq('payment_request_amount-input')
75
+
76
+ expect(get_list_response.records.first.payment_request_currency).to eq('payment_request_currency-input')
77
+
78
+ expect(get_list_response.records.first.payment_request_description).to eq('payment_request_description-input')
79
+
80
+ expect(get_list_response.records.first.payment_request_metadata).to eq('payment_request_metadata-input')
81
+
82
+ expect(get_list_response.records.first.payment_request_scheme).to eq('payment_request_scheme-input')
83
+
84
+ expect(get_list_response.records.first.redirect_uri).to eq('redirect_uri-input')
85
+
86
+ expect(get_list_response.records.first.updated_at).to eq('updated_at-input')
87
+ end
88
+
89
+ it 'exposes the cursors for before and after' do
90
+ expect(get_list_response.before).to eq(nil)
91
+ expect(get_list_response.after).to eq('ABC123')
92
+ end
93
+
94
+ specify { expect(get_list_response.api_response.headers).to eql('content-type' => 'application/json') }
95
+
96
+ describe 'retry behaviour' do
97
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
98
+
99
+ it 'retries timeouts' do
100
+ stub = stub_request(:get, %r{.*api.gocardless.com/billing_request_templates}).
101
+ to_timeout.then.to_return(status: 200, headers: response_headers, body: body)
102
+
103
+ get_list_response
104
+ expect(stub).to have_been_requested.twice
105
+ end
106
+
107
+ it 'retries 5XX errors' do
108
+ stub = stub_request(:get, %r{.*api.gocardless.com/billing_request_templates}).
109
+ to_return(status: 502,
110
+ headers: { 'Content-Type' => 'text/html' },
111
+ body: '<html><body>Response from Cloudflare</body></html>').
112
+ then.to_return(status: 200, headers: response_headers, body: body)
113
+
114
+ get_list_response
115
+ expect(stub).to have_been_requested.twice
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ describe '#all' do
122
+ let!(:first_response_stub) do
123
+ stub_request(:get, %r{.*api.gocardless.com/billing_request_templates$}).to_return(
124
+ body: {
125
+ 'billing_request_templates' => [{
126
+
127
+ 'authorisation_url' => 'authorisation_url-input',
128
+ 'created_at' => 'created_at-input',
129
+ 'id' => 'id-input',
130
+ 'mandate_request_currency' => 'mandate_request_currency-input',
131
+ 'mandate_request_metadata' => 'mandate_request_metadata-input',
132
+ 'mandate_request_scheme' => 'mandate_request_scheme-input',
133
+ 'mandate_request_verify' => 'mandate_request_verify-input',
134
+ 'metadata' => 'metadata-input',
135
+ 'name' => 'name-input',
136
+ 'payment_request_amount' => 'payment_request_amount-input',
137
+ 'payment_request_currency' => 'payment_request_currency-input',
138
+ 'payment_request_description' => 'payment_request_description-input',
139
+ 'payment_request_metadata' => 'payment_request_metadata-input',
140
+ 'payment_request_scheme' => 'payment_request_scheme-input',
141
+ 'redirect_uri' => 'redirect_uri-input',
142
+ 'updated_at' => 'updated_at-input',
143
+ }],
144
+ meta: {
145
+ cursors: { after: 'AB345' },
146
+ limit: 1,
147
+ },
148
+ }.to_json,
149
+ headers: response_headers
150
+ )
151
+ end
152
+
153
+ let!(:second_response_stub) do
154
+ stub_request(:get, %r{.*api.gocardless.com/billing_request_templates\?after=AB345}).to_return(
155
+ body: {
156
+ 'billing_request_templates' => [{
157
+
158
+ 'authorisation_url' => 'authorisation_url-input',
159
+ 'created_at' => 'created_at-input',
160
+ 'id' => 'id-input',
161
+ 'mandate_request_currency' => 'mandate_request_currency-input',
162
+ 'mandate_request_metadata' => 'mandate_request_metadata-input',
163
+ 'mandate_request_scheme' => 'mandate_request_scheme-input',
164
+ 'mandate_request_verify' => 'mandate_request_verify-input',
165
+ 'metadata' => 'metadata-input',
166
+ 'name' => 'name-input',
167
+ 'payment_request_amount' => 'payment_request_amount-input',
168
+ 'payment_request_currency' => 'payment_request_currency-input',
169
+ 'payment_request_description' => 'payment_request_description-input',
170
+ 'payment_request_metadata' => 'payment_request_metadata-input',
171
+ 'payment_request_scheme' => 'payment_request_scheme-input',
172
+ 'redirect_uri' => 'redirect_uri-input',
173
+ 'updated_at' => 'updated_at-input',
174
+ }],
175
+ meta: {
176
+ limit: 2,
177
+ cursors: {},
178
+ },
179
+ }.to_json,
180
+ headers: response_headers
181
+ )
182
+ end
183
+
184
+ it 'automatically makes the extra requests' do
185
+ expect(client.billing_request_templates.all.to_a.length).to eq(2)
186
+ expect(first_response_stub).to have_been_requested
187
+ expect(second_response_stub).to have_been_requested
188
+ end
189
+
190
+ describe 'retry behaviour' do
191
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
192
+
193
+ it 'retries timeouts' do
194
+ first_response_stub = stub_request(:get, %r{.*api.gocardless.com/billing_request_templates$}).to_return(
195
+ body: {
196
+ 'billing_request_templates' => [{
197
+
198
+ 'authorisation_url' => 'authorisation_url-input',
199
+ 'created_at' => 'created_at-input',
200
+ 'id' => 'id-input',
201
+ 'mandate_request_currency' => 'mandate_request_currency-input',
202
+ 'mandate_request_metadata' => 'mandate_request_metadata-input',
203
+ 'mandate_request_scheme' => 'mandate_request_scheme-input',
204
+ 'mandate_request_verify' => 'mandate_request_verify-input',
205
+ 'metadata' => 'metadata-input',
206
+ 'name' => 'name-input',
207
+ 'payment_request_amount' => 'payment_request_amount-input',
208
+ 'payment_request_currency' => 'payment_request_currency-input',
209
+ 'payment_request_description' => 'payment_request_description-input',
210
+ 'payment_request_metadata' => 'payment_request_metadata-input',
211
+ 'payment_request_scheme' => 'payment_request_scheme-input',
212
+ 'redirect_uri' => 'redirect_uri-input',
213
+ 'updated_at' => 'updated_at-input',
214
+ }],
215
+ meta: {
216
+ cursors: { after: 'AB345' },
217
+ limit: 1,
218
+ },
219
+ }.to_json,
220
+ headers: response_headers
221
+ )
222
+
223
+ second_response_stub = stub_request(:get, %r{.*api.gocardless.com/billing_request_templates\?after=AB345}).
224
+ to_timeout.then.
225
+ to_return(
226
+ body: {
227
+ 'billing_request_templates' => [{
228
+
229
+ 'authorisation_url' => 'authorisation_url-input',
230
+ 'created_at' => 'created_at-input',
231
+ 'id' => 'id-input',
232
+ 'mandate_request_currency' => 'mandate_request_currency-input',
233
+ 'mandate_request_metadata' => 'mandate_request_metadata-input',
234
+ 'mandate_request_scheme' => 'mandate_request_scheme-input',
235
+ 'mandate_request_verify' => 'mandate_request_verify-input',
236
+ 'metadata' => 'metadata-input',
237
+ 'name' => 'name-input',
238
+ 'payment_request_amount' => 'payment_request_amount-input',
239
+ 'payment_request_currency' => 'payment_request_currency-input',
240
+ 'payment_request_description' => 'payment_request_description-input',
241
+ 'payment_request_metadata' => 'payment_request_metadata-input',
242
+ 'payment_request_scheme' => 'payment_request_scheme-input',
243
+ 'redirect_uri' => 'redirect_uri-input',
244
+ 'updated_at' => 'updated_at-input',
245
+ }],
246
+ meta: {
247
+ limit: 2,
248
+ cursors: {},
249
+ },
250
+ }.to_json,
251
+ headers: response_headers
252
+ )
253
+
254
+ client.billing_request_templates.all.to_a
255
+
256
+ expect(first_response_stub).to have_been_requested
257
+ expect(second_response_stub).to have_been_requested.twice
258
+ end
259
+
260
+ it 'retries 5XX errors' do
261
+ first_response_stub = stub_request(:get, %r{.*api.gocardless.com/billing_request_templates$}).to_return(
262
+ body: {
263
+ 'billing_request_templates' => [{
264
+
265
+ 'authorisation_url' => 'authorisation_url-input',
266
+ 'created_at' => 'created_at-input',
267
+ 'id' => 'id-input',
268
+ 'mandate_request_currency' => 'mandate_request_currency-input',
269
+ 'mandate_request_metadata' => 'mandate_request_metadata-input',
270
+ 'mandate_request_scheme' => 'mandate_request_scheme-input',
271
+ 'mandate_request_verify' => 'mandate_request_verify-input',
272
+ 'metadata' => 'metadata-input',
273
+ 'name' => 'name-input',
274
+ 'payment_request_amount' => 'payment_request_amount-input',
275
+ 'payment_request_currency' => 'payment_request_currency-input',
276
+ 'payment_request_description' => 'payment_request_description-input',
277
+ 'payment_request_metadata' => 'payment_request_metadata-input',
278
+ 'payment_request_scheme' => 'payment_request_scheme-input',
279
+ 'redirect_uri' => 'redirect_uri-input',
280
+ 'updated_at' => 'updated_at-input',
281
+ }],
282
+ meta: {
283
+ cursors: { after: 'AB345' },
284
+ limit: 1,
285
+ },
286
+ }.to_json,
287
+ headers: response_headers
288
+ )
289
+
290
+ second_response_stub = stub_request(:get, %r{.*api.gocardless.com/billing_request_templates\?after=AB345}).
291
+ to_return(
292
+ status: 502,
293
+ body: '<html><body>Response from Cloudflare</body></html>',
294
+ headers: { 'Content-Type' => 'text/html' }
295
+ ).then.to_return(
296
+ body: {
297
+ 'billing_request_templates' => [{
298
+
299
+ 'authorisation_url' => 'authorisation_url-input',
300
+ 'created_at' => 'created_at-input',
301
+ 'id' => 'id-input',
302
+ 'mandate_request_currency' => 'mandate_request_currency-input',
303
+ 'mandate_request_metadata' => 'mandate_request_metadata-input',
304
+ 'mandate_request_scheme' => 'mandate_request_scheme-input',
305
+ 'mandate_request_verify' => 'mandate_request_verify-input',
306
+ 'metadata' => 'metadata-input',
307
+ 'name' => 'name-input',
308
+ 'payment_request_amount' => 'payment_request_amount-input',
309
+ 'payment_request_currency' => 'payment_request_currency-input',
310
+ 'payment_request_description' => 'payment_request_description-input',
311
+ 'payment_request_metadata' => 'payment_request_metadata-input',
312
+ 'payment_request_scheme' => 'payment_request_scheme-input',
313
+ 'redirect_uri' => 'redirect_uri-input',
314
+ 'updated_at' => 'updated_at-input',
315
+ }],
316
+ meta: {
317
+ limit: 2,
318
+ cursors: {},
319
+ },
320
+ }.to_json,
321
+ headers: response_headers
322
+ )
323
+
324
+ client.billing_request_templates.all.to_a
325
+
326
+ expect(first_response_stub).to have_been_requested
327
+ expect(second_response_stub).to have_been_requested.twice
328
+ end
329
+ end
330
+ end
331
+
332
+ describe '#get' do
333
+ let(:id) { 'ID123' }
334
+
335
+ subject(:get_response) { client.billing_request_templates.get(id) }
336
+
337
+ context 'passing in a custom header' do
338
+ let!(:stub) do
339
+ stub_url = '/billing_request_templates/:identity'.gsub(':identity', id)
340
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).
341
+ with(headers: { 'Foo' => 'Bar' }).
342
+ to_return(
343
+ body: {
344
+ 'billing_request_templates' => {
345
+
346
+ 'authorisation_url' => 'authorisation_url-input',
347
+ 'created_at' => 'created_at-input',
348
+ 'id' => 'id-input',
349
+ 'mandate_request_currency' => 'mandate_request_currency-input',
350
+ 'mandate_request_metadata' => 'mandate_request_metadata-input',
351
+ 'mandate_request_scheme' => 'mandate_request_scheme-input',
352
+ 'mandate_request_verify' => 'mandate_request_verify-input',
353
+ 'metadata' => 'metadata-input',
354
+ 'name' => 'name-input',
355
+ 'payment_request_amount' => 'payment_request_amount-input',
356
+ 'payment_request_currency' => 'payment_request_currency-input',
357
+ 'payment_request_description' => 'payment_request_description-input',
358
+ 'payment_request_metadata' => 'payment_request_metadata-input',
359
+ 'payment_request_scheme' => 'payment_request_scheme-input',
360
+ 'redirect_uri' => 'redirect_uri-input',
361
+ 'updated_at' => 'updated_at-input',
362
+ },
363
+ }.to_json,
364
+ headers: response_headers
365
+ )
366
+ end
367
+
368
+ subject(:get_response) do
369
+ client.billing_request_templates.get(id, headers: {
370
+ 'Foo' => 'Bar',
371
+ })
372
+ end
373
+
374
+ it 'includes the header' do
375
+ get_response
376
+ expect(stub).to have_been_requested
377
+ end
378
+ end
379
+
380
+ context 'when there is a billing_request_template to return' do
381
+ before do
382
+ stub_url = '/billing_request_templates/:identity'.gsub(':identity', id)
383
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
384
+ body: {
385
+ 'billing_request_templates' => {
386
+
387
+ 'authorisation_url' => 'authorisation_url-input',
388
+ 'created_at' => 'created_at-input',
389
+ 'id' => 'id-input',
390
+ 'mandate_request_currency' => 'mandate_request_currency-input',
391
+ 'mandate_request_metadata' => 'mandate_request_metadata-input',
392
+ 'mandate_request_scheme' => 'mandate_request_scheme-input',
393
+ 'mandate_request_verify' => 'mandate_request_verify-input',
394
+ 'metadata' => 'metadata-input',
395
+ 'name' => 'name-input',
396
+ 'payment_request_amount' => 'payment_request_amount-input',
397
+ 'payment_request_currency' => 'payment_request_currency-input',
398
+ 'payment_request_description' => 'payment_request_description-input',
399
+ 'payment_request_metadata' => 'payment_request_metadata-input',
400
+ 'payment_request_scheme' => 'payment_request_scheme-input',
401
+ 'redirect_uri' => 'redirect_uri-input',
402
+ 'updated_at' => 'updated_at-input',
403
+ },
404
+ }.to_json,
405
+ headers: response_headers
406
+ )
407
+ end
408
+
409
+ it 'wraps the response in a resource' do
410
+ expect(get_response).to be_a(GoCardlessPro::Resources::BillingRequestTemplate)
411
+ end
412
+ end
413
+
414
+ context 'when nothing is returned' do
415
+ before do
416
+ stub_url = '/billing_request_templates/:identity'.gsub(':identity', id)
417
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
418
+ body: '',
419
+ headers: response_headers
420
+ )
421
+ end
422
+
423
+ it 'returns nil' do
424
+ expect(get_response).to be_nil
425
+ end
426
+ end
427
+
428
+ context "when an ID is specified which can't be included in a valid URI" do
429
+ let(:id) { '`' }
430
+
431
+ it "doesn't raise an error" do
432
+ expect { get_response }.to_not raise_error(/bad URI/)
433
+ end
434
+ end
435
+
436
+ describe 'retry behaviour' do
437
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
438
+
439
+ it 'retries timeouts' do
440
+ stub_url = '/billing_request_templates/:identity'.gsub(':identity', id)
441
+
442
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/).
443
+ to_timeout.then.to_return(status: 200, headers: response_headers)
444
+
445
+ get_response
446
+ expect(stub).to have_been_requested.twice
447
+ end
448
+
449
+ it 'retries 5XX errors, other than 500s' do
450
+ stub_url = '/billing_request_templates/:identity'.gsub(':identity', id)
451
+
452
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/).
453
+ to_return(status: 502,
454
+ headers: { 'Content-Type' => 'text/html' },
455
+ body: '<html><body>Response from Cloudflare</body></html>').
456
+ then.to_return(status: 200, headers: response_headers)
457
+
458
+ get_response
459
+ expect(stub).to have_been_requested.twice
460
+ end
461
+
462
+ it 'retries 500 errors returned by the API' do
463
+ stub_url = '/billing_request_templates/:identity'.gsub(':identity', id)
464
+
465
+ gocardless_error = {
466
+ 'error' => {
467
+ 'message' => 'Internal server error',
468
+ 'documentation_url' => 'https://developer.gocardless.com/#gocardless',
469
+ 'errors' => [{
470
+ 'message' => 'Internal server error',
471
+ 'reason' => 'internal_server_error',
472
+ }],
473
+ 'type' => 'gocardless',
474
+ 'code' => 500,
475
+ 'request_id' => 'dummy_request_id',
476
+ 'id' => 'dummy_exception_id',
477
+ },
478
+ }
479
+
480
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/).
481
+ to_return(status: 500,
482
+ headers: response_headers,
483
+ body: gocardless_error.to_json).
484
+ then.to_return(status: 200, headers: response_headers)
485
+
486
+ get_response
487
+ expect(stub).to have_been_requested.twice
488
+ end
489
+ end
490
+ end
491
+
492
+ describe '#create' do
493
+ subject(:post_create_response) { client.billing_request_templates.create(params: new_resource) }
494
+ context 'with a valid request' do
495
+ let(:new_resource) do
496
+ {
497
+
498
+ 'authorisation_url' => 'authorisation_url-input',
499
+ 'created_at' => 'created_at-input',
500
+ 'id' => 'id-input',
501
+ 'mandate_request_currency' => 'mandate_request_currency-input',
502
+ 'mandate_request_metadata' => 'mandate_request_metadata-input',
503
+ 'mandate_request_scheme' => 'mandate_request_scheme-input',
504
+ 'mandate_request_verify' => 'mandate_request_verify-input',
505
+ 'metadata' => 'metadata-input',
506
+ 'name' => 'name-input',
507
+ 'payment_request_amount' => 'payment_request_amount-input',
508
+ 'payment_request_currency' => 'payment_request_currency-input',
509
+ 'payment_request_description' => 'payment_request_description-input',
510
+ 'payment_request_metadata' => 'payment_request_metadata-input',
511
+ 'payment_request_scheme' => 'payment_request_scheme-input',
512
+ 'redirect_uri' => 'redirect_uri-input',
513
+ 'updated_at' => 'updated_at-input',
514
+ }
515
+ end
516
+
517
+ before do
518
+ stub_request(:post, %r{.*api.gocardless.com/billing_request_templates}).
519
+ with(
520
+ body: {
521
+ 'billing_request_templates' => {
522
+
523
+ 'authorisation_url' => 'authorisation_url-input',
524
+ 'created_at' => 'created_at-input',
525
+ 'id' => 'id-input',
526
+ 'mandate_request_currency' => 'mandate_request_currency-input',
527
+ 'mandate_request_metadata' => 'mandate_request_metadata-input',
528
+ 'mandate_request_scheme' => 'mandate_request_scheme-input',
529
+ 'mandate_request_verify' => 'mandate_request_verify-input',
530
+ 'metadata' => 'metadata-input',
531
+ 'name' => 'name-input',
532
+ 'payment_request_amount' => 'payment_request_amount-input',
533
+ 'payment_request_currency' => 'payment_request_currency-input',
534
+ 'payment_request_description' => 'payment_request_description-input',
535
+ 'payment_request_metadata' => 'payment_request_metadata-input',
536
+ 'payment_request_scheme' => 'payment_request_scheme-input',
537
+ 'redirect_uri' => 'redirect_uri-input',
538
+ 'updated_at' => 'updated_at-input',
539
+ },
540
+ }
541
+ ).
542
+ to_return(
543
+ body: {
544
+ 'billing_request_templates' =>
545
+
546
+ {
547
+
548
+ 'authorisation_url' => 'authorisation_url-input',
549
+ 'created_at' => 'created_at-input',
550
+ 'id' => 'id-input',
551
+ 'mandate_request_currency' => 'mandate_request_currency-input',
552
+ 'mandate_request_metadata' => 'mandate_request_metadata-input',
553
+ 'mandate_request_scheme' => 'mandate_request_scheme-input',
554
+ 'mandate_request_verify' => 'mandate_request_verify-input',
555
+ 'metadata' => 'metadata-input',
556
+ 'name' => 'name-input',
557
+ 'payment_request_amount' => 'payment_request_amount-input',
558
+ 'payment_request_currency' => 'payment_request_currency-input',
559
+ 'payment_request_description' => 'payment_request_description-input',
560
+ 'payment_request_metadata' => 'payment_request_metadata-input',
561
+ 'payment_request_scheme' => 'payment_request_scheme-input',
562
+ 'redirect_uri' => 'redirect_uri-input',
563
+ 'updated_at' => 'updated_at-input',
564
+ },
565
+
566
+ }.to_json,
567
+ headers: response_headers
568
+ )
569
+ end
570
+
571
+ it 'creates and returns the resource' do
572
+ expect(post_create_response).to be_a(GoCardlessPro::Resources::BillingRequestTemplate)
573
+ end
574
+
575
+ describe 'retry behaviour' do
576
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
577
+
578
+ it 'retries timeouts' do
579
+ stub = stub_request(:post, %r{.*api.gocardless.com/billing_request_templates}).
580
+ to_timeout.then.to_return(status: 200, headers: response_headers)
581
+
582
+ post_create_response
583
+ expect(stub).to have_been_requested.twice
584
+ end
585
+
586
+ it 'retries 5XX errors' do
587
+ stub = stub_request(:post, %r{.*api.gocardless.com/billing_request_templates}).
588
+ to_return(status: 502,
589
+ headers: { 'Content-Type' => 'text/html' },
590
+ body: '<html><body>Response from Cloudflare</body></html>').
591
+ then.to_return(status: 200, headers: response_headers)
592
+
593
+ post_create_response
594
+ expect(stub).to have_been_requested.twice
595
+ end
596
+ end
597
+ end
598
+
599
+ context 'with a request that returns a validation error' do
600
+ let(:new_resource) { {} }
601
+
602
+ before do
603
+ stub_request(:post, %r{.*api.gocardless.com/billing_request_templates}).to_return(
604
+ body: {
605
+ error: {
606
+ type: 'validation_failed',
607
+ code: 422,
608
+ errors: [
609
+ { message: 'test error message', field: 'test_field' },
610
+ ],
611
+ },
612
+ }.to_json,
613
+ headers: response_headers,
614
+ status: 422
615
+ )
616
+ end
617
+
618
+ it 'throws the correct error' do
619
+ expect { post_create_response }.to raise_error(GoCardlessPro::ValidationError)
620
+ end
621
+ end
622
+
623
+ context 'with a request that returns an idempotent creation conflict error' do
624
+ let(:id) { 'ID123' }
625
+
626
+ let(:new_resource) do
627
+ {
628
+
629
+ 'authorisation_url' => 'authorisation_url-input',
630
+ 'created_at' => 'created_at-input',
631
+ 'id' => 'id-input',
632
+ 'mandate_request_currency' => 'mandate_request_currency-input',
633
+ 'mandate_request_metadata' => 'mandate_request_metadata-input',
634
+ 'mandate_request_scheme' => 'mandate_request_scheme-input',
635
+ 'mandate_request_verify' => 'mandate_request_verify-input',
636
+ 'metadata' => 'metadata-input',
637
+ 'name' => 'name-input',
638
+ 'payment_request_amount' => 'payment_request_amount-input',
639
+ 'payment_request_currency' => 'payment_request_currency-input',
640
+ 'payment_request_description' => 'payment_request_description-input',
641
+ 'payment_request_metadata' => 'payment_request_metadata-input',
642
+ 'payment_request_scheme' => 'payment_request_scheme-input',
643
+ 'redirect_uri' => 'redirect_uri-input',
644
+ 'updated_at' => 'updated_at-input',
645
+ }
646
+ end
647
+
648
+ let!(:post_stub) do
649
+ stub_request(:post, %r{.*api.gocardless.com/billing_request_templates}).to_return(
650
+ body: {
651
+ error: {
652
+ type: 'invalid_state',
653
+ code: 409,
654
+ errors: [
655
+ {
656
+ message: 'A resource has already been created with this idempotency key',
657
+ reason: 'idempotent_creation_conflict',
658
+ links: {
659
+ conflicting_resource_id: id,
660
+ },
661
+ },
662
+ ],
663
+ },
664
+ }.to_json,
665
+ headers: response_headers,
666
+ status: 409
667
+ )
668
+ end
669
+
670
+ let!(:get_stub) do
671
+ stub_url = "/billing_request_templates/#{id}"
672
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).
673
+ to_return(
674
+ body: {
675
+ 'billing_request_templates' => {
676
+
677
+ 'authorisation_url' => 'authorisation_url-input',
678
+ 'created_at' => 'created_at-input',
679
+ 'id' => 'id-input',
680
+ 'mandate_request_currency' => 'mandate_request_currency-input',
681
+ 'mandate_request_metadata' => 'mandate_request_metadata-input',
682
+ 'mandate_request_scheme' => 'mandate_request_scheme-input',
683
+ 'mandate_request_verify' => 'mandate_request_verify-input',
684
+ 'metadata' => 'metadata-input',
685
+ 'name' => 'name-input',
686
+ 'payment_request_amount' => 'payment_request_amount-input',
687
+ 'payment_request_currency' => 'payment_request_currency-input',
688
+ 'payment_request_description' => 'payment_request_description-input',
689
+ 'payment_request_metadata' => 'payment_request_metadata-input',
690
+ 'payment_request_scheme' => 'payment_request_scheme-input',
691
+ 'redirect_uri' => 'redirect_uri-input',
692
+ 'updated_at' => 'updated_at-input',
693
+ },
694
+ }.to_json,
695
+ headers: response_headers
696
+ )
697
+ end
698
+
699
+ context 'with default behaviour' do
700
+ it 'fetches the already-created resource' do
701
+ post_create_response
702
+ expect(post_stub).to have_been_requested
703
+ expect(get_stub).to have_been_requested
704
+ end
705
+ end
706
+
707
+ context 'with on_idempotency_conflict: :raise' do
708
+ let(:client) do
709
+ GoCardlessPro::Client.new(
710
+ access_token: 'SECRET_TOKEN',
711
+ on_idempotency_conflict: :raise
712
+ )
713
+ end
714
+
715
+ it 'raises an IdempotencyConflict error' do
716
+ expect { post_create_response }.
717
+ to raise_error(GoCardlessPro::IdempotencyConflict)
718
+ end
719
+ end
720
+ end
721
+ end
722
+
723
+ describe '#update' do
724
+ subject(:put_update_response) { client.billing_request_templates.update(id, params: update_params) }
725
+ let(:id) { 'ABC123' }
726
+
727
+ context 'with a valid request' do
728
+ let(:update_params) { { 'hello' => 'world' } }
729
+
730
+ let!(:stub) do
731
+ stub_url = '/billing_requests/:identity'.gsub(':identity', id)
732
+ stub_request(:put, /.*api.gocardless.com#{stub_url}/).to_return(
733
+ body: {
734
+ 'billing_request_templates' => {
735
+
736
+ 'authorisation_url' => 'authorisation_url-input',
737
+ 'created_at' => 'created_at-input',
738
+ 'id' => 'id-input',
739
+ 'mandate_request_currency' => 'mandate_request_currency-input',
740
+ 'mandate_request_metadata' => 'mandate_request_metadata-input',
741
+ 'mandate_request_scheme' => 'mandate_request_scheme-input',
742
+ 'mandate_request_verify' => 'mandate_request_verify-input',
743
+ 'metadata' => 'metadata-input',
744
+ 'name' => 'name-input',
745
+ 'payment_request_amount' => 'payment_request_amount-input',
746
+ 'payment_request_currency' => 'payment_request_currency-input',
747
+ 'payment_request_description' => 'payment_request_description-input',
748
+ 'payment_request_metadata' => 'payment_request_metadata-input',
749
+ 'payment_request_scheme' => 'payment_request_scheme-input',
750
+ 'redirect_uri' => 'redirect_uri-input',
751
+ 'updated_at' => 'updated_at-input',
752
+ },
753
+ }.to_json,
754
+ headers: response_headers
755
+ )
756
+ end
757
+
758
+ it 'updates and returns the resource' do
759
+ expect(put_update_response).to be_a(GoCardlessPro::Resources::BillingRequestTemplate)
760
+ expect(stub).to have_been_requested
761
+ end
762
+
763
+ describe 'retry behaviour' do
764
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
765
+
766
+ it 'retries timeouts' do
767
+ stub_url = '/billing_requests/:identity'.gsub(':identity', id)
768
+ stub = stub_request(:put, /.*api.gocardless.com#{stub_url}/).
769
+ to_timeout.then.to_return(status: 200, headers: response_headers)
770
+
771
+ put_update_response
772
+ expect(stub).to have_been_requested.twice
773
+ end
774
+
775
+ it 'retries 5XX errors' do
776
+ stub_url = '/billing_requests/:identity'.gsub(':identity', id)
777
+ stub = stub_request(:put, /.*api.gocardless.com#{stub_url}/).
778
+ to_return(status: 502,
779
+ headers: { 'Content-Type' => 'text/html' },
780
+ body: '<html><body>Response from Cloudflare</body></html>').
781
+ then.to_return(status: 200, headers: response_headers)
782
+
783
+ put_update_response
784
+ expect(stub).to have_been_requested.twice
785
+ end
786
+ end
787
+ end
788
+ end
789
+ end