user3597950
user3597950

Reputation: 10241

Stripe error while creating customer: You passed an empty string for 'card'

Stripe will not accept payments, the charge keeps failing. I am getting this error:

Started POST "/charges" for 127.0.0.1 at 2014-06-09 06:30:58 -0700
Processing by ChargesController#create as HTML
  Parameters: {"utf8"=>"√", "authenticity_token"=>"9bqk2Q6NEM1ehHDJPgncNQ3gnP0X1hQK6y6ENm3Yb1g=", "c
harge"=>{"contest_id"=>"36", "user_id"=>"", "amount"=>"12", "stripe_card_token"=>""}, "card_number"=
>"4242 4242 4242 4242", "card_code"=>"478", "button"=>""}
Stripe error while creating customer: You passed an empty string for 'card'. We assume empty values
are an attempt to unset a parameter; however 'card' cannot be unset. You should remove 'card' from y
our request or supply a non-empty value
Redirected to http://localhost:3000/
Completed 302 Found in 477ms (ActiveRecord: 0.0ms)

It seems as if it might not be creating the stripe token? What is going on here?

This is my form:

 <% content_for :head do %>
        <%= tag :meta, :name => "stripe-key", :content => STRIPE_PUBLIC_KEY %>
    <% end %>
    <%= form_for @charge, :html => { :class => 'form' } do |f| %>
        <div class="form-inputs">
            <div class="row">
                <%= f.hidden_field :contest_id, value: @contest.id  %>
                <%= f.hidden_field :user_id %>
                <%= f.hidden_field :amount %>
                <!-- TODO CHANGE THIS ACCORDINGLY  -->
                <%= f.hidden_field :stripe_card_token %>
                <div class="small-12 medium-6 large-2 columns">
                    <%= f.label "Amount:", class: "text-right" %>
                </div>
                <div class="small-12 medium-6 large-10 columns" style="margin-bottom: 0.5rem; margin-top: 0.5rem;">
                    <%= number_to_currency(@charge.amount, :unit => "$") %>
                </div>
                <% if @charge.stripe_card_token.present? %>
                  Credit card has been provided.
                <% else %>
                  <div class="small-12 medium-6 large-2 columns">
                    <%= label_tag :card_number, "Credit Card Number:", class: "text-right" %>
                  </div>
                  <div class="small-12 medium-6 large-10 columns">
                    <%= text_field_tag :card_number %>
                  </div>
                  <div class="small-12 medium-6 large-2 columns">
                    <%= label_tag :card_code, "Security Code on Card (CVV):", class: "text-right" %>
                  </div>
                  <div class="small-12 medium-6 large-10 columns end">
                    <%= text_field_tag :card_code %>
                  </div>
                  <div class="small-12 medium-6 large-2 columns">
                    <%= label_tag :card_month, "Card Expiration:", class: "text-right" %>
                  </div>
                  <div class="small-6 medium-3 columns">
                    <%= select_month nil, {add_month_numbers: true}, {name: nil, id: "card_month"} %>
                  </div>
                  <div class="small-6 medium-3 columns">
                    <%= select_year nil, {start_year: Date.today.year, end_year: Date.today.year+15}, {name: nil, id: "card_year"} %>
                  </div>
                <% end %>
                <div id="stripe_error">
                  <noscript>JavaScript is not enabled and is required for this form. First enable it in your web browser settings.</noscript>
                </div>

            </div>
            <div class="row">
                <div class="form-actions small-9 small-offset-2 columns">
                    <%= button_tag :type => "button", :class => "radius" do  %>
                        <%= link_to "Back", @contest, :style => "color: white" %>
                    <% end %>
                    <%= f.button :Donate, :class => "radius" %>
                </div>
            </div>
        </div>
    <% end %>
    <% content_for :js do %>
        <%= javascript_include_tag "https://js.stripe.com/v1", "application" %> 
        <%= javascript_include_tag asset_path("stripe/stripe.js"), "application" %>
    <% end %>

This is my charges controller:

class ChargesController < ApplicationController

    def new
      @contest = Contest.find(params[:id])
      @charge = Charge.new(amount: params[:amount])
    end

    def create
      @charge = Charge.new(charges_params)
      if @charge.save_with_payment(@charge)
        redirect_to root_path, :notice => "Contribution was recorded succesfully!"
      else
        redirect_to root_path, :notice => "Transaction was not able to be recorded"
      end
    end

    def charges_params
        params.require(:charge).permit(:stripe_card_token, :contest_id, :user_id, :amount)
    end

