gocardless_pro 2.21.0 → 2.25.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +25 -43
  3. data/Gemfile +7 -0
  4. data/gocardless_pro.gemspec +2 -2
  5. data/lib/gocardless_pro.rb +6 -0
  6. data/lib/gocardless_pro/api_service.rb +1 -1
  7. data/lib/gocardless_pro/client.rb +11 -1
  8. data/lib/gocardless_pro/resources/event.rb +12 -0
  9. data/lib/gocardless_pro/resources/payer_authorisation.rb +119 -0
  10. data/lib/gocardless_pro/resources/payout.rb +2 -0
  11. data/lib/gocardless_pro/resources/payout_item.rb +6 -0
  12. data/lib/gocardless_pro/resources/tax_rate.rb +51 -0
  13. data/lib/gocardless_pro/services/bank_details_lookups_service.rb +0 -10
  14. data/lib/gocardless_pro/services/base_service.rb +11 -0
  15. data/lib/gocardless_pro/services/creditor_bank_accounts_service.rb +0 -10
  16. data/lib/gocardless_pro/services/creditors_service.rb +0 -10
  17. data/lib/gocardless_pro/services/currency_exchange_rates_service.rb +0 -10
  18. data/lib/gocardless_pro/services/customer_bank_accounts_service.rb +0 -10
  19. data/lib/gocardless_pro/services/customer_notifications_service.rb +0 -10
  20. data/lib/gocardless_pro/services/customers_service.rb +0 -10
  21. data/lib/gocardless_pro/services/events_service.rb +0 -10
  22. data/lib/gocardless_pro/services/instalment_schedules_service.rb +0 -10
  23. data/lib/gocardless_pro/services/mandate_import_entries_service.rb +0 -10
  24. data/lib/gocardless_pro/services/mandate_imports_service.rb +0 -10
  25. data/lib/gocardless_pro/services/mandate_pdfs_service.rb +0 -10
  26. data/lib/gocardless_pro/services/mandates_service.rb +0 -10
  27. data/lib/gocardless_pro/services/payer_authorisations_service.rb +208 -0
  28. data/lib/gocardless_pro/services/payments_service.rb +0 -10
  29. data/lib/gocardless_pro/services/payout_items_service.rb +0 -10
  30. data/lib/gocardless_pro/services/payouts_service.rb +0 -10
  31. data/lib/gocardless_pro/services/redirect_flows_service.rb +0 -10
  32. data/lib/gocardless_pro/services/refunds_service.rb +6 -11
  33. data/lib/gocardless_pro/services/subscriptions_service.rb +1 -11
  34. data/lib/gocardless_pro/services/tax_rates_service.rb +74 -0
  35. data/lib/gocardless_pro/version.rb +1 -1
  36. data/spec/resources/payer_authorisation_spec.rb +418 -0
  37. data/spec/resources/payout_item_spec.rb +5 -0
  38. data/spec/resources/payout_spec.rb +8 -0
  39. data/spec/resources/tax_rate_spec.rb +198 -0
  40. data/spec/services/creditor_bank_accounts_service_spec.rb +1 -1
  41. data/spec/services/customer_bank_accounts_service_spec.rb +1 -1
  42. data/spec/services/customer_notifications_service_spec.rb +1 -1
  43. data/spec/services/customers_service_spec.rb +1 -1
  44. data/spec/services/instalment_schedules_service_spec.rb +1 -1
  45. data/spec/services/mandate_imports_service_spec.rb +2 -2
  46. data/spec/services/mandates_service_spec.rb +2 -2
  47. data/spec/services/payer_authorisations_service_spec.rb +572 -0
  48. data/spec/services/payments_service_spec.rb +2 -2
  49. data/spec/services/payout_items_service_spec.rb +9 -0
  50. data/spec/services/payouts_service_spec.rb +12 -0
  51. data/spec/services/redirect_flows_service_spec.rb +1 -1
  52. data/spec/services/subscriptions_service_spec.rb +3 -3
  53. data/spec/services/tax_rates_service_spec.rb +381 -0
  54. metadata +19 -7
@@ -20,6 +20,7 @@ describe GoCardlessPro::Resources::PayoutItem do
20
20
 
21
21
  'amount' => 'amount-input',
22
22
  'links' => 'links-input',
23
+ 'taxes' => 'taxes-input',
23
24
  'type' => 'type-input',
24
25
  }],
