gocardless_pro 2.23.0 → 2.28.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +23 -4
  3. data/lib/gocardless_pro.rb +27 -0
  4. data/lib/gocardless_pro/api_service.rb +4 -0
  5. data/lib/gocardless_pro/client.rb +41 -1
  6. data/lib/gocardless_pro/error/authentication_error.rb +4 -0
  7. data/lib/gocardless_pro/error/permission_error.rb +4 -0
  8. data/lib/gocardless_pro/error/rate_limit_error.rb +4 -0
  9. data/lib/gocardless_pro/middlewares/raise_gocardless_errors.rb +12 -1
  10. data/lib/gocardless_pro/resources/bank_authorisation.rb +85 -0
  11. data/lib/gocardless_pro/resources/billing_request.rb +108 -0
  12. data/lib/gocardless_pro/resources/billing_request_flow.rb +72 -0
  13. data/lib/gocardless_pro/resources/billing_request_template.rb +68 -0
  14. data/lib/gocardless_pro/resources/creditor.rb +2 -3
  15. data/lib/gocardless_pro/resources/event.rb +12 -0
  16. data/lib/gocardless_pro/resources/institution.rb +45 -0
  17. data/lib/gocardless_pro/resources/payer_authorisation.rb +131 -0
  18. data/lib/gocardless_pro/resources/payout_item.rb +4 -0
  19. data/lib/gocardless_pro/resources/scenario_simulator.rb +42 -0
  20. data/lib/gocardless_pro/resources/tax_rate.rb +3 -0
  21. data/lib/gocardless_pro/resources/webhook.rb +62 -0
  22. data/lib/gocardless_pro/services/bank_authorisations_service.rb +80 -0
  23. data/lib/gocardless_pro/services/billing_request_flows_service.rb +70 -0
  24. data/lib/gocardless_pro/services/billing_request_templates_service.rb +131 -0
  25. data/lib/gocardless_pro/services/billing_requests_service.rb +352 -0
  26. data/lib/gocardless_pro/services/creditor_bank_accounts_service.rb +0 -4
  27. data/lib/gocardless_pro/services/creditors_service.rb +0 -2
  28. data/lib/gocardless_pro/services/customer_bank_accounts_service.rb +0 -4
  29. data/lib/gocardless_pro/services/customers_service.rb +0 -2
  30. data/lib/gocardless_pro/services/instalment_schedules_service.rb +0 -6
  31. data/lib/gocardless_pro/services/institutions_service.rb +56 -0
  32. data/lib/gocardless_pro/services/mandate_imports_service.rb +0 -6
  33. data/lib/gocardless_pro/services/mandates_service.rb +0 -6
  34. data/lib/gocardless_pro/services/payer_authorisations_service.rb +202 -0
  35. data/lib/gocardless_pro/services/payments_service.rb +0 -6
  36. data/lib/gocardless_pro/services/redirect_flows_service.rb +0 -4
  37. data/lib/gocardless_pro/services/refunds_service.rb +0 -2
  38. data/lib/gocardless_pro/services/scenario_simulators_service.rb +170 -0
  39. data/lib/gocardless_pro/services/subscriptions_service.rb +10 -13
  40. data/lib/gocardless_pro/services/webhooks_service.rb +111 -0
  41. data/lib/gocardless_pro/version.rb +1 -1
  42. data/spec/api_service_spec.rb +12 -1
  43. data/spec/middlewares/raise_gocardless_errors_spec.rb +30 -0
  44. data/spec/resources/bank_authorisation_spec.rb +259 -0
  45. data/spec/resources/billing_request_flow_spec.rb +219 -0
  46. data/spec/resources/billing_request_spec.rb +782 -0
  47. data/spec/resources/billing_request_template_spec.rb +502 -0
  48. data/spec/resources/institution_spec.rb +103 -0
  49. data/spec/resources/payer_authorisation_spec.rb +418 -0
  50. data/spec/resources/scenario_simulator_spec.rb +63 -0
  51. data/spec/resources/webhook_spec.rb +323 -0
  52. data/spec/services/bank_authorisations_service_spec.rb +353 -0
  53. data/spec/services/billing_request_flows_service_spec.rb +253 -0
  54. data/spec/services/billing_request_templates_service_spec.rb +789 -0
  55. data/spec/services/billing_requests_service_spec.rb +1082 -0
  56. data/spec/services/creditor_bank_accounts_service_spec.rb +0 -13
  57. data/spec/services/creditors_service_spec.rb +0 -13
  58. data/spec/services/customer_bank_accounts_service_spec.rb +0 -13
  59. data/spec/services/customers_service_spec.rb +0 -13
  60. data/spec/services/instalment_schedules_service_spec.rb +0 -26
  61. data/spec/services/institutions_service_spec.rb +223 -0
  62. data/spec/services/mandate_imports_service_spec.rb +0 -13
  63. data/spec/services/mandates_service_spec.rb +0 -13
  64. data/spec/services/payer_authorisations_service_spec.rb +559 -0
  65. data/spec/services/payments_service_spec.rb +0 -13
  66. data/spec/services/redirect_flows_service_spec.rb +0 -13
  67. data/spec/services/refunds_service_spec.rb +0 -13
  68. data/spec/services/scenario_simulators_service_spec.rb +74 -0
  69. data/spec/services/subscriptions_service_spec.rb +0 -13
  70. data/spec/services/webhooks_service_spec.rb +545 -0
  71. metadata +54 -3
