【问题标题】:Rails - how to correctly implement a payment/booking processRails - 如何正确实施付款/预订流程
【发布时间】:2017-02-12 13:57:18
【问题描述】:

我正在 Rails 中构建一个事件应用程序,并且我正在使用 Stripe 来收取我的付款。我花了一段时间,但我的付款工作正常。但是,当我尝试增加预订的空间数量时,系统只会收取一次付款。因此,如果一个活动的每个“空间”是 10 英镑,并且有人想要预订 5 个空间,无论付款表格是否注明了正确的金额(本例中为 50 英镑),都只会收取 10 英镑。 我很确定这是一个 MVC 问题,但似乎无法发现如何解决它。在我看来,我有以下代码-

bookings.new.html.erb

    <div class="col-md-6 col-md-offset-3" id="eventshow">
  <div class="row">
    <div class="panel panel-default">
        <div class="panel-heading">
            <h2>Confirm Your Booking</h2>
        </div>
                  <div class="calculate-total">
                              <p>
                                  Confirm number of spaces you wish to book here:
                                    <input type="number" placeholder="1"  min="1" value="1" class="num-spaces">
                              </p>
                                <p>
                                    Total Amount
                                    £<span class="total" data-unit-cost="<%= @event.price %>">0</span>
                                </p>
                          </div>





                <%= simple_form_for [@event, @booking], id: "new_booking" do |form| %>



                 <span class="payment-errors"></span>

                <div class="form-row">
                    <label>
                      <span>Card Number</span>
                      <input type="text" size="20" data-stripe="number"/>
                    </label>
                </div>

                <div class="form-row">
                  <label>
                  <span>CVC</span>
                  <input type="text" size="4" data-stripe="cvc"/>
                  </label>
                </div>

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

               <%= form.button :submit %>


            </div> 

<% end %>
<% end %>

      </div>
  </div>
</div>  

<script type="text/javascript">
    $('.calculate-total input').on('keyup change', calculateBookingPrice);

function calculateBookingPrice() {
  var unitCost = parseFloat($('.calculate-total .total').data('unit-cost')),
      numSpaces = parseInt($('.calculate-total .num-spaces').val()),
      total = (numSpaces * unitCost).toFixed(2);

  if (isNaN(total)) {
    total = 0;
  }

  $('.calculate-total span.total').text(total);
}

  $(document).ready(calculateBookingPrice)

</script>



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

(code for stripe)

这在实际付款页面上运行良好,如下例所示 -

因此,在此示例中,我使用了@event.price 1 英镑/空间/预订。但是,如果我继续处理此预订,而不是 Stripe 收取的 3 英镑,它只会显示为 1 英镑。

我认为这可能与@event.price 是我在我的观点中表达的事实有关,但是,当我用@booking.total_amount 替换@event.price 时,我只会得到我的总金额付款表格(如上)显示为零,无论我使用数量函数做什么,它都保持为零。

从视图代码中可以看出,我的数量函数是用一些 javascript 处理的。我是否缺少此功能中的某些内容以使其正常工作?

我的控制器中是否有正确的参数?这是我的控制器代码 -

bookings_controller.rb

     class BookingsController < ApplicationController

    before_action :authenticate_user!

    def new
        # booking form
        # I need to find the event that we're making a booking on
        @event = Event.find(params[:event_id])
        # and because the event "has_many :bookings"
        @booking = @event.bookings.new(quantity: params[:quantity])
        # which person is booking the event?
        @booking.user = current_user
        #@total_amount = @event.price * @booking.quantity


    end

    def create

        # actually process the booking
        @event = Event.find(params[:event_id])
        @booking = @event.bookings.new(booking_params)
        @booking.user = current_user

            if 
                @booking.reserve
                flash[:success] = "Your place on our event has been booked"
                redirect_to event_path(@event)
            else
                flash[:error] = "Booking unsuccessful"
                render "new"
            end
    end


    private

    def booking_params
        params.require(:booking).permit(:stripe_token, :quantity, :event_id, :stripe_charge_id, :total_amount)
    end



end

这是我的带有验证的模型代码 -

Booking.rb

     class Booking < ActiveRecord::Base

    belongs_to :event
    belongs_to :user


    validates :total_amount, presence: true, numericality: { greater_than: 0 }
    validates :quantity, :total_amount, presence: true

    def reserve
        # Don't process this booking if it isn't valid
        self.valid?



                # Free events don't need to do anything special
                if event.is_free?
                save!

                # Paid events should charge the customer's card
                else

                    begin
                        self.total_amount = event.price * self.quantity
                        charge = Stripe::Charge.create(
                            amount: total_amount * 100,
                            currency: "gbp",
                            source: stripe_token, 
                            description: "Booking created for amount #{total_amount}")
                        self.stripe_charge_id = charge.id
                        save!
                    rescue Stripe::CardError => e
                    errors.add(:base, e.message)
                    false
                end
            end 

    end
end

这让我困惑了一段时间。我对 Rails 还很陌生,所以确信更有经验的眼睛可以发现我哪里出错了。任何指导表示赞赏。

