stripe 2.8.0 → 2.9.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 747667dd050969e755253ea2c5328142ebff0dd2
4
- data.tar.gz: f60cfc0497a8b0044811283e893a667755f3f6cd
3
+ metadata.gz: 0f9ea5d1ce3e948a439878fadba81ef52b47c24e
4
+ data.tar.gz: 00d62fe82daf1570ada3004672ddd15b6fe2425b
5
5
  SHA512:
6
- metadata.gz: b405457d2c0615be9e9fdf1ea20229b95365fb0a615d2f1b508fe0b4466c8842e7a7469317208e477280df0eb51d280e1e82928d5465de54fdca7e786df44faa
7
- data.tar.gz: 8718ad60ec44fd3af67bfb5b8567c56a35141ce14937f6a88ed7d455084ed491cb93a3d2fb45c31d2297d0324667ba8d43517aea65b87c72aea7c28240ed9863
6
+ metadata.gz: 227094e5bb975bbd0acaf044d5f3708055fb8c12f043af80dd4851a8a784646f2f506d45c4a87ea166cbae285085623eb7aebb1366a1f5dec035a95437fede56
7
+ data.tar.gz: 3158ef959bc7d5ed39bc26b9482515b3c8aec2fdf0d96d6f6a87c2481d254517688e994d6b6ea8fd7006a70d809f42af8ab76eb0caa621e6ec1f69bf47a85c9b
@@ -1,3 +1,7 @@
1
+ === 2.9.0 2017-05-18
2
+
3
+ * Support for OAuth operations in `Stripe::OAuth`
4
+
1
5
  === 2.8.0 2017-04-28
2
6
 
3
7
  * Support for checking webhook signatures
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.8.0
1
+ 2.9.0
@@ -69,6 +69,9 @@ require 'stripe/three_d_secure'
69
69
  require 'stripe/token'
70
70
  require 'stripe/transfer'
71
71
 
72
+ # OAuth
73
+ require 'stripe/oauth'
74
+
72
75
  module Stripe
73
76
  DEFAULT_CA_BUNDLE_PATH = File.dirname(__FILE__) + '/data/ca-certificates.crt'
74
77
 
@@ -90,7 +93,7 @@ module Stripe
90
93
  @read_timeout = 80
91
94
 
92
95
  class << self
93
- attr_accessor :stripe_account, :api_key, :api_base, :verify_ssl_certs, :api_version, :connect_base, :uploads_base,
96
+ attr_accessor :stripe_account, :api_key, :api_base, :verify_ssl_certs, :api_version, :client_id, :connect_base, :uploads_base,
94
97
  :open_timeout, :read_timeout
95
98
 
96
99
  attr_reader :max_network_retry_delay, :initial_network_retry_delay
@@ -93,11 +93,12 @@ module Stripe
93
93
  raise NoMethodError.new('Overridding legal_entity can cause serious issues. Instead, set the individual fields of legal_entity like blah.legal_entity.first_name = \'Blah\'')
94
94
  end
95
95
 
96
- def deauthorize(client_id, opts={})
97
- opts = {:api_base => Stripe.connect_base}.merge(Util.normalize_opts(opts))
98
- resp, opts = request(:post, '/oauth/deauthorize', { 'client_id' => client_id, 'stripe_user_id' => self.id }, opts)
99
- opts.delete(:api_base) # the api_base here is a one-off, don't persist it
100
- Util.convert_to_stripe_object(resp.data, opts)
96
+ def deauthorize(client_id=nil, opts={})
97
+ params = {
98
+ client_id: client_id,
99
+ stripe_user_id: self.id,
100
+ }
101
+ OAuth.deauthorize(params, opts)
101
102
  end
102
103
 
103
104
  ARGUMENT_NOT_PROVIDED = Object.new
@@ -100,4 +100,44 @@ module Stripe
100
100
  @sig_header = sig_header
101
101
  end
102
102
  end