@@ -0,0 +1,170 @@
1
+ require_relative './base_service'
2
+
3
+ # encoding: utf-8
4
+ #
5
+ # This client is automatically generated from a template and JSON schema definition.
6
+ # See https://github.com/gocardless/gocardless-pro-ruby#contributing before editing.
7
+ #
8
+
9
+ module GoCardlessPro
10
+ module Services
11
+ # Service for making requests to the ScenarioSimulator endpoints
12
+ class ScenarioSimulatorsService < BaseService
13
+ # Runs the specific scenario simulator against the specific resource
14
+ # Example URL: /scenario_simulators/:identity/actions/run
15
+ #
16
+ # @param identity # The unique identifier of the simulator, used to initiate simulations.
17
+ # One of:
18
+ # <ul>
19
+ # <li>`creditor_verification_status_action_required`: Sets a creditor's
20
+ # `verification status` to `action required`, meaning that the creditor
21
+ # must provide further information to GoCardless in order to verify their
22
+ # account to receive payouts.</li>
23
+ # <li>`creditor_verification_status_in_review`: Sets a creditor's
24
+ # `verification status` to `in review`, meaning that the creditor has
25
+ # provided all of the information requested by GoCardless to verify their
26
+ # account, and is now awaiting review.</li>
27
+ # <li>`creditor_verification_status_successful`: Sets a creditor's
28
+ # `verification status` to `successful`, meaning that the creditor is
29
+ # fully verified and can receive payouts.</li>
30
+ # <li>`payment_confirmed`: Transitions a payment through to `confirmed`.
31
+ # It must start in the `pending_submission` state, and its mandate must be
32
+ # in the `activated` state (unless it is a payment for ACH, BECS, BECS_NZ
33
+ # or SEPA, in which cases the mandate may be `pending_submission`, since
34
+ # their mandates are submitted with their first payment).</li>
35
+ # <li>`payment_paid_out`: Transitions a payment through to `paid_out`,
36
+ # having been collected successfully and paid out to you. It must start in
37
+ # the `pending_submission` state, and its mandate must be in the
38
+ # `activated` state (unless it is a payment for ACH, BECS, BECS_NZ or
39
+ # SEPA, in which cases the mandate may be `pending_submission`, since
40
+ # their mandates are submitted with their first payment).</li>
41
+ # <li>`payment_failed`: Transitions a payment through to `failed`. It must
42
+ # start in the `pending_submission` state, and its mandate must be in the
43
+ # `activated` state (unless it is a payment for ACH, BECS, BECS_NZ or
44
+ # SEPA, in which cases the mandate may be `pending_submission`, since
45
+ # their mandates are submitted with their first payment).</li>
46
+ # <li>`payment_charged_back`: Behaves the same as the `payout_paid_out`
47
+ # simulator, except that the payment is transitioned to `charged_back`
48
+ # after it is paid out, having been charged back by the customer.</li>
49
+ # <li>`payment_chargeback_settled`: Behaves the same as the
50
+ # `payment_charged_back` simulator, except that the charged back payment
51
+ # is additionally included as a debit item in a payout, thereby settling
52
+ # the charged back payment.</li>
53
+ # <li>`payment_late_failure`: Transitions a payment through to
54
+ # `late_failure`, having been apparently collected successfully
55
+ # beforehand. It must start in the `pending_submission` state, and its
56
+ # mandate must be in the `activated` state (unless it is a payment for
57
+ # ACH, BECS, BECS_NZ or SEPA, in which cases the mandate may be
58
+ # `pending_submission`, since their mandates are submitted with their
59
+ # first payment). Not compatible with Autogiro mandates.</li>
60
+ # <li>`payment_late_failure_settled`: Behaves the same as the
61
+ # `payment_late_failure` simulator, except that the late failure is
62
+ # additionally included as a debit item in a payout, thereby settling the
63
+ # late failure.</li>
64
+ # <li>`payment_submitted`: Transitions a payment to `submitted`, without
65
+ # proceeding any further. It must start in the `pending_submission`
66
+ # state.</li>
67
+ # <li>`mandate_activated`: Transitions a mandate through to `activated`,
68
+ # having been submitted to the banks and set up successfully. It must
69
+ # start in the `pending_submission` state. Not compatible with ACH, BECS,
70
+ # BECS_NZ and SEPA mandates, which are submitted and activated with their
71
+ # first payment.</li>
72
+ # <li>`mandate_customer_approval_granted`: Transitions a mandate through
73
+ # to `pending_submission`, as if the customer approved the mandate
74
+ # creation. It must start in the `pending_customer_approval` state.
75
+ # Compatible only with Bacs and SEPA mandates, which support customer
76
+ # signatures on the mandate. All payments associated with the mandate will
77
+ # be transitioned to `pending_submission`. All subscriptions associated
78
+ # with the mandate will become `active`.</li>
79
+ # <li>`mandate_customer_approval_skipped`: Transitions a mandate through
80
+ # to `pending_submission`, as if the customer skipped the mandate approval
81
+ # during the mandate creation process. It must start in the
82
+ # `pending_customer_approval` state. Compatible only with Bacs and SEPA
83
+ # mandates, which support customer signatures on the mandate. All payments
84
+ # associated with the mandate will be transitioned to
85
+ # `pending_submission`. All subscriptions associated with the mandate will
86
+ # become `active`.</li>
87
+ # <li>`mandate_failed`: Transitions a mandate through to `failed`, having
88
+ # been submitted to the banks but found to be invalid (for example due to
89
+ # invalid bank details). It must start in the `pending_submission` or
90
+ # `submitted` states. Not compatible with SEPA mandates, which are
91
+ # submitted with their first payment.</li>
92
+ # <li>`mandate_expired`: Transitions a mandate through to `expired`,
93
+ # having been submitted to the banks, set up successfully and then expired
94
+ # because no collection attempts were made against it for longer than the
95
+ # scheme's dormancy period (13 months for Bacs, 3 years for SEPA, 15
96
+ # months for ACH, Betalingsservice, and BECS). It must start in the
97
+ # `pending_submission` state. Not compatible with Autogiro, BECS NZ, and
98
+ # PAD mandates, which do not expire.</li>
99
+ # <li>`mandate_transferred`: Transitions a mandate through to
100
+ # `transferred`, having been submitted to the banks, set up successfully
101
+ # and then moved to a new bank account due to the customer using the UK's
102
+ # Current Account Switching Service (CASS). It must start in the
103
+ # `pending_submission` state. Only compatible with Bacs mandates.</li>
104
+ # <li>`mandate_transferred_with_resubmission`: Transitions a mandate
105
+ # through `transferred` and resubmits it to the banks, can be caused be
106
+ # the UK's Current Account Switching Service (CASS) or when a customer
107
+ # contacts GoCardless to change their bank details. It must start in the
108
+ # `pending_submission` state. Only compatible with Bacs, SEPA and Autogiro
109
+ # mandates.</li>
110
+ # <li>`refund_paid`: Transitions a refund to `paid`. It must start in
111
+ # either the `pending_submission` or `submitted` state.</li>
112
+ # <li>`refund_settled`: Transitions a refund to `paid`, if it's not
113
+ # already, then generates a payout that includes the refund, thereby
114
+ # settling the funds. It must start in one of `pending_submission`,
115
+ # `submitted` or `paid` states.</li>
116
+ # <li>`refund_bounced`: Transitions a refund to `bounced`. It must start
117
+ # in either the `pending_submission`, `submitted`, or `paid` state.</li>
118
+ # <li>`refund_returned`: Transitions a refund to `refund_returned`. The
119
+ # refund must start in `pending_submission`.</li>
120
+ # <li>`payout_bounced`: Transitions a payout to `bounced`. It must start
121
+ # in the `paid` state.</li>
122
+ # <li>`billing_request_fulfilled`: Authorises the billing request, fulfils
123
+ # it, and moves the associated payment to `failed`. The billing request
124
+ # must be in the `pending` state, with all actions completed except for
125
+ # `bank_authorisation`. Only billing requests with a `payment_request` are
126
+ # supported.</li>
127
+ # <li>`billing_request_fulfilled_and_payment_failed`: Authorises the
128
+ # billing request, fulfils it, and moves the associated payment to
129
+ # `failed`. The billing request must be in the `pending` state, with all
130
+ # actions completed except for `bank_authorisation`. Only billing requests
131
+ # with a `payment_request` are supported.</li>
132
+ # <li>`billing_request_fulfilled_and_payment_paid_out`: Authorises the
133
+ # billing request, fulfils it, and moves the associated payment to
134
+ # `paid_out`. The billing request must be in the `pending` state, with all
135
+ # actions completed except for `bank_authorisation`. Only billing requests
136
+ # with a `payment_request` are supported.</li>
137
+ # </ul>
138
+ # @param options [Hash] parameters as a hash, under a params key.
139
+ def run(identity, options = {})
140
+ path = sub_url('/scenario_simulators/:identity/actions/run', 'identity' => identity)
141
+
142
+ params = options.delete(:params) || {}
143
+ options[:params] = {}
144
+ options[:params]['data'] = params
145
+
146
+ options[:retry_failures] = false
147
+
148
+ response = make_request(:post, path, options)
149
+
150
+ return if response.body.nil?
151
+
152
+ Resources::ScenarioSimulator.new(unenvelope_body(response.body), response)
153
+ end
154
+
155
+ private
156
+
157
+ # Unenvelope the response of the body using the service's `envelope_key`
158
+ #
159
+ # @param body [Hash]
160
+ def unenvelope_body(body)
161
+ body[envelope_key] || body['data']
162
+ end
163
+
164
+ # return the key which API responses will envelope data under
165
+ def envelope_key
166
+ 'scenario_simulators'
167
+ end
168
+ end
169
+ end
170
+ end
@@ -34,8 +34,6 @@ module GoCardlessPro
34
34
  raise IdempotencyConflict, e.error
