gocardless_pro 2.17.1 → 2.21.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 (30) hide show
  1. checksums.yaml +4 -4
  2. data/lib/gocardless_pro.rb +3 -0
  3. data/lib/gocardless_pro/client.rb +6 -1
  4. data/lib/gocardless_pro/resources/creditor_bank_account.rb +1 -2
  5. data/lib/gocardless_pro/resources/currency_exchange_rate.rb +44 -0
  6. data/lib/gocardless_pro/resources/customer_notification.rb +3 -5
  7. data/lib/gocardless_pro/resources/event.rb +2 -1
  8. data/lib/gocardless_pro/resources/mandate_import.rb +5 -8
  9. data/lib/gocardless_pro/resources/mandate_import_entry.rb +3 -5
  10. data/lib/gocardless_pro/resources/payout.rb +2 -0
  11. data/lib/gocardless_pro/resources/redirect_flow.rb +2 -0
  12. data/lib/gocardless_pro/resources/subscription.rb +38 -29
  13. data/lib/gocardless_pro/services/currency_exchange_rates_service.rb +67 -0
  14. data/lib/gocardless_pro/services/customers_service.rb +1 -2
  15. data/lib/gocardless_pro/services/instalment_schedules_service.rb +81 -5
  16. data/lib/gocardless_pro/services/mandates_service.rb +1 -1
  17. data/lib/gocardless_pro/services/payouts_service.rb +21 -0
  18. data/lib/gocardless_pro/services/subscriptions_service.rb +129 -0
  19. data/lib/gocardless_pro/version.rb +1 -1
  20. data/spec/resources/currency_exchange_rate_spec.rb +103 -0
  21. data/spec/resources/instalment_schedule_spec.rb +193 -1
  22. data/spec/resources/payout_spec.rb +45 -0
  23. data/spec/resources/redirect_flow_spec.rb +9 -0
  24. data/spec/resources/subscription_spec.rb +210 -0
  25. data/spec/services/currency_exchange_rates_service_spec.rb +223 -0
  26. data/spec/services/instalment_schedules_service_spec.rb +270 -1
  27. data/spec/services/payouts_service_spec.rb +74 -0
  28. data/spec/services/redirect_flows_service_spec.rb +9 -0
  29. data/spec/services/subscriptions_service_spec.rb +240 -0
  30. metadata +9 -3
