paypal-sdk-rest-pmrb 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +15 -0
  3. data/README.md +265 -0
  4. data/Rakefile +15 -0
  5. data/data/DigiCertHighAssuranceEVRootCA.pem +23 -0
  6. data/data/DigiCertSHA2ExtendedValidationServerCA.pem +28 -0
  7. data/data/paypal.crt +193 -0
  8. data/lib/generators/paypal/sdk/USAGE +3 -0
  9. data/lib/generators/paypal/sdk/install_generator.rb +17 -0
  10. data/lib/generators/paypal/sdk/templates/paypal.rb +2 -0
  11. data/lib/generators/paypal/sdk/templates/paypal.yml +29 -0
  12. data/lib/paypal-sdk-core.rb +38 -0
  13. data/lib/paypal-sdk-rest.rb +2 -0
  14. data/lib/paypal-sdk/core/api.rb +20 -0
  15. data/lib/paypal-sdk/core/api/base.rb +169 -0
  16. data/lib/paypal-sdk/core/api/data_types/array_with_block.rb +44 -0
  17. data/lib/paypal-sdk/core/api/data_types/base.rb +225 -0
  18. data/lib/paypal-sdk/core/api/data_types/enum.rb +26 -0
  19. data/lib/paypal-sdk/core/api/data_types/simple_types.rb +52 -0
  20. data/lib/paypal-sdk/core/api/ipn.rb +66 -0
  21. data/lib/paypal-sdk/core/api/rest.rb +177 -0
  22. data/lib/paypal-sdk/core/authentication.rb +66 -0
  23. data/lib/paypal-sdk/core/config.rb +253 -0
  24. data/lib/paypal-sdk/core/credential.rb +16 -0
  25. data/lib/paypal-sdk/core/credential/base.rb +27 -0
  26. data/lib/paypal-sdk/core/credential/certificate.rb +32 -0
  27. data/lib/paypal-sdk/core/credential/signature.rb +22 -0
  28. data/lib/paypal-sdk/core/credential/third_party/subject.rb +25 -0
  29. data/lib/paypal-sdk/core/credential/third_party/token.rb +39 -0
  30. data/lib/paypal-sdk/core/exceptions.rb +112 -0
  31. data/lib/paypal-sdk/core/logging.rb +50 -0
  32. data/lib/paypal-sdk/core/openid_connect.rb +140 -0
  33. data/lib/paypal-sdk/core/openid_connect/api.rb +50 -0
  34. data/lib/paypal-sdk/core/openid_connect/data_types.rb +73 -0
  35. data/lib/paypal-sdk/core/openid_connect/get_api.rb +28 -0
  36. data/lib/paypal-sdk/core/openid_connect/request_data_type.rb +52 -0
  37. data/lib/paypal-sdk/core/openid_connect/set_api.rb +36 -0
  38. data/lib/paypal-sdk/core/util.rb +11 -0
  39. data/lib/paypal-sdk/core/util/http_helper.rb +171 -0
  40. data/lib/paypal-sdk/core/util/oauth_signature.rb +64 -0
  41. data/lib/paypal-sdk/core/util/ordered_hash.rb +165 -0
  42. data/lib/paypal-sdk/rest.rb +39 -0
  43. data/lib/paypal-sdk/rest/api.rb +23 -0
  44. data/lib/paypal-sdk/rest/data_types.rb +2597 -0
  45. data/lib/paypal-sdk/rest/error_hash.rb +39 -0
  46. data/lib/paypal-sdk/rest/get_api.rb +20 -0
  47. data/lib/paypal-sdk/rest/request_data_type.rb +53 -0
  48. data/lib/paypal-sdk/rest/set_api.rb +42 -0
  49. data/lib/paypal-sdk/rest/version.rb +7 -0
  50. data/spec/README.md +22 -0
  51. data/spec/config/cacert.pem +171 -0
  52. data/spec/config/cert_key.pem +33 -0
  53. data/spec/config/paypal.yml +35 -0
  54. data/spec/config/sample_data.yml +3 -0
  55. data/spec/core/api/data_type_spec.rb +289 -0
  56. data/spec/core/api/rest_spec.rb +211 -0
  57. data/spec/core/config_spec.rb +192 -0
  58. data/spec/core/logging_spec.rb +28 -0
  59. data/spec/core/openid_connect_spec.rb +153 -0
  60. data/spec/invoice_examples_spec.rb +38 -0
  61. data/spec/log/http.log +175 -0
  62. data/spec/log/rest_http.log +0 -0
  63. data/spec/payments_examples_spec.rb +437 -0
  64. data/spec/payouts_examples_spec.rb +74 -0
  65. data/spec/rest/data_types_spec.rb +62 -0
  66. data/spec/rest/error_hash_spec.rb +83 -0
  67. data/spec/spec_helper.rb +37 -0
  68. data/spec/subscription_examples_spec.rb +227 -0
  69. data/spec/support/sample_data.rb +5 -0
  70. data/spec/web_profile_examples_spec.rb +106 -0
  71. data/spec/webhooks_examples_spec.rb +93 -0
  72. metadata +177 -0
