gocardless_pro 2.23.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 (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