@@ -0,0 +1,223 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoCardlessPro::Services::CurrencyExchangeRatesService 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.currency_exchange_rates.list }
15
+
16
+ let(:body) do
17
+ {
18
+ 'currency_exchange_rates' => [{
19
+
20
+ 'rate' => 'rate-input',
21
+ 'source' => 'source-input',
22
+ 'target' => 'target-input',
23
+ 'time' => 'time-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/currency_exchange_rates}).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::CurrencyExchangeRate)
43
+
44
+ expect(get_list_response.records.first.rate).to eq('rate-input')
45
+
46
+ expect(get_list_response.records.first.source).to eq('source-input')
47
+
48
+ expect(get_list_response.records.first.target).to eq('target-input')
49
+
50
+ expect(get_list_response.records.first.time).to eq('time-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/currency_exchange_rates}).
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/currency_exchange_rates}).
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/currency_exchange_rates$}).to_return(
88
+ body: {
89
+ 'currency_exchange_rates' => [{
90
+
91
+ 'rate' => 'rate-input',
92
+ 'source' => 'source-input',
93
+ 'target' => 'target-input',
94
+ 'time' => 'time-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/currency_exchange_rates\?after=AB345}).to_return(
107
+ body: {
108
+ 'currency_exchange_rates' => [{
109
+
110
+ 'rate' => 'rate-input',
111
+ 'source' => 'source-input',
112
+ 'target' => 'target-input',
113
+ 'time' => 'time-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.currency_exchange_rates.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/currency_exchange_rates$}).to_return(
135
+ body: {
136
+ 'currency_exchange_rates' => [{
137
+
138
+ 'rate' => 'rate-input',
139
+ 'source' => 'source-input',
140
+ 'target' => 'target-input',
141
+ 'time' => 'time-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/currency_exchange_rates\?after=AB345}).
152
+ to_timeout.then.
153
+ to_return(
154
+ body: {
155
+ 'currency_exchange_rates' => [{
156
+
157
+ 'rate' => 'rate-input',
158
+ 'source' => 'source-input',
159
+ 'target' => 'target-input',
160
+ 'time' => 'time-input',
161
+ }],
162
+ meta: {
163
+ limit: 2,
164
+ cursors: {},
165
+ },
166
+ }.to_json,
167
+ headers: response_headers
168
+ )
169
+
170
+ client.currency_exchange_rates.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/currency_exchange_rates$}).to_return(
178
+ body: {
179
+ 'currency_exchange_rates' => [{
180
+
181
+ 'rate' => 'rate-input',
182
+ 'source' => 'source-input',
183
+ 'target' => 'target-input',
184
+ 'time' => 'time-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/currency_exchange_rates\?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
+ 'currency_exchange_rates' => [{
202
+
203
+ 'rate' => 'rate-input',
204
+ 'source' => 'source-input',
205
+ 'target' => 'target-input',
206
+ 'time' => 'time-input',
207
+ }],
208
+ meta: {
209
+ limit: 2,
210
+ cursors: {},
211
+ },
212
+ }.to_json,
213
+ headers: response_headers
214
+ )
215
+
216
+ client.currency_exchange_rates.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
@@ -10,7 +10,216 @@ describe GoCardlessPro::Services::InstalmentSchedulesService do
10
10
  let(:response_headers) { { 'Content-Type' => 'application/json' } }
11
11
 
12
12
  describe '#create' do
13
- subject(:post_create_response) { client.instalment_schedules.create(params: new_resource) }
13
+ subject(:post_create_response) { client.instalment_schedules.create_with_dates(params: new_resource) }
14
+ context 'with a valid request' do
15
+ let(:new_resource) do
16
+ {
17
+
18
+ 'created_at' => 'created_at-input',
19
+ 'currency' => 'currency-input',
20
+ 'id' => 'id-input',
21
+ 'links' => 'links-input',
22
+ 'metadata' => 'metadata-input',
23
+ 'name' => 'name-input',
24
+ 'payment_errors' => 'payment_errors-input',
25
+ 'status' => 'status-input',
26
+ 'total_amount' => 'total_amount-input',
27
+ }
28
+ end
29
+
30
+ before do
31
+ stub_request(:post, %r{.*api.gocardless.com/instalment_schedules}).
32
+ with(
33
+ body: {
34
+ 'instalment_schedules' => {
35
+
36
+ 'created_at' => 'created_at-input',
37
+ 'currency' => 'currency-input',
38
+ 'id' => 'id-input',
39
+ 'links' => 'links-input',
40
+ 'metadata' => 'metadata-input',
41
+ 'name' => 'name-input',
42
+ 'payment_errors' => 'payment_errors-input',
43
+ 'status' => 'status-input',
44
+ 'total_amount' => 'total_amount-input',
45
+ },
46
+ }
47
+ ).
48
+ to_return(
49
+ body: {
50
+ 'instalment_schedules' =>
51
+
52
+ {
53
+
54
+ 'created_at' => 'created_at-input',
55
+ 'currency' => 'currency-input',
56
+ 'id' => 'id-input',
57
+ 'links' => 'links-input',
58
+ 'metadata' => 'metadata-input',
59
+ 'name' => 'name-input',
60
+ 'payment_errors' => 'payment_errors-input',
61
+ 'status' => 'status-input',
62
+ 'total_amount' => 'total_amount-input',
63
+ },
64
+
65
+ }.to_json,
66
+ headers: response_headers
67
+ )
68
+ end
69
+
70
+ it 'creates and returns the resource' do
71
+ expect(post_create_response).to be_a(GoCardlessPro::Resources::InstalmentSchedule)
72
+ end
73
+
74
+ describe 'retry behaviour' do
75
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
76
+
77
+ it 'retries timeouts' do
78
+ stub = stub_request(:post, %r{.*api.gocardless.com/instalment_schedules}).
79
+ to_timeout.then.to_return(status: 200, headers: response_headers)
80
+
81
+ post_create_response
82
+ expect(stub).to have_been_requested.twice
83
+ end
84
+
85
+ it 'retries 5XX errors' do
86
+ stub = stub_request(:post, %r{.*api.gocardless.com/instalment_schedules}).
87
+ to_return(status: 502,
88
+ headers: { 'Content-Type' => 'text/html' },
89
+ body: '<html><body>Response from Cloudflare</body></html>').
90
+ then.to_return(status: 200, headers: response_headers)
91
+
92
+ post_create_response
93
+ expect(stub).to have_been_requested.twice
94
+ end
95
+ end
96
+ end
97
+
98
+ context 'with a request that returns a validation error' do
99
+ let(:new_resource) { {} }
100
+
101
+ before do
102
+ stub_request(:post, %r{.*api.gocardless.com/instalment_schedules}).to_return(
103
+ body: {
104
+ error: {
105
+ type: 'validation_failed',
106
+ code: 422,
107
+ errors: [
108
+ { message: 'test error message', field: 'test_field' },
109
+ ],
110
+ },
111
+ }.to_json,
112
+ headers: response_headers,
113
+ status: 422
114
+ )
115
+ end
116
+
117
+ it 'throws the correct error' do
118
+ expect { post_create_response }.to raise_error(GoCardlessPro::ValidationError)
119
+ end
120
+ end
121
+
122
+ context 'with a request that returns an idempotent creation conflict error' do
123
+ let(:id) { 'ID123' }
124
+
125
+ let(:new_resource) do
126
+ {
127
+
128
+ 'created_at' => 'created_at-input',
129
+ 'currency' => 'currency-input',
130
+ 'id' => 'id-input',
131
+ 'links' => 'links-input',
132
+ 'metadata' => 'metadata-input',
133
+ 'name' => 'name-input',
134
+ 'payment_errors' => 'payment_errors-input',
135
+ 'status' => 'status-input',
136
+ 'total_amount' => 'total_amount-input',
137
+ }
138
+ end
139
+
140
+ let!(:post_stub) do
141
+ stub_request(:post, %r{.*api.gocardless.com/instalment_schedules}).to_return(
142
+ body: {
143
+ error: {
144
+ type: 'invalid_state',
145
+ code: 409,
146
+ errors: [
147
+ {
148
+ message: 'A resource has already been created with this idempotency key',
149
+ reason: 'idempotent_creation_conflict',
150
+ links: {
151
+ conflicting_resource_id: id,
152
+ },
153
+ },
154
+ ],
155
+ },
156
+ }.to_json,
157
+ headers: response_headers,
158
+ status: 409
159
+ )
160
+ end
161
+
162
+ let!(:get_stub) do
163
+ stub_url = "/instalment_schedules/#{id}"
164
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).
165
+ to_return(
166
+ body: {
167
+ 'instalment_schedules' => {
168
+
169
+ 'created_at' => 'created_at-input',
170
+ 'currency' => 'currency-input',
171
+ 'id' => 'id-input',
172
+ 'links' => 'links-input',
173
+ 'metadata' => 'metadata-input',
174
+ 'name' => 'name-input',
175
+ 'payment_errors' => 'payment_errors-input',
176
+ 'status' => 'status-input',
177
+ 'total_amount' => 'total_amount-input',
178
+ },
179
+ }.to_json,
180
+ headers: response_headers
181
+ )
182
+ end
183
+
184
+ context 'with default behaviour' do
185
+ it 'fetches the already-created resource' do
186
+ post_create_response
187
+ expect(post_stub).to have_been_requested
188
+ expect(get_stub).to have_been_requested
189
+ end
190
+ end
191
+
192
+ context 'with on_idempotency_conflict: :raise' do
193
+ let(:client) do
194
+ GoCardlessPro::Client.new(
195
+ access_token: 'SECRET_TOKEN',
196
+ on_idempotency_conflict: :raise
197
+ )
198
+ end
199
+
200
+ it 'raises an IdempotencyConflict error' do
201
+ expect { post_create_response }.
202
+ to raise_error(GoCardlessPro::IdempotencyConflict)
203
+ end
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
+ end
219
+ end
220
+
221
+ describe '#create' do
222
+ subject(:post_create_response) { client.instalment_schedules.create_with_schedule(params: new_resource) }
14
223
  context 'with a valid request' do
15
224
  let(:new_resource) do
16
225
  {
@@ -619,6 +828,66 @@ describe GoCardlessPro::Services::InstalmentSchedulesService do
619
828
  end
620
829
  end
621
830
 
831
+ describe '#update' do
832
+ subject(:put_update_response) { client.instalment_schedules.update(id, params: update_params) }
833
+ let(:id) { 'ABC123' }
834
+
835
+ context 'with a valid request' do
836
+ let(:update_params) { { 'hello' => 'world' } }
837
+
838
+ let!(:stub) do
839
+ stub_url = '/instalment_schedules/:identity'.gsub(':identity', id)
840
+ stub_request(:put, /.*api.gocardless.com#{stub_url}/).to_return(
841
+ body: {
842
+ 'instalment_schedules' => {
843
+
844
+ 'created_at' => 'created_at-input',
845
+ 'currency' => 'currency-input',
846
+ 'id' => 'id-input',
847
+ 'links' => 'links-input',
848
+ 'metadata' => 'metadata-input',
849
+ 'name' => 'name-input',
850
+ 'payment_errors' => 'payment_errors-input',
851
+ 'status' => 'status-input',
852
+ 'total_amount' => 'total_amount-input',
853
+ },
854
+ }.to_json,
855
+ headers: response_headers
856
+ )
857
+ end
858
+
859
+ it 'updates and returns the resource' do
860
+ expect(put_update_response).to be_a(GoCardlessPro::Resources::InstalmentSchedule)
861
+ expect(stub).to have_been_requested
862
+ end
863
+
864
+ describe 'retry behaviour' do
865
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
866
+
867
+ it 'retries timeouts' do
868
+ stub_url = '/instalment_schedules/:identity'.gsub(':identity', id)
869
+ stub = stub_request(:put, /.*api.gocardless.com#{stub_url}/).
870
+ to_timeout.then.to_return(status: 200, headers: response_headers)
871
+
872
+ put_update_response
873
+ expect(stub).to have_been_requested.twice
874
+ end
875
+
876
+ it 'retries 5XX errors' do
877
+ stub_url = '/instalment_schedules/:identity'.gsub(':identity', id)
878
+ stub = stub_request(:put, /.*api.gocardless.com#{stub_url}/).
879
+ to_return(status: 502,
880
+ headers: { 'Content-Type' => 'text/html' },
881
+ body: '<html><body>Response from Cloudflare</body></html>').
882
+ then.to_return(status: 200, headers: response_headers)
883
+
884
+ put_update_response
885
+ expect(stub).to have_been_requested.twice
886
+ end
887
+ end
888
+ end
889
+ end
890
+
622
891
  describe '#cancel' do
623
892
  subject(:post_response) { client.instalment_schedules.cancel(resource_id) }
624
893