paypal-sdk-rest-pmrb 1.8.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.
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
+