paypal 1.0.1 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/README +45 -7
  2. data/Rakefile +17 -113
  3. data/lib/helper.rb +187 -38
  4. data/lib/notification.rb +56 -10
  5. data/lib/paypal.rb +1 -6
  6. metadata +33 -27
data/README CHANGED
@@ -6,19 +6,19 @@ business account and tell paypal where to send the IPN ( Instant payment notific
6
6
 
7
7
  == Download
8
8
 
9
- * Preferred method of installation is using rubygems. gem install --source http://dist.leetsoft.com paypal
9
+ * Preferred method of installation is using rubygems. gem install paypal --source http://dist.leetsoft.com
10
10
  * Alternatively you can get the library packaged at http://dist.leetsoft.com/pkg/
11
11
 
12
12
  == Requirements
13
13
 
14
- * Ruby 1.8.2 (may work with previous versions) With Open SSL support compiled in.
14
+ * Ruby 1.8.2 (may work with previous versions) With OpenSSL support compiled in.
15
15
  * Valid paypal business account.
16
- * The money library from http://dist.leetsoft.com/api/money
16
+ * (optional) The money library from http://dist.leetsoft.com/api/money
17
17
 
18
18
  == Installation
19
19
 
20
- 1) Either install the rubygem for this library ( rake install from source
21
- or gem install --source <url> paypal using gems directly )
20
+ 1) Either install the rubygem for this library (rake install from source) or use gems directly:
21
+ gem install paypal --source http://dist.leetsoft.com/
22
22
 
23
23
  2) Create a new controller which handels the paypal related tasks.
24
24
  script/generate controller payment
@@ -27,6 +27,7 @@ business account and tell paypal where to send the IPN ( Instant payment notific
27
27
  module PaymentHelper
28
28
  include Paypal::Helpers
29
29
  end
30
+
30
31
  4) Create a paypal_ipn ( or similar ) action like the one in the "Example rails controller" appendix.
31
32
 
32
33
  Within the new payment controller you can now create pages from which users can be sent to paypal. You always
@@ -61,19 +62,56 @@ sandbox account to use while the application is running in development mode.
61
62
  == Example paypal forward page
62
63
 
63
64
  <%= paypal_form_tag %>
64
- <%= paypal_setup "Item 500", Money.us_dollar(50000), "bob@bigbusiness.com", :notify_url => url_for(:path_only => false, :action => 'paypal_ipn') %>
65
+ <%= paypal_setup "Item 500", Money.us_dollar(50000), "bob@bigbusiness.com", :notify_url => url_for(:only_path => false, :action => 'paypal_ipn') %>
65
66
 
66
67
  Please press here to pay $500US using paypal. <br/>
67
68
  <%= submit_tag "Go to paypal >>" %>
68
69
 
69
70
  <% end_form_tag %>
70
71
 
72
+ == Using encrypted form data
73
+
74
+ Paypal supports encrypted form data to prevent tampering by third parties.
75
+ You must have a verified paypal account to use this functionality.
76
+
77
+ 1) Create a private key for yourself
78
+
79
+ openssl genrsa -out business_key.prm 1024
80
+
81
+ 2) Create a public certificate to share with Paypal
82
+
83
+ openssl req -new -key business_key.pem -x509 -days 3650 -out business_cert.pem
84
+
85
+ 3) Upload the public certificate to Paypal (under Profile -> Encrypted Payment Settings -> Your Public Certificates -> Add),
86
+ and note the "Cert ID" that Paypal shows for the certificate.
87
+
88
+ 4) Update your controller to include the details for your key and certificate.
89
+
90
+ @business_key = File::read("business_key.pem")
91
+ @business_cert = File::read("business_cert.pem")
92
+ @business_certid = "certid from paypal"
93
+
94
+ 5) Update your views to populate the :business_key, :business_cert and :business_certid options in 'paypal_setup' - the rest of the signature is the same.
95
+
96
+ 6) When you're ready to go live, download the production Paypal certificate and override the default certificate.
97
+
98
+ Paypal::Notification.paypal_cert = File::read("paypal_cert.pem")
99
+
71
100
  == Troubleshooting
72
101
 
73
102
  uninitalized constant Paypal - Make sure your ruby has openssl support
74
103
 
75
104
  == Changelog
76
105
 
