Search code examples
javascriptpythondjangobraintree

Braintree JSv3 payment_method_nonce Value Bad With HostedFields


I have looked at a few posts on here with the same issue but under different circumstances that don't supply me with an answer to my particular issue...

I was using Braintree JSv2 with my Django project and all was working fine. Since I have migrated over to v3 of Braintree, the only issue I seem to have right now is that the value inputted to "payment_method_nonce" is not there...

Here is the code that is supposed to be dumping the payment_method_nonce value:

document.querySelector('input[name="payment_method_nonce"]').value = payload.nonce;

And here is the code that is supposed to be grabbing it on the python side:

client_payment_nonce = request.POST['payment_method_nonce']

When submitting this in my dev environment, I get an error (MultiValueDictKeyError) for "payment_method_nonce".

I am using Django 1.9 and Python 2.7. I am also using the example given by Braintree for a simple integration using HostedFields...

Small test

So I manually added an input field in my form with name "payment_method_nonce" just to see if not having a field was causing some issue. I know it is injected by Braintree but just testing a thought. It seems that although the value of payment_method_nonce is supposed to be my nonce, I didn't type anything into the input box and it was still coming back as null.

Full Snippets of Form and HostedFields

<form action="/booking/" method="post" id="checkout_form">
                    {% csrf_token %}
                <div class="payment">
                    <span>Payment</span>
                        <!--input elements for user card data-->
                        <div class="hosted-fields" id="card-number"></div>

                        <div class="hosted-fields" id="postal-code"></div>

                        <div class="hosted-fields" id="expiration-date"></div>

                        <div class="hosted-fields" id="cvv"></div>

                        <div class="btns">
                            <input type="hidden" name="payment_method_nonce">
                            <input type="submit" value="Complete Booking" id="pay-button">
                        </div>
                </div>
            </form>

Note: I had just changed the payment_method_nonce field to type="hidden" instead of type="text" but still have the same effect...

<!-- load the required client component -->
    <script src="https://js.braintreegateway.com/web/3.15.0/js/client.min.js"></script>
    <!-- load the hosted fields component -->
    <script src="https://js.braintreegateway.com/web/3.15.0/js/hosted-fields.min.js"></script>
    <!-- Braintree setup -->
    <script>
        var client_token = "{{ request.session.braintree_client_token }}"
        var form = document.querySelector('#checkout-form');
        var submit = document.querySelector('input[type="submit"]');

        braintree.client.create({
            authorization: client_token
        }, function (clientErr, clientInstance) {
            if (clientErr) {
                // Handle error in client creation
                return;
            }
            braintree.hostedFields.create({
                client: clientInstance,
                styles: {
                    'input': {
                        'font-size': '14px'
                    },
                    'input.invalid': {
                        'color': 'red'
                    },
                    'input.valid': {
                        'color': 'green'
                    }
                },
                fields: {
                    number: {
                        selector: '#card-number',
                        placeholder: 'Credit Card Number'
                    },
                    cvv: {
                        selector: '#cvv',
                        placeholder: '123'
                    },
                    expirationDate: {
                        selector: '#expiration-date',
                        placeholder: '10/2019'
                    },
                    postalCode: {
                        selector: '#postal-code',
                        placeholder: '10014'
                    }
                }
            }, function (hostedFieldsErr, hostedFieldsInstance) {
                if (hostedFieldsErr) {
                    // handle error in Hosted Fields creation
                    return;
                }

                submit.removeAttribute('disabled');

                form.addEventListener('submit', function (event) {
                    event.preventDefault();

                    hostedFieldsInstance.tokenize(function (tokenizeErr, payload) {
                        if (tokenizeErr) {
                            // handle error in Hosted Fields tokenization
                            return;
                        }
                        // Put `payload.nonce` into the `payment_method_nonce`
                        document.querySelector('input[name="payment_method_nonce"]').value = payload.nonce;
                        document.querySelector('input[id="pay-button"]').value = "Please wait...";
                        form.submit();
                    });
                }, false);
            });
        });
    </script>

Note: the line document.querySelector('input[id="pay-button"]').value = "Please wait..."; doesn't fire (I know this because the button does not change values). Maybe these querySelector lines just aren't working?

Something New Noticed

I just went back to my page and hit the submit button without even entering any information. In v2 of Braintree, I would not be able to click the submit button until all fields were filled in... Maybe the values in my form aren't even being sent to braintree to receive a nonce and that's why there is an empty string being returned..?


Solution

  • Moral of the story

    Review your code... Multiple times. As pointed out by C Joseph, I have my form ID as something different than what my var form is referencing...

    <form action="/booking/" method="post" id="checkout_form">

    var form = document.querySelector('#checkout-form');