How to add params to success_url with Stripe - ruby-on-rails

I have a website where I have 3 subscription offers
Offer 1 = 4.99 euros for 30 days. (1 month)
Offer 2 = 12.99 euros for 90 days. (3 month)
Offer 3 = 23.99 euros for 180 days. (6 month)
With stripe I would like to add the right subscription duration depending on the offer chosen by the customer.
How to add a parameter to params?
I want to use it on the success method. How to "inject" my_params in the success_url ?
here is my controller:
class CheckoutController < ApplicationController
before_action :user_logged_in?, only: [:create]
def create
#user = current_user
#total = params[:total].to_d
#session = Stripe::Checkout::Session.create(
payment_method_types: ['card'],
line_items: [
{
name: 'Rails Stripe Checkout',
amount: (#total * 100).to_i,
currency: 'eur',
quantity: 1
},
],
success_url: checkout_success_url + '?session_id={CHECKOUT_SESSION_ID}',
cancel_url: checkout_cancel_url
)
respond_to do |format|
format.js
end
end
def success
#session = Stripe::Checkout::Session.retrieve(params[:session_id])
#payment_intent = Stripe::PaymentIntent.retrieve(#session.payment_intent)
#user = current_user
if params[:my_params] == 2
if #user.sub_lvl < 2
#user.update(sub_lvl: 2)
end
if #user.end_sub_date < Time.now
#user.update(end_sub_date: Time.now + 30.days)
else
#user.update(end_sub_date: end_sub_date + 30.days)
end
end
if params[:my_params] == 3
if #user.sub_lvl < 3
#user.update(sub_lvl: 3)
end
if #user.end_sub_date < Time.now
#user.update(end_sub_date: Time.now + 60.days)
else
#user.update(end_sub_date: end_sub_date + 60.days)
end
end
if params[:my_params] == 4
if #user.sub_lvl < 4
#user.update(sub_lvl: 4)
end
if #user.end_sub_date < Time.now
#user.update(end_sub_date: Time.now + 180.days)
else
#user.update(end_sub_date: end_sub_date + 180.days)
end
end
end
def cancel
#session = Stripe::Checkout::Session.retrieve(params[:session_id])
#payment_intent = Stripe::PaymentIntent.retrieve(#session.payment_intent)
end
private
def user_logged_in?
unless current_user
redirect_to new_user_session_path
end
end
end

To add params you would dynamically generate your Checkout Session success_url when you create your Checkout Session. This would require passing the necessary params to your create method like you do with dynamically accessing the amount.
Alternatively, you could just rely on the amount_subtotal of the Checkout Session to indicate which the Subscription plan.

Related

How to set karma to appropriate user

