gocardless_pro 2.25.0 → 2.27.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +3 -3
  3. data/lib/gocardless_pro.rb +18 -0
  4. data/lib/gocardless_pro/client.rb +31 -1
  5. data/lib/gocardless_pro/resources/bank_authorisation.rb +87 -0
  6. data/lib/gocardless_pro/resources/billing_request.rb +86 -0
  7. data/lib/gocardless_pro/resources/billing_request_flow.rb +62 -0
  8. data/lib/gocardless_pro/resources/creditor.rb +2 -3
  9. data/lib/gocardless_pro/resources/institution.rb +45 -0
  10. data/lib/gocardless_pro/resources/payer_authorisation.rb +3 -0
  11. data/lib/gocardless_pro/resources/scenario_simulator.rb +42 -0
  12. data/lib/gocardless_pro/resources/webhook.rb +62 -0
  13. data/lib/gocardless_pro/services/bank_authorisations_service.rb +82 -0
  14. data/lib/gocardless_pro/services/billing_request_flows_service.rb +47 -0
  15. data/lib/gocardless_pro/services/billing_requests_service.rb +325 -0
  16. data/lib/gocardless_pro/services/institutions_service.rb +56 -0
  17. data/lib/gocardless_pro/services/payer_authorisations_service.rb +5 -5
  18. data/lib/gocardless_pro/services/scenario_simulators_service.rb +148 -0
  19. data/lib/gocardless_pro/services/subscriptions_service.rb +8 -3
  20. data/lib/gocardless_pro/services/webhooks_service.rb +113 -0
  21. data/lib/gocardless_pro/version.rb +1 -1
  22. data/spec/resources/bank_authorisation_spec.rb +259 -0
  23. data/spec/resources/billing_request_flow_spec.rb +129 -0
  24. data/spec/resources/billing_request_spec.rb +736 -0
  25. data/spec/resources/institution_spec.rb +103 -0
  26. data/spec/resources/scenario_simulator_spec.rb +63 -0
  27. data/spec/resources/webhook_spec.rb +323 -0
  28. data/spec/services/bank_authorisations_service_spec.rb +366 -0
  29. data/spec/services/billing_request_flows_service_spec.rb +152 -0
  30. data/spec/services/billing_requests_service_spec.rb +1042 -0
  31. data/spec/services/institutions_service_spec.rb +223 -0
  32. data/spec/services/scenario_simulators_service_spec.rb +74 -0
  33. data/spec/services/webhooks_service_spec.rb +545 -0
  34. metadata +39 -3
@@ -0,0 +1,56 @@
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 Institution endpoints
12
+ class InstitutionsService < BaseService
13
+ # Returns a list of all supported institutions.
14
+ # Example URL: /institutions
15
+ # @param options [Hash] parameters as a hash, under a params key.
16
+ def list(options = {})
17
+ path = '/institutions'
18
+
19
+ options[:retry_failures] = true
20
+
21
+ response = make_request(:get, path, options)
22
+
23
+ ListResponse.new(
24
+ response: response,
25
+ unenveloped_body: unenvelope_body(response.body),
26
+ resource_class: Resources::Institution
27
+ )
28
+ end
29
+
30
+ # Get a lazily enumerated list of all the items returned. This is simmilar to the `list` method but will paginate for you automatically.
31
+ #
32
+ # @param options [Hash] parameters as a hash. If the request is a GET, these will be converted to query parameters.
33
+ # Otherwise they will be the body of the request.
34
+ def all(options = {})
35
+ Paginator.new(
36
+ service: self,
37
+ options: options
38
+ ).enumerator
39
+ end
40
+
41
+ private
42
+
43
+ # Unenvelope the response of the body using the service's `envelope_key`
44
+ #
45
+ # @param body [Hash]
46
+ def unenvelope_body(body)
47
+ body[envelope_key] || body['data']
48
+ end
49
+
50
+ # return the key which API responses will envelope data under
51
+ def envelope_key
52
+ 'institutions'
53
+ end
54
+ end
55
+ end
56
+ end
@@ -71,7 +71,7 @@ module GoCardlessPro
71
71
  end
72
72
 
73
73
  # Updates a Payer Authorisation. Updates the Payer Authorisation with the