25
26
  meta: {
@@ -38,6 +39,8 @@ describe GoCardlessPro::Resources::PayoutItem do
38
39
 
39
40
  expect(get_list_response.records.first.amount).to eq('amount-input')
40
41
 
42
+ expect(get_list_response.records.first.taxes).to eq('taxes-input')
43
+
41
44
  expect(get_list_response.records.first.type).to eq('type-input')
42
45
  end
43
46
 
@@ -58,6 +61,7 @@ describe GoCardlessPro::Resources::PayoutItem do
58
61
 
59
62
  'amount' => 'amount-input',
60
63
  'links' => 'links-input',
64
+ 'taxes' => 'taxes-input',
61
65
  'type' => 'type-input',
62
66
  }],
63
67
  meta: {
@@ -76,6 +80,7 @@ describe GoCardlessPro::Resources::PayoutItem do
76
80
 
77
81
  'amount' => 'amount-input',
78
82
  'links' => 'links-input',
83
+ 'taxes' => 'taxes-input',
79
84
  'type' => 'type-input',
80
85
  }],
81
86
  meta: {
@@ -30,6 +30,7 @@ describe GoCardlessPro::Resources::Payout do
30
30
  'payout_type' => 'payout_type-input',
31
31
  'reference' => 'reference-input',
32
32
  'status' => 'status-input',
33
+ 'tax_currency' => 'tax_currency-input',
33
34
  }],
34
35
  meta: {
35
36
  cursors: {
@@ -66,6 +67,8 @@ describe GoCardlessPro::Resources::Payout do
66
67
  expect(get_list_response.records.first.reference).to eq('reference-input')
67
68
 
68
69
  expect(get_list_response.records.first.status).to eq('status-input')
70
+
71
+ expect(get_list_response.records.first.tax_currency).to eq('tax_currency-input')
69
72
  end
70
73
 
71
74
  it 'exposes the cursors for before and after' do
@@ -95,6 +98,7 @@ describe GoCardlessPro::Resources::Payout do
95
98
  'payout_type' => 'payout_type-input',
96
99
  'reference' => 'reference-input',
97
100
  'status' => 'status-input',
101
+ 'tax_currency' => 'tax_currency-input',
98
102
  }],
99
103
  meta: {
100
104
  cursors: { after: 'AB345' },
@@ -122,6 +126,7 @@ describe GoCardlessPro::Resources::Payout do
122
126
  'payout_type' => 'payout_type-input',
123
127
  'reference' => 'reference-input',
124
128
  'status' => 'status-input',
129
+ 'tax_currency' => 'tax_currency-input',
125
130
  }],
126
131
  meta: {
127
132
  limit: 2,
@@ -165,6 +170,7 @@ describe GoCardlessPro::Resources::Payout do
165
170
  'payout_type' => 'payout_type-input',
166
171
  'reference' => 'reference-input',
167
172
  'status' => 'status-input',
173
+ 'tax_currency' => 'tax_currency-input',
168
174
  },
169
175
  }.to_json,
170
176
  headers: response_headers
@@ -202,6 +208,7 @@ describe GoCardlessPro::Resources::Payout do
202
208
  'payout_type' => 'payout_type-input',
203
209
  'reference' => 'reference-input',
204
210
  'status' => 'status-input',
211
+ 'tax_currency' => 'tax_currency-input',
205
212
  },
206
213
  }.to_json,
207
214
  headers: response_headers
@@ -261,6 +268,7 @@ describe GoCardlessPro::Resources::Payout do
261
268
  'payout_type' => 'payout_type-input',
262
269
  'reference' => 'reference-input',
263
270
  'status' => 'status-input',
271
+ 'tax_currency' => 'tax_currency-input',
264
272
  },
265
273
  }.to_json,
266
274
  headers: response_headers
