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
@@ -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
+ certificate_properties = {}
25
+ certificate_properties.delete_if{|k,v| RemoveProperties.include? k }
26
+ certificate_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,43 @@
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, start_time, end_time = Time.now)
19
+ duration = sprintf("%.3f", end_time - start_time)
20
+ logger.info "[#{duration}] #{message}"
21
+ end
22
+
23
+ class << self
24
+
25
+ # Get or Create configured logger based on the default environment configuration
26
+ def logger
27
+ @logger ||= Logger.new(Config.config.logfile || STDERR)
28
+ end
29
+
30
+ # Set logger directly and clear the loggers cache.
31
+ # === Attributes
32
+ # * <tt>logger</tt> -- Logger object
33
+ # === Example
34
+ # Logging.logger = Logger.new(STDERR)
35
+ def logger=(logger)
36
+ @logger = logger
37
+ end
38
+
39
+ end
40
+ end
41
+
42
+ end
43
+
@@ -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("&")
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).gsub('.', '%2E').gsub('-', '%2D')
60
+ end
61
+ end
62
+ end
63
+ end
64
+
@@ -0,0 +1,7 @@
1
+ module PayPal
2
+ module SDK
3
+ module Core
4
+ VERSION = "0.1.0"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,33 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIICXAIBAAKBgQCx/rJhKDRYhs9WZj66FA+HidsrKwvep3g+YNbm1fXmjcg2rEmC
3
+ kq71+Ftw9rx0Uz7vmg6vcsASUgOyeNG7mVB1SsXc6j+JAzZsmpzxHI0QKm+nBNTS
4
+ OAp5NWn6NZQfu3BNAJ/Mok/iL3am2DWXV6dU74J66rBpwIJfzs9kmw8ZGwIDAQAB
5
+ AoGAa/V1sCQ4i7FItLjTNv3P5X+h5W74hhXBguQttFj2Ct7YHwEknQPnBt2aaMve
6
+ xhdvxtgELDpHcVU5VNifLU/yUg3+DSr/YkpBWOcNTCt1seW/z5s+jr2fQERQKbyf
7
+ SXWMTqwrQ19iQoCPYaj7Drf68JhksQCaYN650g7+B/QmSBECQQDp6r75fzDtEWrr
8
+ O4Sl9plK6CRLqQQ3LveAw4JV31N2UAqgAYtzRqD6K+SviAVtX9xxuv983qQxsfX4
9
+ ozE9sGXPAkEAwsxwR1s2Acuy10h3Xj6VtnFB3PpUrkSI9c9ZxF4CMf/+AS/b2UEe
10
+ QhH60WHY8ccgKT/DoPWBcEu2o0f9nPw29QJBAI480zHNeMe/Hp+5iliM0fvtmxxy
11
+ wwB3S8L9n4RuD0dTNpLDPbO0D/DvvdhKwtoWP2rcxbx9eaRKTYKKYUfcupsCQAkP
12
+ SQmIjHJ47tBkZmjTsFLT4aRNYDLarSQBiMNBPAjnRwD3INpx1N5tx6SFUHmuMSi5
13
+ 9nc9888tNklRx9HNSSECQHgs9ExBpA6WbRVcgiizOKH7fmNxAB5f6TQ2W1QHMUb+
14
+ UhZpwuDelOIfzJAQUZGTZk8a8uVmyXU5hTf3ZDbrnJ8=
15
+ -----END RSA PRIVATE KEY-----
16
+
17
+ -----BEGIN CERTIFICATE-----
18
+ MIICpjCCAg+gAwIBAgIDD96nMA0GCSqGSIb3DQEBBQUAMIGfMQswCQYDVQQGEwJV
19
+ UzETMBEGA1UECBMKQ2FsaWZvcm5pYTERMA8GA1UEBxMIU2FuIEpvc2UxFTATBgNV
20
+ BAoTDFBheVBhbCwgSW5jLjEWMBQGA1UECxQNc2FuZGJveF9jZXJ0czEbMBkGA1UE
21
+ AxQSc2FuZGJveF9jYW1lcmNoYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwu
22
+ Y29tMB4XDTA5MTAxNTA2Mzg1N1oXDTE5MTAxMzA2Mzg1N1owgYAxLTArBgNVBAMU
23
+ JHBsYXRmb18xMjU1MTcwNjk0X2Jpel9hcGkxLmdtYWlsLmNvbTEiMCAGA1UEChMZ
24
+ cGxhdGZvcm0gc2RrJ3MgVGVzdCBTdG9yZTERMA8GA1UEBxMIU2FuIEpvc2UxCzAJ
25
+ BgNVBAgTAkNBMQswCQYDVQQGEwJVUzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
26
+ gYEAsf6yYSg0WIbPVmY+uhQPh4nbKysL3qd4PmDW5tX15o3INqxJgpKu9fhbcPa8
27
+ dFM+75oOr3LAElIDsnjRu5lQdUrF3Oo/iQM2bJqc8RyNECpvpwTU0jgKeTVp+jWU
28
+ H7twTQCfzKJP4i92ptg1l1enVO+CeuqwacCCX87PZJsPGRsCAwEAAaMNMAswCQYD
29
+ VR0TBAIwADANBgkqhkiG9w0BAQUFAAOBgQCgH3kwXMJtcAaCBQLKz5TGFogJp/C3
30
+ 06MvjYzdbDrx9Rjf/252UhD8dMPUP5FhU1KXduL+KIEYawPbDQ9+lV58JgM12R0p
31
+ EhCODDI/lDvzbfxUnYgkJ5cnFhTZpcAqVzWuinUnG8jAL9XKiEyu/C73ePMPWPbt
32
+ otoWi+Tk828Qlw==
33
+ -----END CERTIFICATE-----
@@ -0,0 +1,29 @@
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
+ logfile: spec/log/test.log
10
+
11
+ development:
12
+ <<: *default
13
+
14
+ with_certificate:
15
+ <<: *default
16
+ username: platfo_1255170694_biz_api1.gmail.com
17
+ password: 2DPPKUPKB7DQLXNR
18
+ signature:
19
+ cert_path: <%= File.expand_path("../cert_key.pem", __FILE__) %>
20
+ app_id: APP-80W284485P519543T
21
+
22
+ with_oauth_token:
23
+ <<: *default
24
+ token: ESTy2hio5WJQo1iixkH29I53RJxaS0Gvno1A6.YQXZgktxbY4I2Tdg
25
+ token_secret: ZKPhUYuwJwYsfWdzorozWO2U9pI
26
+
27
+ with_proxy:
28
+ <<: *default
29
+ http_proxy: <%= ENV['http_proxy'] %>
@@ -0,0 +1,164 @@
1
+ require 'spec_helper'
2
+
3
+ describe PayPal::SDK::Core::API::DataTypes::Base do
4
+
5
+ DataType = PayPal::SDK::Core::API::DataTypes::Base
6
+
7
+ class TestCurrency < DataType
8
+
9
+ # Members
10
+ object_of :amount, String
11
+ object_of :type, String, :namespace => "ns"
12
+ # Attributes
13
+ add_attribute :code
14
+ end
15
+
16
+ class TestType < DataType
17
+ object_of :fromCurrency, TestCurrency
18
+ array_of :toCurrency, TestCurrency
19
+ object_of :firstname, String, :name => "first-name"
20
+ end
21
+
22
+ class TestSimpleType < DataType
23
+ include PayPal::SDK::Core::API::DataTypes::SimpleTypes
24
+ object_of :created_on, Date
25
+ object_of :created_at, DateTime
26
+ end
27
+
28
+ it "should create member object automatically" do
29
+ test_type = TestType.new
30
+ test_type.fromCurrency.should be_a TestCurrency
31
+ test_type.toCurrency.should be_a Array
32
+ test_type.toCurrency[0].should be_a TestCurrency
33
+ test_type.toCurrency[1].should be_a TestCurrency
34
+ test_type.toCurrency[0].amount.should eql nil
35
+ test_type.fromCurrency.amount.should eql nil
36
+ test_type.fromCurrency.type.should eql nil
37
+ end
38
+
39
+ it "should convert the given data to configured type" do
40
+ test_type = TestType.new( :fromCurrency => { :code => "USD", :amount => "50.0"})
41
+ test_type.fromCurrency.should be_a TestCurrency
42
+ test_type.fromCurrency.code.should eql "USD"
43
+ test_type.fromCurrency.amount.should eql "50.0"
44
+ end
45
+
46
+ it "should allow block with initializer" do
47
+ test_type = TestType.new do
48
+ fromCurrency do
49
+ self.code = "USD"
50
+ self.amount = "50.0"
51
+ end
52
+ end
53
+ test_type.fromCurrency.code.should eql "USD"
54
+ test_type.fromCurrency.amount.should eql "50.0"
55
+ end
56
+
57
+ it "should allow block with member" do
58
+ test_type = TestType.new
59
+ test_type.fromCurrency do
60
+ self.code = "USD"
61
+ self.amount = "50.0"
62
+ end
63
+ test_type.fromCurrency.code.should eql "USD"
64
+ test_type.fromCurrency.amount.should eql "50.0"
65
+ end
66
+
67
+ it "should assign value to attribute" do
68
+ test_currency = TestCurrency.new( :@code => "USD", :amount => "50" )
69
+ test_currency.code.should eql "USD"
70
+ end
71
+
72
+ it "should allow configured Class object" do
73
+ test_currency = TestCurrency.new( :code => "USD", :amount => "50" )
74
+ test_type = TestType.new( :fromCurrency => test_currency )
75
+ test_type.fromCurrency.should eql test_currency
76
+ end
77
+
78
+ it "should allow snakecase" do
79
+ test_type = TestType.new( :from_currency => {} )
80
+ test_type.from_currency.should be_a TestCurrency
81
+ test_type.from_currency.should eql test_type.fromCurrency
82
+ end
83
+
84
+ it "should allow array" do
85
+ test_type = TestType.new( :toCurrency => [{ :code => "USD", :amount => "50.0" }] )
86
+ test_type.toCurrency.should be_a Array
87
+ test_type.toCurrency.first.should be_a TestCurrency
88
+ end
89
+
90
+ it "should allow only configured fields" do
91
+ lambda do
92
+ TestType.new( :notExist => "testing")
93
+ end.should raise_error
94
+ end
95
+
96
+ it "should not convert empty hash" do
97
+ test_type = TestType.new( :fromCurrency => {} )
98
+ test_type.to_hash.should eql({})
99
+ end
100
+
101
+ it "should not convert empty array" do
102
+ test_type = TestType.new( :toCurrency => [] )
103
+ test_type.to_hash.should eql({})
104
+ end
105
+
106
+ it "should not convert array of empty hash" do
107
+ test_type = TestType.new( :toCurrency => [ {} ] )
108
+ test_type.to_hash.should eql({})
109
+ end
110
+
111
+ it "should return empty hash" do
112
+ test_type = TestType.new
113
+ test_type.to_hash.should eql({})
114
+ end
115
+
116
+ it "should convert to hash" do
117
+ test_currency = TestCurrency.new(:amount => "500")
118
+ test_currency.to_hash.should eql(:amount => "500")
119
+ end
120
+
121
+ it "should convert to hash with key as string" do
122
+ test_currency = TestCurrency.new(:amount => "500")
123
+ test_currency.to_hash(:symbol => false).should eql("amount" => "500")
124
+ end
125
+
126
+ it "should convert attribute key with @" do
127
+ test_currency = TestCurrency.new( :code => "USD", :amount => "50" )
128
+ test_currency.to_hash[:@code].should eql "USD"
129
+ end
130
+
131
+ it "should convert attribute key without @" do
132
+ test_currency = TestCurrency.new( :code => "USD", :amount => "50" )
133
+ test_currency.to_hash(:attribute => false)[:code].should eql "USD"
134
+ end
135
+
136
+ it "should convert to hash with namespace" do
137
+ test_currency = TestCurrency.new(:amount => "500", :type => "USD" )
138
+ hash = test_currency.to_hash
139
+ hash[:amount].should eql "500"
140
+ hash[:"ns:type"].should eql "USD"
141
+ hash = test_currency.to_hash(:namespace => false)
142
+ hash[:amount].should eql "500"
143
+ hash[:type].should eql "USD"
144
+ end
145
+
146
+ it "should allow namespace" do
147
+ test_currency = TestCurrency.new(:amount => "500", :"ns:type" => "USD" )
148
+ test_currency.type.should eql "USD"
149
+ end
150
+
151
+ it "should use name option in members" do
152
+ test_type = TestType.new( :firstname => "FirstName")
153
+ test_type.to_hash.should eql({:"first-name" => "FirstName" })
154
+ end
155
+
156
+ it "should allow date" do
157
+ date_time = "2010-10-10T10:10:10"
158
+ test_simple_type = TestSimpleType.new( :created_on => date_time, :created_at => date_time )
159
+ test_simple_type.created_on.should be_a Date
160
+ test_simple_type.created_at.should be_a DateTime
161
+ end
162
+
163
+ end
164
+