103
+
104
+ module OAuth
105
+ # OAuthError is raised when the OAuth API returns an error.
106
+ class OAuthError < StripeError
107
+ attr_accessor :code
108
+
109
+ def initialize(code, description, http_status: nil, http_body: nil, json_body: nil,
110
+ http_headers: nil)
111
+ super(description, http_status: http_status, http_body: http_body,
112
+ json_body: json_body, http_headers: http_headers)
113
+ @code = code
114
+ end
115
+ end
116
+
117
+ # InvalidGrantError is raised when a specified code doesn't exist, is
118
+ # expired, has been used, or doesn't belong to you; a refresh token doesn't
119
+ # exist, or doesn't belong to you; or if an API key's mode (live or test)
120
+ # doesn't match the mode of a code or refresh token.
121
+ class InvalidGrantError < OAuthError
122
+ end
123
+
124
+ # InvalidRequestError is raised when a code, refresh token, or grant type
125
+ # parameter is not provided, but was required.
126
+ class InvalidRequestError < OAuthError
127
+ end
128
+
129
+ # InvalidScopeError is raised when an invalid scope parameter is provided.
130
+ class InvalidScopeError < OAuthError
131
+ end
132
+
133
+ # UnsupportedGrantTypeError is raised when an unuspported grant type
134
+ # parameter is specified.
135
+ class UnsupportedGrantTypeError < OAuthError
136
+ end
137
+
138
+ # UnsupportedResponseTypeError is raised when an unsupported response type
139
+ # parameter is specified.
140
+ class UnsupportedResponseTypeError < OAuthError
141
+ end
142
+ end
103
143
  end
@@ -0,0 +1,56 @@
1
+ module Stripe
2
+ module OAuth
3
+ module OAuthOperations
4
+ extend APIOperations::Request::ClassMethods
5
+
6
+ def self.request(method, url, params, opts)
7
+ opts = Util.normalize_opts(opts)
8
+ opts[:client] ||= StripeClient.active_client
9
+ opts[:api_base] ||= Stripe.connect_base
10
+
11
+ super(method, url, params, opts)
12
+ end
13
+ end
14
+
15
+ def self.get_client_id(params={})
16
+ client_id = params[:client_id] || Stripe.client_id
17
+ unless client_id
18
+ raise AuthenticationError.new('No client_id provided. ' \
19
+ 'Set your client_id using "Stripe.client_id = <CLIENT-ID>". ' \
20
+ 'You can find your client_ids in your Stripe dashboard at ' \
21
+ 'https://dashboard.stripe.com/account/applications/settings, ' \
22
+ 'after registering your account as a platform. See ' \
23
+ 'https://stripe.com/docs/connect/standalone-accounts for details, ' \
24
+ 'or email support@stripe.com if you have any questions.')
25
+ end
26
+ client_id
27
+ end
28
+
29
+ def self.authorize_url(params={}, opts={})
30
+ base = opts[:connect_base] || Stripe.connect_base
31
+
32
+ params[:client_id] = get_client_id(params)
33
+ params[:response_type] ||= 'code'
34
+ query = Util.encode_parameters(params)
35
+
36
+ "#{base}/oauth/authorize?#{query}"
37
+ end
38
+
39
+ def self.token(params={}, opts={})
40
+ opts = Util.normalize_opts(opts)
41
+ resp, opts = OAuthOperations.request(
42
+ :post, '/oauth/token', params, opts)
43
+ # This is just going to return a generic StripeObject, but that's okay
44
+ Util.convert_to_stripe_object(resp.data, opts)
45
+ end
46
+
47
+ def self.deauthorize(params={}, opts={})
48
+ opts = Util.normalize_opts(opts)
49
+ params[:client_id] = get_client_id(params)
50
+ resp, opts = OAuthOperations.request(
51
+ :post, '/oauth/deauthorize', params, opts)
52
+ # This is just going to return a generic StripeObject, but that's okay
53
+ Util.convert_to_stripe_object(resp.data, opts)
54
+ end
55
+ end
56
+ end
@@ -197,7 +197,7 @@ module Stripe
197
197
  case e
198
198
  when Faraday::ClientError
