gocardless_pro 2.23.0 → 2.28.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +23 -4
  3. data/lib/gocardless_pro.rb +27 -0
  4. data/lib/gocardless_pro/api_service.rb +4 -0
  5. data/lib/gocardless_pro/client.rb +41 -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 +85 -0
  11. data/lib/gocardless_pro/resources/billing_request.rb +108 -0
  12. data/lib/gocardless_pro/resources/billing_request_flow.rb +72 -0
  13. data/lib/gocardless_pro/resources/billing_request_template.rb +68 -0
  14. data/lib/gocardless_pro/resources/creditor.rb +2 -3
  15. data/lib/gocardless_pro/resources/event.rb +12 -0
  16. data/lib/gocardless_pro/resources/institution.rb +45 -0
  17. data/lib/gocardless_pro/resources/payer_authorisation.rb +131 -0
  18. data/lib/gocardless_pro/resources/payout_item.rb +4 -0
  19. data/lib/gocardless_pro/resources/scenario_simulator.rb +42 -0
  20. data/lib/gocardless_pro/resources/tax_rate.rb +3 -0
  21. data/lib/gocardless_pro/resources/webhook.rb +62 -0
  22. data/lib/gocardless_pro/services/bank_authorisations_service.rb +80 -0
  23. data/lib/gocardless_pro/services/billing_request_flows_service.rb +70 -0
  24. data/lib/gocardless_pro/services/billing_request_templates_service.rb +131 -0
  25. data/lib/gocardless_pro/services/billing_requests_service.rb +352 -0
  26. data/lib/gocardless_pro/services/creditor_bank_accounts_service.rb +0 -4
  27. data/lib/gocardless_pro/services/creditors_service.rb +0 -2
  28. data/lib/gocardless_pro/services/customer_bank_accounts_service.rb +0 -4
  29. data/lib/gocardless_pro/services/customers_service.rb +0 -2
  30. data/lib/gocardless_pro/services/instalment_schedules_service.rb +0 -6
  31. data/lib/gocardless_pro/services/institutions_service.rb +56 -0
  32. data/lib/gocardless_pro/services/mandate_imports_service.rb +0 -6
  33. data/lib/gocardless_pro/services/mandates_service.rb +0 -6
  34. data/lib/gocardless_pro/services/payer_authorisations_service.rb +202 -0
  35. data/lib/gocardless_pro/services/payments_service.rb +0 -6
  36. data/lib/gocardless_pro/services/redirect_flows_service.rb +0 -4
  37. data/lib/gocardless_pro/services/refunds_service.rb +0 -2
  38. data/lib/gocardless_pro/services/scenario_simulators_service.rb +170 -0
  39. data/lib/gocardless_pro/services/subscriptions_service.rb +10 -13
  40. data/lib/gocardless_pro/services/webhooks_service.rb +111 -0
  41. data/lib/gocardless_pro/version.rb +1 -1
  42. data/spec/api_service_spec.rb +12 -1
  43. data/spec/middlewares/raise_gocardless_errors_spec.rb +30 -0
  44. data/spec/resources/bank_authorisation_spec.rb +259 -0
  45. data/spec/resources/billing_request_flow_spec.rb +219 -0
  46. data/spec/resources/billing_request_spec.rb +782 -0
  47. data/spec/resources/billing_request_template_spec.rb +502 -0
  48. data/spec/resources/institution_spec.rb +103 -0
  49. data/spec/resources/payer_authorisation_spec.rb +418 -0
  50. data/spec/resources/scenario_simulator_spec.rb +63 -0
  51. data/spec/resources/webhook_spec.rb +323 -0
  52. data/spec/services/bank_authorisations_service_spec.rb +353 -0
  53. data/spec/services/billing_request_flows_service_spec.rb +253 -0
  54. data/spec/services/billing_request_templates_service_spec.rb +789 -0
  55. data/spec/services/billing_requests_service_spec.rb +1082 -0
  56. data/spec/services/creditor_bank_accounts_service_spec.rb +0 -13
  57. data/spec/services/creditors_service_spec.rb +0 -13
  58. data/spec/services/customer_bank_accounts_service_spec.rb +0 -13
  59. data/spec/services/customers_service_spec.rb +0 -13
  60. data/spec/services/instalment_schedules_service_spec.rb +0 -26
  61. data/spec/services/institutions_service_spec.rb +223 -0
  62. data/spec/services/mandate_imports_service_spec.rb +0 -13
  63. data/spec/services/mandates_service_spec.rb +0 -13
  64. data/spec/services/payer_authorisations_service_spec.rb +559 -0
  65. data/spec/services/payments_service_spec.rb +0 -13
  66. data/spec/services/redirect_flows_service_spec.rb +0 -13
  67. data/spec/services/refunds_service_spec.rb +0 -13
  68. data/spec/services/scenario_simulators_service_spec.rb +74 -0
  69. data/spec/services/subscriptions_service_spec.rb +0 -13
  70. data/spec/services/webhooks_service_spec.rb +545 -0
  71. metadata +54 -3
