Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update how braintree client tokens are generated (Implementation 2) #79

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions app/helpers/braintree_view_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,28 @@ def braintree_transaction_link(payment_id)
payment_id
end
end

##
# Generate a client token for a user
# @return [String, NilClass]
#
def braintree_client_token_for(user = nil)
options = {}
options[:customer_id] = user.braintree_customer_id if user.present?

braintree_gateway.generate_client_token(options)
rescue => error
Copy link
Contributor Author

@skukx skukx Feb 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious on thoughts about rescuing here. I rescue so that we don't return a 500 resulting in the user not getting a rendered page (Maybe braintree is down).

This is important as a customer may not even be using the Braintree payment method and we don't want to block them completely

Rails.logger.error(error)
nil
end

private

def braintree_gateway
@gateway ||= if params[:payment_method_id]
Solidus::Gateway::BraintreeGateway.find_by!(id: params[:payment_method_id])
else
Solidus::Gateway::BraintreeGateway.find_by!(active: true)
end
end
end
51 changes: 50 additions & 1 deletion app/models/solidus/gateway/braintree_gateway.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ def create_profile(payment)
email = user ? user.email : payment.order.email
address = (payment.source.address || payment.order.bill_address).try(:active_merchant_hash)

return add_payment_method(payment) if user&.braintree_customer_id