I'm trying to add a user karma feature to my app and I'm almost done, just that the karma is being awarded to a different user.
NB, My like system is from scratch and not acts_as_votable.
What I want:
When a user upvotes a book, I want a +1 karma be awarded to the
book.user
If a user's books are downvoted more then they upvoted, I want such
user have negative karma.
What I'm getting:
When a book is upvoted, the user who upvoted the book gets the +1
karma instead of the book.user.
When a user with 0 karma gets his/her book downvoted, the karma incrment by 1 instead of decrementing.
class AddKarmaToUsers < ActiveRecord::Migration[6.0]
def change
add_column :users, :karma, :integer, default: 0
end
end
My code:
vote.rb
class Vote < ApplicationRecord
belongs_to :user
belongs_to :book
validates_uniqueness_of :user_id, scope: :book_id
after_create :increment_vote, :add_karma
after_destroy :decrement_vote, :remove_karma
private
def increment_vote
field = self.upvote ? :upvotes : :downvotes
Book.find(self.book_id).increment(field).save
end
def decrement_vote
field = self.upvote ? :upvotes : :downvotes
Book.find(self.book_id).decrement(field).save
end
def add_karma
user = User.find(self.user_id)
user.increment(:karma, 1).save
end
def remove_karma
user = User.find(self.user_id)
user.decrement(:karma, 1).save
end
end
votes_controller.rb
class VotesController < ApplicationController
def create
book_id = params[:book_id]
vote = Vote.new
vote.book_id = params[:book_id]
vote.upvote = params[:upvote]
vote.user_id = current_user.id
#check if vote by this user exists
existing_vote = Vote.where(user_id: current_user.id, book_id: book_id)
#new_vote = existing_vote.size < 1
respond_to do |format|
format.js {
if existing_vote.size > 0
#destroy existing vote
existing_vote.first.destroy
else
#save new vote
if vote.save
#success = true
else
#success = false
end
# #total_upvotes = #book.upvotes
# #total_downvotes = #book.downvotes
end
#book = Book.find(book_id)
#is_upvote = params[:upvote]
render "votes/create"
}
end
end
private
def vote_params
params.require(:vote).permit(:upvote, :book_id)
end
end
First of all when using active record relations you don't need to call Model.find in the class, just call the relation with it's name:
def increment_vote
field = self.upvote ? :upvotes : :downvotes
book.increment(field).save
end
def add_karma
user.increment(:karma, 1).save
end
In add_karma and remove_karma you are referencing the user that the vote belongs to, and not the user that owns the book. To achieve your goal you should also increment and decrement karma on the book's owner:
def add_karma
user.increment(:karma, 1).save
book.user.increment(:karma, self.upvote ? 1 : -1).save
end
def remove_karma
user.increment(:karma, 1).save
book.user.decrement(:karma, 1).save
end
You could rewrite your controller to simplify the code:
class VotesController < ApplicationController
def create
#vote = current_user.votes.find_or_initialize_by vote_params[:book_id]
#vote.assign_attributes vote_params
#success = #vote.save
# instead of #book = #vote.book just use #vote.book in your view
#book = #vote.book
# instead of #is_upvote you can use #vote.upvote in your view
#is_upvote = #vote.upvote
respond_to do |format|
format.js { render 'votes/create'}
end
end
private
def vote_params
params.require(:vote).permit(:upvote, :book_id)
end
end

How to send a get to a server and wait a get in my app in Ruby on Rails

