Skip to content

Commit

Permalink
Merge pull request #29 from jhawthorn/hosted_fields
Browse files Browse the repository at this point in the history
Use braintree's Hosted Fields
  • Loading branch information
jhawthorn committed Apr 26, 2016
2 parents e48b3af + b903e5b commit ab3ea1c
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 41 deletions.
2 changes: 0 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@ addons:
postgresql: "9.3"
env:
matrix:
- SOLIDUS_BRANCH=v1.0 DB=postgres
- SOLIDUS_BRANCH=v1.1 DB=postgres
- SOLIDUS_BRANCH=v1.2 DB=postgres
- SOLIDUS_BRANCH=v1.3 DB=postgres
- SOLIDUS_BRANCH=master DB=postgres
- SOLIDUS_BRANCH=v1.0 DB=mysql
- SOLIDUS_BRANCH=v1.1 DB=mysql
- SOLIDUS_BRANCH=v1.2 DB=mysql
- SOLIDUS_BRANCH=v1.3 DB=mysql
Expand Down
3 changes: 2 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
source 'https://rubygems.org'

# Specify your gem's dependencies in solidus_braintree.gemspec
gem "solidus", github: "solidusio/solidus", branch: "master"
branch = ENV.fetch('SOLIDUS_BRANCH', 'master')
gem "solidus", github: "solidusio/solidus", branch: branch

gem 'pg'
gem 'mysql2'
Expand Down
68 changes: 36 additions & 32 deletions app/views/spree/checkout/payment/_braintree.html.erb
Original file line number Diff line number Diff line change
@@ -1,39 +1,43 @@
<%= image_tag 'credit_cards/credit_card.gif', :id => 'credit-card-image' %>
<% param_prefix = "payment_source[#{payment_method.id}]" %>
<div class="braintree-payment">
<div class="braintree-cc-input">
<%= image_tag 'credit_cards/credit_card.gif', :id => 'credit-card-image' %>
<% param_prefix = "payment_source[#{payment_method.id}]" %>

<p class="field">
<%= label_tag "name_on_card_#{payment_method.id}", Spree.t(:name_on_card) %><span class="required">*</span><br />
<%= text_field_tag "#{param_prefix}[name]", "#{@order.billing_firstname} #{@order.billing_lastname}", { id: "name_on_card_#{payment_method.id}", :autocomplete => "cc-name" } %>
</p>
<p class="field">
<%= label_tag "name_on_card_#{payment_method.id}", Spree.t(:name_on_card) %><span class="required">*</span><br />
<%= text_field_tag "#{param_prefix}[name]", "#{@order.billing_firstname} #{@order.billing_lastname}", { id: "name_on_card_#{payment_method.id}", :autocomplete => "cc-name" } %>
</p>

<p class="field" data-hook="card_number">
<%= label_tag "card_number", Spree.t(:card_number) %><span class="required">*</span><br />
<%= text_field_tag "", '', {:id => 'card_number', :class => 'required cardNumber', :size => 19, :maxlength => 19, :autocomplete => "cc-number", type: "tel", :data => { "braintree-name" => "number" }} %>
&nbsp;
<span id="card_type" style="display:none;">
( <span id="looks_like" ><%= Spree.t(:card_type_is) %> <span id="type"></span></span>
<span id="unrecognized"><%= Spree.t(:unrecognized_card_type) %></span>
)
</span>
</p>
<p class="field" data-hook="card_number">
<%= label_tag "braintree_card_number", Spree.t(:card_number) %><span class="required">*</span><br />
<label for="braintree_card_number" id="braintree_card_number" class="braintree-hosted-field"></label>
&nbsp;
<span id="card_type" style="display:none;">
( <span id="looks_like" ><%= Spree.t(:card_type_is) %> <span id="type"></span></span>
<span id="unrecognized"><%= Spree.t(:unrecognized_card_type) %></span>
)
</span>
</p>

<p class="field" data-hook="card_expiration">
<%= label_tag "card_expiry", Spree.t(:expiration) %><span class="required">*</span><br />
<%= text_field_tag "#{param_prefix}[expiry]", '', {:id => 'card_expiry', :class => "required cardExpiry", :placeholder => "MM / YY", type: "tel"} %>
<%= hidden_field_tag "", '', :id => 'braintree_expiration_date', :data => { "braintree-name" => "expiration_date" } %>
</p>
<p class="field" data-hook="card_expiration">
<%= label_tag "braintree_card_expiry", Spree.t(:expiration) %><span class="required">*</span><br />
<label for="braintree_card_expiry" id="braintree_card_expiry" class="braintree-hosted-field"></label>
</p>

<p class="field" data-hook="card_code">
<%= label_tag "card_code", Spree.t(:card_code) %><span class="required">*</span><br />
<%= text_field_tag "", '', {:id => 'card_code', :class => 'required cardCode', :size => 5, type: "tel", autocomplete: "off", :data => { "braintree-name" => "cvv" }} %>
<%= link_to "(#{Spree.t(:what_is_this)})", spree.cvv_path, :target => '_blank', "data-hook" => "cvv_link", :id => "cvv_link" %>
</p>
<p class="field" data-hook="card_code">
<%= label_tag "braintree_card_code", Spree.t(:card_code) %><span class="required">*</span><br />

