I've setup a Purchase Model & Purchases Controller to allow users buy Items from my Rails application via PayPal (IPN).Everything works fine apart from listening for PayPal's IPN answer.How can you actually listen for this post notification properly / what have I done wrong?
Weird is that if I link a return_url to /hook it works, but the notify_url seemingly does not work.Why does PayPal even pass the parameters to the Return URL? - you don't want the User to have to use the Return URL to unlock his bought items.
Opening PayPal in new Tab with Button => works
Passing necessary Item information to PayPal => works
Buy Item with PayPal => works
List item as :status => "Completed" in Database => does not work!
User Model:
has_many :purchases
Purchase Model:
class Purchase < ActiveRecord::Base
belongs_to :tool
belongs_to :user
def paypal_url(return_path)
values = {
business: "merchant#example.com",
cmd: "_xclick",
upload: 1,
invoice: id,
amount: tool.price,
item_name: tool.title,
notify_url: "http://5947992e.ngrok.io/hook" #Test Server
}
"#{Rails.application.secrets.paypal_host}/cgi-bin/webscr?" + values.to_query
end
end
Purchases Controller:
class PurchasesController < ApplicationController
def new
#purchase = Purchase.new(:tool_id => params[:tool_id], :user_id => current_user.id)
if #purchase.save
redirect_to #purchase.paypal_url(purchase_path(#purchase))
else
render :new
end
end
protect_from_forgery except: [:hook]
def hook
params.permit! # Permit all Paypal input params
status = params[:payment_status]
if status == "Completed"
#purchase = Purchase.find(params[:invoice])
#purchase.update_attributes(status: status, transaction_id: params[:txn_id], purchased_at: Time.now)
#purchase.save!
#user = #tool.user
#user.earned_money += #tool.price
#user.save!
end
render nothing: true
end
end
Routes:
post "/purchases/:id" => "purchases#show"
post "/hook" => "purchases#hook"
Item Show View (haml):
= link_to image_tag('paypal.png'), new_purchase_path(:tool_id => #tool.id), target: "_blank"
Thanks in advance for each answer! Please tell me if you need additional information.
Related
I'm currently trying to make a 'Buy now' button with a fixed price amount.
After the user pays I want to redirect them to the root url and send them an email with an attached PDF file.
I've been researching how to create a simple checkout using paypal but with no success, I've found tutorials that are years old and some of the code is deprecated.
I've tried using BRAINTREE and it worked perfectly on testing/sandbox but I am unable to create a production account since I currently live in Puerto Rico(this limits my options for payment gateways).
What I've done so far
Following a tutorial
I've created a scaffold for products with name and unit_price
In my product model:
# This defines the paypal url for a given product sale
def paypal_url(return_url)
values = {
:business => YOUR_MERCHANT_EMAIL,
:cmd => '_cart',
:upload => 1,
:return => return_url,
:invoice => UNIQUE_INTEGER
}
values.merge!({
"amount_1" => unit_price,
"item_name_1" => name,
"item_number_1" => id,
"quantity_1" => '1'
})
# This is a paypal sandbox url and should be changed for production.
# Better define this url in the application configuration setting on environment
# basis.
"https://www.sandbox.paypal.com/cgi-bin/webscr?" + values.to_query
end
In the tutorial they said that this should be enough to be able to process the payment but they ask to click the 'Buy now' link which I have no idea where to point it or how to create it.
If it's not much to ask, could someone point me in the right direction here (step by step -> easy single checkout payment with paypal -> documentations).
Thanks a million.
EDIT:
Was able to create the checkout button:
<%= link_to 'checkout', product.paypal_url(products_url) %>
Now it works BUT how do I make it so you get redirected back to my website with a notice?
Thanks!
Ok so after a full day of research and testing, I've manage to get almost everything working. Here's what I've done
Step 1
rails g scaffold product name:string unit_price:decimal
product controller:
def index
#products = Product.all
if #products.length != 0
#product = Product.find(1)
end
end
Then create your first product
Step 2
In the index for products you can put a button like this for a paypal checkout:
<%= link_to 'checkout', #product.paypal_url(payment_notification_index_url, root_url) %>
Step 3
in the product model
# This defines the paypal url for a given product sale
def paypal_url(return_url, cancel_return)
values = {
:business => 'your_sandbox_facilitato_email#example.com',
:cmd => '_xclick',
:upload => 1,
:return => return_url,
:rm => 2,
# :notify_url => notify_url,
:cancel_return => cancel_return
}
values.merge!({
"amount" => unit_price,
"item_name" => name,
"item_number" => id,
"quantity" => '1'
})
# For test transactions use this URL
"https://www.sandbox.paypal.com/cgi-bin/webscr?" + values.to_query
end
has_many :payment_notifications
You can find more info about the HTML Variables for PayPal Payments Standard
In this code the most important ones for me are:
:return
The URL to which PayPal redirects buyers' browser after they complete their payments. For example, specify a URL on your site that displays a "Thank you for your payment" page.
:notify_url
The URL to which PayPal posts information about the payment, in the form of Instant Payment Notification messages.
:cancel_return
A URL to which PayPal redirects the buyers' browsers if they cancel checkout before completing their payments. For example, specify a URL on your website that displays a "Payment Canceled" page.
and
:rm
Return method. The FORM METHOD used to send data to the URL specified
by the return variable. Allowable values are:
0 – all shopping cart payments use the GET method
1 – the buyer's browser is redirected to the return URL by using the
GET method, but no payment variables are included
2 – the buyer's browser is redirected to the return URL by using the
POST method, and all payment variables are included
Step 4
rails g controller PaymentNotification create
In here you need to add the following:
class PaymentNotificationController < ApplicationController
protect_from_forgery except: [:create]
def create
# #payment = PaymentNotification.create!(params: params, product_id: params[:invoice], status: params[:payment_status], transaction_id: params[:txn_id] )
#payment = PaymentNotification.create!(params: params, product_id: 1, status: params[:payment_status], transaction_id: params[:txn_id])
# render nothing: true
if #payment.status == 'Completed'
redirect_to root_url, notice: 'Success!'
else
redirect_to root_url, notice: 'Error'
end
end
end
Step 5
rails g model PaymentNotification
in here add the following
class PaymentNotification < ActiveRecord::Base
belongs_to :product
serialize :params
after_create :success_message
private
def success_message
if status == "Completed"
puts 'Completed'
...
else
puts 'error'
...
end
end
end
in routes:
resources :payment_notification, only: [:create]
And now you should be able to have a complete processing payment via paypal.
Don't forget to rake db:migrate after each scaffold and model creationg.
Another thing, in order to get the automatic redirect, you have to specify the url in the sandbox of paypal. Click here to know how
If I forgot something let me know, been working for more than 10 hours to get this working lol
I am trying to setup a system where if User A is reading a status from User B and clicks 'I'm interested' it will send a pre default message to User B's inbox (create new record in the Message table). This is sort of like on social networks where users sends a 'wink' to another user. I was able to do a similar setup for another section on the app that works, but I can't get this 'wink' feature to work. After the user clicks 'I'm interested', that link should no longer be available to the user for that status. I get a undefined method 'interested= error.
Intrigued Controller:
def create
#intrigue = current_user.intrigues.build(intrigue_params)
end
def destroy
#intrigue.destroy
end
def repost
#intrigue = Intrigue.find(params[:id]).repost(current_user)
end
#Need help with below code
def interested
#intrigue = User.find(params[:id])
#message = Message.create(:subject => "#{user_id} is Interested in you",
:sender_id => #user_id,
:recipient_id => #intrigue.user_id,
:body => "I saw your date and I'm interested")
#intrigue.message = #message
render :new, alert: 'Your message was sent.'
end
Routes:
resources :intrigues do
member do
post :repost
post :interested
end
end
I added has_one :intrigue to the Message model.
I added belongs_to :message to the Intrigue model.
The intrigues table has the following columns: id, content, user_id
The messages table has the following columns: id, sender_id, recipient_id, subject, body
Remove the line #intrigue.message = #message
Your sender id and recipient id are incorrect also.
def interested
#intrigue = User.find(params[:id])
#intrigue = current_user
#recipient = Intrigue.find(params[:id])
#message = Message.create(:subject => "Someone is Interested in you",
:sender_id => #intrigue.id,
:recipient_id => #recipient.user_id,
:body => "I saw your date and I'm interested")
render :new, alert: 'Your message was sent.'
end
That will have it working with no errors. I would also add a redirect.
This issue is about: ActiveMerchant + PaypalExpressCheckout + Rails 3.2
I've been trying to build a Paypal Express Checkout on my Rails 3.2 app. Most of the tutorials out there are outdated so I followed a few then read the Paypal Express Checkout integration guide. I've already set up my Sandobx and my paypal informations.
When I try to process the payment by clicking on my "Buy now" link from my view:
<%= link_to image_tag('http://img36.imageshack.us/img36/249/buttonpaypal.png'),
action: 'checkout', controller: 'orders'%>
I am getting the following error:
This transaction is invalid. Please return to the recipient's website to complete
you transaction using their regular checkout flow.
Return to merchant
At this time, we are unable to process your request. Please return to and try
another option.
--- My Controller:
class OrdersController < ApplicationController
include ActiveMerchant::Billing
def checkout
setup_response = ::GATEWAY.setup_purchase(2000,
:ip => request.remote_ip,
:return_url => url_for('confirm'),
:cancel_return_url => url_for(root_path)
)
redirect_to ::GATEWAY.redirect_url_for(setup_response.token)
end
end
--- My Initializer ActiveMerchant.rb:
ActiveMerchant::Billing::Base.mode = :test
::GATEWAY = ActiveMerchant::Billing::PaypalExpressGateway.new(
:login => "I_PUT_MY_EMAIL_HERE",
:password => "I_PUT_MY_PASS_HERE",
:signature => "I_PUT_MY_SIGNATURE_HERE",
:allow_guest_checkout => true
)
--- My routes: routes.rb:
resources :orders do
# Im not sure why 'get :checkout' by itself doesn't work.
get :checkout, :on => :new
get :confirm
get :complete
end
get "pages/index"
This is the gist: https://gist.github.com/11be6cef6a97632343b9
Can anyone point me to a 'recent' tutorial or help me figure out what I am doing wrong here?
The easiest way is to do as follow:
1.) You must create a paypal test account.
2.) Create a Cart Model:
$ rails g model Cart purchased_at:datetime
3.) In your Cart Model Type:
class Cart < ActiveRecord::Base
def paypal_url(return_url)
values = {
# get it form your http://sandbox.paypal.com account
:business => 'ENTER_THE_SELLER_PAYPAL_EMAIL_ADDRESS',
:cmd => '_cart',
:upload => 1,
:return => return_url,
:invoice => id
}
# These values set up the details for the item on paypal.
values.merge!({
# The amount is in cents
"amount_1" => ENTER_AN_AMOUNT_HERE,
"item_name_1" => ENTER_THE_ITEM_NAME_HERE,
"item_number_1" => 1,
"quantity_1" => 1
})
"https://www.sandbox.paypal.com/cgi-bin/webscr?" + values.to_query
end
end
4.) On the appllication_controller.rb file add this
def current_cart
session[:cart_id] ||= Cart.create!.id
#current_cart ||= Cart.find(session[:cart_id])
end
5.) On your the view where you want the checkout button add this:
# 'products_url' is just the url where you would like to redirect
# the user after the transaction
<%= link_to 'Buy with PAYPAL', #cart.paypal_url(products_url) %>
6.) On the controller show action of the view where you want the checkout add this:
def show
...
#cart = current_cart
end
Thats it! This is a PaypalExpressCheckout without a 'real' Cart since I built this Cart without using a Line Item. But you could add a Line Item to it following the Railscast #141 Paypal Basics http://railscasts.com/episodes/141-paypal-basics
There's a recent tutorial here: http://spin.atomicobject.com/2011/10/24/integrating-paypal-express-with-rails-3-1-part-1/.
I want to integrate Paypal within the Devise user registration process. What I want is to have a standard rails form based on the devise resource, that also has custom fields belonging to the user's model.
When a user fills in those fields and clicks on signup, it will be redirected to Paypal, when he clears from paypal and returns to our site then the user data must be created.
For the scenario where the user fill's out the paypal form but doesn't come back to our site, we have to keep record of user before redirecting to Paypal.
For this we can create a flag in user model and use Paypal IPN and when the user transaction notified, set that flag.
But in the case when the user is redirected to Paypal but doesn't complete the transaction, if the user returns to registration and signup again, our model should not throw error saying that the email entered already exists in the table.
How can we handle all these scenarios, is there any gem or plugin available to work with?
Here i am posting the detail code for performing the whole process.
registration_controller.rb
module Auth
class RegistrationController < Devise::RegistrationsController
include Auth::RegistrationHelper
def create
#user = User.new params[:user]
if #user.valid?
redirect_to get_subscribe_url(#user, request)
else
super
end
end
end
end
registration_helper.rb
module Auth::RegistrationHelper
def get_subscribe_url(user, request)
url = Rails.env == "production" ? "https://www.paypal.com/cgi-bin/webscr/?" : "https://www.sandbox.paypal.com/cgi-bin/webscr/?"
url + {
:ip => request.remote_ip,
:cmd => '_s-xclick',
:hosted_button_id => (Rails.env == "production" ? "ID_FOR_BUTTON" : "ID_FOR_BUTTON"),
:return_url => root_url,
:cancel_return_url => root_url,
:notify_url => payment_notifications_create_url,
:allow_note => true,
:custom => Base64.encode64("#{user.email}|#{user.organization_type_id}|#{user.password}")
}.to_query
end
end
payment_notification_controller.rb
class PaymentNotificationsController < ApplicationController
protect_from_forgery :except => [:create]
layout "single_column", :only => :show
def create
#notification = PaymentNotification.new
#notification.transaction_id = params[:ipn_track_id]
#notification.params = params
#notification.status = "paid"
#custom = Base64.decode64(params[:custom])
#custom = #custom.split("|")
#user = User.new
#user.email = #custom[0]
#user.organization_type_id = #custom[1].to_i
#user.password = #custom[2]
if #user.valid?
#user.save
#notification.user = #user
#notification.save
#user.send_confirmation_instructions
end
render :nothing => true
end
def show
end
end
I have created a Ruby on Rails application where users can track workouts. The can do so either privately or publicly. On workouts which are public ( workout.share == 1 ) I allow users to comment. When a comment is created on a workout, the workout owner is notified via email. That all works great.
I am now looking for some advice on the best way to allow users who have commented on a workout, to also be notified via email. Here is an example.
User A creates Workout 1. User B comments on Workout 1 and User A receives an email notification. User C also comments on Workout 1 and both User A and User B receive email notifications.
What is the best way to tell my application to loop through all the users who have commented on Workout 1 and send an email to them?
Currently I am sending an email to the workout owner with the following code in the comments_controller (I realize this could be cleaner code):
class CommentsController < ApplicationController
...
def create
#workout = Workout.find(params[:workout_id])
#comment = #workout.comments.build(params[:comment])
#comment.user = current_user
respond_to do |format|
if #comment.save
if #comment.workout.email_notification == 1
#comment.deliver_comment_notification_mail!
format.html { redirect_to( projects_path) }
format.js
else
format.html { redirect_to( projects_path) }
format.js
end
else
end
end
end
...
and in comment_mailer.rb
def comment_notification_mail(comment)
subject "Someone commented on your Workout"
recipients("#{comment.workout.user.username} <#{comment.workout.user.email}>")
from("foobar")
body :comment => comment,
:commenter => comment.user,
:workout => comment.workout,
:commentee => comment.workout.user,
:workout_url => workout_url(comment.workout),
:commenter_url => user_url(comment.user)
end
To find out a workout owner and commenter is not a hard job. My suggestions are:
move the code of sending email in your controller to your model, using #after_create, eg:
class Comment < ActiveRecord::Base
#...
after_create :notify_subscribers
def subscribers
(self.workout.commenters << self.workout.owner).uniq
end
def notify_subscribers
#... implemented below
end
end
using delayed_job or other tools to put the email sending job to background, or the request would be blocked until all the emails has been sent. eg, in the #notify_owner_and_commenter method
def notify_subscribers
self.subscribers.each do |user|
CommentMailer.send_later :deliver_comment_notification_mail!(self, user)
end
end
Then you need to refactor you #deliver_comment_notification_mail! method with two arguments.
Delayed job ref: https://github.com/tobi/delayed_job
From my POV, it's all the work of the mailer. I'd just rewrite the comment_notification_mail to something more neutral (which could speak to workout owner and commenters).
Then something like:
def comment_notification_mail(comment)
recs = [comment.workout.user]
recs << comment.workout.comments(&:user)
recs -= comment.user
subject "Someone commented on your Workout"
recipients(recs.inject('') { |acc, r| "#{r.username} <#{r.email}>" })
from("foobar")
body :comment => comment,
:commenter => comment.user,
:workout => comment.workout,
:commentee => comment.workout.user,
:workout_url => workout_url(comment.workout),
:commenter_url => user_url(comment.user)
end
Of course, if mails are not supposed to be public, send by bcc ;)