gocardless_pro 2.12.0 → 2.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +50 -0
  3. data/README.md +15 -1
  4. data/gocardless_pro.gemspec +2 -0
  5. data/lib/gocardless_pro.rb +3 -0
  6. data/lib/gocardless_pro/api_service.rb +3 -0
  7. data/lib/gocardless_pro/client.rb +6 -1
  8. data/lib/gocardless_pro/error/invalid_state_error.rb +2 -0
  9. data/lib/gocardless_pro/resources/creditor.rb +4 -0
  10. data/lib/gocardless_pro/resources/customer.rb +5 -0
  11. data/lib/gocardless_pro/resources/customer_notification.rb +97 -0
  12. data/lib/gocardless_pro/resources/event.rb +2 -0
  13. data/lib/gocardless_pro/resources/mandate_import.rb +1 -1
  14. data/lib/gocardless_pro/resources/mandate_import_entry.rb +1 -1
  15. data/lib/gocardless_pro/services/creditor_bank_accounts_service.rb +20 -2
  16. data/lib/gocardless_pro/services/creditors_service.rb +10 -1
  17. data/lib/gocardless_pro/services/customer_bank_accounts_service.rb +20 -2
  18. data/lib/gocardless_pro/services/customer_notifications_service.rb +66 -0
  19. data/lib/gocardless_pro/services/customers_service.rb +10 -1
  20. data/lib/gocardless_pro/services/mandate_imports_service.rb +40 -6
  21. data/lib/gocardless_pro/services/mandates_service.rb +30 -3
  22. data/lib/gocardless_pro/services/payments_service.rb +30 -3
  23. data/lib/gocardless_pro/services/redirect_flows_service.rb +20 -2
  24. data/lib/gocardless_pro/services/refunds_service.rb +11 -6
  25. data/lib/gocardless_pro/services/subscriptions_service.rb +20 -2
  26. data/lib/gocardless_pro/version.rb +1 -1
  27. data/spec/resources/customer_notification_spec.rb +73 -0
  28. data/spec/resources/customer_spec.rb +13 -0
  29. data/spec/resources/event_spec.rb +7 -0
  30. data/spec/services/creditor_bank_accounts_service_spec.rb +33 -4
  31. data/spec/services/creditors_service_spec.rb +33 -4
  32. data/spec/services/customer_bank_accounts_service_spec.rb +33 -4
  33. data/spec/services/customer_notifications_service_spec.rb +84 -0
  34. data/spec/services/customers_service_spec.rb +50 -4
  35. data/spec/services/events_service_spec.rb +11 -0
  36. data/spec/services/mandate_imports_service_spec.rb +33 -4
  37. data/spec/services/mandates_service_spec.rb +33 -4
  38. data/spec/services/payments_service_spec.rb +33 -4
  39. data/spec/services/redirect_flows_service_spec.rb +33 -4
  40. data/spec/services/refunds_service_spec.rb +33 -4
  41. data/spec/services/subscriptions_service_spec.rb +33 -4
  42. metadata +23 -3
  43. data/circle.yml +0 -11
@@ -29,7 +29,16 @@ module GoCardlessPro
29
29
  # Response doesn't raise any errors until #body is called
30
30
  response.tap(&:body)
31
31
  rescue InvalidStateError => e
32
- return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
32
+ if e.idempotent_creation_conflict?
33
+ case @api_service.on_idempotency_conflict
34
+ when :raise
35
+ raise IdempotencyConflict, e.error
36
+ when :fetch
37
+ return get(e.conflicting_resource_id)
38
+ else
39
+ raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
40
+ end
41
+ end
33
42
 
34
43
  raise e
35
44
  end
@@ -33,7 +33,16 @@ module GoCardlessPro
33
33
  # Response doesn't raise any errors until #body is called
34
34
  response.tap(&:body)
35
35
  rescue InvalidStateError => e
36
- return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
36
+ if e.idempotent_creation_conflict?
37
+ case @api_service.on_idempotency_conflict
38
+ when :raise
39
+ raise IdempotencyConflict, e.error
40
+ when :fetch
41
+ return get(e.conflicting_resource_id)
42
+ else
43
+ raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
44
+ end
45
+ end
37
46
 
38
47
  raise e
39
48
  end
@@ -64,6 +73,13 @@ module GoCardlessPro
64
73
  # GoCardless team. Once the import has been submitted, it can no longer have
65
74
  # entries
66
75
  # added to it.
