rails 4条带付款导致订单项无效错误

时间:2015-05-25 16:30:24

标签: ruby-on-rails-4 stripe-payments

与使用Stripe付款的其他人一样,我收到错误“无法向没有活动卡的客户收费”。表单没有附加令牌,因为javascript在表单中没有匹配的ID,但现在我收到rails错误并且卡没有收费,客户被创建,错误表明存在无效行item,当没有运行创建客户和充值卡的代码时,创建的订单没有错误。

订单视图

<script type="text/javascript" src="https://js.stripe.com/v2/">

$(function(){

  Stripe.setPublishableKey('<%= Rails.configuration.stripe[:PUBLISHABLE_KEY] %>');

});

</script>

<div class = "Power Me" >

  <fieldset>

    <legend> Please enter your details </legend>

    <%= render 'form', object: @object %>

  </fieldset>

</div>

呈现订单表格

<%= form_for(@order, :html => {:id => 'payment-form'}) do |f| %>

  <% if @order.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@order.errors.count, "error") %> prohibited this order from being saved:</h2>

      <ul>
      <% @order.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">

    <%= "Order Total: #{order_total.to_s}" %>
    <%= "Order Currency: #{order_currency.to_s}" %>
  </div>

  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label :Address_line_1 %><br>
    <%= f.text_area :address_line_1 %>
  </div>
  <div class="field">
    <%= f.label :Address_Line_2 %><br>
    <%= f.text_area :address_line_2 %>
  </div>
  <div class="field">
    <%= f.label :City %><br>
    <%= f.text_area :address_city %>
  </div>
  <div class="field">
    <%= f.label :Region %><br>
    <%= f.text_area :address_state %>
  </div>
  <div class="field">
    <%= f.label :Postcode %><br>
    <%= f.text_area :address_zip %>
  </div>
  <div class="field">
    <%= f.label :Country %><br>
    <%= f.select :address_country, Order::CC_COUNTRIES, prompt: 'Select the country' %>
  </div>
  <div class="field">
    <%= f.label :email %><br>
    <%= f.text_field :email, :placeholder => "you@example.com" %>
  </div>
  <div class="field">
    <%= f.label :Payment_Type %><br>
    <%= f.select :pay_type, Order::PAYMENT_TYPES, prompt: 'Select a payment method' %>
  </div>
  <div class="form-row">
        <label>Card Number</label>
        <input type="text" size="20" autocomplete="off" data-stripe="number" id="number" class="credit-number", placeholder = "**** **** **** ****" pattern="[\d ]*" />
    </div>

    <div class="form-row">
        <label>Security Code/CVC</label>
        <input type="text" size="4" autocomplete="off" data-stripe="cvc" id="cvc" class="credit-scurity" placeholder="***" pattern="\d*" />
    </div>

    <div class="form-row">
        <label>Expiration (MM/YYYY)</label>
        <input type="text" size="2" data-stripe="exp-month" id="exp-month" class="card-expiry-month" placeholder="MM" pattern="\d*" />
        <span> / </span>
        <input type="text" size="4" data-stripe="exp-year" id="exp-year" class="card-expiry-year" placeholder="YYYY" pattern="\d*" />
    </div>

  <div class="actions">
    <%= f.submit 'Pay', :class =>"stripe-button" %>
  </div>
<% end %>

订单型号

class Order < ActiveRecord::Base

#  attr_accessor :stripeToken

  PAYMENT_TYPES = ["credit card"]

  CC_COUNTRIES = ["United Kingdom", "France", "Italy"]

  validates :name, :address_line_1, :address_zip, :email, presence: true

#  validates :pay_type, inclusion: PAYMENT_TYPES

  has_many :line_items, dependent: :destroy

  def add_line_items_from_cart(cart)

    cart.line_items.each do |item|

      item.cart_id = nil

      line_items << item

    end

  end
end

订单控制器

class OrdersController < ApplicationController

  include CurrentCart

  before_action :set_cart, only: [:new, :create]

  before_action :set_order, only: [:show, :edit, :update, :destroy]

  # GET /orders
  # GET /orders.json
  def index
    @orders = Order.all
  end

  # GET /orders/1
  # GET /orders/1.json
  def show
  end

  # GET /orders/new
  def new

    if @cart.line_items.empty?

      redirect_to store_url, notice: "Your cart is empty"

      return

    end

    @order = Order.new

  end

  # GET /orders/1/edit
  def edit
  end

  # POST /orders
  # POST /orders.json
  def create

    @order = Order.new(order_params)

    @order.add_line_items_from_cart(@cart)

    respond_to do |format|
      if @order.save

        Cart.destroy(session[:cart_id])

        session[:cart_id] = nil

        format.html { redirect_to @order, notice: 'Order was successfully created.' }
        format.json { render action: 'show', status: :created, location: @order }
      else
        format.html { render action: 'new' }
        format.json { render json: @order.errors, status: :unprocessable_entity }
      end
    end

    Stripe.api_key = "sk_test_BsdqHq0SQuPqHIsm46lcpX4v"

    @amount = order_total.to_i * 100

    token = params[:stripeToken]
