paypal-sdk-core 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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