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.
@@ -0,0 +1,19 @@
1
+ module Paypal
2
+ module Helpers
3
+ module Rails
4
+ # Convenience helper. Can replace <%= form_tag Paypal::Notification.ipn_url %>
5
+ # takes optional url parameter, default is Paypal::Notification.ipn_url
6
+ def paypal_form_tag(url = Paypal::Config.ipn_url, options = {})
7
+ form_tag(url, options)
8
+ end
9
+
10
+ def paypal_form_tag(url = Paypal::Config.ipn_url, options = {}, &block)
11
+ if block
12
+ concat(form_tag(url, options)+capture(&block)+"</form>", block.binding)
13
+ else
14
+ form_tag(url, options)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,188 @@
1
+ require 'rack'
2
+ require 'net/http'
3
+
4
+ module Paypal
5
+ class NoDataError < StandardError; end
6
+
7
+ # Parser and handler for incoming Instant payment notifications from paypal.
8
+ # The Example shows a typical handler in a rails application. Note that this
9
+ # is an example, please read the Paypal API documentation for all the details
10
+ # on creating a safe payment controller.
11
+ #
12
+ # Example (Sinatra)
13
+ # def raw_post
14
+ # request.env["rack.input"].read
15
+ # end
16
+ #
17
+ # post "/paypal_ipn" do
18
+ # notify = Paypal::Notification.new(raw_post)
19
+ #
20
+ # order = Order.find(notify.item_id)
21
+ #
22
+ # if notify.acknowledge
23
+ # begin
24
+ # if notify.complete? and order.total == notify.amount and notify.business == 'sales@myshop.com'
25
+ # order.status = 'success'
26
+ #
27
+ # shop.ship(order)
28
+ # else
29
+ # logger.error("Failed to verify Paypal's notification, please investigate")
30
+ # end
31
+ #
32
+ # rescue => e
33
+ # order.status = 'failed'
34
+ # raise
35
+ # ensure
36
+ # order.save
37
+ # end
38
+ # end
39
+ #
40
+ # nil
41
+ # end
42
+ # end
43
+ #
44
+ # Example (Rails)
45
+ #
46
+ # class BackendController < ApplicationController
47
+ #
48
+ # def paypal_ipn
49
+ # notify = Paypal::Notification.new(request.raw_post)
50
+ #
51
+ # order = Order.find(notify.item_id)
52
+ #
53
+ # if notify.acknowledge
54
+ # begin
55
+ #
56
+ # if notify.complete? and order.total == notify.amount and notify.business == 'sales@myshop.com'
57
+ # order.status = 'success'
58
+ #
59
+ # shop.ship(order)
60
+ # else
61
+ # logger.error("Failed to verify Paypal's notification, please investigate")
62
+ # end
63
+ #
64
+ # rescue => e
65
+ # order.status = 'failed'
66
+ # raise
67
+ # ensure
68
+ # order.save
69
+ # end
70
+ # end
71
+ #
72
+ # render :nothing
73
+ # end
74
+ # end
75
+ class Notification
76
+ attr_accessor :params
77
+ attr_accessor :raw
78
+
79
+ # Creates a new paypal object. Pass the raw html you got from paypal in.
80
+ # In a rails application this looks something like this
81
+ #
82
+ # def paypal_ipn
83
+ # paypal = Paypal::Notification.new(request.raw_post)
84
+ # ...
85
+ # end
86
+ def initialize(post)
87
+ raise NoDataError if post.to_s.empty?
88
+
89
+ @params = {}
90
+ @raw = ""
91
+
92
+ parse(post)
93
+ end
94
+
95
+ # Transaction statuses
96
+ def canceled_reversal?
97
+ status == :Canceled_Reversal
98
+ end
99
+
100
+ def completed?
101
+ status == :Completed
102
+ end
103
+
104
+ def denied?
105
+ status == :Denied
106
+ end
107
+
108
+ def expired?
109
+ status == :Expired
110
+ end
111
+
112
+ def failed?
113
+ status == :Failed
114
+ end
115
+
116
+ def pending?
117
+ status == :Pending
118
+ end
119
+
120
+ def processed?
121
+ status == :Processed
122
+ end
123
+
124
+ def refunded?
125
+ status == :Refunded
126
+ end
127
+
128
+ def reversed?
129
+ status == :Reversed
130
+ end
131
+
132
+ def voided?
133
+ status == :Voided
134
+ end
135
+
136
+ # Acknowledge the transaction to paypal. This method has to be called after a new
137
+ # ipn arrives. Paypal will verify that all the information we received are correct and will return a
138
+ # ok or a fail.
139
+ #
140
+ # Example:
141
+ #
142
+ # def paypal_ipn
143
+ # notify = PaypalNotification.new(request.raw_post)
144
+ #
145
+ # if notify.acknowledge
146
+ # ... process order ... if notify.complete?
147
+ # else
148
+ # ... log possible hacking attempt ...
149
+ # end
150
+ def acknowledge
151
+ payload = raw
152
+
153
+ uri = URI.parse(Paypal::Config.ipn_url)
154
+ request = Net::HTTP::Post.new(Paypal::Config.ipn_validation_path)
155
+ request['Content-Length'] = "#{payload.size}"
156
+ request['User-Agent'] = "paypal ruby -- http://github.com/JonathanTron/paypal"
157
+
158
+ http = Net::HTTP.new(uri.host, uri.port)
159
+
160
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE unless @ssl_strict
161
+ http.use_ssl = true
162
+
163
+ request = http.request(request, payload)
164
+
165
+ raise StandardError.new("Faulty paypal result: #{request.body}") unless ["VERIFIED", "INVALID"].include?(request.body)
166
+
167
+ request.body == "VERIFIED"
168
+ end
169
+
170
+ def method_missing(method, *args)
171
+ params[method.to_s] || super
172
+ end
173
+
174
+ private
175
+
176
+ def status
177
+ @status ||= (params['payment_status'] ? params['payment_status'].to_sym : nil)
178
+ end
179
+
180
+ # Take the posted data and move the relevant data into a hash
181
+ def parse(post)
182
+ @raw = post
183
+ self.params = Rack::Utils.parse_query(post)
184
+ # Rack allows duplicate keys in queries, we need to use only the last value here
185
+ self.params.each{|k,v| self.params[k] = v.last if v.respond_to?(:last)}
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,5 @@
1
+ require 'active_support'
2
+ require File.join(File.dirname(__FILE__),'helpers', '/rails')
3
+
4
+ ActionView::Base.send(:include, Paypal::Helpers::Common)
5
+ ActionView::Base.send(:include, Paypal::Helpers::Rails)
@@ -0,0 +1,3 @@
1
+ module Paypal
2
+ VERSION = "3.0.0pre"
3
+ end
data/lib/paypal.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'cgi'
2
+ require 'net/http'
3
+ require 'net/https'
4
+
5
+ require "paypal/version"
6
+ require "paypal/config"
7
+ require "paypal/notification"
8
+ require "paypal/helpers/common"
data/misc/multiruby.sh ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+
3
+ RUBYOPT=""
4
+
5
+ for i in 1.9.1 ree 1.8.6; do
6
+ rvm $i && echo `ruby -v` && bundle install > /dev/null && bundle exec rake spec && bundle show
7
+ done
data/misc/paypal.psd ADDED
Binary file
@@ -0,0 +1,154 @@
1
+ require "spec_helper"
2
+
3
+ describe Paypal::Config do
4
+ after do
5
+ Paypal.class_eval do
6
+ remove_const :Config if const_defined?(:Config)
7
+ end
8
+ load "paypal/config.rb"
9
+ end
10
+
11
+ describe "mode" do
12
+ it "should be :sandbox by default" do
13
+ Paypal::Config.mode.should eql(:sandbox)
14
+ end
15
+
16
+ it "should accept :sandbox" do
17
+ lambda {
18
+ Paypal::Config.mode = :sandbox
19
+ }.should_not raise_error
20
+ end
21
+
22
+ it "should accept :production" do
23
+ lambda {
24
+ Paypal::Config.mode = :production
25
+ }.should_not raise_error
26
+ end
27
+
28
+ it "should not accept something different than :production or :sandbox" do
29
+ lambda {
30
+ Paypal::Config.mode = :demo
31
+ }.should raise_error(ArgumentError)
32
+ end
33
+ end
34
+
35
+ describe "when setting mode to sandbox" do
36
+ before do
37
+ Paypal::Config.mode = :sandbox
38
+ end
39
+
40
+ it "should be in :sandbox mode" do
41
+ Paypal::Config.mode.should eql(:sandbox)
42
+ end
43
+
44
+ it "should have ipn url for sandbox" do
45
+ Paypal::Config.ipn_url.should eql("https://www.sandbox.paypal.com/cgi-bin/webscr")
46
+ end
47
+
48
+ it "should have the ipn validation path for sandbox" do
49
+ Paypal::Config.ipn_validation_path.should eql("/cgi-bin/webscr?cmd=_notify-validate")
50
+ end
51
+
52
+ it "should have the ipn validation url for sandbox" do
53
+ Paypal::Config.ipn_validation_url.should eql("https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_notify-validate")
54
+ end
55
+
56
+ it "should have paypal cert for sandbox" do
57
+ Paypal::Config.paypal_cert.should eql(
58
+ "-----BEGIN CERTIFICATE-----
59
+ MIIDoTCCAwqgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBmDELMAkGA1UEBhMCVVMx
60
+ EzARBgNVBAgTCkNhbGlmb3JuaWExETAPBgNVBAcTCFNhbiBKb3NlMRUwEwYDVQQK
61
+ EwxQYXlQYWwsIEluYy4xFjAUBgNVBAsUDXNhbmRib3hfY2VydHMxFDASBgNVBAMU
62
+ C3NhbmRib3hfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMB4XDTA0
63
+ MDQxOTA3MDI1NFoXDTM1MDQxOTA3MDI1NFowgZgxCzAJBgNVBAYTAlVTMRMwEQYD
64
+ VQQIEwpDYWxpZm9ybmlhMREwDwYDVQQHEwhTYW4gSm9zZTEVMBMGA1UEChMMUGF5
65
+ UGFsLCBJbmMuMRYwFAYDVQQLFA1zYW5kYm94X2NlcnRzMRQwEgYDVQQDFAtzYW5k
66
+ Ym94X2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbTCBnzANBgkqhkiG
67
+ 9w0BAQEFAAOBjQAwgYkCgYEAt5bjv/0N0qN3TiBL+1+L/EjpO1jeqPaJC1fDi+cC
68
+ 6t6tTbQ55Od4poT8xjSzNH5S48iHdZh0C7EqfE1MPCc2coJqCSpDqxmOrO+9QXsj
69
+ HWAnx6sb6foHHpsPm7WgQyUmDsNwTWT3OGR398ERmBzzcoL5owf3zBSpRP0NlTWo
70
+ nPMCAwEAAaOB+DCB9TAdBgNVHQ4EFgQUgy4i2asqiC1rp5Ms81Dx8nfVqdIwgcUG
71
+ A1UdIwSBvTCBuoAUgy4i2asqiC1rp5Ms81Dx8nfVqdKhgZ6kgZswgZgxCzAJBgNV
72
+ BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMREwDwYDVQQHEwhTYW4gSm9zZTEV
73
+ MBMGA1UEChMMUGF5UGFsLCBJbmMuMRYwFAYDVQQLFA1zYW5kYm94X2NlcnRzMRQw
74
+ EgYDVQQDFAtzYW5kYm94X2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNv
75
+ bYIBADAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAFc288DYGX+GX2+W
76
+ P/dwdXwficf+rlG+0V9GBPJZYKZJQ069W/ZRkUuWFQ+Opd2yhPpneGezmw3aU222
77
+ CGrdKhOrBJRRcpoO3FjHHmXWkqgbQqDWdG7S+/l8n1QfDPp+jpULOrcnGEUY41Im
78
+ jZJTylbJQ1b5PBBjGiP0PpK48cdF
79
+ -----END CERTIFICATE-----")
80
+ end
81
+
82
+ it "should allow setting a new sandbox cert" do
83
+ lambda {
84
+ Paypal::Config.paypal_sandbox_cert = "TEST"
85
+ }.should_not raise_error
86
+ Paypal::Config.paypal_cert.should eql("TEST")
87
+ end
88
+ end
89
+
90
+ describe "when setting mode to :production" do
91
+ before do
92
+ Paypal::Config.mode = :production
93
+ end
94
+
95
+ it "should be in :production mode" do
96
+ Paypal::Config.mode.should eql(:production)
97
+ end
98
+
99
+ it "should have ipn url for production" do
100
+ Paypal::Config.ipn_url.should eql("https://www.paypal.com/cgi-bin/webscr")
101
+ end
102
+
103
+ it "should have the ipn validation path for production" do
104
+ Paypal::Config.ipn_validation_path.should eql("/cgi-bin/webscr?cmd=_notify-validate")
105
+ end
106
+
107
+ it "should have the ipn validation url for production" do
108
+ Paypal::Config.ipn_validation_url.should eql("https://www.paypal.com/cgi-bin/webscr?cmd=_notify-validate")
109
+ end
110
+
111
+ describe "when paypal_production_cert was not set" do
112
+ before do
113
+ @old_cert, Paypal::Config.paypal_production_cert = Paypal::Config.paypal_production_cert, nil
114
+ end
115
+
116
+ after do
117
+ Paypal::Config.paypal_production_cert = @old_cert
118
+ end
119
+
120
+ it "should raise an error" do
121
+ lambda {
122
+ Paypal::Config.paypal_cert
123
+ }.should raise_error(StandardError)
124
+ end
125
+ end
126
+
127
+ describe "when paypal_production_cert was set" do
128
+ before do
129
+ @old_cert, Paypal::Config.paypal_production_cert = Paypal::Config.paypal_production_cert, "TEST"
130
+ end
131
+
132
+ after do
133
+ Paypal::Config.paypal_production_cert = @old_cert
134
+ end
135
+
136
+ it "should not raise an error" do
137
+ lambda {
138
+ Paypal::Config.paypal_cert
139
+ }.should_not raise_error(StandardError)
140
+ end
141
+
142
+ it "should use the paypal production certificate" do
143
+ Paypal::Config.paypal_cert.should eql("TEST")
144
+ end
145
+ end
146
+
147
+ it "should allow setting a new production cert" do
148
+ lambda {
149
+ Paypal::Config.paypal_production_cert = "TEST"
150
+ }.should_not raise_error
151
+ Paypal::Config.paypal_cert.should eql("TEST")
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,19 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIC/jCCAmegAwIBAgIJAPlQUrHaPFrXMA0GCSqGSIb3DQEBBQUAMF4xCzAJBgNV
3
+ BAYTAkZSMRMwEQYDVQQIEwpTb21lLVN0YXRlMR4wHAYDVQQKExVPcGVuaG9vZCAt
4
+ IFBheXBhbCBnZW0xGjAYBgNVBAMTEU9wZW5ob29kUGF5cGFsR2VtMB4XDTEwMDMy
5
+ ODE1MjgwM1oXDTIwMDMyNTE1MjgwM1owXjELMAkGA1UEBhMCRlIxEzARBgNVBAgT
6
+ ClNvbWUtU3RhdGUxHjAcBgNVBAoTFU9wZW5ob29kIC0gUGF5cGFsIGdlbTEaMBgG
7
+ A1UEAxMRT3Blbmhvb2RQYXlwYWxHZW0wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ
8
+ AoGBAMaHlHZGjccN7p2xV6ro+TWc34EnRWhXRe1g9z/YwxhKA3Iu9etrU8m8Bbdh
9
+ EpBQTv/IOp7EG7kiUWcdyZp5gEQn6vX/+VIyvIJEDIiyWbxk2TBr6ofb0N8dwebh
10
+ M1T4Z7RvpqSAQWjbqFnN32IWR8SzmnZ5Hl/omfOsII14rWmDAgMBAAGjgcMwgcAw
11
+ HQYDVR0OBBYEFDDSdBbUWCfW8kVhoIkjEQm0a5P1MIGQBgNVHSMEgYgwgYWAFDDS
12
+ dBbUWCfW8kVhoIkjEQm0a5P1oWKkYDBeMQswCQYDVQQGEwJGUjETMBEGA1UECBMK
13
+ U29tZS1TdGF0ZTEeMBwGA1UEChMVT3Blbmhvb2QgLSBQYXlwYWwgZ2VtMRowGAYD
14
+ VQQDExFPcGVuaG9vZFBheXBhbEdlbYIJAPlQUrHaPFrXMAwGA1UdEwQFMAMBAf8w
15
+ DQYJKoZIhvcNAQEFBQADgYEAtnLucD1VU50JDcS/uuerhsBCHYwYUyrksYd43Stz
16
+ LaFLEhNXlciWvAanfMp182F1b5SLQdE9WqGZ4bDHWlgVeFa6zqcbmu7O9Z7xi0pS
17
+ f6lx3qllyjbn1zPbuiPOImM81dvXODYXD8fRvBc4+uTy4Xqin5rlwTvsM5TxJgHw
18
+ sSc=
19
+ -----END CERTIFICATE-----
@@ -0,0 +1,15 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIICXgIBAAKBgQDGh5R2Ro3HDe6dsVeq6Pk1nN+BJ0VoV0XtYPc/2MMYSgNyLvXr
3
+ a1PJvAW3YRKQUE7/yDqexBu5IlFnHcmaeYBEJ+r1//lSMryCRAyIslm8ZNkwa+qH
4
+ 29DfHcHm4TNU+Ge0b6akgEFo26hZzd9iFkfEs5p2eR5f6JnzrCCNeK1pgwIDAQAB
5
+ AoGBAKI289K0kXHc62TEG/rVQ5NP67vPB8Ja1RNB0KTFi5uUFj69KklCuAK09ALD
6
+ eXSbegE7bNJ/MJvT86mKyEjFEEyvpupEy7uzqZtWJUWHkDZTN2nyiz+kyRvhzldi
7
+ DlMOA0S3sNF6il94Chjb+F6/b+8d6cd8zi86tN5NJ0RLBiJ5AkEA6RrJxclKjPIb
8
+ GEtQ7W/hvhne2fx+F/mXdUg4TXBzupk9G5fgFpB05NDrLVs8gIJisBf7h/9OQ69B
9
+ Qvps9V4GxwJBANoHbtGQDBZDka1RSOsRubSqRMDWOYiE0KJcGmEdwoOz/zH0YcWI
10
+ iYQRv0k/sGITDAUQW+cqMEd82zME/XR0W2UCQQCgc98FdNwLDq+V1mn7NfMGPpqG
11
+ I+XLrPMTOMYAj/IpNEe60ZzfC0pbIm9vRgjsUFOL8MYjw5nkvyLF7sjHUJSLAkEA
12
+ k0IB7zjDcSYh9lW4UkyeiQ+XcEdAhJxv7bkXeBCJmDqyWiMdBjW566Gw+OAzWYpT
13
+ y/fImtE72ozR4Bu2tDkCoQJAHWVaDrzDANnUP8/xGoPxbWI8lYE+kKpyZtyTOgWm
14
+ mXbXCGjqKRA5HcDMoNmd0+pY/3pXx5VjHOiL2UsKrSnTaQ==
15
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,56 @@
1
+ require "spec_helper"
2
+
3
+ describe Paypal::Helpers::Common do
4
+ include Paypal::Helpers::Common
5
+
6
+ describe ".paypal_setup(item_number, amount, business, options = {})" do
7
+ describe "with default options" do
8
+ before do
9
+ @result = paypal_setup(1, "10.00", "trash@openhood.com")
10
+ end
11
+
12
+ {
13
+ "item_number" => "1",
14
+ "amount" => "10.00",
15
+ "currency_code" => "USD",
16
+ "business" => "trash@openhood.com",
17
+ "cmd" => "_xclick",
18
+ "quantity" => "1",
19
+ "item_number" => "1",
20
+ "item_name" => "Store purchase",
21
+ "no_shipping" => "1",
22
+ "no_note" => "1",
23
+ "charset" => "utf-8"
24
+ }.each do |key, value|
25
+ it "should include #{key} with #{value}" do
26
+ @result.should have_css("input[type=hidden][name=\"#{key}\"][value=\"#{value}\"]")
27
+ end
28
+ end
29
+ end
30
+
31
+ describe "with :business_key, :business_cert and :business_certid params filled" do
32
+ before do
33
+ @result = paypal_setup(1, "10.00", "trash@openhood.com", {
34
+ :business_key => File.read(File.join(File.dirname(__FILE__), "../fixtures/business_key.pem")),
35
+ :business_cert => File.read(File.join(File.dirname(__FILE__), "../fixtures/business_cert.pem")),
36
+ :business_certid => "CERTID"
37
+ })
38
+ end
39
+
40
+ it "should include cmd with _s-xclick" do
41
+ @result.should have_css("input[type=hidden][name=cmd][value=_s-xclick]")
42
+ end
43
+ it "should include cmd with encrypted datas" do
44
+ @result.should have_css("input[type=hidden][name=cmd][value*=PKCS7]")
45
+ end
46
+ end
47
+
48
+ describe "with unknown options" do
49
+ it "should raise an error" do
50
+ lambda do
51
+ paypal_setup(1, "10.00", "trash@openhood.com", :unknown_option => "unknown")
52
+ end.should raise_error(ArgumentError, "Unknown option #{[:unknown_option].inspect}")
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,148 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Paypal::Notification do
4
+ before do
5
+ Paypal::Config.mode = :sandbox
6
+ end
7
+
8
+ def valid_http_raw_data
9
+ "mc_gross=500.00&address_status=confirmed&payer_id=EVMXCLDZJV77Q&tax=0.00&address_street=164+Waverley+Street&payment_date=15%3A23%3A54+Apr+15%2C+2005+PDT&payment_status=Completed&address_zip=K2P0V6&first_name=Tobias&mc_fee=15.05&address_country_code=CA&address_name=Tobias+Luetke&notify_version=1.7&custom=cusdata&payer_status=unverified&business=tobi%40leetsoft.com&address_country=Canada&address_city=Ottawa&quantity=1&payer_email=tobi%40snowdevil.ca&verify_sign=AEt48rmhLYtkZ9VzOGAtwL7rTGxUAoLNsuf7UewmX7UGvcyC3wfUmzJP&txn_id=6G996328CK404320L&payment_type=instant&last_name=Luetke&address_state=Ontario&receiver_email=tobi%40leetsoft.com&payment_fee=&receiver_id=UQ8PDYXJZQD9Y&txn_type=web_accept&item_name=Store+Purchase&mc_currency=CAD&item_number=&test_ipn=1&payment_gross=&shipping=0.00&invoice=myinvoice&pending_reason=mypending_reason&reason_code=myreason_code&memo=mymemo&payment_type=mypayment_type&exchange_rate=myexchange_rate"
10
+ end
11
+
12
+ def self.valid_parsed_raw_data
13
+ {"payment_gross"=>"", "receiver_id"=>"UQ8PDYXJZQD9Y", "payer_email"=>"tobi@snowdevil.ca", "address_city"=>"Ottawa", "address_country"=>"Canada", "business"=>"tobi@leetsoft.com", "address_name"=>"Tobias Luetke", "payment_status"=>"Completed", "tax"=>"0.00", "reason_code"=>"myreason_code", "receiver_email"=>"tobi@leetsoft.com", "invoice"=>"myinvoice", "verify_sign"=>"AEt48rmhLYtkZ9VzOGAtwL7rTGxUAoLNsuf7UewmX7UGvcyC3wfUmzJP", "address_street"=>"164 Waverley Street", "memo"=>"mymemo", "mc_currency"=>"CAD", "txn_type"=>"web_accept", "quantity"=>"1", "address_zip"=>"K2P0V6", "pending_reason"=>"mypending_reason", "item_name"=>"Store Purchase", "txn_id"=>"6G996328CK404320L", "address_country_code"=>"CA", "payment_fee"=>"", "address_state"=>"Ontario", "payer_status"=>"unverified", "notify_version"=>"1.7", "shipping"=>"0.00", "mc_fee"=>"15.05", "payment_date"=>"15:23:54 Apr 15, 2005 PDT", "address_status"=>"confirmed", "test_ipn"=>"1", "payment_type"=>"mypayment_type", "first_name"=>"Tobias", "last_name"=>"Luetke", "payer_id"=>"EVMXCLDZJV77Q", "mc_gross"=>"500.00", "exchange_rate"=>"myexchange_rate", "item_number"=>"", "custom"=>"cusdata"}
14
+ end
15
+
16
+ describe "without data" do
17
+ it "should raise Paypal::NoDataError" do
18
+ lambda {
19
+ Paypal::Notification.new(nil)
20
+ }.should raise_error(Paypal::NoDataError)
21
+ end
22
+ end
23
+
24
+ describe "with valid raw data" do
25
+ before do
26
+ @notification = Paypal::Notification.new(valid_http_raw_data)
27
+ end
28
+
29
+ it "should store raw data" do
30
+ @notification.raw.should eql(valid_http_raw_data)
31
+ end
32
+
33
+ describe "#params" do
34
+ it "should not be empty" do
35
+ @notification.params.should_not be_empty
36
+ end
37
+
38
+ valid_parsed_raw_data.each do |key, value|
39
+ it "should have #{key}=#{value}" do
40
+ @notification.params[key].should eql(value)
41
+ end
42
+ end
43
+
44
+ it "should have unescaped values" do
45
+ @notification.params["payment_date"].should eql("15:23:54 Apr 15, 2005 PDT")
46
+ end
47
+
48
+ it "should include only the last value for duplicate key" do
49
+ @notification.params["payment_type"].should eql("mypayment_type")
50
+ end
51
+ end
52
+
53
+ it "should define method to access each query params" do
54
+ self.class.valid_parsed_raw_data.each do |key, _|
55
+ lambda {
56
+ @notification.send(key.to_sym)
57
+ }.should_not raise_error
58
+ end
59
+ end
60
+
61
+ ["Canceled_Reversal",
62
+ "Completed",
63
+ "Denied",
64
+ "Expired",
65
+ "Failed",
66
+ "Pending",
67
+ "Processed",
68
+ "Refunded",
69
+ "Reversed",
70
+ "Voided"].each do |status|
71
+ describe "when transaction payment_status = '#{status}'" do
72
+ it "should be #{status.downcase}" do
73
+ old_status, @notification.params["payment_status"] = @notification.params["payment_status"], status
74
+
75
+ @notification.send(:"#{status.downcase}?").should be_true
76
+
77
+ @notification.params["payment_status"] = old_status
78
+ end
79
+ end
80
+ end
81
+
82
+ describe "#acknowledge" do
83
+ before do
84
+ @paypal_validation_url = "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_notify-validate"
85
+ end
86
+
87
+ it "should send a post request to Paypal at #{@paypal_validation_url}" do
88
+ FakeWeb.register_uri(:post, @paypal_validation_url, :body => 'VERIFIED')
89
+
90
+ lambda { @notification.acknowledge }.should_not raise_error(FakeWeb::NetConnectNotAllowedError)
91
+
92
+ FakeWeb.clean_registry
93
+ end
94
+
95
+ describe "when Paypal response is VERIFIED" do
96
+ before do
97
+ FakeWeb.register_uri(:post, @paypal_validation_url, :body => 'VERIFIED')
98
+ end
99
+
100
+ it "should return true" do
101
+ @notification.acknowledge.should be(true)
102
+ end
103
+
104
+ after do
105
+ FakeWeb.clean_registry
106
+ end
107
+ end
108
+
109
+ describe "when Paypal response is INVALID" do
110
+ before do
111
+ FakeWeb.register_uri(:post, @paypal_validation_url, :body => 'INVALID')
112
+ end
113
+
114
+ it "should return false" do
115
+ @notification.acknowledge.should be(false)
116
+ end
117
+
118
+ after do
119
+ FakeWeb.clean_registry
120
+ end
121
+ end
122
+
123
+ describe "when Paypal response is not a recognize value" do
124
+ before do
125
+ FakeWeb.register_uri(:post, @paypal_validation_url, :body => 'BAD_VALUE')
126
+ end
127
+
128
+ it "should raise StandardError" do
129
+ lambda {
130
+ @notification.acknowledge
131
+ }.should raise_error(StandardError)
132
+ end
133
+
134
+ it "should include body response in StandardError" do
135
+ begin
136
+ @notification.acknowledge
137
+ rescue StandardError => e
138
+ e.message.should =~ /BAD_VALUE/
139
+ end
140
+ end
141
+
142
+ after do
143
+ FakeWeb.clean_registry
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,40 @@
1
+ begin
2
+ # Try to require the preresolved locked set of gems.
3
+ require ::File.expand_path('../.bundle/environment', __FILE__)
4
+ rescue LoadError
5
+ # Fall back on doing an unlocked resolve at runtime.
6
+ require "rubygems"
7
+ require "bundler"
8
+ Bundler.setup
9
+ end
10
+
11
+ Bundler.require :default
12
+
13
+ require "paypal"
14
+
15
+ # Do not allow connection to non registered URLs, so we can catch if specifics were called
16
+ FakeWeb.allow_net_connect = false
17
+
18
+ Rspec::Matchers.define :have_css do |css|
19
+ match do |text|
20
+ html = Nokogiri::HTML(text)
21
+ !html.css(css).empty?
22
+ end
23
+
24
+ failure_message_for_should do |text|
25
+ "expected to find css expression #{css} in:\n#{text}"
26
+ end
27
+
28
+ failure_message_for_should_not do |text|
29
+ "expected not to find css expression #{css} in:\n#{text}"
30
+ end
31
+
32
+ description do
33
+ "have css in #{expected}"
34
+ end
35
+ end
36
+
37
+ Rspec.configure do |c|
38
+ c.mock_framework = :rspec
39
+ c.color_enabled = true
40
+ end