Why are stripeTokens no longer being generated for my registration form?

60 views Asked by At

I migrated most of Payola to Rails 5.2, and I was able to create subscriptions for users without issue up until this point. I'm puzzled at why my form is unable to generate stripeTokens and store them with the form. I receive the error stripeToken required for new customer with paid subscription. How can I fix my registration form to properly capture the stripeToken before committing the active job? I'm using the 2017 API.

UPDATE: referencing subscription_form_onestep.js. Stripe no longer allows you to use Stripe.card.createToken. I'm currently trying to see if using Stripe.createToken will work without issue.

UPDATE 2: the form is still not passing the stripeToken when the submission process begins.

UPDATE 3: It works after changing Stripe.card.createToken to Stripe.createToken and adding <%= yield(:head) %> to application.html.

registration/new.html.erb

{"guid":"74l524","status":"pending","error":null}

Server Log Error

 [ActiveJob] [Payola::Worker::ActiveJob] [2597f80c-eb4c-4d51-a009-07e35668b07c]   Plan Load (5.0ms)  SELECT  "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT $2  [["id", 4], ["LIMIT", 1]]
 [ActiveJob] [Payola::Worker::ActiveJob] [2597f80c-eb4c-4d51-a009-07e35668b07c]   Payola::Subscription Exists (1.0ms)  SELECT  1 AS one FROM "payola_subscriptions" WHERE "payola_subscriptions"."guid" = $1 AND "payola_subscriptions"."id" != $2 LIMIT $3  [["guid", "gjfsnd"], ["id", 49], 
 ["LIMIT", 1]]
 [ActiveJob] [Payola::Worker::ActiveJob] [2597f80c-eb4c-4d51-a009-07e35668b07c]   Payola::Subscription Update (1.0ms)  UPDATE "payola_subscriptions" SET "error" = $1, "updated_at" = $2 WHERE "payola_subscriptions"."id" = $3  [["error", "stripeToken required for new customer with paid subscription"], ["updated_at", "2019-02-24 15:10:20.714048"], 
  ["id", 49]]
 [ActiveJob] [Payola::Worker::ActiveJob] [2597f80c-eb4c-4d51-a009-07e35668b07c]   PaperTrail::Version Create (1.0ms)  INSERT INTO "versions" ("item_type", "item_id", "event", "object", "created_at") 

subscription_form_onestep.js

var PayolaOnestepSubscriptionForm = {
initialize: function () {
    $(document).off('submit.payola-onestep-subscription-form').on(
        'submit.payola-onestep-subscription-form', '.payola-onestep-subscription-form',
        function () {
            return PayolaOnestepSubscriptionForm.handleSubmit($(this));
        }
    );
},

handleSubmit: function (form) {
    if (!PayolaOnestepSubscriptionForm.validateForm(form)) {
        return false;
    }
    $(form).find("input[type=submit]").prop("disabled", true);
    $('.payola-spinner').css('visibility', 'visible');
    Stripe.card.createToken(form, function (status, response) {

        PayolaOnestepSubscriptionForm.stripeResponseHandler(form, status, response);
    });
    return false;
},

validateForm: function (form) {
    var cardNumber = $("input[data-stripe='number']").val();
    if (!Stripe.card.validateCardNumber(cardNumber)) {
        PayolaOnestepSubscriptionForm.showError(form, 'The card number is not a valid credit card number.');

        return false;
    }
    if ($("[data-stripe='exp']").length) {
        var valid = !Stripe.card.validateExpiry($("[data-stripe='exp']").val());
    } else {
        var expMonth = $("[data-stripe='exp_month']").val();
        var expYear = $("[data-stripe='exp_year']").val();
        var valid = !Stripe.card.validateExpiry(expMonth, expYear);
    }
    if (valid) {
        PayolaOnestepSubscriptionForm.showError(form, "Your card's expiration month/year is invalid.");
        return false
    }

    var cvc = $("input[data-stripe='cvc']").val();
    if (!Stripe.card.validateCVC(cvc)) {
        PayolaOnestepSubscriptionForm.showError(form, "Your card's security code is invalid.");
        return false;
    }

    return true;
},

stripeResponseHandler: function (form, status, response) {
    if (response.error) {
        PayolaOnestepSubscriptionForm.showError(form, response.error.message);
    } else {
        var email = form.find("[data-payola='email']").val();
        var coupon = form.find("[data-payola='coupon']").val();
        var quantity = form.find("[data-payola='quantity']").val();

        var base_path = form.data('payola-base-path');
        var plan_type = form.data('payola-plan-type');
        var plan_id = form.data('payola-plan-id');

        var action = $(form).attr('action');

        form.append($('<input type="hidden" name="plan_type">').val(plan_type));
        form.append($('<input type="hidden" name="plan_id">').val(plan_id));
        console.log(response.id + "stripeToken Value DEBUG");
        form.append($('<input type="hidden" name="stripeToken">').val(response.id));
        form.append($('<input type="hidden" name="stripeEmail">').val(email));
        form.append($('<input type="hidden" name="coupon">').val(coupon));
        form.append($('<input type="hidden" name="quantity">').val(quantity));
        form.append(PayolaOnestepSubscriptionForm.authenticityTokenInput());
        $.ajax({
            type: "POST",
            url: action,
            data: form.serialize(),
            success: function (data) {
                PayolaOnestepSubscriptionForm.poll(form, 60, data.guid, base_path);
            },
            error: function (data) {
                PayolaOnestepSubscriptionForm.showError(form, jQuery.parseJSON(data.responseText).error);
            }
        });

    }
},

poll: function (form, num_retries_left, guid, base_path) {
    if (num_retries_left === 0) {
        PayolaOnestepSubscriptionForm.showError(form, "This seems to be taking too long. Please contact support and give them transaction ID: " + guid);
    }
    var handler = function (data) {
        if (data.status === "active") {
            window.location = base_path + '/confirm_subscription/' + guid;
        } else {
            setTimeout(function () {
                PayolaOnestepSubscriptionForm.poll(form, num_retries_left - 1, guid, base_path);
            }, 500);
        }
    };
    var errorHandler = function (jqXHR) {
        PayolaOnestepSubscriptionForm.showError(form, jQuery.parseJSON(jqXHR.responseText).error);
    };

    if (typeof guid != 'undefined') {
        $.ajax({
            type: 'GET',
            dataType: 'json',
            url: base_path + '/subscription_status/' + guid,
            success: handler,
            error: errorHandler
        });
    }
},

showError: function (form, message) {

    $('.payola-spinner').css("visibility", "hidden");
    $(form).find("input[type=submit]").prop('disabled', false).trigger('error', message);

    var error_selector = form.data('payola-error-selector');
    if (error_selector) {
        $(error_selector).text(message);
        $(error_selector).show();
    } else {
        form.find('.payola-payment-error').text(message);
        form.find('.payola-payment-error').show();
    }
},

authenticityTokenInput: function () {
    return $('<input type="hidden" name="authenticity_token"></input>').val($('meta[name="csrf-token"]').attr("content"));
 }
};

PayolaOnestepSubscriptionForm.initialize();

routes.rb

  scope module: 'payola' do
    post 'payola/buy/:product_class/:permalink' => 'transactions#create', as: :buy
    get 'payola/confirm/:guid' => 'transactions#show', as: :confirm
    get 'payola/status/:guid' => 'transactions#status', as: :status
    post 'payola/subscribe/:plan_class/:plan_id' => 'subscriptions#create', as: :subscribe
    get 'payola/confirm_subscription/:guid' => 'subscriptions#show', as: :confirm_subscription
    get 'payola/subscription_status/:guid' => 'subscriptions#status', as: :subscription_status
    delete 'payola/cancel_subscription/:guid' => 'subscriptions#destroy', as: :cancel_subscription
    post 'payola/change_plan/:guid' => 'subscriptions#change_plan', as: :change_subscription_plan
    post 'payola/change_quantity/:guid' => 'subscriptions#change_quantity', as: :change_subscription_quantity
    post 'payola/update_card/:guid' => 'subscriptions#update_card', as: :update_card
    post 'payola/update_customer/:id' => 'customers#update', as: :update_customer
    post 'payola/create_card/:customer_id' => 'cards#create', as: :create_card
    delete 'payola/destroy_card/:id/:customer_id' => 'cards#destroy', as: :destroy_card
    mount StripeEvent::Engine => '/events'
  end

new.html.erb

<% content_for :head do %>
  <%= render 'payola/transactions/stripe_header' %>
<% end %>