199
199
  if e.response
200
- handle_api_error(e.response)
200
+ handle_error_response(e.response)
201
201
  else
202
202
  handle_network_error(e, retry_count, api_base)
203
203
  end
@@ -229,12 +229,12 @@ module Stripe
229
229
  str
230
230
  end
231
231
 
232
- def handle_api_error(http_resp)
232
+ def handle_error_response(http_resp)
233
233
  begin
234
234
  resp = StripeResponse.from_faraday_hash(http_resp)
235
- error = resp.data[:error]
235
+ error_data = resp.data[:error]
236
236
 
237
- unless error && error.is_a?(Hash)
237
+ unless error_data
238
238
  raise StripeError.new("Indeterminate error")
239
239
  end
240
240
 
@@ -242,47 +242,79 @@ module Stripe
242
242
  raise general_api_error(http_resp[:status], http_resp[:body])
243
243
  end
244
244
 
245
+ if error_data.is_a?(String)
246
+ error = specific_oauth_error(resp, error_data)
247
+ end
248
+ if error.nil?
249
+ error = specific_api_error(resp, error_data)
250
+ end
251
+
252
+ error.response = resp
253
+ raise(error)
254
+ end
255
+
256
+ def specific_api_error(resp, error_data)
245
257
  case resp.http_status
246
258
  when 400, 404
247
259
  error = InvalidRequestError.new(
248
- error[:message], error[:param],
260
+ error_data[:message], error_data[:param],
249
261
  http_status: resp.http_status, http_body: resp.http_body,
250
262
  json_body: resp.data, http_headers: resp.http_headers
251
263
  )
252
264
  when 401
253
265
  error = AuthenticationError.new(
254
- error[:message],
266
+ error_data[:message],
255
267
  http_status: resp.http_status, http_body: resp.http_body,
256
268
  json_body: resp.data, http_headers: resp.http_headers
257
269
  )
258
270
  when 402
259
271
  error = CardError.new(
260
- error[:message], error[:param], error[:code],
272
+ error_data[:message], error_data[:param], error_data[:code],
261
273
  http_status: resp.http_status, http_body: resp.http_body,
262
274
  json_body: resp.data, http_headers: resp.http_headers
263
275
  )
264
276
  when 403
265
277
  error = PermissionError.new(
266
- error[:message],
278
+ error_data[:message],
267
279
  http_status: resp.http_status, http_body: resp.http_body,
268
280
  json_body: resp.data, http_headers: resp.http_headers
269
281
  )
270
282
  when 429
271
283
  error = RateLimitError.new(
272
- error[:message],
284
+ error_data[:message],
273
285
  http_status: resp.http_status, http_body: resp.http_body,
274
286
  json_body: resp.data, http_headers: resp.http_headers
275
287
  )
276
288
  else
277
289
  error = APIError.new(
278
- error[:message],
290
+ error_data[:message],
279
291
  http_status: resp.http_status, http_body: resp.http_body,
280
292
  json_body: resp.data, http_headers: resp.http_headers
281
293
  )
282
294
  end
283
295
 
284
- error.response = resp
285
- raise(error)
296
+ error
297
+ end
298
+
299
+ # Attempts to look at a response's error code and return an OAuth error if
300
+ # one matches. Will return `nil` if the code isn't recognized.
301
+ def specific_oauth_error(resp, error_code)
302
+ description = resp.data[:error_description] || error_code
303
+
304
+ args = [error_code, description, {
305
+ http_status: resp.http_status, http_body: resp.http_body,
306
+ json_body: resp.data, http_headers: resp.http_headers
307
+ }]
308
+
309
+ case error_code
310
+ when 'invalid_grant' then OAuth::InvalidGrantError.new(*args)
311
+ when 'invalid_request' then OAuth::InvalidRequestError.new(*args)
312
+ when 'invalid_scope' then OAuth::InvalidScopeError.new(*args)
313
+ when 'unsupported_grant_type' then OAuth::UnsupportedGrantTypeError.new(*args)
314
+ when 'unsupported_response_type' then OAuth::UnsupportedResponseTypeError.new(*args)
315
+ else
316
+ nil
317
+ end
286
318
  end
