mirror of
https://github.com/stripe/stripe-ruby.git
synced 2025-08-10 00:01:09 -04:00
Split out independent parts of Stripe.request and cleanup code
Make the SSL checks, user agent assembly, and request header generation separate methods. Also switch to class instance variables instead of class variables and accessors instead of custom functions, and wrap lines to 80 characters. Original patches come with thanks to Stevie Graham (with some style fixups from me). (fixes #53, fixes #54)
This commit is contained in:
parent
1b9f776f65
commit
fe3eb47e60
274
lib/stripe.rb
274
lib/stripe.rb
@ -45,188 +45,178 @@ require 'stripe/errors/invalid_request_error'
|
||||
require 'stripe/errors/authentication_error'
|
||||
|
||||
module Stripe
|
||||
@@ssl_bundle_path = File.join(File.dirname(__FILE__), 'data/ca-certificates.crt')
|
||||
@@api_key = nil
|
||||
@@api_base = 'https://api.stripe.com'
|
||||
@@verify_ssl_certs = true
|
||||
@@api_version = nil
|
||||
@api_base = 'https://api.stripe.com'
|
||||
|
||||
@ssl_bundle_path = File.dirname(__FILE__) + '/data/ca-certificates.crt'
|
||||
@verify_ssl_certs = true
|
||||
|
||||
class << self
|
||||
attr_accessor :api_key, :api_base, :verify_ssl_certs, :api_version
|
||||
end
|
||||
|
||||
def self.api_url(url='')
|
||||
@@api_base + url
|
||||
end
|
||||
|
||||
def self.api_key=(api_key)
|
||||
@@api_key = api_key
|
||||
end
|
||||
|
||||
def self.api_key
|
||||
@@api_key
|
||||
end
|
||||
|
||||
def self.api_base=(api_base)
|
||||
@@api_base = api_base
|
||||
end
|
||||
|
||||
def self.api_base
|
||||
@@api_base
|
||||
end
|
||||
|
||||
def self.verify_ssl_certs=(verify)
|
||||
@@verify_ssl_certs = verify
|
||||
end
|
||||
|
||||
def self.verify_ssl_certs
|
||||
@@verify_ssl_certs
|
||||
end
|
||||
|
||||
def self.api_version=(version)
|
||||
@@api_version = version
|
||||
end
|
||||
|
||||
def self.api_version
|
||||
@@api_version
|
||||
@api_base + url
|
||||
end
|
||||
|
||||
def self.request(method, url, api_key, params={}, headers={})
|
||||
api_key ||= @@api_key
|
||||
raise AuthenticationError.new('No API key provided. (HINT: set your API key using "Stripe.api_key = <API-KEY>". You can generate API keys from the Stripe web interface. See https://stripe.com/api for details, or email support@stripe.com if you have any questions.)') unless api_key
|
||||
|
||||
if !verify_ssl_certs
|
||||
unless @no_verify
|
||||
$stderr.puts "WARNING: Running without SSL cert verification. Execute 'Stripe.verify_ssl_certs = true' to enable verification."
|
||||
@no_verify = true
|
||||
end
|
||||
ssl_opts = { :verify_ssl => false }
|
||||
elsif !Util.file_readable(@@ssl_bundle_path)
|
||||
unless @no_bundle
|
||||
$stderr.puts "WARNING: Running without SSL cert verification because #{@@ssl_bundle_path} isn't readable"
|
||||
@no_bundle = true
|
||||
end
|
||||
ssl_opts = { :verify_ssl => false }
|
||||
else
|
||||
ssl_opts = {
|
||||
:verify_ssl => OpenSSL::SSL::VERIFY_PEER,
|
||||
:ssl_ca_file => @@ssl_bundle_path
|
||||
}
|
||||
unless api_key ||= @api_key
|
||||
raise AuthenticationError.new('No API key provided.' +
|
||||
'Set your API key using "Stripe.api_key = <API-KEY>". ' +
|
||||
'You can generate API keys from the Stripe web interface. ' +
|
||||
'See https://stripe.com/api for details, or email support@stripe.com ' +
|
||||
'if you have any questions.')
|
||||
end
|
||||
|
||||
request_opts = { :verify_ssl => false }
|
||||
|
||||
if ssl_preflight_passed?
|
||||
request_opts.update(:verify_ssl => OpenSSL::SSL::VERIFY_PEER,
|
||||
:ssl_ca_file => @ssl_bundle_path)
|
||||
end
|
||||
uname = (@@uname ||= RUBY_PLATFORM =~ /linux|darwin/i ? `uname -a 2>/dev/null`.strip : nil)
|
||||
lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})"
|
||||
ua = {
|
||||
:bindings_version => Stripe::VERSION,
|
||||
:lang => 'ruby',
|
||||
:lang_version => lang_version,
|
||||
:platform => RUBY_PLATFORM,
|
||||
:publisher => 'stripe',
|
||||
:uname => uname
|
||||
}
|
||||
|
||||
params = Util.objects_to_ids(params)
|
||||
url = self.api_url(url)
|
||||
url = api_url(url)
|
||||
|
||||
case method.to_s.downcase.to_sym
|
||||
when :get, :head, :delete
|
||||
# Make params into GET parameters
|
||||
if params && params.count > 0
|
||||
query_string = Util.flatten_params(params).collect{|key, value| "#{key}=#{Util.url_encode(value)}"}.join('&')
|
||||
url += "?#{query_string}"
|
||||
end
|
||||
url += "?#{uri_encode(params)}" if params && params.any?
|
||||
payload = nil
|
||||
else
|
||||
payload = Util.flatten_params(params).collect{|(key, value)| "#{key}=#{Util.url_encode(value)}"}.join('&')
|
||||
payload = uri_encode(params)
|
||||
end
|
||||
|
||||
request_opts.update(:headers => request_headers.update(headers),
|
||||
:method => method, :open_timeout => 30,
|
||||
:payload => payload, :url => url, :timeout => 80)
|
||||
|
||||
begin
|
||||
headers = { :x_stripe_client_user_agent => Stripe::JSON.dump(ua) }.merge(headers)
|
||||
rescue => e
|
||||
headers = {
|
||||
:x_stripe_client_raw_user_agent => ua.inspect,
|
||||
:error => "#{e} (#{e.class})"
|
||||
}.merge(headers)
|
||||
end
|
||||
|
||||
headers = {
|
||||
:user_agent => "Stripe/v1 RubyBindings/#{Stripe::VERSION}",
|
||||
:authorization => "Bearer #{api_key}",
|
||||
:content_type => 'application/x-www-form-urlencoded'
|
||||
}.merge(headers)
|
||||
|
||||
if self.api_version
|
||||
headers[:stripe_version] = self.api_version
|
||||
end
|
||||
|
||||
opts = {
|
||||
:method => method,
|
||||
:url => url,
|
||||
:headers => headers,
|
||||
:open_timeout => 30,
|
||||
:payload => payload,
|
||||
:timeout => 80
|
||||
}.merge(ssl_opts)
|
||||
|
||||
begin
|
||||
response = execute_request(opts)
|
||||
response = execute_request(request_opts)
|
||||
rescue SocketError => e
|
||||
self.handle_restclient_error(e)
|
||||
handle_restclient_error(e)
|
||||
rescue NoMethodError => e
|
||||
# Work around RestClient bug
|
||||
if e.message =~ /\WRequestFailed\W/
|
||||
e = APIConnectionError.new('Unexpected HTTP response code')
|
||||
self.handle_restclient_error(e)
|
||||
handle_restclient_error(e)
|
||||
else
|
||||
raise
|
||||
end
|
||||
rescue RestClient::ExceptionWithResponse => e
|
||||
if rcode = e.http_code and rbody = e.http_body
|
||||
self.handle_api_error(rcode, rbody)
|
||||
handle_api_error(rcode, rbody)
|
||||
else
|
||||
self.handle_restclient_error(e)
|
||||
handle_restclient_error(e)
|
||||
end
|
||||
rescue RestClient::Exception, Errno::ECONNREFUSED => e
|
||||
self.handle_restclient_error(e)
|
||||
handle_restclient_error(e)
|
||||
end
|
||||
|
||||
rbody = response.body
|
||||
rcode = response.code
|
||||
begin
|
||||
# Would use :symbolize_names => true, but apparently there is
|
||||
# some library out there that makes symbolize_names not work.
|
||||
resp = Stripe::JSON.load(rbody)
|
||||
rescue MultiJson::DecodeError
|
||||
raise APIError.new("Invalid response object from API: #{rbody.inspect} (HTTP response code was #{rcode})", rcode, rbody)
|
||||
end
|
||||
|
||||
resp = Util.symbolize_names(resp)
|
||||
[resp, api_key]
|
||||
parse(response)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.ssl_preflight_passed?
|
||||
if !verify_ssl_certs && !@no_verify
|
||||
$stderr.puts "WARNING: Running without SSL cert verification. " +
|
||||
"Execute 'Stripe.verify_ssl_certs = true' to enable verification."
|
||||
|
||||
@no_verify = true
|
||||
|
||||
elsif !Util.file_readable(@ssl_bundle_path) && !@no_bundle
|
||||
$stderr.puts "WARNING: Running without SSL cert verification " +
|
||||
"because #{@ssl_bundle_path} isn't readable"
|
||||
|
||||
@no_bundle = true
|
||||
end
|
||||
|
||||
!(@no_verify || @no_nobundle)
|
||||
end
|
||||
|
||||
def self.user_agent
|
||||
@uname ||= `uname -a 2>/dev/null`.strip if RUBY_PLATFORM =~ /linux|darwin/i
|
||||
lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})"
|
||||
|
||||
{
|
||||
:bindings_version => Stripe::VERSION,
|
||||
:lang => 'ruby',
|
||||
:lang_version => lang_version,
|
||||
:platform => RUBY_PLATFORM,
|
||||
:publisher => 'stripe',
|
||||
:uname => @uname
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
def self.uri_encode(params)
|
||||
Util.flatten_params(params).
|
||||
map { |k,v| "#{k}=#{Util.url_encode(v)}" }.join('&')
|
||||
end
|
||||
|
||||
def self.request_headers
|
||||
headers = {
|
||||
:user_agent => "Stripe/v1 RubyBindings/#{Stripe::VERSION}",
|
||||
:authorization => "Bearer #{api_key}",
|
||||
:content_type => 'application/x-www-form-urlencoded'
|
||||
}
|
||||
|
||||
headers[:stripe_version] = api_version if api_version
|
||||
|
||||
begin
|
||||
headers.update(:x_stripe_client_user_agent => Stripe::JSON.dump(user_agent))
|
||||
rescue => e
|
||||
headers.update(:x_stripe_client_raw_user_agent => user_agent.inspect,
|
||||
:error => "#{e} (#{e.class})")
|
||||
end
|
||||
end
|
||||
|
||||
def self.execute_request(opts)
|
||||
RestClient::Request.execute(opts)
|
||||
end
|
||||
|
||||
def self.parse(response)
|
||||
begin
|
||||
# Would use :symbolize_names => true, but apparently there is
|
||||
# some library out there that makes symbolize_names not work.
|
||||
response = Stripe::JSON.load(response.body)
|
||||
rescue MultiJson::DecodeError
|
||||
raise general_api_error(response.code, response.body)
|
||||
end
|
||||
|
||||
[Util.symbolize_names(response), api_key]
|
||||
end
|
||||
|
||||
def self.general_api_error(rcode, rbody)
|
||||
APIError.new("Invalid response object from API: #{rbody.inspect} " +
|
||||
"(HTTP response code was #{rcode})", rcode, rbody)
|
||||
end
|
||||
|
||||
def self.handle_api_error(rcode, rbody)
|
||||
begin
|
||||
error_obj = Stripe::JSON.load(rbody)
|
||||
error_obj = Util.symbolize_names(error_obj)
|
||||
error = error_obj[:error] or raise StripeError.new # escape from parsing
|
||||
|
||||
rescue MultiJson::DecodeError, StripeError
|
||||
raise APIError.new("Invalid response object from API: #{rbody.inspect} (HTTP response code was #{rcode})", rcode, rbody)
|
||||
raise general_api_error(rcode, rbody)
|
||||
end
|
||||
|
||||
case rcode
|
||||
when 400, 404 then
|
||||
raise invalid_request_error(error, rcode, rbody, error_obj)
|
||||
when 400, 404
|
||||
raise invalid_request_error error, rcode, rbody, error_obj
|
||||
when 401
|
||||
raise authentication_error(error, rcode, rbody, error_obj)
|
||||
raise authentication_error error, rcode, rbody, error_obj
|
||||
when 402
|
||||
raise card_error(error, rcode, rbody, error_obj)
|
||||
raise card_error error, rcode, rbody, error_obj
|
||||
else
|
||||
raise api_error(error, rcode, rbody, error_obj)
|
||||
raise api_error error, rcode, rbody, error_obj
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def self.invalid_request_error(error, rcode, rbody, error_obj)
|
||||
InvalidRequestError.new(error[:message], error[:param], rcode, rbody, error_obj)
|
||||
InvalidRequestError.new(error[:message], error[:param], rcode,
|
||||
rbody, error_obj)
|
||||
end
|
||||
|
||||
def self.authentication_error(error, rcode, rbody, error_obj)
|
||||
@ -234,7 +224,8 @@ module Stripe
|
||||
end
|
||||
|
||||
def self.card_error(error, rcode, rbody, error_obj)
|
||||
CardError.new(error[:message], error[:param], error[:code], rcode, rbody, error_obj)
|
||||
CardError.new(error[:message], error[:param], error[:code],
|
||||
rcode, rbody, error_obj)
|
||||
end
|
||||
|
||||
def self.api_error(error, rcode, rbody, error_obj)
|
||||
@ -244,15 +235,28 @@ module Stripe
|
||||
def self.handle_restclient_error(e)
|
||||
case e
|
||||
when RestClient::ServerBrokeConnection, RestClient::RequestTimeout
|
||||
message = "Could not connect to Stripe (#{@@api_base}). Please check your internet connection and try again. If this problem persists, you should check Stripe's service status at https://twitter.com/stripestatus, or let us know at support@stripe.com."
|
||||
message = "Could not connect to Stripe (#{@api_base}). " +
|
||||
"Please check your internet connection and try again. " +
|
||||
"If this problem persists, you should check Stripe's service status at " +
|
||||
"https://twitter.com/stripestatus, or let us know at support@stripe.com."
|
||||
|
||||
when RestClient::SSLCertificateNotVerified
|
||||
message = "Could not verify Stripe's SSL certificate. Please make sure that your network is not intercepting certificates. (Try going to https://api.stripe.com/v1 in your browser.) If this problem persists, let us know at support@stripe.com."
|
||||
message = "Could not verify Stripe's SSL certificate. " +
|
||||
"Please make sure that your network is not intercepting certificates. " +
|
||||
"(Try going to https://api.stripe.com/v1 in your browser.) " +
|
||||
"If this problem persists, let us know at support@stripe.com."
|
||||
|
||||
when SocketError
|
||||
message = "Unexpected error communicating when trying to connect to Stripe. HINT: You may be seeing this message because your DNS is not working. To check, try running 'host stripe.com' from the command line."
|
||||
message = "Unexpected error communicating when trying to connect to Stripe. " +
|
||||
"You may be seeing this message because your DNS is not working. " +
|
||||
"To check, try running 'host stripe.com' from the command line."
|
||||
|
||||
else
|
||||
message = "Unexpected error communicating with Stripe. If this problem persists, let us know at support@stripe.com."
|
||||
message = "Unexpected error communicating with Stripe. " +
|
||||
"If this problem persists, let us know at support@stripe.com."
|
||||
|
||||
end
|
||||
message += "\n\n(Network error: #{e.message})"
|
||||
raise APIConnectionError.new(message)
|
||||
|
||||
raise APIConnectionError.new(message + "\n\n(Network error: #{e.message})")
|
||||
end
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user