gocardless_pro 1.1.0 → 2.0.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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -4
  3. data/lib/gocardless_pro.rb +1 -0
  4. data/lib/gocardless_pro/api_service.rb +2 -0
  5. data/lib/gocardless_pro/client.rb +4 -3
  6. data/lib/gocardless_pro/error/invalid_state_error.rb +17 -0
  7. data/lib/gocardless_pro/middlewares/raise_gocardless_errors.rb +50 -0
  8. data/lib/gocardless_pro/request.rb +38 -1
  9. data/lib/gocardless_pro/resources/creditor.rb +2 -2
  10. data/lib/gocardless_pro/resources/creditor_bank_account.rb +11 -11
  11. data/lib/gocardless_pro/resources/customer_bank_account.rb +2 -2
  12. data/lib/gocardless_pro/resources/event.rb +2 -2
  13. data/lib/gocardless_pro/resources/mandate.rb +2 -2
  14. data/lib/gocardless_pro/resources/payment.rb +7 -7
  15. data/lib/gocardless_pro/resources/payout.rb +7 -6
  16. data/lib/gocardless_pro/resources/redirect_flow.rb +2 -2
  17. data/lib/gocardless_pro/resources/refund.rb +2 -2
  18. data/lib/gocardless_pro/resources/subscription.rb +2 -2
  19. data/lib/gocardless_pro/response.rb +2 -54
  20. data/lib/gocardless_pro/services/bank_details_lookups_service.rb +3 -0
  21. data/lib/gocardless_pro/services/creditor_bank_accounts_service.rb +31 -2
  22. data/lib/gocardless_pro/services/creditors_service.rb +21 -1
  23. data/lib/gocardless_pro/services/customer_bank_accounts_service.rb +34 -2
  24. data/lib/gocardless_pro/services/customers_service.rb +21 -1
  25. data/lib/gocardless_pro/services/events_service.rb +5 -0
  26. data/lib/gocardless_pro/services/mandate_pdfs_service.rb +3 -0
  27. data/lib/gocardless_pro/services/mandates_service.rb +47 -3
  28. data/lib/gocardless_pro/services/payments_service.rb +47 -3
  29. data/lib/gocardless_pro/services/payouts_service.rb +5 -0
  30. data/lib/gocardless_pro/services/redirect_flows_service.rb +28 -2
  31. data/lib/gocardless_pro/services/refunds_service.rb +21 -1
  32. data/lib/gocardless_pro/services/subscriptions_service.rb +34 -2
  33. data/lib/gocardless_pro/version.rb +1 -1
  34. data/spec/api_service_spec.rb +106 -0
  35. data/spec/middlewares/raise_gocardless_errors_spec.rb +98 -0
  36. data/spec/resources/bank_details_lookup_spec.rb +102 -19
  37. data/spec/resources/creditor_bank_account_spec.rb +416 -40
  38. data/spec/resources/creditor_spec.rb +414 -53
  39. data/spec/resources/customer_bank_account_spec.rb +452 -40
  40. data/spec/resources/customer_spec.rb +457 -45
  41. data/spec/resources/event_spec.rb +171 -72
  42. data/spec/resources/mandate_pdf_spec.rb +100 -17
  43. data/spec/resources/mandate_spec.rb +501 -44
  44. data/spec/resources/payment_spec.rb +531 -48
  45. data/spec/resources/payout_spec.rb +189 -45
  46. data/spec/resources/redirect_flow_spec.rb +277 -43
  47. data/spec/resources/refund_spec.rb +349 -34
  48. data/spec/resources/subscription_spec.rb +531 -53
  49. data/spec/response_spec.rb +12 -79
  50. data/spec/services/bank_details_lookups_service_spec.rb +67 -2
  51. data/spec/services/creditor_bank_accounts_service_spec.rb +309 -31
  52. data/spec/services/creditors_service_spec.rb +343 -33
  53. data/spec/services/customer_bank_accounts_service_spec.rb +335 -32
  54. data/spec/services/customers_service_spec.rb +364 -36
  55. data/spec/services/events_service_spec.rb +185 -24
  56. data/spec/services/mandate_pdfs_service_spec.rb +66 -2
  57. data/spec/services/mandates_service_spec.rb +341 -33
  58. data/spec/services/payments_service_spec.rb +355 -35
  59. data/spec/services/payouts_service_spec.rb +206 -26
  60. data/spec/services/redirect_flows_service_spec.rb +137 -7
  61. data/spec/services/refunds_service_spec.rb +301 -27
  62. data/spec/services/subscriptions_service_spec.rb +377 -38
  63. metadata +6 -3
