0
votes

I'm building an events app using Rails and Stripe to handle payments. I've used javascript for my booking page in order to allow a user to book and pay for multiple spaces rather than just one at a time. The booking page allows for this as you can see here -

Booking page view

However on my Stripe dashboard only one payment has been taken for one space -

Stripe payment

How do I solve this so it takes the full payment indicated on the booking view?

Here's my Controller and view code -

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
        #@booking.quantity = @booking.quantity
        @total_amount = @booking.quantity.to_f * @event.price.to_f


    end




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

        Booking.transaction do

            @event.reload
            if @event.bookings.count > @event.number_of_spaces
            flash[:warning] = "Sorry, this event is fully booked."
            raise ActiveRecord::Rollback, "event is fully booked"
            end 
        end




        if @booking.save

            # CHARGE THE USER WHO'S BOOKED
            # #{} == puts a variable into a string
            Stripe::Charge.create(amount: @event.price_pennies, currency: "gbp",
                card: @booking.stripe_token, description: "Booking number #{@booking.id}")

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

        if @event.is_free?

            @booking.save!
            flash[:success] = "Your place on our event has been booked"
            redirect_to event_path(@event)
        end
    end


    private

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



end

booking.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', 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>

<script type="text/javascript">
  Stripe.setPublishableKey('<%= STRIPE_PUBLIC_KEY %>');
  var stripeResponseHandler = function(status, response) {
    var $form = $('#new_booking');

    if (response.error) {
    // Show the errors on the form
    $form.find('.payment-errors').text(response.error.message);
    $form.find('input[type=submit]').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="booking[stripe_token]"     />').val(token));
    // and submit
    $form.get(0).submit();
    }
  };

  // jQuery(function($)  { - changed to the line below
  $(document).on("ready page:load", function () {

    $('#new_booking').submit(function(event) {
      var $form = $(this);

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

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

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

Do I need to add 'price' to my booking_params? I already have 'quantity' in there?

Do I need to add a 'total_amount' column to my bookings table? Or is it related to my Stripe action in the controller and the amount?

Any help is appreciated.

1

1 Answers

0
votes

You need to create Stripe.order and pass items: [], where you can pass quantity. In your case it will be one item.

Please check out Stripe API for more information

Update

Here step by step guide:

  1. Login to Stripe account, create a product and make it active. This will be your booking. Save it.
  2. When you inside the product, you'll see Inventory. Find + Add SKU. Click it.
  3. You'll see the popup window like this enter image description here
  4. Fill in whatever you need to. In your case currency, price and inventory, perhaps image. ID will be generated automatically. Press Add SKU
  5. Now you'll have SKU for this product. As you probably guessed already, you can have many SKU's for the same product and the price can be different.
  6. Copy newly created SKU id
  7. Now you can create Stripe::Order like this:

    response = Stripe::Order.create(
      currency: 'usd',
      items: [
        {
          type: 'sku',
          parent: 'sku_9AJ4OfKbdRuoGi', # SKU id
          quantity: 2
        }
      ],
      customer: nil,  # replace with customer id if needed
      email: '[email protected]'
    )
    

This is how your response would look like:

#<Stripe::Order:0x3ff36d37e2b8 id=or_18ryyuBq3wgBfJrxa44Jzm3T> JSON: {
  "id": "or_18ryyuBq3wgBfJrxa44Jzm3T",
  "object": "order",
  "amount": 19998,
  "amount_returned": null,
  "application": null,
  "application_fee": null,
  "charge": null,
  "created": 1473465868,
  "currency": "usd",
  "customer": null,
  "email": "[email protected]",
  "items": [
    {"object":"order_item","amount":19998,"currency":"usd","description":"booking","parent":"sku_9AJ4OfKbdRuoGi","quantity":2,"type":"sku"},
    {"object":"order_item","amount":0,"currency":"usd","description":"Taxes (included)","parent":null,"quantity":null,"type":"tax"},
    {"object":"order_item","amount":0,"currency":"usd","description":"Free shipping","parent":"ship_free-shipping","quantity":null,"type":"shipping"}
  ],
  "livemode": false,
  "metadata": {},
  "returns": {"object":"list","data":[],"has_more":false,"total_count":0,"url":"/v1/order_returns?order=or_18ryyuBq3wgBfJrxa44Jzm3T"},
  "selected_shipping_method": "ship_free-shipping",
  "shipping": null,
  "shipping_methods": [
    {"id":"ship_free-shipping","amount":0,"currency":"usd","delivery_estimate":null,"description":"Free shipping"}
  ],
  "status": "created",
  "status_transitions": null,
  "updated": 1473465868
}

When examining the response of any API call or webhook notification, use the Order object’s status attribute to verify the current status of the order and respond accordingly.

  • When an order is first requested, the resulting order object has an initial status of created.
  • When an order is paid, status changes to paid. You should now fulfill the order.
  • After fulfilling the order, update the Order object to change status to fulfilled. Only orders in the paid state can be marked as fulfilled.
  • If the customer cancels the order (through your site) before it’s been fulfilled, update status to canceled which will refund the payment.
  • If the order has been fulfilled and the customer returns the purchased items, update status to returned which will refund the payment.

Read order guide for more details.

Now you should see new order in Stripe's dashboard

Hope this helps.