testing stripe with stripe.js in rails 3 - ruby-on-rails

My app uses the stripe.js java library to generate the stripe_card_token same as ryan bates did here.
I'm trying to add unit tests to my controller and I'm getting troubles in the generation of the token. I couldn't find the function to generate the token anywhere in the Stripe API, as it seems to be available only in the javascript.
How can I generate the stripe_token in the test?
Here's the part of the controller dealing with stripe:
def save_with_payment!
if valid?
customer = Stripe::Customer.create(
description: "#{user.email} - #{user.id} - #{billing_name}",
email: billing_email,
plan: plan,
card: stripe_card_token)
self.stripe_customer_token = customer.id
plan = validate_actual_plan customer.subscriptions.data[0].plan.id
save!
end
rescue Stripe::InvalidRequestError => e
logger.error "Stripe error while creating customer: #{e.message}"
errors.add :base, "There was a problem with your credit card.
Your card wasn't charged. #{e.message}"
return false
end
Here's the controller:
def create
#subscription = Subscription.new(params[:subscription])
#subscription.user_id = current_user.id
#subscription.expiration_date = 1.month.from_now
#subscription.stripe_card_token = params[:subscription][:stripe_card_token]
respond_to do |format|
if #subscription.save_with_payment!
if current_user.upgrade_plan :premium
format.html { redirect_to user_trades_path(current_user), notice: 'Subscription was successfully created. Compliments you are now subscribed to the premium plan' }
format.json { render json: user_trades_path(current_user), status: :created, location: #subscription }
else
format.html { redirect_to home_pricing_path, notice: 'Error while upgrading your account, please contact us' }
format.json { render json: home_pricing_path, status: :created, location: #subscription }
end
else
format.html { render action: "new" }
format.json { render json: #subscription.errors, status: :unprocessable_entity }
end
end
end
This is the Coffeescript generating the token:
processCard: ->
card =
number: $(page.cardNumber).val()
cvc: $(page.cardCode).val()
expMonth: $(page.cardMonth).val()
expYear: $(page.cardYear).val()
Stripe.card.createToken(card, #handleStripeResponse)
Here's the test:
test "should create subscription" do
begin
StripeMock.start
card = {
number: "4242424242424242",
cvc: "123",
expMonth: 1.month.from_now.month,
expYear: 1.year.from_now.year,
}
token = Stripe.card.createToken(card)
assert_difference('Subscription.count') do
post :create, subscription: {
billing_email: #subscription.billing_email,
billing_city: #subscription.billing_city,
billing_country: #subscription.billing_country,
billing_name: #subscription.billing_name,
billing_street2: #subscription.billing_street2,
billing_street: #subscription.billing_street,
billing_zip: #subscription.billing_zip,
stripe_card_token: token,
plan: #subscription.plan }
end
assert_redirected_to subscription_path(assigns(:subscription))
ensure
StripeMock.stop
end
end

I figured that creating the token was indeed possible with the Stripe API, as described here:
https://stripe.com/docs/api#create_card_token
I had also found an issue with the plan I was going to subscribe, because I'm using StripeMock, there's no connection to the real/test stripe at all. this means that my mock doesn't know about the plans I try to subscribe.
I had to generate a little function to generate the plan as well.
here's its code:
def create_plan
plan = {
:amount => 1800,
:interval => 'month',
:name => 'premium',
:currency => 'gbp',
:id => 'premium'
}
response = Stripe::Plan.create plan
end

Related

Allowing only one update for specific param?

How can I make it so once a param is filled, it can't be updated or edited again?
Issue: I have a param, :video, that once filled it will charge the customer. I want to make it so once it's filled, it can't be updated or edited again.
I have taken preventative measures on the views side that once a video is uploaded the edit/update form is hidden and can't be created again but one thing i noticed that if an update fails for any reason, the page can be went back to and the update form can be filled out again (even if the video is uploaded), thus again charging the customer twice.
How it works:
A customer creates an order, a stripe id is created, once the seller fulfills the order (by uploading a file or video), the charge is captured.
def charge_update
respond_to do |format|
#amount = (#order.order_price).to_i * 100
#amount_seller = (#order.order_price).to_i * 75
if #order.update(order_charge)
begin
charge = Stripe::Charge.create({
:amount => (#order.order_price).to_i * 100,
:description => 'Rails Stripe customer',
:currency => 'usd',
:customer => #order.stripe_customer_token,
:destination => {
:amount => #amount_seller ,
:account => (#order.seller.stripe_token),
}
})
rescue Stripe::CardError => e
charge_error = e.message
end
#order.update_column(:order_status, 2)
format.html { redirect_to ([#user, #order]), notice: 'Order was successfully uploaded.' }
format.json { render :show, status: :ok, location: #order }
elsif
if charge_error
flash[:error] = charge_error
redirect_to user_order_path([#user, #order])
else
format.html { render :edit }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
end
end
private
def order_charge
params.require(:order).permit(:video, :order_status)
end
Feel free to let me know if there is a better way to go about validating the charge on the customer. My biggest concern is a customer being charged more than once because a seller either messes up, is malicious, etc. I want to guarantee a customer will only be charged once. The only way I can think of is to only allow one update to the video column.
Either that or creating a separate table for videos with order_id being unique to each video table ID.
UPDATE
I ended up doing the following which works... If any has any ideas of a better way, I am open to suggestions!
def charge_update
respond_to do |format|
if #order.video.present?
format.html { redirect_to ([#user, #order]), notice: 'Order already completed!.' }
format.json { render :show, status: :ok, location: #order }
else
#amount = (#order.order_price).to_i * 100
#amount_seller = (#order.order_price).to_i * 75
if #order.update(order_charge)
begin
...
...
...
end
The better way is to make a table that has these values
order_id
video_id
user_id
charged (Boolean Value )
once the customer made an order and charged it , you can fill these values and make charged value as true .
you can check if he made an order for that video before or not before making a new payment for it again .
here is an example for the model you can make for that
class CustomerOrder < ActiveRecord::Base
belongs_to :customer
belongs_to :order
belongs_to :video
end
before the customer make a new order , you can check if there is any relation
#customerorder =CustomerOrder.where(video_id: #video.id , user_id: #user.id , charged :true )
I would implement a callback with ActiveModel::Dirty to initiate a rollback on any change after changing the column value to anything not from empty or nil.
class Amount < ActiveRecord::Base
include ActiveModel::Dirty
before_save :check_video
attr_accessor :video # or whatever your video attribute is
private
def check_video
false if video_changed? && !video_was.nil? # or empty or whatever
true
end
end
I would ensure in the model layer that a record cannot be updated anymore once it was successfully created.
One simple way might be to override the readonly? method that is used by Rails to check for read-only records internally:
# in your model
def readonly?
super || video.present? && persisted?
end
Calling save on a read-only instance will raise an ActiveRecord::ReadOnlyRecord exception.
With this method, you can write if #order.readonly? in your controller and view to check if the #order is still updatedable.
I ended up creating a controller only for uploading the video files.
This way I can create the upload with the charges code in the create, and then allow updating without the need to separate the updates...
This is the gist of the controller so far:
def create
#video_order = VideoOrder.new(video_order_params)
#order = Order.find(params[:order_id])
#video_order.order_id = #order.id
respond_to do |format|
if #order.video_order.present?
format.html { redirect_to #order, notice: 'Already complete dog!.' }
format.json { render :show, status: :created, location: #video_order }
else
if #video_order.valid?
begin
charge = Stripe::Charge.create({
:amount => (#order.order_price).to_i * 100,
:description => 'Rails Stripe customer',
:currency => 'usd',
:customer => #order.stripe_customer_token,
:destination => {
:amount => #amount_seller ,
:account => (#order.seller.stripe_token),
}
})
rescue Stripe::CardError => e
charge_error = e.message
end
if charge_error
flash[:error] = charge_error
redirect_to '/'
else
if #video_order.save
#order.update_column(:order_status, 2)
format.html { redirect_to #order, notice: 'Video order was successfully created.' }
format.json { render :show, status: :created, location: #video_order }
else
format.html { render :new }
format.json { render json: #video_order.errors, status: :unprocessable_entity }
end
end
end
end
end
end
# PATCH/PUT /video_orders/1
# PATCH/PUT /video_orders/1.json
def update
respond_to do |format|
if #video_order.update(video_order_params)
format.html { redirect_to user_order_path([#user, #order]), notice: 'Video order was successfully updated.' }
format.json { render :show, status: :ok, location: #video_order }
else
format.html { render :edit }
format.json { render json: #video_order.errors, status: :unprocessable_entity }
end
end
end
I also have preventative measures in the create method just in case.

Not able to call a controller action in Rspec

I have been trying to implement an RSpec for a Controller called "Estimate controller" to test whether my mailing functionality (sending estimate) working properly or not. But I'm not able to call the controller action from my RSpec. I need to set some values (to, subject, message, cc, current_user, attachments) in a hash and send that hash to Estimate controller.Here is what I tried..
estimates_controller_spec.rb
describe "post 'send_estimate'" do
it "should send estimate " do
#estimate = Fabricate(:estimate, id: Faker::Number.number(10), validity: "12/12/2014", total_value: 1222.00, user_id:#user.id, project_id: #project_id)
est_params = {
to: "rspec#rails.com",
subject: "Estimate",
message: "Check out the Estiamte details",
cc: "respec#rails.com",
current_user: #user,
attachments: ""
}
expect{
post :send_estimate, estimate: est_params
}.to change { ActionMailer::Base.deliveries.count }.by(1)
end
end
estimates_controller.rb
def send_estimate
respond_to do |format|
if #estimate.send_email(params[:to], params[:subject], params[:message], params[:cc], current_user, params[:attachments])
#estimate.create_activity :send_estimate, owner: current_user, recipient: #estimate.project
format.html { redirect_to lead_path(#estimate.project), notice: "Email sent Successfully"}
format.json { head :no_content, status: :ok}
else
format.json { render json: #estimate.errors }
format.html { redirect_to contacts_path, notice: 'Something went wrong' }
end
end
end

Stripe: undefined method 'last4' when retrieving last 4 digits of Stripe charge - Rails

My rails app is successfully taking payments using Stripe but I'm getting an undefined method error when trying to retrieve the last 4 digits of the credit card from a successful charge.
error:
undefined method `last4' for #<Stripe::Charge:0x007ff2704febc8>
app/models/order.rb:33:in `stripe_retrieve'
app/controllers/orders_controller.rb:54:in `block in create'
app/controllers/orders_controller.rb:52:in `create'
orders_controller.rb
def create
if current_user
#order = current_user.orders.new(params[:order])
else
#order = Order.new(params[:order])
end
respond_to do |format|
if #order.save_with_payment
#order.stripe_retrieve
format.html { redirect_to auctions_path, :notice => 'Your payment of $1 has been successfully processed and your credit card has been linked to your account.' }
format.json { render json: #order, status: :created, location: #order }
format.js
else
format.html { render action: "new" }
format.json { render json: #order.errors, status: :unprocessable_entity }
format.js
end
end
end
order.rb
def save_with_payment
if valid?
customer = Stripe::Customer.create(description: email, card: stripe_card_token)
self.stripe_customer_token = customer.id
self.user.update_column(:customer_id, customer.id)
save!
Stripe::Charge.create(
:amount => (total * 100).to_i, # in cents
:currency => "usd",
:customer => customer.id
)
end
rescue Stripe::InvalidRequestError => e
logger.error "Stripe error while creating customer: #{e.message}"
errors.add :base, "There was a problem with your credit card."
false
end
def stripe_retrieve
charge = Stripe::Charge.retrieve("ch_10U9oojbTJN535")
self.last_4_digits = charge.last4
self.user.update_column(:last_4_digits, charge.last4)
save!
end
Here's the Stripe docs that show how to retrieve a charge, you can see that 'last4' is correct so why does it come up as undefined method?
https://stripe.com/docs/api?lang=ruby#retrieve_charge
The response will return a card which itself has a last4. So the card is its own object.
charge.card.last4
Here's the docs:
#<Stripe::Charge id=ch_0ZHWhWO0DKQ9tX 0x00000a> JSON: {
"card": {
"type": "Visa",
"address_line1_check": null,
"address_city": null,
"country": "US",
"exp_month": 3,
"address_zip": null,
"exp_year": 2015,
"address_state": null,
"object": "card",
"address_country": null,
"cvc_check": "unchecked",
"address_line1": null,
"name": null,
"last4": "1111",

How would I test a param is getting modified in a RSpec controller test?

I am setting a user through modifying the params instead of creating a hidden_field in the form. As far as I understand, this is a more secure way of handling mass-assignment.
def update
#exercise = Exercise.find(params[:id])
#this is the important part
if params[:exercise][:log_entries_attributes].present?
params[:exercise][:log_entries_attributes].each do |value|
value[1].merge!(:user_id => current_user.id)
end
end
#end the important part
respond_to do |format|
if #exercise.update_attributes(params[:exercise])
format.html { redirect_to_back_or_default #exercise, notice: "Exercise was successfully updated." }
format.mobile { redirect_to #exercise, notice: 'Exercise was successfully updated.' }
format.json { head :ok }
else
format.html { render action: "edit" }
format.mobile { redirect_to #exercise, notice: "#{#exercise.errors.full_messages.to_sentence}" }
format.json { render json: #exercise.errors, status: :unprocessable_entity }
end
end
end
In my spec I have the following:
describe "with log_entry_attributes" do
it "updates log_entries_attributes and sets user" do
exercise = FactoryGirl.create(:exercise)
log_entry = FactoryGirl.build(:log_entry)
exercise.log_entries << log_entry
exercise.save
controller.stub(:current_user).and_return(#user)
put :update, :id => exercise.id, :exercise => FactoryGirl.build(:exercise, "log_entries_attributes" => {":0" => {"reps" => "5", "weight" => "5"}}).attributes.symbolize_keys
assigns(:exercise).log_entries.first.user.should eq(#user)
end
end
I get undefined method user for nil:NilClass. I think I know why I get undefined method user. There's just no way to get the association through assigns. I'm not sure how to test that the user_id is being set properly through the current_user. Any help?
Work with mocked object:
exercise = double "exercise"
Exercise.should_receive(:find).and_return(exercise)
and test with:
exercise.should_receive(:update_attributes).with(correct_params)

How to render view first and send email in background with Ruby on Rails and ActionMailer

My app has a simple signup where the user types in his/her email address and POSTs the request. The request is then sent to my server using AJAX, an email is sent to the user's email using ActionMailer, and a thank you message is rendered using jQuery. With the code I have currently, the thank-you message is rendered only AFTER the email is sent, so it takes some time for the thank-you message to show. However, I'd like the thank-you message to be rendered first, and the email to be sent to the user in the background, so that the user can immediately know that his/her email was saved. Is there a way to process email in the background with Rails?
Below is my current code.
In users_controller.rb
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'Thank you for signing up!' }
format.js
format.json { render json: #user, status: :created, location: #user }
Notifier.email_saved(#user).deliver
else
format.html { render action: "new" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
In mailers/notifier.rb
class Notifier < ActionMailer::Base
default from: "First Last <my#email.com>"
def email_saved(user)
#email = user.email
mail to: #email, subject: 'Auto-Response: Thank you for signing up'
end
end
In users/create.js.erb
$("<div class='alert alert-success'>Thank you for showing your interest! A confirmation email will be sent to you shortly.</div>").insertAfter("#notice");
If you want to send mail only, you should use better "Resque" or "Delayed Job" than "Ajax".
#271 Resque - RailsCasts http://railscasts.com/episodes/271-resque
Delayed Job (DJ) | Heroku Dev Center https://devcenter.heroku.com/articles/delayed-job
But if you want to send mail using Ajax, please use below snippets as a reference.
#app/controllers/users_controller.rb
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'Thank you for signing up!', sign_up_flag: 1 }
format.js
format.json { render json: #user, status: :created, location: #user }
else
format.html { render action: "new" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
def send_mail(user_id)
begin
user = User.find(user_id)
Notifier.sign_up_mail(user.email).deliver
render :nothing => true, :status => 200
rescue
render :nothing => true, :status => 500
end
end
#app/mailers/notifier.rb
class Notifier < ActionMailer::Base
default from: "First Last <my#email.com>"
def sign_up_mail(email)
mail to: email, subject: 'Auto-Response: Thank you for signing up'
end
end
#app/views/???.html.erb
<% if #sign_up_flag == 1 %>
$(document).ready(function(){
$.ajax({
type: "POST",
url: "/sendmail",
data: "",
success : function(){},
error : function() {}
});
});
<% end %>
#config/routes.rb
post '/sendmail' => 'users#send_mail'
Thanks.

Resources