35
35
  when :fetch
36
36
  return get(e.conflicting_resource_id)
37
- else
38
- raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
39
37
  end
40
38
  end
41
39
 
@@ -140,14 +138,15 @@ module GoCardlessPro
140
138
  # Pause a subscription object.
141
139
  # No payments will be created until it is resumed.
142
140
  #
143
- # This can only be used when a subscription collecting a fixed number of
144
- # payments (created using `count`)
145
- # or when they continue forever (created without `count` or `end_date`)
141
+ # This can only be used when a subscription is collecting a fixed number of
142
+ # payments (created using `count`),
143
+ # when they continue forever (created without `count` or `end_date`) or
144
+ # the subscription is already paused for a number of cycles.
146
145
  #
147
146
  # When `pause_cycles` is omitted the subscription is paused until the [resume
148
147
  # endpoint](#subscriptions-resume-a-subscription) is called.
149
148
  # If the subscription is collecting a fixed number of payments, `end_date` will
150
- # be set to `nil`.
149
+ # be set to `null`.
151
150
  # When paused indefinitely, `upcoming_payments` will be empty.
152
151
  #
153
152
  # When `pause_cycles` is provided the subscription will be paused for the number
@@ -166,7 +165,11 @@ module GoCardlessPro
166
165
  # - `validation_failed` if invalid data is provided when attempting to pause a
