I am having a registration page where user will enter personal info along with credit card info. This credit card info is integrated with activemerchant and as well as this info is stored in the database.
This is my form:
<%= semantic_form_for(@account, :url => account_create_path, :html => { :multipart => true, :class => 'billing'}) do |f| %>
<div class="section section-first">
<%= f.inputs :for => :user do |u| %>
<h3>Account Information</h3>
<%= u.input :name, :input_html => {:placeholder => "Name", :value => @account.user.name} %>
<%= u.input :email, :input_html => {:placeholder => "Email", :value => @account.user.email} %>
<% end %>
</div>
<div class="section">
<%= f.inputs :for => :creditcard do |c| %>
<h3>Credit Card Information</h3>
<%= c.input :brand, :selected => @creditcard.brand.nil? ? "visa" : @creditcard.brand, :label => "Credit Card", :as => :select, :class => 'dropkick', :include_blank => false, :collection => Saas::Config.gateway == "bogus" ? [['Bogus', 'bogus']] : [['Visa', 'visa'], ['MasterCard', 'master'], ['American Express', 'american_express'], ['Discover', 'discover']] %>
<%= c.input :number, :input_html => { :placeholder => "Card Number"}, :label => "Card Number", :as => :numeric %>
<li class="select required" id="account_creditcard_expiration_input">
<label for="account_creditcard_expiration">Card Expires On<abbr title="required">*</abbr></label>
<%= c.select :year, (Time.now.year .. 10.years.from_now.year), :selected => @creditcard.year.nil? ? Time.now.year : @creditcard.year, :class => 'dropkick dd-small' %>
<%= c.select :month, [['1 - January', 1], ['2 - February', 2], ['3 - March', 3], ['4 - April', 4], ['5 - May', 5], ['6 - June', 6], ['7 - July', 7], ['8 - August', 8], ['9 - September', 9], ['10 - October', 10], ['11 - November', 11], ['12 - December', 12]], :selected => @creditcard.month.nil? ? "1" : @creditcard.month, :class => 'dropkick' %>
</li>
<%= c.input :verification_value, :label => "CVV Code", :input_html => { :placeholder => "CVV Code", :value => @creditcard.verification_value,
:type => "password",
:class => 'short' } %>
<% end %>
<% end %>
Now the fields like card number, expiry date, etc; in the above form has to be in braintree drop-in ui(here itself the credit card number is validated by braintree). How can I modify this form? Please help.
This is my model, account.rb:
def valid_subscription?
return if errors.any?
self.build_subscription(:plan => @plan, :next_renewal_at => @plan_start, :creditcard => @creditcard, :address => @address, :affiliate => @affiliate)
@address.first_name = @creditcard.first_name
@address.last_name = @creditcard.last_name
self.subscription.store_card(@creditcard, :billing_address => @address.to_activemerchant)
if !subscription.valid?
errors.add(:base, "Error with payment: #{subscription.errors.full_messages.to_sentence}")
return false
end
end
accounts_controller:
class AccountsController < ApplicationController
before_filter :build_account, :only => [:new, :create]
before_filter :build_user, :only => [:new, :create]
before_filter :load_billing, :only => [:new, :create, :billing]
def create
@address.first_name = @creditcard.first_name
@address.last_name = @creditcard.last_name
@account.address = @address
@account.creditcard = @creditcard
if @account.new_record?
if @account.save
flash[:notice] = 'Account was created.'
bypass_sign_in(@user)
redirect_to session[:previous_url] || user_reports_path(@user)
else
render :action => 'new'
end
else
@user.account_id = @account.id
if @user.save
flash[:notice] = 'User was created.'
bypass_sign_in(@user)
redirect_to session[:previous_url] || user_reports_path(@user)
else
render :action => 'new'
end
end
end
def billing
@user = current_user
@account = Account.find(params[:id])
if request.put?
@address.first_name = @creditcard.first_name
@address.last_name = @creditcard.last_name
puts @address.first_name
if @creditcard.valid? & @address.valid?
if @subscription.store_card(@creditcard, :billing_address => @address.to_activemerchant, :ip => request.remote_ip)
flash[:notice] = "Your billing information has been updated."
redirect_to settings_path(@user)
end
end
end
end
protected
def resource
@account ||= current_account
end
def build_account
@account = params[:account_name].blank? ? Account.new : Account.find_by_name(params[:account_name])
end
def build_user
@account.user = @user = User.new(params[:account].blank? ? nil : params[:account][:user])
end
def load_billing
@creditcard = ActiveMerchant::Billing::CreditCard.new(params[:account].blank? ? {} : params[:account][:creditcard])
@address = SubscriptionAddress.new(params[:account].blank? ? {} : params[:account][:address])
end
end
This is another model associated with account model, subscription.rb:
class Subscription < ActiveRecord::Base
attr_accessor :creditcard, :address
def store_card(creditcard, gw_options = {})
# Clear out payment info if switching to CC from PayPal
destroy_gateway_record(paypal) if paypal?
@response = if billing_id.blank?
gateway.store(creditcard, gw_options)
else
gateway.update(billing_id, creditcard, gw_options)
end
if @response.success?
if active_card = @response.params['active_card']
# Stripe token-based response
self.card_number = "XXXX-XXXX-XXXX-#{active_card['last4']}"
self.card_expiration = "%02d-%d" % [active_card['exp_month'], active_card['exp_year']]
else
self.card_number = creditcard.display_number
self.card_expiration = "%02d-%d" % [creditcard.expiry_date.month, creditcard.expiry_date.year]
end
set_billing
else
errors.add(:base, @response.message)
false
end
end
def card_storage
self.store_card(@creditcard, :billing_address => @address.to_activemerchant) if @creditcard && @address && card_number.blank?
end
def set_billing
self.billing_id = @response.token
end
end
production.rb:
config.after_initialize do
ActiveMerchant::Billing::Base.mode = :production
::GATEWAY = ActiveMerchant::Billing::AuthorizeNetGateway.new(
:login => "xxxxxxx",
:password => "xxxxxxxxxxxxxx" )
end
Disclaimer: I work at Braintree.
Short answer: you can't. The Drop-in is meant as a secure replacement for a credit card form you host. To use the Drop-in, you should take out the entire credit card section of your form and account model, and—instead of expecting to handle and store credit card data—receive a payment method nonce returned by the Drop-in and use it via the Braintree transaction or payment method API.
Long answer: the Drop-in is a pre-built form hosted by Braintree that we'll insert via an iframe into a form on your page. When the form is submitted, the credit card (or PayPal, etc) information from the Drop-in is sent to Braintree, and a payment method nonce is returned to your page (either via a JavaScript callback or inserted into a hidden field in your form.) The nonce is a randomly-generated string that stands in for the payment info, and can be passed through your application without security risks. Check out the Braintree developer docs for more details and sample code.
The primary reason for all this is security. It's against industry regulations (called the PCI security standards) to store credit card information in a non-secure environment, and it is against PCI regulations to ever store the CVV/security code of a credit card. Even having credit card data passing through your site can put it at risk. The Drop-in (or our Hosted Fields integration) makes it much easier to meet PCI standards and reduces the burden on you of securing your site.
To sum up: you should remove the integration of customer credit cards from ActiveMerchant and not include them in your schema at all. Instead, include a blank div in your form for the Drop-in to be inserted into, and use the payment method nonce the Drop-in returns in your model/controller. How you integrate the nonce, server-side Braintree API calls, and their results into your rails model is up to you. We have a full example rails app that uses the Drop-in: feel free to take a look at the braintree_rails_example repo on the Braintree github page for ideas.