74
- # request data.Can be invoked as many times as needed. Only fields present in
74
+ # request data. Can be invoked as many times as needed. Only fields present in
75
75
  # the request will be modified. An empty array of incomplete_fields means that
76
76
  # the resource is valid. This endpoint has been designed this way so you do not
77
77
  # need to save any payer data on your servers or the browser while still being
@@ -100,8 +100,8 @@ module GoCardlessPro
100
100
  end
101
101
 
102
102
  # Submits all the data previously pushed to this PayerAuthorisation for
103
- # verification.This time, a 200 HTTP status is returned if the resource is valid
104
- # and a 422 error response in case of validation errors. After it is
103
+ # verification. This time, a 200 HTTP status is returned if the resource is
104
+ # valid and a 422 error response in case of validation errors. After it is
105
105
  # successfully submitted, the Payer Authorisation can no longer be edited.
106
106
  # Example URL: /payer_authorisations/:identity/actions/submit
107
107
  #
@@ -149,8 +149,8 @@ module GoCardlessPro
149
149
  # The main use of the confirm endpoint is to enable integrators to acknowledge
150
150
  # the end of the setup process.
151
151
  # They might want to make the payers go through some other steps after they go
152
- # through our flow or make them go through the necessary verification
153
- # mechanism(upcomming feature).
152
+ # through our flow or make them go through the necessary verification mechanism
153
+ # (upcoming feature).
154
154
  # </p>
155
155
  # Example URL: /payer_authorisations/:identity/actions/confirm
156
156
  #