@@ -0,0 +1,198 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoCardlessPro::Resources::TaxRate 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.tax_rates.list }
15
+
16
+ before do
17
+ stub_request(:get, %r{.*api.gocardless.com/tax_rates}).to_return(
18
+ body: {
19
+ 'tax_rates' => [{
20
+
21
+ 'end_date' => 'end_date-input',
22
+ 'id' => 'id-input',
23
+ 'jurisdiction' => 'jurisdiction-input',
24
+ 'percentage' => 'percentage-input',
25
+ 'start_date' => 'start_date-input',
26
+ 'type' => 'type-input',
27
+ }],
28
+ meta: {
29
+ cursors: {
30
+ before: nil,
31
+ after: 'ABC123',
32
+ },
33
+ },
34
+ }.to_json,
35
+ headers: response_headers
36
+ )
37
+ end
38
+
39
+ it 'wraps each item in the resource class' do
40
+ expect(get_list_response.records.map(&:class).uniq.first).to eq(GoCardlessPro::Resources::TaxRate)
41
+
42
+ expect(get_list_response.records.first.end_date).to eq('end_date-input')
43
+
44
+ expect(get_list_response.records.first.id).to eq('id-input')
45
+
46
+ expect(get_list_response.records.first.jurisdiction).to eq('jurisdiction-input')
47
+
48
+ expect(get_list_response.records.first.percentage).to eq('percentage-input')
49
+
50
+ expect(get_list_response.records.first.start_date).to eq('start_date-input')
51
+
52
+ expect(get_list_response.records.first.type).to eq('type-input')
53
+ end
54
+
55
+ it 'exposes the cursors for before and after' do
56
+ expect(get_list_response.before).to eq(nil)
57
+ expect(get_list_response.after).to eq('ABC123')
58
+ end
59
+
60
+ specify { expect(get_list_response.api_response.headers).to eql('content-type' => 'application/json') }
61
+ end
62
+ end
63
+
64
+ describe '#all' do
65
+ let!(:first_response_stub) do
66
+ stub_request(:get, %r{.*api.gocardless.com/tax_rates$}).to_return(
67
+ body: {
68
+ 'tax_rates' => [{
69
+
70
+ 'end_date' => 'end_date-input',
71
+ 'id' => 'id-input',
72
+ 'jurisdiction' => 'jurisdiction-input',
73
+ 'percentage' => 'percentage-input',
74
+ 'start_date' => 'start_date-input',
75
+ 'type' => 'type-input',
76
+ }],
77
+ meta: {
78
+ cursors: { after: 'AB345' },
79
+ limit: 1,
80
+ },
81
+ }.to_json,
82
+ headers: response_headers
83
+ )
84
+ end
85
+
86
+ let!(:second_response_stub) do
87
+ stub_request(:get, %r{.*api.gocardless.com/tax_rates\?after=AB345}).to_return(
88
+ body: {
89
+ 'tax_rates' => [{
90
+
91
+ 'end_date' => 'end_date-input',
92
+ 'id' => 'id-input',
93
+ 'jurisdiction' => 'jurisdiction-input',
94
+ 'percentage' => 'percentage-input',
95
+ 'start_date' => 'start_date-input',
96
+ 'type' => 'type-input',
97
+ }],
98
+ meta: {
99
+ limit: 2,
100
+ cursors: {},
101
+ },
102
+ }.to_json,
103
+ headers: response_headers
104
+ )
105
+ end
106
+
107
+ it 'automatically makes the extra requests' do
108
+ expect(client.tax_rates.all.to_a.length).to eq(2)
109
+ expect(first_response_stub).to have_been_requested
110
+ expect(second_response_stub).to have_been_requested
111
+ end
112
+ end
113
+
114
+ describe '#get' do
115
+ let(:id) { 'ID123' }
116
+
117
+ subject(:get_response) { client.tax_rates.get(id) }
118
+
119
+ context 'passing in a custom header' do
120
+ let!(:stub) do
121
+ stub_url = '/tax_rates/:identity'.gsub(':identity', id)
122
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).
123
+ with(headers: { 'Foo' => 'Bar' }).
124
+ to_return(
125
+ body: {
126
+ 'tax_rates' => {
127
+
128
+ 'end_date' => 'end_date-input',
129
+ 'id' => 'id-input',
130
+ 'jurisdiction' => 'jurisdiction-input',
131
+ 'percentage' => 'percentage-input',
132
+ 'start_date' => 'start_date-input',
133
+ 'type' => 'type-input',
134
+ },
135
+ }.to_json,
136
+ headers: response_headers
137
+ )
138
+ end
139
+
140
+ subject(:get_response) do
141
+ client.tax_rates.get(id, headers: {
142
+ 'Foo' => 'Bar',
143
+ })
144
+ end
145
+
146
+ it 'includes the header' do
147
+ get_response
148
+ expect(stub).to have_been_requested
149
+ end
150
+ end
151
+
152
+ context 'when there is a tax_rate to return' do
153
+ before do
154
+ stub_url = '/tax_rates/:identity'.gsub(':identity', id)
155
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
156
+ body: {
157
+ 'tax_rates' => {
158
+
159
+ 'end_date' => 'end_date-input',
160
+ 'id' => 'id-input',
161
+ 'jurisdiction' => 'jurisdiction-input',
162
+ 'percentage' => 'percentage-input',
163
+ 'start_date' => 'start_date-input',
164
+ 'type' => 'type-input',
165
+ },
166
+ }.to_json,
167
+ headers: response_headers
168
+ )
169
+ end
170
+
171
+ it 'wraps the response in a resource' do
172
+ expect(get_response).to be_a(GoCardlessPro::Resources::TaxRate)
173
+ end
174
+ end
175
+
176
+ context 'when nothing is returned' do
177
+ before do
178
+ stub_url = '/tax_rates/:identity'.gsub(':identity', id)
179
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
180
+ body: '',
181
+ headers: response_headers
182
+ )
183
+ end
184
+
185
+ it 'returns nil' do
186
+ expect(get_response).to be_nil
187
+ end
188
+ end
189
+
190
+ context "when an ID is specified which can't be included in a valid URI" do
191
+ let(:id) { '`' }
192
+
193
+ it "doesn't raise an error" do
194
+ expect { get_response }.to_not raise_error(/bad URI/)
195
+ end
196
+ end
197
+ end
198
+ end
@@ -692,7 +692,7 @@ describe GoCardlessPro::Services::CreditorBankAccountsService do
692
692
  stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