【问题讨论】:

  • 请提供MCVE。没有人会浏览 2K 的文本和图像。
  • 另外,请花点时间正确地格式化您的代码......它使事情变得更容易阅读。 (注意:在 ruby​​ 中,使用 2 个空格 进行缩进是一个强约定。)
  • 非常感谢您的建议。我已经编辑了这个问题。如果您能提供您可能拥有的任何解决方案,我将不胜感激。
  • 我可能不会因此而真正受欢迎,但需要说一下。您的问题和代码暴露出对创建这样一个系统的非常基本的原则缺乏理解。我不知道你是否得到报酬,但你真的需要专家帮助(我不是说关于 SO 的答案)。如果你打算这样做,你应该首先完成标准的 Rails 书,也许还有另一个较小的项目。您正在处理信用卡、登录信息和其他人的钱——这些事情只有在更深入地了解如何正确构建它的情况下才能完成。
  • 这很公平,我对你的诚实没有意见。不,我没有得到报酬,我正在为自己建造这个。一个人如何在不首先尝试构建它的情况下获得对某事物的更深入理解?如何“缩减”支付系统 - 要么接受您的付款,要么不接受?

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


【解决方案1】:

请注意,这不是逐行修复,而是朝着正确方向轻推。

您将所有内容都塞进了一组过于有限的模型和控制器中。

我明白了——“太多的课程”一开始可能看起来很可怕。但是,让部分完成一项工作并且做得很好,总比拥有几个封装整个领域的神类要好。后者是维护的噩梦。

例如,考虑用户想要预订 2 位成人和 1 位儿童的情况。您的单一预订模式并没有被削减。

# respresents an order
class Booking < ActiveRecord::Base
  enum status: [:open, :payed]
  belongs_to :event
  belongs_to :user

  has_many :items
  has_many :payments

  accepts_nested_attributes_for :items

  # get a sub_total (minus taxes)
  def sub_total
    items.map(&:sub_total).sum
  end

  def grand_total
    sub_total + taxes
  end
end

# represents an item (a seat for example) in a booking
class Item < ActiveRecord::Base
  belongs_to :booking

  def sub_total
    (price - rebate) * quantity
  end
end

# respresents a payment attached to a booking
class Payment < ActiveRecord::Base
  belongs_to :booking

  # charges payment to stripe and saves
  def charge!
  end
end

您还需要为每个作业使用单独的控制器:

class BookingsController < ActiveRecord::Base

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

  # GET /bookings/:booking_id
  # Show a users
  def show
    @booking = Booking.joins(:event, :payments).find(params[:id])
  end

  # GET /events/:event_id/bookings/new
  def new
    @booking = @event.bookings.new(user: current_user)
  end

  # POST /events/:event_id/bookings
  def create
    @booking = @event.bookings.new(booking_params) do |b|
      b.user = current_user
    end

    if @booking.save
      if @booking.free?
        redirect_to @booking
      else
        redirect_to new_payment_path(@booking)
      end
    else
      render :new
    end
  end

  private 

     def set_event
        @event = Event.find(params[:id])
     end

     def booking_params
       params.require(:booking).permit(items_attributes: [:foo, :bar])
     end
end

class PaymentsController < ActiveRecord::Base

  before_action :set_booking

  # GET /bookings/:booking_id/payments/new
  def new
    @payment = @booking.payments.new 
  end

  # POST /bookings/:booking_id/payments
  def create
    @payment = @booking.payments.new do |p|
      p.total = @booking.grand_total
    end

    if !@payment.valid?
      render :new
    else
      if @payment.charge!
        redirect_to @booking, success: 'Thank you for your payment!'
      else
        render :new
      end
    end
  end

  private
    def set_booking
      @booking = Booking.find(params[:id])
    end

    def payment_params
      params.require(:payment).permit(:foo, :bar)
    end
end

但我希望将整个内容放在一个页面中!

将整个事情作为一个单一的表单/发布请求来创建一个非常脆弱和复杂的流程,控制器中有大量的代码路径。

此外,您的 javascript 代码让恶意用户可以通过更改 DOM 来设置他们认为合适的任何价格。

首先了解如何以同步方式正确执行此操作。一旦成功,您就可以向控制器添加 JSON 响应,并将流程实现为多个 AJAX 调用。

【讨论】:

  • 您还需要一个附加到 Event 的模型来保存不同类型的票价。
  • 我工作的第一个支付系统只有两个巨大的 PHP 脚本和一堆 SOAP 调用,它几乎解决了你的解决方案所遇到的所有问题——通过将它分成一系列我能够做到的步骤使其更加健壮,实际上提高了转化率。
  • 感谢以上所有细节。认为你是对的 - 我需要重新考虑我是如何做到这一点的。欣赏细节。
【解决方案2】:

问题在于您只需更改显示总量的&lt;span&gt; 标签,而不是发送到服务器/条带本身的数据。这一行:

$('.calculate-total span.total').text(total);

只更新 textual 值,而不是发送到您的服务器的实际参数。

我认为您可以通过检查发送到您的BookingsController 的参数来验证这一点。如果您在控制器中打印出params,我怀疑您会看到您的Javascript 没有更新quantity 参数。

我真的不记得simple_form 是如何工作的,但是您应该能够为您的quantity 属性添加一个隐藏的输入字段,然后通过您的Javascript 函数进行设置。像这样的:

function calculateBookingPrice() {
  var unitCost = parseFloat($('.calculate-total .total').data('unit-cost')),
      numSpaces = parseInt($('.calculate-total .num-spaces').val()),
      total = (numSpaces * unitCost).toFixed(2);

  if (isNaN(total)) {
    total = 0;
  }

  $('.calculate-total span.total').text(total);

  // assumes there's an input field with id "booking-quantity".
  $('#booking-quantity').val(total); 
}

$(document).ready(calculateBookingPrice)

【讨论】:

  • 那我是否也需要在我的条带代码中进行更改?我没有在我的问题中显示条带代码,因为我不确定是否有必要并且被告知要编辑显示的代码量。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-20
  • 2014-12-10
  • 1970-01-01
  • 1970-01-01
  • 2015-11-07
  • 1970-01-01
相关资源
最近更新 更多