paypal-sdk-rest 0.10.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/README.md +44 -0
- data/lib/generators/paypal/sdk/USAGE +3 -0
- data/lib/generators/paypal/sdk/install_generator.rb +17 -0
- data/lib/generators/paypal/sdk/templates/paypal.rb +2 -0
- data/lib/generators/paypal/sdk/templates/paypal.yml +31 -0
- data/lib/paypal-sdk-core.rb +38 -0
- data/lib/paypal-sdk/core/api.rb +20 -0
- data/lib/paypal-sdk/core/api/base.rb +162 -0
- data/lib/paypal-sdk/core/api/data_types/array_with_block.rb +44 -0
- data/lib/paypal-sdk/core/api/data_types/base.rb +224 -0
- data/lib/paypal-sdk/core/api/data_types/enum.rb +26 -0
- data/lib/paypal-sdk/core/api/data_types/simple_types.rb +52 -0
- data/lib/paypal-sdk/core/api/ipn.rb +66 -0
- data/lib/paypal-sdk/core/api/rest.rb +163 -0
- data/lib/paypal-sdk/core/authentication.rb +66 -0
- data/lib/paypal-sdk/core/config.rb +249 -0
- data/lib/paypal-sdk/core/credential.rb +16 -0
- data/lib/paypal-sdk/core/credential/base.rb +27 -0
- data/lib/paypal-sdk/core/credential/certificate.rb +32 -0
- data/lib/paypal-sdk/core/credential/signature.rb +22 -0
- data/lib/paypal-sdk/core/credential/third_party/subject.rb +25 -0
- data/lib/paypal-sdk/core/credential/third_party/token.rb +39 -0
- data/lib/paypal-sdk/core/exceptions.rb +96 -0
- data/lib/paypal-sdk/core/logging.rb +45 -0
- data/lib/paypal-sdk/core/openid_connect.rb +122 -0
- data/lib/paypal-sdk/core/openid_connect/api.rb +49 -0
- data/lib/paypal-sdk/core/openid_connect/data_types.rb +73 -0
- data/lib/paypal-sdk/core/openid_connect/get_api.rb +28 -0
- data/lib/paypal-sdk/core/openid_connect/request_data_type.rb +52 -0
- data/lib/paypal-sdk/core/openid_connect/set_api.rb +36 -0
- data/lib/paypal-sdk/core/util.rb +11 -0
- data/lib/paypal-sdk/core/util/http_helper.rb +159 -0
- data/lib/paypal-sdk/core/util/oauth_signature.rb +64 -0
- data/lib/paypal-sdk/core/util/ordered_hash.rb +165 -0
- data/lib/paypal-sdk/rest/data_types.rb +1 -0
- data/lib/paypal-sdk/rest/version.rb +1 -1
- data/spec/config/paypal.yml +27 -0
- data/spec/config/sample_data.yml +3 -0
- data/spec/core/api/data_type_spec.rb +189 -0
- data/spec/core/api/rest_spec.rb +147 -0
- data/spec/core/config_spec.rb +192 -0
- data/spec/core/logging_spec.rb +28 -0
- data/spec/core/openid_connect_spec.rb +144 -0
- data/spec/log/http.log +71 -32
- data/spec/log/rest_http.log +133 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/support/sample_data.rb +5 -0
- metadata +82 -5
@@ -0,0 +1,16 @@
|
|
1
|
+
module PayPal
|
2
|
+
module SDK
|
3
|
+
module Core
|
4
|
+
module Credential
|
5
|
+
autoload :Base, "paypal-sdk/core/credential/base"
|
6
|
+
autoload :Certificate, "paypal-sdk/core/credential/certificate"
|
7
|
+
autoload :Signature, "paypal-sdk/core/credential/signature"
|
8
|
+
|
9
|
+
module ThirdParty
|
10
|
+
autoload :Token, "paypal-sdk/core/credential/third_party/token"
|
11
|
+
autoload :Subject, "paypal-sdk/core/credential/third_party/subject"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module PayPal::SDK::Core
|
2
|
+
module Credential
|
3
|
+
|
4
|
+
# Base credential Class for authentication
|
5
|
+
class Base
|
6
|
+
attr_accessor :username, :password, :app_id, :device_ipaddress, :sandbox_email_address
|
7
|
+
|
8
|
+
# Initialize authentication configurations
|
9
|
+
# === Arguments
|
10
|
+
# * <tt>config</tt> -- Configuration object
|
11
|
+
def initialize(config)
|
12
|
+
self.username = config.username
|
13
|
+
self.password = config.password
|
14
|
+
self.app_id = config.app_id
|
15
|
+
self.device_ipaddress = config.device_ipaddress
|
16
|
+
self.sandbox_email_address = config.sandbox_email_address
|
17
|
+
end
|
18
|
+
|
19
|
+
# Return credential properties
|
20
|
+
def properties
|
21
|
+
{ :username => username, :password => password, :app_id => app_id,
|
22
|
+
:device_ipaddress => device_ipaddress, :sandbox_email_address => sandbox_email_address }
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module PayPal::SDK::Core
|
2
|
+
module Credential
|
3
|
+
|
4
|
+
# Certificate class for SSL Certificate authentication
|
5
|
+
class Certificate < Base
|
6
|
+
|
7
|
+
attr_reader :cert_path
|
8
|
+
|
9
|
+
def initialize(config)
|
10
|
+
super
|
11
|
+
@cert_path = config.cert_path
|
12
|
+
end
|
13
|
+
|
14
|
+
# Return SSL certificate
|
15
|
+
def cert
|
16
|
+
@cert ||= OpenSSL::X509::Certificate.new(cert_content)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Return SSL certificate key
|
20
|
+
def key
|
21
|
+
@key = OpenSSL::PKey::RSA.new(cert_content)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
# Return certificate content from the configured file.
|
26
|
+
def cert_content
|
27
|
+
@cert_content ||= File.read(cert_path)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module PayPal::SDK::Core
|
2
|
+
module Credential
|
3
|
+
class Signature < Base
|
4
|
+
|
5
|
+
attr_accessor :signature
|
6
|
+
|
7
|
+
# Initialize configuration
|
8
|
+
# === Argument
|
9
|
+
# * <tt>config</tt> -- Configuration object
|
10
|
+
def initialize(config)
|
11
|
+
super
|
12
|
+
self.signature = config.signature
|
13
|
+
end
|
14
|
+
|
15
|
+
# Return properties for authentication
|
16
|
+
def properties
|
17
|
+
super.merge({ :signature => signature })
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module PayPal::SDK::Core
|
2
|
+
module Credential
|
3
|
+
module ThirdParty
|
4
|
+
class Subject
|
5
|
+
|
6
|
+
attr_accessor :subject, :credential
|
7
|
+
|
8
|
+
# Initialize configuration
|
9
|
+
# === Arguments
|
10
|
+
# * <tt>credential</tt> -- Credential object
|
11
|
+
# * <tt>config</tt> -- Configuration object
|
12
|
+
def initialize(credential, config)
|
13
|
+
@credential = credential
|
14
|
+
@subject = config.subject
|
15
|
+
end
|
16
|
+
|
17
|
+
# Return properties for authentication.
|
18
|
+
def properties
|
19
|
+
credential.properties.merge( :subject => subject )
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module PayPal::SDK::Core
|
2
|
+
module Credential
|
3
|
+
module ThirdParty
|
4
|
+
class Token
|
5
|
+
|
6
|
+
attr_accessor :token, :token_secret, :credential, :url
|
7
|
+
|
8
|
+
# Initialize Token credentials
|
9
|
+
# === Arguments
|
10
|
+
# * <tt>credential</tt> -- Credential Object
|
11
|
+
# * <tt>config</tt> -- Configuration object
|
12
|
+
# * <tt>url</tt> -- Request url
|
13
|
+
def initialize(credential, config, url)
|
14
|
+
@credential = credential
|
15
|
+
@token = config.token
|
16
|
+
@token_secret = config.token_secret
|
17
|
+
@url = url
|
18
|
+
end
|
19
|
+
|
20
|
+
RemoveProperties = [ :username, :password, :signature ]
|
21
|
+
|
22
|
+
# Return credential properties for authentication.
|
23
|
+
def properties
|
24
|
+
credential_properties = credential.properties
|
25
|
+
credential_properties.delete_if{|k,v| RemoveProperties.include? k }
|
26
|
+
credential_properties.merge( :authorization => oauth_authentication )
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
# Return OAuth authentication string.
|
31
|
+
def oauth_authentication
|
32
|
+
Util::OauthSignature.new(credential.username, credential.password, token, token_secret, url).
|
33
|
+
authorization_string
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
module PayPal::SDK::Core
|
5
|
+
module Exceptions
|
6
|
+
class ConnectionError < StandardError # :nodoc:
|
7
|
+
attr_reader :response
|
8
|
+
|
9
|
+
def initialize(response, message = nil)
|
10
|
+
@response = response
|
11
|
+
@message = message
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
begin
|
16
|
+
response_body = JSON.parse(response.body)
|
17
|
+
debug_id = response_body["debug_id"]
|
18
|
+
rescue
|
19
|
+
end
|
20
|
+
message = "Failed."
|
21
|
+
message << " Response code = #{response.code}." if response.respond_to?(:code)
|
22
|
+
message << " Response message = #{response.message}." if response.respond_to?(:message)
|
23
|
+
message << " Response debug ID = #{debug_id}." if debug_id
|
24
|
+
message
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Raised when a Timeout::Error occurs.
|
29
|
+
class TimeoutError < ConnectionError
|
30
|
+
def initialize(message)
|
31
|
+
@message = message
|
32
|
+
end
|
33
|
+
def to_s; @message ;end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Raised when a OpenSSL::SSL::SSLError occurs.
|
37
|
+
class SSLError < ConnectionError
|
38
|
+
def initialize(message)
|
39
|
+
@message = message
|
40
|
+
end
|
41
|
+
def to_s; @message ;end
|
42
|
+
end
|
43
|
+
|
44
|
+
# 3xx Redirection
|
45
|
+
class Redirection < ConnectionError # :nodoc:
|
46
|
+
def to_s
|
47
|
+
response['Location'] ? "#{super} => #{response['Location']}" : super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class MissingParam < ArgumentError # :nodoc:
|
52
|
+
end
|
53
|
+
|
54
|
+
class MissingConfig < StandardError # :nodoc:
|
55
|
+
end
|
56
|
+
|
57
|
+
# 4xx Client Error
|
58
|
+
class ClientError < ConnectionError # :nodoc:
|
59
|
+
end
|
60
|
+
|
61
|
+
# 400 Bad Request
|
62
|
+
class BadRequest < ClientError # :nodoc:
|
63
|
+
end
|
64
|
+
|
65
|
+
# 401 Unauthorized
|
66
|
+
class UnauthorizedAccess < ClientError # :nodoc:
|
67
|
+
end
|
68
|
+
|
69
|
+
# 403 Forbidden
|
70
|
+
class ForbiddenAccess < ClientError # :nodoc:
|
71
|
+
end
|
72
|
+
|
73
|
+
# 404 Not Found
|
74
|
+
class ResourceNotFound < ClientError # :nodoc:
|
75
|
+
end
|
76
|
+
|
77
|
+
# 409 Conflict
|
78
|
+
class ResourceConflict < ClientError # :nodoc:
|
79
|
+
end
|
80
|
+
|
81
|
+
# 410 Gone
|
82
|
+
class ResourceGone < ClientError # :nodoc:
|
83
|
+
end
|
84
|
+
|
85
|
+
# 5xx Server Error
|
86
|
+
class ServerError < ConnectionError # :nodoc:
|
87
|
+
end
|
88
|
+
|
89
|
+
# 405 Method Not Allowed
|
90
|
+
class MethodNotAllowed < ClientError # :nodoc:
|
91
|
+
def allowed_methods
|
92
|
+
@response['Allow'].split(',').map { |verb| verb.strip.downcase.to_sym }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module PayPal::SDK::Core
|
4
|
+
# Include Logging module to provide logger functionality.
|
5
|
+
# == Configure logger
|
6
|
+
# Logging.logger = Logger.new(STDERR)
|
7
|
+
#
|
8
|
+
# == Example
|
9
|
+
# include Logger
|
10
|
+
# logger.info "Debug message"
|
11
|
+
module Logging
|
12
|
+
|
13
|
+
# Get logger object
|
14
|
+
def logger
|
15
|
+
@logger ||= Logging.logger
|
16
|
+
end
|
17
|
+
|
18
|
+
def log_event(message, &block)
|
19
|
+
start_time = Time.now
|
20
|
+
block.call
|
21
|
+
ensure
|
22
|
+
logger.info sprintf("[%.3fs] %s", Time.now - start_time, message)
|
23
|
+
end
|
24
|
+
|
25
|
+
class << self
|
26
|
+
|
27
|
+
# Get or Create configured logger based on the default environment configuration
|
28
|
+
def logger
|
29
|
+
@logger ||= Logger.new(STDERR)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Set logger directly and clear the loggers cache.
|
33
|
+
# === Attributes
|
34
|
+
# * <tt>logger</tt> -- Logger object
|
35
|
+
# === Example
|
36
|
+
# Logging.logger = Logger.new(STDERR)
|
37
|
+
def logger=(logger)
|
38
|
+
@logger = logger
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
@@ -0,0 +1,122 @@
|
|
1
|
+
|
2
|
+
module PayPal::SDK
|
3
|
+
module Core
|
4
|
+
module OpenIDConnect
|
5
|
+
autoload :API, "paypal-sdk/core/openid_connect/api"
|
6
|
+
autoload :SetAPI, "paypal-sdk/core/openid_connect/set_api"
|
7
|
+
autoload :GetAPI, "paypal-sdk/core/openid_connect/get_api"
|
8
|
+
autoload :RequestDataType, "paypal-sdk/core/openid_connect/request_data_type"
|
9
|
+
autoload :DataTypes, "paypal-sdk/core/openid_connect/data_types"
|
10
|
+
|
11
|
+
include DataTypes
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def api
|
15
|
+
RequestDataType.api
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_config(*args)
|
19
|
+
RequestDataType.set_config(*args)
|
20
|
+
end
|
21
|
+
alias_method :config=, :set_config
|
22
|
+
|
23
|
+
AUTHORIZATION_URL = "paypal.com/webapps/auth/protocol/openidconnect/v1/authorize"
|
24
|
+
ENDSESSION_URL = "paypal.com/webapps/auth/protocol/openidconnect/v1/endsession"
|
25
|
+
DEFAULT_SCOPE = "openid"
|
26
|
+
|
27
|
+
def authorize_url(params = {})
|
28
|
+
uri = URI(url_for_mode(AUTHORIZATION_URL))
|
29
|
+
uri.query = api.encode_www_form({
|
30
|
+
:response_type => "code",
|
31
|
+
:scope => DEFAULT_SCOPE,
|
32
|
+
:client_id => RequestDataType.client_id,
|
33
|
+
:redirect_uri => api.config.openid_redirect_uri
|
34
|
+
}.merge(params))
|
35
|
+
uri.to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
def logout_url(params = {})
|
39
|
+
uri = URI(url_for_mode(ENDSESSION_URL))
|
40
|
+
uri.query = api.encode_www_form({
|
41
|
+
:logout => "true",
|
42
|
+
:redirect_uri => api.config.openid_redirect_uri
|
43
|
+
}.merge(params))
|
44
|
+
uri.to_s
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def url_for_mode(url)
|
50
|
+
"https://www.#{"sandbox." if api.api_mode == :sandbox}#{url}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
module DataTypes
|
55
|
+
class Tokeninfo < Base
|
56
|
+
include RequestDataType
|
57
|
+
PATH = "v1/identity/openidconnect/tokenservice"
|
58
|
+
|
59
|
+
class << self
|
60
|
+
def create_from_authorization_code(options, http_header = {})
|
61
|
+
options = { :code => options } if options.is_a? String
|
62
|
+
options = options.merge( :grant_type => "authorization_code" )
|
63
|
+
Tokeninfo.new(api.post(PATH, with_credentials(options), http_header))
|
64
|
+
end
|
65
|
+
alias_method :create, :create_from_authorization_code
|
66
|
+
|
67
|
+
def create_from_refresh_token(options, http_header = {})
|
68
|
+
options = { :refresh_token => options } if options.is_a? String
|
69
|
+
options = options.merge( :grant_type => "refresh_token" )
|
70
|
+
Tokeninfo.new(api.post(PATH, with_credentials(options), http_header))
|
71
|
+
end
|
72
|
+
alias_method :refresh, :create_from_refresh_token
|
73
|
+
|
74
|
+
def with_credentials(options = {})
|
75
|
+
options = options.dup
|
76
|
+
[ :client_id, :client_secret ].each do |key|
|
77
|
+
options[key] = self.send(key) unless options[key] or options[key.to_s]
|
78
|
+
end
|
79
|
+
options
|
80
|
+
end
|
81
|
+
|
82
|
+
def authorize_url(options = {})
|
83
|
+
OpenIDConnect.authorize_url(options)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def refresh(options = {})
|
88
|
+
tokeninfo = self.class.refresh({
|
89
|
+
:refresh_token => self.refresh_token}.merge(options))
|
90
|
+
self.merge!(tokeninfo.to_hash)
|
91
|
+
end
|
92
|
+
|
93
|
+
def userinfo(options = {})
|
94
|
+
Userinfo.get({ :access_token => self.access_token }.merge(options))
|
95
|
+
end
|
96
|
+
|
97
|
+
def logout_url(options = {})
|
98
|
+
OpenIDConnect.logout_url({ :id_token => self.id_token }.merge(options))
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
class Userinfo < Base
|
104
|
+
include RequestDataType
|
105
|
+
PATH = "v1/identity/openidconnect/userinfo"
|
106
|
+
|
107
|
+
class << self
|
108
|
+
def get_userinfo(options = {}, http_header = {})
|
109
|
+
options = { :access_token => options } if options.is_a? String
|
110
|
+
options = options.merge( :schema => "openid" ) unless options[:schema] or options["schema"]
|
111
|
+
Userinfo.new(api.post(PATH, options, http_header))
|
112
|
+
end
|
113
|
+
alias_method :get, :get_userinfo
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Alias for the Core::OpenIDConnect constant
|
121
|
+
OpenIDConnect = Core::OpenIDConnect
|
122
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
|
3
|
+
module PayPal::SDK
|
4
|
+
module Core
|
5
|
+
module OpenIDConnect
|
6
|
+
class API < Core::API::Base
|
7
|
+
|
8
|
+
DEFAULT_OPENID_ENDPOINT = {
|
9
|
+
:sandbox => "https://api.sandbox.paypal.com",
|
10
|
+
:live => "https://api.paypal.com" }
|
11
|
+
|
12
|
+
def initialize(environment = nil, options = {})
|
13
|
+
super("", environment, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def service_endpoint
|
17
|
+
self.config.openid_endpoint || DEFAULT_OPENID_ENDPOINT[self.api_mode] || DEFAULT_OPENID_ENDPOINT[:sandbox]
|
18
|
+
end
|
19
|
+
|
20
|
+
def format_request(payload)
|
21
|
+
payload[:uri].path = url_join(payload[:uri].path, payload[:action])
|
22
|
+
payload[:body] = encode_www_form(payload[:params]) if payload[:params]
|
23
|
+
payload[:header] = {"Content-Type" => "application/x-www-form-urlencoded" }.merge(payload[:header])
|
24
|
+
payload
|
25
|
+
end
|
26
|
+
|
27
|
+
def format_response(payload)
|
28
|
+
payload[:data] =
|
29
|
+
if payload[:response].code >= "200" and payload[:response].code <= "299"
|
30
|
+
MultiJson.load(payload[:response].body)
|
31
|
+
elsif payload[:response].content_type == "application/json"
|
32
|
+
{ "error" => MultiJson.load(payload[:response].body) }
|
33
|
+
else
|
34
|
+
{ "error" => { "name" => payload[:response].code, "message" => payload[:response].message,
|
35
|
+
"developer_msg" => payload[:response] } }
|
36
|
+
end
|
37
|
+
payload
|
38
|
+
end
|
39
|
+
|
40
|
+
class << self
|
41
|
+
def user_agent
|
42
|
+
@user_agent ||= "PayPalSDK/openid-connect-ruby #{VERSION} (#{sdk_library_details})"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|