167
166
  # subscription.
168
167
  #
169
- # - `subscription_not_active` if the subscription is no longer active.
168
+ # - `subscription_paused_cannot_update_cycles` if the subscription is already
169
+ # paused for a number of cycles and the request provides a value for
170
+ # `pause_cycle`.
171
+ #
172
+ # - `subscription_cannot_be_paused` if the subscription cannot be paused.
170
173
  #
171
174
  # - `subscription_already_ended` if the subscription has taken all payments.
172
175
  #
@@ -198,8 +201,6 @@ module GoCardlessPro
198
201
  raise IdempotencyConflict, e.error
199
202
  when :fetch
200
203
  return get(e.conflicting_resource_id)
201
- else
202
- raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
203
204
  end
204
205
  end
205
206
 
@@ -253,8 +254,6 @@ module GoCardlessPro
253
254
  raise IdempotencyConflict, e.error
254
255
  when :fetch
255
256
  return get(e.conflicting_resource_id)
256
- else
257
- raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
258
257
  end
259
258
  end
260
259
 
@@ -297,8 +296,6 @@ module GoCardlessPro
297
296
  raise IdempotencyConflict, e.error
298
297
  when :fetch
299
298
  return get(e.conflicting_resource_id)
300
- else
301
- raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
302
299
  end