<div class="container">
  <div class="form-padding-top">
    <%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => {
    :class => 'payola-onestep-subscription-form',
    'data-payola-base-path' => payola_path,
    'data-payola-plan-type' => resource.plan.plan_class,
    'data-payola-plan-id' => resource.plan.id}) do |f| %>

  <div class="form-group">
    <div class="col-md-8">
      <%= devise_error_messages! %>
      <span id="error_explanation" class="payola-payment-error"></span>
    </div>
  </div>

  <fieldset class="form-group">
    <legend><strong>Sign up</legend>
  </fieldset>

  <div class="form-group">
    <div class="col-md-4">
      <label>Select a plan for your company</label>
      <%= f.collection_select(:plan_id, Plan.user_plan, :id, :name, {}, {:class => 'form-control'}) %>
    </div>
  </div>

  <div class="form-group">
    <label for="fg-1" class="col-md-5 col-form-label">Email</label>
    <div class="col-md-5">
      <%= f.email_field :email, label: false, required: true, autofocus: true, placeholder: 'What is your email address?', class: 'form-control' %>
    </div>
  </div>

  <div class="form-group">
    <label class="col-md-5 col-form-label">Username</label>
    <div class="col-md-5">
      <span class="username-icon-styles">
        <span id="valid-username" class="fa fa-check-circle"></span>
        <span id="invalid-username" class="fa fa-times-circle"></span>
      </span>
      <%= f.text_field :username, label: false, required: true, placeholder: 'Create a username for your account', class: 'form-control' %>
    </div>
  </div>

      <p class="status" style="display: block;">
          <span class="status_icon"></span>
          <span class="status_message"></span>
        </p>
      </div>
    </div>
  </div>
  <div class="form-group">
    <div class="col-md-2">
      <label>CVC</label>
      <%= text_field :card_code, nil, name: nil, class: 'form-control', data: {stripe: 'cvc'}, placeholder: '000', maxlength: 3 %>
    </div>
  </div>
  <br/>
  <div class="form-group">
    <div class="col-md-2">
      <label>Card Expiration</label>
      <%= select_month nil, {use_two_digit_numbers: true}, {name: nil, data: {stripe: 'exp_month'}, class: 'form-control'} %>
      <%= select_year nil, {start_year: Date.today.year, end_year: Date.today.year + 10}, {name: nil, data: {stripe: 'exp_year'}, class: 'form-control'} %>
    </div>
  </div>

  <div class="form-group">
    <div class="col-md-5">
      <label>Password</label>
      <%= f.password_field :password, required: true, hint: ("#{@minimum_password_length} characters minimum, include special characters @!%$& and capital letters" if @minimum_password_length), class: 'form-control', label: false %>
    </div>
  </div>
  <div class="form-group">
    <div class="col-md-5">
      <label for="password-confirm">Password Confirm</label>
      <%= f.password_field :password_confirmation, required: true, label: false, class: 'form-control' %>
    </div>
  </div>

  <%= f.submit "Sign up", :class => 'bttn-material-flat bttn-md bttn-primary button', data: {disable_with: false} %>
  <div class="text-center">
    <div class="payola-spinner">Loading&#8230;</div>
  </div>
<% end %>

registrations_controller.rb

class Users::RegistrationsController < Devise::RegistrationsController
  include Payola::StatusBehavior
  before_action :cancel_subscription, only: [:destroy]

  def new
    build_resource({})
    unless params[:plan].nil?
      @plan = Plan.find_by!(stripe_id: params[:plan])
      resource.plan = @plan
    end
    yield resource if block_given?
    respond_with resource
  end

  def create
    build_resource(sign_up_params)
    plan = Plan.find_by!(id: params[:user][:plan_id].to_i)
    resource.role = User.roles[plan.stripe_id]
    resource.save
    yield resource if block_given?
    if resource.persisted?
      if resource.active_for_authentication?
    set_flash_message :notice, :signed_up if is_flashing_format?
    sign_up(resource_name, resource)
    subscribe
  else
    set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_flashing_format?
    expire_data_after_sign_in!
    subscribe
  end
else
  clean_up_passwords resource
  resource.errors.full_messages.each {|i| flash[i] = i}
  end
end

  def user_change_plan
    plan = Plan.find_by!(id: params[:user][:plan_id].to_i)
    unless plan == current_user.plan
      role = User.roles[plan.stripe_id]
      if current_user.update_attributes!(plan: plan, role: role)
    subscription = Payola::Subscription.find_by!(owner_id: current_user.id)
    Payola::ChangeSubscriptionPlan.call(subscription, plan)
    redirect_to edit_user_registration_path, notice: 'Plan changed.'
  else
    flash[:alert] = 'Unable to change plan.'
    build_resource
    render :edit
   end
  end
 end

  def user_change_credit_card
    @subscription = Payola::Subscription.find_by!(owner_id: current_user.id)
    if @subscription.save
      Payola::UpdateCard.call(@subscription, params[:stripeToken])
      redirect_to edit_user_registration_path, notice: 'Your Credit Card Was Successfully Updated.'
    else
      build_resource
      redirect_to edit_user_registration_path, notice: "There was an error, please check for mistakes with your card information"
    end
  end

  def cancel_user_subscription
    @subscription = Payola::Subscription.find_by!(owner_id: current_user.id, state: 'active')
    if @subscription.save
      Payola::CancelSubscription.call(@subscription) if [email protected]?
      current_user.plan_id = nil
      current_user.deactivated = true
      current_user.save
      redirect_to edit_user_registration_path, notice: "Your Plan was Successfully Cancelled."
    else
      build_resource
      redirect_to edit_user_registration_path, notice: "Something went wrong, please try again."
    end
  end

  private

  def after_update_path_for(resource)
    authenticated_user_root_path
  end

  def after_sign_up_path_for(resource)
    u_dashboard_index_path
  end

  def subscribe
    params[:plan] = current_user.plan
    params[:stripeEmail] = current_user.email
    params[:trial_start] = DateTime.now
    params[:trial_end] = DateTime.now + 6.days
    subscription = Payola::CreateSubscription.call(params, current_user)
    current_user.save
    render_payola_status(subscription)
  end
end
0

There are 0 answers