@@ -76,7 +76,7 @@ module GoCardlessPro
76
76
 
77
77
  # Return the links that the resource has
78
78
  def links
79
- @links_links ||= Links.new(@links)
79
+ @redirect_flow_links ||= Links.new(@links)
80
80
  end
81
81
 
82
82
  # Provides the redirect_flow resource as a hash of all its readable attributes
@@ -86,7 +86,7 @@ module GoCardlessPro
86
86
 
87
87
  class Links
88
88
  def initialize(links)
89
- @links = links
89
+ @links = links || {}
90
90
  end
91
91
 
92
92
  def creditor
@@ -47,7 +47,7 @@ module GoCardlessPro
47
47
 
48
48
  # Return the links that the resource has
49
49
  def links
50
- @links_links ||= Links.new(@links)
50
+ @refund_links ||= Links.new(@links)
51
51
  end
52
52
 
53
53
  # Provides the refund resource as a hash of all its readable attributes
@@ -57,7 +57,7 @@ module GoCardlessPro
57
57
 
58
58
  class Links
59
59
  def initialize(links)
60
- @links = links
60
+ @links = links || {}
61
61
  end
62
62
 
63
63
  def payment
@@ -128,7 +128,7 @@ module GoCardlessPro
128
128
 
129
129
  # Return the links that the resource has
130
130
  def links
131
- @links_links ||= Links.new(@links)
131
+ @subscription_links ||= Links.new(@links)
132
132
  end
133
133
 
134
134
  # Provides the subscription resource as a hash of all its readable attributes
@@ -138,7 +138,7 @@ module GoCardlessPro
138
138
 
139
139
  class Links
140
140
  def initialize(links)
141
- @links = links
141
+ @links = links || {}
142
142
  end
143
143
 
144
144
  def mandate
@@ -12,27 +12,13 @@ module GoCardlessPro
12
12
  @response = response
13
13
  end
14
14
 
15
- # Return the body of the API response
15
+ # Return the body of parsed JSON body of the API response
16
16
  def body
17
- json? ? handle_json : handle_raw
18
- end
19
-
20
- # Returns true if the response is JSON
21
- def json?
22
- content_type = @response.headers['Content-Type'] ||
23
- @response.headers['content-type'] || ''
24
- content_type.include?('application/json')
25
- end
26
-
27
- # Returns true if the response is an error
28
- def error?
29
- @response.status >= 400
17
+ JSON.parse(@response.body) unless @response.body.empty?
30
18
  end
31
19
 
32
20
  # Returns the meta hash of the response
33
21
  def meta
34
- fail ResponseError, 'Cannot fetch meta for non JSON response' unless json?
35
-
36
22
  json_body.fetch('meta', {})
37
23
  end
38
24
 
@@ -40,43 +26,5 @@ module GoCardlessPro
40
26
  def limit
41
27
  meta.fetch('limit', nil)
42
28
  end
43
-
44
- private
45
-
46
- def json_body
47
- @json_body ||= JSON.parse(@response.body) unless @response.body.empty?
48
- end
49
-
50
- def raw_body
51
- @response.body
52
- end
53
-
54
- def handle_json
55
- if error?
56
- type = json_body['error']['type']
57
- fail(error_class_for_type(type), json_body['error'])
58
- else
59
- json_body
60
- end
61
- end
62
-
63
- def error_class_for_type(type)
64
- {
65
- validation_failed: GoCardlessPro::ValidationError,
66
- gocardless: GoCardlessPro::GoCardlessError,
67
- invalid_api_usage: GoCardlessPro::InvalidApiUsageError,
68
- invalid_state: GoCardlessPro::InvalidStateError
69
- }.fetch(type.to_sym)
70
- end
71
-
72
- def handle_raw
73
- default_raw_message = {
74
- 'message' => "Something went wrong with this raw request\n" \
75
- "status: #{@response.status}\n" \
76
- "headers: #{@response.headers}\n" \
77
- "body: #{@response.body}"
78
- }
79
- error? ? fail(ApiError, default_raw_message) : raw_body
80
- end
81
29
  end
