paypal-sdk-core 0.1.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 (34) hide show
  1. data/Gemfile +11 -0
  2. data/README.md +16 -0
  3. data/Rakefile +5 -0
  4. data/lib/generators/paypal/sdk/USAGE +3 -0
  5. data/lib/generators/paypal/sdk/install_generator.rb +17 -0
  6. data/lib/generators/paypal/sdk/templates/paypal.rb +2 -0
  7. data/lib/generators/paypal/sdk/templates/paypal.yml +32 -0
  8. data/lib/paypal-sdk-core.rb +40 -0
  9. data/lib/paypal-sdk/core/api/base.rb +166 -0
  10. data/lib/paypal-sdk/core/api/data_types/base.rb +236 -0
  11. data/lib/paypal-sdk/core/api/data_types/enum.rb +26 -0
  12. data/lib/paypal-sdk/core/api/data_types/simple_types.rb +47 -0
  13. data/lib/paypal-sdk/core/api/merchant.rb +122 -0
  14. data/lib/paypal-sdk/core/api/platform.rb +75 -0
  15. data/lib/paypal-sdk/core/authentication.rb +79 -0
  16. data/lib/paypal-sdk/core/config.rb +138 -0
  17. data/lib/paypal-sdk/core/credential/base.rb +27 -0
  18. data/lib/paypal-sdk/core/credential/certificate.rb +32 -0
  19. data/lib/paypal-sdk/core/credential/signature.rb +22 -0
  20. data/lib/paypal-sdk/core/credential/third_party/subject.rb +25 -0
  21. data/lib/paypal-sdk/core/credential/third_party/token.rb +39 -0
  22. data/lib/paypal-sdk/core/logging.rb +43 -0
  23. data/lib/paypal-sdk/core/util/oauth_signature.rb +64 -0
  24. data/lib/paypal-sdk/core/version.rb +7 -0
  25. data/spec/config/cert_key.pem +33 -0
  26. data/spec/config/paypal.yml +29 -0
  27. data/spec/core/api/data_type_spec.rb +164 -0
  28. data/spec/core/api/merchant_spec.rb +114 -0
  29. data/spec/core/api/platform_spec.rb +107 -0
  30. data/spec/core/config_spec.rb +48 -0
  31. data/spec/core/logging_spec.rb +28 -0
  32. data/spec/log/test.log +84 -0
  33. data/spec/spec_helper.rb +11 -0
  34. metadata +109 -0
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in paypal-sdk-core.gemspec
4
+ gemspec
5
+
6
+ gem 'rake', :require => false
7
+
8
+ group :test do
9
+ gem 'rspec'
10
+ gem 'simplecov', :require => false
11
+ end
data/README.md ADDED
@@ -0,0 +1,16 @@
1
+ # Paypal::Sdk::Core
2
+
3
+ Core library for PayPal ruby SDKs.
4
+
5
+ SDKs:
6
+
7
+ * [paypal-sdk-adaptivepayments](https://github.com/paypal/adaptivepayments-sdk-ruby)
8
+ * [paypal-sdk-adaptiveaccounts](https://github.com/paypal/adaptiveaccounts-sdk-ruby)
9
+ * [paypal-sdk-invoice](https://github.com/paypal/invoice-sdk-ruby)
10
+ * [paypal-sdk-permissions](https://github.com/paypal/permissions-sdk-ruby)
11
+ * [paypal-sdk-merchant](https://github.com/paypal/merchant-sdk-ruby)
12
+ * [paypal-sdk-buttonmanager](https://github.com/paypal/buttonmanager-sdk-ruby)
13
+
14
+
15
+ Note: Don't use this library directly
16
+
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new
5
+ task :default => :spec
@@ -0,0 +1,3 @@
1
+ To copy a PayPal SDK default configuration and initializer to your Rails App.
2
+
3
+ rails g paypal-sdk:install
@@ -0,0 +1,17 @@
1
+ module Paypal
2
+ module Sdk
3
+ module Generators
4
+ class InstallGenerator < Rails::Generators::Base
5
+ source_root File.expand_path('../templates', __FILE__)
6
+
7
+ def copy_config_file
8
+ copy_file "paypal.yml", "config/paypal.yml"
9
+ end
10
+
11
+ def copy_initializer_file
12
+ copy_file "paypal.rb", "config/initializers/paypal.rb"
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,2 @@
1
+ PayPal::SDK::Core::Config.load("config/paypal.yml", Rails.env)
2
+ PayPal::SDK::Core::Config.logger = Rails.logger
@@ -0,0 +1,32 @@
1
+ test: &default
2
+ username: jb-us-seller_api1.paypal.com
3
+ password: WX4WTU3S8MY44S7F
4
+ signature: AFcWxV21C7fd0v3bYYYRCpSSRl31A7yDhhsPUU2XhtMoZXsWHFxu-RWy
5
+ app_id: APP-80W284485P519543T
6
+ http_timeout: 30
7
+ mode: sandbox
8
+ sandbox_email_address: Platform.sdk.seller@gmail.com
9
+
10
+ development:
11
+ <<: *default
12
+
13
+ production:
14
+ <<: *default
15
+ mode: live
16
+
17
+ # with_certificate:
18
+ # <<: *default
19
+ # username: platfo_1255170694_biz_api1.gmail.com
20
+ # password: 2DPPKUPKB7DQLXNR
21
+ # signature:
22
+ # cert_path: "config/cert_key.pem"
23
+ # app_id: APP-80W284485P519543T
24
+ #
25
+ # with_oauth_token:
26
+ # <<: *default
27
+ # token: ESTy2hio5WJQo1iixkH29I53RJxaS0Gvno1A6.YQXZgktxbY4I2Tdg
28
+ # token_secret: ZKPhUYuwJwYsfWdzorozWO2U9pI
29
+ #
30
+ # with_proxy:
31
+ # <<: *default
32
+ # http_proxy: http://proxy-ipaddress:3129/
@@ -0,0 +1,40 @@
1
+ require "paypal-sdk/core/version"
2
+ require "paypal-sdk/core/config"
3
+ require "paypal-sdk/core/logging"
4
+
5
+ module PayPal
6
+ module SDK
7
+ module Core
8
+
9
+ autoload :Authentication, "paypal-sdk/core/authentication"
10
+
11
+ module API
12
+ autoload :Base, "paypal-sdk/core/api/base"
13
+ autoload :Merchant, "paypal-sdk/core/api/merchant"
14
+ autoload :Platform, "paypal-sdk/core/api/platform"
15
+
16
+ module DataTypes
17
+ autoload :Base, "paypal-sdk/core/api/data_types/base"
18
+ autoload :Enum, "paypal-sdk/core/api/data_types/enum"
19
+ autoload :SimpleTypes, "paypal-sdk/core/api/data_types/simple_types"
20
+ end
21
+ end
22
+
23
+ module Util
24
+ autoload :OauthSignature, "paypal-sdk/core/util/oauth_signature"
25
+ end
26
+
27
+ module Credential
28
+ autoload :Base, "paypal-sdk/core/credential/base"
29
+ autoload :Certificate, "paypal-sdk/core/credential/certificate"
30
+ autoload :Signature, "paypal-sdk/core/credential/signature"
31
+
32
+ module ThirdParty
33
+ autoload :Token, "paypal-sdk/core/credential/third_party/token"
34
+ autoload :Subject, "paypal-sdk/core/credential/third_party/subject"
35
+ end
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,166 @@
1
+ require 'net/http'
2
+
3
+ module PayPal::SDK::Core
4
+
5
+ module API
6
+ # API class provide default functionality for accessing the API web services.
7
+ # == Example
8
+ # api = API::Base.new("AdaptivePayments")
9
+ # response = api.request("GetPaymentOptions", "")
10
+ class Base
11
+
12
+ include Configuration
13
+ include Logging
14
+ include Authentication
15
+
16
+ attr_accessor :http, :uri, :service_name
17
+
18
+ DEFAULT_END_POINTS = {
19
+ :sandbox => {
20
+ :platform => { # NVP EndPoint
21
+ :three_token => "https://svcs.sandbox.paypal.com/",
22
+ :certificate => "https://svcs.sandbox.paypal.com/"
23
+ },
24
+ :merchant => { # SOAP EndPoint
25
+ :three_token => "https://api-3t.sandbox.paypal.com/2.0",
26
+ :certificate => "https://api.sandbox.paypal.com/2.0"
27
+ }
28
+ },
29
+ :live => {
30
+ :platform => { # NVP EndPoint
31
+ :three_token => "https://svcs.paypal.com/",
32
+ :certificate => "https://svcs.paypal.com/"
33
+ },
34
+ :merchant => { # SOAP EndPoint
35
+ :three_token => "https://api-3t.paypal.com/2.0",
36
+ :certificate => "https://api.paypal.com/2.0"
37
+ }
38
+ }
39
+ }
40
+ DEFAULT_HTTP_HEADER = {}
41
+
42
+ # Initialize API object
43
+ # === Argument
44
+ # * <tt>service_name</tt> -- (Optional) Service name
45
+ # * <tt>environment</tt> -- (Optional) Configuration environment to load
46
+ # * <tt>options</tt> -- (Optional) Override configuration.
47
+ # === Example
48
+ # new("AdaptivePayments")
49
+ # new("AdaptivePayments", :development)
50
+ # new(:wsdl_service) # It load wsdl_service configuration
51
+ def initialize(service_name = "", environment = nil, options = {})
52
+ unless service_name.is_a? String
53
+ environment, options, service_name = service_name, environment || {}, ""
54
+ end
55
+ @service_name = service_name
56
+ set_config(environment, options)
57
+ end
58
+
59
+ # Override set_config method to create http connection on changing the configuration.
60
+ def set_config(*args)
61
+ super
62
+ create_http_connection
63
+ end
64
+
65
+ # Create HTTP connection based on given service name or configured end point
66
+ def create_http_connection
67
+ service_path = "#{service_endpoint}/#{service_name}"
68
+ @uri = URI.parse(service_path)
69
+ if config.http_proxy
70
+ proxy = URI.parse(config.http_proxy)
71
+ @http = Net::HTTP.new(@uri.host, @uri.port, proxy.host, proxy.port, proxy.user, proxy.password)
72
+ else
73
+ @http = Net::HTTP.new(@uri.host, @uri.port)
74
+ end
75
+ @uri.path = @uri.path.gsub(/\/+/, "/")
76
+ configure_http_connection
77
+ end
78
+
79
+ # Configure HTTP connection based on configuration.
80
+ def configure_http_connection
81
+ http.use_ssl = true
82
+ http.ca_file = config.ca_file if config.ca_file
83
+ if config.http_timeout
84
+ http.open_timeout = config.http_timeout
85
+ http.read_timeout = config.http_timeout
86
+ end
87
+ add_certificate(http)
88
+ end
89
+
90
+ # Get configured API mode( sandbox or live)
91
+ def api_mode
92
+ api_modes = DEFAULT_END_POINTS.keys
93
+ config_mode = ( config.mode || api_modes.first ).to_sym
94
+ api_modes.include?(config_mode) ? config_mode : api_modes.first
95
+ end
96
+
97
+ # Get default endpoint for the given service name
98
+ # === Argument
99
+ # * <tt>name</tt> -- Service name ( platform or merchant)
100
+ # === Returns
101
+ # Return service end point based on the configured API mode.
102
+ def default_end_point(name)
103
+ default_end_point = DEFAULT_END_POINTS[api_mode][name]
104
+ if default_end_point
105
+ config.cert_path ? default_end_point[:certificate] : default_end_point[:three_token]
106
+ end
107
+ end
108
+
109
+ # Get service end point
110
+ def service_endpoint
111
+ config.end_point ? default_end_point(config.end_point.to_sym) : config.end_point
112
+ end
113
+
114
+ # Default Http header
115
+ def default_http_header
116
+ DEFAULT_HTTP_HEADER
117
+ end
118
+
119
+ # Generate HTTP request for given action and parameters
120
+ # === Arguments
121
+ # * <tt>action</tt> -- Action to perform
122
+ # * <tt>params</tt> -- (Optional) Parameters for the action
123
+ # * <tt>initheader</tt> -- (Optional) HTTP header
124
+ def request(action, params = {}, initheader = {})
125
+ uri, content, header = format_request(action, params)
126
+ initheader = default_http_header.merge(header).merge(initheader)
127
+ initheader.delete_if{|key, val| val.nil? }
128
+ start_time = Time.now
129
+ response = @http.post(uri.path, content, initheader)
130
+ format_response(action, response)
131
+ rescue Net::HTTPBadGateway, Errno::ECONNRESET, Errno::ECONNABORTED, SocketError => error
132
+ format_error(error, error.message)
133
+ ensure
134
+ log_event("Request: #{action}", start_time)
135
+ end
136
+
137
+ # Format Request data. It will be override by child class
138
+ # == Arguments
139
+ # * <tt>action</tt> -- Request action
140
+ # * <tt>params</tt> -- Request parameters
141
+ # == Return
142
+ # * <tt>path</tt> -- Formated request uri object
143
+ # * <tt>params</tt> -- Formated request Parameters
144
+ # * <tt>header</tt> -- HTTP Header
145
+ def format_request(action, params)
146
+ [ @uri, params, {} ]
147
+ end
148
+
149
+ # Format Response object. It will be override by child class
150
+ # == Argument
151
+ # * <tt>action</tt> -- Request action
152
+ # * <tt>response</tt> -- HTTP response object
153
+ def format_response(action, response)
154
+ response
155
+ end
156
+
157
+ # Format Error object. It will be override by child class.
158
+ # == Arguments
159
+ # * <tt>exception</tt> -- Exception object.
160
+ # * <tt>message</tt> -- Readable error message.
161
+ def format_error(exception, message)
162
+ raise exception
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,236 @@
1
+ require 'date'
2
+
3
+ module PayPal::SDK::Core
4
+ module API
5
+
6
+ module DataTypes
7
+
8
+ # Create attributes and restrict the object type.
9
+ # == Example
10
+ # class ConvertCurrencyRequest < Core::API::DataTypes::Base
11
+ # object_of :baseAmountList, CurrencyList
12
+ # object_of :convertToCurrencyList, CurrencyCodeList
13
+ # object_of :countryCode, String
14
+ # object_of :conversionType, String
15
+ # end
16
+ class Base
17
+
18
+ HashOptions = { :attribute => true, :namespace => true, :symbol => true }
19
+ ContentKey = :value
20
+
21
+ include SimpleTypes
22
+
23
+ class << self
24
+
25
+ # Add attribute
26
+ # === Arguments
27
+ # * <tt>name</tt> -- attribute name
28
+ # * <tt>options</tt> -- options
29
+ def add_attribute(name, options = {})
30
+ add_member(name, SimpleTypes::String, options.merge( :attribute => true ))
31
+ end
32
+
33
+ # Fields list for the DataTye
34
+ def members
35
+ @members ||=
36
+ begin
37
+ parent_members = superclass.instance_variable_get("@members")
38
+ parent_members ? parent_members.dup : {}
39
+ end
40
+ end
41
+
42
+ # Add Field to class variable hash and generate methods
43
+ # === Example
44
+ # add_member(:errorMessage, String) # Generate Code
45
+ # # attr_reader :errorMessage
46
+ # # alias_method :error_message, :errorMessage
47
+ # # alias_method :error_message=, :errorMessage=
48
+ def add_member(member_name, klass, options = {})
49
+ member_name = member_name.to_sym
50
+ members[member_name] = options.merge( :type => klass )
51
+ member_variable_name = "@#{member_name}"
52
+ define_method "#{member_name}=" do |value|
53
+ object = options[:array] ? convert_array(value, klass) : convert_object(value, klass)
54
+ instance_variable_set(member_variable_name, object)
55
+ end
56
+ default_value = ( options[:array] ? [] : ( klass < Base ? {} : nil ) )
57
+ define_method member_name do |&block|
58
+ value = instance_variable_get(member_variable_name) || ( default_value && send("#{member_name}=", default_value) )
59
+ value.instance_eval(&block) if block
60
+ value
61
+ end
62
+ define_alias_methods(member_name, options)
63
+ end
64
+
65
+ # Define alias methods for getter and setter
66
+ def define_alias_methods(member_name, options)
67
+ snakecase_name = snakecase(member_name)
68
+ alias_method snakecase_name, member_name
69
+ alias_method "#{snakecase_name}=", "#{member_name}="
70
+ alias_method "#{options[:namespace]}:#{member_name}=", "#{member_name}=" if options[:namespace]
71
+ if options[:attribute]
72
+ alias_method "@#{member_name}=", "#{member_name}="
73
+ alias_method "@#{options[:namespace]}:#{member_name}=", "#{member_name}=" if options[:namespace]
74
+ end
75
+ end
76
+
77
+ # define method for given member and the class name
78
+ # === Example
79
+ # object_of(:errorMessage, ErrorMessage) # Generate Code
80
+ # # def errorMessage=(options)
81
+ # # @errorMessage = ErrorMessage.new(options)
82
+ # # end
83
+ # # add_member :errorMessage, ErrorMessage
84
+ def object_of(key, klass, options = {})
85
+ add_member(key, klass, options)
86
+ end
87
+
88
+ # define method for given member and the class name
89
+ # === Example
90
+ # array_of(:errorMessage, ErrorMessage) # It Generate below code
91
+ # # def errorMessage=(array)
92
+ # # @errorMessage = array.map{|options| ErrorMessage.new(options) }
93
+ # # end
94
+ # # add_member :errorMessage, ErrorMessage
95
+ def array_of(key, klass, options = {})
96
+ add_member(key, klass, options.merge(:array => true))
97
+ end
98
+
99
+ # Generate snakecase string.
100
+ # === Example
101
+ # snakecase("errorMessage")
102
+ # # error_message
103
+ def snakecase(string)
104
+ string.to_s.gsub(/([a-z])([A-Z])/, '\1_\2').gsub(/([A-Z])([A-Z][a-z])/, '\1_\2').downcase
105
+ end
106
+
107
+ end
108
+
109
+ # Initialize options.
110
+ def initialize(options = {}, &block)
111
+ if options.is_a? Hash
112
+ options.each do |key, value|
113
+ begin
114
+ send("#{key}=", value)
115
+ rescue TypeError, ArgumentError => error
116
+ raise TypeError, "#{error.message}(#{value.inspect}) for #{self.class.name}.#{key} member"
117
+ end
118
+ end
119
+ elsif members[ContentKey]
120
+ self.value = options
121
+ else
122
+ raise ArgumentError, "invalid data(#{options.inspect}) for #{self.class.name} class"
123
+ end
124
+ self.instance_eval(&block) if block
125
+ end
126
+
127
+ # Create Array with default value.
128
+ class ArrayWithDefault < ::Array
129
+ def initialize(&block)
130
+ @block = block
131
+ super()
132
+ end
133
+
134
+ def [](key)
135
+ super(key) || send(:"[]=", key, nil)
136
+ end
137
+
138
+ def []=(key, value)
139
+ super(key, @block ? @block.call(value) : value )
140
+ end
141
+
142
+ def merge!(array)
143
+ if array.is_a? Array
144
+ array.each_with_index do |object, index|
145
+ self[index] = object
146
+ end
147
+ elsif array.is_a? Hash and array.keys.first.to_s =~ /^\d+$/
148
+ array.each do |key, object|
149
+ self[key.to_i] = object
150
+ end
151
+ else
152
+ self[0] = array
153
+ end
154
+ self
155
+ end
156
+ end
157
+
158
+ # Create array of objects.
159
+ # === Example
160
+ # covert_array([{ :amount => "55", :code => "USD"}], CurrencyType)
161
+ # covert_array({ "0" => { :amount => "55", :code => "USD"} }, CurrencyType)
162
+ # covert_array({ :amount => "55", :code => "USD"}, CurrencyType)
163
+ # # @return
164
+ # # [ <CurrencyType#object @amount="55" @code="USD" > ]
165
+ def convert_array(array, klass)
166
+ default_value = ( klass < Base ? {} : nil )
167
+ data_type_array = ArrayWithDefault.new{|object| convert_object(object || default_value, klass) }
168
+ data_type_array.merge!(array)
169
+ end
170
+
171
+ # Create object based on given data.
172
+ # === Example
173
+ # covert_object({ :amount => "55", :code => "USD"}, CurrencyType )
174
+ # # @return
175
+ # # <CurrencyType#object @amount="55" @code="USD" >
176
+ def convert_object(object, klass)
177
+ object.is_a?(klass) ? object : ( ( object.nil? or object == "" ) ? nil : klass.new(object) )
178
+ end
179
+
180
+ # Alias instance method for the class method.
181
+ def members
182
+ self.class.members
183
+ end
184
+
185
+ # Get configured member names
186
+ def member_names
187
+ members.keys
188
+ end
189
+
190
+ # Create Hash based configured members
191
+ def to_hash(options = {})
192
+ options = HashOptions.merge(options)
193
+ hash = {}
194
+ member_names.each do |member|
195
+ value = value_to_hash(instance_variable_get("@#{member}"), options)
196
+ hash[hash_key(member, options)] = value unless skip_value?(value)
197
+ end
198
+ hash
199
+ end
200
+
201
+ # Skip nil, empty array and empty hash.
202
+ def skip_value?(value)
203
+ value.nil? || ( ( value.is_a?(Array) || value.is_a?(Hash) ) && value.empty? )
204
+ end
205
+
206
+ # Generate Hash key for given member name based on configuration
207
+ # === Example
208
+ # hash_key(:amount) # @return :"ebl:amount"
209
+ # hash_key(:type) # @return :"@type"
210
+ def hash_key(key, options = {})
211
+ unless key == ContentKey
212
+ member_option = members[key]
213
+ key = member_option[:name] if member_option.include? :name
214
+ key = "#{member_option[:namespace]}:#{key}" if member_option[:namespace] and options[:namespace]
215
+ key = "@#{key}" if member_option[:attribute] and options[:attribute]
216
+ end
217
+ options[:symbol] ? key.to_sym : key.to_s
218
+ end
219
+
220
+ # Covert the object to hash based on class.
221
+ def value_to_hash(value, options = {})
222
+ case value
223
+ when Array
224
+ value = value.map{|object| value_to_hash(object, options) }
225
+ value.delete_if{|v| skip_value?(v) }
226
+ value
227
+ when Base
228
+ value.to_hash(options)
229
+ else
230
+ value
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end