paypal-sdk-core 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -24,12 +24,16 @@ module PayPal::SDK::Core
24
24
  "X-PAYPAL-REQUEST-DATA-FORMAT" => "JSON",
25
25
  "X-PAYPAL-RESPONSE-DATA-FORMAT" => "JSON"
26
26
  }
27
- DEFAULT_PARAMS = Util::OrderedHash.new.merge!({
27
+ DEFAULT_PARAMS = Util::OrderedHash.new.merge!({
28
28
  "requestEnvelope" => { "errorLanguage" => "en_US" } })
29
+ DEFAULT_END_POINTS = {
30
+ :sandbox => "https://svcs.sandbox.paypal.com/",
31
+ :live => "https://svcs.paypal.com/"
32
+ }
29
33
 
30
- # Get NVP service end point
34
+ # Get service end point
31
35
  def service_endpoint
32
- config.platform_end_point || super || default_end_point(:platform)
36
+ config.platform_endpoint || config.endpoint || DEFAULT_END_POINTS[api_mode]
33
37
  end
34
38
 
35
39
  # Format the Request.
@@ -39,13 +43,14 @@ module PayPal::SDK::Core
39
43
  # === Return
40
44
  # * <tt>request_path</tt> -- Generated URL for requested action
41
45
  # * <tt>request_content</tt> -- Format parameters in JSON with default values.
42
- def format_request(action, params)
43
- uri = @uri.dup
44
- uri.path = @uri.path.sub(/\/?$/, "/#{action}")
45
- credential_properties = credential(uri.to_s).properties
46
+ def format_request(payload)
47
+ payload[:uri].path = url_join(payload[:uri].path, payload[:action])
48
+ credential_properties = credential(payload[:uri].to_s).properties
46
49
  header = map_header_value(NVP_AUTH_HEADER, credential_properties).
47
50
  merge(DEFAULT_NVP_HTTP_HEADER)
48
- [ uri, MultiJson.dump(DEFAULT_PARAMS.merge(params)), header ]
51
+ payload[:header] = header.merge(payload[:header])
52
+ payload[:body] = MultiJson.dump(DEFAULT_PARAMS.merge(payload[:params]))
53
+ payload
49
54
  end
50
55
 
51
56
  # Format the Response object
@@ -54,12 +59,14 @@ module PayPal::SDK::Core
54
59
  # * <tt>response</tt> -- HTTP response object
55
60
  # === Return
56
61
  # Parse response content using JSON and return the Hash object
57
- def format_response(action, response)
58
- if response.code == "200"
59
- MultiJson.load(response.body)
60
- else
61
- format_error(response, response.message)
62
- end
62
+ def format_response(payload)
63
+ payload[:data] =
64
+ if payload[:response].code == "200"
65
+ MultiJson.load(payload[:response].body)
66
+ else
67
+ format_error(payload[:response], payload[:response].message)
68
+ end
69
+ payload
63
70
  end
64
71
 
65
72
  # Format Error object.
@@ -0,0 +1,139 @@
1
+ require 'multi_json'
2
+
3
+ module PayPal::SDK::Core
4
+ module API
5
+ class REST < Base
6
+ NVP_AUTH_HEADER = {
7
+ :sandbox_email_address => "X-PAYPAL-SANDBOX-EMAIL-ADDRESS",
8
+ :device_ipaddress => "X-PAYPAL-DEVICE-IPADDRESS"
9
+ }
10
+ DEFAULT_HTTP_HEADER = {
11
+ "Content-Type" => "application/json"
12
+ }
13
+
14
+ DEFAULT_REST_END_POINTS = {
15
+ :sandbox => "https://api.sandbox.paypal.com",
16
+ :live => "https://api.paypal.com"
17
+ }
18
+ TOKEN_REQUEST_PARAMS = "grant_type=client_credentials"
19
+
20
+ # Get REST service end point
21
+ def service_endpoint
22
+ config.rest_endpoint || super || DEFAULT_REST_END_POINTS[api_mode]
23
+ end
24
+
25
+ # Token endpoint
26
+ def token_endpoint
27
+ config.rest_token_endpoint || service_endpoint
28
+ end
29
+
30
+ # Clear cached values.
31
+ def set_config(*args)
32
+ @token_uri = nil
33
+ @token = nil
34
+ super
35
+ end
36
+
37
+ # URI object token endpoint
38
+ def token_uri
39
+ @token_uri ||=
40
+ begin
41
+ new_uri = URI.parse(token_endpoint)
42
+ new_uri.path = "/v1/oauth2/token" if new_uri.path =~ /^\/?$/
43
+ new_uri
44
+ end
45
+ end
46
+
47
+ # Generate Oauth token or Get cached
48
+ def token
49
+ @token ||=
50
+ begin
51
+ basic_auth = ["#{config.client_id}:#{config.client_secret}"].pack('m').delete("\r\n")
52
+ token_headers = default_http_header.merge( "Authorization" => "Basic #{basic_auth}" )
53
+ response = http_call( :method => :post, :uri => token_uri, :body => TOKEN_REQUEST_PARAMS, :header => token_headers )
54
+ MultiJson.load(response.body)["access_token"]
55
+ end
56
+ end
57
+
58
+ # token setter
59
+ def token=(new_token)
60
+ @token = new_token
61
+ end
62
+
63
+ # Override the API call to handle Token Expire
64
+ def api_call(payload)
65
+ backup_payload = payload.dup
66
+ begin
67
+ response = super(payload)
68
+ rescue UnauthorizedAccess => error
69
+ if @token and config.client_id
70
+ # Reset cached token and Retry api request
71
+ @token = nil
72
+ response = super(backup_payload)
73
+ else
74
+ raise error
75
+ end
76
+ end
77
+ response
78
+ end
79
+
80
+ # Validate HTTP response
81
+ def handle_response(response)
82
+ super
83
+ rescue BadRequest => error
84
+ # Catch BadRequest to get validation error message from the response.
85
+ error.response
86
+ end
87
+
88
+ # Format request payload
89
+ # === Argument
90
+ # * payload( uri, action, params, header)
91
+ # === Generate
92
+ # * payload( uri, body, header )
93
+ def format_request(payload)
94
+ # Request URI
95
+ payload[:uri].path = url_join(payload[:uri].path, payload[:action])
96
+ # HTTP Header
97
+ credential_properties = credential(payload[:uri].to_s).properties
98
+ header = map_header_value(NVP_AUTH_HEADER, credential_properties)
99
+ payload[:header] = header.merge("Authorization" => "Bearer #{token}").
100
+ merge(DEFAULT_HTTP_HEADER).merge(payload[:header])
101
+ # Post Data
102
+ payload[:body] = MultiJson.dump(payload[:params])
103
+ payload
104
+ end
105
+
106
+ # Format response payload
107
+ # === Argument
108
+ # * payload( response )
109
+ # === Generate
110
+ # * payload( data )
111
+ def format_response(payload)
112
+ payload[:data] =
113
+ if payload[:response].code >= "200" and payload[:response].code <= "299"
114
+ MultiJson.load(payload[:response].body)
115
+ elsif payload[:response].content_type == "application/json"
116
+ { "error" => MultiJson.load(payload[:response].body) }
117
+ else
118
+ { "error" => { "name" => payload[:response].code, "message" => payload[:response].message,
119
+ "developer_msg" => payload[:response] } }
120
+ end
121
+ payload
122
+ end
123
+
124
+ # Formate Exception object
125
+ def format_error(exception, message)
126
+ { "error" => { "name" => "ERROR", "message" => message, "developer_msg" => exception } }
127
+ end
128
+
129
+ # Log PayPal-Request-Id header
130
+ def log_http_call(payload)
131
+ if payload[:header] and payload[:header]["PayPal-Request-Id"]
132
+ logger.info "PayPal-Request-Id: #{payload[:header]["PayPal-Request-Id"]}"
133
+ end
134
+ super
135
+ end
136
+
137
+ end
138
+ end
139
+ end
@@ -30,6 +30,11 @@ module PayPal::SDK::Core
30
30
  end
31
31
  end
32
32
 
33
+ # Get base credential type
34
+ def base_credential_type
35
+ config.cert_path ? :certificate : :three_token
36
+ end
37
+
33
38
  # Get third party credential
34
39
  def third_party_credential(url)
35
40
  if config.token and config.token_secret
@@ -41,26 +46,8 @@ module PayPal::SDK::Core
41
46
 
42
47
  # Clear cached variables on changing the configuration.
43
48
  def set_config(*args)
44
- super
45
49
  @base_credential = nil
46
- end
47
-
48
- # Generate header based on given header keys and properties
49
- # === Arguments
50
- # * <tt>header_keys</tt> -- List of Header keys for the properties
51
- # * <tt>properties</tt> -- properties
52
- # === Return
53
- # Hash with header as key property as value
54
- # === Example
55
- # map_header_value( { :username => "X-PAYPAL-USERNAME"}, { :username => "guest" })
56
- # # Return: { "X-PAYPAL-USERNAME" => "guest" }
57
- def map_header_value(header_keys, properties)
58
- header = {}
59
- properties.each do |key, value|
60
- key = header_keys[key]
61
- header[key] = value if key
62
- end
63
- header
50
+ super
64
51
  end
65
52
 
66
53
  # Configure ssl certificate to HTTP object
@@ -25,8 +25,25 @@ module PayPal::SDK::Core
25
25
  # === Arguments
26
26
  # * <tt>env</tt> -- Environment
27
27
  # * <tt>override_configurations</tt> (Optional) -- To override the default configuration.
28
+ # === Examples
29
+ # obj.set_config(api.config)
30
+ # obj.set_config(:http_timeout => 30)
31
+ # obj.set_config(:development)
32
+ # obj.set_config(:development, :http_timeout => 30)
28
33
  def set_config(env, override_configurations = {})
29
- @config = env.is_a?(Config) ? env : Config.config(env, override_configurations)
34
+ @config =
35
+ case env
36
+ when Config
37
+ env
38
+ when Hash
39
+ begin
40
+ config.dup.merge!(env)
41
+ rescue Errno::ENOENT => error
42
+ Config.new(env)
43
+ end
44
+ else
45
+ Config.config(env, override_configurations)
46
+ end
30
47
  end
31
48
 
32
49
  alias_method :config=, :set_config
@@ -45,15 +62,31 @@ module PayPal::SDK::Core
45
62
  # # Read configuration attributes
46
63
  # config = Config.config
47
64
  # config.username
48
- # config.end_point
65
+ # config.endpoint
49
66
  class Config
67
+
68
+ include Logging
69
+ include Exceptions
70
+
50
71
  attr_accessor :username, :password, :signature, :app_id, :cert_path,
51
72
  :token, :token_secret, :subject,
52
- :http_timeout, :http_retry, :http_proxy, :ca_file,
73
+ :http_timeout, :http_proxy,
53
74
  :device_ipaddress, :sandbox_email_address,
54
- :mode, :end_point, :merchant_end_point, :platform_end_point, :ipn_end_point,
55
- :redirect_url, :dev_central_url,
56
- :logfile
75
+ :mode, :endpoint, :merchant_endpoint, :platform_endpoint, :ipn_endpoint,
76
+ :rest_endpoint, :rest_token_endpoint, :client_id, :client_secret
77
+
78
+ alias_method :end_point=, :endpoint=
79
+ alias_method :end_point, :endpoint
80
+ alias_method :platform_end_point=, :platform_endpoint=
81
+ alias_method :platform_end_point, :platform_endpoint
82
+ alias_method :merchant_end_point=, :merchant_endpoint=
83
+ alias_method :merchant_end_point, :merchant_endpoint
84
+ alias_method :ipn_end_point=, :ipn_endpoint=
85
+ alias_method :ipn_end_point, :ipn_endpoint
86
+ alias_method :rest_end_point, :rest_endpoint
87
+ alias_method :rest_end_point=, :rest_endpoint=
88
+ alias_method :rest_token_end_point, :rest_token_endpoint
89
+ alias_method :rest_token_end_point=, :rest_token_endpoint=
57
90
 
58
91
  # Create Config object
59
92
  # === Options(Hash)
@@ -66,6 +99,37 @@ module PayPal::SDK::Core
66
99
  merge!(options)
67
100
  end
68
101
 
102
+ def logfile=(filename)
103
+ logger.warn '`logfile=` is deprecated, Please use `PayPal::SDK::Core::Config.logger = Logger.new(STDERR)`'
104
+ end
105
+
106
+ def redirect_url=(redirect_url)
107
+ logger.warn '`redirect_url=` is deprecated.'
108
+ end
109
+
110
+ def dev_central_url=(dev_central_url)
111
+ logger.warn '`dev_central_url=` is deprecated.'
112
+ end
113
+
114
+ def ssl_options
115
+ @ssl_options ||= {}.freeze
116
+ end
117
+
118
+ def ssl_options=(options)
119
+ options = Hash[options.map{|key, value| [key.to_sym, value] }]
120
+ @ssl_options = ssl_options.merge(options).freeze
121
+ end
122
+
123
+ def ca_file=(ca_file)
124
+ logger.warn '`ca_file=` is deprecated, Please configure `ca_file=` under `ssl_options`'
125
+ self.ssl_options = { :ca_file => ca_file }
126
+ end
127
+
128
+ def http_verify_mode=(verify_mode)
129
+ logger.warn '`http_verify_mode=` is deprecated, Please configure `verify_mode=` under `ssl_options`'
130
+ self.ssl_options = { :verify_mode => verify_mode }
131
+ end
132
+
69
133
  # Override configurations
70
134
  def merge!(options)
71
135
  options.each do |key, value|
@@ -74,6 +138,12 @@ module PayPal::SDK::Core
74
138
  self
75
139
  end
76
140
 
141
+ # Validate required configuration
142
+ def required!(*names)
143
+ names = names.select{|name| send(name).nil? }
144
+ raise MissingConfig.new("Required configuration(#{names.join(", ")})") if names.any?
145
+ end
146
+
77
147
  class << self
78
148
 
79
149
  @@config_cache = {}
@@ -141,7 +211,7 @@ module PayPal::SDK::Core
141
211
  # Set configuration
142
212
  def configurations=(configs)
143
213
  @@config_cache = {}
144
- @@configurations = Hash[configs.map{|k,v| [k.to_s, v] }]
214
+ @@configurations = configs && Hash[configs.map{|k,v| [k.to_s, v] }]
145
215
  end
146
216
 
147
217
  private
@@ -0,0 +1,87 @@
1
+ module PayPal::SDK::Core
2
+ module Exceptions
3
+ class ConnectionError < StandardError # :nodoc:
4
+ attr_reader :response
5
+
6
+ def initialize(response, message = nil)
7
+ @response = response
8
+ @message = message
9
+ end
10
+
11
+ def to_s
12
+ message = "Failed."
13
+ message << " Response code = #{response.code}." if response.respond_to?(:code)
14
+ message << " Response message = #{response.message}." if response.respond_to?(:message)
15
+ message
16
+ end
17
+ end
18
+
19
+ # Raised when a Timeout::Error occurs.
20
+ class TimeoutError < ConnectionError
21
+ def initialize(message)
22
+ @message = message
23
+ end
24
+ def to_s; @message ;end
25
+ end
26
+
27
+ # Raised when a OpenSSL::SSL::SSLError occurs.
28
+ class SSLError < ConnectionError
29
+ def initialize(message)
30
+ @message = message
31
+ end
32
+ def to_s; @message ;end
33
+ end
34
+
35
+ # 3xx Redirection
36
+ class Redirection < ConnectionError # :nodoc:
37
+ def to_s
38
+ response['Location'] ? "#{super} => #{response['Location']}" : super
39
+ end
40
+ end
41
+
42
+ class MissingParam < ArgumentError # :nodoc:
43
+ end
44
+
45
+ class MissingConfig < StandardError # :nodoc:
46
+ end
47
+
48
+ # 4xx Client Error
49
+ class ClientError < ConnectionError # :nodoc:
50
+ end
51
+
52
+ # 400 Bad Request
53
+ class BadRequest < ClientError # :nodoc:
54
+ end
55
+
56
+ # 401 Unauthorized
57
+ class UnauthorizedAccess < ClientError # :nodoc:
58
+ end
59
+
60
+ # 403 Forbidden
61
+ class ForbiddenAccess < ClientError # :nodoc:
62
+ end
63
+
64
+ # 404 Not Found
65
+ class ResourceNotFound < ClientError # :nodoc:
66
+ end
67
+
68
+ # 409 Conflict
69
+ class ResourceConflict < ClientError # :nodoc:
70
+ end
71
+
72
+ # 410 Gone
73
+ class ResourceGone < ClientError # :nodoc:
74
+ end
75
+
76
+ # 5xx Server Error
77
+ class ServerError < ConnectionError # :nodoc:
78
+ end
79
+
80
+ # 405 Method Not Allowed
81
+ class MethodNotAllowed < ClientError # :nodoc:
82
+ def allowed_methods
83
+ @response['Allow'].split(',').map { |verb| verb.strip.downcase.to_sym }
84
+ end
85
+ end
86
+ end
87
+ end