76
+ #
77
+ # In our sandbox environment, to aid development, we automatically process
78
+ # mandate
79
+ # imports approximately 10 seconds after they are submitted. This will allow you
80
+ # to
81
+ # test both the "submitted" response and wait for the webhook to confirm the
82
+ # processing has begun.
67
83
  # Example URL: /mandate_imports/:identity/actions/submit
68
84
  #
69
85
  # @param identity # Unique identifier, beginning with "IM".
@@ -83,7 +99,16 @@ module GoCardlessPro
83
99
  # Response doesn't raise any errors until #body is called
84
100
  response.tap(&:body)
85
101
  rescue InvalidStateError => e
86
- return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
102
+ if e.idempotent_creation_conflict?
103
+ case @api_service.on_idempotency_conflict
104
+ when :raise
105
+ raise IdempotencyConflict, e.error
106
+ when :fetch
107
+ return get(e.conflicting_resource_id)
108
+ else
109
+ raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
110
+ end
111
+ end
87
112
 
88
113
  raise e
89
114
  end
@@ -97,9 +122,9 @@ module GoCardlessPro
97
122
  # mandates
98
123
  # being set up in GoCardless. Once the import has been cancelled, it can no
99
124
  # longer have
100
- # entries added to it. Mandate imports which have already been submitted cannot
101
- # be
102
- # cancelled.
125
+ # entries added to it. Mandate imports which have already been submitted or
126
+ # processed
127
+ # cannot be cancelled.
103
128
  # Example URL: /mandate_imports/:identity/actions/cancel
104
129
  #
105
130
  # @param identity # Unique identifier, beginning with "IM".
@@ -119,7 +144,16 @@ module GoCardlessPro
119
144
  # Response doesn't raise any errors until #body is called
120
145
  response.tap(&:body)
121
146
  rescue InvalidStateError => e
122
- return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
147
+ if e.idempotent_creation_conflict?
148
+ case @api_service.on_idempotency_conflict
149
+ when :raise
150
+ raise IdempotencyConflict, e.error
151
+ when :fetch
152
+ return get(e.conflicting_resource_id)
153
+ else
154
+ raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
155
+ end
156
+ end
123
157
 
124
158
  raise e
125
159
  end
@@ -29,7 +29,16 @@ module GoCardlessPro
29
29
  # Response doesn't raise any errors until #body is called
30
30
  response.tap(&:body)
31
31
  rescue InvalidStateError => e
32
- return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
32
+ if e.idempotent_creation_conflict?
33
+ case @api_service.on_idempotency_conflict
34
+ when :raise
35
+ raise IdempotencyConflict, e.error
36
+ when :fetch
37
+ return get(e.conflicting_resource_id)
38
+ else
39
+ raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
40
+ end
41
+ end
33
42
 
34
43
  raise e
35
44
  end
@@ -131,7 +140,16 @@ module GoCardlessPro
131
140
  # Response doesn't raise any errors until #body is called
132
141
  response.tap(&:body)
133
142
  rescue InvalidStateError => e
134
- return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
143
+ if e.idempotent_creation_conflict?
144
+ case @api_service.on_idempotency_conflict
145
+ when :raise
146
+ raise IdempotencyConflict, e.error
147
+ when :fetch
148
+ return get(e.conflicting_resource_id)
149
+ else
150
+ raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
151
+ end
152
+ end
135
153
 
136
154
  raise e
137
155
  end
@@ -171,7 +189,16 @@ module GoCardlessPro
171
189
  # Response doesn't raise any errors until #body is called
172
190
  response.tap(&:body)
173
191
  rescue InvalidStateError => e
174
- return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
192
+ if e.idempotent_creation_conflict?
193
+ case @api_service.on_idempotency_conflict
194
+ when :raise
195
+ raise IdempotencyConflict, e.error
196
+ when :fetch
197
+ return get(e.conflicting_resource_id)
198
+ else
199
+ raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
200
+ end
201
+ end
175
202
 
176
203
  raise e
177
204
  end
@@ -34,7 +34,16 @@ module GoCardlessPro
34
34
  # Response doesn't raise any errors until #body is called
35
35
  response.tap(&:body)
36
36
  rescue InvalidStateError => e
37
- return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
37
+ if e.idempotent_creation_conflict?
38
+ case @api_service.on_idempotency_conflict
39
+ when :raise
40
+ raise IdempotencyConflict, e.error
41
+ when :fetch
42
+ return get(e.conflicting_resource_id)
43
+ else
44
+ raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
45
+ end
46
+ end
38
47
 
