Pass a text_field custom parameter to a link_to paypal IPN - ruby-on-rails

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.

Related

Pass an object from one controller action to another

I have a request object that I want to pass to my charges_controller. I can pass it in to the new action but after that new action is called I need that same request object passed to the create object in the same charges_controller. I can not get it. Here is the initial request_controller action passing the request_id to the charges controller.
class Users::RequestsController < ApplicationController
before_filter :authenticate_user!
def create
#user = current_user
if #request = #user.request.create!(authorization_params)
if #request.user_paying == true
redirect_to new_users_charge_path(id: #request)
else
redirect_to users_dashboard_path
end
else
redirect_to users_dashboard_path, :error => "There is something wrong and your request has not been submitted."
end
end````
It then passes it to the new method in the charges controller. Here is my charges controller.
class Managers::ChargesController < ApplicationController
before_filter :authenticate_user!
def new
#request = Request.find(params[:id])
#user = current_user
#amount = #request.user_amount
end
def create
# Amount in cents
#request = Request.find params[:request_id]
#user = current_user
#amount = #request.user_amount
customer = Stripe::Customer.create(
:email => #user.email,
:card => params[:stripeToken]
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => #amount,
:description => 'Manager Paid Report',
:currency => 'usd'
)
#request.report_paid = true
#request.save!
redirect_to managers_dashboard_path, :notice => "You have succesfully requested a pre-paid report from #{#reportapproval.tenant_last_name}."
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to managers_charges_path
end
end
I can get the request info into the charges/new.html view. But when I create the charge the request is not found. How can I pass the request to the create action?
Here is the new view...
<h4>Total Payment Due: <%= number_to_currency(#amount.to_i/100.0) %>
<%= form_tag users_charges_path do %>
<%= hidden_field_tag :request_id, #request.id %>
<br />
<script src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="<%= Rails.configuration.stripe[:publishable_key] %>"
data-description="Payment"
data-amount="<%= #amount %>"
data-locale="auto" >
</script>
<% end %>
Controllers simply respond to HTTP requests - there is no way to forward a request internally in Rails. This is a very conscious design decision.
If you need to stash data in between requests you would either save it in the database, the session or some sort of caching mechanism like Memcached/Redis.
Most commonly in Rails you stash data in the database and pass ID's or other unique identifiers in the request parameters.
If you need to pass the id to the "Request" object you would either pass it in the request url or in the request body in the case of PUT/PATCH/POST.
So in your case you need to ensure that the #new action either posts to:
post "/managers/charges/:request_id"
Or that the form includes
<%= hidden_field_tag 'request_id', #request.id %>
PS
Request although not technically a reserved word is a really bad name for a model since it will eventually cause conflicts and confusion with ActionDispatch::Request especially if you use the controller instance variable #request.
As you can see from the my answer above it gets confusing as h*ll since the word request is allready has a very specific connotation in web development.
The same applies to Response.
Use PaymentRequest or any other synonym.
Assuming you have a form in the charges/newview, you can set the request_id using a hidden form field:
<%= hidden_field_tag :request_id, #request.id %>
You should add the line above between <%= form_for ... do |f| %> and <% end %>.
In that example, you were assuming the create action would somehow have a request_id passed in automatically. Rails doesn't do that, in every request you need to explicitly provide all the information for the controller to process that request.
I hope that helps! ;)

Step by step to create a simple checkout using paypal on rubyonrails

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

Ruby on Rails: Send email from show page

I am a Rails noob and have a problem sending an email from a show page. There are several contact form tutorials out there but I cannot find one where I send an email from a page like a 'show' page. I have big errors in my routes I believe. In the model I state that Users have several Promotions and on the promotions show page I want to allow the current_user to send an email to #user.
here is app/mailers/quote_mailer.rb
class QuoteMailer < ActionMailer::Base
default :from => "tim#example.com"
def quote_mail(promotion)
#user = user
mail(:to => user.email, :subject => "You have an inquiry homeboy!")
end
end
In promotions_controller I put this action which I think might be wrong:
def quotedeliver
QuoteMailer.quote_mail.deliver
flash[:notice] = 'report sent!'
redirect_to root_path # or wherever
end
Here is the form that I use to send the email (the :url is probably wrong but I dont know how it should look)
<%= form_for quote_mail, :url => quotedeliver_promotion_path(promotion), :html => {:method => :put } do |f| %>
<%= f.text_area :body %>
<%= f.submit %>
<% end %>
I would love some help with this. I cannot find anything like it on stackoverflow, I have been trying for days. Thank you!
You are probably missing the route in config/routes.rb
you can define it like
post '/quotedeliver_promotion' => 'promotions#quotedeliver', :as => quotedeliver_promotion
Note that quotedeliver has to be rewritten quote_deliver to follow ruby syntax conventions. When you call
QuoteMailer.quote_mail.deliver
You are not giving the parameter, so try this
QuoteMailer.quote_mail(current_user).deliver
And change your method with
def quote_mail(user)
mail ....
end
and you are all good
There are some excellent screen casts (through Railscasts) regarding sending e-mail http://railscasts.com/?tag_id=28.
One last thing, do not attach your mail sending method to a show action (if you are doing currently), the reason is show action is only for view something and users might be refreshing that page, So if you attach a mailer to that, mails might go out for each refresh.
def quote_mail(promotion)
#user = user
mail(:to => user.email, :subject => "You have an inquiry homeboy!")
end
from where you are access user variable, it should be promotion i think.

RoutingError on empty field, conflicting with friendly_id

I have a edit form for #users.
In there I have a text_field :username
<%= form_for #user, :url => { :action => "update" } do |f| %>
<%= render 'shared/error_messages', :target => f.object %>
<div class="field">
<%= f.label :username %><br />
<%= f.text_field :username %>
</div>
In my User model I use the friendly_id gem, set to :username as well. So my urls look like domain.com/users/:username instead of :id.
has_friendly_id :username
Now in my application layout, I also use #user.username in my navigation to link to the profile.
All works well, EXCEPT if I leave my :username field empty on save. It fails to save cause of validations,
validates :username, :presence => true, :uniqueness => { :case_sensitive => false }, :length => { :maximum => 50 }
and tries to render "edit" again. But to render "edit" we need the username to create the link in the navbar. And apparently it's passed on as username => "", even though it rightfully so failed to save and proper validations are in place.
def update
#user = current_user
if #user.update_attributes(params[:user])
flash[:success] = "Account updated."
redirect_to :back
else
#title = "Edit"
render "edit"
end
end
So I end up with a RoutingError:
No route matches {:action=>"show", :controller=>"users", :id=>#<User id: 6, username: "", persistence_token: "c2f2545659c186131537155347f46f7da5eb0d51b27707e71da...", created_at: "2011-03-14 14:26:48", updated_at: "2011-03-15 01:54:33", email: "test#test.com", crypted_password: "0d6489b1447d278bc4f7c86bab13787f226a10a302b43ec02ff...", password_salt: "Lq2G80iSVeaitB5PDwf", perishable_token: "Tm7Jzyq8QutfaxL3JLZ8", active: true>}
First of all, redirecting to :back, even if you successfully change the username, will likely fail because the URL itself is going to be different. So you should probably do
redirect_to #user
instead.
If you want to use the username field as the basis of a friendly_id, then it's probably best to not let it be blank, which you could enforce by adding a validation to the model:
class User < ActiveRecord::Base
...
validates_presence_of :username
...
end
Alternatively, if for some reason in your app makes sense to have them blank, you can force Rails to generate the URL based on the numeric id rather than the friendly_id in these cases.
To do this, you would need to set username value to nil rather than blank, and then either do
redirect_to #user
or
redirect_to user_url(#user)
In this case you'll also want to use the :allow_nil => true option to has_friendly_id.
I'm the author of FriendlyId, if this doesn't solve your problem feel free to send a message to FriendlyId's Google Group or to me personally at norman#njclarke.com, and I'll try to help you out.
You don't really need to re-render the edit screen if your validation fails.
It'd probably work better if you updated your page using ajax and use a remote_form_for (Rails 2) or form_for :remote => true (Rails 3) when submitting your form. That way if your form passes the validation you can just redirect the user like you're trying to do, but you don't need to actually leave the page if your validation fails, you can just send the validation message back to the form.
Simone Carletti's blog has a pretty decent example on how to do it.
http://www.simonecarletti.com/blog/2010/06/unobtrusive-javascript-in-rails-3/
Ryan Bates has put up a simple example of how to use it as well.
http://railscasts.com/episodes/205-unobtrusive-javascript

Rails: Custom template for email "deliver_" method?

I'm building an email system that stores my different emails in the database and calls the appropriate "deliver_" method via method_missing (since I can't explicitly declare methods since they're user-generated).
My problem is that my rails app still tries to render the template for whatever the generated email is, though those templates don't exist. I want to force all emails to use the same template (views/test_email.html.haml), which will be setup to draw their formatting from my database records.
How can I accomplish this? I tried adding render :template => 'test_email' in the test_email method in emailer_controller with no luck.
models/emailer.rb:
class Emailer < ActionMailer::Base
def method_missing(method, *args)
# not been implemented yet
logger.info "method missing was called!!"
end
end
controller/emailer_controller.rb:
class EmailerController < ApplicationController
def test_email
#email = Email.find(params[:id])
Emailer.send("deliver_#{#email.name}")
end
end
views/emails/index.html.haml:
%h1 Listing emails
%table{ :cellspacing => 0 }
%tr
%th Name
%th Subject
- #emails.each do |email|
%tr
%td=h email.name
%td=h email.subject
%td= link_to 'Show', email
%td= link_to 'Edit', edit_email_path(email)
%td= link_to 'Send Test Message', :controller => 'emailer', :action => 'test_email', :params => { :id => email.id }
%td= link_to 'Destroy', email, :confirm => 'Are you sure?', :method => :delete
%p= link_to 'New email', new_email_path
Error I'm getting with the above:
Template is missing
Missing template
emailer/name_of_email_in_database.erb in view
path app/views
Try multipart may that work
def test_email
#email = Email.find(params[:id])
Emailer.send("deliver_#{#email.name}")
part :content_type => 'multipart/alternative' do |copy|
copy.part :content_type => 'text/plain' do |plain|
plain.body = render( :file => "conta.text.plain.erb", :email=>#email )
end
copy.part :content_type => 'text/html' do |html|
html.body = render( :file => "conta.text.html.erb", :email => #email )
end
end
end
Aph, I feel silly. I figured it out:
models/emailer.rb:
class Emailer < ActionMailer::Base
def method_missing(method, *args)
logger.info "method missing was called!!"
recipients "Test <test#test.com>"
body "#{ Email.find_by_name(method.to_s).body }"
end
end
Since the incoming method is basically the name of the record, I can pull the body content stored in the database directly and pass that in as the body of the email, bypassing the templates altogether.
I'd go "up a level"
In other words, use the same view for everyone's email. But in the view, render different text depending on the user_id.
Remember that the view can call the render method.
In my case, I let users upload email templates using the liquid template system. (Google for rails liquid templates.) Using liquid is good since it is safe--users can't include arbitrary ruby code in their templates.
Then my email view renders the liquid template and the customized email is thereby generated.

Resources