<% if @order.bill_address %>
<%= fields_for "#{param_prefix}[address_attributes]", @order.bill_address do |f| %>
<%= render :partial => 'spree/address/form_hidden', :locals => { :form => f } %>
<label for="braintree_card_code" id="braintree_card_code" class="braintree-hosted-field card-code"></label>
<%= link_to "(#{Spree.t(:what_is_this)})", spree.cvv_path, :target => '_blank', "data-hook" => "cvv_link", :id => "cvv_link" %>
</p>
</div>

<% if @order.bill_address %>
<%= fields_for "#{param_prefix}[address_attributes]", @order.bill_address do |f| %>
<%= render :partial => 'spree/address/form_hidden', :locals => { :form => f } %>
<% end %>
<% end %>
<% end %>

<%= hidden_field_tag "#{param_prefix}[cc_type]", '', :id => "cc_type", :class => 'ccType' %>
<%= hidden_field_tag "order[payments_attributes][][payment_method_nonce]", '', :id => "payment_method_nonce" %>
<%= hidden_field_tag "#{param_prefix}[cc_type]", '', :id => "cc_type", :class => 'ccType' %>
<%= hidden_field_tag "order[payments_attributes][][payment_method_nonce]", '', :id => "payment_method_nonce" %>
</div>
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
//= require "vendor/braintree"

SolidusBraintree = {
getFrontendStyles: function(){
/* Emulation of inherited attributes through an iframe */
var $source = $('.braintree-hosted-field');
return {
input: {
"font-family": $source.css("font-family"),
"font-size": $source.css("font-size"),
"color": $source.css("color"),
}
};
}
}

Spree.routes.payment_client_token_api = Spree.pathFor("api/payment_client_token")

var braintreeDropinIntegration;
Expand Down Expand Up @@ -33,6 +47,18 @@ var initializeBraintree = function(data) {
})
braintree.setup(data.client_token, "custom", {
id: "checkout_form_payment",
hostedFields: {
styles: SolidusBraintree.getFrontendStyles(),
number: {
selector: "#braintree_card_number"
},
cvv: {
selector: "#braintree_card_code"
},
expirationDate: {
selector: "#braintree_card_expiry"
}
},
onReady: function (integration) {
braintreeDropinIntegration = integration;
},
Expand Down
26 changes: 26 additions & 0 deletions lib/assets/stylesheets/spree/frontend/solidus_braintree.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.braintree-hosted-field {
display: inline-block;
border: 1px solid #d9d9db;
font-family: "Ubuntu", sans-serif;
font-size: 13px;
height: 2em;
width: 16em;
cursor: text;
padding: 0 5px;

&.braintree-hosted-fields-focused {
border-color: #00ADEE;
}

&.braintree-hosted-fields-valid {
border-color: #63CE63;
}

&.braintree-hosted-fields-invalid {
border-color: red;
}

&.card-code {
width: 6em;
}
}
4 changes: 4 additions & 0 deletions lib/generators/solidus_braintree/install/install_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ def add_javascripts
append_file "vendor/assets/javascripts/spree/backend/all.js", "//= require spree/backend/braintree/solidus_braintree\n"
end

def add_stylesheets
inject_into_file "vendor/assets/stylesheets/spree/frontend/all.css", " *= require spree/frontend/solidus_braintree\n", before: '*/', verbose: true
end

def add_migrations
run 'bundle exec rake railties:install:migrations FROM=solidus_braintree'
end
Expand Down
18 changes: 14 additions & 4 deletions spec/features/checkout_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@
# Payment
expect(page).to have_content(gateway.name)

fill_in 'Card Number', with: '4111111111111111'
fill_in 'Expiration', with: "12/20"
fill_in 'Card Code', with: '123'
braintree_fill_in 'Card Number', with: '4111111111111111'
braintree_fill_in 'Expiration', with: "12/20"
braintree_fill_in 'Card Code', with: '123'

click_on 'Save and Continue'
click_on 'Place Order'

# Previous step can take a long time, so we allow an extra delay
click_on 'Place Order', wait: 30
expect(page).to have_content('Your order has been processed successfully')

# Assert the payment details were stored correctly
Expand Down Expand Up @@ -64,4 +66,12 @@ def fill_in_address
fill_in "Phone", with: "(555) 555-5555"
end
end

def braintree_fill_in(label_text, with:)
label = find(:label, label_text)
frame = find("##{label[:for]} iframe")
within_frame(frame) do
find('input').set(with)
end
end
end
7 changes: 5 additions & 2 deletions spec/solidus/gateway/braintree_gateway_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@
),
payment_method: payment_method,
payment_method_nonce: nonce,
amount: amount
)
end
let(:amount) { 5.00 }

let(:card) { payment.source }

context 'a customer profile' do
Expand Down Expand Up @@ -244,10 +247,10 @@
end
end

context 'failure', transaction_clean: false do
context 'failure' do
let(:amount) { 4001.00 }
it "fails" do
expect{
payment.amount = 4001.00
payment.capture!
}.to raise_error(Spree::Core::GatewayError)
expect(payment).to be_failed
Expand Down

0 comments on commit ab3ea1c

Please sign in to comment.