ruby-paypal-extended 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,17 @@
1
+ == PayPal Ruby SDK - Extended ==
2
+
3
+ This is an extension of the Ruby SDK provided by PayPal. The intention is to
4
+ make some of the function calls a little more object oriented, and a little
5
+ less hash based.
6
+
7
+ This will also be packaged as a gem instead of a Rails plugin (buried inside
8
+ a Rails project...)
9
+
10
+ == Testing ==
11
+ There is minimal testing of the basic API functionality, because it came
12
+ pre-written from PayPal, and I'm going to assume that PayPal put it together
13
+ correctly.
14
+
15
+ For new code (and refactoring) there are tests in the /test directory. To run
16
+ them, use rake:
17
+ rake test
@@ -0,0 +1,10 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ task :default => :test
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs << "test"
8
+ t.test_files = FileList['test/*test.rb']
9
+ t.verbose = true
10
+ end
@@ -0,0 +1,41 @@
1
+ require 'ruby-paypal-extended/paypal_exception'
2
+ require 'ruby-paypal-extended/profile'
3
+ require 'ruby-paypal-extended/transaction'
4
+ require 'ruby-paypal-extended/caller'
5
+ require 'ruby-paypal-extended/operations/operation'
6
+ require 'ruby-paypal-extended/operations/mass_pay'
7
+ require 'ruby-paypal-extended/ipn/notification'
8
+
9
+ module PayPalSDK
10
+
11
+ # Various configuration values
12
+ module Config
13
+ # The PayPal NVP API version that this client can talk to.
14
+ CLIENT_VERSION = "56.0"
15
+
16
+ # The name of this client
17
+ CLIENT_SOURCE = "ruby-paypal-extended"
18
+
19
+ # The hostname for the sandbox API target
20
+ SANDBOX_API_HOST = "api-3t.sandbox.paypal.com"
21
+
22
+ # The path for the sandbox API target
23
+ SANDBOX_API_PATH = "/nvp"
24
+
25
+ # The hostname for the production API target
26
+ PRODUCTION_API_HOST = "api-3t.paypal.com"
27
+
28
+ # The path for the production API target
29
+ PRODUCTION_API_PATH = "/nvp"
30
+
31
+ # The URL to the sandbox IPN endpoint
32
+ SANDBOX_IPN_URL = 'https://www.sandbox.paypal.com/cgi-bin/webscr'
33
+
34
+ # The URL to the productino IPN endpoint
35
+ PRODUCTION_IPN_URL = 'https://www.paypal.com/cgi-bin/webscr'
36
+
37
+ # The Hashes used by Profile
38
+ SANDBOX_API_ENDPOINT = {"SERVER" => SANDBOX_API_HOST, "SERVICE" => SANDBOX_API_PATH}
39
+ PRODUCTION_API_ENDPOINT = {"SERVER" => PRODUCTION_API_HOST, "SERVICE" => PRODUCTION_API_PATH}
40
+ end
41
+ end
@@ -0,0 +1,82 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'uri'
4
+ require 'cgi'
5
+ # The module has a class and a wrapper method wrapping NET:HTTP methods to simplify calling PayPal APIs.
6
+
7
+ module PayPalSDK
8
+ class Caller
9
+ attr_reader :ssl_strict
10
+
11
+ # Headers for ?
12
+ @@headers = {'Content-Type' => 'html/text'}
13
+
14
+ # Creates a new Caller object.
15
+ #
16
+ # <tt>profile</tt> - A PayPalSDK::Profile object.
17
+ def initialize(profile, ssl_verify_mode=false)
18
+ @ssl_strict = ssl_verify_mode
19
+
20
+ @profile = profile
21
+
22
+ # Some short names for readability
23
+ @pi = @profile.proxy_info
24
+ @cre = @profile.credentials
25
+ @ci = @profile.client_info
26
+ @ep = @profile.endpoints
27
+ end
28
+
29
+
30
+ # This method uses HTTP::Net library to talk to PayPal WebServices. This is the method what merchants should mostly care about.
31
+ # It expects an hash arugment which has the method name and paramter values of a particular PayPal API.
32
+ # It assumes and uses the credentials of the merchant which is the attribute value of credentials of profile class in PayPalSDKProfiles module.
33
+ # It assumes and uses the client information which is the attribute value of client_info of profile class of PayPalSDKProfiles module.
34
+ # It will also work behind a proxy server. If the calls need be to made via a proxy sever, set USE_PROXY flag to true and specify proxy server and port information in the profile class.
35
+ def call(request_hash)
36
+ req_data = request_post_data(request_hash)
37
+
38
+ if (@profile.use_proxy?)
39
+ if( @pi["USER"].nil? || @pi["PASSWORD"].nil? )
40
+ http = Net::HTTP::Proxy(@pi["ADDRESS"],@pi["PORT"]).new(@ep["SERVER"], 443)
41
+ else
42
+ http = Net::HTTP::Proxy(@pi["ADDRESS"],@pi["PORT"],@pi["USER"], @pi["PASSWORD"]).new(@ep["SERVER"], 443)
43
+ end
44
+ else
45
+ http = Net::HTTP.new(@ep["SERVER"], 443)
46
+ end
47
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE #unless ssl_strict
48
+ http.use_ssl = true;
49
+
50
+ contents, unparseddata = http.post2(@ep["SERVICE"], req_data, @headers)
51
+ data = CGI::parse(unparseddata)
52
+ transaction = Transaction.new(data)
53
+ end
54
+
55
+ # Builds the post data for sending a request to PayPal, converting
56
+ # hash values to CGI request (NVP) format.
57
+ # It expects an hash arugment which has the method name and paramter values of a particular PayPal API.
58
+ def request_post_data(request_hash)
59
+ "#{hash2cgiString(request_hash)}&#{hash2cgiString(@cre)}&#{hash2cgiString(@ci)}"
60
+ end
61
+
62
+ protected
63
+
64
+ # Method to convert a hash to a string of name and values delimited by '&' as name1=value1&name2=value2...&namen=valuen.
65
+ def hash2cgiString(h)
66
+ # Order the keys alphabetically. It's not strictly necessary but makes
67
+ # output easier to determine (helps testing)
68
+ # This requires that the keys be strings, since symbols aren't sortable
69
+ h2 = {}
70
+ h.each {|key, value| h2[key.to_s] = value}
71
+ alpha_keys = h2.keys.sort
72
+
73
+ # Escape all the values first
74
+ alpha_keys.each {|key| h2[key] = CGI::escape(h2[key].to_s) if (h2[key])}
75
+
76
+ # Create the string
77
+ alpha_keys.collect {|key| "#{key}=#{h2[key]}"}.join('&')
78
+ end
79
+
80
+ end
81
+ end
82
+
@@ -0,0 +1,194 @@
1
+ #--
2
+ # Copyright (c) 2005 Tobias Luetke
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require 'net/http'
25
+ require 'net/https'
26
+ require 'cgi'
27
+
28
+ module PayPalSDK
29
+ module IPN
30
+
31
+ # Top-level exceptin class for any IPN related exceptions.
32
+ class IPNException < PayPalException
33
+
34
+ end
35
+
36
+ # Parser and handler for incoming instant payment notifications from Paypal.
37
+ # The example shows a typical handler in a Rails application. Note that this
38
+ # is an example, please read the Paypal API documentation for all the details
39
+ # on creating a safe payment controller.
40
+ #
41
+ # Example
42
+ #
43
+ # class BackendController < ApplicationController
44
+ # def paypal_ipn
45
+ # notify = Paypal::Notification.new(request.raw_post, false)
46
+ # order = Order.find(notify.item_id)
47
+ #
48
+ # # Verify this IPN with Paypal.
49
+ # if notify.acknowledge
50
+ # # Paypal said this IPN is legit.
51
+ # begin
52
+ # if notify.complete? && order.total == notify.amount
53
+ # begin
54
+ # order.status = 'success'
55
+ # shop.ship(order)
56
+ # order.save!
57
+ # rescue => e
58
+ # order.status = 'failed'
59
+ # order.save!
60
+ # raise
61
+ # end
62
+ # else
63
+ # logger.error("We received a payment notification, but the " <<
64
+ # "payment doesn't seem to be complete. Please " <<
65
+ # "investigate. Transaction ID #{notify.transaction_id}.")
66
+ # end
67
+ # else
68
+ # # Paypal said this IPN is not correct.
69
+ # # ... log possible hacking attempt here ...
70
+ # end
71
+ #
72
+ # render :nothing => true
73
+ # end
74
+ # end
75
+ class Notification
76
+ # The IPN URL to connect to. Defaults to production
77
+ attr_accessor :ipn_url
78
+
79
+ # The parsed Paypal IPN data parameters.
80
+ attr_accessor :params
81
+
82
+ # The raw Paypal IPN data that was received.
83
+ attr_accessor :raw
84
+
85
+ # Creates a new Paypal::Notification object. As the first argument,
86
+ # pass the raw POST data that you've received from Paypal.
87
+ #
88
+ # In a Rails application this looks something like this:
89
+ #
90
+ # def paypal_ipn
91
+ # paypal = Paypal::Notification.new(request.raw_post)
92
+ # ...
93
+ # end
94
+ def initialize(post, use_production = false)
95
+ @ipn_url = use_production ? PayPalSDK::Config::PRODUCTION_IPN_URL : PayPalSDK::Config::SANDBOX_IPN_URL
96
+ empty!
97
+ parse(post)
98
+ end
99
+
100
+ # Returns the status of this transaction.
101
+ def status
102
+ params['payment_status']
103
+ end
104
+
105
+ # When was this payment received by the client.
106
+ # sometimes it can happen that we get the notification much later.
107
+ # One possible scenario is that our web application was down. In this case paypal tries several
108
+ # times an hour to inform us about the notification
109
+ def payment_date
110
+ Time.parse(params['payment_date'])
111
+ end
112
+
113
+ # Id of this transaction (paypal number)
114
+ def transaction_id
115
+ params['txn_id']
116
+ end
117
+
118
+ # What type of transaction are we dealing with?
119
+ def type
120
+ params['txn_type']
121
+ end
122
+
123
+ # This is the invocie which you passed to paypal
124
+ def test?
125
+ params['test_ipn'] == '1'
126
+ end
127
+
128
+ # reset the notification.
129
+ def empty!
130
+ @params = {}
131
+ @raw = ""
132
+ end
133
+
134
+ # Acknowledge the transaction to paypal. This method has to be called after a new
135
+ # IPN arrives. Paypal will verify that all the information we received are
136
+ # correct and will return a ok or a fail.
137
+ #
138
+ # Example:
139
+ #
140
+ # def paypal_ipn
141
+ # notify = PaypalNotification.new(request.raw_post)
142
+ #
143
+ # if notify.acknowledge
144
+ # ... process order ... if notify.complete?
145
+ # else
146
+ # ... log possible hacking attempt ...
147
+ # end
148
+ # end
149
+ def acknowledge
150
+ payload = raw
151
+
152
+ uri = URI.parse(self.ipn_url)
153
+ request_path = "#{uri.path}?cmd=_notify-validate"
154
+
155
+ request = Net::HTTP::Post.new(request_path)
156
+ request['Content-Length'] = "#{payload.size}"
157
+ request['User-Agent'] = "ruby-paypal-extended -- http://github.com/MicahWedemeyer/ruby-paypal-extended"
158
+
159
+ http = Net::HTTP.new(uri.host, uri.port)
160
+
161
+ if uri.scheme == "https"
162
+ http.use_ssl = true
163
+ # http://www.ruby-lang.org/en/news/2007/10/04/net-https-vulnerability/
164
+ if http.respond_to?(:enable_post_connection_check)
165
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
166
+ http.enable_post_connection_check = true
167
+ store = OpenSSL::X509::Store.new
168
+ store.set_default_paths
169
+ http.cert_store = store
170
+ else
171
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
172
+ end
173
+ end
174
+
175
+ request = http.request(request, payload)
176
+
177
+ raise IPNException.new("Faulty paypal result: #{request.body}") unless ["VERIFIED", "INVALID"].include?(request.body)
178
+
179
+ request.body == "VERIFIED"
180
+ end
181
+
182
+ private
183
+
184
+ # Take the posted data and move the relevant data into a hash
185
+ def parse(post)
186
+ @raw = post
187
+ for line in post.split('&')
188
+ key, value = *line.scan( %r{^(\w+)\=(.*)$} ).flatten
189
+ @params[key] = CGI.unescape(value)
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,89 @@
1
+ module PayPalSDK
2
+ module Operations
3
+
4
+ # Represents an arity mismatch where the number of receipients does
5
+ # not match all the other arguments
6
+ class MassPayArityException < PayPalException
7
+ end
8
+
9
+ # Represents the MassPay operation
10
+ class MassPay < Operation
11
+ attr_accessor :receiver_identifiers
12
+ attr_accessor :amounts
13
+ attr_accessor :receivertype
14
+ attr_accessor :currency_code
15
+ attr_accessor :email_subject
16
+ attr_accessor :unique_ids
17
+ attr_accessor :notes
18
+
19
+ # Creates a new MassPay operation.
20
+ #
21
+ # <tt>receiver_identifiers</tt> - An array of identifiers for the receivers, either
22
+ # email addresses or paypal IDs. As specified in the docs, they must all be
23
+ # one or the other.
24
+ # <tt>amounts</tt> - An array of numeric values representing the amounts to send to each recipient.
25
+ # <tt>receivertype</tt> - Must be either "UserID" or "EmailAddress". Defaults to EmailAddress
26
+ # <tt>currency_code</tt> - The 3 letter currency code
27
+ # <tt>email_subect</tt> - Subject of the email that will be sent to everyone
28
+ # <tt>unique_ids</tt> - An array of unique identifiers to attach to each transaction. Optional
29
+ # <tt>notes</tt> - An array of notes to attach to each transaction. Optional
30
+ def initialize(opts)
31
+ opts = {
32
+ :receivertype => "EmailAddress",
33
+ :currency_code => "USD"
34
+ }.merge(opts)
35
+
36
+ @receiver_identifiers = opts[:receiver_identifiers]
37
+ @amounts = opts[:amounts]
38
+ @receivertype = opts[:receivertype]
39
+ @currency_code = opts[:currency_code]
40
+ @email_subject = opts[:email_subject]
41
+ @unique_ids = opts[:unique_ids]
42
+ @notes = opts[:notes]
43
+
44
+ @n_recip = @receiver_identifiers.size
45
+ check_arity
46
+ end
47
+
48
+ protected
49
+
50
+ def call_hash
51
+ # Double-check the arity before making the hash
52
+ check_arity
53
+
54
+ h = {
55
+ :method => "MassPay",
56
+ :receivertype => @receivertype,
57
+ :currency_code => @currency_code,
58
+ :email_subject => @email_subject
59
+ }
60
+
61
+ id_key = @receivertype == "UserID" ? "l_receiverid" : "l_email"
62
+
63
+ @receiver_identifiers.each_index do |i|
64
+ h["#{id_key}#{i}".to_sym] = @receiver_identifiers[i]
65
+ h["l_amt#{i}".to_sym] = @amounts[i]
66
+ h["l_uniqueid#{i}".to_sym] = @unique_ids[i] if @unique_ids
67
+ h["l_note#{i}".to_sym] = @notes[i] if @notes
68
+ end
69
+
70
+ h
71
+ end
72
+
73
+ def check_arity
74
+ # Sloppy...
75
+ bad_sizes = []
76
+ bad_sizes << "amounts has #{@amounts.size} values" if @amounts.size != @n_recip
77
+ bad_sizes << "unique_ids has #{@unique_ids.size} values" if !@unique_ids.nil? && (@unique_ids.size != @n_recip)
78
+ bad_sizes << "notes has #{@notes.size} values" if !@notes.nil? && (@notes.size != @n_recip)
79
+
80
+ unless bad_sizes.empty?
81
+ msg = "Arity mismatch: #{@n_recip} user identifiers, but "
82
+ msg += bad_sizes.join(" and")
83
+ raise MassPayArityException.new(msg)
84
+ end
85
+ end
86
+
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,26 @@
1
+ module PayPalSDK
2
+ module Operations
3
+ # Represents a base class method that is not yet implemented.
4
+ class NotImplementedException < PayPalException
5
+ end
6
+
7
+ # Abstract base class for all the operations
8
+ class Operation
9
+
10
+ # Executes the operation and returns the response wrapped in
11
+ # a Transaction
12
+ def call(caller)
13
+ caller.call(call_hash)
14
+ end
15
+
16
+ protected
17
+
18
+ # Translates this Operation into the Hash expected by Caller
19
+ # Every Operation Class must implement this method
20
+ def call_hash
21
+ raise NotImplementedException.new("Must implement call_hash")
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,6 @@
1
+ module PayPalSDK
2
+ # Base Exception class for all execeptions generated by this library
3
+ class PayPalException < Exception
4
+
5
+ end
6
+ end
@@ -0,0 +1,35 @@
1
+ module PayPalSDK
2
+
3
+ # This class holds a merchant's API crednetials and PayPal endpoint information
4
+ class Profile
5
+
6
+ attr_accessor :credentials
7
+ attr_accessor :endpoints
8
+ attr_accessor :client_info
9
+ attr_accessor :proxy_info
10
+
11
+ # Proxy information of the client environment.
12
+ DEFAULT_PROXY_INFO = {"USE_PROXY" => false, "ADDRESS" => nil, "PORT" => nil, "USER" => nil, "PASSWORD" => nil }
13
+
14
+ # Creates a new Profile, setting it with the options provided
15
+ # Options are:
16
+ # <tt>credentials</tt> - A hash of user credentials with keys of "USER", "PWD", and "SIGNATURE"
17
+ # <tt>use_production</tt> - Set to true to interact with the production server. Defaults to false
18
+ # <tt>proxy_info</tt> - A hash of proxy server info with keys of "USE_PROXY", "ADDRESS", "PORT", "USER", "PASSWORD"
19
+ def initialize(credentials, use_production = false, proxy_info = nil)
20
+ @credentials = credentials
21
+ @proxy_info = proxy_info || DEFAULT_PROXY_INFO
22
+
23
+ @endpoints = use_production ? PayPalSDK::Config::PRODUCTION_API_ENDPOINT : PayPalSDK::Config::SANDBOX_API_ENDPOINT
24
+
25
+ @client_info = {
26
+ "VERSION" => PayPalSDK::Config::CLIENT_VERSION,
27
+ "SOURCE" => PayPalSDK::Config::CLIENT_SOURCE
28
+ }
29
+ end
30
+
31
+ def use_proxy?
32
+ @proxy_info["USE_PROXY"]
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,17 @@
1
+ module PayPalSDK
2
+ # Wrapper class to wrap response hash from PayPal as an object and to provide nice helper methods
3
+ class Transaction
4
+ def initialize(data)
5
+ @success = data["ACK"].to_s != "Failure"
6
+ @response = data
7
+ end
8
+
9
+ def success?
10
+ @success
11
+ end
12
+
13
+ def response
14
+ @response
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,44 @@
1
+ PAYPAL, INC.
2
+
3
+ SDK LICENSE
4
+
5
+ NOTICE TO USER: PayPal, Inc. is providing the Software and Documentation for use under the terms of this Agreement. Any use, reproduction, modification or distribution of the Software or Documentation, or any derivatives or portions hereof, constitutes your acceptance of this Agreement.
6
+
7
+ As used in this Agreement, "PayPal" means PayPal, Inc. "Software" means the software code accompanying this agreement. "Documentation" means the documents, specifications and all other items accompanying this Agreement other than the Software.
8
+
9
+ 1. LICENSE GRANT Subject to the terms of this Agreement, PayPal hereby grants you a non-exclusive, worldwide, royalty free license to use, reproduce, prepare derivative works from, publicly display, publicly perform, distribute and sublicense the Software for any purpose, provided the copyright notice below appears in a conspicuous location within the source code of the distributed Software and this license is distributed in the supporting documentation of the Software you distribute. Furthermore, you must comply with all third party licenses in order to use the third party software contained in the Software.
10
+
11
+ Subject to the terms of this Agreement, PayPal hereby grants you a non-exclusive, worldwide, royalty free license to use, reproduce, publicly display, publicly perform, distribute and sublicense the Documentation for any purpose. You may not modify the Documentation.
12
+
13
+ No title to the intellectual property in the Software or Documentation is transferred to you under the terms of this Agreement. You do not acquire any rights to the Software or the Documentation except as expressly set forth in this Agreement.
14
+
15
+ If you choose to distribute the Software in a commercial product, you do so with the understanding that you agree to defend, indemnify and hold harmless PayPal and its suppliers against any losses, damages and costs arising from the claims, lawsuits or other legal actions arising out of such distribution. You may distribute the Software in object code form under your own license, provided that your license agreement:
16
+
17
+ (a) complies with the terms and conditions of this license agreement;
18
+
19
+ (b) effectively disclaims all warranties and conditions, express or implied, on behalf of PayPal;
20
+
21
+ (c) effectively excludes all liability for damages on behalf of PayPal;
22
+
23
+ (d) states that any provisions that differ from this Agreement are offered by you alone and not PayPal; and
24
+
25
+ (e) states that the Software is available from you or PayPal and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.
26
+
27
+ 2. DISCLAIMER OF WARRANTY
28
+ PAYPAL LICENSES THE SOFTWARE AND DOCUMENTATION TO YOU ONLY ON AN "AS IS" BASIS WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. PAYPAL MAKES NO WARRANTY THAT THE SOFTWARE OR DOCUMENTATION WILL BE ERROR-FREE. Each user of the Software or Documentation is solely responsible for determining the appropriateness of using and distributing the Software and Documentation and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs, or equipment, and unavailability or interruption of operations. Use of the Software and Documentation is made with the understanding that PayPal will not provide you with any technical or customer support or maintenance. Some states or jurisdictions do not allow the exclusion of implied warranties or limitations on how long an implied warranty may last, so the above limitations may not apply to you. To the extent permissible, any implied warranties are limited to ninety (90) days.
29
+
30
+
31
+ 3. LIMITATION OF LIABILITY
32
+ PAYPAL AND ITS SUPPLIERS SHALL NOT BE LIABLE FOR LOSS OR DAMAGE ARISING OUT OF THIS AGREEMENT OR FROM THE USE OF THE SOFTWARE OR DOCUMENTATION. IN NO EVENT WILL PAYPAL OR ITS SUPPLIERS BE LIABLE TO YOU OR ANY THIRD PARTY FOR ANY DIRECT, INDIRECT, CONSEQUENTIAL, INCIDENTAL, OR SPECIAL DAMAGES INCLUDING LOST PROFITS, LOST SAVINGS, COSTS, FEES, OR EXPENSES OF ANY KIND ARISING OUT OF ANY PROVISION OF THIS AGREEMENT OR THE USE OR THE INABILITY TO USE THE SOFTWARE OR DOCUMENTATION, HOWEVER CAUSED AND UNDER ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. PAYPAL'S AGGREGATE LIABILITY AND THAT OF ITS SUPPLIERS UNDER OR IN CONNECTION WITH THIS AGREEMENT SHALL BE LIMITED TO THE AMOUNT PAID BY YOU FOR THE SOFTWARE AND DOCUMENTATION.
33
+
34
+ 4. TRADEMARK USAGE
35
+ PayPal is a trademark PayPal, Inc. in the United States and other countries. Such trademarks may not be used to endorse or promote any product unless expressly permitted under separate agreement with PayPal.
36
+
37
+ 5. TERM
38
+ Your rights under this Agreement shall terminate if you fail to comply with any of the material terms or conditions of this Agreement and do not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all your rights under this Agreement terminate, you agree to cease use and distribution of the Software and Documentation as soon as reasonably practicable.
39
+
40
+ 6. GOVERNING LAW AND JURISDICTION. This Agreement is governed by the statutes and laws of the State of California, without regard to the conflicts of law principles thereof. If any part of this Agreement is found void and unenforceable, it will not affect the validity of the balance of the Agreement, which shall remain valid and enforceable according to its terms. Any dispute arising out of or related to this Agreement shall be brought in the courts of Santa Clara County, California, USA.
41
+
42
+ 7. GENERAL
43
+ You acknowledge that you have read this Agreement, understand it, and that it is the complete and exclusive statement of your agreement with PayPal which supersedes any prior agreement, oral or written, between PayPal and you with respect to the licensing to you of the Software and Documentation. No variation of the terms of this Agreement will be enforceable against PayPal unless PayPal gives its express consent in writing signed by an authorized signatory of PayPal.
44
+
@@ -0,0 +1,39 @@
1
+ require 'test_helper'
2
+
3
+ class CallerTest < Test::Unit::TestCase
4
+ include PayPalSDK
5
+
6
+ def setup
7
+ @p = Profile.new(creds)
8
+ @c = Caller.new(@p)
9
+ end
10
+
11
+ def test_request_post_data
12
+ post_data = @c.request_post_data(req_hash)
13
+ assert post_data.is_a?(String)
14
+
15
+ expected = "currencycode=USD&emailsubject=You+have+received+a+payment&l_amt0=5.00&l_receiverid0=12345&method=MassPay&receivertype=UserID&PWD=QFZCWN5HZM8VBG7Q&SIGNATURE=A.d9eRKfd1yVkRrtmMfCFLTqa6M9AyodL0SJkhYztxUi8W9pCXF6.4NI&USER=sdk-three_api1.sdk.com&SOURCE=ruby-paypal-extended&VERSION=56.0"
16
+ assert_equal expected, post_data
17
+ end
18
+
19
+ protected
20
+
21
+ def creds(opts = {})
22
+ {
23
+ "USER" => "sdk-three_api1.sdk.com",
24
+ "PWD" => "QFZCWN5HZM8VBG7Q",
25
+ "SIGNATURE" => "A.d9eRKfd1yVkRrtmMfCFLTqa6M9AyodL0SJkhYztxUi8W9pCXF6.4NI"
26
+ }.merge(opts)
27
+ end
28
+
29
+ def req_hash(opts = {})
30
+ {
31
+ :method => "MassPay",
32
+ :emailsubject => "You have received a payment",
33
+ :currencycode => "USD",
34
+ :receivertype => "UserID",
35
+ :l_receiverid0 => "12345",
36
+ :l_amt0 => "5.00"
37
+ }.merge(opts)
38
+ end
39
+ end
@@ -0,0 +1,64 @@
1
+ require 'test_helper'
2
+
3
+ class MassPayTest < Test::Unit::TestCase
4
+ include PayPalSDK
5
+ include PayPalSDK::Operations
6
+
7
+ def setup
8
+ @mp = MassPay.new(mass_pay_opts)
9
+ end
10
+
11
+ def test_constructor_wrong_arity
12
+ assert_raise MassPayArityException do
13
+ opts = mass_pay_opts(:unique_ids => ["a","b"]) # Missing a unique identifier
14
+ MassPay.new(opts)
15
+ end
16
+ end
17
+
18
+ def test_call_hash
19
+ h = @mp.send(:call_hash)
20
+ assert_equal "MassPay", h[:method]
21
+
22
+ assert_equal "USD", h[:currency_code]
23
+ assert_equal "UserID", h[:receivertype]
24
+
25
+ assert_equal "1", h[:l_receiverid0]
26
+ assert_equal 1.00, h[:l_amt0]
27
+ assert_equal "a", h[:l_uniqueid0]
28
+
29
+ assert_equal "3", h[:l_receiverid2]
30
+ assert_equal 10.00, h[:l_amt2]
31
+ assert_equal "c", h[:l_uniqueid2]
32
+
33
+ assert_nil h[:l_email0]
34
+ end
35
+
36
+ def test_call_hash_email
37
+ opts = mass_pay_opts(
38
+ :receiver_identifiers => ["a@b.com", "a@c.com", "a@d.com"],
39
+ :receivertype => "EmailAddress"
40
+ )
41
+ mp = MassPay.new(opts)
42
+
43
+ h = mp.send(:call_hash)
44
+
45
+ assert_equal "EmailAddress", h[:receivertype]
46
+ assert_equal "a@b.com", h[:l_email0]
47
+ assert_equal "a@d.com", h[:l_email2]
48
+
49
+ assert_nil h[:l_receiverid0]
50
+ end
51
+
52
+ protected
53
+
54
+ def mass_pay_opts(opts = {})
55
+ {
56
+ :receiver_identifiers => ["1", "2", "3"],
57
+ :receivertype => "UserID",
58
+ :amounts => [1.00, 5.00, 10.00],
59
+ :currency_code => "USD",
60
+ :unique_ids => ["a", "b", "c"]
61
+ }.merge(opts)
62
+ end
63
+
64
+ end
@@ -0,0 +1,29 @@
1
+ class OperationTest < Test::Unit::TestCase
2
+ include PayPalSDK::Operations
3
+
4
+ def setup
5
+ @mock_caller = flexmock(:caller)
6
+
7
+ # We'll use the MassPay operation
8
+ @op = MassPay.new(mass_pay_opts)
9
+ end
10
+
11
+ def test_call
12
+ vals = @op.send(:call_hash)
13
+ @mock_caller.should_receive(:call).with(vals).once.and_return(true)
14
+
15
+ @op.call(@mock_caller)
16
+ end
17
+
18
+ protected
19
+
20
+ def mass_pay_opts(opts = {})
21
+ {
22
+ :receiver_identifiers => ["1", "2", "3"],
23
+ :receiver_type => "UserID",
24
+ :amounts => [1.00, 5.00, 10.00],
25
+ :currency_code => "USD",
26
+ :unique_ids => ["a", "b", "c"]
27
+ }.merge(opts)
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ require 'test_helper'
2
+
3
+ class ProfileTest < Test::Unit::TestCase
4
+ include PayPalSDK
5
+
6
+ def setup
7
+ @p = Profile.new(creds)
8
+ end
9
+
10
+ def test_constructor
11
+ @p = Profile.new(
12
+ creds,
13
+ true,
14
+ {"USE_PROXY" => true, "ADDRESS" => "foo", "PORT" => 99, "USER" => nil, "PASSWORD" => nil }
15
+ )
16
+
17
+ assert @p.use_proxy?
18
+ assert_equal 99, @p.proxy_info["PORT"]
19
+ assert_equal "api-3t.paypal.com", @p.endpoints["SERVER"]
20
+ end
21
+
22
+ protected
23
+
24
+ def creds(opts = {})
25
+ {
26
+ "USER" => "sdk-three_api1.sdk.com",
27
+ "PWD" => "QFZCWN5HZM8VBG7Q",
28
+ "SIGNATURE" => "A.d9eRKfd1yVkRrtmMfCFLTqa6M9AyodL0SJkhYztxUi8W9pCXF6.4NI"
29
+ }.merge(opts)
30
+ end
31
+ end
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'flexmock/test_unit'
4
+ require 'ruby-paypal-extended'
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-paypal-extended
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 5
8
+ - 1
9
+ version: 0.5.1
10
+ platform: ruby
11
+ authors:
12
+ - Micah Wedemeyer
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2009-05-10 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: ruby-paypal-extended is a Ruby library for interacting with PayPal via the NVP (Name Value Pair) REST interface.
22
+ email: micah@aisleten.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - lib/ruby-paypal-extended/caller.rb
31
+ - lib/ruby-paypal-extended/ipn/notification.rb
32
+ - lib/ruby-paypal-extended/operations/mass_pay.rb
33
+ - lib/ruby-paypal-extended/operations/operation.rb
34
+ - lib/ruby-paypal-extended/paypal_exception.rb
35
+ - lib/ruby-paypal-extended/profile.rb
36
+ - lib/ruby-paypal-extended/transaction.rb
37
+ - lib/ruby-paypal-extended.rb
38
+ - licenses/LICENSE.txt
39
+ - Rakefile
40
+ - README
41
+ - test/caller_test.rb
42
+ - test/mass_pay_test.rb
43
+ - test/operation_test.rb
44
+ - test/profile_test.rb
45
+ - test/test_helper.rb
46
+ has_rdoc: true
47
+ homepage: http://github.com/MicahWedemeyer/ruby-paypal-extended
48
+ licenses: []
49
+
50
+ post_install_message:
51
+ rdoc_options: []
52
+
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ segments:
67
+ - 0
68
+ version: "0"
69
+ requirements: []
70
+
71
+ rubyforge_project:
72
+ rubygems_version: 1.3.6
73
+ signing_key:
74
+ specification_version: 3
75
+ summary: A library for interfacing with PayPal.
76
+ test_files: []
77
+