@@ -0,0 +1,50 @@
1
+ require 'multi_json'
2
+ require 'paypal-sdk/rest/version'
3
+
4
+ module PayPal::SDK
5
+ module Core
6
+ module OpenIDConnect
7
+ class API < Core::API::Base
8
+
9
+ DEFAULT_OPENID_ENDPOINT = {
10
+ :sandbox => "https://api.sandbox.paypal.com",
11
+ :live => "https://api.paypal.com" }
12
+
13
+ def initialize(environment = nil, options = {})
14
+ super("", environment, options)
15
+ end
16
+
17
+ def service_endpoint
18
+ self.config.openid_endpoint || DEFAULT_OPENID_ENDPOINT[self.api_mode] || DEFAULT_OPENID_ENDPOINT[:sandbox]
19
+ end
20
+
21
+ def format_request(payload)
22
+ payload[:uri].path = url_join(payload[:uri].path, payload[:action])
23
+ payload[:body] = encode_www_form(payload[:params]) if payload[:params]
24
+ payload[:header] = {"Content-Type" => "application/x-www-form-urlencoded" }.merge(payload[:header])
25
+ payload
26
+ end
27
+
28
+ def format_response(payload)
29
+ payload[:data] =
30
+ if payload[:response].code >= "200" and payload[:response].code <= "299"
31
+ MultiJson.load(payload[:response].body)
32
+ elsif payload[:response].content_type == "application/json"
33
+ { "error" => MultiJson.load(payload[:response].body) }
34
+ else
35
+ { "error" => { "name" => payload[:response].code, "message" => payload[:response].message,
36
+ "developer_msg" => payload[:response] } }
37
+ end
38
+ payload
39
+ end
40
+
41
+ class << self
42
+ def user_agent
43
+ @user_agent ||= "PayPalSDK/openid-connect-ruby #{PayPal::SDK::REST::VERSION} (#{sdk_library_details})"
44
+ end
45
+ end
46
+
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,73 @@
1
+ require 'paypal-sdk-core'
2
+
3
+ module PayPal::SDK::Core
4
+ module OpenIDConnect
5
+ module DataTypes
6
+ class Base < PayPal::SDK::Core::API::DataTypes::Base
7
+ end
8
+
9
+ class Address < Base
10
+ def self.load_members
11
+ object_of :street_address, String
12
+ object_of :locality, String
13
+ object_of :region, String
14
+ object_of :postal_code, String
15
+ object_of :country, String
16
+ end
17
+ end
18
+
19
+ class Userinfo < Base
20
+ def self.load_members
21
+ object_of :user_id, String
22
+ object_of :sub, String
23
+ object_of :name, String
24
+ object_of :given_name, String
25
+ object_of :family_name, String
26
+ object_of :middle_name, String
27
+ object_of :picture, String
28
+ object_of :email, String
29
+ object_of :email_verified, Boolean
30
+ object_of :gender, String
31
+ object_of :birthday, String
32
+ object_of :zoneinfo, String
33
+ object_of :locale, String
34
+ object_of :language, String
35
+ object_of :verified, Boolean
36
+ object_of :phone_number, String
37
+ object_of :address, Address
38
+ object_of :verified_account, Boolean
39
+ object_of :account_type, String
40
+ object_of :account_creation_date, String
41
+ object_of :age_range, String
42
+ object_of :payer_id, String
43
+ end
44
+ end
45
+
46
+ class Tokeninfo < Base
47
+ def self.load_members
48
+ object_of :scope, String
49
+ object_of :access_token, String
50
+ object_of :refresh_token, String
51
+ object_of :token_type, String
52
+ object_of :id_token, String
53
+ object_of :expires_in, Integer
54
+ end
55
+ end
56
+
57
+ class Error < Base
58
+ def self.load_members
59
+ object_of :error, String
60
+ object_of :error_description, String
61
+ object_of :error_uri, String
62
+ end
63
+ end
64
+
65
+
66
+ constants.each do |data_type_klass|
67
+ data_type_klass = const_get(data_type_klass)
68
+ data_type_klass.load_members if defined? data_type_klass.load_members
69
+ end
70
+
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,28 @@
1
+ module PayPal::SDK
2
+ module Core
3
+ module OpenIDConnect
4
+ module GetAPI
5
+ # Get API object
6
+ # === Example
7
+ # Payment.api
8
+ # payment.api
9
+ def api
10
+ @api || parent_api
11
+ end
12
+
13
+ # Parent API object
14
+ def parent_api
15
+ superclass.respond_to?(:api) ? superclass.api : RequestDataType.api
16
+ end
17
+
18
+ def client_id
19
+ api.config.openid_client_id || api.config.client_id
20
+ end
21
+
22
+ def client_secret
23
+ api.config.openid_client_secret || api.config.client_secret
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,52 @@
1
+ module PayPal::SDK
2
+ module Core
3
+ module OpenIDConnect
4
+ module RequestDataType
5
+
6
+ # Get a local API object or Class level API object
7
+ def api
8
+ @api || self.class.api
9
+ end
10
+
11
+ class << self
12
+ # Global API object
13
+ # === Example
14
+ # RequestDataType.api
15
+ def api
16
+ @api ||= API.new({})
17
+ end
18
+
19
+ def client_id
20
+ api.config.openid_client_id || api.config.client_id
21
+ end
22
+
23
+ def client_secret
24
+ api.config.openid_client_secret || api.config.client_secret
25
+ end
26
+
27
+ # Setter for RequestDataType.api
28
+ # === Example
29
+ # RequestDataType.set_config(..)
30
+ include SetAPI
31
+
32
+ # Configure depended module, when RequestDataType is include.
33
+ # === Example
34
+ # class Payment < DataTypes
35
+ # include RequestDataType
36
+ # end
37
+ # Payment.set_config(..)
38
+ # payment.set_config(..)
39
+ # Payment.api
40
+ # payment.api
41
+ def included(klass)
42
+ klass.class_eval do
43
+ extend GetAPI
44
+ extend SetAPI
45
+ include SetAPI
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,36 @@
1
+ module PayPal::SDK
2
+ module Core
3
+ module OpenIDConnect
4
+ module SetAPI
5
+ # Set new api
6
+ # === Examples
7
+ # payment.set_config(:development)
8
+ # payment.set_config(:client_id => "XYZ", :client_secret => "SECRET")
9
+ # payment.set_config
10
+ # payment.api = API.new(:development)
11
+ def set_config(*args)
12
+ if args[0].is_a?(API)
13
+ @api = args[0]
14
+ else
15
+ @api ||= API.new({})
16
+ @api.set_config(*args) # Just override the configuration and Not
17
+ @api
18
+ end
19
+ end
20
+ alias_method :config=, :set_config
21
+ alias_method :set_api, :set_config
22
+ alias_method :api=, :set_config
23
+
24
+ # Override client id
25
+ def client_id=(client_id)
26
+ set_config(:client_id => client_id)
27
+ end
28
+
29
+ # Override client secret
30
+ def client_secret=(client_secret)
31
+ set_config(:client_secret => client_secret)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,11 @@
1
+ module PayPal
2
+ module SDK
3
+ module Core
4
+ module Util
5
+ autoload :OauthSignature, "paypal-sdk/core/util/oauth_signature"
6
+ autoload :OrderedHash, "paypal-sdk/core/util/ordered_hash"
7
+ autoload :HTTPHelper, "paypal-sdk/core/util/http_helper"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,171 @@
1
+ require 'net/https'
2
+ require 'uri'
3
+ require 'cgi'
4
+
5
+ module PayPal::SDK::Core
6
+ module Util
7
+ module HTTPHelper
8
+
9
+ include Configuration
10
+ include Logging
11
+ include Authentication
12
+ include Exceptions
13
+
14
+ # Create HTTP connection based on given service name or configured end point
15
+ def create_http_connection(uri)
16
+ new_http(uri).tap do |http|
17
+ if config.http_timeout
18
+ http.open_timeout = config.http_timeout
19
+ http.read_timeout = config.http_timeout
20
+ end
21
+ configure_ssl(http) if uri.scheme == "https"
22
+ end
23
+ end
24
+
25
+ # New raw HTTP object
26
+ def new_http(uri)
27
+ if config.http_proxy
28
+ proxy = URI.parse(config.http_proxy)
29
+ Net::HTTP.new(uri.host, uri.port, proxy.host, proxy.port, proxy.user, proxy.password)
30
+ else
31
+ Net::HTTP.new(uri.host, uri.port)
32
+ end
33
+ end
34
+
35
+ # Default ca file
36
+ def default_ca_file
37
+ File.expand_path("../../../../../data/paypal.crt", __FILE__)
38
+ end
39
+
40
+ # Apply ssl configuration to http object
41
+ def configure_ssl(http)
42
+ http.tap do |https|
43
+ https.use_ssl = true
44
+ https.ca_file = default_ca_file
45
+ https.verify_mode = OpenSSL::SSL::VERIFY_PEER
46
+ begin
47
+ https.ssl_version = :TLSv1_2
48
+ rescue => error
49
+ logger.warn("WARNING: Your system does not support TLSv1.2. Per PCI Security Council mandate (https://github.com/paypal/TLS-update), you MUST update to latest security library.")
50
+ end
51
+ config.ssl_options.each do |key, value|
52
+ http.send("#{key}=", value)
53
+ end
54
+ add_certificate(https)
55
+ end
56
+ end
57
+
58
+ # Join url
59
+ def url_join(path, action)
60
+ path.sub(/\/?$/, "/#{action}")
61
+ end
62
+
63
+ # Make Http call
64
+ # * payload - Hash(:http, :method, :uri, :body, :header)
65
+ def http_call(payload)
66
+ response =
67
+ log_http_call(payload) do
68
+ http = payload[:http] || create_http_connection(payload[:uri])
69
+ http.start do |session|
70
+ if [ :get, :delete, :head ].include? payload[:method]
71
+ session.send(payload[:method], payload[:uri].request_uri, payload[:header])
72
+ else
73
+ session.send(payload[:method], payload[:uri].request_uri, payload[:body], payload[:header])
74
+ end
75
+ end
76
+ end
77
+
78
+ handle_response(response)
79
+ end
80
+
81
+ # Log Http call
82
+ # * payload - Hash(:http, :method, :uri, :body, :header)
83
+ def log_http_call(payload)
84
+ logger.info "Request[#{payload[:method]}]: #{payload[:uri].to_s}"
85
+
86
+ logger.debug "Request.body=#{payload[:body]}\trequest.header=#{payload[:header]}"
87
+
88
+ start_time = Time.now
89
+ response = yield
90
+ logger.info sprintf("Response[%s]: %s, Duration: %.3fs", response.code,
91
+ response.message, Time.now - start_time)
92
+
93
+ logger.add(
94
+ response_details_log_level(response),
95
+ "Response.body=#{response.body}\tResponse.header=#{response.to_hash}"
96
+ )
97
+
98
+ response
99
+ end
100
+
101
+ # Generate header based on given header keys and properties
102
+ # === Arguments
103
+ # * <tt>header_keys</tt> -- List of Header keys for the properties
104
+ # * <tt>properties</tt> -- properties
105
+ # === Return
106
+ # Hash with header as key property as value
107
+ # === Example
108
+ # map_header_value( { :username => "X-PAYPAL-USERNAME"}, { :username => "guest" })
109
+ # # Return: { "X-PAYPAL-USERNAME" => "guest" }
110
+ def map_header_value(header_keys, properties)
111
+ header = {}
112
+ properties.each do |key, value|
113
+ key = header_keys[key]
114
+ header[key] = value.to_s if key and value
115
+ end
116
+ header
117
+ end
118
+
119
+ def encode_www_form(hash)
120
+ if defined? URI.encode_www_form
121
+ URI.encode_www_form(hash)
122
+ else
123
+ hash.map{|key, value| "#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}" }.join("&")
124
+ end
125
+ end
126
+
127
+ # Handles response and error codes from the remote service.
128
+ def handle_response(response)
129
+ case response.code.to_i
130
+ when 301, 302, 303, 307
131
+ raise(Redirection.new(response))
132
+ when 200...400
133
+ response
134
+ when 400
135
+ raise(BadRequest.new(response))
136
+ when 401
137
+ raise(UnauthorizedAccess.new(response))
138
+ when 403
139
+ raise(ForbiddenAccess.new(response))
140
+ when 404
141
+ raise(ResourceNotFound.new(response))
142
+ when 405
143
+ raise(MethodNotAllowed.new(response))
144
+ when 409
145
+ raise(ResourceConflict.new(response))
146
+ when 410
147
+ raise(ResourceGone.new(response))
148
+ when 422
149
+ raise(ResourceInvalid.new(response))
150
+ when 401...500
151
+ raise(ClientError.new(response))
152
+ when 500...600
153
+ raise(ServerError.new(response))
154
+ else
155
+ raise(ConnectionError.new(response, "Unknown response code: #{response.code}"))
156
+ end
157
+ end
158
+
159
+ private
160
+
161
+ def response_details_log_level(response)
162
+ if (400...600).cover?(response.code.to_i)
163
+ Logger::WARN
164
+ else
165
+ Logger::DEBUG
166
+ end
167
+ end
168
+
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,64 @@
1
+ require 'uri'
2
+ require 'cgi'
3
+ require 'openssl'
4
+ require 'base64'
5
+
6
+ module PayPal::SDK::Core
7
+ module Util
8
+ class OauthSignature
9
+ attr_accessor :username, :password, :token, :token_secret, :url, :timestamp
10
+
11
+ def initialize(username, password, token, token_secret, url, timestamp = nil)
12
+ @username = username
13
+ @password = password
14
+ @token = token
15
+ @token_secret = token_secret
16
+ @url = url
17
+ @timestamp = timestamp || Time.now.to_i.to_s
18
+ end
19
+
20
+ def authorization_string
21
+ signature = oauth_signature
22
+ "token=#{token},signature=#{signature},timestamp=#{timestamp}"
23
+ end
24
+
25
+ def oauth_signature
26
+ key = [
27
+ paypal_encode(password),
28
+ paypal_encode(token_secret),
29
+ ].join("&").gsub(/%[0-9A-F][0-9A-F]/, &:downcase )
30
+
31
+ digest = OpenSSL::HMAC.digest('sha1', key, base_string)
32
+ Base64.encode64(digest).chomp
33
+ end
34
+
35
+ def base_string
36
+ params = {
37
+ "oauth_consumer_key" => username,
38
+ "oauth_version" => "1.0",
39
+ "oauth_signature_method" => "HMAC-SHA1",
40
+ "oauth_token" => token,
41
+ "oauth_timestamp" => timestamp,
42
+ }
43
+ sorted_query_string = params.sort.map{|v| v.join("=") }.join("&")
44
+
45
+ base = [
46
+ "POST",
47
+ paypal_encode(url),
48
+ paypal_encode(sorted_query_string)
49
+ ].join("&")
50
+ base = base.gsub(/%[0-9A-F][0-9A-F]/, &:downcase )
51
+ end
52
+
53
+ # The PayPalURLEncoder java class percent encodes everything other than 'a-zA-Z0-9 _'.
54
+ # Then it converts ' ' to '+'.
55
+ # Ruby's CGI.encode takes care of the ' ' and '*' to satisfy PayPal
56
+ # (but beware, URI.encode percent encodes spaces, and does nothing with '*').
57
+ # Finally, CGI.encode does not encode '.-', which we need to do here.
58
+ def paypal_encode str
59
+ CGI.escape(str.to_s).gsub('.', '%2E').gsub('-', '%2D')
60
+ end
61
+ end
62
+ end
63
+ end
64
+