@@ -212,19 +212,6 @@ describe GoCardlessPro::Services::CreditorBankAccountsService do
212
212
  to raise_error(GoCardlessPro::IdempotencyConflict)
213
213
  end
214
214
  end
215
-
216
- context 'with on_idempotency_conflict: :unknown' do
217
- let(:client) do
218
- GoCardlessPro::Client.new(
219
- access_token: 'SECRET_TOKEN',
220
- on_idempotency_conflict: :unknown
221
- )
222
- end
223
-
224
- it 'raises an ArgumentError' do
225
- expect { post_create_response }.to raise_error(ArgumentError)
226
- end
227
- end
228
215
  end
229
216
  end
230
217
 
@@ -252,19 +252,6 @@ describe GoCardlessPro::Services::CreditorsService do
252
252
  to raise_error(GoCardlessPro::IdempotencyConflict)
253
253
  end
254
254
  end
255
-
256
- context 'with on_idempotency_conflict: :unknown' do
257
- let(:client) do
258
- GoCardlessPro::Client.new(
259
- access_token: 'SECRET_TOKEN',
260
- on_idempotency_conflict: :unknown
261
- )
262
- end
263
-
264
- it 'raises an ArgumentError' do
265
- expect { post_create_response }.to raise_error(ArgumentError)
266
- end
267
- end
268
255
  end
269
256
  end
270
257
 
@@ -212,19 +212,6 @@ describe GoCardlessPro::Services::CustomerBankAccountsService do
212
212
  to raise_error(GoCardlessPro::IdempotencyConflict)
213
213
  end
214
214
  end
215
-
216
- context 'with on_idempotency_conflict: :unknown' do
217
- let(:client) do
218
- GoCardlessPro::Client.new(
219
- access_token: 'SECRET_TOKEN',
220
- on_idempotency_conflict: :unknown
221
- )
222
- end
223
-
224
- it 'raises an ArgumentError' do
225
- expect { post_create_response }.to raise_error(ArgumentError)
226
- end
227
- end
228
215
  end
229
216
  end
230
217
 
@@ -247,19 +247,6 @@ describe GoCardlessPro::Services::CustomersService do
247
247
  to raise_error(GoCardlessPro::IdempotencyConflict)
248
248
  end
249
249
  end
250
-
251
- context 'with on_idempotency_conflict: :unknown' do
252
- let(:client) do
253
- GoCardlessPro::Client.new(
254
- access_token: 'SECRET_TOKEN',
255
- on_idempotency_conflict: :unknown
256
- )
257
- end
258
-
259
- it 'raises an ArgumentError' do
260
- expect { post_create_response }.to raise_error(ArgumentError)
261
- end
262
- end
263
250
  end
264
251
  end
265
252
 
@@ -202,19 +202,6 @@ describe GoCardlessPro::Services::InstalmentSchedulesService do
202
202
  to raise_error(GoCardlessPro::IdempotencyConflict)
203
203
  end
204
204
  end
205
-
206
- context 'with on_idempotency_conflict: :unknown' do
207
- let(:client) do
208
- GoCardlessPro::Client.new(
209
- access_token: 'SECRET_TOKEN',
210
- on_idempotency_conflict: :unknown
211
- )
212
- end
213
-
214
- it 'raises an ArgumentError' do
215
- expect { post_create_response }.to raise_error(ArgumentError)
216
- end
217
- end
218
205
  end
219
206
  end
220
207
 
@@ -411,19 +398,6 @@ describe GoCardlessPro::Services::InstalmentSchedulesService do
411
398
  to raise_error(GoCardlessPro::IdempotencyConflict)
412
399
  end
413
400
  end
414
-
415
- context 'with on_idempotency_conflict: :unknown' do
416
- let(:client) do
417
- GoCardlessPro::Client.new(
418
- access_token: 'SECRET_TOKEN',
419
- on_idempotency_conflict: :unknown
420
- )
421
- end
422
-
423
- it 'raises an ArgumentError' do
424
- expect { post_create_response }.to raise_error(ArgumentError)
425
- end
426
- end
427
401
  end
428
402
  end
429
403
 
