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,366 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoCardlessPro::Services::BankAuthorisationsService 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 '#get' do
13
+ let(:id) { 'ID123' }
14
+
15
+ subject(:get_response) { client.bank_authorisations.get(id) }
16
+
17
+ context 'passing in a custom header' do
18
+ let!(:stub) do
19
+ stub_url = '/bank_authorisations/:identity'.gsub(':identity', id)
20
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).
21
+ with(headers: { 'Foo' => 'Bar' }).
22
+ to_return(
23
+ body: {
24
+ 'bank_authorisations' => {
25
+
26
+ 'authorisation_type' => 'authorisation_type-input',
27
+ 'created_at' => 'created_at-input',
28
+ 'expires_at' => 'expires_at-input',
29
+ 'id' => 'id-input',
30
+ 'last_visited_at' => 'last_visited_at-input',
31
+ 'links' => 'links-input',
32
+ 'redirect_uri' => 'redirect_uri-input',
33
+ 'short_url' => 'short_url-input',
34
+ 'url' => 'url-input',
35
+ },
36
+ }.to_json,
37
+ headers: response_headers
38
+ )
39
+ end
40
+
41
+ subject(:get_response) do
42
+ client.bank_authorisations.get(id, headers: {
43
+ 'Foo' => 'Bar',
44
+ })
45
+ end
46
+
47
+ it 'includes the header' do
48
+ get_response
49
+ expect(stub).to have_been_requested
50
+ end
51
+ end
52
+
53
+ context 'when there is a bank_authorisation to return' do
54
+ before do
55
+ stub_url = '/bank_authorisations/:identity'.gsub(':identity', id)
56
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
57
+ body: {
58
+ 'bank_authorisations' => {
59
+
60
+ 'authorisation_type' => 'authorisation_type-input',
61
+ 'created_at' => 'created_at-input',
62
+ 'expires_at' => 'expires_at-input',
63
+ 'id' => 'id-input',
64
+ 'last_visited_at' => 'last_visited_at-input',
65
+ 'links' => 'links-input',
66
+ 'redirect_uri' => 'redirect_uri-input',
67
+ 'short_url' => 'short_url-input',
68
+ 'url' => 'url-input',
69
+ },
70
+ }.to_json,
71
+ headers: response_headers
72
+ )
73
+ end
74
+
75
+ it 'wraps the response in a resource' do
76
+ expect(get_response).to be_a(GoCardlessPro::Resources::BankAuthorisation)
77
+ end
78
+ end
79
+
80
+ context 'when nothing is returned' do
81
+ before do
82
+ stub_url = '/bank_authorisations/:identity'.gsub(':identity', id)
83
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
84
+ body: '',
85
+ headers: response_headers
86
+ )
87
+ end
88
+
89
+ it 'returns nil' do
90
+ expect(get_response).to be_nil
91
+ end
92
+ end
93
+
94
+ context "when an ID is specified which can't be included in a valid URI" do
95
+ let(:id) { '`' }
96
+
97
+ it "doesn't raise an error" do
98
+ expect { get_response }.to_not raise_error(/bad URI/)
99
+ end
100
+ end
101
+
102
+ describe 'retry behaviour' do
103
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
104
+
105
+ it 'retries timeouts' do
106
+ stub_url = '/bank_authorisations/:identity'.gsub(':identity', id)
107
+
108
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/).
109
+ to_timeout.then.to_return(status: 200, headers: response_headers)
110
+
111
+ get_response
112
+ expect(stub).to have_been_requested.twice
113
+ end
114
+
115
+ it 'retries 5XX errors, other than 500s' do
116
+ stub_url = '/bank_authorisations/:identity'.gsub(':identity', id)
117
+
118
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/).
119
+ to_return(status: 502,
120
+ headers: { 'Content-Type' => 'text/html' },
121
+ body: '<html><body>Response from Cloudflare</body></html>').
122
+ then.to_return(status: 200, headers: response_headers)
123
+
124
+ get_response
125
+ expect(stub).to have_been_requested.twice
126
+ end
127
+
128
+ it 'retries 500 errors returned by the API' do
129
+ stub_url = '/bank_authorisations/:identity'.gsub(':identity', id)
130
+
131
+ gocardless_error = {
132
+ 'error' => {
133
+ 'message' => 'Internal server error',
134
+ 'documentation_url' => 'https://developer.gocardless.com/#gocardless',
135
+ 'errors' => [{
136
+ 'message' => 'Internal server error',
137
+ 'reason' => 'internal_server_error',
138
+ }],
139
+ 'type' => 'gocardless',
140
+ 'code' => 500,
141
+ 'request_id' => 'dummy_request_id',
142
+ 'id' => 'dummy_exception_id',
143
+ },
144
+ }
145
+
146
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/).
147
+ to_return(status: 500,
148
+ headers: response_headers,
149
+ body: gocardless_error.to_json).
150
+ then.to_return(status: 200, headers: response_headers)
151
+
152
+ get_response
153
+ expect(stub).to have_been_requested.twice
154
+ end
155
+ end
156
+ end
157
+
158
+ describe '#create' do
159
+ subject(:post_create_response) { client.bank_authorisations.create(params: new_resource) }
160
+ context 'with a valid request' do
161
+ let(:new_resource) do
162
+ {
163
+
164
+ 'authorisation_type' => 'authorisation_type-input',
165
+ 'created_at' => 'created_at-input',
166
+ 'expires_at' => 'expires_at-input',
167
+ 'id' => 'id-input',
168
+ 'last_visited_at' => 'last_visited_at-input',
169
+ 'links' => 'links-input',
170
+ 'redirect_uri' => 'redirect_uri-input',
171
+ 'short_url' => 'short_url-input',
172
+ 'url' => 'url-input',
173
+ }
174
+ end
175
+
176
+ before do
177
+ stub_request(:post, %r{.*api.gocardless.com/bank_authorisations}).
178
+ with(
179
+ body: {
180
+ 'bank_authorisations' => {
181
+
182
+ 'authorisation_type' => 'authorisation_type-input',
183
+ 'created_at' => 'created_at-input',
184
+ 'expires_at' => 'expires_at-input',
185
+ 'id' => 'id-input',
186
+ 'last_visited_at' => 'last_visited_at-input',
187
+ 'links' => 'links-input',
188
+ 'redirect_uri' => 'redirect_uri-input',
189
+ 'short_url' => 'short_url-input',
190
+ 'url' => 'url-input',
191
+ },
192
+ }
193
+ ).
194
+ to_return(
195
+ body: {
196
+ 'bank_authorisations' =>
197
+
198
+ {
199
+
200
+ 'authorisation_type' => 'authorisation_type-input',
201
+ 'created_at' => 'created_at-input',
202
+ 'expires_at' => 'expires_at-input',
203
+ 'id' => 'id-input',
204
+ 'last_visited_at' => 'last_visited_at-input',
205
+ 'links' => 'links-input',
206
+ 'redirect_uri' => 'redirect_uri-input',
207
+ 'short_url' => 'short_url-input',
208
+ 'url' => 'url-input',
209
+ },
210
+
211
+ }.to_json,
212
+ headers: response_headers
213
+ )
214
+ end
215
+
216
+ it 'creates and returns the resource' do
217
+ expect(post_create_response).to be_a(GoCardlessPro::Resources::BankAuthorisation)
218
+ end
219
+
220
+ describe 'retry behaviour' do
221
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
222
+
223
+ it 'retries timeouts' do
224
+ stub = stub_request(:post, %r{.*api.gocardless.com/bank_authorisations}).
225
+ to_timeout.then.to_return(status: 200, headers: response_headers)
226
+
227
+ post_create_response
228
+ expect(stub).to have_been_requested.twice
229
+ end
230
+
231
+ it 'retries 5XX errors' do
232
+ stub = stub_request(:post, %r{.*api.gocardless.com/bank_authorisations}).
233
+ to_return(status: 502,
234
+ headers: { 'Content-Type' => 'text/html' },
235
+ body: '<html><body>Response from Cloudflare</body></html>').
236
+ then.to_return(status: 200, headers: response_headers)
237
+
238
+ post_create_response
239
+ expect(stub).to have_been_requested.twice
240
+ end
241
+ end
242
+ end
243
+
244
+ context 'with a request that returns a validation error' do
245
+ let(:new_resource) { {} }
246
+
247
+ before do
248
+ stub_request(:post, %r{.*api.gocardless.com/bank_authorisations}).to_return(
249
+ body: {
250
+ error: {
251
+ type: 'validation_failed',
252
+ code: 422,
253
+ errors: [
254
+ { message: 'test error message', field: 'test_field' },
255
+ ],
256
+ },
257
+ }.to_json,
258
+ headers: response_headers,
259
+ status: 422
260
+ )
261
+ end
262
+
263
+ it 'throws the correct error' do
264
+ expect { post_create_response }.to raise_error(GoCardlessPro::ValidationError)
265
+ end
266
+ end
267
+
268
+ context 'with a request that returns an idempotent creation conflict error' do
269
+ let(:id) { 'ID123' }
270
+
271
+ let(:new_resource) do
272
+ {
273
+
274
+ 'authorisation_type' => 'authorisation_type-input',
275
+ 'created_at' => 'created_at-input',
276
+ 'expires_at' => 'expires_at-input',
277
+ 'id' => 'id-input',
278
+ 'last_visited_at' => 'last_visited_at-input',
279
+ 'links' => 'links-input',
280
+ 'redirect_uri' => 'redirect_uri-input',
281
+ 'short_url' => 'short_url-input',
282
+ 'url' => 'url-input',
283
+ }
284
+ end
285
+
286
+ let!(:post_stub) do
287
+ stub_request(:post, %r{.*api.gocardless.com/bank_authorisations}).to_return(
288
+ body: {
289
+ error: {
290
+ type: 'invalid_state',
291
+ code: 409,
292
+ errors: [
293
+ {
294
+ message: 'A resource has already been created with this idempotency key',
295
+ reason: 'idempotent_creation_conflict',
296
+ links: {
297
+ conflicting_resource_id: id,
298
+ },
299
+ },
300
+ ],
301
+ },
302
+ }.to_json,
303
+ headers: response_headers,
304
+ status: 409
305
+ )
306
+ end
307
+
308
+ let!(:get_stub) do
309
+ stub_url = "/bank_authorisations/#{id}"
310
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).
311
+ to_return(
312
+ body: {
313
+ 'bank_authorisations' => {
314
+
315
+ 'authorisation_type' => 'authorisation_type-input',
316
+ 'created_at' => 'created_at-input',
317
+ 'expires_at' => 'expires_at-input',
318
+ 'id' => 'id-input',
319
+ 'last_visited_at' => 'last_visited_at-input',
320
+ 'links' => 'links-input',
321
+ 'redirect_uri' => 'redirect_uri-input',
322
+ 'short_url' => 'short_url-input',
323
+ 'url' => 'url-input',
324
+ },
325
+ }.to_json,
326
+ headers: response_headers
327
+ )
328
+ end
329
+
330
+ context 'with default behaviour' do
331
+ it 'fetches the already-created resource' do
332
+ post_create_response
333
+ expect(post_stub).to have_been_requested
334
+ expect(get_stub).to have_been_requested
335
+ end
336
+ end
337
+
338
+ context 'with on_idempotency_conflict: :raise' do
339
+ let(:client) do
340
+ GoCardlessPro::Client.new(
341
+ access_token: 'SECRET_TOKEN',
342
+ on_idempotency_conflict: :raise
343
+ )
344
+ end
345
+
346
+ it 'raises an IdempotencyConflict error' do
347
+ expect { post_create_response }.
348
+ to raise_error(GoCardlessPro::IdempotencyConflict)
349
+ end
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
+ end
365
+ end
366
+ end
@@ -0,0 +1,152 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoCardlessPro::Services::BillingRequestFlowsService 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 '#create' do
13
+ subject(:post_create_response) { client.billing_request_flows.create(params: new_resource) }
14
+ context 'with a valid request' do
15
+ let(:new_resource) do
16
+ {
17
+
18
+ 'authorisation_url' => 'authorisation_url-input',
19
+ 'created_at' => 'created_at-input',
20
+ 'expires_at' => 'expires_at-input',
21
+ 'links' => 'links-input',
22
+ 'redirect_uri' => 'redirect_uri-input',
23
+ }
24
+ end
25
+
26
+ before do
27
+ stub_request(:post, %r{.*api.gocardless.com/billing_request_flows}).
28
+ with(
29
+ body: {
30
+ 'billing_request_flows' => {
31
+
32
+ 'authorisation_url' => 'authorisation_url-input',
33
+ 'created_at' => 'created_at-input',
34
+ 'expires_at' => 'expires_at-input',
35
+ 'links' => 'links-input',
36
+ 'redirect_uri' => 'redirect_uri-input',
37
+ },
38
+ }
39
+ ).
40
+ to_return(
41
+ body: {
42
+ 'billing_request_flows' =>
43
+
44
+ {
45
+
46
+ 'authorisation_url' => 'authorisation_url-input',
47
+ 'created_at' => 'created_at-input',
48
+ 'expires_at' => 'expires_at-input',
49
+ 'links' => 'links-input',
50
+ 'redirect_uri' => 'redirect_uri-input',
51
+ },
52
+
53
+ }.to_json,
54
+ headers: response_headers
55
+ )
56
+ end
57
+
58
+ it 'creates and returns the resource' do
59
+ expect(post_create_response).to be_a(GoCardlessPro::Resources::BillingRequestFlow)
60
+ end
61
+
62
+ describe 'retry behaviour' do
63
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
64
+
65
+ it 'retries timeouts' do
66
+ stub = stub_request(:post, %r{.*api.gocardless.com/billing_request_flows}).
67
+ to_timeout.then.to_return(status: 200, headers: response_headers)
68
+
69
+ post_create_response
70
+ expect(stub).to have_been_requested.twice
71
+ end
72
+
73
+ it 'retries 5XX errors' do
74
+ stub = stub_request(:post, %r{.*api.gocardless.com/billing_request_flows}).
75
+ to_return(status: 502,
76
+ headers: { 'Content-Type' => 'text/html' },
77
+ body: '<html><body>Response from Cloudflare</body></html>').
78
+ then.to_return(status: 200, headers: response_headers)
79
+
80
+ post_create_response
81
+ expect(stub).to have_been_requested.twice
82
+ end
83
+ end
84
+ end
85
+
86
+ context 'with a request that returns a validation error' do
87
+ let(:new_resource) { {} }
88
+
89
+ before do
90
+ stub_request(:post, %r{.*api.gocardless.com/billing_request_flows}).to_return(
91
+ body: {
92
+ error: {
93
+ type: 'validation_failed',
94
+ code: 422,
95
+ errors: [
96
+ { message: 'test error message', field: 'test_field' },
97
+ ],
98
+ },
99
+ }.to_json,
100
+ headers: response_headers,
101
+ status: 422
102
+ )
103
+ end
104
+
105
+ it 'throws the correct error' do
106
+ expect { post_create_response }.to raise_error(GoCardlessPro::ValidationError)
107
+ end
108
+ end
109
+
110
+ context 'with a request that returns an idempotent creation conflict error' do
111
+ let(:id) { 'ID123' }
112
+
113
+ let(:new_resource) do
114
+ {
115
+
116
+ 'authorisation_url' => 'authorisation_url-input',
117
+ 'created_at' => 'created_at-input',
118
+ 'expires_at' => 'expires_at-input',
119
+ 'links' => 'links-input',
120
+ 'redirect_uri' => 'redirect_uri-input',
121
+ }
122
+ end
123
+
124
+ let!(:post_stub) do
125
+ stub_request(:post, %r{.*api.gocardless.com/billing_request_flows}).to_return(
126
+ body: {
127
+ error: {
128
+ type: 'invalid_state',
129
+ code: 409,
130
+ errors: [
131
+ {
132
+ message: 'A resource has already been created with this idempotency key',
133
+ reason: 'idempotent_creation_conflict',
134
+ links: {
135
+ conflicting_resource_id: id,
136
+ },
137
+ },
138
+ ],
139
+ },
140
+ }.to_json,
141
+ headers: response_headers,
142
+ status: 409
143
+ )
144
+ end
145
+
146
+ it 'raises an InvalidStateError' do
147
+ expect { post_create_response }.to raise_error(GoCardlessPro::InvalidStateError)
148
+ expect(post_stub).to have_been_requested
149
+ end
150
+ end
151
+ end
152
+ end