stateless-systems-paypal 2.1.3
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/MIT-LICENSE +22 -0
- data/README +151 -0
- data/Rakefile +126 -0
- data/init.rb +29 -0
- data/lib/helper.rb +315 -0
- data/lib/notification.rb +271 -0
- data/lib/paypal.rb +31 -0
- data/misc/PayPal - Instant Payment Notification - Technical Overview.pdf +0 -0
- data/misc/paypal.psd +0 -0
- data/test/helper_test.rb +197 -0
- data/test/mocks/http_mock.rb +47 -0
- data/test/mocks/method_mock.rb +33 -0
- data/test/notification_test.rb +99 -0
- data/test/remote/remote_test.rb +16 -0
- data/test/sample.ipn.response.txt +1 -0
- metadata +78 -0
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
|