gocardless_pro 2.12.0 → 2.13.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 (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',