i am currently using braintree hosted fields, to embed the credit cards into my app. i am looking at how i pass the payment nonce from the view to the controller. the javascript i have seems to be triggering the braintree api and returning a nonce to my alert but i just need to now push this through to the controller to execute the final piece of the code
within the controller create method i have
def create
result = Braintree::PaymentMethod.create(
:customer_id => current_user.customer_cim_id,
:payment_method_nonce => nonce_from_the_client,
:cardholder_name => "#{current_user.first_name} #{current_user.last_name}",
:options => {
:make_default => true,
:fail_on_duplicate_payment_method => true
}
)
new view
- title t('.title')
= form_for(@payment_method, :url => myaccount_payment_methods_path(@payment_method), :html => {:id => 'cardForm'}) do |f|
= render :partial => 'form', :locals => {:f => f}
/ Load Hosted Fields component.
%script{:src => '//js.braintreegateway.com/web/3.0.0-beta.10/js/hosted-fields.min.js'}
form view
.mdl-grid
.panel
%header.panel__header
%h1 Card Payment
.panel__content
.textfield--float-label
%label.hosted-field--label{:for => "card-number"}
%i.material-icons credit_card
Card Number
#card-number.hosted-field
.textfield--float-label
%label.hosted-field--label{:for => "expiration-date"}
%i.material-icons date_range
Expiration Date
#expiration-date.hosted-field
.textfield--float-label
%label.hosted-field--label{:for => "cvv"}
%i.material-icons lock
CVV
#cvv.hosted-field
%footer.panel__footer
= f.submit class: 'pay-button', id: 'button-pay', disabled: true
application.js
var form = document.querySelector('#cardForm');
var submit = document.querySelector('input[type="submit"]');
braintree.client.create({
authorization: 'sandbox_92dswc7q_mbmb637xwpzgxbrd'
}, function (err, clientInstance) {
if (err) {
console.error(err);
return;
}
// Create input fields and add text styles
braintree.hostedFields.create({
client: clientInstance,
styles: {
'input': {
'color': '#282c37',
'font-size': '16px',
'transition': 'color 0.1s',
'line-height': '3'
},
// Style the text of an invalid input
'input.invalid': {
'color': '#E53A40'
},
// placeholder styles need to be individually adjusted
'::-webkit-input-placeholder': {
'color': 'rgba(0,0,0,0.6)'
},
':-moz-placeholder': {
'color': 'rgba(0,0,0,0.6)'
},
'::-moz-placeholder': {
'color': 'rgba(0,0,0,0.6)'
},
':-ms-input-placeholder ': {
'color': 'rgba(0,0,0,0.6)'
}
},
// Add information for individual fields
fields: {
number: {
selector: '#card-number',
placeholder: '1111 1111 1111 1111'
},
cvv: {
selector: '#cvv',
placeholder: '123'
},
expirationDate: {
selector: '#expiration-date',
placeholder: '10 / 2019'
}
}
}, function (err, hostedFieldsInstance) {
if (err) {
console.error(err);
return;
}
hostedFieldsInstance.on('validityChange', function (event) {
// Check if all fields are valid, then show submit button
var formValid = Object.keys(event.fields).every(function (key) {
return event.fields[key].isValid;
});
if (formValid) {
$('.pay-button').prop("disabled", false);
} else {
$('.pay-button').prop("disabled", true);
}
});
hostedFieldsInstance.on('empty', function (event) {
$('header').removeClass('header-slide');
$('#card-image').removeClass();
$(form).removeClass();
});
hostedFieldsInstance.on('cardTypeChange', function (event) {
// Change card bg depending on card type
if (event.cards.length === 1) {
$(form).removeClass().addClass(event.cards[0].type);
$('#card-image').removeClass().addClass(event.cards[0].type);
$('header').addClass('header-slide');
// Change the CVV length for AmericanExpress cards
if (event.cards[0].code.size === 4) {
hostedFieldsInstance.setPlaceholder('cvv', '1234');
}
} else {
hostedFieldsInstance.setPlaceholder('cvv', '123');
}
});
submit.addEventListener('click', function (event) {
event.preventDefault();
hostedFieldsInstance.tokenize(function (err, payload) {
if (err) {
console.error(err);
return;
}
// This is where you would submit payload.nonce to your server
alert('Got a nonce: ' + payload.nonce);
// If this was a real integration, this is where you would
// send the nonce to your server.
console.log('Got a nonce: ' + payload.nonce);
});
}, false);
});
});
Full disclosure: I work at Braintree. If you have any further questions, feel free to contact support.
Right after your alert
line in application.js, you will want to send a request to your server that contains the payment method nonce. For example you can do this with Ajax:
$.ajax({
type: "POST",
url: your_payment_url,
data: {"payment_method_nonce":payload.nonce}
});
Then within your Ruby on Rails controller, you can call Transaction.sale
using the payment method nonce in the request to complete the transaction.
For more information on hosted fields, please check out this link.
Edit on Vault question:
If you're using Vault, you can charge users without needing a payment nonce each time. After creating the customer (either through the control panel or through Customer.create
, you can retrieve a payment_method_token directly through the payment_methods
attribute of the Customer object. To charge the user later, retrieve their payment_method_token on your server and call Transaction.sale
, passing in the payment_method_token
.
result = Braintree::Transaction.sale(
:amount => "your_amount",
:payment_method_token => "payment_method_token"
)