106
+ 2006-02-10 -- 1.5.1
107
+ * added complete list of valid paypal options (Paul Hart)
108
+
109
+ 2006-02-02 -- 1.5.0
110
+ * Now report an error when invalid option is passed to paypal_setup
111
+ * Had to rename parameters cancel_url to cancel_return and return_url to return, please update your app
112
+ * Improved the test coverage strategy for helper tests
113
+ * Added support for encrypted form data (Paul Hart)
114
+
77
115
  2005-09-16 -- 0.9.6
78
116
  * Added readme note about the openssl requirement
79
117
 
@@ -87,4 +125,4 @@ uninitalized constant Paypal - Make sure your ruby has openssl support
87
125
  2005-07-22 -- 0.9.1
88
126
  * support for cancel_url as well as notify_url. This means you can now set the IPN callback address from the paypal_setup method and
89
127
  you don't have to do that in the paypal admin interface!
90
- * Removed the actual form tag from the paypal_setup generated code to conform better with docs
128
+ * Removed the actual form tag from the paypal_setup generated code to conform better with docs
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ require 'rake/rdoctask'
5
5
  require 'rake/gempackagetask'
6
6
  require 'rake/contrib/rubyforgepublisher'
7
7
 
8
- PKG_VERSION = "1.0.1"
8
+ PKG_VERSION = "1.8.0"
9
9
  PKG_NAME = "paypal"
10
10
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
11
11
 
@@ -19,17 +19,25 @@ PKG_FILES = FileList[
19
19
  ].exclude(/\bCVS\b|~$/)
20
20
 
21
21
  desc "Default Task"
22
- task :default => [ :test ]
22
+ task :default => [ :test, :test_remote ]
23
23
 
24
24
  desc "Delete tar.gz / zip / rdoc"
25
25
  task :cleanup => [ :rm_packages, :clobber_rdoc ]
26
26
 
27
27
  # Run the unit tests
28
- Rake::TestTask.new("test") { |t|
28
+ Rake::TestTask.new :test do |t|
29
29
  t.libs << "test"
30
30
  t.pattern = 'test/*_test.rb'
31
+ t.ruby_opts << '-rubygems'
31
32
  t.verbose = false
32
- }
33
+ end
34
+
35
+ Rake::TestTask.new :test_remote do |t|
36
+ t.libs << "test"
37
+ t.pattern = 'test/remote/*_test.rb'
38
+ t.ruby_opts << '-rubygems'
39
+ t.verbose = false
40
+ end
33
41
 
34
42
  desc "Create a rubygem and install it. Might need root rights"
35
43
  task :install => [:package] do
@@ -107,115 +115,11 @@ RELEASE_NAME = "REL #{PKG_VERSION}"
107
115
 
108
116
  desc "Publish the release files to RubyForge."
109
117
  task :release => [:gem] do
110
- files = ["gem"].map { |ext| "pkg/#{PKG_FILE_NAME}.#{ext}" }
111
-
112
- if RUBY_FORGE_PROJECT then
113
- require 'net/http'
114
- require 'open-uri'
115
-
116
- project_uri = "http://rubyforge.org/projects/#{RUBY_FORGE_PROJECT}/"
117
- project_data = open(project_uri) { |data| data.read }
118
- group_id = project_data[/[?&]group_id=(\d+)/, 1]
119
- raise "Couldn't get group id" unless group_id
120
-
121
- # This echos password to shell which is a bit sucky
122
- if ENV["RUBY_FORGE_PASSWORD"]
123
- password = ENV["RUBY_FORGE_PASSWORD"]
124
- else
125
- print "#{RUBY_FORGE_USER}@rubyforge.org's password: "
126
- password = STDIN.gets.chomp
127
- end
118
+ `rubyforge login`
128
119
 
