paypal 1.0.1 → 1.8.0
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/README +45 -7
- data/Rakefile +17 -113
- data/lib/helper.rb +187 -38
- data/lib/notification.rb +56 -10
- data/lib/paypal.rb +1 -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
|
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
|
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 (
|
21
|
-
|
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(:
|
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
|
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
|
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
|
-
|
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
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
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
|
data/lib/helper.rb
CHANGED
@@ -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
|
36
|
+
# The last parameter is a options hash. You can set or override any Paypal-recognized parameter, including:
|
37
37
|
#
|
38
|
-
# * <tt>:
|
39
|
-
#
|
40
|
-
# * <tt>:
|
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>:
|
49
|
-
#
|
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
|
-
:
|
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
|
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
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
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
|
data/lib/notification.rb
CHANGED
@@ -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 = '
|
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
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
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
|
data/lib/paypal.rb
CHANGED
@@ -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.
|
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
|
7
|
-
date:
|
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
|
-
|
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
|
-
|
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
|
-
|
29
|
+
- Tobias Luetke
|
29
30
|
files:
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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:
|