params = {
first_name: source.first_name,
last_name: source.last_name,
Expand Down Expand Up @@ -107,7 +109,11 @@ def create_profile(payment)
solidus_cc.gateway_customer_profile_id = result.customer.id
solidus_cc.gateway_payment_profile_id = card.token
end
source.save!

source.transaction do
user&.update(braintree_customer_id: result.customer.id)
source.save!
end
else
raise ::Spree::Core::GatewayError, result.message
end
Expand Down Expand Up @@ -190,6 +196,49 @@ def cancel(response)
end

private

def add_payment_method(payment)
source = payment.source

user = payment.order.user
email = user ? user.email : payment.order.email
address = (payment.source.address || payment.order.bill_address).try(:active_merchant_hash)

result = braintree_gateway.payment_method.create(
payment_method_nonce: payment.payment_method_nonce,
customer_id: user.braintree_customer_id,
skukx marked this conversation as resolved.
Show resolved Hide resolved
cardholder_name: source.name,
billing_address: map_address(address),
options: {
verify_card: true
}
)

raise ::Spree::Core::GatewayError, result.message unless result.success?

card = result.payment_method
source.tap do |solidus_cc|
if card.is_a?(::Braintree::PayPalAccount)
solidus_cc.cc_type = 'paypal'
data = {
email: card.email
}
solidus_cc.data = data.to_json
else
solidus_cc.name = card.cardholder_name
solidus_cc.cc_type = CARD_TYPE_MAPPING[card.card_type]
solidus_cc.month = card.expiration_month
solidus_cc.year = card.expiration_year
solidus_cc.last_digits = card.last_4
end
solidus_cc.payment_method = self
solidus_cc.gateway_customer_profile_id = card.customer_id
solidus_cc.gateway_payment_profile_id = card.token
end

source.save!
end

def message_from_result(result)
if result.success?
"OK"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<label><%= radio_button_tag :card, card.id, card == previous_cards.first %> <%= card.display_number %><br /></label>
<% end %>
<% end %>
<label><%= radio_button_tag :card, 'new', false, { id: "card_new#{payment_method.id}" } %> <%= Spree.t(:use_new_cc) %></label>
<label><%= radio_button_tag :card, 'new', false, { id: "card_new#{payment_method.id}" } %> <%= I18n.t('spree.use_new_cc') %></label>
</div>

<div id="card_form<%= payment_method.id %>" data-hook>
Expand All @@ -24,10 +24,11 @@

<div id="braintree-dropin"></div>
<input type="hidden" id="payment_method_nonce" name="payment[payment_method_nonce]">
<%= hidden_field_tag "braintree_client_token", braintree_client_token_for(@order.user) %>

<div class="clear"></div>

<%= label_tag "card_address#{payment_method.id}", Spree.t(:billing_address) %>
<%= label_tag "card_address#{payment_method.id}", I18n.t('spree.billing_address') %>
<% address = @order.bill_address || @order.ship_address || Spree::Address.build_default %>
<%= fields_for "#{param_prefix}[address_attributes]", address do |f| %>
<%= render :partial => 'spree/admin/shared/address_form', :locals => { :f => f, :type => "billing" } %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
<div class="row">
<div class="alpha six columns">
<dl>
<dt><%= Spree.t(:identifier) %>:</dt>
<dt><%= I18n.t('spree.identifier') %>:</dt>
<dd><%= payment.number %></dd>

<dt><%= Spree.t(:response_code) %>:</dt>
<dt><%= I18n.t('spree.response_code') %>:</dt>
<dd><%= braintree_transaction_link(payment.response_code) %></dd>

<dt><%= Spree.t(:name_on_card) %>:</dt>
<dt><%= I18n.t('spree.name_on_card') %>:</dt>
<dd><%= payment.source.name %></dd>

<dt><%= Spree::CreditCard.human_attribute_name(:cc_type) %>:</dt>
Expand Down
17 changes: 9 additions & 8 deletions app/views/spree/checkout/payment/_braintree.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</div>
<div id="#braintree_paypal_container"></div>
</div>

<div class="braintree-cc-input">
<div class="braintree-cc-header">
<%= t('solidus_braintree.creditcard_header_html') %>
Expand All @@ -16,31 +16,31 @@
<% 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 />
<%= label_tag "name_on_card_#{payment_method.id}", I18n.t('spree.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 "braintree_card_number", Spree.t(:card_number) %><span class="required">*</span><br />
<%= label_tag "braintree_card_number", I18n.t('spree.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 id="looks_like" ><%= I18n.t('spree.card_type_is') %> <span id="type"></span></span>
<span id="unrecognized"><%= I18n.t('spree.unrecognized_card_type') %></span>
)
</span>
</p>

<p class="field" data-hook="card_expiration">
<%= label_tag "braintree_card_expiry", Spree.t(:expiration) %><span class="required">*</span><br />
<%= label_tag "braintree_card_expiry", I18n.t('spree.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 "braintree_card_code", Spree.t(:card_code) %><span class="required">*</span><br />
<%= label_tag "braintree_card_code", I18n.t('spree.card_code') %><span class="required">*</span><br />

<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" %>
<%= link_to "(#{I18n.t('spree.what_is_this')})", spree.cvv_path, :target => '_blank', "data-hook" => "cvv_link", :id => "cvv_link" %>
</p>
</div>

Expand All @@ -52,4 +52,5 @@

<%= 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 "braintree_client_token", braintree_client_token_for(@order.user) %>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddBraintreeCustomerIdToSpreeUser < ActiveRecord::Migration[5.1]
def change
add_column :spree_users, :braintree_customer_id, :string, index: true
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,14 @@ Spree.routes.payment_client_token_api = Spree.pathFor("api/payment_client_token"
var braintreeDropinIntegration;
var paymentForm = "#new_payment";
var cardSelector = "#new_payment [name=card]";
var braintreeClientTokenSelector = '#braintree_client_token';

var getClientToken = function(onSuccess) {
return Spree.ajax({
url: Spree.routes.payment_client_token_api,
type: "POST",
data: {
payment_method_id: $('form input[type=radio]:checked').val()
},
error: function(xhr, status) {
show_flash("error", xhr.responseJSON.message);
},
success: function(data) {
onSuccess(data);
}
});
var data = {
client_token: $(braintreeClientTokenSelector).val()
};

onSuccess(data);
};

var attachDropIn = function(data) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,15 @@ Spree.routes.payment_client_token_api = Spree.pathFor("api/payment_client_token"
var braintreeDropinIntegration;
var cardSelector = "#payment-method-fields";
var confirmForm = "#checkout_form_confirm";
var braintreeClientTokenSelector = '#braintree_client_token';
var paymentId;

var getClientToken = function(onSuccess) {
return Spree.ajax({
url: Spree.routes.payment_client_token_api,
type: "POST",
data: {
payment_method_id: paymentId
},
error: function(xhr, status) {
// If it fails it means the payment method was not a Braintree payment method
if (braintreeDropinIntegration) {
braintreeDropinIntegration.teardown();
braintreeDropinIntegration = null;
}
},
success: function(data) {
onSuccess(data);
}
});
var data = {
client_token: $(braintreeClientTokenSelector).val()
};

onSuccess(data);
};

var initializeBraintree = function(data) {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading