Rails & Stripe - Stripe card token not saving to the new customer for payment later (subscription w free trial)

614 Views Asked by At

I'm using Rails, Devise and Stripe for a SaaS application and I'm having trouble saving the customers card information at the point of sign up.

The reason I want to save the information without charging is because it's a subscription plan with a free trial.

So far I can get the card token to be created and the customer to be created and they both appear in the Stripe dashboard logs (POST /v1/tokens and POST /v1/customer).

The problem is the card is not then showing as saved for the customer, i.e. they are both being submitted but they are not connected.

I think it has something to do with how I'm creating my customer in my User model:

Stripe::Customer.create(description: plan_id, email: email, plan: plan_id, card: stripeToken)

But I can't seem to find a solution.

Here is my form:

<%= form_for(resource, as: resource_name, url: registration_path(resource_name), :html => {:id => "payment-form"}) do |f| %>
  <%= devise_error_messages! %>

  <%= hidden_field_tag 'plan', params[:plan] %>

  <div class="field form-group">
    <%= f.label :email %><br />
    <%= f.email_field :email, autofocus: true, class: 'form-control' %>
  </div>
  <div class="field form-group">
    <%= f.label :password %>
    <% if @validatable %>
    <em>(<%= @minimum_password_length %> characters minimum)</em>
    <% end %><br />
    <%= f.password_field :password, autocomplete: "off", class: 'form-control' %>
  </div>
  <div class="field form-group">
    <%= f.label :password_confirmation, "Password Confirmation" %><br />
    <%= f.password_field :password_confirmation, autocomplete: "off", class: 'form-control' %>
  </div>
  <!-- CC -->
    <div class="form-row">
      <label for="card-element">
        Credit or Debit Card
      </label>
      <div id="card-element" class="form-control"></div>

      <!-- Used to display form errors -->
      <div id="card-errors" role="alert"></div>
    </div>
  <!-- / CC -->
  <div class="actions form-group text-center" style="margin-top: 40px;">
    <input type="submit" class="btn btn-success btn-block" value="Create Account">
  </div>
<% end %>

Here is the JS:

#In <head> <script src="https://js.stripe.com/v3/"></script>

var stripe = Stripe('pk_test_sadnasd2n3krekl3k434kla');
  var elements = stripe.elements();
  var card = elements.create('card', { style:
    {
      base: {
        lineHeight: '2'
      }
    }
  });

  // Add an instance of the card UI component into the `card-element` <div>
  card.mount('#card-element');

  function stripeTokenHandler(token) {
    // Insert the token ID into the form so it gets submitted to the server
    var form = document.getElementById('payment-form');
    var hiddenInput = document.createElement('input');
    hiddenInput.setAttribute('type', 'hidden');
    hiddenInput.setAttribute('name', 'stripeToken');
    hiddenInput.setAttribute('value', token.id);
    form.appendChild(hiddenInput);

    // Submit the form
    form.submit();
  }

  function createToken() {
    stripe.createToken(card).then(function(result) {
      if (result.error) {
        // Inform the user if there was an error
        var errorElement = document.getElementById('card-errors');
        errorElement.textContent = result.error.message;
      } else {
        // Send the token to your server
        stripeTokenHandler(result.token);
      }
    });
  };

  // Create a token when the form is submitted.
  var form = document.getElementById('payment-form');
  form.addEventListener('submit', function(e) {
    e.preventDefault();
    createToken();
  });

  card.addEventListener('change', function(event) {
    var displayError = document.getElementById('card-errors');
    if (event.error) {
      displayError.textContent = event.error.message;
    } else {
      displayError.textContent = '';
    }
  });

Here is the controller:

class Users::RegistrationsController < Devise::RegistrationsController
  def create
    super do |resource|
      if params[:plan]
        resource.plan_id = params[:plan]
        if resource.plan_id == "1"
          resource.save_with_payment
        else
          resource.save
        end
      end
    end
  end
end

And here is the model:

class User < ApplicationRecord

  attr_accessor :stripeToken

  def save_with_payment
    if valid?
      customer = Stripe::Customer.create(description: plan_id, email: email, plan: plan_id, card: stripeToken)
      self.stripe_customer_token = customer.id
      save!
    end
  end
end

Can any one help with why the card is not linking with the customer when it is created?

2

There are 2 best solutions below

0
On

stripe token should be passed in source attribute not in card.

it will work we are using this way and you can also refer to the API documentation it doesn't accept card attribute .

it accepts stripeToken as source in customer endpoint Refer

0
On

When you create the customer, you can add the card token(s) as part of the sources object.

If you check the related docs you find that all you need is passing down the already saved token for the customer.

Here's a step by step guide as well on how to attach a source to the customer object using the Ruby gem