end

My Charge Model:

class Charge < ActiveRecord::Base

    attr_accessor :stripe_card_token

    belongs_to :contest
    belongs_to :user

    def save_with_payment(charge)
      if valid?
        Stripe::Charge.create(
          :amount => (charge.amount.to_i)*100,
          :currency => "usd",
          :card => stripe_card_token);
        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."
      false
    end

end

Upvotes: 3

Views: 9144

Answers (3)

Lesha Pipiev
Lesha Pipiev

Reputation: 3333

I encountered the same problem.
my subscription.js.coffee:

jQuery ->
  Stripe.setPublishableKey($('meta[name="stripe-key"]').attr('content'))
  subscription.setupForm()

subscription =
  setupForm: ->
    $('#new_subscription').submit (e) ->
      $('input[type=submit]').attr('disabled', true)
      subscription.processCard()
      return false

  processCard: ->
    card =
      number: $('#card_number').val()
      cvc: $('#card_code').val()
      expMonth: $('#card_month').val()
      expYear: $('#card_year').val()
    Stripe.createToken(card, subscription.handleStripeResponse)

  handleStripeResponse: (status, response) ->
    if status == 200
      $('#subscription_stripe_card_token').val(response.id)
      $('#new_subscription')[0].submit()
    else
      $('#stripe_error').text(response.error.message) 
      $('input[type=submit]').attr('disabled', false)
      false

As you can see when user pushes submit button the function $('#new_subscription').submit (e) is raised. There is almost all of the stripe magic in the submit function.

In my case sometimes the submit function is not raised therefore empty stripe_card_toke parameter is sent to your server.

The strange behavior is caused by Rails turbolinks facilities.
I solved it in such way:

  1. Add to Gemfile a line:
    gem 'jquery-turbolinks'
  2. Add to manifest application.js a line:
    //= require jquery.turbolinks
    So my application.js looks like:
//= require jquery
//= require jquery.turbolinks
//= require jquery_ujs
//= require turbolinks
//= require bootstrap
//= require jquery_nested_form
//= require_tree .

Upvotes: -1

colinm
colinm

Reputation: 4288

Unrelated to stripe_card_token being blank, your server is currently in PCI scope because it's receiving the card number and CVV code. This is probably not what you're intending if you're using Stripe.

From the Stripe Getting Started docs:

Note how input fields representing sensitive card data (number, CVC, expiration month and year) do not have a "name" attribute. This prevents them from hitting your server when the form is submitted.

Rails tag helpers generate a name attribute by default. That's not desirable in this case.

You can override this behavior by passing nil for both name and value, and defining an identifier of some kind:

<%= text_field_tag nil, nil, 'data-stripe' => 'card_number' %>

Here I've used Stripe's currently-recommended data-stripe, but you can just as well use the id attribute (as their older tutorials recommended).

Getting back to your original question, the problem is in your Charge model, where you're overriding Active Record and masking the data in the database:

class Charge < ActiveRecord::Base

    attr_accessor :stripe_card_token     # Don't do this!

attr_accessor should never be used on database columns. Active Record provides its own accessors in order to pull that data from the database; by declaring an accessor here, you're replacing Active Record's functionality with Ruby's base functionality:

irb(main):001:0> Charge.first
  Charge Load (0.3ms)  SELECT  "charges".* FROM "charges"   ORDER BY "charges"."id" ASC LIMIT 1
=> #<Charge id: 1, stripe_card_token: "0xdeadbeef", created_at: "2014-06-11 14:27:40", updated_at: "2014-06-11 14:27:40">
irb(main):002:0> c.stripe_card_token
=> nil

That being said, the Railscast you started from is nearly three years old, violates best practices all over the place, and uses a long-outdated API endpoint. Once you correct your model, you're still better off starting your form and JavaScript over using Stripe's current tutorial and documentation.

Upvotes: 0

emcanes
emcanes

Reputation: 1398

Most likely it looks like stripe_card_token is blank as its

:card => stripe_card_token

try and change that to

:card => charge.stripe_card_token

Upvotes: 0

Related Questions