I'm building an application that handles different types of transactions for different services such as subscriptions, gift subscriptions and purchases.
I have an issue with the gift transactions and activemerchant. Ill give you a brief overview of how it works.
The user creates a gift subscription and fills out the data for it, it is stored in the db and then shown back to the user for review in a custom "show_view", the user then proceeds to enter credit card information in a separate form and when he submits the data, a method from the controller is called to handle the transaction and here is where Im having issues.
This is the gift_subscription.rb model
def gift_purchase
response = GATEWAY.purchase(price, credit_card, gift_purchase_options)
GiftTransaction.create!(:action => "gift_purchase", :amount => price, :response => response)
response.success?
end
private
def gift_purchase_options
{
:ip => ip_address,
:billing_address => {
:name => name + last_name,
:address1 => address1,
:city => city,
:state => state,
:country => "Mexico",
:zip => zip
}
}
end
def validate_card
unless credit_card.valid?
credit_card.errors.full_messages.each do |message|
errors[:base] << message
end
end
end
def credit_card
#credit_card = ActiveMerchant::Billing::CreditCard.new(
:brand => card_type,
:number => card_number,
:verification_value => card_verification,
:month => card_expires_on.month,
:year => card_expires_on.year,
:first_name => name,
:last_name => last_name
)
And here is the gift_subscription_controller.rb
def review
#gift_subscription = GiftSubscription.find(params[:id])
end
def edit_review
#gift_subscription = GiftSubscription.find(params[:id])
end
def update_review
#gift_subscription = GiftSubscription.find(params[:id])
respond_to do |format|
if #gift_subscription.update_attributes(params[:gift_subscription])
format.html { redirect_to "gift_subscriptions/review/#{#gift_subscription.id}", :notice => 'Gift subscription was successfully updated.' }
format.json { head :no_content }
else
format.html { render :action => "edit_review" }
format.json { render :json => #gift_subscription.errors, :status => :unprocessable_entity }
end
end
end
def do_gift_transaction
#gift_subscription = GiftSubscription.find(params[:id])
if #gift_subscription.gift_purchase
redirect_to '/thank_you'
else
redirect_to "/gift_subscriptions/#{#gift_subscription.id}/failed_transaction"
end
end
def failed_transaction
#gift_subscription = GiftSubscription.find(params[:id])
#gift_transactions = #gift_subscription.gift_transactions
end
def transaction_details
#gift_subscription = GiftSubscription.find(params[:id])
end
To make things a little more clear, from the controller create method, it redirects users to the review action where there's an edit_review in case they want to change something, then they go to transaction_details where they enter creditcard info and finally the method do_gift_transaction is called to actually do the transaction.
The error I get is the following
NoMethodError in GiftSubscriptionsController#do_gift_transaction
undefined method `month' for nil:NilClass
Rails.root: /home/peanut/RubymineProjects/GiftBox
Application Trace | Framework Trace | Full Trace
app/models/gift_subscription.rb:44:in `credit_card'
app/models/gift_subscription.rb:12:in `gift_purchase'
app/controllers/gift_subscriptions_controller.rb:113:in `do_gift_transaction'
I've been looking around and I can't seem to find why it doesnt recognize the month... For other subscriptions I have basically the same model (a few diferences) but it works perfectly. Any help here would be much appreciated.
GiftSubscription model attributes
attr_accessible :response, :name, :last_name, :address1, :address2,:city,
:state, :zip, :card_type, :ip_address, :price,
:duration, :created_at, :card_expires_on, :card_number,
:card_verification, :message
has_one :gift_transactions, :class_name => "GiftTransaction"
attr_accessor :card_number, :card_verification
validate :validate_card, :on => :transaction_details
So after a few hours of banging my head it turned out to be quite simple... In the view to enter credit card data, the fields that were supposed to be saved to the db were not being saved because I only had a link_to button to continue and ergo when the credit_card method was called, it was empty.
Thanks to lander16 for pointing this out.
Related
hi i am trying to get my braintree account to display errors when creating a transaction but it doesn't appear to be working
- flash.each do |key, value|
%div{:class => "alert alert-#{key}"}= value
def update
result = Braintree::Transaction.sale(
:amount => params[:amount_to_add].to_f,
# :order_id => "order id",
:customer_id => customer.customer_cim_id,
:tax_amount => (params[:amount_to_add].to_f / 11).round(2),
:options => {
:submit_for_settlement => true
}
)
if result.success?
logger.info "Added to #{params[:amount_to_add].to_f} to #{customer.first_name} #{customer.last_name} (#{customer.customer_cim_id})"
customer.store_credit.add_credit(params[:amount_to_add].to_f)
redirect_to myaccount_store_credit_path
# , :notice => "Successfully updated store credit."
else
result.errors.each do |error|
puts error.message
customer.errors.add(:base, error.message)
render :show, :notice => error.message
end
end
end
I believe the reason you're probably unable to see the errors is because of the :notice option on your render method, which is redundant because render doesn't seem to be using the :notice option, only redirect_to. You may just add the errors to your to your flash, but note that in your view you have to loop through the errors within the flash to render it.
Another way I would think you do this though is add the payment method to your User model
class User|Customer
...
def process_braintree_payment(amount)
result = Braintree::Transaction.sale(
:amount => amount.to_f,
# :order_id => "order id",
:customer_id => customer_cim_id,
:tax_amount => (amount.to_f / 11).round(2),
:options => {
:submit_for_settlement => true
}
)
add_braintree_errors(result.error) unless result.success?
end
def add_braintree_errors(error_object)
error_object.each do |error|
errors.add(:braintree, error.message)
end
end
end
class XController
def update
#customer.process_braintree_payment(params[:amount_to_add])
if #customer.errors.empty?
logger.info "Added to #{params[:amount_to_add].to_f} to #{#customer.first_name} #{#customer.last_name} (#{#customer.customer_cim_id})"
#customer.store_credit.add_credit(params[:amount_to_add].to_f)
redirect_to myaccount_store_credit_path
# , :notice => "Successfully updated store credit."
else
render :show
end
end
end
In your view, you have access to the #customer variable or better you could store the error object in the ActionController#flash, note however, using the same keys for your flash messages, would overwrite the previous value.
My sandbox account doesn't store payment methods for customer in their vault. I am creating a customer object using:
def create_customer
result = Braintree::Customer.create(
:first_name => params[:first_name],
:last_name => params[:last_name],
:email => params[:email],
:phone => params[:phone]
)
if result.success?
render :json => {'result' => result.customer.id}
else
render :json => {'errors' => result.errors}, :status => 400
end
end
and then storing the customer_id in my database for later use.
When creating client_token I am sending the same customer_id to the API. Here is the code for creating client_token:
def client_token
token = Braintree::ClientToken.generate(
:customer_id => params[:customer_id]
)
render :json => {"token" => token}
end
I work at Braintree. If you have more questions about your integration, you can always get in touch with our support team
You need to create a payment method with the nonce you receive back from your client:
result = Braintree::PaymentMethod.create(
:customer_id => "131866",
:payment_method_nonce => nonce_from_the_client
)
I'm a new rails developer who has a basic scaffolded crud application that I modified a bit.
I'm getting this error:
undefined method description for #<ActiveRecord::Relation:0x00000102df26d8>
when I visit john/recipes/46. Here's my view:
<h1 itemprop="name"><%= #recipe.name %></h1>
<ul>
<li><%= link_to 'Edit', edit_recipe_path(#recipe) %></li>
</ul>
<p itemprop="description"><%= #recipe.description %></p>
here's my routes:
match "/:username" => "recipes#index"
scope ':username' do
resources :recipes
end
here's my show index:
def show
#user = User.find_by_username params[:username]
#recipe = Recipe.where(:user_recipe_id => params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #recipe }
end
end
and my model:
before_save :set_next_user_recipe_id
belongs_to :users
validates :user_recipe_id, :uniqueness => {:scope => :user_id}
def to_param
self.user_recipe_id.to_s
end
def set_next_user_recipe_id
self.user_recipe_id ||= get_new_user_recipe_id
end
def get_new_user_recipe_id
user = self.user
max = user.recipes.maximum('user_recipe_id') || 0
max + 1
end
attr_accessible :description, :duration, :author, :url, :name, :yield, :ingredients_attributes, :user_recipe_id, :directions_attributes, :tag_list, :image
The reason I'm doing a Recipe.where(:user_recipe_id => params[:id]) instead of Recipe.where(:id => params[:id]) is because I'm trying to get so instead of john/recipes/46 showing the 46th recipe in the database, instead to show the 46th recipe that belongs to John.
Thanks for all help!
You're only trying to look for one recipe, but your query is searching for multiples. When you use a plain where(...) without ending it with .first, Rails interprets it as "show me all (multiple) Recipes with this user id" instead of "show me the (one) recipe with this id".
So you need to either put .first at the end of your query:
#recipe = Recipe.where(:user_recipe_id => params[:id]).first
or use an ActiveRecord finder that only returns one record:
#recipe = Recipe.find_by_user_recipe_id(params[:id])
I'm building a small proof of concept with Stripe and Ruby on Rails 3.2. So far I've watched the Railscast on how to implement Stripe in a RoR app and it's working really well.
I've built my app by following RailsCast #288 Billing with Stripe. Now my users can add and edit their credit cards and even register to classes and have their credit card billed upon completion.
Now I've been testing with Stripe's numerous test credit cards and I want to catch as many exceptions when raised. I'm using Stripe's example errors in my Registration model as show here:
class Registration < ActiveRecord::Base
belongs_to :user
belongs_to :session
attr_accessible :session_id, :user_id, :session, :user, :stripe_payment_id
validates :user_id, :uniqueness => {:scope => :session_id}
def save_with_payment(user, stripe_card_token)
if valid?
if user.stripe_customer_id.present?
charge = Stripe::Charge.create(
:customer => user.stripe_customer_id,
:amount => self.session.price.to_i * 100,
:description => "Registration for #{self.session.name} (Id:#{self.session.id})",
:currency => 'cad'
)
else
customer = Stripe::Customer.create(
:email => user.email,
:card => stripe_card_token,
:description => user.name
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => self.session.price.to_i * 100,
:description => "Registration for #{self.session.name} (Id:#{self.session.id})",
:currency => 'cad'
)
user.update_attribute(:stripe_customer_id, customer.id)
end
self.stripe_payment_id = charge.id
save!
end
rescue Stripe::CardError => e
body = e.json_body
err = body[:error]
logger.debug "Status is: #{e.http_status}"
logger.debug "Type is: #{err[:type]}"
logger.debug "Code is: #{err[:code]}"
logger.debug "Param is: #{err[:param]}"
logger.debug "Message is: #{err[:message]}"
rescue Stripe::InvalidRequestError => e
# Invalid parameters were supplied to Stripe's API
rescue Stripe::AuthenticationError => e
# Authentication with Stripe's API failed
# (maybe you changed API keys recently)
rescue Stripe::APIConnectionError => e
# Network communication with Stripe failed
rescue Stripe::StripeError => e
# Display a very generic error to the user, and maybe send
# yourself an email
rescue => e
# Something else happened, completely unrelated to Stripe
end
end
I'm merely rescuing from errors right now and not really taking action after one being raised and ultimately I would like to stop the current class registration from happening and redirect a user with a flash error.
I've read about rescure_from but I'm not sure what is the best way to handle of all the possible Stripe errors. I know can't redirect from the model, how would you experts handle this?
Here's my Registration controller:
class Classroom::RegistrationsController < ApplicationController
before_filter :authenticate_user!
def new
if params[:session_id]
#session = Session.find(params[:session_id])
#registration = Registration.new(user: current_user, session: #session)
else
flash[:error] = "Course session is required"
end
rescue ActiveRecord::RecordNotFound
render file: 'public/404', status: :not_found
end
def create
if params[:session_id]
#session = Session.find(params[:session_id])
#registration = Registration.new(user: current_user, session: #session)
if #registration.save_with_payment(current_user, params[:stripe_card_token])
flash[:notice] = "Course registration saved with success."
logger.debug "Course registration saved with success."
mixpanel.track 'Registered to a session', { :distinct_id => current_user.id,
:id => #session.id,
'Name' => #session.name,
'Description' => #session.description,
'Course' => #session.course.name
}
mixpanel.increment current_user.id, { :'Sessions Registered' => 1}
mixpanel.track_charge(current_user.id, #session.price.to_i)
else
flash[:error] = "There was a problem saving the registration."
logger.debug "There was a problem saving the registration."
end
redirect_to root_path
else
flash[:error] = "Session required."
redirect_to root_path
end
end
end
Thanks for taking the time to respond, much appreciated!
Francis
Have you thought of putting the actually Stripe call in a custom validator?
http://apidock.com/rails/ActiveModel/Validations/ClassMethods/validate
That way you could add errors to the object with something like the following
The logic behind this is you only want to save successful transactions as 'transaction' anyway so why not just put the Stripe charge in the validator.
validate :card_validation
def card_validation
begin
charge = Stripe::Charge.create(
:customer => user.stripe_customer_id,
:amount => self.session.price.to_i * 100,
:description => "Registration for #{self.session.name} (Id:#{self.session.id})",
:currency => 'cad'
)
etc etc
rescue => e
errors.add(:credit_card, e.message)
#Then you might have a model to log the transaction error.
Error.create(charge, customer)
end
end
This way you can handle the errors like any other errors you would get from a entry not saving, instead of giving a blank error message, or having to handle every last error from Stripe.
class Classroom::RegistrationsController < ApplicationController
before_filter :authenticate_user!
def create
if params[:session_id]
#session = Session.find(params[:session_id])
params[:registration][:user] = current_user
params[:registration][:session] = #session
params[:registration][:stripe_card_token] = params[:stripe_card_token]
#registration = Registration.new(params[:registration])
respond_with(#registration) do |format|
if #registration.save
format.html {redirect_to root_path, :notice => "SOMETHING HERE TO TELL THEM SUC"}
else
format.html {render}
end
end
else
respond_with do |format|
format.html {redirect_to root_path, :error => "SOMETHING HERE TO TELL THEM GET SESSION"}
end
end
end
end
Second question on here, I'd really like to solve this one myself but I just don't know where to start to debug it.
So here is my error in the browser (which occurs when I go to check out and enter my details in order/_form.html.erb)
ArgumentError in OrdersController#new
You need to supply at least one validation
Rails.root: C:/Users/Ruby/rails_practice/depot4
Application Trace | Framework Trace | Full Trace
app/models/payment_type.rb:6:in <class:PaymentType>'
app/models/payment_type.rb:1:in'
app/models/order.rb:7:in <class:Order>'
app/models/order.rb:1:in'
app/controllers/orders_controller.rb:1:in `'
And here is my def new in OrdersController:
def new
#cart = current_cart
if #cart.line_items.empty?
redirect_to store_url, :notice => "Your cart is empty"
return
end
#hide_checkout_button = true
#order = Order.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #order }
end
end
The thing is that I haven't touch def new, I've been working on def create, which is here:
def create
#order = Order.new(params[:order])
#order.add_line_items_from_cart(current_cart)
#cart = current_cart
#hide_checkout_button = true
pay_type = PaymentType.find( :conditions => ['pay_type = ?', #order.pay_type] )
#order.payment_type_id = pay_type.id
respond_to do |format|
if #order.save
Cart.destroy(session[:cart_id])
session[:cart_id] = nil
format.html { redirect_to(store_url, :notice => 'Thank you for your order.') }
format.json { render json: #order, status: :created, location: #order }
else
format.html { render action: "new" }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
end
What I am trying to do there is create an order which is which belongs_to a payment_type and has_many line_items which belongs_to a cart.
Incidentally, I am also trying to hide_checkout_button with an instance variable when I am on the order page.
The Orders table has a foreign key to the PaymentTypes table and I am trying to find the correct id from this PaymentTypes table for the payment_type submitted by the user.
If I comment out these two lines:
pay_type = PaymentType.find( :conditions => ['pay_type = ?', #order.pay_type] )
#order.payment_type_id = pay_type.id
Sometimes I get a different error:
NoMethodError in OrdersController#new
undefined method `key?' for nil:NilClass
I think this is to do with incorrect caching in the browser but I'm not sure what the connection is.
I will update with the rest after I post this first
Part deux
I know that this is about validation, but I can't see what I am doing wrong... order.rb:
class Order < ActiveRecord::Base
attr_accessible :address, :email, :name, :pay_type, :payment_type_id, :cart_id,
:product_id
has_many :line_items, :dependent => :destroy
belongs_to :payment_type
PAYMENT_TYPES = PaymentType.pluck(:pay_type)
validates :name, :address, :email, :pay_type, :presence => true
validates :pay_type, :inclusion => PAYMENT_TYPES
And then you've got the other side of that belongs_to in payment_type.rb
class PaymentType < ActiveRecord::Base
attr_accessible :pay_type
has_many :orders
validates ***:pay_type,*** :uniqueness
end
I know that I am totally just confusing things but I have one fail in the functionals tests and one error that has something to do with updating an order but I don't know what yet. I am going to work on them to see if by solving them I inadvertently solve this weird error.
If anyone can give me tips on hacking and debugging in rails that would be great. I would love to be able to solve this without typing all of this in here.
I don't think the server trace gives any more information than the browser window in this case but if you need it, or anything else please ask.
UPDATE:
So my problem is that I know how to solve it with a global variable in payment_type.rb, but this means that I have one column of payment types in the Orders table and another of names and payment_type_ids in another column, which is the foreign key.
Since I have the foreign key I shouldn't need a specific column for payment_types in the Orders table. I should just be able to see the value from the PaymentType table in the Orders view.
How do you do this without a Global variable?
UPDATE deux:
Ok, so I never posted this before (from orders_form.html.erb):
26: <div class="field">
27: <%= f.label :pay_type %><br />
28: <%= f.select :pay_type, PaymentType::PAYMENT_TYPES,
29: :prompt => 'Select a payment method' %>
30: </div>
31: <div class="actions">
So I've tried to select for :pay_type in Orders but given options from :pay_type in PaymentTypes.
I can't imagine that matters does it? Seems to be where my problem lies, but can't be sure.
I think the syntax of your validate inclusion of is wrong. It should be something like:
validates :pay_type, :inclusion => { :in => PAYMENT_TYPES }