82
30
  end
@@ -32,6 +32,9 @@ module GoCardlessPro
32
32
  params = options.delete(:params) || {}
33
33
  options[:params] = {}
34
34
  options[:params][envelope_key] = params
35
+
36
+ options[:retry_failures] = true
37
+
35
38
  response = make_request(:post, path, options)
36
39
 
37
40
  return if response.body.nil?
@@ -20,7 +20,19 @@ module GoCardlessPro
20
20
  params = options.delete(:params) || {}
21
21
  options[:params] = {}
22
22
  options[:params][envelope_key] = params
23
- response = make_request(:post, path, options)
23
+
24
+ options[:retry_failures] = true
25
+
26
+ begin
27
+ response = make_request(:post, path, options)
28
+
29
+ # Response doesn't raise any errors until #body is called
30
+ response.tap(&:body)
31
+ rescue InvalidStateError => e
32
+ return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
33
+
34
+ raise e
35
+ end
24
36
 
25
37
  return if response.body.nil?
26
38
 
@@ -34,7 +46,10 @@ module GoCardlessPro
34
46
  def list(options = {})
35
47
  path = '/creditor_bank_accounts'
36
48
 
49
+ options[:retry_failures] = true
50
+
37
51
  response = make_request(:get, path, options)
52
+
38
53
  ListResponse.new(
39
54
  response: response,
40
55
  unenveloped_body: unenvelope_body(response.body),
@@ -61,6 +76,8 @@ module GoCardlessPro
61
76
  def get(identity, options = {})
62
77
  path = sub_url('/creditor_bank_accounts/:identity', 'identity' => identity)
63
78
 
79
+ options[:retry_failures] = true
80
+
64
81
  response = make_request(:get, path, options)
65
82
 
66
83
  return if response.body.nil?
@@ -86,7 +103,19 @@ module GoCardlessPro
86
103
  params = options.delete(:params) || {}
87
104
  options[:params] = {}
88
105
  options[:params]['data'] = params
89
- response = make_request(:post, path, options)
106
+
107
+ options[:retry_failures] = false
108
+
109
+ begin
110
+ response = make_request(:post, path, options)
111
+
112
+ # Response doesn't raise any errors until #body is called
113
+ response.tap(&:body)
114
+ rescue InvalidStateError => e
115
+ return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
116
+
117
+ raise e
118
+ end
90
119
 
91
120
  return if response.body.nil?
92
121
 
@@ -20,7 +20,19 @@ module GoCardlessPro
20
20
  params = options.delete(:params) || {}
21
21
  options[:params] = {}
22
22
  options[:params][envelope_key] = params
23
- response = make_request(:post, path, options)
23
+
24
+ options[:retry_failures] = true
25
+
26
+ begin
27
+ response = make_request(:post, path, options)
28
+
29
+ # Response doesn't raise any errors until #body is called
30
+ response.tap(&:body)
31
+ rescue InvalidStateError => e
32
+ return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
33
+
34
+ raise e
35
+ end
24
36
 
25
37
  return if response.body.nil?
26
38
 
@@ -34,7 +46,10 @@ module GoCardlessPro
34
46
  def list(options = {})
35
47
  path = '/creditors'
36
48
 
49
+ options[:retry_failures] = true
50
+
37
51
  response = make_request(:get, path, options)
52
+
38
53
  ListResponse.new(
39
54
  response: response,
40
55
  unenveloped_body: unenvelope_body(response.body),
@@ -61,6 +76,8 @@ module GoCardlessPro
61
76
  def get(identity, options = {})
62
77
  path = sub_url('/creditors/:identity', 'identity' => identity)
63
78
 
79
+ options[:retry_failures] = true
80
+
64
81
  response = make_request(:get, path, options)
65
82
 
66
83
  return if response.body.nil?
@@ -80,6 +97,9 @@ module GoCardlessPro
80
97
  params = options.delete(:params) || {}
81
98
  options[:params] = {}
82
99
  options[:params][envelope_key] = params
100
+
101
+ options[:retry_failures] = true
102
+
83
103
  response = make_request(:put, path, options)
84
104
 
85
105
  return if response.body.nil?
@@ -35,7 +35,19 @@ module GoCardlessPro
35
35
  params = options.delete(:params) || {}
36
36
  options[:params] = {}
37
37
  options[:params][envelope_key] = params
38
- response = make_request(:post, path, options)
38
+
39
+ options[:retry_failures] = true
40
+
41
+ begin
42
+ response = make_request(:post, path, options)
43
+
44
+ # Response doesn't raise any errors until #body is called
45
+ response.tap(&:body)
46
+ rescue InvalidStateError => e
47
+ return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
48
+
49
+ raise e
50
+ end
39
51
 
40
52
  return if response.body.nil?
41
53
 
@@ -49,7 +61,10 @@ module GoCardlessPro
49
61
  def list(options = {})
50
62
  path = '/customer_bank_accounts'
51
63
 
64
+ options[:retry_failures] = true
65
+
52
66
  response = make_request(:get, path, options)
67
+
53
68
  ListResponse.new(
54
69
  response: response,
55
70
  unenveloped_body: unenvelope_body(response.body),
@@ -76,6 +91,8 @@ module GoCardlessPro
76
91
  def get(identity, options = {})
77
92
  path = sub_url('/customer_bank_accounts/:identity', 'identity' => identity)
78
93
 
94
+ options[:retry_failures] = true
95
+
79
96
  response = make_request(:get, path, options)
80
97
 
81
98
  return if response.body.nil?
@@ -95,6 +112,9 @@ module GoCardlessPro
95
112
  params = options.delete(:params) || {}
96
113
  options[:params] = {}
97
114
  options[:params][envelope_key] = params
115
+
116
+ options[:retry_failures] = true
117
+
98
118
  response = make_request(:put, path, options)
99
119
 
100
120
  return if response.body.nil?
@@ -120,7 +140,19 @@ module GoCardlessPro
120
140
  params = options.delete(:params) || {}
121
141
  options[:params] = {}
122
142
  options[:params]['data'] = params
123
- response = make_request(:post, path, options)
143
+
144
+ options[:retry_failures] = false
145
+
146
+ begin
147
+ response = make_request(:post, path, options)
148
+
149
+ # Response doesn't raise any errors until #body is called
150
+ response.tap(&:body)
151
+ rescue InvalidStateError => e
152
+ return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
153
+
154
+ raise e
155
+ end
124
156
 
125
157
  return if response.body.nil?
126
158
 
@@ -20,7 +20,19 @@ module GoCardlessPro
20
20
  params = options.delete(:params) || {}
21
21
  options[:params] = {}
22
22
  options[:params][envelope_key] = params
23
- response = make_request(:post, path, options)
23
+
24
+ options[:retry_failures] = true
25
+
26
+ begin
27
+ response = make_request(:post, path, options)
28
+
29
+ # Response doesn't raise any errors until #body is called
30
+ response.tap(&:body)
31
+ rescue InvalidStateError => e
32
+ return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
33
+
34
+ raise e
35
+ end
24
36
 
25
37
  return if response.body.nil?
26
38
 
@@ -34,7 +46,10 @@ module GoCardlessPro
34
46
  def list(options = {})
35
47
  path = '/customers'
36
48
 
49
+ options[:retry_failures] = true
50
+
37
51
  response = make_request(:get, path, options)
52
+
38
53
  ListResponse.new(
39
54
  response: response,
40
55
  unenveloped_body: unenvelope_body(response.body),
@@ -61,6 +76,8 @@ module GoCardlessPro
61
76
  def get(identity, options = {})
62
77
  path = sub_url('/customers/:identity', 'identity' => identity)
63
78
 
79
+ options[:retry_failures] = true
80
+
64
81
  response = make_request(:get, path, options)
65
82
 
66
83
  return if response.body.nil?
@@ -80,6 +97,9 @@ module GoCardlessPro
80
97
  params = options.delete(:params) || {}
81
98
  options[:params] = {}
82
99
  options[:params][envelope_key] = params
100
+
101
+ options[:retry_failures] = true
102
+
83
103
  response = make_request(:put, path, options)
84
104
 
85
105
  return if response.body.nil?
@@ -18,7 +18,10 @@ module GoCardlessPro
18
18
  def list(options = {})
19
19
  path = '/events'
20
20
 
21
+ options[:retry_failures] = true
22
+
21
23
  response = make_request(:get, path, options)
24
+
22
25
  ListResponse.new(
23
26
  response: response,
24
27
  unenveloped_body: unenvelope_body(response.body),
@@ -45,6 +48,8 @@ module GoCardlessPro
45
48
  def get(identity, options = {})
46
49
  path = sub_url('/events/:identity', 'identity' => identity)
47
50
 
51
+ options[:retry_failures] = true
52
+
48
53
  response = make_request(:get, path, options)
49
54
 
50
55
  return if response.body.nil?
@@ -31,6 +31,9 @@ module GoCardlessPro
31
31
  params = options.delete(:params) || {}
32
32
  options[:params] = {}
33
33
  options[:params][envelope_key] = params
34
+
35
+ options[:retry_failures] = true
36
+
34
37
  response = make_request(:post, path, options)
35
38
 
36
39
  return if response.body.nil?
@@ -20,7 +20,19 @@ module GoCardlessPro
20
20
  params = options.delete(:params) || {}
21
21
  options[:params] = {}
22
22
  options[:params][envelope_key] = params
23
- response = make_request(:post, path, options)
23
+
24
+ options[:retry_failures] = true
25
+
26
+ begin
27
+ response = make_request(:post, path, options)
28
+
29
+ # Response doesn't raise any errors until #body is called
30
+ response.tap(&:body)
31
+ rescue InvalidStateError => e
32
+ return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
33
+
34
+ raise e
35
+ end
24
36
 
25
37
  return if response.body.nil?
26
38
 
@@ -34,7 +46,10 @@ module GoCardlessPro
34
46
  def list(options = {})
35
47
  path = '/mandates'
36
48
 
49
+ options[:retry_failures] = true
50
+
37
51
  response = make_request(:get, path, options)
52
+
38
53
  ListResponse.new(
39
54
  response: response,
40
55
  unenveloped_body: unenvelope_body(response.body),
@@ -61,6 +76,8 @@ module GoCardlessPro
61
76
  def get(identity, options = {})
62
77
  path = sub_url('/mandates/:identity', 'identity' => identity)
63
78
 
79
+ options[:retry_failures] = true
80
+
64
81
  response = make_request(:get, path, options)
65
82
 
66
83
  return if response.body.nil?
@@ -79,6 +96,9 @@ module GoCardlessPro
79
96
  params = options.delete(:params) || {}
80
97
  options[:params] = {}
81
98
  options[:params][envelope_key] = params
99
+
100
+ options[:retry_failures] = true
101
+
82
102
  response = make_request(:put, path, options)
83
103
 
84
104
  return if response.body.nil?
@@ -102,7 +122,19 @@ module GoCardlessPro
102
122
  params = options.delete(:params) || {}
103
123
  options[:params] = {}
104
124
  options[:params]['data'] = params
105
- response = make_request(:post, path, options)
125
+
126
+ options[:retry_failures] = false
127
+
128
+ begin
129
+ response = make_request(:post, path, options)
130
+
131
+ # Response doesn't raise any errors until #body is called
132
+ response.tap(&:body)
133
+ rescue InvalidStateError => e
134
+ return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
135
+
136
+ raise e
137
+ end
106
138
 
107
139
  return if response.body.nil?
108
140
 
@@ -131,7 +163,19 @@ module GoCardlessPro
131
163
  params = options.delete(:params) || {}
132
164
  options[:params] = {}
133
165
  options[:params]['data'] = params
134
- response = make_request(:post, path, options)
166
+
167
+ options[:retry_failures] = false
168
+
169
+ begin
170
+ response = make_request(:post, path, options)
171
+
172
+ # Response doesn't raise any errors until #body is called
173
+ response.tap(&:body)
174
+ rescue InvalidStateError => e
175
+ return get(e.conflicting_resource_id) if e.idempotent_creation_conflict?
176
+
177
+ raise e
178
+ end
135
179
 
136
180
  return if response.body.nil?
137
181