2

I'm trying to submit a form that creates a new user (devise) and payment token through braintree.js all in one form. My html form looks like this.

<div class="container">
  <h2 class='registration'>Sign up</h2>
  <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: {:class => 'col-sm-12'}) do |f| %>
    <%= devise_error_messages! %>

    <div class="form-horizontal">
      <%= f.label :email, class: "col-sm-3 control-label" %>
      <div class="col-sm-7">
        <%= f.email_field :email, class: "form-control" %>
      </div>
    </div><br /><br />

    <div class="form-horizontal">
      <%= f.label :full_name, class: "col-sm-3 control-label" %>
      <div class="col-sm-7">
        <%= f.text_field :full_name, class: "form-control",autofocus: true %>
      </div>
    </div><br /><br />

    <div class="form-horizontal">
      <%= f.label :password, class: "col-sm-3 control-label" %>
      <div class="col-sm-7">
        <%= f.password_field :password, autocomplete: "off", class: "form-control" %>
      </div>
    </div><br /><br />

    <div class="form-horizontal">
      <%= f.label :password_confirmation, class: "col-sm-3 control-label" %>
      <div class="col-sm-7">
        <%= f.password_field :password_confirmation, autocomplete: "off", class: "form-control" %>
      </div>
    </div><br /><br />

      <input data-braintree-name="number" value="4111111111111111">
      <input data-braintree-name="cvv" value="100">

      <input data-braintree-name="expiration_month" value="10">
      <input data-braintree-name="expiration_year" value="2020">

      <div id="braintree-container"></div>

      <script src="https://js.braintreegateway.com/v2/braintree.js"></script>
      <script>
        braintree.setup("MY BRAINTREE TOKEN",
                        "custom",
                          {
                          container: "braintree-container",
                          paymentMethodNonceInputField: "payment-method-nonce"}
        );
        var client = new braintree.api.Client({clientToken: <%= @client_token %>});
        client.tokenizeCard({number: "4111111111111111", expirationDate: "10/20"}, function (err, nonce) {

      </script>

    <div class="form-group">
      <div class="col-sm-offset-2 col-sm-6">
        <%= f.submit "Sign up", class: "btn btn-primary form-registration", id: "user-form-submit" %>
      </div>
    </div><br /><br />
  <% end %>
</div>

What I think should happen when I submit this from is. Rails creates a new user. Then Braintree.js makes a request to their service with the CC info and send back a params[:payment method_nonce] with values. However what happens is the user gets created, but I get back no information about the payment_method_nonce.

You could ask why am I trying to add all this information from one page? I plan to create a background worker to create subscriptions once users sign up.

Controller (my user controller)

class Users::RegistrationsController < Devise::RegistrationsController
  def new
    @client_token = Braintree::ClientToken.generate()
    super
  end


  def create
    super
  end

 def configure_sign_up_params
    devise_parameter_sanitizer.for(:sign_up) << [:full_name, :credit_card, :cvc,
    :expiration_month, :expiration_year, :token, :client_token]
  end

My schema looks like this

 create_table "users", force: true do |t|
    t.string   "email",                  default: "", null: false
    t.string   "encrypted_password",     default: "", null: false
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string   "full_name",                           null: false
    t.string   "token"
  end

Additional information

-Rails 4 -Ruby 2.0 - Braintree.js v2

jmoon90
  • 339
  • 4
  • 17

1 Answers1

2

I work at Braintree. Braintree's custom integration is designed to give you control over the appearance of your form, while still automatically handling the secure transfer of credit card information to the BT servers and adding a payment method nonce to your form. It is not used in conjunction with a braintree.api.Client and card tokenization; this is only used if you want to handle the tokenization of the card data yourself or to do more complex things with the form before submission.

As for why you are not getting the nonce, are you interpolating the client token in your call to braintree.setup (as you do in the creation of your client)? Also, because you are supplying the paymentMethodNonceInputField to the setup call, this is the name of the hidden input that the BT javascript will use to send you the nonce. This is an optional field, so unless you have a reason for naming this parameter with dashes instead of underscores, you may want to remove it. If you do both these things, the nonce should be accessible within your controller as params[:payment_method_nonce].

Lastly, as we don’t recommend creating a user in the same form as the payment method. It occasionally happens that a user will enter credit card data in the wrong field, in which case it would end up on your server.

To accomplish your goal, you probably need to reverse the order in which things happen, and have two separate forms. First, click submit and let Braintree.js do its thing, then in a callback you pass to it, take the result and put it into the separate form you submit to rails and submit that, then do your user creation etc. with the information that form submits.

You may want to reach out to Braintree support if you have further questions.

agf
  • 171,228
  • 44
  • 289
  • 238
cdeist
  • 229
  • 2
  • 6
  • With Braintree.js you don't need to create two separate forms. As the JS will handle submitting the provided cc info to Braintree's server. I figured the issue out. I'm going to write a small blog post on how to do what I asked for above. – jmoon90 Feb 17 '15 at 14:42