jonathantron-paypal 3.0.0pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ ._*
7
+ *.orig
8
+ Thumbs.db
9
+ doc
10
+ .yardoc
11
+ .bundle
data/CHANGELOG.md ADDED
@@ -0,0 +1,73 @@
1
+ = 3.0.0 (git)
2
+
3
+ Start spec on Paypal::Helpers::Common.paypal_setup
4
+ Fix specs for Paypal::Config
5
+ Upgrade dev dependencies to use Rspec >= 2.0.0.a
6
+ Introduce a Paypal::Config module to handle ipn/certificates (Jonathan Tron)
7
+ Allow access to params as method (via method_missing) (Jonathan Tron)
8
+ Rework Paypal::Notification :
9
+ Remove methods to get params informations (CAUTION !!! THIS BREAK OLD API) (Jonathan Tron)
10
+ Add methods for all possible statuses and rename #complete? as completed? to follow Paypal naming (Jonathan Tron)
11
+ Use Rack::Utils#parse_query in Paypal::Notification for query parsing (Jonathan Tron)
12
+ Add Spec for notification (Jonathan Tron)
13
+ Add paypal/rails.rb to trigger helper inclusion in view (Jonathan Tron)
14
+ Separate actual helpers in two files (paypal/helpers/common.rb and paypal/helpers/rails.rb) (Jonathan Tron)
15
+ Move files around (Jonathan Tron)
16
+ Update the README and switch to MarkDown (Jonathan Tron)
17
+ Add a CHANGELOG file and switch to new format (Jonathan Tron)
18
+ Remove init.rb (Jonathan Tron)
19
+ Configure gem generation with jelewer (Jonathan Tron)
20
+
21
+ = 2008-10-16 -- 2.0.2
22
+
23
+ NEW: Added block style support for paypal_form_tag (paypal_form_tag do blah.. blah... end)
24
+ DEL: Removed patch for 2.0.0, no longer seems suitable.
25
+ NEW: Added testing for the paypal_form_tag block style.
26
+ NEW: Added a sample (actual from paypal sandbox) PayPal server response for IPN, to aid in testing.
27
+
28
+ = 2008-10-15 -- 2.0.1
29
+
30
+ CHG: Modified README.
31
+ FIX: Moved patch to own directory, was being caught by git-hub as gem's README
32
+ NEW: Added patch for currently installed paypal-2.0.0 gem to apply directly on gems directory.
33
+ NEW: Added relevant test statements.
34
+ FIX: removed duplicate 'invoice' method in lib/notification.rb
35
+ NEW: added correct 'custom' method to lib/notification.rb
36
+ NEW: added pending_reason, reason_code, memo, payment_type, exchange_rate methods to lib/notification.rb
37
+
38
+ = 2006-04-20 -- 2.0.0
39
+
40
+ Uses paypal extended syntax. The plugin can now submit shipping and billing addresses using the paypal_address helper.
41
+
42
+ = 2006-04-20 -- 1.7.0
43
+
44
+ Now a rails plugin
45
+
46
+ = 2006-02-10 -- 1.5.1
47
+
48
+ added complete list of valid paypal options (Paul Hart)
49
+
50
+ = 2006-02-02 -- 1.5.0
51
+
52
+ Now report an error when invalid option is passed to paypal_setup
53
+ Had to rename parameters cancel_url to cancel_return and return_url to return, please update your app
54
+ Improved the test coverage strategy for helper tests
55
+ Added support for encrypted form data (Paul Hart)
56
+
57
+ = 2005-09-16 -- 0.9.6
58
+
59
+ Added readme note about the openssl requirement
60
+
61
+ = 2005-07-26 -- 0.9.5
62
+
63
+ Added tax to the helper parameters
64
+ fixed bug when money class was used to pass in amount. Cents were always 00 (doh!)
65
+ Added invoice and custom optional parameters
66
+ Added charset = utf-8 to all paypal posts
67
+ Wrongly used undefined_quanitity parameter in 0.9.1, this caused users to be prompted for the quanitity on the paypal checkout page... fixed
68
+
69
+ = 2005-07-22 -- 0.9.1
70
+
71
+ 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
72
+ you don't have to do that in the paypal admin interface!
73
+ Removed the actual form tag from the paypal_setup generated code to conform better with docs
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source :gemcutter
2
+
3
+ gem "rack", ">= 1.0.0"
4
+ gem "json_pure"
5
+ gem "rake"
6
+ gem "jeweler"
7
+ gem "rcov", ">= 0.9.8"
8
+ gem "rspec", ">= 2.0.0.a"
9
+ gem "nokogiri"
10
+ gem "bluecloth"
11
+ gem "yard"
12
+ gem "fakeweb", :require => "fakeweb"
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ #--
2
+ # Copyright (c) 2005 Tobias Luetke
3
+ # Copyright (c) 2009 Tron Jonathan
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ #++
data/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # Welcome to Paypal ruby library
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 JonathanTron-paypal
10
+ * Alternatively you can get the source code at http://github.com/JonathanTron/paypal
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. sudo gem install JonathanTron-paypal --source=http://gems.github.com
21
+
22
+ 2. Require the library
23
+
24
+ require "paypal"
25
+
26
+ 2.1. If you're using Rails add :
27
+
28
+ require "paypal/rails"
29
+
30
+ 3. Create a paypal_ipn ( or similar ) action like the one in the "Example action" appendix.
31
+
32
+
33
+ ## -- - TODO : REWRITE BELOW THIS POINT - --
34
+
35
+ Within the new payment controller you can now create pages from which users can be sent to paypal. You always 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.
36
+
37
+ # Testing the integration
38
+
39
+ Under https://developer.paypal.com/ you can signup for a paypal developer account.
40
+ This allows you to set up "sandboxed" accounts which work and act like real accounts
41
+ with the difference that no money is exchanged. Its a good idea to sign up for a
42
+ sandbox account to use while the application is running in development mode.
43
+
44
+
45
+ # Example rails controller
46
+
47
+ class BackendController < ApplicationController
48
+
49
+ # Simplification, please write better code then this...
50
+ def paypal_ipn
51
+ notify # Paypal::Notification.new(request.raw_post)
52
+
53
+ if notify.acknowledge
54
+ order # Order.find(notify.item_id)
55
+ order.success # (notify.complete? and order.total # notify.amount) ? 'success' : 'failure'
56
+ order.save
57
+ end
58
+
59
+ render :nothing #> true
60
+ end
61
+ end
62
+
63
+ # Example paypal forward page
64
+
65
+ <%# paypal_form_tag %>
66
+ <%# paypal_setup "Item 500", Money.us_dollar(50000), "bob@bigbusiness.com", :notify_url #> url_for(:only_path #> false, :action #> 'paypal_ipn') %>
67
+
68
+ Please press here to pay $500US using paypal. <br/>
69
+ <%# submit_tag "Go to paypal >>" %>
70
+
71
+ </form>
72
+
73
+ or, with the same results, the block version:
74
+
75
+ <% paypal_form_tag do %>
76
+ <%# paypal_setup "Item 500", Money.us_dollar(50000), "bob@bigbusiness.com", :notify_url #> url_for(:only_path #> false, :action #> 'paypal_ipn') %>
77
+
78
+ Please press here to pay $500US using paypal. <br/>
79
+ <%# submit_tag "Go to paypal >>" %>
80
+
81
+ <% end %>
82
+
83
+ # Using encrypted form data
84
+
85
+ Paypal supports encrypted form data to prevent tampering by third parties.
86
+ You must have a verified paypal account to use this functionality.
87
+
88
+ 1) Create a private key for yourself
89
+
90
+ openssl genrsa -out business_key.pem 1024
91
+
92
+ 2) Create a public certificate to share with Paypal
93
+
94
+ openssl req -new -key business_key.pem -x509 -days 3650 -out business_cert.pem
95
+
96
+ 3) Upload the public certificate to Paypal (under Profile -> Encrypted Payment Settings -> Your Public Certificates -> Add),
97
+ and note the "Cert ID" that Paypal shows for the certificate.
98
+
99
+ 4) Update your controller to include the details for your key and certificate.
100
+
101
+ @business_key # File::read("business_key.pem")
102
+ @business_cert # File::read("business_cert.pem")
103
+ @business_certid # "certid from paypal"
104
+
105
+ 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.
106
+
107
+ 6) When you're ready to go live, download the production Paypal certificate and override the default certificate.
108
+
109
+ Paypal::Notification.paypal_cert # File::read("paypal_cert.pem")
110
+
111
+ 7) Finally, add the following line to your environment.rb or inside an initializer:
112
+
113
+ Paypal::Notification.ipn_url # "https://www.paypal.com/cgi-bin/webscr"
114
+
115
+ # Troubleshooting
116
+
117
+ uninitalized constant Paypal - Make sure your ruby has openssl support
data/Rakefile ADDED
@@ -0,0 +1,45 @@
1
+ require "rubygems"
2
+ require "rake"
3
+
4
+ $:.unshift "lib"
5
+ require "paypal"
6
+
7
+ begin
8
+ require "jeweler"
9
+ Jeweler::Tasks.new do |gem|
10
+ gem.name = "jonathantron-paypal"
11
+ gem.version = Paypal::VERSION
12
+ gem.summary = %Q{Paypal Express Integration}
13
+ gem.description = %Q{Integrate Paypal Express}
14
+ gem.email = "jonathan@tron.name"
15
+ gem.homepage = "http://github.com/JonathanTron/paypal"
16
+ gem.authors = ["Jonathan Tron", "Joseph Halter", "Tobias Luetke"]
17
+ gem.add_dependency "rack", ">= 1.0.0"
18
+ gem.add_development_dependency "rspec", ">= 2.0.0.a"
19
+ gem.add_development_dependency "rcov", ">= 0.9.8"
20
+ gem.add_development_dependency "nokogiri"
21
+ gem.add_development_dependency "bluecloth"
22
+ gem.add_development_dependency "yard"
23
+ gem.add_development_dependency "fakeweb"
24
+ end
25
+ rescue LoadError
26
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
27
+ end
28
+
29
+ require "rspec/core/rake_task"
30
+ ::Rspec::Core::RakeTask.new(:spec)
31
+ ::Rspec::Core::RakeTask.new(:rcov) do |spec|
32
+ spec.rcov = true
33
+ spec.rcov_opts = "--exclude spec/"
34
+ end
35
+ task :spec => :check_dependencies
36
+ task :default => :spec
37
+
38
+ begin
39
+ require "yard"
40
+ YARD::Rake::YardocTask.new
41
+ rescue LoadError
42
+ task :yardoc do
43
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
44
+ end
45
+ end
@@ -0,0 +1,22 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDoTCCAwqgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBmDELMAkGA1UEBhMCVVMx
3
+ EzARBgNVBAgTCkNhbGlmb3JuaWExETAPBgNVBAcTCFNhbiBKb3NlMRUwEwYDVQQK
4
+ EwxQYXlQYWwsIEluYy4xFjAUBgNVBAsUDXNhbmRib3hfY2VydHMxFDASBgNVBAMU
5
+ C3NhbmRib3hfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMB4XDTA0
6
+ MDQxOTA3MDI1NFoXDTM1MDQxOTA3MDI1NFowgZgxCzAJBgNVBAYTAlVTMRMwEQYD
7
+ VQQIEwpDYWxpZm9ybmlhMREwDwYDVQQHEwhTYW4gSm9zZTEVMBMGA1UEChMMUGF5
8
+ UGFsLCBJbmMuMRYwFAYDVQQLFA1zYW5kYm94X2NlcnRzMRQwEgYDVQQDFAtzYW5k
9
+ Ym94X2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbTCBnzANBgkqhkiG
10
+ 9w0BAQEFAAOBjQAwgYkCgYEAt5bjv/0N0qN3TiBL+1+L/EjpO1jeqPaJC1fDi+cC
11
+ 6t6tTbQ55Od4poT8xjSzNH5S48iHdZh0C7EqfE1MPCc2coJqCSpDqxmOrO+9QXsj
12
+ HWAnx6sb6foHHpsPm7WgQyUmDsNwTWT3OGR398ERmBzzcoL5owf3zBSpRP0NlTWo
13
+ nPMCAwEAAaOB+DCB9TAdBgNVHQ4EFgQUgy4i2asqiC1rp5Ms81Dx8nfVqdIwgcUG
14
+ A1UdIwSBvTCBuoAUgy4i2asqiC1rp5Ms81Dx8nfVqdKhgZ6kgZswgZgxCzAJBgNV
15
+ BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMREwDwYDVQQHEwhTYW4gSm9zZTEV
16
+ MBMGA1UEChMMUGF5UGFsLCBJbmMuMRYwFAYDVQQLFA1zYW5kYm94X2NlcnRzMRQw
17
+ EgYDVQQDFAtzYW5kYm94X2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNv
18
+ bYIBADAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAFc288DYGX+GX2+W
19
+ P/dwdXwficf+rlG+0V9GBPJZYKZJQ069W/ZRkUuWFQ+Opd2yhPpneGezmw3aU222
20
+ CGrdKhOrBJRRcpoO3FjHHmXWkqgbQqDWdG7S+/l8n1QfDPp+jpULOrcnGEUY41Im
21
+ jZJTylbJQ1b5PBBjGiP0PpK48cdF
22
+ -----END CERTIFICATE-----
@@ -0,0 +1,58 @@
1
+ module Paypal
2
+ module Config
3
+ IPN_URL_SANDBOX = "https://www.sandbox.paypal.com/cgi-bin/webscr"
4
+ IPN_URL_PRODUCTION = "https://www.paypal.com/cgi-bin/webscr"
5
+
6
+ @@mode = :sandbox
7
+ def self.mode=(new_mode)
8
+ raise ArgumentError.new("Paypal::Config.mode should be either :sandbox or :production (you tried to set it as : #{new_mode})") unless [:sandbox, :production].include?(new_mode.to_sym)
9
+ @@mode = new_mode.to_sym
10
+ end
11
+ def self.mode
12
+ @@mode
13
+ end
14
+
15
+ def self.ipn_url
16
+ case @@mode
17
+ when :sandbox
18
+ IPN_URL_SANDBOX
19
+ when :production
20
+ IPN_URL_PRODUCTION
21
+ end
22
+ end
23
+
24
+ def self.ipn_validation_path
25
+ URI.parse(ipn_url).path + "?cmd=_notify-validate"
26
+ end
27
+
28
+ def self.ipn_validation_url
29
+ "#{ipn_url}?cmd=_notify-validate"
30
+ end
31
+
32
+ @@paypal_sandbox_cert = File.read(File.join(File.dirname(__FILE__), 'certs', 'paypal_sandbox.pem'))
33
+ def self.paypal_sandbox_cert=(new_cert)
34
+ @@paypal_sandbox_cert = new_cert
35
+ end
36
+ def self.paypal_sandbox_cert
37
+ @@paypal_sandbox_cert
38
+ end
39
+
40
+ @@paypal_production_cert = nil
41
+ def self.paypal_production_cert=(new_cert)
42
+ @@paypal_production_cert = new_cert
43
+ end
44
+ def self.paypal_production_cert
45
+ @@paypal_production_cert
46
+ end
47
+
48
+ def self.paypal_cert
49
+ case @@mode
50
+ when :sandbox
51
+ @@paypal_sandbox_cert
52
+ when :production
53
+ raise StandardError.new("You should set Paypal::Config.paypal_production_cert with your paypal production certificate") if @@paypal_production_cert.nil?
54
+ @@paypal_production_cert
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,300 @@
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::Config.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
+ module Common
18
+ # This helper creates the hidden form data which is needed for a paypal purchase.
19
+ #
20
+ # * <tt>item_number</tt> -- The first parameter is the item number. This is for your personal organization and can
21
+ # be arbitrary. Paypal will sent the item number back with the IPN so its a great place to
22
+ # store a user ID or a order ID or something like this.
23
+ #
24
+ # * <tt>amount</tt> -- should be a parameter of type Money ( see http://leetsoft.com/api/money ) but can also
25
+ # be a string of type "50.00" for 50$. If you use the string syntax make sure you set the current
26
+ # currency as part of the options hash. The default is USD
27
+ #
28
+ # * <tt>business</tt> -- This is your paypal account name ( an email ). This needs to be a valid paypal business account.
29
+ #
30
+ # The last parameter is a options hash. You can set or override any Paypal-recognized parameter, including:
31
+ #
32
+ # * <tt>:cmd</tt> -- default is '_xclick' or '_xclick-subscriptions' when you use :subscription.
33
+ # * <tt>:quantity</tt> -- default is '1'.
34
+ # * <tt>:no_note</tt> -- default is '1'.
35
+ # * <tt>:item_name</tt> -- default is 'Store purchase'. This is the name of the purchase which will be displayed
36
+ # on the paypal page.
37
+ # * <tt>:no_shipping</tt> -- default is '1'. By default we tell paypal that no shipping is required. Usually
38
+ # the shipping address should be collected in our application, not by paypal.
39
+ # * <tt>:currency</tt> -- default is 'USD'. If you provide a Money object, that will automatically override
40
+ # the value.
41
+ # * <tt>:charset</tt> -- default is 'utf-8'.
42
+ # * <tt>:notify_url</tt> -- If provided paypal will send its IPN notification once a
43
+ # purchase is made, canceled or any other status changes occur.
44
+ # * <tt>:return</tt> -- If provided paypal will redirect a user back to this url after a
45
+ # successful purchase. Useful for a kind of thankyou page.
46
+ # * <tt>:cancel_return</tt> -- If provided paypal will redirect a user back to this url when
47
+ # the user cancels the purchase.
48
+ # * <tt>:tax</tt> -- the tax for the store purchase. Same format as the amount parameter but optional
49
+ # * <tt>:invoice</tt> -- Unique invoice number. User will never see this. optional
50
+ # * <tt>:custom</tt> -- Custom field. User will never see this. optional
51
+ #
52
+ # Dealing with subscriptions
53
+ #
54
+ # * <tt>:subscription</tt> -- Hash containing the subscription options. optional
55
+ # * <tt>:period</tt> -- One of :monthly, :yearly, :weekly or :daily
56
+ # * <tt>:length</tt> -- How often, based on :period. E.g. 6 :monthly?
57
+ # * <tt>:retry</tt> -- Default is false. Boolean for if paypal should retry failed payments.
58
+ # * <tt>:recurring</tt> -- Default is false. Boolean for if paypal should recur payment and end of period.
59
+ #
60
+ # Generating encrypted form data
61
+ #
62
+ # The helper also supports the generation of encrypted button data. Please see the README for more information
63
+ # on the setup and prerequisite steps for using encrypted forms.
64
+ #
65
+ # The following options must all be provided (as strings) to encrypt the data:
66
+ #
67
+ # * <tt>:business_key</tt> -- The private key you have generated
68
+ # * <tt>:business_cert</tt> -- The public certificate you have also uploaded to Paypal
69
+ # * <tt>:business_certid</tt> -- The certificate ID that Paypal has assigned to your certificate.
70
+ #
71
+ # Examples:
72
+ #
73
+ # <%= paypal_setup @order.id, Money.us_dollar(50000), "bob@bigbusiness.com" %>
74
+ # <%= paypal_setup @order.id, '50.00', "bob@bigbusiness.com", :currency => 'USD' %>
75
+ # <%= paypal_setup @order.id, '50.00', "bob@bigbusiness.com", :currency => 'USD', :notify_url => url_for(:only_path => false, :action => 'paypal_ipn') %>
76
+ # <%= 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 %>
77
+ # <%= 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 %>
78
+ #
79
+ def paypal_setup(item_number, amount, business, options = {})
80
+
81
+ subscription = options.delete(:subscription)
82
+
83
+ misses = (options.keys - valid_setup_options)
84
+ raise ArgumentError, "Unknown option #{misses.inspect}" unless misses.empty?
85
+
86
+ params = {
87
+ :cmd => subscription ? '_ext-enter' : '_xclick',
88
+ :redirect_cmd => subscription ? '_xclick-subscriptions' : nil,
89
+ :quantity => 1,
90
+ :business => business,
91
+ :item_number => item_number,
92
+ :item_name => 'Store purchase',
93
+ :no_shipping => '1',
94
+ :no_note => '1',
95
+ :charset => 'utf-8'
96
+ }.reject{|k,v| v.nil?}.merge(options)
97
+
98
+ params[:currency_code] = amount.currency if amount.respond_to?(:currency)
99
+ params[:currency_code] = params.delete(:currency) if params[:currency]
100
+ params[:currency_code] ||= 'USD'
101
+
102
+ # We accept both strings and money objects as amount
103
+ amount = amount.cents.to_f / 100.0 if amount.respond_to?(:cents)
104
+ amount = sprintf('%.2f', amount)
105
+
106
+ if subscription.nil?
107
+ params[:amount] = amount
108
+ else
109
+ params[:a3] = amount
110
+ params[:p3] = subscription[:length]
111
+ params[:sra] = subscription[:retry] == true ? 1 : 0
112
+ params[:src] = subscription[:recurring] == true ? 1 : 0
113
+ params[:t3] = case subscription[:period]
114
+ when :monthly; 'M'
115
+ when :yearly; 'Y'
116
+ when :weekly; 'W'
117
+ when :daily; 'D'
118
+ end
119
+ end
120
+
121
+ # same for tax
122
+ tax = params[:tax]
123
+ unless tax.nil?
124
+ tax = tax.cents.to_f / 100.0 if tax.respond_to?(:cents)
125
+ params[:tax] = sprintf("%.2f", tax)
126
+ end
127
+
128
+ # look for encryption parameters, save them outsite the parameter hash.
129
+ business_key = params.delete(:business_key)
130
+ business_cert = params.delete(:business_cert)
131
+ business_certid = params.delete(:business_certid)
132
+
133
+ # Build the form
134
+ buttons = []
135
+ # Only attempt an encrypted form if we have all the required fields.
136
+ if business_key and business_cert and business_certid
137
+ require 'openssl'
138
+
139
+ # Convert the key and certificates into OpenSSL-friendly objects.
140
+ paypal_cert = OpenSSL::X509::Certificate.new(Paypal::Config.paypal_cert)
141
+ business_key = OpenSSL::PKey::RSA.new(business_key)
142
+ business_cert = OpenSSL::X509::Certificate.new(business_cert)
143
+ # Put the certificate ID back into the parameter hash the way Paypal wants it.
144
+ params[:cert_id] = business_certid
145
+
146
+ # Prepare a string of data for encryption
147
+ data = ""
148
+ params.each_pair {|k,v| data << "#{k}=#{v}\n"}
149
+
150
+ # Sign the data with our key/certificate pair
151
+ signed = OpenSSL::PKCS7::sign(business_cert, business_key, data, [], OpenSSL::PKCS7::BINARY)
152
+ # Encrypt the signed data with Paypal's public certificate.
153
+ encrypted = OpenSSL::PKCS7::encrypt([paypal_cert], signed.to_der, OpenSSL::Cipher::Cipher::new("DES3"), OpenSSL::PKCS7::BINARY)
154
+
155
+ # The command for encrypted forms is always '_s-xclick'; the real command is in the encrypted data.
156
+ buttons << %Q{<input type="hidden" name="cmd" value="_s-xclick" />}
157
+ buttons << %Q{<input type="hidden" name="cmd" value="#{encrypted}" />}
158
+ else
159
+ # Just emit all the parameters that we have as hidden fields.
160
+ # Note that the sorting isn't really needed, but it makes testing a lot easier for now.
161
+ params.each do |key, value|
162
+ buttons << %Q{<input type="hidden" name="#{key}" value="#{value}" />} unless value.nil?
163
+ end
164
+ end
165
+ buttons.join("\n")
166
+ end
167
+
168
+ # Pass an address to paypal so that all signup forms can be prefilled
169
+ #
170
+ # * <tt>email</tt> -- Customer's email address
171
+ # * <tt>first_name</tt> -- Customer's first name. Must be alpha-numeric, with a 32 character limit
172
+ # * <tt>last_name</tt> -- Customer's last name. Must be alpha-numeric, with a 64 character limit
173
+ # * <tt>address1</tt> -- First line of customer's address. Must be alpha-numeric, with a 100 character limit
174
+ # * <tt>address2</tt> -- Second line of customer's address. Must be alpha-numeric, with a 100 character limit
175
+ # * <tt>city</tt> -- City of customer's address. Must be alpha-numeric, with a 100 character limit
176
+ # * <tt>state</tt> -- State of customer's address. Must be official 2 letter abbreviation
177
+ # * <tt>zip</tt> -- Zip code of customer's address
178
+ # * <tt>night_phone_a</tt> -- Area code of customer's night telephone number
179
+ # * <tt>night_phone_b</tt> -- First three digits of customer's night telephone number
180
+ # * <tt>day_phone_a</tt> -- Area code of customer's daytime telephone number
181
+ # * <tt>day_phone_b</tt> -- First three digits of customer's daytime telephon
182
+ def paypal_address(options = {})
183
+ options.collect do |key, value|
184
+ %Q{<input type="hidden" name="#{key}" value="#{value}" />}
185
+ end.join("\n")
186
+ end
187
+
188
+ private
189
+
190
+ # See https://www.paypal.com/IntegrationCenter/ic_std-variable-reference.html for details on the following options.
191
+ def valid_setup_options
192
+ [
193
+ # Generic Options
194
+ :cmd,
195
+ # IPN Support
196
+ :notify_url,
197
+ # Item Information
198
+ :item_name,
199
+ :quantity,
200
+ :undefined_quantity,
201
+ :on0,
202
+ :os0,
203
+ :on1,
204
+ :os1,
205
+ # Display Information
206
+ :add,
207
+ :cancel_return,
208
+ :cbt,
209
+ :cn,
210
+ :cpp_header_image,
211
+ :cpp_headerback_color,
212
+ :cpp_headerborder_color,
213
+ :cpp_payflow_color,
214
+ :cs,
215
+ :display,
216
+ :image_url,
217
+ :no_note,
218
+ :no_shipping,
219
+ :page_style,
220
+ :return,
221
+ :rm,
222
+ # Transaction Information
223
+ :address_override,
224
+ :currency,
225
+ :currency_code,
226
+ :custom,
227
+ :handling,
228
+ :invoice,
229
+ :redirect_cmd,
230
+ :shipping,
231
+ :tax,
232
+ :tax_cart,
233
+ # Shopping Cart Options
234
+ :amount,
235
+ :business,
236
+ :handling_cart,
237
+ :paymentaction,
238
+ :rupload,
239
+ :charset,
240
+ :upload,
241
+ # Prepopulating PayPal FORMs or Address Overriding
242
+ :address1,
243
+ :address2,
244
+ :city,
245
+ :country,
246
+ :email,
247
+ :first_name,
248
+ :last_name,
249
+ :lc,
250
+ :night_phone_a,
251
+ :night_phone_b,
252
+ :night_phone_c,
253
+ :state,
254
+ :zip,
255
+ # Prepopulating Business Account Sign-up
256
+ :business_address1,
257
+ :business_address2,
258
+ :business_city,
259
+ :business_state,
260
+ :business_country,
261
+ :business_cs_email,
262
+ :business_cs_phone_a,
263
+ :business_cs_phone_b,
264
+ :business_cs_phone_c,
265
+ :business_url,
266
+ :business_night_phone_a,
267
+ :business_night_phone_b,
268
+ :business_night_phone_c,
269
+ # End of list from https://www.paypal.com/IntegrationCenter/ic_std-variable-reference.html
270
+ # The following items are known to exist but are not yet on the above page.
271
+ :business_zip,
272
+ :day_phone_a,
273
+ :day_phone_b,
274
+ :day_phone_c,
275
+ # Subscription Options
276
+ :a1, # Trial Amount 1
277
+ :p1, # Trial Period 1
278
+ :t1, # Trial Period 1 Units (D=days, W=weeks, M=months, Y=years)
279
+ :a2, # Trial Amount 2
280
+ :p2, # Trial Period 2
281
+ :t2, # Trial Period 2 Units
282
+ :a3, # Regular Subscription Amount
283
+ :p3, # Regular Subscription Period
284
+ :t3, # Regular Subscription Period Units
285
+ :src, # Recurring Payments? (1=yes, default=0)
286
+ :sra, # Reattempt Transaction on Failure? (1=yes, default=0)
287
+ :srt, # Recurring Times (number of renewals before auto-cancel, default=forever)
288
+ :usr_manage, # Username and Password Generator? (1=yes, default=0)
289
+ :modify, # Modification Behaviour (0=new subs only, 1=new or modify, 2=modify existing only, default=0)
290
+ # Encryption Options - used internally only.
291
+ :business_key, # Your private key
292
+ :business_cert, # Your public certificate
293
+ :business_certid, # Your public certificate ID (from Paypal)
294
+ # Other
295
+ :bn
296
+ ]
297
+ end
298
+ end
299
+ end
300
+ end