I am using Zapier to search some information in google sheets. I used Webhocks to send a GET to his server with a JSON information. The response of GET is an "OK" and I can't custom this.
So, they will execute a task, find what a I want and return a value, but the response must be a GET in my server, and I don't know how to intercept this response in my route.
I'm trying to study Rails Rack to intercept de request in my app, but I don't know how to send the response to the event that sent the first GET.
How is my middleware:
class DeltaLogger
def initialize app
#app = app
end
def call env
Rails.logger.debug "#{env['QUERY_STRING']}"
#status, #headers, #response = #app.call(env)
[#status, #headers, #response]
end
end
Thanks!
Example
So, to get the value returned from Zapier, I created two routes and a global class cache.
class Zapier
require 'httparty'
def initialize
#answer = ""
#id = 0
end
def request(uri, task)
last_event = Event.last
puts last_event.inspect
if last_event.nil?
last_id = 0
else
last_id = last_event.event_id
end
event_id = last_id + 1
Event.find_or_create_by(event_id: event_id)
result = HTTParty.post(uri.to_str,
:body => {id: event_id, task: task}.to_json,
:headers => {'content-Type' => 'application/json'})
#answer = ""
#id = event_id
end
def response(event_id, value)
if event_id != #id
#answer = ""
else
#answer = value
end
end
def get_answer
#answer
end
end
And my controller:
class ZapierEventsController < ApplicationController
require 'zapier_class'
before_action :get_task, only: [:get]
before_action :get_response, only: [:set]
##zapier ||= Zapier.new
def get
##zapier.request('https://hooks.zapier.com',#task)
sleep 10 #Wait for response
#value = ##zapier.get_answer
render json: { 'value': #value }, status:
end
def set
##zapier.response(#id, #value)
render json: { 'status': 'ok' }, status: 200
end
def get_task
#task = params["task"]
end
def get_response
#id = Integer(params["id"])
#value = params["value"]
end
end
Now i have to make a Task Mananger

ActionController::ParameterMissing in LoansController#new

I know there are lots of these errors discussed and I have read many of them with no success so this is not a first step by posting. I am a developer but new to Ruby so any guidance would be appreciated thanks!
I have 2 models Loan and Growthyear and Loan has_many :growthyears
Existing loans are displaying and I can edit them but trying to create a new Loan now gets this error ever since I added the nested association growthyears.
I can post the new.html.erb but I don't believe it is even getting that far.
ActionController::ParameterMissing in LoansController#new
param is missing or the value is empty: loan
Extracted source (around line #249):
value
else
raise ParameterMissing.new(key)
end
end
loan.rb
class Loan < ActiveRecord::Base
has_many :growthyears
accepts_nested_attributes_for :growthyears, reject_if: proc { |attributes| attributes['growth'].blank? }, allow_destroy: true
end
growthyear.rb
class Growthyear < ActiveRecord::Base
belongs_to :loan
validates_presence_of :growth
end
loans_controller.rb
class LoansController < ApplicationController
def show
#loan = Loan.find(params[:id])
end
def home
end
def destroy
Loan.find(params[:id]).destroy
flash[:success] = "Loan (" + params[:id] + ") deleted " + Time.now.localtime("+10:00").to_s
redirect_to loans_url
end
def index
#loans = Loan.paginate(page: params[:page], :per_page => 5)
end
def new
#loan = Loan.new(user_params)
#loan.growthyears.build
end
def create
#loan = Loan.new(user_params) # Not the final implementation!
if #loan.save
flash[:success] = "(" + #loan.id.to_s + ") " + #loan.name + " New Loan Saved! " + Time.now.localtime("+10:00").to_s
render 'edit'
else
render 'new'
end
end
def update
#loan = Loan.find(params[:id])
#loan.yeargrowth = params[:yearlygrowth]
if #loan.update_attributes(user_params)
flash[:success] = "(" + #loan.id.to_s + ") " + #loan.name + " Loan updated " + Time.now.localtime("+10:00").to_s
end
redirect_to edit_loan_path(#loan.id)
end
def edit
#loan = Loan.find(params[:id])
flash[:success] = "(" + #loan.id.to_s + ") " + #loan.name + " Loan retrieved " + Time.now.localtime("+10:00").to_s
end
private
def user_params
params.require(:loan).permit(:name, :CCV, :Loan, :IRV, :ODV, :period, :simLowInt, :simHighInt, :bSimActive, :LoanBook, growthyears_attributes: [:growth, :id, :_destroy] )
end
end
you should declare only Loan.new, because in new you only initliazie this object so instead this
def new
#loan = Loan.new(user_params)
#loan.growthyears.build
end
put this:
def new
#loan = Loan.new
#loan.growthyears.build
end

Spree offsite payments Payu.in integration: how to mark an order as paid

I am new to ruby/rails/spree. I am implementing Indian payment gateway with spree-3.0.7.
I am able to process the order but payment status is always at balance_due.
Controller code
def confirm
payment_method = Spree::PaymentMethod.find(payment_method_id)
Spree::LogEntry.create({
source: payment_method,
details: params.to_yaml
})
order = current_order || raise(ActiveRecord::RecordNotFound)
if(address = order.bill_address || order.ship_address)
firstname = address.firstname
end
#confirm for correct hash and order amount requested before marking an payment as 'complete'
checksum_matched = payment_method.checksum_ok?([params[:status], '', '', '', '', '', '', params[:udf4], params[:udf3], params[:udf2], params[:udf1], order.email, firstname, #productinfo, params[:amount], params[:txnid]], params[:hash])
if !checksum_matched
flash.alert = 'Malicious transaction detected.'
redirect_to checkout_state_path(order.state)
return
end
#check for order amount
if !payment_method.amount_ok?(order.total, params[:amount])
flash.alert = 'Malicious transaction detected. Order amount not matched.'
redirect_to checkout_state_path(order.state)
return
end
payment = order.payments.create!({
source_type: 'Spree::Gateway::Payumoney',#could be something generated by system
amount: order.total,
payment_method: payment_method
})
payment.started_processing!
payment.pend!
order.next
order.update_attributes({:state => "complete", :completed_at => Time.now})
if order.complete?
order.update!
flash.notice = Spree.t(:order_processed_successfully)
redirect_to order_path(order)
return
else
redirect_to checkout_state_path(order.state)
return
end
end
Gateway/Model Code
require "offsite_payments"
module Spree
class Gateway::Payumoney < Gateway
preference :merchant_id, :string
preference :secret_key, :string
def provider_class
::OffsitePayments.integration('Payu_In')
end
def provider
#assign payment mode
OffsitePayments.mode = preferred_test_mode == true ? :test : :production
provider_class
end
def checksum(items)
provider_class.checksum(preferred_merchant_id, preferred_secret_key, items)
end
def auto_capture?
true
end
def method_type
"payumoney"
end
def support?(source)
true
end
def authorization
self
end
def purchase(amount, source, gateway_options={})
ActiveMerchant::Billing::Response.new(true, "payumoney success")
end
def success?
true
end
def txnid(order)
order.id.to_s + order.number.to_s
end
def service_provider
"payu_paisa"
end
def checksum_ok?(itms, pg_hash)
Digest::SHA512.hexdigest([preferred_secret_key, *itms, preferred_merchant_id].join("|")) == pg_hash
end
def amount_ok?(order_total, pg_amount)
BigDecimal.new(pg_amount) == order_total
end
end
in spree payment doc https://guides.spreecommerce.com/developer/payments.html they have mentioned if auto_capture? return true then purchase method will be called but purchase method is not getting called.
Can anyone point me to right direction?
You need not call the following commands
payment.started_processing!
payment.pend!
Just leave the payment in its initial state. i.e. checkout state and complete your order.
Because when order is completed process_payments! is called.
This method processes unprocessed payments whose criteria is like below
def unprocessed_payments
payments.select { |payment| payment.checkout? }
end
Hope this solves your case :)
I fixed the issue by marking payment as complete.
remove
payment.started_processing!
payment.pend!
add
payment.complete
above
order.next
I have published my code at github as gem
https://github.com/isantoshsingh/spree_payumoney

If current time is 1 or more days earlier than starts_at

I have an exam starts_at field, and what I want to do is, if current date is 1 day or more before exam starts_at I want to redirect to somewhere else. And if current date is the same day as when the exam starts_at I want to redirect to the exam page....for now I just want to get the if statement correct, I will put the redirect later.
Here is my controller.
Student Session Controller
class StudentSessionsController < ApplicationController
before_action :set_student_session
before_filter :config_opentok, except: :update
before_action :try_authenticate_user!, except: :mobile
before_action :check_compatability, except: :mobile
def show
#session = #student_session.session
#session_id = #session.session_id
#token = #opentok.generate_token #session_id, :data => "#{#student_session.id}"
# If Time.now is 1 or more days before exam starts_at show message
if (#session.exam.starts_at =< Time.now)
render :text => "OK"
else
render :text => "Not ok"
end
if #student_session.student.present?
#UserMailer.mobile_link(current_user.email, current_user.name, #student_session).deliver
else
#UserMailer.mobile_link(#student_session.email, #student_session.email, #student_session).deliver
end
ua = UserAgent.parse(request.user_agent)
#student_session.operating_system = ua.os
#student_session.browser = ua.browser
#student_session.browser_version = ua.version.to_s
#student_session.save
render layout: "application_no_header"
end
def mobile
#session = #student_session.session
#session_id = #session.session_id
#token = #opentok.generate_token #session_id, :data => "#{#student_session.id}_mobile"
render layout: false
end
def update
respond_to do |format|
if #student_session.update(student_session_params)
format.js
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_student_session
#student_session = StudentSession.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def student_session_params
params.require(:student_session).permit(:session_status, :publisher_status, :proctor_status, :mobile_status)
end
def config_opentok
#opentok ||= OpenTok::OpenTok.new APP_CONFIG['opentok']['api_key'], APP_CONFIG['opentok']['secret']
end
def try_authenticate_user!
if #student_session.student.present?
authenticate_user!
end
end
def check_compatability
user_agent = UserAgent.parse(request.user_agent)
# http://tokbox.com/opentok/requirements/
unless (user_agent.browser == 'Chrome' and user_agent.version.to_a.first >= 23) or
(user_agent.browser == 'Firefox' and user_agent.version.to_a.first >= 22)
redirect_to '/browser'
end
end
end
I'd suggest you use:
if #session.exam.starts_at.to_date == Date.today
# go to exam
elsif #session.exam.starts_at.to_date < Date.today
# go to place before the exam
else
# go to place after the exam
end
As you're using rails you can do
if (Time.now >= (#session.exam.starts_at - 1.day) )
render :text => "OK"
else
render :text => "Not ok"
end
if ((#session.exam.starts_at - Time.now).to_i / 1.day) >= 1
render :text => "OK"
else
render :text => "Not ok"
end

Resources