693
693
  to_timeout
694
694
 
695
- expect { post_response }.to raise_error(Faraday::TimeoutError)
695
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
696
696
  expect(stub).to have_been_requested
697
697
  end
698
698
  end
@@ -754,7 +754,7 @@ describe GoCardlessPro::Services::CustomerBankAccountsService do
754
754
  stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
755
755
  to_timeout
756
756
 
757
- expect { post_response }.to raise_error(Faraday::TimeoutError)
757
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
758
758
  expect(stub).to have_been_requested
759
759
  end
760
760
  end
@@ -45,7 +45,7 @@ describe GoCardlessPro::Services::CustomerNotificationsService do
45
45
  stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
46
46
  to_timeout
47
47
 
48
- expect { post_response }.to raise_error(Faraday::TimeoutError)
48
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
49
49
  expect(stub).to have_been_requested
50
50
  end
51
51
  end
@@ -882,7 +882,7 @@ describe GoCardlessPro::Services::CustomersService do
882
882
  stub = stub_request(:delete, /.*api.gocardless.com#{stub_url}/).
883
883
  to_timeout
884
884
 
885
- expect { delete_response }.to raise_error(Faraday::TimeoutError)
885
+ expect { delete_response }.to raise_error(Faraday::ConnectionFailed)
886
886
  expect(stub).to have_been_requested
887
887
  end
888
888
  end
@@ -927,7 +927,7 @@ describe GoCardlessPro::Services::InstalmentSchedulesService do
927
927
  stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
928
928
  to_timeout
929
929
 
930
- expect { post_response }.to raise_error(Faraday::TimeoutError)
930
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
931
931
  expect(stub).to have_been_requested
932
932
  end
933
933
  end
@@ -363,7 +363,7 @@ describe GoCardlessPro::Services::MandateImportsService do
363
363
  stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
364
364
  to_timeout
365
365
 
366
- expect { post_response }.to raise_error(Faraday::TimeoutError)
366
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
367
367
  expect(stub).to have_been_requested
368
368
  end
369
369
  end
@@ -432,7 +432,7 @@ describe GoCardlessPro::Services::MandateImportsService do
432
432
  stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
433
433
  to_timeout
434
434
 
435
- expect { post_response }.to raise_error(Faraday::TimeoutError)
435
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
436
436
  expect(stub).to have_been_requested
437
437
  end
438
438
  end
@@ -718,7 +718,7 @@ describe GoCardlessPro::Services::MandatesService do
718
718
  stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
719
719
  to_timeout
720
720
 
721
- expect { post_response }.to raise_error(Faraday::TimeoutError)
721
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
722
722
  expect(stub).to have_been_requested
723
723
  end
724
724
  end
@@ -797,7 +797,7 @@ describe GoCardlessPro::Services::MandatesService do
797
797
  stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
798
798
  to_timeout
799
799
 
800
- expect { post_response }.to raise_error(Faraday::TimeoutError)
800
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
801
801
  expect(stub).to have_been_requested
802
802
  end
803
803
  end
@@ -0,0 +1,572 @@
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
+
345
+ context 'with on_idempotency_conflict: :unknown' do
346
+ let(:client) do
347
+ GoCardlessPro::Client.new(
348
+ access_token: 'SECRET_TOKEN',
349
+ on_idempotency_conflict: :unknown
350
+ )
351
+ end
352
+
353
+ it 'raises an ArgumentError' do
354
+ expect { post_create_response }.to raise_error(ArgumentError)
355
+ end
356
+ end
357
+ end
358
+ end
359
+
360
+ describe '#update' do
361
+ subject(:put_update_response) { client.payer_authorisations.update(id, params: update_params) }
362
+ let(:id) { 'ABC123' }
363
+
364
+ context 'with a valid request' do
365
+ let(:update_params) { { 'hello' => 'world' } }
366
+
367
+ let!(:stub) do
368
+ stub_url = '/payer_authorisations/:identity'.gsub(':identity', id)
369
+ stub_request(:put, /.*api.gocardless.com#{stub_url}/).to_return(
370
+ body: {
371
+ 'payer_authorisations' => {
372
+
373
+ 'bank_account' => 'bank_account-input',
374
+ 'created_at' => 'created_at-input',
375
+ 'customer' => 'customer-input',
376
+ 'id' => 'id-input',
377
+ 'incomplete_fields' => 'incomplete_fields-input',
378
+ 'links' => 'links-input',
379
+ 'mandate' => 'mandate-input',
380
+ 'status' => 'status-input',
381
+ },
382
+ }.to_json,
383
+ headers: response_headers
384
+ )
385
+ end
386
+
387
+ it 'updates and returns the resource' do
388
+ expect(put_update_response).to be_a(GoCardlessPro::Resources::PayerAuthorisation)
389
+ expect(stub).to have_been_requested
390
+ end
391
+
392
+ describe 'retry behaviour' do
393
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
394
+
395
+ it 'retries timeouts' do
396
+ stub_url = '/payer_authorisations/:identity'.gsub(':identity', id)
397
+ stub = stub_request(:put, /.*api.gocardless.com#{stub_url}/).
398
+ to_timeout.then.to_return(status: 200, headers: response_headers)
399
+
400
+ put_update_response
401
+ expect(stub).to have_been_requested.twice
402
+ end
403
+
404
+ it 'retries 5XX errors' do
405
+ stub_url = '/payer_authorisations/:identity'.gsub(':identity', id)
406
+ stub = stub_request(:put, /.*api.gocardless.com#{stub_url}/).
407
+ to_return(status: 502,
408
+ headers: { 'Content-Type' => 'text/html' },
409
+ body: '<html><body>Response from Cloudflare</body></html>').
410
+ then.to_return(status: 200, headers: response_headers)
411
+
412
+ put_update_response
413
+ expect(stub).to have_been_requested.twice
414
+ end
415
+ end
416
+ end
417
+ end
418
+
419
+ describe '#submit' do
420
+ subject(:post_response) { client.payer_authorisations.submit(resource_id) }
421
+
422
+ let(:resource_id) { 'ABC123' }
423
+
424
+ let!(:stub) do
425
+ # /payer_authorisations/%v/actions/submit
426
+ stub_url = '/payer_authorisations/:identity/actions/submit'.gsub(':identity', resource_id)
427
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).to_return(
428
+ body: {
429
+ 'payer_authorisations' => {
430
+
431
+ 'bank_account' => 'bank_account-input',
432
+ 'created_at' => 'created_at-input',
433
+ 'customer' => 'customer-input',
434
+ 'id' => 'id-input',
435
+ 'incomplete_fields' => 'incomplete_fields-input',
436
+ 'links' => 'links-input',
437
+ 'mandate' => 'mandate-input',
438
+ 'status' => 'status-input',
439
+ },
440
+ }.to_json,
441
+ headers: response_headers
442
+ )
443
+ end
444
+
445
+ it 'wraps the response and calls the right endpoint' do
446
+ expect(post_response).to be_a(GoCardlessPro::Resources::PayerAuthorisation)
447
+
448
+ expect(stub).to have_been_requested
449
+ end
450
+
451
+ describe 'retry behaviour' do
452
+ it "doesn't retry errors" do
453
+ stub_url = '/payer_authorisations/:identity/actions/submit'.gsub(':identity', resource_id)
454
+ stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
455
+ to_timeout
456
+
457
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
458
+ expect(stub).to have_been_requested
459
+ end
460
+ end
461
+
462
+ context 'when the request needs a body and custom header' do
463
+ let(:body) { { foo: 'bar' } }
464
+ let(:headers) { { 'Foo' => 'Bar' } }
465
+ subject(:post_response) { client.payer_authorisations.submit(resource_id, body, headers) }
466
+
467
+ let(:resource_id) { 'ABC123' }
468
+
469
+ let!(:stub) do
470
+ # /payer_authorisations/%v/actions/submit
471
+ stub_url = '/payer_authorisations/:identity/actions/submit'.gsub(':identity', resource_id)
472
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).
473
+ with(
474
+ body: { foo: 'bar' },
475
+ headers: { 'Foo' => 'Bar' }
476
+ ).to_return(
477
+ body: {
478
+ 'payer_authorisations' => {
479
+
480
+ 'bank_account' => 'bank_account-input',
481
+ 'created_at' => 'created_at-input',
482
+ 'customer' => 'customer-input',
483
+ 'id' => 'id-input',
484
+ 'incomplete_fields' => 'incomplete_fields-input',
485
+ 'links' => 'links-input',
486
+ 'mandate' => 'mandate-input',
487
+ 'status' => 'status-input',
488
+ },
489
+ }.to_json,
490
+ headers: response_headers
491
+ )
492
+ end
493
+ end
494
+ end
495
+
496
+ describe '#confirm' do
497
+ subject(:post_response) { client.payer_authorisations.confirm(resource_id) }
498
+
499
+ let(:resource_id) { 'ABC123' }
500
+
501
+ let!(:stub) do
502
+ # /payer_authorisations/%v/actions/confirm
503
+ stub_url = '/payer_authorisations/:identity/actions/confirm'.gsub(':identity', resource_id)
504
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).to_return(
505
+ body: {
506
+ 'payer_authorisations' => {
507
+
508
+ 'bank_account' => 'bank_account-input',
509
+ 'created_at' => 'created_at-input',
510
+ 'customer' => 'customer-input',
511
+ 'id' => 'id-input',
512
+ 'incomplete_fields' => 'incomplete_fields-input',
513
+ 'links' => 'links-input',
514
+ 'mandate' => 'mandate-input',
515
+ 'status' => 'status-input',
516
+ },
517
+ }.to_json,
518
+ headers: response_headers
519
+ )
520
+ end
521
+
522
+ it 'wraps the response and calls the right endpoint' do
523
+ expect(post_response).to be_a(GoCardlessPro::Resources::PayerAuthorisation)
524
+
525
+ expect(stub).to have_been_requested
526
+ end
527
+
528
+ describe 'retry behaviour' do
529
+ it "doesn't retry errors" do
530
+ stub_url = '/payer_authorisations/:identity/actions/confirm'.gsub(':identity', resource_id)
531
+ stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/).
532
+ to_timeout
533
+
534
+ expect { post_response }.to raise_error(Faraday::ConnectionFailed)
535
+ expect(stub).to have_been_requested
536
+ end
537
+ end
538
+
539
+ context 'when the request needs a body and custom header' do
540
+ let(:body) { { foo: 'bar' } }
541
+ let(:headers) { { 'Foo' => 'Bar' } }
542
+ subject(:post_response) { client.payer_authorisations.confirm(resource_id, body, headers) }
543
+
544
+ let(:resource_id) { 'ABC123' }
545
+
546
+ let!(:stub) do
547
+ # /payer_authorisations/%v/actions/confirm
548
+ stub_url = '/payer_authorisations/:identity/actions/confirm'.gsub(':identity', resource_id)
549
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).
550
+ with(
551
+ body: { foo: 'bar' },
552
+ headers: { 'Foo' => 'Bar' }
553
+ ).to_return(
554
+ body: {
555
+ 'payer_authorisations' => {
556
+
557
+ 'bank_account' => 'bank_account-input',
558
+ 'created_at' => 'created_at-input',
559
+ 'customer' => 'customer-input',
560
+ 'id' => 'id-input',
561
+ 'incomplete_fields' => 'incomplete_fields-input',
562
+ 'links' => 'links-input',
563
+ 'mandate' => 'mandate-input',
564
+ 'status' => 'status-input',
565
+ },
566
+ }.to_json,
567
+ headers: response_headers
568
+ )
569
+ end
570
+ end
571
+ end
572
+ end