@@ -0,0 +1,223 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoCardlessPro::Services::InstitutionsService 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.institutions.list }
15
+
16
+ let(:body) do
17
+ {
18
+ 'institutions' => [{
19
+
20
+ 'icon_url' => 'icon_url-input',
21
+ 'id' => 'id-input',
22
+ 'logo_url' => 'logo_url-input',
23
+ 'name' => 'name-input',
24
+ }],
25
+ meta: {
26
+ cursors: {
27
+ before: nil,
28
+ after: 'ABC123',
29
+ },
30
+ },
31
+ }.to_json
32
+ end
33
+
34
+ before do
35
+ stub_request(:get, %r{.*api.gocardless.com/institutions}).to_return(
36
+ body: body,
37
+ headers: response_headers
38
+ )
39
+ end
40
+
41
+ it 'wraps each item in the resource class' do
42
+ expect(get_list_response.records.map(&:class).uniq.first).to eq(GoCardlessPro::Resources::Institution)
43
+
44
+ expect(get_list_response.records.first.icon_url).to eq('icon_url-input')
45
+
46
+ expect(get_list_response.records.first.id).to eq('id-input')
47
+
48
+ expect(get_list_response.records.first.logo_url).to eq('logo_url-input')
49
+
50
+ expect(get_list_response.records.first.name).to eq('name-input')
51
+ end
52
+
53
+ it 'exposes the cursors for before and after' do
54
+ expect(get_list_response.before).to eq(nil)
55
+ expect(get_list_response.after).to eq('ABC123')
56
+ end
57
+
58
+ specify { expect(get_list_response.api_response.headers).to eql('content-type' => 'application/json') }
59
+
60
+ describe 'retry behaviour' do
61
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
62
+
63
+ it 'retries timeouts' do
64
+ stub = stub_request(:get, %r{.*api.gocardless.com/institutions}).
65
+ to_timeout.then.to_return(status: 200, headers: response_headers, body: body)
66
+
67
+ get_list_response
68
+ expect(stub).to have_been_requested.twice
69
+ end
70
+
71
+ it 'retries 5XX errors' do
72
+ stub = stub_request(:get, %r{.*api.gocardless.com/institutions}).
73
+ to_return(status: 502,
74
+ headers: { 'Content-Type' => 'text/html' },
75
+ body: '<html><body>Response from Cloudflare</body></html>').
76
+ then.to_return(status: 200, headers: response_headers, body: body)
77
+
78
+ get_list_response
79
+ expect(stub).to have_been_requested.twice
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ describe '#all' do
86
+ let!(:first_response_stub) do
87
+ stub_request(:get, %r{.*api.gocardless.com/institutions$}).to_return(
88
+ body: {
89
+ 'institutions' => [{
90
+
91
+ 'icon_url' => 'icon_url-input',
92
+ 'id' => 'id-input',
93
+ 'logo_url' => 'logo_url-input',
94
+ 'name' => 'name-input',
95
+ }],
96
+ meta: {
97
+ cursors: { after: 'AB345' },
98
+ limit: 1,
99
+ },
100
+ }.to_json,
101
+ headers: response_headers
102
+ )
103
+ end
104
+
105
+ let!(:second_response_stub) do
106
+ stub_request(:get, %r{.*api.gocardless.com/institutions\?after=AB345}).to_return(
107
+ body: {
108
+ 'institutions' => [{
109
+
110
+ 'icon_url' => 'icon_url-input',
111
+ 'id' => 'id-input',
112
+ 'logo_url' => 'logo_url-input',
113
+ 'name' => 'name-input',
114
+ }],
115
+ meta: {
116
+ limit: 2,
117
+ cursors: {},
118
+ },
119
+ }.to_json,
120
+ headers: response_headers
121
+ )
122
+ end
123
+
124
+ it 'automatically makes the extra requests' do
125
+ expect(client.institutions.all.to_a.length).to eq(2)
126
+ expect(first_response_stub).to have_been_requested
127
+ expect(second_response_stub).to have_been_requested
128
+ end
129
+
130
+ describe 'retry behaviour' do
131
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
132
+
133
+ it 'retries timeouts' do
134
+ first_response_stub = stub_request(:get, %r{.*api.gocardless.com/institutions$}).to_return(
135
+ body: {
136
+ 'institutions' => [{
137
+
138
+ 'icon_url' => 'icon_url-input',
139
+ 'id' => 'id-input',
140
+ 'logo_url' => 'logo_url-input',
141
+ 'name' => 'name-input',
142
+ }],
143
+ meta: {
144
+ cursors: { after: 'AB345' },
145
+ limit: 1,
146
+ },
147
+ }.to_json,
148
+ headers: response_headers
149
+ )
150
+
151
+ second_response_stub = stub_request(:get, %r{.*api.gocardless.com/institutions\?after=AB345}).
152
+ to_timeout.then.
153
+ to_return(
154
+ body: {
155
+ 'institutions' => [{
156
+
157
+ 'icon_url' => 'icon_url-input',
158
+ 'id' => 'id-input',
159
+ 'logo_url' => 'logo_url-input',
160
+ 'name' => 'name-input',
161
+ }],
162
+ meta: {
163
+ limit: 2,
164
+ cursors: {},
165
+ },
166
+ }.to_json,
167
+ headers: response_headers
168
+ )
169
+
170
+ client.institutions.all.to_a
171
+
172
+ expect(first_response_stub).to have_been_requested
173
+ expect(second_response_stub).to have_been_requested.twice
174
+ end
175
+
176
+ it 'retries 5XX errors' do
177
+ first_response_stub = stub_request(:get, %r{.*api.gocardless.com/institutions$}).to_return(
178
+ body: {
179
+ 'institutions' => [{
180
+
181
+ 'icon_url' => 'icon_url-input',
182
+ 'id' => 'id-input',
183
+ 'logo_url' => 'logo_url-input',
184
+ 'name' => 'name-input',
185
+ }],
186
+ meta: {
187
+ cursors: { after: 'AB345' },
188
+ limit: 1,
189
+ },
190
+ }.to_json,
191
+ headers: response_headers
192
+ )
193
+
194
+ second_response_stub = stub_request(:get, %r{.*api.gocardless.com/institutions\?after=AB345}).
195
+ to_return(
196
+ status: 502,
197
+ body: '<html><body>Response from Cloudflare</body></html>',
198
+ headers: { 'Content-Type' => 'text/html' }
199
+ ).then.to_return(
200
+ body: {
201
+ 'institutions' => [{
202
+
203
+ 'icon_url' => 'icon_url-input',
204
+ 'id' => 'id-input',
205
+ 'logo_url' => 'logo_url-input',
206
+ 'name' => 'name-input',
207
+ }],
208
+ meta: {
209
+ limit: 2,
210
+ cursors: {},
211
+ },
212
+ }.to_json,
213
+ headers: response_headers
214
+ )
215
+
216
+ client.institutions.all.to_a
217
+
218
+ expect(first_response_stub).to have_been_requested
219
+ expect(second_response_stub).to have_been_requested.twice
220
+ end
221
+ end
222
+ end
223
+ end
@@ -177,19 +177,6 @@ describe GoCardlessPro::Services::MandateImportsService do
177
177
  to raise_error(GoCardlessPro::IdempotencyConflict)
