stateless-systems-paypal 2.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,22 @@
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
+ #++
data/README ADDED
@@ -0,0 +1,151 @@
1
+ == Welcome to Paypal/ruby
2
+
3
+ This library is here to aid with integrating Paypal payments into ruby on rails
4
+ applications or similar. To set this up you will need to log into your paypal
5
+ business account and tell paypal where to send the IPN ( Instant payment notifications ).
6
+
7
+ == Download
8
+
9
+ * Preferred method of installation is using rubygems. gem install paypal --source http://dist.leetsoft.com
10
+ * Alternatively you can get the library packaged at http://dist.leetsoft.com/pkg/
11
+
12
+ == Requirements
13
+
14
+ * Ruby 1.8.2 (may work with previous versions) With OpenSSL support compiled in.
15
+ * Valid paypal business account.
16
+ * (optional) The money library from http://dist.leetsoft.com/api/money
17
+
18
+ == Installation
19
+
20
+ 1) Install the plugin using ./script/plugin install svn://vault.jadedpixel.com/paypal/trunk/paypal
21
+
22
+
23
+ 2) Create a paypal_ipn ( or similar ) action like the one in the "Example rails controller" appendix.
24
+
25
+ Within the new payment controller you can now create pages from which users can be sent to paypal. You always
26
+ have to sent users to paypal using a HTTP Post so a standard link won't work (well OK but you need some javascript for that). The +Paypal::Helper+ namespace has some examples of how such a forward page may look.
27
+
28
+ == Testing the integration
29
+
30
+ Under https://developer.paypal.com/ you can signup for a paypal developer account.
31
+ This allows you to set up "sandboxed" accounts which work and act like real accounts
32
+ with the difference that no money is exchanged. Its a good idea to sign up for a
33
+ sandbox account to use while the application is running in development mode.
34
+
35
+
36
+ == Example rails controller
37
+
38
+ class BackendController < ApplicationController
39
+
40
+ # Simplification, please write better code then this...
41
+ def paypal_ipn
42
+ notify = Paypal::Notification.new(request.raw_post)
43
+
44
+ if notify.acknowledge
45
+ order = Order.find(notify.item_id)
46
+ order.success = (notify.complete? and order.total == notify.amount) ? 'success' : 'failure'
47
+ order.save
48
+ end
49
+
50
+ render :nothing => true
51
+ end
52
+ end
53
+
54
+ == Example paypal forward page
55
+
56
+ <%= paypal_form_tag %>
57
+ <%= paypal_setup "Item 500", Money.us_dollar(50000), "bob@bigbusiness.com", :notify_url => url_for(:only_path => false, :action => 'paypal_ipn') %>
58
+
59
+ Please press here to pay $500US using paypal. <br/>
60
+ <%= submit_tag "Go to paypal >>" %>
61
+
62
+ </form>
63
+
64
+ or, with the same results, the block version:
65
+
66
+ <% paypal_form_tag do %>
67
+ <%= paypal_setup "Item 500", Money.us_dollar(50000), "bob@bigbusiness.com", :notify_url => url_for(:only_path => false, :action => 'paypal_ipn') %>
68
+
69
+ Please press here to pay $500US using paypal. <br/>
70
+ <%= submit_tag "Go to paypal >>" %>
71
+
72
+ <% end %>
73
+
74
+ == Using encrypted form data
75
+
76
+ Paypal supports encrypted form data to prevent tampering by third parties.
77
+ You must have a verified paypal account to use this functionality.
78
+
79
+ 1) Create a private key for yourself
80
+
81
+ openssl genrsa -out business_key.pem 1024
82
+
83
+ 2) Create a public certificate to share with Paypal
84
+
85
+ openssl req -new -key business_key.pem -x509 -days 3650 -out business_cert.pem
86
+
87
+ 3) Upload the public certificate to Paypal (under Profile -> Encrypted Payment Settings -> Your Public Certificates -> Add),
88
+ and note the "Cert ID" that Paypal shows for the certificate.
89
+
90
+ 4) Update your controller to include the details for your key and certificate.
91
+
92
+ @business_key = File::read("business_key.pem")
93
+ @business_cert = File::read("business_cert.pem")
94
+ @business_certid = "certid from paypal"
95
+
96
+ 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.
97
+
98
+ 6) When you're ready to go live, download the production Paypal certificate and override the default certificate.
99
+
100
+ Paypal::Notification.paypal_cert = File::read("paypal_cert.pem")
101
+
102
+ == Troubleshooting
103
+
104
+ uninitalized constant Paypal - Make sure your ruby has openssl support
105
+
106
+ == Changelog
107
+
108
+ 2008-10-16 -- 2.0.2
109
+ NEW: Added block style support for paypal_form_tag (paypal_form_tag do blah.. blah... end)
110
+ DEL: Removed patch for 2.0.0, no longer seems suitable.
111
+ NEW: Added testing for the paypal_form_tag block style.
112
+ NEW: Added a sample (actual from paypal sandbox) PayPal server response for IPN, to aid in testing.
113
+
114
+ 2008-10-15 -- 2.0.1
115
+ CHG: Modified README.
116
+ FIX: Moved patch to own directory, was being caught by git-hub as gem's README
117
+ NEW: Added patch for currently installed paypal-2.0.0 gem to apply directly on gems directory.
118
+ NEW: Added relevant test statements.
119
+ FIX: removed duplicate 'invoice' method in lib/notification.rb
120
+ NEW: added correct 'custom' method to lib/notification.rb
121
+ NEW: added pending_reason, reason_code, memo, payment_type, exchange_rate methods to lib/notification.rb
122
+
123
+ 2006-04-20 -- 2.0.0
124
+ * Uses paypal extended syntax. The plugin can now submit shipping and billing addresses using the paypal_address helper.
125
+
126
+ 2006-04-20 -- 1.7.0
127
+ * Now a rails plugin
128
+
129
+ 2006-02-10 -- 1.5.1
130
+ * added complete list of valid paypal options (Paul Hart)
131
+
132
+ 2006-02-02 -- 1.5.0
133
+ * Now report an error when invalid option is passed to paypal_setup
134
+ * Had to rename parameters cancel_url to cancel_return and return_url to return, please update your app
135
+ * Improved the test coverage strategy for helper tests
136
+ * Added support for encrypted form data (Paul Hart)
137
+
138
+ 2005-09-16 -- 0.9.6
139
+ * Added readme note about the openssl requirement
140
+
141
+ 2005-07-26 -- 0.9.5
142
+ * Added tax to the helper parameters
143
+ * fixed bug when money class was used to pass in amount. Cents were always 00 (doh!)
144
+ * Added invoice and custom optional parameters
145
+ * Added charset = utf-8 to all paypal posts
146
+ * Wrongly used undefined_quanitity parameter in 0.9.1, this caused users to be prompted for the quanitity on the paypal checkout page... fixed
147
+
148
+ 2005-07-22 -- 0.9.1
149
+ * 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
150
+ you don't have to do that in the paypal admin interface!
151
+ * Removed the actual form tag from the paypal_setup generated code to conform better with docs
data/Rakefile ADDED
@@ -0,0 +1,126 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+ require 'rake/rdoctask'
5
+ require 'rake/gempackagetask'
6
+ require 'rake/contrib/rubyforgepublisher'
7
+
8
+ PKG_VERSION = "2.1.3"
9
+ PKG_NAME = "stateless-systems-paypal"
10
+ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
11
+
12
+ PKG_FILES = FileList[
13
+ "lib/**/*",
14
+ "test/*",
15
+ "misc/*",
16
+ "[A-Z]*",
17
+ "MIT-LICENSE",
18
+ "Rakefile"
19
+ ].exclude(/\bCVS\b|~$/)
20
+
21
+ desc "Default Task"
22
+ task :default => [ :test, :test_remote ]
23
+
24
+ desc "Delete tar.gz / zip / rdoc"
25
+ task :cleanup => [ :rm_packages, :clobber_rdoc ]
26
+
27
+ # Run the unit tests
28
+ Rake::TestTask.new :test do |t|
29
+ t.libs << "test"
30
+ t.pattern = 'test/*_test.rb'
31
+ t.ruby_opts << '-rubygems'
32
+ t.verbose = false
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
41
+
42
+ desc "Create a rubygem and install it. Might need root rights"
43
+ task :install => [:package] do
44
+ `gem install pkg/#{PKG_FILE_NAME}.gem`
45
+ end
46
+
47
+ # Genereate the RDoc documentation
48
+
49
+ Rake::RDocTask.new { |rdoc|
50
+ rdoc.rdoc_dir = 'doc'
51
+ rdoc.title = "Paypal library"
52
+ rdoc.rdoc_files.include('README')
53
+ rdoc.rdoc_files.include('lib/**/*.rb')
54
+ }
55
+
56
+ task :lines do
57
+ lines = 0
58
+ codelines = 0
59
+ Dir.foreach("lib") { |file_name|
60
+ next unless file_name =~ /.*rb/
61
+
62
+ f = File.open("lib/" + file_name)
63
+
64
+ while line = f.gets
65
+ lines += 1
66
+ next if line =~ /^\s*$/
67
+ next if line =~ /^\s*#/
68
+ codelines += 1
69
+ end
70
+ }
71
+ puts "Lines #{lines}, LOC #{codelines}"
72
+ end
73
+
74
+
75
+ desc "Publish the gem on leetsoft"
76
+ task :publish => [:rdoc, :package] do
77
+ Rake::SshFilePublisher.new("leetsoft.com", "dist/pkg", "pkg", "#{PKG_FILE_NAME}.zip").upload
78
+ Rake::SshFilePublisher.new("leetsoft.com", "dist/pkg", "pkg", "#{PKG_FILE_NAME}.tgz").upload
79
+ Rake::SshFilePublisher.new("leetsoft.com", "dist/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
80
+ `ssh tobi@leetsoft.com "mkdir -p dist/api/#{PKG_NAME}"`
81
+ Rake::SshDirPublisher.new("leetsoft.com", "dist/api/#{PKG_NAME}", "doc").upload
82
+ `ssh tobi@leetsoft.com './gemupdate'`
83
+ end
84
+
85
+ spec = Gem::Specification.new do |s|
86
+ s.name = PKG_NAME
87
+ s.version = PKG_VERSION
88
+ s.description = s.summary = "Paypal IPN integration library for rails and other web applications"
89
+ s.has_rdoc = true
90
+
91
+ s.files = %w(init.rb README Rakefile MIT-LICENSE) + Dir['lib/**/*'] + Dir['misc/**/*'] + Dir['test/**/*']
92
+ s.files.reject! { |f| /\/\.\_/ }
93
+ s.require_path = 'lib'
94
+ s.autorequire = 'paypal'
95
+ s.author = "Tobias Luetke"
96
+ s.email = "tobi@leetsoft.com"
97
+ s.homepage = "http://dist.leetsoft.com/api/paypal"
98
+
99
+ s.add_dependency('money')
100
+ end
101
+
102
+ Rake::GemPackageTask.new(spec) do |p|
103
+ p.gem_spec = spec
104
+ p.need_tar = true
105
+ p.need_zip = true
106
+ end
107
+
108
+
109
+ # --- Ruby forge release manager by florian gross -------------------------------------------------
110
+
111
+ RUBY_FORGE_PROJECT = 'paypal'
112
+ RUBY_FORGE_USER = 'xal'
113
+ RELEASE_NAME = "REL #{PKG_VERSION}"
114
+
115
+ desc "Publish the release files to RubyForge."
116
+ task :release => [:publish] do
117
+ `rubyforge login`
118
+ release_command = "rubyforge add_release #{PKG_NAME} #{PKG_NAME} 'REL #{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.gem"
119
+ puts release_command
120
+ system(release_command)
121
+
122
+ release_command = "rubyforge add_release #{PKG_NAME} #{PKG_NAME} 'REL #{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.zip"
123
+ puts release_command
124
+ system(release_command)
125
+
126
+ end
data/init.rb ADDED
@@ -0,0 +1,29 @@
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
+
25
+ # Please note: Paypal is by default in *production* mode.
26
+ # During development you will want to add
27
+ #
28
+
29
+ ActionView::Base.send(:include, Paypal::Helpers)
data/lib/helper.rb ADDED
@@ -0,0 +1,315 @@
1
+ module Paypal
2
+ # This is a collection of helpers which aid in the creation of paypal buttons
3
+ #
4
+ # Example:
5
+ #
6
+ # <%= form_tag Paypal::Notification.ipn_url %>
7
+ #
8
+ # <%= paypal_setup "Item 500", Money.us_dollar(50000), "bob@bigbusiness.com" %>
9
+ # Please press here to pay $500US using paypal. <%= submit_tag %>
10
+ #
11
+ # <% end_form_tag %>
12
+ #
13
+ # For this to work you have to include these methods as helpers in your rails application.
14
+ # One way is to add "include Paypal::Helpers" in your application_helper.rb
15
+ # See Paypal::Notification for information on how to catch payment events.
16
+ module Helpers
17
+
18
+ # Convenience helper. Can replace <%= form_tag Paypal::Notification.ipn_url %>
19
+ # takes optional url parameter, default is Paypal::Notification.ipn_url
20
+ def paypal_form_tag(url = Paypal::Notification.ipn_url, options = {})
21
+ form_tag(url, options)
22
+ end
23
+
24
+ def paypal_form_tag(url = Paypal::Notification.ipn_url, options = {}, &block)
25
+ if block
26
+ concat(form_tag(url, options)+capture(&block)+"</form>", block.binding)
27
+ else
28
+ form_tag(url, options)
29
+ end
30
+ end
31
+
32
+ # This helper creates the hidden form data which is needed for a paypal purchase.
33
+ #
34
+ # * <tt>item_number</tt> -- The first parameter is the item number. This is for your personal organization and can
35
+ # be arbitrary. Paypal will sent the item number back with the IPN so its a great place to
36
+ # store a user ID or a order ID or something like this.
37
+ #
38
+ # * <tt>amount</tt> -- should be a parameter of type Money ( see http://leetsoft.com/api/money ) but can also
39
+ # be a string of type "50.00" for 50$. If you use the string syntax make sure you set the current
40
+ # currency as part of the options hash. The default is USD
41
+ #
42
+ # * <tt>business</tt> -- This is your paypal account name ( a email ). This needs to be a valid paypal business account.
43
+ #
44
+ # The last parameter is a options hash. You can set or override any Paypal-recognized parameter, including:
45
+ #
46
+ # * <tt>:cmd</tt> -- default is '_xclick' or '_xclick-subscriptions' when you use :subscription.
47
+ # * <tt>:quantity</tt> -- default is '1'.
48
+ # * <tt>:no_note</tt> -- default is '1'.
49
+ # * <tt>:item_name</tt> -- default is 'Store purchase'. This is the name of the purchase which will be displayed
50
+ # on the paypal page.
51
+ # * <tt>:no_shipping</tt> -- default is '1'. By default we tell paypal that no shipping is required. Usually
52
+ # the shipping address should be collected in our application, not by paypal.
53
+ # * <tt>:currency</tt> -- default is 'USD'. If you provide a Money object, that will automatically override
54
+ # the value.
55
+ # * <tt>:charset</tt> -- default is 'utf-8'.
56
+ # * <tt>:notify_url</tt> -- If provided paypal will send its IPN notification once a
57
+ # purchase is made, canceled or any other status changes occur.
58
+ # * <tt>:return</tt> -- If provided paypal will redirect a user back to this url after a
59
+ # successful purchase. Useful for a kind of thankyou page.
60
+ # * <tt>:cancel_return</tt> -- If provided paypal will redirect a user back to this url when
61
+ # the user cancels the purchase.
62
+ # * <tt>:tax</tt> -- the tax for the store purchase. Same format as the amount parameter but optional
63
+ # * <tt>:invoice</tt> -- Unique invoice number. User will never see this. optional
64
+ # * <tt>:custom</tt> -- Custom field. User will never see this. optional
65
+ #
66
+ # Dealing with subscriptions
67
+ #
68
+ # * <tt>:subscription</tt> -- Hash containing the subscription options. optional
69
+ # * <tt>:period</tt> -- One of :monthly, :yearly, :weekly or :daily
70
+ # * <tt>:length</tt> -- How often, based on :period. E.g. 6 :monthly?
71
+ # * <tt>:retry</tt> -- Default is false. Boolean for if paypal should retry failed payments.
72
+ # * <tt>:recurring</tt> -- Default is false. Boolean for if paypal should recur payment and end of period.
73
+ #
74
+ # Generating encrypted form data
75
+ #
76
+ # The helper also supports the generation of encrypted button data. Please see the README for more information
77
+ # on the setup and prerequisite steps for using encrypted forms.
78
+ #
79
+ # The following options must all be provided (as strings) to encrypt the data:
80
+ #
81
+ # * <tt>:business_key</tt> -- The private key you have generated
82
+ # * <tt>:business_cert</tt> -- The public certificate you have also uploaded to Paypal
83
+ # * <tt>:business_certid</tt> -- The certificate ID that Paypal has assigned to your certificate.
84
+ #
85
+ # Examples:
86
+ #
87
+ # <%= paypal_setup @order.id, Money.us_dollar(50000), "bob@bigbusiness.com" %>
88
+ # <%= paypal_setup @order.id, '50.00', "bob@bigbusiness.com", :currency => 'USD' %>
89
+ # <%= paypal_setup @order.id, '50.00', "bob@bigbusiness.com", :currency => 'USD', :notify_url => url_for(:only_path => false, :action => 'paypal_ipn') %>
90
+ # <%= 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 %>
91
+ # <%= 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 %>
92
+ #
93
+ def paypal_setup(item_number, amount, business, options = {})
94
+
95
+ subscription = options.delete(:subscription)
96
+
97
+ misses = (options.keys - valid_setup_options)
98
+ raise ArgumentError, "Unknown option #{misses.inspect}" if not misses.empty?
99
+
100
+ params = {
101
+ :cmd => subscription ? '_ext-enter' : '_xclick',
102
+ :redirect_cmd => subscription ? '_xclick-subscriptions' : nil,
103
+ :quantity => 1,
104
+ :business => business,
105
+ :item_number => item_number,
106
+ :item_name => 'Store purchase',
107
+ :no_shipping => '1',
108
+ :no_note => '1',
109
+ :charset => 'utf-8'
110
+ }.reject{|k,v| v.nil?}.merge(options)
111
+
112
+ params[:currency_code] = amount.currency if amount.respond_to?(:currency)
113
+ params[:currency_code] = params.delete(:currency) if params[:currency]
114
+ params[:currency_code] ||= 'USD'
115
+
116
+ # We accept both strings and money objects as amount
117
+ amount = amount.cents.to_f / 100.0 if amount.respond_to?(:cents)
118
+ amount = sprintf('%.2f', amount)
119
+
120
+ if subscription.nil?
121
+ params[:amount] = amount
122
+ else
123
+ params[:a3] = amount
124
+ params[:p3] = subscription[:length]
125
+ params[:sra] = subscription[:retry] == true ? 1 : 0
126
+ params[:src] = subscription[:recurring] == true ? 1 : 0
127
+ params[:t3] = case subscription[:period]
128
+ when :monthly; 'M'
129
+ when :yearly; 'Y'
130
+ when :weekly; 'W'
131
+ when :daily; 'D'
132
+ end
133
+ end
134
+
135
+ # same for tax
136
+ tax = params[:tax]
137
+ unless tax.nil?
138
+ tax = tax.cents.to_f / 100.0 if tax.respond_to?(:cents)
139
+ params[:tax] = sprintf("%.2f", tax)
140
+ end
141
+
142
+ # look for encryption parameters, save them outsite the parameter hash.
143
+ business_key = params.delete(:business_key)
144
+ business_cert = params.delete(:business_cert)
145
+ business_certid = params.delete(:business_certid)
146
+
147
+ # Build the form
148
+ returning button = [] do
149
+ # Only attempt an encrypted form if we have all the required fields.
150
+ if business_key and business_cert and business_certid
151
+ require 'openssl'
152
+
153
+ # Convert the key and certificates into OpenSSL-friendly objects.
154
+ paypal_cert = OpenSSL::X509::Certificate.new(Paypal::Notification.paypal_cert)
155
+ business_key = OpenSSL::PKey::RSA.new(business_key)
156
+ business_cert = OpenSSL::X509::Certificate.new(business_cert)
157
+ # Put the certificate ID back into the parameter hash the way Paypal wants it.
158
+ params[:cert_id] = business_certid
159
+
160
+ # Prepare a string of data for encryption
161
+ data = ""
162
+ params.each_pair {|k,v| data << "#{k}=#{v}\n"}
163
+
164
+ # Sign the data with our key/certificate pair
165
+ signed = OpenSSL::PKCS7::sign(business_cert, business_key, data, [], OpenSSL::PKCS7::BINARY)
166
+ # Encrypt the signed data with Paypal's public certificate.
167
+ encrypted = OpenSSL::PKCS7::encrypt([paypal_cert], signed.to_der, OpenSSL::Cipher::Cipher::new("DES3"), OpenSSL::PKCS7::BINARY)
168
+
169
+ # The command for encrypted forms is always '_s-xclick'; the real command is in the encrypted data.
170
+ button << tag(:input, :type => 'hidden', :name => 'cmd', :value => "_s-xclick")
171
+ button << tag(:input, :type => 'hidden', :name => 'encrypted', :value => encrypted)
172
+ else
173
+ # Just emit all the parameters that we have as hidden fields.
174
+ # Note that the sorting isn't really needed, but it makes testing a lot easier for now.
175
+ params.each do |key, value|
176
+ button << tag(:input, :type => 'hidden', :name => key, :value => value) unless value.nil?
177
+ end
178
+ end
179
+ end.join("\n")
180
+ end
181
+
182
+
183
+ # Pass an address to paypal so that all singup forms can be prefilled
184
+ #
185
+ # * <tt>email</tt> -- Customer's email address
186
+ # * <tt>first_name</tt> -- Customer's first name. Must be alpha-numeric, with a 32 character limit
187
+ # * <tt>last_name</tt> -- Customer's last name. Must be alpha-numeric, with a 64 character limit
188
+ # * <tt>address1</tt> -- First line of customer's address. Must be alpha-numeric, with a 100 character limit
189
+ # * <tt>address2</tt> -- Second line of customer's address. Must be alpha-numeric, with a 100 character limit
190
+ # * <tt>city</tt> -- City of customer's address. Must be alpha-numeric, with a 100 character limit
191
+ # * <tt>state</tt> -- State of customer's address. Must be official 2 letter abbreviation
192
+ # * <tt>zip</tt> -- Zip code of customer's address
193
+ # * <tt>night_phone_a</tt> -- Area code of customer's night telephone number
194
+ # * <tt>night_phone_b</tt> -- First three digits of customer's night telephone number
195
+ # * <tt>day_phone_a</tt> -- Area code of customer's daytime telephone number
196
+ # * <tt>day_phone_b</tt> -- First three digits of customer's daytime telephon
197
+ def paypal_address(options = {})
198
+ options.collect do |key, value|
199
+ tag(:input, :type => 'hidden', :name => key, :value => value)
200
+ end.join("\n")
201
+ end
202
+
203
+ private
204
+
205
+ # See https://www.paypal.com/IntegrationCenter/ic_std-variable-reference.html for details on the following options.
206
+ def valid_setup_options
207
+ [
208
+ # Generic Options
209
+ :cmd,
210
+ # IPN Support
211
+ :notify_url,
212
+ # Item Information
213
+ :item_name,
214
+ :quantity,
215
+ :undefined_quantity,
216
+ :on0,
217
+ :os0,
218
+ :on1,
219
+ :os1,
220
+ # Display Information
221
+ :add,
222
+ :cancel_return,
223
+ :cbt,
224
+ :cn,
225
+ :cpp_header_image,
226
+ :cpp_headerback_color,
227
+ :cpp_headerborder_color,
228
+ :cpp_payflow_color,
229
+ :cs,
230
+ :display,
231
+ :image_url,
232
+ :no_note,
233
+ :no_shipping,
234
+ :page_style,
235
+ :return,
236
+ :rm,
237
+ # Transaction Information
238
+ :address_override,
239
+ :currency,
240
+ :currency_code,
241
+ :custom,
242
+ :handling,
243
+ :invoice,
244
+ :redirect_cmd,
245
+ :shipping,
246
+ :tax,
247
+ :tax_cart,
248
+ # Shopping Cart Options
249
+ :amount,
250
+ :business,
251
+ :handling_cart,
252
+ :paymentaction,
253
+ :rupload,
254
+ :charset,
255
+ :upload,
256
+ # Prepopulating PayPal FORMs or Address Overriding
257
+ :address1,
258
+ :address2,
259
+ :city,
260
+ :country,
261
+ :email,
262
+ :first_name,
263
+ :last_name,
264
+ :lc,
265
+ :night_phone_a,
266
+ :night_phone_b,
267
+ :night_phone_c,
268
+ :state,
269
+ :zip,
270
+ # Prepopulating Business Account Sign-up
271
+ :business_address1,
272
+ :business_address2,
273
+ :business_city,
274
+ :business_state,
275
+ :business_country,
276
+ :business_cs_email,
277
+ :business_cs_phone_a,
278
+ :business_cs_phone_b,
279
+ :business_cs_phone_c,
280
+ :business_url,
281
+ :business_night_phone_a,
282
+ :business_night_phone_b,
283
+ :business_night_phone_c,
284
+ # End of list from https://www.paypal.com/IntegrationCenter/ic_std-variable-reference.html
285
+ # The following items are known to exist but are not yet on the above page.
286
+ :business_zip,
287
+ :day_phone_a,
288
+ :day_phone_b,
289
+ :day_phone_c,
290
+ # Subscription Options
291
+ :a1, # Trial Amount 1
292
+ :p1, # Trial Period 1
293
+ :t1, # Trial Period 1 Units (D=days, W=weeks, M=months, Y=years)
294
+ :a2, # Trial Amount 2
295
+ :p2, # Trial Period 2
296
+ :t2, # Trial Period 2 Units
297
+ :a3, # Regular Subscription Amount
298
+ :p3, # Regular Subscription Period
299
+ :t3, # Regular Subscription Period Units
300
+ :src, # Recurring Payments? (1=yes, default=0)
301
+ :sra, # Reattempt Transaction on Failure? (1=yes, default=0)
302
+ :srt, # Recurring Times (number of renewals before auto-cancel, default=forever)
303
+ :usr_manage, # Username and Password Generator? (1=yes, default=0)
304
+ :modify, # Modification Behaviour (0=new subs only, 1=new or modify, 2=modify existing only, default=0)
305
+ # Encryption Options - used internally only.
306
+ :business_key, # Your private key
307
+ :business_cert, # Your public certificate
308
+ :business_certid, # Your public certificate ID (from Paypal)
309
+ # Other
310
+ :bn
311
+ ]
312
+ end
313
+
314
+ end
315
+ end