39
48
  raise e
40
49
  end
@@ -136,7 +145,16 @@ module GoCardlessPro
136
145
  # Response doesn't raise any errors until #body is called
137
146
  response.tap(&:body)
138
147
  rescue InvalidStateError => e
139
- return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
148
+ if e.idempotent_creation_conflict?
149
+ case @api_service.on_idempotency_conflict
150
+ when :raise
151
+ raise IdempotencyConflict, e.error
152
+ when :fetch
153
+ return get(e.conflicting_resource_id)
154
+ else
155
+ raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
156
+ end
157
+ end
140
158
 
141
159
  raise e
142
160
  end
@@ -175,7 +193,16 @@ module GoCardlessPro
175
193
  # Response doesn't raise any errors until #body is called
176
194
  response.tap(&:body)
177
195
  rescue InvalidStateError => e
178
- return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
196
+ if e.idempotent_creation_conflict?
197
+ case @api_service.on_idempotency_conflict
198
+ when :raise
199
+ raise IdempotencyConflict, e.error
200
+ when :fetch
201
+ return get(e.conflicting_resource_id)
202
+ else
203
+ raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
204
+ end
205
+ end
179
206
 
180
207
  raise e
181
208
  end
@@ -30,7 +30,16 @@ module GoCardlessPro
30
30
  # Response doesn't raise any errors until #body is called
31
31
  response.tap(&:body)
32
32
  rescue InvalidStateError => e
33
- return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
33
+ if e.idempotent_creation_conflict?
34
+ case @api_service.on_idempotency_conflict
35
+ when :raise
36
+ raise IdempotencyConflict, e.error
37
+ when :fetch
38
+ return get(e.conflicting_resource_id)
39
+ else
40
+ raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
41
+ end
42
+ end
34
43
 
35
44
  raise e
36
45
  end
@@ -86,7 +95,16 @@ module GoCardlessPro
86
95
  # Response doesn't raise any errors until #body is called
87
96
  response.tap(&:body)
88
97
  rescue InvalidStateError => e
89
- return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
98
+ if e.idempotent_creation_conflict?
99
+ case @api_service.on_idempotency_conflict
100
+ when :raise
101
+ raise IdempotencyConflict, e.error
102
+ when :fetch
103
+ return get(e.conflicting_resource_id)
104
+ else
105
+ raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
106
+ end
107
+ end
90
108
 
91
109
  raise e
92
110
  end
@@ -13,13 +13,9 @@ module GoCardlessPro
13
13
  class RefundsService < BaseService
14
14
  # Creates a new refund object.
15
15
  #
16
- # This fails with:<a name="refund_payment_invalid_state"></a><a
17
- # name="total_amount_confirmation_invalid"></a><a
16
+ # This fails with:<a name="total_amount_confirmation_invalid"></a><a
18
17
  # name="number_of_refunds_exceeded"></a>
19
18
  #
20
- # - `refund_payment_invalid_state` error if the linked
21
- # [payment](#core-endpoints-payments) isn't either `confirmed` or `paid_out`.
22
- #
23
19
  # - `total_amount_confirmation_invalid` if the confirmation amount doesn't match
24
20
  # the total amount refunded for the payment. This safeguard is there to prevent
25
21
  # two processes from creating refunds without awareness of each other.
@@ -44,7 +40,16 @@ module GoCardlessPro
44
40
  # Response doesn't raise any errors until #body is called
45
41
  response.tap(&:body)
46
42
  rescue InvalidStateError => e
47
- return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
43
+ if e.idempotent_creation_conflict?
44
+ case @api_service.on_idempotency_conflict
45
+ when :raise
46
+ raise IdempotencyConflict, e.error
47
+ when :fetch
48
+ return get(e.conflicting_resource_id)
49
+ else
50
+ raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
51
+ end
52
+ end
48
53
 
49
54
  raise e
50
55
  end
@@ -29,7 +29,16 @@ module GoCardlessPro
29
29
  # Response doesn't raise any errors until #body is called
30
30
  response.tap(&:body)
31
31
  rescue InvalidStateError => e
32
- return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
32
+ if e.idempotent_creation_conflict?
33
+ case @api_service.on_idempotency_conflict
34
+ when :raise
35
+ raise IdempotencyConflict, e.error
36
+ when :fetch
37
+ return get(e.conflicting_resource_id)
38
+ else
39
+ raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
40
+ end
41
+ end
33
42
 
