I'm using Braintree Drop-in with my Django Project. It's working completely fine when I use it in development environment (manage.py runserver). But when I'm accessing the same on elastic beanstalk im getting the error "can only concatenate str (not "tuple") to str". The following is my code.
extras.py
from django.conf import settings
import braintree
gateway = braintree.BraintreeGateway(
braintree.Configuration(
braintree.Environment.Sandbox,
merchant_id=settings.BT_MERCHANT_ID,
public_key=settings.BT_PUBLIC_KEY,
private_key=settings.BT_PRIVATE_KEY,
)
)
def generate_client_token():
return gateway.client_token.generate()
def transact(options):
return gateway.transaction.sale(options)
def find_transaction(id):
return gateway.transaction.find(id)
signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
#from django.conf import settings
from invoices.extras import gateway
@receiver(post_save, sender=User)
def create_phone(sender, instance, created, **kwargs):
if created:
gateway.customer.create({
"first_name": instance.first_name,
"last_name": instance.last_name,
"email": instance.email,
"phone": instance.phone_number,
"id": instance.id,
})
views.py
@login_required()
def customer_invoice_view(request, ticket_id):
ticket = get_object_or_404(Ticket, pk=ticket_id)
customer = ticket.customer
client_token = gateway.client_token.generate({"customer_id": str(customer.id)})
invoice = ticket.invoice
if request.method == 'POST':
result = transact({
'amount': invoice.amount_payable,
'payment_method_nonce': request.POST['payment_method_nonce'],
'options': {
"submit_for_settlement": True
}
})
if result.is_success or result.transaction:
invoice.is_paid = True
invoice.save()
ticket = invoice.ticket
ticket.status ='Payment Made'
ticket.paid = True
ticket.save()
return redirect(customer_invoice_view, ticket_id=ticket.id)
else:
for x in result.errors.deep_errors:
messages.info(request, x)
return redirect(customer_invoice_view, ticket_id=ticket.id)
context = {
'ticket' : ticket,
'invoice' : invoice,
'client_token' : client_token,
}
return render(request, 'invoice/ticket_invoice_view.html', context)
ticket_invoice_view.html
<div class="p-3">
{% if not invoice.is_paid%}
<div class="row no-gutters">
<div class="mb-2">
<ul>
{% for message in messages %}
<li class="text-dark">{{message}}</li>
{% endfor %}
</ul>
</div>
</div>
<div class="row no-gutters my-4">
<form id="payment-form" method="post" action="" autocomplete="off">
{% csrf_token %}
<section>
<div class="bt-drop-in-wrapper">
<div id="bt-dropin"></div>
</div>
</section>
<input type="hidden" id="nonce" name="payment_method_nonce" />
<button class="btn-success" type="submit" id="submit-button"><span>Make Payment</span></button>
</form>
</div>
{% endif %}
</div>
<script src="https://js.braintreegateway.com/web/dropin/1.25.0/js/dropin.min.js"></script>
<script>
var form = document.querySelector('#payment-form');
var client_token = '{{ client_token }}';
braintree.dropin.create({
authorization: client_token,
container: '#bt-dropin',
paypal: {
flow: 'vault'
}
}, function (createErr, instance) {
form.addEventListener('submit', function (event) {
event.preventDefault();
instance.requestPaymentMethod(function (err, payload) {
if (err) {
console.log('Error', err);
return;
}
// Add the nonce to the form and submit
document.querySelector('#nonce').value = payload.nonce;
form.submit();
});
});
});
</script>
When I'm running the project locally (manage.py runserver) it's working fine, when user is created, a customer is created in Braintree with user-Id as Customer-ID. And the payments are also getting processed without any issues. But when the same is done through elastic beanstalk I'm getting the error "can only concatenate str (not "tuple") to str".
The following is the traceback.
TypeError: can only concatenate str (not "tuple") to str
File "django/core/handlers/exception.py", line 47, in inner
response = get_response(request)
File "django/core/handlers/base.py", line 179, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "django/contrib/auth/decorators.py", line 21, in _wrapped_view
return view_func(request, *args, **kwargs)
File "accounts/decorators.py", line 20, in wrapper_func
return view_func(request, *args, **kwargs)
File "invoices/views.py", line 33, in customer_invoice_view
client_token = gateway.client_token.generate({"customer_id": str(customer.id),},)
File "braintree/client_token_gateway.py", line 26, in generate
response = self.config.http().post(self.config.base_merchant_path() + "/client_token", params)
File "braintree/configuration.py", line 113, in base_merchant_path
return "/merchants/" + self.merchant_id
This is the traceback of the Post Save Signal Error.
TypeError: can only concatenate str (not "tuple") to str
File "django/core/handlers/exception.py", line 47, in inner
response = get_response(request)
File "django/core/handlers/base.py", line 179, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "django/views/generic/base.py", line 70, in view
return self.dispatch(request, *args, **kwargs)
File "django/utils/decorators.py", line 43, in _wrapper
return bound_method(*args, **kwargs)
File "django/views/decorators/debug.py", line 89, in sensitive_post_parameters_wrapper
return view(request, *args, **kwargs)
File "allauth/account/views.py", line 230, in dispatch
return super(SignupView, self).dispatch(request, *args, **kwargs)
File "allauth/account/views.py", line 75, in dispatch
request, *args, **kwargs
File "allauth/account/views.py", line 204, in dispatch
return super(CloseableSignupMixin, self).dispatch(request, *args, **kwargs)
File "django/views/generic/base.py", line 98, in dispatch
return handler(request, *args, **kwargs)
File "allauth/account/views.py", line 102, in post
response = self.form_valid(form)
File "allauth/account/views.py", line 246, in form_valid
self.user = form.save(self.request)
File "allauth/account/forms.py", line 419, in save
adapter.save_user(request, user, self)
File "allauth/account/adapter.py", line 246, in save_user
user.save()
File "django/contrib/auth/base_user.py", line 67, in save
super().save(*args, **kwargs)
File "django/db/models/base.py", line 754, in save
force_update=force_update, update_fields=update_fields)
File "django/db/models/base.py", line 803, in save_base
update_fields=update_fields, raw=raw, using=using,
File "django/dispatch/dispatcher.py", line 179, in send
for receiver in self._live_receivers(sender)
File "django/dispatch/dispatcher.py", line 179, in <listcomp>
for receiver in self._live_receivers(sender)
File "accounts/signals.py", line 30, in create_phone
"id": instance.id,
File "braintree/customer_gateway.py", line 24, in create
return self._post("/customers", {"customer": params})
File "braintree/customer_gateway.py", line 79, in _post
response = self.config.http().post(self.config.base_merchant_path() + url, params)
File "braintree/configuration.py", line 113, in base_merchant_path
return "/merchants/" + self.merchant_id
I will be thankful if someone can help me to resolve it.
Thank you for the well detailed question!
The error is the clue. It says you're trying to concatenate a tuple and a string which isn't allowed. Since the line is return "/merchants/" + self.merchant_id
and I don't see a comma indicating an extranous tuple, it means the problem is with self.merchant_id
. That appears to be set from settings.BT_MERCHANT_ID
. My hunch is that settings.BT_MERCHANT_ID
has a comma at the end of it when it shouldn't causing it to be a tuple.
For example:
x = 1
results in x
being set to the integer 1
. But
x = 1,
Results in x
being set to the tuple (1, )
. It's a painful gotcha that bites every python developer eventually.