129
- login_response = Net::HTTP.start("rubyforge.org", 80) do |http|
130
- data = [
131
- "login=1",
132
- "form_loginname=#{RUBY_FORGE_USER}",
133
- "form_pw=#{password}"
134
- ].join("&")
135
- http.post("/account/login.php", data)
136
- end
137
-
138
- cookie = login_response["set-cookie"]
139
- raise "Login failed" unless cookie
140
- headers = { "Cookie" => cookie }
141
-
142
- release_uri = "http://rubyforge.org/frs/admin/?group_id=#{group_id}"
143
- release_data = open(release_uri, headers) { |data| data.read }
144
- package_id = release_data[/[?&]package_id=(\d+)/, 1]
145
- raise "Couldn't get package id" unless package_id
146
-
147
- first_file = true
148
- release_id = ""
149
-
150
- files.each do |filename|
151
- basename = File.basename(filename)
152
- file_ext = File.extname(filename)
153
- file_data = File.open(filename, "rb") { |file| file.read }
154
-
155
- puts "Releasing #{basename}..."
156
-
157
- release_response = Net::HTTP.start("rubyforge.org", 80) do |http|
158
- release_date = Time.now.strftime("%Y-%m-%d %H:%M")
159
- type_map = {
160
- ".zip" => "3000",
161
- ".tgz" => "3110",
162
- ".gz" => "3110",
163
- ".gem" => "1400"
164
- }; type_map.default = "9999"
165
- type = type_map[file_ext]
166
- boundary = "rubyqMY6QN9bp6e4kS21H4y0zxcvoor"
167
-
168
- query_hash = if first_file then
169
- {
170
- "group_id" => group_id,
171
- "package_id" => package_id,
172
- "release_name" => RELEASE_NAME,
173
- "release_date" => release_date,
174
- "type_id" => type,
175
- "processor_id" => "8000", # Any
176
- "release_notes" => "",
177
- "release_changes" => "",
178
- "preformatted" => "1",
179
- "submit" => "1"
180
- }
181
- else
182
- {
183
- "group_id" => group_id,
184
- "release_id" => release_id,
185
- "package_id" => package_id,
186
- "step2" => "1",
187
- "type_id" => type,
188
- "processor_id" => "8000", # Any
189
- "submit" => "Add This File"
190
- }
191
- end
192
-
193
- query = "?" + query_hash.map do |(name, value)|
194
- [name, URI.encode(value)].join("=")
195
- end.join("&")
196
-
197
- data = [
198
- "--" + boundary,
199
- "Content-Disposition: form-data; name=\"userfile\"; filename=\"#{basename}\"",
200
- "Content-Type: application/octet-stream",
201
- "Content-Transfer-Encoding: binary",
202
- "", file_data, ""
203
- ].join("\x0D\x0A")
204
-
205
- release_headers = headers.merge(
206
- "Content-Type" => "multipart/form-data; boundary=#{boundary}"
207
- )
208
-
209
- target = first_file ? "/frs/admin/qrs.php" : "/frs/admin/editrelease.php"
210
- http.post(target + query, data, release_headers)
211
- end
212
-
213
- if first_file then
214
- release_id = release_response.body[/release_id=(\d+)/, 1]
215
- raise("Couldn't get release id") unless release_id
216
- end
217
-
218
- first_file = false
219
- end
120
+ for ext in %w( gem tgz zip )
121
+ release_command = "rubyforge add_release #{PKG_NAME} #{PKG_NAME} 'REL #{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}"
122
+ puts release_command
123
+ system(release_command)
220
124
  end
221
125
  end
@@ -17,8 +17,8 @@ module Paypal
17
17
 
18
18
  # Convenience helper. Can replace <%= form_tag Paypal::Notification.ipn_url %>
19
19
  # takes optional url parameter, default is Paypal::Notification.ipn_url
20
- def paypal_form_tag(url = Paypal::Notification.ipn_url)
21
- form_tag(url)
20
+ def paypal_form_tag(url = Paypal::Notification.ipn_url, options = {})
21
+ form_tag(url, options)
22
22
  end
23
23
 
24
24
  # This helper creates the hidden form data which is needed for a paypal purchase.
@@ -33,77 +33,226 @@ module Paypal
33
33
  #
34
34
  # * <tt>business</tt> -- This is your paypal account name ( a email ). This needs to be a valid paypal business account.
35
35
  #
36
- # The last parameter is a options hash. You can override several things:
36
+ # The last parameter is a options hash. You can set or override any Paypal-recognized parameter, including:
37
37
  #
38
- # * <tt>:notify_url</tt> -- default is nil. Supply an url which paypal will send its IPN notification to once a
39
- # purchase is made, canceled or any other status changes occure.
40
- # * <tt>:return_url</tt> -- default is nil. If provided paypal will redirect a user back to this url after a
41
- # successful purchase. Useful for a kind of thankyou page.
42
- # * <tt>:cancel_url</tt> -- default is nil. If provided paypal will redirect a user back to this url when
43
- # the user cancels the purchase.
38
+ # * <tt>:cmd</tt> -- default is '_xclick'.
39
+ # * <tt>:quantity</tt> -- default is '1'.
40
+ # * <tt>:no_note</tt> -- default is '1'.
44
41
  # * <tt>:item_name</tt> -- default is 'Store purchase'. This is the name of the purchase which will be displayed
45
42
  # on the paypal page.
46
43
  # * <tt>:no_shipping</tt> -- default is '1'. By default we tell paypal that no shipping is required. Usually
47
44
  # the shipping address should be collected in our application, not by paypal.
48
- # * <tt>:no_note</tt> -- default is '1'
49
- # * <tt>:currency</tt> -- default is 'USD'
45
+ # * <tt>:currency</tt> -- default is 'USD'. If you provide a Money object, that will automatically override
46
+ # the value.
47
+ # * <tt>:charset</tt> -- default is 'utf-8'.
48
+ # * <tt>:notify_url</tt> -- If provided paypal will send its IPN notification once a
49
+ # purchase is made, canceled or any other status changes occur.
50
+ # * <tt>:return</tt> -- If provided paypal will redirect a user back to this url after a
51
+ # successful purchase. Useful for a kind of thankyou page.
52
+ # * <tt>:cancel_return</tt> -- If provided paypal will redirect a user back to this url when
53
+ # the user cancels the purchase.
50
54
  # * <tt>:tax</tt> -- the tax for the store purchase. Same format as the amount parameter but optional
51
55
  # * <tt>:invoice</tt> -- Unique invoice number. User will never see this. optional
52
56
  # * <tt>:custom</tt> -- Custom field. User will never see this. optional
53
- # * <tt>:no_utf8</tt> -- if set to false this prevents the charset = utf-8 hidden field. (I don't know why you
54
- # would want to disable this... )
55
57
  #
58
+ # Generating encrypted form data
59
+ #
60
+ # The helper also supports the generation of encrypted button data. Please see the README for more information
61
+ # on the setup and prerequisite steps for using encrypted forms.
62
+ #
63
+ # The following options must all be provided (as strings) to encrypt the data:
64
+ #
65
+ # * <tt>:business_key</tt> -- The private key you have generated
66
+ # * <tt>:business_cert</tt> -- The public certificate you have also uploaded to Paypal
67
+ # * <tt>:business_certid</tt> -- The certificate ID that Paypal has assigned to your certificate.
68
+ #
56
69
  # Examples:
57
70
  #
58
71
  # <%= paypal_setup @order.id, Money.us_dollar(50000), "bob@bigbusiness.com" %>
59
72
  # <%= paypal_setup @order.id, '50.00', "bob@bigbusiness.com", :currency => 'USD' %>
60
73
  # <%= paypal_setup @order.id, '50.00', "bob@bigbusiness.com", :currency => 'USD', :notify_url => url_for(:only_path => false, :action => 'paypal_ipn') %>
61
74
  # <%= paypal_setup @order.id, Money.ca_dollar(50000), "bob@bigbusiness.com", :item_name => 'Snowdevil shop purchase', :return_url => paypal_return_url, :cancel_url => paypal_cancel_url, :notify_url => paypal_ipn_url %>
75
+ # <%= paypal_setup @order.id, Money.ca_dollar(50000), "bob@bigbusiness.com", :item_name => 'Snowdevil shop purchase', :return_url => paypal_return_url, :cancel_url => paypal_cancel_url, :business_key => @business_key, :business_cert => @business_cert, :business_certid => @business_certid %>
62
76
  #
63
77
  def paypal_setup(item_number, amount, business, options = {})
78
+
79
+ misses = (options.keys - valid_setup_options)
80
+ raise ArgumentError, "Unknown option #{misses.inspect}" if not misses.empty?
64
81
 
65
82
  params = {
83
+ :cmd => "_xclick",
84
+ :quantity => 1,
85
+ :business => business,
86
+ :item_number => item_number,
66
87
  :item_name => 'Store purchase',
67
88
  :no_shipping => '1',
68
89
  :no_note => '1',
69
- :currency => 'USD',
70
- :return_url => nil
90
+ :charset => 'utf-8'
71
91
  }.merge(options)
92
+
93
+ params[:currency_code] = amount.currency if amount.respond_to?(:currency)
94
+ params[:currency_code] = params.delete(:currency) if params[:currency]
95
+ params[:currency_code] ||= 'USD'
72
96
 
73
- # We accept both, strings and money objects as amount
97
+ # We accept both strings and money objects as amount
74
98
  amount = amount.cents.to_f / 100.0 if amount.respond_to?(:cents)
75
- amount = sprintf("%.2f", amount)
99
+ params[:amount] = sprintf("%.2f", amount)
76
100
 
77
101
  # same for tax
78
102
  tax = params[:tax]
79
103
  if tax
80
104
  tax = tax.cents.to_f / 100.0 if tax.respond_to?(:cents)
81
- tax = sprintf("%.2f", tax)
105
+ params[:tax] = sprintf("%.2f", tax)
82
106
  end
83
-
107
+
108
+ # look for encryption parameters, save them outsite the parameter hash.
109
+ business_key = params.delete(:business_key)
110
+ business_cert = params.delete(:business_cert)
111
+ business_certid = params.delete(:business_certid)
112
+
84
113
  # Build the form
85
114
  returning button = [] do
86
- button << tag(:input, :type => 'hidden', :name => 'cmd', :value => "_xclick")
87
- button << tag(:input, :type => 'hidden', :name => 'quantity', :value => 1)
88
- button << tag(:input, :type => 'hidden', :name => 'business', :value => business)
89
- button << tag(:input, :type => 'hidden', :name => 'amount', :value => amount)
90
- button << tag(:input, :type => 'hidden', :name => 'item_number', :value => item_number)
91
- button << tag(:input, :type => 'hidden', :name => 'item_name', :value => params[:item_name])
92
- button << tag(:input, :type => 'hidden', :name => 'no_shipping', :value => params[:no_shipping])
93
- button << tag(:input, :type => 'hidden', :name => 'no_note', :value => params[:no_note])
94
- button << tag(:input, :type => 'hidden', :name => 'return', :value => params[:return_url]) if params[:return_url]
95
- button << tag(:input, :type => 'hidden', :name => 'notify_url', :value => params[:notify_url]) if params[:notify_url]
96
- button << tag(:input, :type => 'hidden', :name => 'cancel_return', :value => params[:cancel_url]) if params[:cancel_url]
97
- button << tag(:input, :type => 'hidden', :name => 'tax', :value => tax) if tax
98
- button << tag(:input, :type => 'hidden', :name => 'invoice', :value => params[:invoice]) if params[:invoice]
99
- button << tag(:input, :type => 'hidden', :name => 'custom', :value => params[:custom]) if params[:custom]
115
+ # Only attempt an encrypted form if we have all the required fields.
116
+ if business_key and business_cert and business_certid
117
+ require 'openssl'
118
+
119
+ # Convert the key and certificates into OpenSSL-friendly objects.
120
+ paypal_cert = OpenSSL::X509::Certificate.new(Paypal::Notification.paypal_cert)
121
+ business_key = OpenSSL::PKey::RSA.new(business_key)
122
+ business_cert = OpenSSL::X509::Certificate.new(business_cert)
123
+ # Put the certificate ID back into the parameter hash the way Paypal wants it.
124
+ params[:cert_id] = business_certid
125
+
126
+ # Prepare a string of data for encryption
127
+ data = ""
128
+ params.each_pair {|k,v| data << "#{k}=#{v}\n"}
100
129
 
101
- # if amount was a object of type money or something compatible we will use its currency,
102
- # otherwise get the currency from the options. default is USD
103
- button << tag(:input, :type => 'hidden', :name => 'currency_code', :value => amount.respond_to?(:currency) ? amount.currency : params[:currency])
104
- button << tag(:input, :type => 'hidden', :name => 'charset', :value => 'utf-8') unless params[:no_utf8]
130
+ # Sign the data with our key/certificate pair
131
+ signed = OpenSSL::PKCS7::sign(business_cert, business_key, data, [], OpenSSL::PKCS7::BINARY)
132
+ # Encrypt the signed data with Paypal's public certificate.
133
+ encrypted = OpenSSL::PKCS7::encrypt([paypal_cert], signed.to_der, OpenSSL::Cipher::Cipher::new("DES3"), OpenSSL::PKCS7::BINARY)
134
+
135
+ # The command for encrypted forms is always '_s-xclick'; the real command is in the encrypted data.
136
+ button << tag(:input, :type => 'hidden', :name => 'cmd', :value => "_s-xclick")
137
+ button << tag(:input, :type => 'hidden', :name => 'encrypted', :value => encrypted)
138
+ else
139
+ # Just emit all the parameters that we have as hidden fields.
140
+ # Note that the sorting isn't really needed, but it makes testing a lot easier for now.
141
+ params.each do |key, value|
142
+ button << tag(:input, :type => 'hidden', :name => key, :value => value)
143
+ end
144
+ end
105
145
  end.join("\n")
106
146
  end
107
147
 
148
+ private
149
+
150
+ # See https://www.paypal.com/IntegrationCenter/ic_std-variable-reference.html for details on the following options.
151
+ def valid_setup_options
152
+ [
153
+ # Generic Options
154
+ :cmd,
155
+ # IPN Support
156
+ :notify_url,
157
+ # Item Information
158
+ :item_name,
159
+ :quantity,
160
+ :undefined_quantity,
161
+ :on0,
162
+ :os0,
163
+ :on1,
164
+ :os1,
165
+ # Display Information
166
+ :add,
167
+ :cancel_return,
168
+ :cbt,
169
+ :cn,
170
+ :cpp_header_image,
171
+ :cpp_headerback_color,
172
+ :cpp_headerborder_color,
173
+ :cpp_payflow_color,
174
+ :cs,
175
+ :display,
176
+ :image_url,
177
+ :no_note,
178
+ :no_shipping,
179
+ :page_style,
180
+ :return,
181
+ :rm,
182
+ # Transaction Information
183
+ :address_override,
184
+ :currency,
185
+ :currency_code,
186
+ :custom,
187
+ :handling,
188
+ :invoice,
189
+ :redirect_cmd,
190
+ :shipping,
191
+ :tax,
192
+ :tax_cart,
193
+ # Shopping Cart Options
194
+ :amount,
195
+ :business,
196
+ :handling_cart,
197
+ :paymentaction,
198
+ :rupload,
199
+ :charset,
200
+ :upload,
201
+ # Prepopulating PayPal FORMs or Address Overriding
202
+ :address1,
203
+ :address2,
204
+ :city,
205
+ :country,
206
+ :email,
207
+ :first_name,
208
+ :last_name,
209
+ :lc,
210
+ :night_phone_a,
211
+ :night_phone_b,
212
+ :night_phone_c,
213
+ :state,
214
+ :zip,
215
+ # Prepopulating Business Account Sign-up
216
+ :business_address1,
217
+ :business_address2,
218
+ :business_city,
219
+ :business_state,
220
+ :business_country,
221
+ :business_cs_email,
222
+ :business_cs_phone_a,
223
+ :business_cs_phone_b,
224
+ :business_cs_phone_c,
225
+ :business_url,
226
+ :business_night_phone_a,
227
+ :business_night_phone_b,
228
+ :business_night_phone_c,
229
+ # End of list from https://www.paypal.com/IntegrationCenter/ic_std-variable-reference.html
230
+ # The following items are known to exist but are not yet on the above page.
231
+ :business_zip,
232
+ :day_phone_a,
233
+ :day_phone_b,
234
+ :day_phone_c,
235
+ # Subscription Options
236
+ :a1, # Trial Amount 1
237
+ :p1, # Trial Period 1
238
+ :t1, # Trial Period 1 Units (D=days, W=weeks, M=months, Y=years)
239
+ :a2, # Trial Amount 2
240
+ :p2, # Trial Period 2
241
+ :t2, # Trial Period 2 Units
242
+ :a3, # Regular Subscription Amount
243
+ :p3, # Regular Subscription Period
244
+ :t3, # Regular Subscription Period Units
245
+ :src, # Recurring Payments? (1=yes, default=0)
246
+ :sra, # Reattempt Transaction on Failure? (1=yes, default=0)
247
+ :srt, # Recurring Times (number of renewals before auto-cancel, default=forever)
248
+ :usr_manage, # Username and Password Generator? (1=yes, default=0)
249
+ :modify, # Modification Behaviour (0=new subs only, 1=new or modify, 2=modify existing only, default=0)
250
+ # Encryption Options - used internally only.
251
+ :business_key, # Your private key
252
+ :business_cert, # Your public certificate
253
+ :business_certid # Your public certificate ID (from Paypal)
254
+ ]
255
+ end
256
+
108
257
  end
109
- end
258
+ end
@@ -48,7 +48,38 @@ module Paypal
48
48
  # Paypal::Notification.ipn_url = http://www.paypal.com/cgi-bin/webscr
49
49
  #
50
50
  cattr_accessor :ipn_url
51
- @@ipn_url = 'http://www.sandbox.paypal.com/cgi-bin/webscr'
51
+ @@ipn_url = 'https://www.sandbox.paypal.com/cgi-bin/webscr'
52
+
53
+
54
+ # Overwrite this certificate. It contains the Paypal sandbox certificate by default.
55
+ #
56
+ # Example:
57
+ # Paypal::Notification.paypal_cert = File::read("paypal_cert.pem")
58
+ cattr_accessor :paypal_cert
59
+ @@paypal_cert = """
60
+ -----BEGIN CERTIFICATE-----
61
+ MIIDoTCCAwqgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBmDELMAkGA1UEBhMCVVMx
62
+ EzARBgNVBAgTCkNhbGlmb3JuaWExETAPBgNVBAcTCFNhbiBKb3NlMRUwEwYDVQQK
63
+ EwxQYXlQYWwsIEluYy4xFjAUBgNVBAsUDXNhbmRib3hfY2VydHMxFDASBgNVBAMU
64
+ C3NhbmRib3hfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMB4XDTA0
65
+ MDQxOTA3MDI1NFoXDTM1MDQxOTA3MDI1NFowgZgxCzAJBgNVBAYTAlVTMRMwEQYD
66
+ VQQIEwpDYWxpZm9ybmlhMREwDwYDVQQHEwhTYW4gSm9zZTEVMBMGA1UEChMMUGF5
67
+ UGFsLCBJbmMuMRYwFAYDVQQLFA1zYW5kYm94X2NlcnRzMRQwEgYDVQQDFAtzYW5k
68
+ Ym94X2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbTCBnzANBgkqhkiG
69
+ 9w0BAQEFAAOBjQAwgYkCgYEAt5bjv/0N0qN3TiBL+1+L/EjpO1jeqPaJC1fDi+cC
70
+ 6t6tTbQ55Od4poT8xjSzNH5S48iHdZh0C7EqfE1MPCc2coJqCSpDqxmOrO+9QXsj
71
+ HWAnx6sb6foHHpsPm7WgQyUmDsNwTWT3OGR398ERmBzzcoL5owf3zBSpRP0NlTWo
72
+ nPMCAwEAAaOB+DCB9TAdBgNVHQ4EFgQUgy4i2asqiC1rp5Ms81Dx8nfVqdIwgcUG
73
+ A1UdIwSBvTCBuoAUgy4i2asqiC1rp5Ms81Dx8nfVqdKhgZ6kgZswgZgxCzAJBgNV
74
+ BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMREwDwYDVQQHEwhTYW4gSm9zZTEV
75
+ MBMGA1UEChMMUGF5UGFsLCBJbmMuMRYwFAYDVQQLFA1zYW5kYm94X2NlcnRzMRQw
76
+ EgYDVQQDFAtzYW5kYm94X2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNv
77
+ bYIBADAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAFc288DYGX+GX2+W
78
+ P/dwdXwficf+rlG+0V9GBPJZYKZJQ069W/ZRkUuWFQ+Opd2yhPpneGezmw3aU222
79
+ CGrdKhOrBJRRcpoO3FjHHmXWkqgbQqDWdG7S+/l8n1QfDPp+jpULOrcnGEUY41Im
80
+ jZJTylbJQ1b5PBBjGiP0PpK48cdF
81
+ -----END CERTIFICATE-----
82
+ """
52
83
 
53
84
  # Creates a new paypal object. Pass the raw html you got from paypal in.
54
85
  # In a rails application this looks something like this
@@ -124,8 +155,10 @@ module Paypal
124
155
  # This combines the gross and currency and returns a proper Money object.
125
156
  # this requires the money library located at http://dist.leetsoft.com/api/money
126
157
  def amount
158
+ require 'money'
127
159
  amount = gross.sub(/[^\d]/, '').to_i
128
- Money.new(amount, currency)
160
+ return Money.new(amount, currency) rescue ArgumentError
161
+ return Money.new(amount) # maybe you have an own money object which doesn't take a currency?
129
162
  end
130
163
 
131
164
  # reset the notification.
@@ -148,13 +181,26 @@ module Paypal
148
181
  # else
149
182
  # ... log possible hacking attempt ...
150
183
  # end
151
- def acknowledge
184
+ def acknowledge
185
+ payload = raw
186
+
152
187
  uri = URI.parse(self.class.ipn_url)
