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 +4 -4
- data/History.txt +4 -0
- data/VERSION +1 -1
- data/lib/stripe.rb +4 -1
- data/lib/stripe/account.rb +6 -5
- data/lib/stripe/errors.rb +40 -0
- data/lib/stripe/oauth.rb +56 -0
- data/lib/stripe/stripe_client.rb +44 -12
- data/lib/stripe/version.rb +1 -1
- data/test/stripe/oauth_test.rb +85 -0
- data/test/stripe/stripe_client_test.rb +39 -3
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f9ea5d1ce3e948a439878fadba81ef52b47c24e
|
4
|
+
data.tar.gz: 00d62fe82daf1570ada3004672ddd15b6fe2425b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 227094e5bb975bbd0acaf044d5f3708055fb8c12f043af80dd4851a8a784646f2f506d45c4a87ea166cbae285085623eb7aebb1366a1f5dec035a95437fede56
|
7
|
+
data.tar.gz: 3158ef959bc7d5ed39bc26b9482515b3c8aec2fdf0d96d6f6a87c2481d254517688e994d6b6ea8fd7006a70d809f42af8ab76eb0caa621e6ec1f69bf47a85c9b
|
data/History.txt
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.9.0
|
data/lib/stripe.rb
CHANGED
@@ -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
|
data/lib/stripe/account.rb
CHANGED
@@ -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
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
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
|
data/lib/stripe/errors.rb
CHANGED
@@ -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
|
data/lib/stripe/oauth.rb
ADDED
@@ -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
|
data/lib/stripe/stripe_client.rb
CHANGED
@@ -197,7 +197,7 @@ module Stripe
|
|
197
197
|
case e
|
198
198
|
when Faraday::ClientError
|
199
199
|
if e.response
|
200
|
-
|
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
|
232
|
+
def handle_error_response(http_resp)
|
233
233
|
begin
|
234
234
|
resp = StripeResponse.from_faraday_hash(http_resp)
|
235
|
-
|
235
|
+
error_data = resp.data[:error]
|
236
236
|
|
237
|
-
unless
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
285
|
-
|
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)
|
data/lib/stripe/version.rb
CHANGED
@@ -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
|
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({
|
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: "{\"
|
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.
|
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-
|
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
|