303
300
  end
304
301
 
@@ -0,0 +1,111 @@
1
+ require_relative './base_service'
2
+
3
+ # encoding: utf-8
4
+ #
5
+ # This client is automatically generated from a template and JSON schema definition.
6
+ # See https://github.com/gocardless/gocardless-pro-ruby#contributing before editing.
7
+ #
8
+
9
+ module GoCardlessPro
10
+ module Services
11
+ # Service for making requests to the Webhook endpoints
12
+ class WebhooksService < BaseService
13
+ # Returns a [cursor-paginated](#api-usage-cursor-pagination) list of your
14
+ # webhooks.
15
+ # Example URL: /webhooks
16
+ # @param options [Hash] parameters as a hash, under a params key.
17
+ def list(options = {})
18
+ path = '/webhooks'
19
+
20
+ options[:retry_failures] = true
21
+
22
+ response = make_request(:get, path, options)
23
+
24
+ ListResponse.new(
25
+ response: response,
26
+ unenveloped_body: unenvelope_body(response.body),
27
+ resource_class: Resources::Webhook
28
+ )
29
+ end
30
+
31
+ # Get a lazily enumerated list of all the items returned. This is simmilar to the `list` method but will paginate for you automatically.
32
+ #
33
+ # @param options [Hash] parameters as a hash. If the request is a GET, these will be converted to query parameters.
34
+ # Otherwise they will be the body of the request.
35
+ def all(options = {})
36
+ Paginator.new(
37
+ service: self,
38
+ options: options
39
+ ).enumerator
40
+ end
41
+
42
+ # Retrieves the details of an existing webhook.
43
+ # Example URL: /webhooks/:identity
44
+ #
45
+ # @param identity # Unique identifier, beginning with "WB".
46
+ # @param options [Hash] parameters as a hash, under a params key.
47
+ def get(identity, options = {})
48
+ path = sub_url('/webhooks/:identity', 'identity' => identity)
49
+
50
+ options[:retry_failures] = true
51
+
52
+ response = make_request(:get, path, options)
53
+
54
+ return if response.body.nil?
55
+
56
+ Resources::Webhook.new(unenvelope_body(response.body), response)
57
+ end
58
+
59
+ # Requests for a previous webhook to be sent again
60
+ # Example URL: /webhooks/:identity/actions/retry
61
+ #
62
+ # @param identity # Unique identifier, beginning with "WB".
63
+ # @param options [Hash] parameters as a hash, under a params key.
64
+ def retry(identity, options = {})
65
+ path = sub_url('/webhooks/:identity/actions/retry', 'identity' => identity)
66
+
67
+ params = options.delete(:params) || {}
68
+ options[:params] = {}
69
+ options[:params]['data'] = params
70
+
71
+ options[:retry_failures] = false
72
+
73
+ begin
74
+ response = make_request(:post, path, options)
75
+
76
+ # Response doesn't raise any errors until #body is called
77
+ response.tap(&:body)
78
+ rescue InvalidStateError => e
79
+ if e.idempotent_creation_conflict?
80
+ case @api_service.on_idempotency_conflict
81
+ when :raise
82
+ raise IdempotencyConflict, e.error
83
+ when :fetch
84
+ return get(e.conflicting_resource_id)
85
+ end
86
+ end
87
+
88
+ raise e
89
+ end
90
+
91
+ return if response.body.nil?
92
+
93
+ Resources::Webhook.new(unenvelope_body(response.body), response)
94
+ end
95
+
96
+ private
97
+
98
+ # Unenvelope the response of the body using the service's `envelope_key`
99
+ #
100
+ # @param body [Hash]
101
+ def unenvelope_body(body)
102
+ body[envelope_key] || body['data']
103
+ end
104
+
105
+ # return the key which API responses will envelope data under
106
+ def envelope_key
107
+ 'webhooks'
108
+ end
109
+ end
110
+ end
111
+ end
@@ -4,5 +4,5 @@ end
4
4
 