=begin
    begin

      customer = Stripe::Customer.create(

        :email => order_params[:email]

      )

      charge = Stripe::Charge.create(

        :customer    => customer.id,
        :amount => @amount, # amount in cents, again
        :currency => order_currency,
        :card => token,
        :description => order_params[:email]
      )

      redirect_to root_path

    rescue Stripe::CardError => e

      @error = e

    end
=end
  end

  # PATCH/PUT /orders/1
  # PATCH/PUT /orders/1.json
  def update
    respond_to do |format|
      if @order.update(order_params)
        format.html { redirect_to @order, notice: 'Order was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: 'edit' }
        format.json { render json: @order.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /orders/1
  # DELETE /orders/1.json
  def destroy
    @order.destroy
    respond_to do |format|
      format.html { redirect_to orders_url }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_order
      @order = Order.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def order_params
      params.require(:order).permit(:name, :email, :pay_type, :address_line_1, :address_line_2, :address_city, :address_state, :address_zip, :address_country)
    end
end

我的申请助手

module ApplicationHelper

      def order_total

        total = LineItem.joins(:product).select("sum(line_items.quantity * products.price) as total").where("cart_id = ?", session[:cart_id]).first.total

      end

      def order_currency

        currency = LineItem.joins(:product).joins(:currency).select("currencies.name as iso_name").where("cart_id = ?", session[:cart_id]).first.iso_name

      end


      # Returns the full title on a per-page basis.
      def full_title(page_title)
        base_title = "Ruby on Rails Tutorial Sample App"
        if page_title.empty?
          base_title
        else
          "#{base_title} | #{page_title}"
        end
      end

      def bootstrap_class_for flash_type

        { success: "alert-success", error: "alert-danger", alert: "alert-warning", notice: "alert-info" }[flash_type] || flash_type.to_s

      end

      def flash_messages(opts = {})

        flash.each do |msg_type, message|

          concat(content_tag(:div, message, class: "alert #{bootstrap_class_for(msg_type)} fade in") do

            concat message
         end)

        end

          nil

      end

      # before filters

      def signed_in_user

        unless signed_in?

          store_location

          redirect_to signin_url, notice: "Please sign in."

        end

      end

      def correct_user

        @user = User.find(params[:id])

        redirect_to(root_url) unless current_user?(@user)

      end

      def admin_user

        redirect_to(root_url) unless current_user.admin?

      end

end

Orders.js

$('#payment-form').submit(function(event) {

  var form = $(this);

  form.find('button').prop('disabled', true);

  Stripe.createToken(form, stripeResponseHandler);

  return false;

});

1 个答案:

答案 0 :(得分:0)

下面的工作订单表单,你不能将字段传递给Stripe并在rails表单中使用它们来保存在db中,它的任一个或(前面的例子试图在db中保存地址字段并将它们作为一部分传递给条带交易验证。

<%= form_for(@order, :html => {:id => 'payment-form'}) do |f| %>

  <% if @order.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@order.errors.count, "error") %> prohibited this order from being saved:</h2>

      <ul>
      <% @order.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">

    <%= "Order Total: #{order_total.to_s}" %>
    <%= "Order Currency: #{order_currency.to_s}" %>

  </div>

  <div class="form-row">
        <label>Full Name</label>
        <input type="text" size="20" autocomplete="off" data-stripe="name" />
  </div>

  <div class="field">
    <%= f.label :email %><br>
    <%= f.text_field :email, :placeholder => "you@example.com" %>
  </div>
  <div class="field">
    <%= f.label :Payment_Type %><br>
    <%= f.select :pay_type, Order::PAYMENT_TYPES, prompt: 'Select a payment method', :selected => "credit card" %>
  </div>

  <div class="form-row">
        <label>Address Line 1</label>
        <input type="text" size="20" autocomplete="off" data-stripe="address_line1" />
    </div>

  <div class="form-row">
        <label>Address Line 2</label>
        <input type="text" size="20" autocomplete="off" />
    </div>

  <div class="form-row">
        <label>Address City</label>
        <input type="text" size="20" autocomplete="off" />
    </div>
  <div class="form-row">
        <label>Address State</label>
        <input type="text" size="20" autocomplete="off" />
    </div>


  <div class="form-row">
        <label>Zip/Postcode</label>
        <input type="text" size="20" autocomplete="off" data-stripe="address_zip" />
    </div>



  <div class="form-row">
        <label>Country</label>
        <input type="text" size="20" autocomplete="off" data-stripe="address_country" />
    </div>
  <div class="form-row">
        <label>Card Number</label>
        <input type="text" size="20" autocomplete="off" data-stripe="number" placeholder = "**** **** **** ****" pattern="[\d ]*" />
    </div>

    <div class="form-row">
        <label>Security Code/CVC</label>
        <input type="text" size="4" autocomplete="off" data-stripe="cvc" placeholder="***" pattern="\d*" />
    </div>

    <div class="form-row">
        <label>Expiration (MM/YYYY)</label>
        <input type="text" size="2" data-stripe="exp-month" placeholder="MM" pattern="\d*" />
        <span> / </span>
        <input type="text" size="4" data-stripe="exp-year" placeholder="YYYY" pattern="\d*" />
    </div>

  <div class="actions">
    <%= f.submit 'Pay', :class => "button" %>
  </div>
<% end %>

表单需要在下面的javascript中引用的id

var stripeResponseHandler = function(status, response) {
  var $form = $('#payment-form');

  if (response.error) {
    // Show the errors on the form
    $form.find('.payment-errors').text(response.error.message);
    $form.find('button').prop('disabled', false);
  } else {
    // token contains id, last4, and card type
    var token = response.id;
    // Insert the token into the form so it gets submitted to the server
    $form.append($('<input type="hidden" name="stripeToken" />').val(token));
    // and re-submit
    $form.get(0).submit();
  }
};


jQuery(function($) {

  $('#payment-form').submit(function(e) {
  var $form = $(this);

  // Disable the submit button to prevent repeated clicks
  $form.find('button').prop('disabled', true);

  Stripe.card.createToken($form, stripeResponseHandler);

  // Prevent the form from submitting with the default action
  return false;
  });
});

订单型号,已删除字段

class Order < ActiveRecord::Base

  PAYMENT_TYPES = ["credit card"]

  CC_COUNTRIES = ["United Kingdom", "France", "Italy"]

  validates :email, presence: true

  validates :pay_type, inclusion: PAYMENT_TYPES

  has_many :line_items, dependent: :destroy

  def add_line_items_from_cart(cart)

    cart.line_items.each do |item|

      item.cart_id = nil

      line_items << item

    end

  end
end

申请表标题

<!DOCTYPE html>
<html>
  <head>
    <title><%= full_title(yield(:title)) %></title>
    <%= stylesheet_link_tag "application", media: "all",
                                           "data-turbolinks-track" => true %>
    <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
    <script type="text/javascript" src="https://js.stripe.com/v2/"></script>
    <script type="text/javascript">

      $(function(){

        Stripe.setPublishableKey('<%= Rails.configuration.stripe[:publishable_key] %>');

      });

    </script>
    <%= csrf_meta_tags %>
    <%= render 'layouts/shim' %>
  </head>
  <body>

订单控制器

class OrdersController < ApplicationController

  include CurrentCart

  before_action :set_cart, only: [:new, :create]

  before_action :set_order, only: [:show,:edit, :update, :destroy]

#  before_action :admin_user, only: [:destroy, :show]


  # GET /orders
  # GET /orders.json
  def index
    @orders = Order.all
  end

  # GET /orders/1
  # GET /orders/1.json
  def show
  end

  # GET /orders/new
  def new

    if @cart.line_items.empty?

      redirect_to store_url, notice: "Your cart is empty"

      return

    end

    @order = Order.new

  end

  # GET /orders/1/edit
  def edit
  end

  # POST /orders
  # POST /orders.json
  def create

    begin

      @order = Order.new(order_params)

      @order.add_line_items_from_cart(@cart)

      Stripe.api_key = "sk_test_xxxxxxxxxxxxxxx"

      @amount = order_total.to_i * 100

      token = params[:stripeToken]

      # Create a Customer
      customer = Stripe::Customer.create(

        :description => order_params[:email], 
        :card => token, 

      )


      charge = Stripe::Charge.create(

        :customer    => customer.id,
        :amount => @amount, # amount in cents, again
        :currency => order_currency,
       # :card => token,
        :description => order_params[:email]
      )

      respond_to do |format|

        if @order.save

          puts "saving now"

          Cart.destroy(session[:cart_id])

          session[:cart_id] = nil

          format.html { redirect_to @order, notice: 'Order was successfully created.' }
          format.json { render action: 'show', status: :created, location: @order }
        else
          format.html { render action: 'new' }
          format.json { render json: @order.errors, status: :unprocessable_entity }
        end
      end

      rescue Stripe::CardError => e

      flash[:error] = e.message
      redirect_to root_path
    end
  end

  # PATCH/PUT /orders/1
  # PATCH/PUT /orders/1.json
  def update
    respond_to do |format|
      if @order.update(order_params)
        format.html { redirect_to @order, notice: 'Order was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: 'edit' }
        format.json { render json: @order.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /orders/1
  # DELETE /orders/1.json
  def destroy
    @order.destroy
    respond_to do |format|
      format.html { redirect_to orders_url }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_order

      @order = Order.find(params[:id])

    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def order_params

      params.require(:order).permit(:email, :pay_type)

    end
end