287
319
 
288
320
  def handle_network_error(e, retry_count, api_base=nil)
@@ -1,3 +1,3 @@
1
1
  module Stripe
2
- VERSION = '2.8.0'
2
+ VERSION = '2.9.0'
3
3
  end
@@ -0,0 +1,85 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ module Stripe
4
+ class OAuthTest < Test::Unit::TestCase
5
+ setup do
6
+ Stripe.client_id = 'ca_test'
7
+ end
8
+
9
+ teardown do
10
+ Stripe.client_id = nil
11
+ end
12
+
13
+ context ".authorize_url" do
14
+ should "return the authorize URL" do
15
+ uri_str = OAuth.authorize_url({
16
+ scope: 'read_write',
17
+ state: 'csrf_token',
18
+ stripe_user: {
19
+ email: 'test@example.com',
20
+ url: 'https://example.com/profile/test',
21
+ country: 'US',
22
+ },
23
+ })
24
+
25
+ uri = URI::parse(uri_str)
26
+ params = CGI::parse(uri.query)
27
+
28
+ assert_equal('https', uri.scheme)
29
+ assert_equal('connect.stripe.com', uri.host)
30
+ assert_equal('/oauth/authorize', uri.path)
31
+
32
+ assert_equal(['ca_test'], params['client_id'])
33
+ assert_equal(['read_write'], params['scope'])
34
+ assert_equal(['test@example.com'], params['stripe_user[email]'])
35
+ assert_equal(['https://example.com/profile/test'], params['stripe_user[url]'])
36
+ assert_equal(['US'], params['stripe_user[country]'])
37
+ end
38
+ end
39
+
40
+ context ".token" do
41
+ should "exchange a code for an access token" do
42
+ # The OpenAPI fixtures don't cover the OAuth endpoints, so we just
43
+ # stub the request manually.
44
+ stub_request(:post, "#{Stripe.connect_base}/oauth/token").
45
+ with(body: {
46
+ 'grant_type' => 'authorization_code',
47
+ 'code' => 'this_is_an_authorization_code',
48
+ }).
49
+ to_return(body: JSON.generate({
50
+ access_token: 'sk_access_token',
51
+ scope: 'read_only',
52
+ livemode: false,
53
+ token_type: 'bearer',
54
+ refresh_token: 'sk_refresh_token',
55
+ stripe_user_id: 'acct_test',
56
+ stripe_publishable_key: 'pk_test',
57
+ }))
58
+
59
+ resp = OAuth.token({
60
+ grant_type: 'authorization_code',
61
+ code: 'this_is_an_authorization_code',
62
+ })
63
+ assert_equal('sk_access_token', resp.access_token)
64
+ end
65
+ end
66
+
67
+ context ".deauthorize" do
68
+ should "deauthorize an account" do
69
+ # The OpenAPI fixtures don't cover the OAuth endpoints, so we just
70
+ # stub the request manually.
71
+ stub_request(:post, "#{Stripe.connect_base}/oauth/deauthorize").
72
+ with(body: {
73
+ 'client_id' => 'ca_test',
74
+ 'stripe_user_id' => 'acct_test_deauth',
75
+ }).
76
+ to_return(body: JSON.generate({
77
+ stripe_user_id: 'acct_test_deauth',
78
+ }))
79
+
80
+ resp = OAuth.deauthorize({stripe_user_id: 'acct_test_deauth'})
81
+ assert_equal('acct_test_deauth', resp.stripe_user_id)
82
+ end
83
+ end
84
+ end
85
+ end
@@ -243,16 +243,16 @@ module Stripe
243
243
  assert_equal 'Invalid response object from API: "" (HTTP response code was 200)', e.message
244
244
  end
245
245
 
246
- should "handle error response with non-object error value" do
246
+ should "handle error response with unknown value" do
247
247
  stub_request(:post, "#{Stripe.api_base}/v1/charges").