@@ -0,0 +1,148 @@
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_paid_out`: Transitions a payment through to `paid_out`,
31
+ # having been collected successfully and paid out to you. It must start in
32
+ # the `pending_submission` state, and its mandate must be in the
33
+ # `activated` state (unless it is a payment for ACH, BECS, BECS_NZ or
34
+ # SEPA, in which cases the mandate may be `pending_submission`, since
35
+ # their mandates are submitted with their first payment).</li>
36
+ # <li>`payment_failed`: Transitions a payment through to `failed`. It must
37
+ # start in 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_charged_back`: Behaves the same as the `payout_paid_out`
42
+ # simulator, except that the payment is transitioned to `charged_back`
43
+ # after it is paid out, having been charged back by the customer.</li>
44
+ # <li>`payment_chargeback_settled`: Behaves the same as the
45
+ # `payment_charged_back` simulator, except that the charged back payment
46
+ # is additionally included as a debit item in a payout, thereby settling
47
+ # the charged back payment.</li>
48
+ # <li>`payment_late_failure`: Transitions a payment through to
49
+ # `late_failure`, having been apparently collected successfully
50
+ # beforehand. It must start in the `pending_submission` state, and its
51
+ # mandate must be in the `activated` state (unless it is a payment for
52
+ # ACH, BECS, BECS_NZ or SEPA, in which cases the mandate may be
53
+ # `pending_submission`, since their mandates are submitted with their
54
+ # first payment). Not compatible with Autogiro mandates.</li>
55
+ # <li>`payment_late_failure_settled`: Behaves the same as the
56
+ # `payment_late_failure` simulator, except that the late failure is
57
+ # additionally included as a debit item in a payout, thereby settling the
58
+ # late failure.</li>
59
+ # <li>`payment_submitted`: Transitions a payment to `submitted`, without
60
+ # proceeding any further. It must start in the `pending_submission`
61
+ # state.</li>
62
+ # <li>`mandate_activated`: Transitions a mandate through to `activated`,
63
+ # having been submitted to the banks and set up successfully. It must
64
+ # start in the `pending_submission` state. Not compatible with ACH, BECS,
65
+ # BECS_NZ and SEPA mandates, which are submitted and activated with their
66
+ # first payment.</li>
67
+ # <li>`mandate_customer_approval_granted`: Transitions a mandate through
68
+ # to `pending_submission`, as if the customer approved the mandate
69
+ # creation. It must start in the `pending_customer_approval` state.
70
+ # Compatible only with Bacs and SEPA mandates, which support customer
71
+ # signatures on the mandate. All payments associated with the mandate will
72
+ # be transitioned to `pending_submission`. All subscriptions associated
73
+ # with the mandate will become `active`.</li>
74
+ # <li>`mandate_customer_approval_skipped`: Transitions a mandate through
75
+ # to `pending_submission`, as if the customer skipped the mandate approval
76
+ # during the mandate creation process. It must start in the
77
+ # `pending_customer_approval` state. Compatible only with Bacs and SEPA
78
+ # mandates, which support customer signatures on the mandate. All payments
79
+ # associated with the mandate will be transitioned to
80
+ # `pending_submission`. All subscriptions associated with the mandate will
81
+ # become `active`.</li>
82
+ # <li>`mandate_failed`: Transitions a mandate through to `failed`, having
83
+ # been submitted to the banks but found to be invalid (for example due to
84
+ # invalid bank details). It must start in the `pending_submission` or
85
+ # `submitted` states. Not compatible with ACH, BECS, BECS_NZ and SEPA
86
+ # mandates, which are submitted with their first payment.</li>
87
+ # <li>`mandate_expired`: Transitions a mandate through to `expired`,
88
+ # having been submitted to the banks, set up successfully and then expired
89
+ # because no collection attempts were made against it for longer than the
90
+ # scheme's dormancy period (13 months for Bacs, 3 years for SEPA, 15
91
+ # months for ACH, Betalingsservice, and BECS). It must start in the
92
+ # `pending_submission` state. Not compatible with Autogiro, BECS NZ, and
93
+ # PAD mandates, which do not expire.</li>
94
+ # <li>`mandate_transferred`: Transitions a mandate through to
95
+ # `transferred`, having been submitted to the banks, set up successfully
96
+ # and then moved to a new bank account due to the customer using the UK's
97
+ # Current Account Switching Service (CASS). It must start in the
98
+ # `pending_submission` state. Only compatible with Bacs mandates.</li>
99
+ # <li>`mandate_transferred_with_resubmission`: Transitions a mandate
100
+ # through `transferred` and resubmits it to the banks, can be caused be
101
+ # the UK's Current Account Switching Service (CASS) or when a customer
102
+ # contacts GoCardless to change their bank details. It must start in the
103
+ # `pending_submission` state. Only compatible with Bacs, SEPA and Autogiro
104
+ # mandates.</li>
105
+ # <li>`refund_paid`: Transitions a refund to `paid`. It must start in
106
+ # either the `pending_submission` or `submitted` state.</li>
107
+ # <li>`refund_bounced`: Transitions a refund to `bounced`. It must start
108
+ # in either the `pending_submission`, `submitted`, or `paid` state.</li>
109
+ # <li>`payout_bounced`: Transitions a payout to `bounced`. It must start
110
+ # in the `paid` state.</li>
111
+ # <li>`payout_create`: Creates a payout containing payments in
112
+ # `confirmed`, `failed` & `charged_back` states; refunds in `submitted` &
113
+ # `bounced`; and all related fees. Can only be used with a positive total
114
+ # payout balance and when some eligible items exist.</li>
115
+ # </ul>
116
+ # @param options [Hash] parameters as a hash, under a params key.
117
+ def run(identity, options = {})
118
+ path = sub_url('/scenario_simulators/:identity/actions/run', 'identity' => identity)
119
+
120
+ params = options.delete(:params) || {}
121
+ options[:params] = {}
122
+ options[:params]['data'] = params
123
+
124
+ options[:retry_failures] = false
125
+
126
+ response = make_request(:post, path, options)
127
+
128
+ return if response.body.nil?
129
+
130
+ Resources::ScenarioSimulator.new(unenvelope_body(response.body), response)
131
+ end
132
+
133
+ private
134
+
135
+ # Unenvelope the response of the body using the service's `envelope_key`
136
+ #
137
+ # @param body [Hash]
138
+ def unenvelope_body(body)
139
+ body[envelope_key] || body['data']
140
+ end
141
+
142
+ # return the key which API responses will envelope data under
143
+ def envelope_key
144
+ 'scenario_simulators'
145
+ end
146
+ end
147
+ end
148
+ end
@@ -141,8 +141,9 @@ module GoCardlessPro
141
141
  # No payments will be created until it is resumed.
142
142
  #