34
43
  raise e
35
44
  end
@@ -154,7 +163,16 @@ module GoCardlessPro
154
163
  # Response doesn't raise any errors until #body is called
155
164
  response.tap(&:body)
156
165
  rescue InvalidStateError => e
157
- return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
166
+ if e.idempotent_creation_conflict?
167
+ case @api_service.on_idempotency_conflict
168
+ when :raise
169
+ raise IdempotencyConflict, e.error
170
+ when :fetch
171
+ return get(e.conflicting_resource_id)
172
+ else
173
+ raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
174
+ end
175
+ end
158
176
 
159
177
  raise e
160
178
  end
@@ -4,5 +4,5 @@ end
4
4
 
5
5
  module GoCardlessPro
6
6
  # Current version of the GC gem
7
- VERSION = '2.12.0'.freeze
7
+ VERSION = '2.13.0'.freeze
8
8
  end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoCardlessPro::Resources::CustomerNotification 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 '#handle' do
13
+ subject(:post_response) { client.customer_notifications.handle(resource_id) }
14
+
15
+ let(:resource_id) { 'ABC123' }
16
+
17
+ let!(:stub) do
18
+ # /customer_notifications/%v/actions/handle
19
+ stub_url = '/customer_notifications/:identity/actions/handle'.gsub(':identity', resource_id)
20
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).to_return(
21
+ body: {
22
+ 'customer_notifications' => {
23
+
24
+ 'action_taken' => 'action_taken-input',
25
+ 'action_taken_at' => 'action_taken_at-input',
26
+ 'action_taken_by' => 'action_taken_by-input',
27
+ 'id' => 'id-input',
28
+ 'links' => 'links-input',
29
+ 'type' => 'type-input',
30
+ },
31
+ }.to_json,
32
+ headers: response_headers
33
+ )
34
+ end
35
+
36
+ it 'wraps the response and calls the right endpoint' do
37
+ expect(post_response).to be_a(GoCardlessPro::Resources::CustomerNotification)
38
+
39
+ expect(stub).to have_been_requested
40
+ end
41
+
42
+ context 'when the request needs a body and custom header' do
43
+ let(:body) { { foo: 'bar' } }
44
+ let(:headers) { { 'Foo' => 'Bar' } }
45
+ subject(:post_response) { client.customer_notifications.handle(resource_id, body, headers) }
46
+
47
+ let(:resource_id) { 'ABC123' }
48
+
49
+ let!(:stub) do
50
+ # /customer_notifications/%v/actions/handle
51
+ stub_url = '/customer_notifications/:identity/actions/handle'.gsub(':identity', resource_id)
52
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).
53
+ with(
54
+ body: { foo: 'bar' },
55
+ headers: { 'Foo' => 'Bar' }
56
+ ).to_return(
57
+ body: {
58
+ 'customer_notifications' => {
59
+
60
+ 'action_taken' => 'action_taken-input',
61
+ 'action_taken_at' => 'action_taken_at-input',
62
+ 'action_taken_by' => 'action_taken_by-input',
63
+ 'id' => 'id-input',
64
+ 'links' => 'links-input',
65
+ 'type' => 'type-input',
66
+ },
67
+ }.to_json,
68
+ headers: response_headers
69
+ )
70
+ end
71
+ end
72
+ end
73
+ end
@@ -29,6 +29,7 @@ describe GoCardlessPro::Resources::Customer do
29
29
  'id' => 'id-input',
30
30
  'language' => 'language-input',
31
31
  'metadata' => 'metadata-input',
32
+ 'phone_number' => 'phone_number-input',
32
33
  'postal_code' => 'postal_code-input',
33
34
  'region' => 'region-input',
34
35
  'swedish_identity_number' => 'swedish_identity_number-input',
@@ -55,6 +56,7 @@ describe GoCardlessPro::Resources::Customer do
55
56
  'id' => 'id-input',
56
57
  'language' => 'language-input',
57
58
  'metadata' => 'metadata-input',
59
+ 'phone_number' => 'phone_number-input',
58
60
  'postal_code' => 'postal_code-input',
59
61
  'region' => 'region-input',
60
62
  'swedish_identity_number' => 'swedish_identity_number-input',
@@ -81,6 +83,7 @@ describe GoCardlessPro::Resources::Customer do
81
83
  'id' => 'id-input',