248
- to_return(body: JSON.generate({ error: "foo" }), status: 500)
248
+ to_return(body: JSON.generate({ bar: "foo" }), status: 500)
249
249
 
250
250
  client = StripeClient.new
251
251
  e = assert_raises Stripe::APIError do
252
252
  client.execute_request(:post, '/v1/charges')
253
253
  end
254
254
 
255
- assert_equal 'Invalid response object from API: "{\"error\":\"foo\"}" (HTTP response code was 500)', e.message
255
+ assert_equal 'Invalid response object from API: "{\"bar\":\"foo\"}" (HTTP response code was 500)', e.message
256
256
  end
257
257
 
258
258
  should "raise InvalidRequestError on 400" do
@@ -332,6 +332,42 @@ module Stripe
332
332
  assert_equal(true, e.json_body.kind_of?(Hash))
333
333
  end
334
334
  end
335
+
336
+ should "raise OAuth::InvalidRequestError when error is a string with value 'invalid_request'" do
337
+ stub_request(:post, "#{Stripe.connect_base}/oauth/token").
338
+ to_return(body: JSON.generate({
339
+ error: "invalid_request",
340
+ error_description: "No grant type specified",
341
+ }), status: 400)
342
+
343
+ client = StripeClient.new
344
+ opts = {api_base: Stripe.connect_base}
345
+ e = assert_raises Stripe::OAuth::InvalidRequestError do
346
+ client.execute_request(:post, '/oauth/token', opts)
347
+ end
348
+
349
+ assert_equal(400, e.http_status)
350
+ assert_equal(true, !!e.http_body)
351
+ assert_equal('No grant type specified', e.message)
352
+ end
353
+
354
+ should "raise OAuth::InvalidGrantError when error is a string with value 'invalid_grant'" do
355
+ stub_request(:post, "#{Stripe.connect_base}/oauth/token").
356
+ to_return(body: JSON.generate({
357
+ error: "invalid_grant",
358
+ error_description: "This authorization code has already been used. All tokens issued with this code have been revoked.",
359
+ }), status: 400)
360
+
361
+ client = StripeClient.new
362
+ opts = {api_base: Stripe.connect_base}
363
+ e = assert_raises Stripe::OAuth::InvalidGrantError do
364
+ client.execute_request(:post, '/oauth/token', opts)
365
+ end
366
+
367
+ assert_equal(400, e.http_status)
368
+ assert_equal('invalid_grant', e.code)
369
+ assert_equal('This authorization code has already been used. All tokens issued with this code have been revoked.', e.message)
370
+ end
335
371
  end
336
372
 
337
373
  context "idempotency keys" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stripe
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.8.0
4
+ version: 2.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stripe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-04-28 00:00:00.000000000 Z
11
+ date: 2017-05-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -75,6 +75,7 @@ files:
75
75
  - lib/stripe/invoice_item.rb
76
76
  - lib/stripe/invoice_line_item.rb
77
77
  - lib/stripe/list_object.rb
78
+ - lib/stripe/oauth.rb
78
79
  - lib/stripe/order.rb
79
80
  - lib/stripe/order_return.rb
80
81
  - lib/stripe/payout.rb
@@ -128,6 +129,7 @@ files:
128
129
  - test/stripe/invoice_line_item_test.rb
129
130
  - test/stripe/invoice_test.rb
130
131
  - test/stripe/list_object_test.rb
132
+ - test/stripe/oauth_test.rb
131
133
  - test/stripe/order_return_test.rb
132
134
  - test/stripe/order_test.rb
133
135
  - test/stripe/payout_test.rb
@@ -201,6 +203,7 @@ test_files:
201
203
  - test/stripe/invoice_line_item_test.rb
202
204
  - test/stripe/invoice_test.rb
203
205
  - test/stripe/list_object_test.rb
206
+ - test/stripe/oauth_test.rb
204
207
  - test/stripe/order_return_test.rb
205
208
  - test/stripe/order_test.rb
206
209
  - test/stripe/payout_test.rb