143
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`)
144
+ # payments (created using `count`),
145
+ # when they continue forever (created without `count` or `end_date`) or
146
+ # the subscription is paused for a number of cycles.
146
147
  #
147
148
  # When `pause_cycles` is omitted the subscription is paused until the [resume
148
149
  # endpoint](#subscriptions-resume-a-subscription) is called.
@@ -166,7 +167,11 @@ module GoCardlessPro
166
167
  # - `validation_failed` if invalid data is provided when attempting to pause a
167
168
  # subscription.
168
169
  #
169
- # - `subscription_not_active` if the subscription is no longer active.
170
+ # - `subscription_paused_cannot_update_cycles` if the subscription is already
171
+ # paused for a number of cycles and the request provides a value for
172
+ # `pause_cycle`.
173
+ #
174
+ # - `subscription_cannot_be_paused` if the subscription cannot be paused.
170
175
  #
171
176
  # - `subscription_already_ended` if the subscription has taken all payments.
172
177
  #
@@ -0,0 +1,113 @@
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
+ else
86
+ raise ArgumentError, 'Unknown mode for :on_idempotency_conflict'
87
+ end
88
+ end
89
+
90
+ raise e
91
+ end
92
+
93
+ return if response.body.nil?
94
+
95
+ Resources::Webhook.new(unenvelope_body(response.body), response)
96
+ end
97
+
98
+ private
99
+
100
+ # Unenvelope the response of the body using the service's `envelope_key`
101
+ #
102
+ # @param body [Hash]
103
+ def unenvelope_body(body)
104
+ body[envelope_key] || body['data']
105
+ end
106
+
107
+ # return the key which API responses will envelope data under
108
+ def envelope_key
109
+ 'webhooks'
110
+ end
111
+ end
112
+ end
113
+ end
@@ -4,5 +4,5 @@ end
4
4
 
5
5
  module GoCardlessPro
6
6
  # Current version of the GC gem
7
- VERSION = '2.25.0'.freeze
7
+ VERSION = '2.27.0'.freeze
8
8
  end
@@ -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
+ 'created_at' => 'created_at-input',
28
+ 'expires_at' => 'expires_at-input',
29
+ 'id' => 'id-input',
30
+ 'last_visited_at' => 'last_visited_at-input',
31
+ 'links' => 'links-input',
32
+ 'redirect_uri' => 'redirect_uri-input',
33
+ 'short_url' => 'short_url-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
+ 'created_at' => 'created_at-input',
62
+ 'expires_at' => 'expires_at-input',
63
+ 'id' => 'id-input',
64
+ 'last_visited_at' => 'last_visited_at-input',
65
+ 'links' => 'links-input',
66
+ 'redirect_uri' => 'redirect_uri-input',
67
+ 'short_url' => 'short_url-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
+ 'created_at' => 'created_at-input',
111
+ 'expires_at' => 'expires_at-input',
112
+ 'id' => 'id-input',
113
+ 'last_visited_at' => 'last_visited_at-input',
114
+ 'links' => 'links-input',
115
+ 'redirect_uri' => 'redirect_uri-input',
116
+ 'short_url' => 'short_url-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
+ 'created_at' => 'created_at-input',
129
+ 'expires_at' => 'expires_at-input',
130
+ 'id' => 'id-input',
131
+ 'last_visited_at' => 'last_visited_at-input',
132
+ 'links' => 'links-input',
133
+ 'redirect_uri' => 'redirect_uri-input',
134
+ 'short_url' => 'short_url-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
+ 'created_at' => 'created_at-input',
147
+ 'expires_at' => 'expires_at-input',
148
+ 'id' => 'id-input',
149
+ 'last_visited_at' => 'last_visited_at-input',
150
+ 'links' => 'links-input',
151
+ 'redirect_uri' => 'redirect_uri-input',
152
+ 'short_url' => 'short_url-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
+ 'created_at' => 'created_at-input',
198
+ 'expires_at' => 'expires_at-input',
199
+ 'id' => 'id-input',
200
+ 'last_visited_at' => 'last_visited_at-input',
201
+ 'links' => 'links-input',
202
+ 'redirect_uri' => 'redirect_uri-input',
203
+ 'short_url' => 'short_url-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
+ 'created_at' => 'created_at-input',
239
+ 'expires_at' => 'expires_at-input',
240
+ 'id' => 'id-input',
241
+ 'last_visited_at' => 'last_visited_at-input',
242
+ 'links' => 'links-input',
243
+ 'redirect_uri' => 'redirect_uri-input',
244
+ 'short_url' => 'short_url-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