Last active
April 8, 2022 14:09
-
-
Save Tyrdall/64b52940510da403c61840148eefaa76 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
- 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” |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% 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">×</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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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') |
same here, I just see the text saying "payments processed with Braintree" and then the Pay Now button... Having trouble finding the fields.
Hi!
in line 16 of forms.py there is a typo:
require=False,
should be
required=False,
Thanks for the nice tutorial.
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
After setting up the code. Drop-in does not show host fields using checkout template. am I missing something?