153
- status = nil
154
- Net::HTTP.start(uri.host, uri.port) do |request|
155
- status = request.post(uri.path, raw + "&cmd=_notify-validate").body
156
- end
157
- status == "VERIFIED"
188
+ request_path = "#{uri.path}?cmd=_notify-validate"
189
+
190
+ request = Net::HTTP::Post.new(request_path)
191
+ request['Content-Length'] = "#{payload.size}"
192
+ request['User-Agent'] = "paypal-ruby -- http://rubyforge.org/projects/paypal/"
193
+
194
+ http = Net::HTTP.new(uri.host, uri.port)
195
+
196
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE unless @ssl_strict
197
+ http.use_ssl = true
198
+
199
+ request = http.request(request, payload)
200
+
201
+ raise StandardError.new("Faulty paypal result: #{request.body}") unless ["VERIFIED", "INVALID"].include?(request.body)
202
+
203
+ request.body == "VERIFIED"
158
204
  end
159
205
 
160
206
  private
@@ -165,8 +211,8 @@ module Paypal
165
211
  for line in post.split('&')
166
212
  key, value = *line.scan( %r{^(\w+)\=(.*)$} ).flatten
167
213
  params[key] = CGI.unescape(value)
168
- end
214
+ end
169
215
  end
170
216
 
171
217
  end
172
- end
218
+ end
@@ -24,13 +24,8 @@
24
24
  require 'cgi'
25
25
  require 'net/http'
26
26
  require 'net/https'
27
+ require 'active_support'
27
28
 
28
- begin
29
- require 'money'
30
- rescue LoadError
31
- require 'rubygems'
32
- require_gem 'money'
33
- end
34
29
 
35
30
  require File.dirname(__FILE__) + '/notification'
36
31
  require File.dirname(__FILE__) + '/helper'
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.10
2
+ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: paypal
5
5
  version: !ruby/object:Gem::Version
6
- version: 1.0.1
7
- date: 2005-09-17
6
+ version: 1.8.0
7
+ date: 2006-04-09 00:00:00 -04:00
8
8
  summary: Paypal IPN integration library for rails and other web applications
9
9
  require_paths:
10
- - lib
10
+ - lib
11
11
  email: tobi@leetsoft.com
12
12
  homepage: http://dist.leetsoft.com/api/paypal
13
13
  rubyforge_project:
@@ -18,37 +18,43 @@ bindir: bin
18
18
  has_rdoc: true
19
19
  required_ruby_version: !ruby/object:Gem::Version::Requirement
20
20
  requirements:
21
- -
22
- - ">"
23
- - !ruby/object:Gem::Version
24
- version: 0.0.0
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
25
24
  version:
26
25
  platform: ruby
26
+ signing_key:
27
+ cert_chain:
27
28
  authors:
28
- - Tobias Luetke
29
+ - Tobias Luetke
29
30
  files:
30
- - README
31
- - Rakefile
32
- - MIT-LICENSE
33
- - lib/helper.rb
34
- - lib/notification.rb
35
- - lib/paypal.rb
36
- - "misc/PayPal - Instant Payment Notification - Technical Overview.pdf"
37
- - misc/paypal.psd
31
+ - README
32
+ - Rakefile
33
+ - MIT-LICENSE
34
+ - lib/helper.rb
35
+ - lib/notification.rb
36
+ - lib/paypal.rb
37
+ - misc/PayPal - Instant Payment Notification - Technical Overview.pdf
38
+ - misc/paypal.psd
38
39
  test_files: []
40
+
39
41
  rdoc_options: []
42
+
40
43
  extra_rdoc_files: []
44
+
41
45
  executables: []
46
+
42
47
  extensions: []
48
+
43
49
  requirements: []
50
+
44
51
  dependencies:
45
- - !ruby/object:Gem::Dependency
46
- name: money
47
- version_requirement:
48
- version_requirements: !ruby/object:Gem::Version::Requirement
49
- requirements:
50
- -
51
- - ">"
52
- - !ruby/object:Gem::Version
53
- version: 0.0.0
54
- version:
52
+ - !ruby/object:Gem::Dependency
53
+ name: money
54
+ version_requirement:
55
+ version_requirements: !ruby/object:Gem::Version::Requirement
56
+ requirements:
57
+ - - ">"
58
+ - !ruby/object:Gem::Version
59
+ version: 0.0.0
60
+ version: