Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Paypal IPN integration in about 30 lines of Sinatra.
diff --git a/app/account.rb b/app/account.rb
index a108685..c89ad34 100644
--- a/app/account.rb
+++ b/app/account.rb
@@ -1,4 +1,22 @@
+require 'open-uri'
+
module Sol
+ class Url
+ def self.get(url)
+ open(url).read
+ end
+ end
+
+ class PaypalVerifier
+ def initialize(query)
+ @query = query
+ end
+ def verified?
+ url = "https://www.paypal.com/cgi-bin/webscr?cmd=_notify-validate&#{@query}"
+ Url.get(url) == "VERIFIED"
+ end
+ end
+
class AccountApp < Sinatra::Application
get '/?' do
env['warden'].authenticate!
@@ -26,6 +44,18 @@ module Sol
redirect '/'
end
+ post '/ipn/?' do
+ return "FAILED VERIFICATION" unless (PaypalVerifier.new(request.env["rack.input"].read).verified?)
+ return "PAYMENT NOT COMPLETE" unless (params[:payment_status] == 'Completed')
+ if (@account = Account.where(:username => params[:option_selection1]).first)
+ @account.update_attribute(:purchased_at, Time.now)
+ Pony.mail(:to => @account.email, :subject => "Your Sol Trader account is activated", :body => erb(:activation_email, :layout => false))
+ "OK"
+ else
+ "NOT FOUND"
+ end
+ end
+
post '/purchased' do
if params[:secret] == Build::SECRET
@account = Account.where(:username => params[:username]).first
diff --git a/spec/integration/recording_purchase_spec.rb b/spec/integration/recording_purchase_spec.rb
index 8380889..debb21e 100644
--- a/spec/integration/recording_purchase_spec.rb
+++ b/spec/integration/recording_purchase_spec.rb
@@ -40,3 +40,56 @@ describe "Recording purchases" do
post '/purchased', :username => 'username', :secret => Build::SECRET
end
end
+
+describe "Paypal IPN" do
+ include Rack::Test::Methods
+
+ def app
+ Sol::AccountApp
+ end
+
+ let(:fields) {{
+ :email => 'foo@example.com',
+ :password => 'password',
+ :password_confirmation => 'password',
+ :username => 'username'
+ }}
+
+ before do
+ Account.create!(fields)
+ Pony.stub(:mail)
+ Sol::Url.stub(:get => "VERIFIED")
+ end
+
+ it "verifies that the paypal address is genuine" do
+ Sol::Url.should_receive(:get).with("https://www.paypal.com/cgi-bin/webscr?cmd=_notify-validate&test=ipn_query_string").and_return("VERIFIED")
+ post '/ipn', :test => 'ipn_query_string'
+ end
+
+ it "doesn't do anything if the account does not verify" do
+ Sol::Url.stub(:get => "INVALID")
+ post '/ipn', 'ipn_query_string'
+ Account.first.should_not be_game_owner
+ Pony.should_not_receive(:mail)
+ end
+
+ it "records purchases for usernames" do
+ post '/ipn', :option_selection1 => fields[:username], :payment_status => "Completed"
+ Account.first.should be_game_owner
+ end
+
+ it "does not record purchases for unknown username" do
+ post '/ipn', :option_selection1 => 'unknown'
+ Account.first.should_not be_game_owner
+ end
+
+ it "does not record purchases unless the payment_status is Completed" do
+ post '/ipn', :option_selection1 => fields[:username]
+ Account.first.should_not be_game_owner
+ end
+
+ it "sends email to the purchaser" do
+ Pony.should_receive(:mail).with(hash_including(:to => Account.first.email))
+ post '/ipn', :option_selection1 => fields[:username], :payment_status => "Completed"
+ end
+end
diff --git a/views/account.erb b/views/account.erb
index c478f26..7eface2 100644
--- a/views/account.erb
+++ b/views/account.erb
@@ -68,6 +68,7 @@
<input type="hidden" name="no_shipping" value="1">
<input type="hidden" name="rm" value="1">
<input type="hidden" name="return" value="http://soltrader.net/success">
+ <input type="hidden" name="notify_url" value="http://soltrader.net/account/ipn">
<input type="hidden" name="bn" value="PP-BuyNowBF:btn_buynow_LG.gif:NonHosted">
<input type="image" src="https://www.paypalobjects.com/en_GB/i/btn/btn_buynow_LG.gif" border="0" name="submit" alt="PayPal — The safer, easier way to pay online.">
<img alt="" border="0" src="https://www.paypalobjects.com/en_GB/i/scr/pixel.gif" width="1" height="1">
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.