82
84
  'language' => 'language-input',
83
85
  'metadata' => 'metadata-input',
86
+ 'phone_number' => 'phone_number-input',
84
87
  'postal_code' => 'postal_code-input',
85
88
  'region' => 'region-input',
86
89
  'swedish_identity_number' => 'swedish_identity_number-input',
@@ -140,6 +143,7 @@ describe GoCardlessPro::Resources::Customer do
140
143
  'id' => 'id-input',
141
144
  'language' => 'language-input',
142
145
  'metadata' => 'metadata-input',
146
+ 'phone_number' => 'phone_number-input',
143
147
  'postal_code' => 'postal_code-input',
144
148
  'region' => 'region-input',
145
149
  'swedish_identity_number' => 'swedish_identity_number-input',
@@ -189,6 +193,7 @@ describe GoCardlessPro::Resources::Customer do
189
193
  'id' => 'id-input',
190
194
  'language' => 'language-input',
191
195
  'metadata' => 'metadata-input',
196
+ 'phone_number' => 'phone_number-input',
192
197
  'postal_code' => 'postal_code-input',
193
198
  'region' => 'region-input',
194
199
  'swedish_identity_number' => 'swedish_identity_number-input',
@@ -229,6 +234,7 @@ describe GoCardlessPro::Resources::Customer do
229
234
  'id' => 'id-input',
230
235
  'language' => 'language-input',
231
236
  'metadata' => 'metadata-input',
237
+ 'phone_number' => 'phone_number-input',
232
238
  'postal_code' => 'postal_code-input',
233
239
  'region' => 'region-input',
234
240
  'swedish_identity_number' => 'swedish_identity_number-input',
@@ -275,6 +281,8 @@ describe GoCardlessPro::Resources::Customer do
275
281
 
276
282
  expect(get_list_response.records.first.metadata).to eq('metadata-input')
277
283
 
284
+ expect(get_list_response.records.first.phone_number).to eq('phone_number-input')
285
+
278
286
  expect(get_list_response.records.first.postal_code).to eq('postal_code-input')
279
287
 
280
288
  expect(get_list_response.records.first.region).to eq('region-input')
@@ -311,6 +319,7 @@ describe GoCardlessPro::Resources::Customer do
311
319
  'id' => 'id-input',
312
320
  'language' => 'language-input',
313
321
  'metadata' => 'metadata-input',
322
+ 'phone_number' => 'phone_number-input',
314
323
  'postal_code' => 'postal_code-input',
315
324
  'region' => 'region-input',
316
325
  'swedish_identity_number' => 'swedish_identity_number-input',
@@ -343,6 +352,7 @@ describe GoCardlessPro::Resources::Customer do
343
352
  'id' => 'id-input',
344
353
  'language' => 'language-input',
345
354
  'metadata' => 'metadata-input',
355
+ 'phone_number' => 'phone_number-input',
346
356
  'postal_code' => 'postal_code-input',
347
357
  'region' => 'region-input',
348
358
  'swedish_identity_number' => 'swedish_identity_number-input',
@@ -391,6 +401,7 @@ describe GoCardlessPro::Resources::Customer do
391
401
  'id' => 'id-input',
392
402
  'language' => 'language-input',
393
403
  'metadata' => 'metadata-input',
404
+ 'phone_number' => 'phone_number-input',
394
405
  'postal_code' => 'postal_code-input',
395
406
  'region' => 'region-input',
396
407
  'swedish_identity_number' => 'swedish_identity_number-input',
@@ -433,6 +444,7 @@ describe GoCardlessPro::Resources::Customer do
433
444
  'id' => 'id-input',
434
445
  'language' => 'language-input',
435
446
  'metadata' => 'metadata-input',
447
+ 'phone_number' => 'phone_number-input',
436
448
  'postal_code' => 'postal_code-input',
437
449
  'region' => 'region-input',
438
450
  'swedish_identity_number' => 'swedish_identity_number-input',
@@ -497,6 +509,7 @@ describe GoCardlessPro::Resources::Customer do
497
509
  'id' => 'id-input',
498
510
  'language' => 'language-input',
499
511
  'metadata' => 'metadata-input',
512
+ 'phone_number' => 'phone_number-input',
500
513
  'postal_code' => 'postal_code-input',
501
514
  'region' => 'region-input',
502
515
  'swedish_identity_number' => 'swedish_identity_number-input',