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
Related
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.
I'm trying to send an email to the client when he successfully makes a transaction using paypal.
I've already manage to send the custom email parameter to paypal in a custom parameter they provide.
What I have right now
My product model:
class Product < ActiveRecord::Base
# This defines the paypal url for a given product sale
def paypal_url(return_url, cancel_return, useremail)
values = {
:business => 'your_business_email#example.com',
:cmd => '_xclick',
:upload => 1,
:return => return_url,
:rm => 2,
:cancel_return => cancel_return,
:custom => useremail
}
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
end
here, I'm passing a parameter for the :custom object which I have it hardcoded in the button link_to helper here:
<%= link_to 'checkout', #product.paypal_url(payment_notification_index_url, root_url, 'testing#testing.com') %>
This works perfectly, and I am able to store the custom email in the database:
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], email: params[:custom] )
# render nothing: true
if #payment.status == 'Completed'
PaymentTransactions.success(#payment).deliver_now
redirect_to root_url, notice: 'Success!'
else
redirect_to root_url, notice: 'Error'
end
end
end
Question
How do I get the client to input their email in a field and pass that value into the parameters of the link_to so that paypal return the email so I can store it in the database and send an email to the client?
Thanks
You should not use link_to, but form_tag with method: :get
<%= form_tag (#product.paypal_url(payment_notification_index_url, root_url, :custom)),method: :post do %>
<%= text_field_tag :custom %>
<%= submit_tag 'checkout' %>
<% end %>
This might be more than what you're expecting...but read on.
Before you dive further into the implementation, keep in mind that, you're using the sandbox version of Paypal for testing, and in production, you'd want the paypal_url to return an encrypted url for the user as to avoid tampering of the transaction, such as changing the price (more details at Railscast #143).
Now, realize that any approaches on the client-side via javascript to get the user email field and modify the link will not be secure as the link should be generated from your server after encryption (and you'd need to pass in the user email as part of the call).
So, what can you do? Use ajax to send the request to the server containing the parameters (e.g. return_url, user_email, etc..), and respond in the server with an encrypted link. Then, you can use javascript to replace the link and allow user to click that instead.
As you realize, the implementation above is very general and any answer would not suit your specific case. You should keep the above in mind as you'd be required to do that anyway down the road.
am currently working on integrating my sample site with Authorize.NET.
So I created a Sandbox account in Authorize.NET.
Using this test account, I am testing DPM and using Relay Response URL.
I have Public IP
I am setting the x_relay_response="TRUE"; x_relay_url="http://182.180.157.5:3000/payments/relay_response";
Controller
class PaymentsController < ApplicationController
layout 'authorize_net'
helper :authorize_net
protect_from_forgery :except => :relay_response
# GET
# Displays a payment form.
def payment
#amount = 125.00
#sim_transaction = AuthorizeNet::SIM::Transaction.new(AUTHORIZE_NET_CONFIG['api_login_id'], AUTHORIZE_NET_CONFIG['api_transaction_key'], #amount, :relay_response => true, :relay_url => "http://182.180.157.5:3000/payments/relay_response")
end
# POST
# Returns relay response when Authorize.Net POSTs to us.
def relay_response
puts "In Relay Response"
puts "Params: #{params}"
sim_response = AuthorizeNet::SIM::Response.new(params)
if sim_response.success?(AUTHORIZE_NET_CONFIG['api_login_id'], AUTHORIZE_NET_CONFIG['merchant_hash_value'])
render :text => sim_response.direct_post_reply("http://182.180.157.5:3000/payments/receipt", :include => true)
else
render
end
end
# GET
# Displays a receipt.
def receipt
#auth_code = params[:x_auth_code]
end
end
Getting This Error
An error occurred while trying to report this transaction to the merchant. An e-mail has been sent to the merchant informing them of the error. The following is the result of the attempt to charge your credit card.
This transaction has been approved.
It is advisable for you to contact the merchant to verify that you will receive the product or service.
Any Solution For This
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 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 ;)