Reputation: 441
I'm trying to build a Braintree payment form following the 'Django by Example 3' book but the form is not able to be filled:
As you can see, the form is displayed in the browser but there's no chance by editing this 3 fields. Actually is being shown like 'images'.
Below my template:
{% extends "shop/base.html" %}
{% block title %}Pay by credit card{% endblock %}
{% block content %}
<h1>Pay by credit card</h1>
<form id="payment" method="post">
<label for="card-number">Card Number</label>
<div id="card-number" class="field"></div>
<label for="cvv">CVV</label>
<div id="cvv" class="field"></div>
<label for="expiration-date">Expiration Date</label>
<div id="expiration-date" class="field"></div>
<input type="hidden" id="nonce" name="payment_method_nonce"
value="">
{% csrf_token %}
<input type="submit" value="Pay">
</form>
<!-- includes the Braintree JS client SDK -->
<script src="https://js.braintreegateway.com/web/3.44.2/js/client.
min.js"></script>
<script src="https://js.braintreegateway.com/web/3.44.2/js/hostedfields.
min.js"></script>
<script>
var form = document.querySelector('#payment');
var submit = document.querySelector('input[type="submit"]');
braintree.client.create({
authorization: '{{ client_token }}'
}, function (clientErr, clientInstance) {
if (clientErr) {
console.error(clientErr);
return;
}
braintree.hostedFields.create({
client: clientInstance,
styles: {
'input': {'font-size': '13px'},
'input.invalid': {'color': 'red'},
'input.valid': {'color': 'green'}
},
fields: {
number: {selector: '#card-number'},
cvv: {selector: '#cvv'},
expirationDate: {selector: '#expiration-date'}
}
}, function (hostedFieldsErr, hostedFieldsInstance) {
if (hostedFieldsErr) {
console.error(hostedFieldsErr);
return;
}
submit.removeAttribute('disabled');
form.addEventListener('submit', function (event) {
event.preventDefault();
hostedFieldsInstance.tokenize(function (tokenizeErr,
payload) {
if (tokenizeErr) {
console.error(tokenizeErr);
return;
}
// set nonce to send to the server
document.getElementById('nonce').value = payload.nonce;
// submit form
document.getElementById('payment').submit();
});
}, false);
});
});
</script>
{% endblock %}
Here is what the book says:
"This is the template that displays the payment form and processes the payment. You define containers instead of elements for the credit card input fields: the credit card number, CVV number, and expiration date. This is how you specify the fields that the Braintree JavaScript client will render in the iframe. You also include an element named payment_method_nonce that you will use to send the token nonce to your view once it is generated by the Braintree JavaScript client."
Any help?
Upvotes: 2
Views: 1511
Reputation: 1
I had the same problem as koniecpolski. The book Django 3 By Example is right. When we copy and paste from the book (PDF) the symbol "-" is lost so it goes from: hosted-fields to hostedfields. Also, if we check the code that comes with the book, the code is right. So far in my experience all of the errors and mistakes come from copy and paste. When something is not working my best option is to check the code that comes with the book to see the right formatting or missing code.
Upvotes: 0
Reputation: 1
Check whether there are wrong codes in your views.py! I have same problem, but I check my coding with copying original coding. I found the "payment_process def " returned wrong value.The wrong code was "clinet". The correct is "client".Don't give up braintree and Djagno 3 by example.
Upvotes: 0
Reputation: 49
I managed to solve this problem (because I'm doing Packt Django 3 By Example course too) by copying from the Braintree site and replacing given in the book SDK script import lines.
<!-- includes the Braintree JS client SDK -->
<script src="https://js.braintreegateway.com/web/3.44.2/js/client.min.js"></script>
<script src="https://js.braintreegateway.com/web/3.44.2/js/hosted-fields.min.js"></script>
I guess something was wrong in "original links" [from the book] for example hostedfields instead of hosted-fields. Now it's working for me
Upvotes: 1
Reputation: 6891
I had the same problem going through the book and I eventually figured it out, here is my work around it. You should use the Drop-in UI which is a ready-made payment UI that offers the quickest way to integrate and start securely accepting payments with Braintree and then use:
https://js.braintreegateway.com/web/dropin/1.18.0/js/dropin.min.js
instead of:
<script src="https://js.braintreegateway.com/web/3.44.2/js/client.min.js"></script>
<script src="https://js.braintreegateway.com/web/3.44.2/js/hostedfields.min.js"></script>
In process.html do the following:
{% extends "shop/base.html" %}
{% block title %} Pay by credit card {% endblock %}
{% block sidenavigation %}
{% endblock %}
{% block content %}
<h1>Pay by credit card</h1>
<!-- includes the Braintree JS client SDK -->
<script src="https://js.braintreegateway.com/web/dropin/1.18.0/js/dropin.min.js"></script>
<form autocomplete="off">
{% if braintree_error %}
<div class="alert alert-danger fade in">
<button class="close" data-dismiss="alert">×</button>
{{ braintree_error|safe }}
</div>
{% endif %}
<div class="braintree-notifications"></div>
<div id="braintree-dropin"></div>
<input style="background-color: #0783ca" id="submit-button" class="btn btn-success btn-lg btn-block" type="button" value="Pay now!" />
</form>
<script>
var braintree_client_token = "{{ client_token}}";
var button = document.querySelector('#submit-button');
braintree.dropin.create({
authorization: "{{client_token}}",
container: '#braintree-dropin',
card: {
cardholderName: {
required: false
}
}
}, function (createErr, instance) {
button.addEventListener('click', function () {
instance.requestPaymentMethod(function (err, payload) {
$.ajax({
type: 'POST',
url: '{% url "payment:process" %}',
data: {
'paymentMethodNonce': payload.nonce,
'csrfmiddlewaretoken': '{{ csrf_token }}'}
}).done(function (result) {
//do accordingly
});
});
});
});
</script>
{% endblock %}
In payment/views.py
def payment_process(request):
"""The view that processes the payment"""
order_id = request.session.get('order_id')
order = get_object_or_404(Order, id=order_id)
total_cost = order.get_total_cost()
if request.method == 'POST':
# retrieve nonce
# retrieve nonce
nonce = request.POST.get('paymentMethodNonce', None)
# # create User
customer_kwargs = {
"first_name": order.first_name,
"last_name": order.last_name,
"email": order.email
}
customer_create = gateway.customer.create(customer_kwargs)
customer_id = customer_create.customer.id
#create and submit transaction
result = gateway.transaction.sale({
'amount': f'{total_cost:.2f}',
'payment_method_nonce': nonce,
'options': {
'submit_for_settlement': True
}
})
print(result)
if result.is_success:
#mark the order as paid
order.paid = True
# store the unique transaction id
order.braintree_id = result.transaction.id
order.save()
return redirect('payment:done')
else:
return redirect('payment:canceled')
else:
# generate token
client_token = gateway.client_token.generate()
return render(
request,
'payment/process.html',
{
'order':order,
'client_token': client_token
}
)
As you have noticed we are using ajax after the result is a success you should do something. Hope that it helps
Upvotes: 1