178
178
  end
179
179
  end
180
-
181
- context 'with on_idempotency_conflict: :unknown' do
182
- let(:client) do
183
- GoCardlessPro::Client.new(
184
- access_token: 'SECRET_TOKEN',
185
- on_idempotency_conflict: :unknown
186
- )
187
- end
188
-
189
- it 'raises an ArgumentError' do
190
- expect { post_create_response }.to raise_error(ArgumentError)
191
- end
192
- end
193
180
  end
194
181
  end
195
182
 
@@ -202,19 +202,6 @@ describe GoCardlessPro::Services::MandatesService do
202
202
  to raise_error(GoCardlessPro::IdempotencyConflict)
203
203
  end
204
204
  end
205
-
206
- context 'with on_idempotency_conflict: :unknown' do
207
- let(:client) do
208
- GoCardlessPro::Client.new(
209
- access_token: 'SECRET_TOKEN',
210
- on_idempotency_conflict: :unknown
211
- )
212
- end
213
-
214
- it 'raises an ArgumentError' do
215
- expect { post_create_response }.to raise_error(ArgumentError)
216
- end
217
- end
218
205
  end
219
206
  end
220
207
 
@@ -0,0 +1,559 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoCardlessPro::Services::PayerAuthorisationsService 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.payer_authorisations.get(id) }
16
+
17
+ context 'passing in a custom header' do
18
+ let!(:stub) do
19
+ stub_url = '/payer_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
+ 'payer_authorisations' => {
25
+
26
+ 'bank_account' => 'bank_account-input',
27
+ 'created_at' => 'created_at-input',
28
+ 'customer' => 'customer-input',
29
+ 'id' => 'id-input',
30
+ 'incomplete_fields' => 'incomplete_fields-input',
31
+ 'links' => 'links-input',
32
+ 'mandate' => 'mandate-input',
33
+ 'status' => 'status-input',
34
+ },
35
+ }.to_json,
36
+ headers: response_headers
37
+ )
38
+ end
39
+
40
+ subject(:get_response) do
41
+ client.payer_authorisations.get(id, headers: {
42
+ 'Foo' => 'Bar',
43
+ })
44
+ end
45
+
46
+ it 'includes the header' do
47
+ get_response
48
+ expect(stub).to have_been_requested
49
+ end
50
+ end
51
+
52
+ context 'when there is a payer_authorisation to return' do
53
+ before do
54
+ stub_url = '/payer_authorisations/:identity'.gsub(':identity', id)
55
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
56
+ body: {
57
+ 'payer_authorisations' => {
58
+
59
+ 'bank_account' => 'bank_account-input',
60
+ 'created_at' => 'created_at-input',
61
+ 'customer' => 'customer-input',
62
+ 'id' => 'id-input',
63
+ 'incomplete_fields' => 'incomplete_fields-input',
64
+ 'links' => 'links-input',
65
+ 'mandate' => 'mandate-input',
66
+ 'status' => 'status-input',
67
+ },
68
+ }.to_json,
69
+ headers: response_headers
70
+ )
71
+ end
72
+
73
+ it 'wraps the response in a resource' do
74
+ expect(get_response).to be_a(GoCardlessPro::Resources::PayerAuthorisation)
75
+ end
76
+ end
77
+
78
+ context 'when nothing is returned' do
79
+ before do
80
+ stub_url = '/payer_authorisations/:identity'.gsub(':identity', id)
81
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
82
+ body: '',
83
+ headers: response_headers
84
+ )
85
+ end
86
+
87
+ it 'returns nil' do
88
+ expect(get_response).to be_nil
89
+ end
90
+ end
91
+
92
+ context "when an ID is specified which can't be included in a valid URI" do
93
+ let(:id) { '`' }
94
+
95
+ it "doesn't raise an error" do
96
+ expect { get_response }.to_not raise_error(/bad URI/)
97
+ end
98
+ end
99
+
100
+ describe 'retry behaviour' do
101
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
102
+
103
+ it 'retries timeouts' do
104
+ stub_url = '/payer_authorisations/:identity'.gsub(':identity', id)
105
+
106
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/).
107
+ to_timeout.then.to_return(status: 200, headers: response_headers)
108
+
109
+ get_response
110
+ expect(stub).to have_been_requested.twice
111
+ end
112
+
113
+ it 'retries 5XX errors, other than 500s' do
114
+ stub_url = '/payer_authorisations/:identity'.gsub(':identity', id)
115
+
116
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/).
117
+ to_return(status: 502,
118
+ headers: { 'Content-Type' => 'text/html' },
119
+ body: '<html><body>Response from Cloudflare</body></html>').
120
+ then.to_return(status: 200, headers: response_headers)
121
+
122
+ get_response
123
+ expect(stub).to have_been_requested.twice
124
+ end
125
+
126
+ it 'retries 500 errors returned by the API' do
127
+ stub_url = '/payer_authorisations/:identity'.gsub(':identity', id)
128
+
129
+ gocardless_error = {
130
+ 'error' => {
131
+ 'message' => 'Internal server error',
132
+ 'documentation_url' => 'https://developer.gocardless.com/#gocardless',
133
+ 'errors' => [{
134
+ 'message' => 'Internal server error',
135
+ 'reason' => 'internal_server_error',
136
+ }],
137
+ 'type' => 'gocardless',
138
+ 'code' => 500,
139
+ 'request_id' => 'dummy_request_id',
140
+ 'id' => 'dummy_exception_id',
141
+ },
142
+ }
143
+
144
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/).
145
+ to_return(status: 500,
146
+ headers: response_headers,
147
+ body: gocardless_error.to_json).
148
+ then.to_return(status: 200, headers: response_headers)
149
+
150
+ get_response
151
+ expect(stub).to have_been_requested.twice
152
+ end
153
+ end
154
+ end
155
+
156
+ describe '#create' do
157
+ subject(:post_create_response) { client.payer_authorisations.create(params: new_resource) }
158
+ context 'with a valid request' do
159
+ let(:new_resource) do
160
+ {
161
+
162
+ 'bank_account' => 'bank_account-input',
163
+ 'created_at' => 'created_at-input',
164
+ 'customer' => 'customer-input',
165
+ 'id' => 'id-input',
166
+ 'incomplete_fields' => 'incomplete_fields-input',
167
+ 'links' => 'links-input',
168
+ 'mandate' => 'mandate-input',
169
+ 'status' => 'status-input',
170
+ }
171
+ end
172
+
173
+ before do
174
+ stub_request(:post, %r{.*api.gocardless.com/payer_authorisations}).
175
+ with(
176
+ body: {
177
+ 'payer_authorisations' => {
178
+
179
+ 'bank_account' => 'bank_account-input',
180
+ 'created_at' => 'created_at-input',
181
+ 'customer' => 'customer-input',
182
+ 'id' => 'id-input',
183
+ 'incomplete_fields' => 'incomplete_fields-input',
184
+ 'links' => 'links-input',
185
+ 'mandate' => 'mandate-input',
186
+ 'status' => 'status-input',
187
+ },
188
+ }
189
+ ).
190
+ to_return(
191
+ body: {
192
+ 'payer_authorisations' =>
193
+
194
+ {
195
+
196
+ 'bank_account' => 'bank_account-input',
197
+ 'created_at' => 'created_at-input',
198
+ 'customer' => 'customer-input',
199
+ 'id' => 'id-input',
200
+ 'incomplete_fields' => 'incomplete_fields-input',
201
+ 'links' => 'links-input',
202
+ 'mandate' => 'mandate-input',
203
+ 'status' => 'status-input',
204
+ },
205
+
206
+ }.to_json,
207
+ headers: response_headers
208
+ )
209
+ end
210
+
211
+ it 'creates and returns the resource' do
212
+ expect(post_create_response).to be_a(GoCardlessPro::Resources::PayerAuthorisation)
213
+ end
214
+
215
+ describe 'retry behaviour' do
216
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
217
+
218
+ it 'retries timeouts' do
219
+ stub = stub_request(:post, %r{.*api.gocardless.com/payer_authorisations}).
220
+ to_timeout.then.to_return(status: 200, headers: response_headers)
221
+
222
+ post_create_response
223
+ expect(stub).to have_been_requested.twice
224
+ end
225
+
226
+ it 'retries 5XX errors' do
227
+ stub = stub_request(:post, %r{.*api.gocardless.com/payer_authorisations}).
228
+ to_return(status: 502,
229
+ headers: { 'Content-Type' => 'text/html' },
230
+ body: '<html><body>Response from Cloudflare</body></html>').
231
+ then.to_return(status: 200, headers: response_headers)
232
+
233
+ post_create_response
234
+ expect(stub).to have_been_requested.twice
235
+ end
236
+ end
237
+ end
238
+
239
+ context 'with a request that returns a validation error' do
240
+ let(:new_resource) { {} }
241
+
242
+ before do
243
+ stub_request(:post, %r{.*api.gocardless.com/payer_authorisations}).to_return(
244
+ body: {
245
+ error: {
246
+ type: 'validation_failed',
247
+ code: 422,
248
+ errors: [
249
+ { message: 'test error message', field: 'test_field' },
250
+ ],
251
+ },
252
+ }.to_json,
253
+ headers: response_headers,
254
+ status: 422
255
+ )
256
+ end
257
+
258
+ it 'throws the correct error' do
259
+ expect { post_create_response }.to raise_error(GoCardlessPro::ValidationError)
260
+ end
261
+ end
262
+
263
+ context 'with a request that returns an idempotent creation conflict error' do
264
+ let(:id) { 'ID123' }
265
+
266
+ let(:new_resource) do
267
+ {
268
+
269
+ 'bank_account' => 'bank_account-input',
270
+ 'created_at' => 'created_at-input',
271
+ 'customer' => 'customer-input',
272
+ 'id' => 'id-input',
273
+ 'incomplete_fields' => 'incomplete_fields-input',
274
+ 'links' => 'links-input',
275
+ 'mandate' => 'mandate-input',
276
+ 'status' => 'status-input',
277
+ }
278
+ end
279
+
280
+ let!(:post_stub) do
281
+ stub_request(:post, %r{.*api.gocardless.com/payer_authorisations}).to_return(
282
+ body: {
283
+ error: {
284
+ type: 'invalid_state',
285
+ code: 409,
286
+ errors: [
287
+ {
288
+ message: 'A resource has already been created with this idempotency key',
289
+ reason: 'idempotent_creation_conflict',
290
+ links: {
291
+ conflicting_resource_id: id,
292
+ },
293
+ },
294
+ ],
295
+ },
296
+ }.to_json,
297
+ headers: response_headers,
298
+ status: 409
299
+ )
300
+ end
301
+
302
+ let!(:get_stub) do
303
+ stub_url = "/payer_authorisations/#{id}"
304
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).
305
+ to_return(
306
+ body: {
307
+ 'payer_authorisations' => {
308
+
309
+ 'bank_account' => 'bank_account-input',
310
+ 'created_at' => 'created_at-input',
311
+ 'customer' => 'customer-input',
312
+ 'id' => 'id-input',
313
+ 'incomplete_fields' => 'incomplete_fields-input',
314
+ 'links' => 'links-input',
315
+ 'mandate' => 'mandate-input',
316
+ 'status' => 'status-input',
317
+ },
318
+ }.to_json,
319
+ headers: response_headers
320
+ )
321
+ end
322
+
323
+ context 'with default behaviour' do
324
+ it 'fetches the already-created resource' do
325
+ post_create_response
326
+ expect(post_stub).to have_been_requested
327
+ expect(get_stub).to have_been_requested
328
+ end
329
+ end
330
+
331
+ context 'with on_idempotency_conflict: :raise' do
332
+ let(:client) do
333
+ GoCardlessPro::Client.new(
334
+ access_token: 'SECRET_TOKEN',
335
+ on_idempotency_conflict: :raise
336
+ )
337
+ end
338
+
339
+ it 'raises an IdempotencyConflict error' do
340
+ expect { post_create_response }.
341
+ to raise_error(GoCardlessPro::IdempotencyConflict)
342
+ end
343
+ end
344
+ end
345
+ end
346
+
347
+ describe '#update' do
348
+ subject(:put_update_response) { client.payer_authorisations.update(id, params: update_params) }
349
+ let(:id) { 'ABC123' }
350
+
351
+ context 'with a valid request' do
352
+ let(:update_params) { { 'hello' => 'world' } }
353
+
354
+ let!(:stub) do
355
+ stub_url = '/payer_authorisations/:identity'.gsub(':identity', id)
356
+ stub_request(:put, /.*api.gocardless.com#{stub_url}/).to_return(
357
+ body: {
358
+ 'payer_authorisations' => {
359
+
360
+ 'bank_account' => 'bank_account-input',
361
+ 'created_at' => 'created_at-input',
362
+ 'customer' => 'customer-input',
363
+ 'id' => 'id-input',
364
+ 'incomplete_fields' => 'incomplete_fields-input',
365
+ 'links' => 'links-input',
366
+ 'mandate' => 'mandate-input',
367
+ 'status' => 'status-input',
368
+ },
369
+ }.to_json,
370
+ headers: response_headers
371
+ )
372
+ end
373
+
374
+ it 'updates and returns the resource' do
375
+ expect(put_update_response).to be_a(GoCardlessPro::Resources::PayerAuthorisation)
376
+ expect(stub).to have_been_requested
377
+ end
378
+
379
+ describe 'retry behaviour' do
380
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
381
+
382
+ it 'retries timeouts' do
383
+ stub_url = '/payer_authorisations/:identity'.gsub(':identity', id)
384
+ stub = stub_request(:put, /.*api.gocardless.com#{stub_url}/).
385
+ to_timeout.then.to_return(status: 200, headers: response_headers)
386
+
387
+ put_update_response
388
+ expect(stub).to have_been_requested.twice
389
+ end
390
+
391
+ it 'retries 5XX errors' do
392
+ stub_url = '/payer_authorisations/:identity'.gsub(':identity', id)
393
+ stub = stub_request(:put, /.*api.gocardless.com#{stub_url}/).
394
+ to_return(status: 502,
395
+ headers: { 'Content-Type' => 'text/html' },
396
+ body: '<html><body>Response from Cloudflare</body></html>').
397
+ then.to_return(status: 200, headers: response_headers)
398
+
399
+ put_update_response
400
+ expect(stub).to have_been_requested.twice
401
+ end
402
+ end
403
+ end
404
+ end
405
+
406
+ describe '#submit' do
407
+ subject(:post_response) { client.payer_authorisations.submit(resource_id) }
408
+
409
+ let(:resource_id) { 'ABC123' }
410
+
411
+ let!(:stub) do
412
+ # /payer_authorisations/%v/actions/submit
413
+ stub_url = '/payer_authorisations/:identity/actions/submit'.gsub(':identity', resource_id)
414
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).to_return(
415
+ body: {
416
+ 'payer_authorisations' => {
417
+
418
+ 'bank_account' => 'bank_account-input',
419
+ 'created_at' => 'created_at-input',
420
+ 'customer' => 'customer-input',
421
+ 'id' => 'id-input',
422
+ 'incomplete_fields' => 'incomplete_fields-input',
423
+ 'links' => 'links-input',
424
+ 'mandate' => 'mandate-input',
425
+ 'status' => 'status-input',
426
+ },
427
+ }.to_json,
428
+ headers: response_headers
429
+ )
430
+ end
431
+
432
+ it 'wraps the response and calls the right endpoint' do
433
+ expect(post_response).to be_a(GoCardlessPro::Resources::PayerAuthorisation)
434
+
435
+ expect(stub).to have_been_requested
436
+ end
437
+
438
+ describe 'retry behaviour' do
439
+ it "doesn't retry errors" do
440
+ stub_url = '/payer_authorisations/:identity/actions/submit'.gsub(':identity', resource_id)
441
+ stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
442
+ to_timeout
443
+
444
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
445
+ expect(stub).to have_been_requested
446
+ end
447
+ end
448
+
449
+ context 'when the request needs a body and custom header' do
450
+ let(:body) { { foo: 'bar' } }
451
+ let(:headers) { { 'Foo' => 'Bar' } }
452
+ subject(:post_response) { client.payer_authorisations.submit(resource_id, body, headers) }
453
+
454
+ let(:resource_id) { 'ABC123' }
455
+
456
+ let!(:stub) do
457
+ # /payer_authorisations/%v/actions/submit
458
+ stub_url = '/payer_authorisations/:identity/actions/submit'.gsub(':identity', resource_id)
459
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).
460
+ with(
461
+ body: { foo: 'bar' },
462
+ headers: { 'Foo' => 'Bar' }
463
+ ).to_return(
464
+ body: {
465
+ 'payer_authorisations' => {
466
+
467
+ 'bank_account' => 'bank_account-input',
468
+ 'created_at' => 'created_at-input',
469
+ 'customer' => 'customer-input',
470
+ 'id' => 'id-input',
471
+ 'incomplete_fields' => 'incomplete_fields-input',
472
+ 'links' => 'links-input',
473
+ 'mandate' => 'mandate-input',
474
+ 'status' => 'status-input',
475
+ },
476
+ }.to_json,
477
+ headers: response_headers
478
+ )
479
+ end
480
+ end
481
+ end
482
+
483
+ describe '#confirm' do
484
+ subject(:post_response) { client.payer_authorisations.confirm(resource_id) }
485
+
486
+ let(:resource_id) { 'ABC123' }
487
+
488
+ let!(:stub) do
489
+ # /payer_authorisations/%v/actions/confirm
490
+ stub_url = '/payer_authorisations/:identity/actions/confirm'.gsub(':identity', resource_id)
491
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).to_return(
492
+ body: {
493
+ 'payer_authorisations' => {
494
+
495
+ 'bank_account' => 'bank_account-input',
496
+ 'created_at' => 'created_at-input',
497
+ 'customer' => 'customer-input',
498
+ 'id' => 'id-input',
499
+ 'incomplete_fields' => 'incomplete_fields-input',
500
+ 'links' => 'links-input',
501
+ 'mandate' => 'mandate-input',
502
+ 'status' => 'status-input',
503
+ },
504
+ }.to_json,
505
+ headers: response_headers
506
+ )
507
+ end
508
+
509
+ it 'wraps the response and calls the right endpoint' do
510
+ expect(post_response).to be_a(GoCardlessPro::Resources::PayerAuthorisation)
511
+
512
+ expect(stub).to have_been_requested
513
+ end
514
+
515
+ describe 'retry behaviour' do
516
+ it "doesn't retry errors" do
517
+ stub_url = '/payer_authorisations/:identity/actions/confirm'.gsub(':identity', resource_id)
518
+ stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
519
+ to_timeout
520
+
521
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
522
+ expect(stub).to have_been_requested
523
+ end
524
+ end
525
+
526
+ context 'when the request needs a body and custom header' do
527
+ let(:body) { { foo: 'bar' } }
528
+ let(:headers) { { 'Foo' => 'Bar' } }
529
+ subject(:post_response) { client.payer_authorisations.confirm(resource_id, body, headers) }
530
+
531
+ let(:resource_id) { 'ABC123' }
532
+
533
+ let!(:stub) do
534
+ # /payer_authorisations/%v/actions/confirm
535
+ stub_url = '/payer_authorisations/:identity/actions/confirm'.gsub(':identity', resource_id)
536
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).
537
+ with(
538
+ body: { foo: 'bar' },
539
+ headers: { 'Foo' => 'Bar' }
540
+ ).to_return(
541
+ body: {
542
+ 'payer_authorisations' => {
543
+
544
+ 'bank_account' => 'bank_account-input',
545
+ 'created_at' => 'created_at-input',
546
+ 'customer' => 'customer-input',
547
+ 'id' => 'id-input',
548
+ 'incomplete_fields' => 'incomplete_fields-input',
549
+ 'links' => 'links-input',
550
+ 'mandate' => 'mandate-input',
551
+ 'status' => 'status-input',
552
+ },
553
+ }.to_json,
554
+ headers: response_headers
555
+ )
556
+ end
557
+ end
558
+ end
559
+ end