5
5
  module GoCardlessPro
6
6
  # Current version of the GC gem
7
- VERSION = '2.23.0'.freeze
7
+ VERSION = '2.28.0'.freeze
8
8
  end
@@ -1,8 +1,11 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe GoCardlessPro::ApiService do
4
- subject(:service) { described_class.new('https://api.example.com', 'secret_token') }
4
+ subject(:service) do
5
+ described_class.new('https://api.example.com', 'secret_token', options)
6
+ end
5
7
 
8
+ let(:options) { {} }
6
9
  let(:default_response) do
7
10
  {
8
11
  status: 200,
@@ -174,4 +177,12 @@ describe GoCardlessPro::ApiService do
174
177
  expect(stub).to have_been_requested
175
178
  end
176
179
  end
180
+
181
+ describe 'when passing an invalid :on_idempotency_conflict' do
182
+ let(:options) { { on_idempotency_conflict: :junk } }
183
+
184
+ it 'raises an error' do
185
+ expect { service }.to raise_error(ArgumentError)
186
+ end
187
+ end
177
188
  end
@@ -74,6 +74,36 @@ describe GoCardlessPro::Middlewares::RaiseGoCardlessErrors do
74
74
  end
75
75
  end
76
76
 
77
+ context 'for a Permission error' do
78
+ let(:status) { 403 }
79
+ let(:body) { { error: { type: 'invalid_api_usage' } }.to_json }
80
+
81
+ it 'raises a GoCardlessError' do
82
+ expect { connection.post('https://api.gocardless.com/widgets') }.
83
+ to raise_error(GoCardlessPro::PermissionError)
84
+ end
85
+ end
86
+
87
+ context 'for a RateLimit error' do
88
+ let(:status) { 429 }
89
+ let(:body) { { error: { type: 'invalid_api_usage' } }.to_json }
90
+
91
+ it 'raises a GoCardlessError' do
92
+ expect { connection.post('https://api.gocardless.com/widgets') }.
93
+ to raise_error(GoCardlessPro::RateLimitError)
94
+ end
95
+ end
96
+
97
+ context 'for a Authentication error' do
98
+ let(:status) { 401 }
99
+ let(:body) { { error: { type: 'invalid_api_usage' } }.to_json }
100
+
101
+ it 'raises a GoCardlessError' do
102
+ expect { connection.post('https://api.gocardless.com/widgets') }.
103
+ to raise_error(GoCardlessPro::AuthenticationError)
104
+ end
105
+ end
106
+
77
107
  context 'for an invalid API usage error' do
78
108
  let(:status) { 400 }
79
109
  let(:body) { { error: { type: 'invalid_api_usage' } }.to_json }
@@ -0,0 +1,259 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoCardlessPro::Resources::BankAuthorisation 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.bank_authorisations.get(id) }
16
+
17
+ context 'passing in a custom header' do
18
+ let!(:stub) do
19
+ stub_url = '/bank_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
+ 'bank_authorisations' => {
25
+
26
+ 'authorisation_type' => 'authorisation_type-input',
27
+ 'authorised_at' => 'authorised_at-input',
28
+ 'created_at' => 'created_at-input',
29
+ 'expires_at' => 'expires_at-input',
30
+ 'id' => 'id-input',
31
+ 'last_visited_at' => 'last_visited_at-input',
32
+ 'links' => 'links-input',
33
+ 'redirect_uri' => 'redirect_uri-input',
34
+ 'url' => 'url-input',
35
+ },
36
+ }.to_json,
37
+ headers: response_headers
38
+ )
39
+ end
40
+
41
+ subject(:get_response) do
42
+ client.bank_authorisations.get(id, headers: {
43
+ 'Foo' => 'Bar',
44
+ })
45
+ end
46
+
47
+ it 'includes the header' do
48
+ get_response
49
+ expect(stub).to have_been_requested
50
+ end
51
+ end
52
+
53
+ context 'when there is a bank_authorisation to return' do
54
+ before do
55
+ stub_url = '/bank_authorisations/:identity'.gsub(':identity', id)
56
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
57
+ body: {
58
+ 'bank_authorisations' => {
59
+
60
+ 'authorisation_type' => 'authorisation_type-input',
61
+ 'authorised_at' => 'authorised_at-input',
62
+ 'created_at' => 'created_at-input',
63
+ 'expires_at' => 'expires_at-input',
64
+ 'id' => 'id-input',
65
+ 'last_visited_at' => 'last_visited_at-input',
66
+ 'links' => 'links-input',
67
+ 'redirect_uri' => 'redirect_uri-input',
68
+ 'url' => 'url-input',
69
+ },
70
+ }.to_json,
71
+ headers: response_headers
72
+ )
73
+ end
74
+
75
+ it 'wraps the response in a resource' do
76
+ expect(get_response).to be_a(GoCardlessPro::Resources::BankAuthorisation)
77
+ end
78
+ end
79
+
80
+ context 'when nothing is returned' do
81
+ before do
82
+ stub_url = '/bank_authorisations/:identity'.gsub(':identity', id)
83
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
84
+ body: '',
85
+ headers: response_headers
86
+ )
87
+ end
88
+
89
+ it 'returns nil' do
90
+ expect(get_response).to be_nil
91
+ end
92
+ end
93
+
94
+ context "when an ID is specified which can't be included in a valid URI" do
95
+ let(:id) { '`' }
96
+
97
+ it "doesn't raise an error" do
98
+ expect { get_response }.to_not raise_error(/bad URI/)
99
+ end
100
+ end
101
+ end
102
+
103
+ describe '#create' do
104
+ subject(:post_create_response) { client.bank_authorisations.create(params: new_resource) }
105
+ context 'with a valid request' do
106
+ let(:new_resource) do
107
+ {
108
+
109
+ 'authorisation_type' => 'authorisation_type-input',
110
+ 'authorised_at' => 'authorised_at-input',
111
+ 'created_at' => 'created_at-input',
112
+ 'expires_at' => 'expires_at-input',
113
+ 'id' => 'id-input',
114
+ 'last_visited_at' => 'last_visited_at-input',
115
+ 'links' => 'links-input',
116
+ 'redirect_uri' => 'redirect_uri-input',
117
+ 'url' => 'url-input',
118
+ }
119
+ end
120
+
121
+ before do
122
+ stub_request(:post, %r{.*api.gocardless.com/bank_authorisations}).
123
+ with(
124
+ body: {
125
+ 'bank_authorisations' => {
126
+
127
+ 'authorisation_type' => 'authorisation_type-input',
128
+ 'authorised_at' => 'authorised_at-input',
129
+ 'created_at' => 'created_at-input',
130
+ 'expires_at' => 'expires_at-input',
131
+ 'id' => 'id-input',
132
+ 'last_visited_at' => 'last_visited_at-input',
133
+ 'links' => 'links-input',
134
+ 'redirect_uri' => 'redirect_uri-input',
135
+ 'url' => 'url-input',
136
+ },
137
+ }
138
+ ).
139
+ to_return(
140
+ body: {
141
+ 'bank_authorisations' =>
142
+
143
+ {
144
+
145
+ 'authorisation_type' => 'authorisation_type-input',
146
+ 'authorised_at' => 'authorised_at-input',
147
+ 'created_at' => 'created_at-input',
148
+ 'expires_at' => 'expires_at-input',
149
+ 'id' => 'id-input',
150
+ 'last_visited_at' => 'last_visited_at-input',
151
+ 'links' => 'links-input',
152
+ 'redirect_uri' => 'redirect_uri-input',
153
+ 'url' => 'url-input',
154
+ },
155
+
156
+ }.to_json,
157
+ headers: response_headers
158
+ )
159
+ end
160
+
161
+ it 'creates and returns the resource' do
162
+ expect(post_create_response).to be_a(GoCardlessPro::Resources::BankAuthorisation)
163
+ end
164
+ end
165
+
166
+ context 'with a request that returns a validation error' do
167
+ let(:new_resource) { {} }
168
+
169
+ before do
170
+ stub_request(:post, %r{.*api.gocardless.com/bank_authorisations}).to_return(
171
+ body: {
172
+ error: {
173
+ type: 'validation_failed',
174
+ code: 422,
175
+ errors: [
176
+ { message: 'test error message', field: 'test_field' },
177
+ ],
178
+ },
179
+ }.to_json,
180
+ headers: response_headers,
181
+ status: 422
182
+ )
183
+ end
184
+
185
+ it 'throws the correct error' do
186
+ expect { post_create_response }.to raise_error(GoCardlessPro::ValidationError)
187
+ end
188
+ end
189
+
190
+ context 'with a request that returns an idempotent creation conflict error' do
191
+ let(:id) { 'ID123' }
192
+
193
+ let(:new_resource) do
194
+ {
195
+
196
+ 'authorisation_type' => 'authorisation_type-input',
197
+ 'authorised_at' => 'authorised_at-input',
198
+ 'created_at' => 'created_at-input',
199
+ 'expires_at' => 'expires_at-input',
200
+ 'id' => 'id-input',
201
+ 'last_visited_at' => 'last_visited_at-input',
202
+ 'links' => 'links-input',
203
+ 'redirect_uri' => 'redirect_uri-input',
204
+ 'url' => 'url-input',
205
+ }
206
+ end
207
+
208
+ let!(:post_stub) do
209
+ stub_request(:post, %r{.*api.gocardless.com/bank_authorisations}).to_return(
210
+ body: {
211
+ error: {
212
+ type: 'invalid_state',
213
+ code: 409,
214
+ errors: [
215
+ {
216
+ message: 'A resource has already been created with this idempotency key',
217
+ reason: 'idempotent_creation_conflict',
218
+ links: {
219
+ conflicting_resource_id: id,
220
+ },
221
+ },
222
+ ],
223
+ },
224
+ }.to_json,
225
+ headers: response_headers,
226
+ status: 409
227
+ )
228
+ end
229
+
230
+ let!(:get_stub) do
231
+ stub_url = "/bank_authorisations/#{id}"
232
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).
233
+ to_return(
234
+ body: {
235
+ 'bank_authorisations' => {
236
+
237
+ 'authorisation_type' => 'authorisation_type-input',
238
+ 'authorised_at' => 'authorised_at-input',
239
+ 'created_at' => 'created_at-input',
240
+ 'expires_at' => 'expires_at-input',
241
+ 'id' => 'id-input',
242
+ 'last_visited_at' => 'last_visited_at-input',
243
+ 'links' => 'links-input',
244
+ 'redirect_uri' => 'redirect_uri-input',
245
+ 'url' => 'url-input',
246
+ },
247
+ }.to_json,
248
+ headers: response_headers
249
+ )
250
+ end
251
+
252
+ it 'fetches the already-created resource' do
253
+ post_create_response
254
+ expect(post_stub).to have_been_requested
255
+ expect(get_stub).to have_been_requested
256
+ end
257
+ end
258
+ end
259
+ end