paypal-sdk-core 0.1.5 → 0.2.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.
@@ -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