Skip to content

Instantly share code, notes, and snippets.

@Tyrdall
Last active April 8, 2022 14:09
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save Tyrdall/64b52940510da403c61840148eefaa76 to your computer and use it in GitHub Desktop.
Save Tyrdall/64b52940510da403c61840148eefaa76 to your computer and use it in GitHub Desktop.
"""
- Add some settings -
Log in to your sandbox account and get your API keys plus your merchant ID.
"""
BRAINTREE_PRODUCTION = False # We'll need this later to switch between the sandbox and live account
BRAINTREE_MERCHANT_ID = “your_merchant_id”
BRAINTREE_PUBLIC_KEY = “your_public_key”
BRAINTREE_PRIVATE_KEY = “your_private_key”
{% extends "base.html" %}
{% block main %}
<form method="post" action="." autocomplete="off">
{% csrf_token %}
<p>Payments are safely processed with <a href='https://www.braintreepayments.com/' target='_blank'>Braintree</a>.</p>
{% if braintree_error %}
<div class="alert alert-danger fade in">
<button class="close" data-dismiss="alert">&times;</button>
{{ braintree_error|safe }}
</div>
{% endif %}
<div class="braintree-notifications"></div>
<div id="braintree-dropin"></div>
<input class="btn btn-success btn-lg btn-block" type="submit" value="Pay now!" />
</form>
{% endblock %}
<script>
// Remember? You generated the client token in your view.
var braintree_client_token = "{{ braintree_client_token }}";
requirejs(['jquery', 'jsi18n', 'https://js.braintreegateway.com/js/braintree-2.28.0.min.js'], function($, jsi18n, braintree) {
function braintreeSetup() {
// Here you tell Braintree to add the drop-in to your division above
braintree.setup(braintree_client_token, "dropin", {
container: "braintree-dropin"
,onError: function (obj) {
// Errors will be added to the html code
$('[type=submit]').prop('disabled', false);
$('.braintree-notifications').html('<p class="alert alert-danger">' + obj.message + '</p>');
}
});
}
braintreeSetup();
$('form').submit(function () {
$('[type=submit]').prop('disabled', true);
$('.braintree-notifications').html('');
});
});
</script>
"""
Add a simple form, which includes the hidden payment nonce field.
You might want to add other fields like an address, quantity or
an amount.
"""
from django import forms
from django.utils.translation import ugettext_lazy as _
class CheckoutForm(forms.Form):
payment_method_nonce = forms.CharField(
max_length=1000,
widget=forms.widgets.HiddenInput,
require=False, # In the end it's a required field, but I wanted to provide a custom exception message
)
def clean(self):
self.cleaned_data = super(CheckoutForm, self).clean()
# Braintree nonce is missing
if not self.cleaned_data.get('payment_method_nonce'):
raise forms.ValidationError(_(
'We couldn\'t verify your payment. Please try again.'))
return self.cleaned_data
"""
Adds simple form view, which communicates with Braintree.
There are four steps to finally process a transaction:
1. Create a client token (views.py)
2. Send it to Braintree (js)
3. Receive a payment nonce from Braintree (js)
4. Send transaction details and payment nonce to Braintree (views.py)
"""
import braintree
from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse
from django.utils.decorators import method_decorator
from django.views import generic
from . import forms
class CheckoutView(generic.FormView):
"""This view lets the user initiate a payment."""
form_class = forms.CheckoutForm
template_name = 'checkout.html'
@method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
# We need the user to assign the transaction
self.user = request.user
# Ha! There it is. This allows you to switch the
# Braintree environments by changing one setting
if settings.BRAINTREE_PRODUCTION:
braintree_env = braintree.Environment.Production
else:
braintree_env = braintree.Environment.Sandbox
# Configure Braintree
braintree.Configuration.configure(
braintree_env,
merchant_id=settings.BRAINTREE_MERCHANT_ID,
public_key=settings.BRAINTREE_PUBLIC_KEY,
private_key=settings.BRAINTREE_PRIVATE_KEY,
)
# Generate a client token. We'll send this to the form to
# finally generate the payment nonce
# You're able to add something like ``{"customer_id": 'foo'}``,
# if you've already saved the ID
self.braintree_client_token = braintree.ClientToken.generate({})
return super(CheckoutView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
ctx = super(CheckoutView, self).get_context_data(**kwargs)
ctx.update({
'braintree_client_token': self.braintree_client_token,
})
return ctx
def form_valid(self, form):
# Braintree customer info
# You can, for sure, use several approaches to gather customer infos
# For now, we'll simply use the given data of the user instance
customer_kwargs = {
"first_name": self.user.first_name,
"last_name": self.user.last_name,
"email": self.user.email,
}
# Create a new Braintree customer
# In this example we always create new Braintree users
# You can store and re-use Braintree's customer IDs, if you want to
result = braintree.Customer.create(customer_kwargs)
if not result.is_success:
# Ouch, something went wrong here
# I recommend to send an error report to all admins
# , including ``result.message`` and ``self.user.email``
context = self.get_context_data()
# We re-generate the form and display the relevant braintree error
context.update({
'form': self.get_form(self.get_form_class()),
'braintree_error': u'{} {}'.format(
result.message, _('Please get in contact.'))
})
return self.render_to_response(context)
# If the customer creation was successful you might want to also
# add the customer id to your user profile
customer_id = result.customer.id
"""
Create a new transaction and submit it.
I don't gather the whole address in this example, but I can
highly recommend to do that. It will help you to avoid any
fraud issues, since some providers require matching addresses
"""
address_dict = {
"first_name": self.user.first_name,
"last_name": self.user.last_name,
"street_address": 'street',
"extended_address": 'street_2',
"locality": 'city',
"region": 'state_or_region',
"postal_code": 'postal_code',
"country_code_alpha2": 'alpha2_country_code',
"country_code_alpha3": 'alpha3_country_code',
"country_name": 'country',
"country_code_numeric": 'numeric_country_code',
}
# You can use the form to calculate a total or add a static total amount
# I'll use a static amount in this example
result = braintree.Transaction.sale({
"customer_id": customer_id,
"amount": 100,
"payment_method_nonce": form.cleaned_data['payment_method_nonce'],
"descriptor": {
# Definitely check out https://developers.braintreepayments.com/reference/general/validation-errors/all/python#descriptor
"name": "COMPANY.*test",
},
"billing": address_dict,
"shipping": address_dict,
"options": {
# Use this option to store the customer data, if successful
'store_in_vault_on_success': True,
# Use this option to directly settle the transaction
# If you want to settle the transaction later, use ``False`` and later on
# ``braintree.Transaction.submit_for_settlement("the_transaction_id")``
'submit_for_settlement': True,
},
})
if not result.is_success:
# Card could've been declined or whatever
# I recommend to send an error report to all admins
# , including ``result.message`` and ``self.user.email``
context = self.get_context_data()
context.update({
'form': self.get_form(self.get_form_class()),
'braintree_error': _(
'Your payment could not be processed. Please check your'
' input or use another payment method and try again.')
})
return self.render_to_response(context)
# Finally there's the transaction ID
# You definitely want to send it to your database
transaction_id = result.transaction.id
# Now you can send out confirmation emails or update your metrics
# or do whatever makes you and your customers happy :)
return super(CheckoutView, self).form_valid(form)
def get_success_url(self):
# Add your preferred success url
return reverse('foo')
@hgamit
Copy link

hgamit commented Sep 16, 2018

After setting up the code. Drop-in does not show host fields using checkout template. am I missing something?

@weirdaze
Copy link

weirdaze commented Jan 8, 2019

same here, I just see the text saying "payments processed with Braintree" and then the Pay Now button... Having trouble finding the fields.

@jeromecc
Copy link

Hi!
in line 16 of forms.py there is a typo:
require=False,
should be
required=False,
Thanks for the nice tutorial.

@muhumar
Copy link

muhumar commented Mar 3, 2019

Only the link to braintree is appearing no credit card or paypal showing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment