I'm building an events app using Ruby on Rails. Events can be either free or paid. In each instance I want to be able to assign a unique booking number for users when they book their places on an event.
What do I need to do to ensure both paid and free events are assigned a number in the server and a booking is then confirmed? Is it as simple as adding a booking_number column in my bookings table?
This is my model and controller code -
booking.rb
class Booking < ActiveRecord::Base
belongs_to :event
belongs_to :user
#validates :quantity, presence: true, numericality: { greater_than: 0 }
validates :total_amount, presence: true, numericality: { greater_than: 0 }
validates :quantity, :total_amount, presence: true
def reserve
# Don't process this booking if it isn't valid
self.valid?
# We can always set this, even for free events because their price will be 0.
#self.total_amount = booking.quantity * event.price
# Free events don't need to do anything special
if event.is_free?
save!
# Paid events should charge the customer's card
else
begin
self.total_amount = event.price_pennies * self.quantity
charge = Stripe::Charge.create(
amount: total_amount,
currency: "gbp",
source: stripe_token,
description: "Booking created for amount #{total_amount}")
self.stripe_charge_id = charge.id
save!
rescue Stripe::CardError => e
errors.add(:base, e.message)
false
end
end
#end
end
end
bookings_controller.rb
class BookingsController < ApplicationController
before_action :authenticate_user!
def new
# booking form
# I need to find the event that we're making a booking on
#event = Event.find(params[:event_id])
# and because the event "has_many :bookings"
#booking = #event.bookings.new(quantity: params[:quantity])
# which person is booking the event?
#booking.user = current_user
##total_amount = #event.price * #booking.quantity
end
def create
puts params
# actually process the booking
#event = Event.find(params[:event_id])
#booking = #event.bookings.new(booking_params)
#booking.user = current_user
if
#booking.reserve
flash[:success] = "Your place on our event has been booked"
redirect_to event_path(#event)
else
flash[:error] = "Booking unsuccessful"
render "new"
end
end
private
def booking_params
params.require(:booking).permit(:stripe_token, :quantity, :event_id, :stripe_charge_id, :total_amount)
end
end
This is my views code. There's an if/else statement here depending on whether its a paid event or not. The free events 'booking_id' simply shows as blank at the moment - not sure why.
booking.new.html.erb
<% if #event.is_free? %>
<div class="col-md-6 col-md-offset-3" id="eventshow">
<div class="row">
<div class="panel panel-default">
<div class="panel-heading">
<h2>Your Booking Confirmation</h2>
</div>
<div class="panel-body">
<h1>Hi there</h1>
<p>You have placed a booking on <%= #event.title %></p>
<p>Your order number is <%= #booking.booking_id %></p>
<p>We hope you have a wonderful time. Enjoy!</p>
<p>Love from Mama Knows Best</p>
</div>
<div class="panel-footer">
<%= link_to "Home", root_path %>
</div>
</div>
</div>
</div>
<% else %>
<div class="col-md-6 col-md-offset-3" id="eventshow">
<div class="row">
<div class="panel panel-default">
<div class="panel-heading">
<h2>Confirm Your Booking</h2>
</div>
<%= simple_form_for [#event, #booking], id: "new_booking" do |form| %>
<div class="calculate-total">
<p>
Confirm number of spaces you wish to book here:
<input type="number" placeholder="1" name="booking[quantity]" min="1" value="1" class="num-spaces">
</p>
<p>
Total Amount
£<span class="total" data-unit-cost="<%= #event.price %>">0</span>
</p>
</div>
<span class="payment-errors"></span>
<div class="form-row">
<label>
<span>Card Number</span>
<input type="text" size="20" data-stripe="number"/>
</label>
</div>
<div class="form-row">
<label>
<span>CVC</span>
<input type="text" size="4" data-stripe="cvc"/>
</label>
</div>
<div class="form-row">
<label>
<span>Expiration (MM/YYYY)</span>
<input type="text" size="2" data-stripe="exp-month"/>
</label>
<span> / </span>
<input type="text" size="4" data-stripe="exp-year"/>
</div>
</div>
<div class="panel-footer">
<%= form.button :submit %>
</div>
<% end %>
<% end %>
</div>
</div>
</div>
So you can use the SecureRandom Generator like this, and assuming your model is named Booking
### Add this in your model
before_create :add_booking_number
validates_uniqueness_of :add_booking_number
def add_booking_number
begin
self.booking_number = SecureRandom.hex(2).upcase
other_booking = booking_number.find_by(booking_number: self.booking_number)
end while other_booking
end
To use the Moniker "MAMA" you can do
def add_event_ident
self.booking_number = "MAMA" + '- ' + SecureRandom.hex(2).upcase
...
end
the (2) at the end of the .hex controls the length of the generated string (2) will produce 4 random letters. You can find the best length that works for you by going into rails console and playing with it ie.
SecureRandom.hex(4) ...
You can create a new column like booking_id in your booking table and assign a unique random before the booking object is created.
Ref: Source
If you want the random to be human readable, you can try this
Related
I'm new to Rails and I'm using translate to post here.
I'm practicing rails and for that I set up a poll (voting) system.
Until then the voting is working normally, the votes are stored correctly, however, I would like to set up a screen on the frontend to display the result, and that's where I'm having difficulty.
How can I sum the votes from the PollOptions table of the "Votes" column. I would like to display it like this:
name: 10 votes or X% of votes
name: 10 votes or X% of votes
name: 10 votes or X% of votes
As I understand it, I need to create a method in the controller that will do this sum?
Any documentation links/tips, I'd appreciate it.
polls_controller
class PollsController < ApplicationController
def index
#poll = Poll.where(finished_at: nil).last
#poll_options = #poll.poll_options.order :name
end
def vote
#poll_option = PollOption.find params[:poll_option_id]
#poll_option.increment! :votes
respond_to do |format|
format.turbo_stream { render :vote }
end
end
end
model:
poll
class Poll < ApplicationRecord
has_many :poll_options
end
poll_option
class PollOption < ApplicationRecord
belongs_to :poll
end
In this view, I would like to call the result of the vote.
view _poll.html.erb
<div id="poll-container" class="col-lg-8 mx-lg-auto">
<div class="card shadow">
<div class="card-header">
<h2 class="card-title text-center">
<%= #poll.name %>
<p>
<%= #poll_options.pluck(:name).to_sentence(last_word_connector: ' ou ') %>
</h2>
</div>
<div class="card-body">
<div class="d-flex flex-column bd-highlight mb-3">
<% #poll_options.each do |item| %>
<p>
<%= button_to vote_url,
method: :post,
class: "btn btn-outline-secondary",
params: { poll_option_id: item.id } do %>
<h2>
<%= item.name %>
<%= image_tag item.photo, size: "40x40" %>
</h2>
<% end %>
<% end %>
</div>
</div>
</div>
</div>
https://i.stack.imgur.com/JQfQP.png
Thanks in advance for any guidance.
You can use ActiveRecord::Calculations sum (.sum(:votes)) to sum all the rows for the votes column under poll_options.
To find all the votes of a poll you need the following
#poll.poll_options.sum(:votes)
Then to create the % for the PHP for example you could do
total_votes = #poll.poll_options.sum(:votes)
php_votes = #poll.poll_options.where(name: 'PHP').votes
(php_votes / total_votes).to_i * 100
very new and need guidance, I am a very junior dev. Working on my first project: auth. project - that i've been working on for several months. but I am hitting a wall on how to pull my user.id from my User_controller and import or pull that data into my index.html.erb view.
plan is to have "user.id". render on the page with a label field dynamically. So, if a X user signs in, that "user_id" will show on this label form field by default to show X user is signed, and create some logic later to have my controller validate user and allow to prompt a new modal to change password, but when after user is login, and it routes to /dashboard view, For what ever reason, I can't get the user_id to show in my Label >user_id< using from bootstrap. I feel like this might be easy, but I'm not sure I am fully understand how rails can inject that data resource on the page dynamically
I have tried <%= #{:user_id}%>, change the label type id:/user_id with <%= #{:user_id}%>
this is what my user.controller looks like:
class UserController < ApplicationController
def index
# rendering json on page: user data
render json: User.all
end
def create
user = User.new(
first_name: params[:fname],
last_name: params[:lname],
username: params[:username],
password: params[:password],
password_confirmation: params[:password_confirmation]
)
if user.save
session[:user_id] = user.id
flash[:success] = "text"
#redirect_to '/login'
else
flash[:warning] = "text"
#redirect_to 'register'
end
end
def show
# render plain: params[:id]
#render json: userdata[:users].select {|user| user.get_id() == params[:id].to_i}
if User.exists?(params[:id])
render json: User.find(params[:id].to_i)
else
render plain: "that user doesnt exist: #{params[:id]}"
end
end
def validate
puts params
username = params[:username]
exists = User.exists?(username: username)
# puts exists
render json: {"exists": exists, "username": username}
end
end
and this is my html.erb
<% content_for :content do %>
<div class="container">
<div class="row">
<div class="col-12">
<h1 class=display-4>Welcome to your home page </h1>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-12">
<div class="jumbotron">
<p class="lead">needing to change your password - <strong class="text-success">reset your password easy</strong>.</p>
</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-12">
<form class="row g-3">
<div class="col-auto">
<label for="username" class="visually-hidden">Example:user_id</label>
<input type="text" readonly class="form-control-plaintext" id="username" value="Helloguy123#">
</div>
<div class="col-auto">
<label for="password" class="visually-hidden">Password</label>
<input type="password" class="form-control" id="password" placeholder="Password">
</div>
<div class="col-auto">
<button type="submit" class="btn btn-primary mb-3">Confirm identity</button>
</div>
</form>
<% end %>
<%= render template: "layouts/application" %>
I have Stripe integrated with Stripe Gem and I can successfully create charges and a Stripe user; I just can't get the two to have an association. If I associate the :source => params[:stripeToken] the charge goes through, but if I associate it with my user model :source => current_user.stripe_customer, I get a No such token: cus_BX2RXGoOVkpvhL error.
Here are my request parameters when associating with my user model.
Request
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"/KI7rzkJsM2uXIynoZkWKa1L+kNgyVcw+UsqfPNOnLYO3t4vfL+R7Yb4XhonyhwTbqlyWTtgYjey71ZroSmoRA==",
"charge"=>"2200",
"stripeToken"=>"tok_1B9yneHbiQgBeZWGq78mXM2l",
"card_brand"=>"Visa",
"card_exp_month"=>"4",
"card_exp_year"=>"2024",
"card_last4"=>"4242"}
views/donations/show.html.erb
...
<%= link_to 'Confirm Donation', new_checkout_path(charge: #donation.amount), class: 'btn btn-primary' %>
...
views/checkouts/new.html.erb
<section>
<h1>Secure Payment</h1>
<div class="pages-content">
<% if current_user.card_last4? %>
<%= form_tag checkout_path, id: 'existing-card' do %>
<p>Pay with existing card:</p>
<div><strong>Card on file:</strong>
<%= current_user.card_brand %> (**** **** **** <%= current_user.card_last4 %>)
</div>
<div><strong>Expiration:</strong> <%= current_user.card_exp_month %> / <%= current_user.card_exp_year %></div>
<p>or <%= link_to 'add a new card', '#', class: 'show-card-form' %></p>
<%= hidden_field_tag :charge, params[:charge] %>
<input type="submit" class="btn btn-default" value="Submit Payment">
<% end %>
<% end %>
<%= form_tag checkout_path, id: 'payment-form', style: (current_user.card_last4? ? 'display:none' : nil) do %>
<div class="stripe-wrapper">
<div class="form-row">
<label for="card-element">
Enter Your Credit or Debit Card
</label>
<div id="card-element">
<!-- a Stripe Element will be inserted here. -->
</div>
<%= hidden_field_tag :charge, params[:charge] %>
<!-- Used to display Element errors -->
<div id="card-errors" role="alert"></div>
</div>
<button>Submit</button>
</div>
<% end %>
</div>
</section>
models/user.rb
class User < ApplicationRecord
...
def stripe_customer
if stripe_id?
Stripe::Customer.retrieve(stripe_id)
else
stripe_customer = Stripe::Customer.create(email: email)
update(stripe_id: stripe_customer.id)
stripe_customer
end
end
You need to do this instead, if you're charging the Customer by ID:
:customer => current_user.stripe_customer
I had a similar problem once and i remember that it was because my partner was using a different [Publishable key] on Testing mode, So i gave him mine and it worked, I would like you to make sure that the user is already exists on stripe.
I hope this help you.
I am making a checkout form with stripe and rails. I have a shop_controller that serves a list of products in the shop#index. Then that leads to the shop#show where the stripe form is located, and users can purchase a product.
Right now when clicking the button to make a purchase (using the fake card stripe gives you), all that happens is the button endlessly spins, and I get the following errors in the console:
GET https://checkout.stripe.com/api/account/lookup?email=MYEMAILADDRESS&key=&locale=en-US 400 (Bad Request)
and
Uncaught SecurityError: Failed to read the 'contentDocument' property from 'HTMLIFrameElement': Blocked a frame with origin "http://localhost:3000" from accessing a frame with origin "https://checkout.stripe.com". The frame requesting access has a protocol of "http", the frame being accessed has a protocol of "https". Protocols must match.
When I am adding my email to the form, I am also getting this error:
The specified value "MYEMAILADDRESS" is not a valid email address.
Here is my set up
routes.rb
resources :shop
shop_controller
class ShopController < ApplicationController
def index
#products = Product.all
end
def show
#product = Product.find(params[:id])
end
def new
end
# the create happens in the show action
def create
#product = Product.find(params[:id])
customer = Stripe::Customer.create(
:email => params[:stripeEmail],
:source => params[:stripeToken]
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => #product.price * 100,
:description => #product.description,
:currency => 'usd'
)
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to new_charge_path
end
end
show.html.erb (where the form is rendered to pay)
<div class='shop-show'>
<div class="container">
<div class="row text-center">
<div class="col-lg-12 col-xs-12">
<h2><%= #product.name %></h2>
<hr class="generic">
</div>
</div>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<div class="white_panel">
<%= form_for #product, url: url_for(controller: 'shop', action: 'create' ) do %>
<% if flash[:error].present? %>
<div id="error_explanation">
<p><%= flash[:error] %></p>
</div>
<% end %>
<script src="http://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="<%= Rails.configuration.stripe[:publishable_key] %>"
data-description="<%= #product.description %>"
data-amount="<%= #product.price * 100 %>"
data-locale="auto"
data-shipping-address=true>
</script>
<% end %>
<img class="img-responsive" width='400' height='250' src="<%= #product.pictures.first.image.url %>">
<h2><%= #product.description %></h2>
</div>
</div>
</div>
</div>
</div>
Has anyone set up this kind of functionality before? This was closely followed using the link provided by stripe: https://stripe.com/docs/checkout/rails. Any help with this set up would be very appreciated. Thanks.
The Checkout ctript tag's URL there needs to be retrieved over HTTPS. Try swapping this:
<script src="http://checkout.stripe.com/checkout.js"
for this:
<script src="https://checkout.stripe.com/checkout.js"
I am trying to set up stripe in rails and I can enter in the credit card info but when it redirects I get the following error:
No route matches [POST] "/enrollment/1"
I am confused because when I rake routes I get:
enrollment_index GET /enrollment(.:format) enrollment#index
POST /enrollment(.:format) enrollment#create
new_enrollment GET /enrollment/new(.:format) enrollment#new
edit_enrollment GET /enrollment/:id/edit(.:format) enrollment#edit
enrollment GET /enrollment/:id(.:format) enrollment#show
PATCH /enrollment/:id(.:format) enrollment#update
PUT /enrollment/:id(.:format) enrollment#update
DELETE /enrollment/:id(.:format) enrollment#destroy
So I am confused why it is trying to post because in my view, I specify that the method should be put:
<div class='container'>
<div class='jumbotron enroll-page'>
<div class='row'>
<h1>Enroll Here</h1>
<%= form_tag enrollment_path(1), :method => :put do %>
<article>
<label class="amount">
<span>Amount: $5.00</span>
</label>
</article>
<script src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="<%= Rails.configuration.stripe[:publishable_key] %>"
data-description="A month's subscription"
data-amount="500"></script>
<% end %>
</div>
</div>
</div>
My controller looks like this:
class EnrollmentController < ApplicationController
before_filter :authenticate_user!, unless: :admin_signed_in?
def new
end
def create
#get credit card details submitted by form
token = params[:stripeToken]
customer = Stripe::Customer.create(
:card => token,
:plan => 3455,
:email => current_user.email
)
current_user.paid = true
current_user.stripeid = customer.id
current_user.save
redirect_to courses_path, :notice => 'Your enrollment is complete!'
end
end
Thanks so much for any help that you can give!
EDIT: I think I have it. Ajay was totally right that this works better if I name the controller "enrollments" instead of enrollment. In the new.html.erb view, it should look like this instead:
<div class='container'>
<div class='jumbotron enroll-page'>
<div class='row'>
<h1>Enroll Here</h1>
<%= form_tag enrollments_path do %>
<article>
<label class="amount">
<span>Amount: $5.00</span>
</label>
</article>
<script src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="<%= Rails.configuration.stripe[:publishable_key] %>"
data-description="A month's subscription"
data-amount="500"></script>
<% end %>
</div>
</div>
</div>
check in routes the resource must be enrollments not enrollment
#routes.rb
resources :enrollments
Now when you run
rake routes | grep 'enrollment'
new_enrollment GET /enrollments/new(.:format) enrollments#new
#app/controller/enrollments_controller.rb
class EnrollmentsController < ApplicationController
def new
#enrollment = Enrollment.new
end
def edit
#enrollment = Enrollment.where(id: params[:id]).first
end
def create
#catch your params and do whatever.
end
def update
# catch your params and do your operation.
end
end
Now comes the View :
app/views/enrollments/new.html.erb
<%= render 'form %>
app/views/enrollments/edit.html.erb
<%= render 'form' %>
now in your app/views/enrollments/_form.html.erb
<%= form_tag(#enrollment)do %>
<%= label_tag :amount %>
<%= text_field_tag :amount, 50.00 %>
<%= submit_tag %>
<% end %>
In your console, the log should be something like this :
Started GET "/enrollments/new" for 127.0.0.1 at 2015-01-10 06:18:08 +0530
Processing by EnrollmentsController#new as HTML
This confirms you are hitting the new page not the edit. Check it !
Inspect your form, this should look like this, check it.