Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

WallyJ's avatar

Laravel Cashier - Undefined variable: token

I'm setting up Cashier for Braintree.

I have the following code:

$user = Auth::user();
            //Check if user has subscribed with their credit card to Braintree
            if ($user->subscribedToPlan('main', 'annual')) {
                //Go to User home page
                // $user = Auth::user();
                return view('home', ['user' => $user]);
            } else {
                //For a user that has not put in their credit card info through Braintree yet
                $user->newSubscription('main', 'annual')
                ->trialDays(365)
                ->create($token, [
                    'email' => $user->email,
                    'phone' => $user->phone,
                ]);

            }

This is in my HomeController. When I go to /home, I get "Undefined variable: token".

I see that I don't declare the variable anywhere else, but I thought that was handled by Cashier.

What am I missing?

0 likes
29 replies
sajeeshe's avatar

from where does this $token comes ?

 ->create($token, [

WallyJ's avatar

I was aware of the issue of non-support of Braintree in 6.0, so I stuck with 5..8 for this project.

How do I check the version of cashier?

I just followed the docs and used:

composer require laravel/cashier-braintree

to install it. So I am assuming since it is the "-braintree" package, that it should work.

WallyJ's avatar

I think the token is supposed to come from the Drop-In UI from Braintree, as a form input. I'm going to work on that and see what I find out. Anyone with Cashier experience, feel free to chime in.

WallyJ's avatar

There are so many mixed messages out there about how to use Cashier, it is quite frustrating. A token needs to be created server-side, but the examples given by Braintree's website aren't built for Cashier. Anyone have any additional wisdom or resources?

WallyJ's avatar

Yes. I searched... and searched some more. With varying instructions. I focused originally on the Laravel Cashier documentation designed specifically for Braintree on Laravel's site, as I mentioned previously. I will try the links you sent and let you know. Thanks.

WallyJ's avatar

I've looked through the links you've sent, as well as others I have found, and interestingly, I can't find a tutorial out there that uses the syntax of:

$user = User::find(1);

$user->newSubscription('main', 'monthly')
            ->trialDays(10)
            ->create($token);

Which is found in the Laravel Cashier Docs for Braintree - https://laravel.com/docs/5.8/braintree#subscriptions

And the docs say nothing about getting the Braintree Drop-in UI, or how to get the info from the UI to the controller.

The only tutorials I found use Javascript in the view to run the CC in the Drop-In UI, then simply show a modal style "Successful" box. I would think the standard practice would be to send a user to a route to show them a "Thank You" view or something like that, like I experience everywhere when I buy something online.

I have never seen the docs be so far off what I find people talking about in tutorials.

I do have the Drop-In UI showing now, but am unsure of how to connect it back to a route (Plus it isn't processing when I hit the submit button anyway)

Currently this is my view code:

@extends('layouts.app')

@section('content')

<div class="container border">
    <div class="row justify-content-center">
        <div class="col-md-12">
            @include('flash-message')
        </div>
    </div>
    <div class="row sectioncontent">
        <div class="col-md-12">
            <div id="dropin-container"></div>
            <button id="submit-button">Process for Next Year</button>
        </div>
    </div>
</div>

<script>

var button = document.querySelector('#submit-button');

    braintree.dropin.create({
      authorization: "{{ Braintree_ClientToken::generate() }}",
      container: '#dropin-container'
    }, function (createErr, instance) {
      button.addEventListener('click', function () {
        instance.requestPaymentMethod(function (err, payload) {
          $.get('{{ route('payment.subscribe') }}', {payload}, function (response) {
            if (response.success) {
              alert('Payment successfull!');
            } else {
              alert('Payment failed');
            }
          }, 'json');
        });
      });
    });
</script>

@endsection

Not sure why my button isn't doing anything. And not sure how to send my user to a route after processing, though it seems that I could change the script code to something like this:

instance.requestPaymentMethod(function (err, payload) {
          $.get('{{ route('payment.subscribe') }}', {payload}, function (response) {
            if (response.success) {
              $.get('{{ route('payment.success') }}');
            } else {
              $.get('{{ route('payment.failed') }}');
            }

And code the functions accordingly.

Sti3bas's avatar

@wallyj

Not sure why my button isn't doing anything.

Do you get any error in the console?

And not sure how to send my user to a route after processing

if(response.success) {
    window.location.replace("{{ route('payment.success') }}");
    return;
}

window.location.replace("{{ route('payment.failed') }}");
WallyJ's avatar

No. No errors in the Console. Nothing in the Console. Should have mentioned that.

Sti3bas's avatar

@wallyj do you see a request to payment.subscribe route in the network tab?

WallyJ's avatar

Nothing happens in the network tab either. Seems like the javascript isn't firing on the button push.

Sti3bas's avatar

@wallyj if you add console.log('test') in your click event listener do you see test in the console?

WallyJ's avatar

No. So weird.

Btw, these are my script lines from app.blade.php:

<!-- Scripts -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
    <script src="https://js.braintreegateway.com/web/dropin/1.20.1/js/dropin.min.js"></script>
    <script src="{{ asset('js/app.js') }}" defer></script>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-3-typeahead/4.0.1/bootstrap3-typeahead.min.js"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css" />

I don't know if that affects my issue.

WallyJ's avatar

I added the console log test:

var button = document.querySelector('#submit-button');

    braintree.dropin.create({
      authorization: "{{ Braintree_ClientToken::generate() }}",
      container: '#dropin-container'
    }, function (createErr, instance) {
      button.addEventListener('click', function () {
        console.log('test');
        instance.requestPaymentMethod(function (err, payload) {
          $.get('{{ route('payment.subscribe') }}', {payload}, function (response) {
            if (response.success) {
              alert('Payment successfull!');
            } else {
              alert('Payment failed');
            }
          }, 'json');
        });
      });
    });
</script>

But it didn't do show anything in the console.

WallyJ's avatar

Technically the Drop-In UI is part of that script, and it's showing up. So the script is firing, just not the button push.

WallyJ's avatar

I am loading Vue for functionality on other pages, but it loads in my app.blade.php, so it's loading for this page as well. I think that might be killing the javascript EventListener.

Do you think that could be it?

Sti3bas's avatar

@wallyj you can try to remove all scripts except dropin.min.js and check if it solves the issue.

WallyJ's avatar

Makes sense. Should have tried that. Thanks.

I did that, and it worked partially. I received this error in the console:

Error in callback function dropin.min.js:1 ReferenceError: $ is not defined at cc:107 at dropin.min.js:1

Not sure if the issue is in my code or the dropin.min.js file.

Sti3bas's avatar

@wallyj that's because you're using $.get in your code which is jQuery function. Try to load jQuery and check if it works.

WallyJ's avatar

Also, it only works when I comment out the app.js file, which I have to run... This really sucks.

WallyJ's avatar

Ok. So, I turned on JQuery, but had to comment out this line to get it to work:

<script src="{{ asset('js/app.js') }}" defer></script>

This is fairly mission critical to my project. Not sure why it is killing the Braintree UI processing.

Sti3bas's avatar

@wallyj and why you're loading multiple jQuery versions? Why you're loading vue from CDN (you're probably already importing it in your app.js)?

WallyJ's avatar

When I setup typeahead, it required JQuery,(I think), so I added the lines of code for it. Then for Braintree's Drop-In, the docs/tutorials showed the other line of code, so I added it. Not sure which one I should use, so I left them both.

Also, whatever Vue tutorial I used showed the CDN link, so I used it.

I guess that's a problem with a lot of tutorials, they don't necessarily explain that something they suggest is an option, but you can do it another way for another reason.

WallyJ's avatar

Plus, even when I comment out the lines to make it work, I receive a 500 error in the console:

VM5025:1 GET http://app.test/payment/subscribe?payload%5Bnonce%5D=tokencc_bd_34c6p5_ptdfxx_hwj4g8_gsw4t2_3m6&payload%5Bdetails%5D%5BexpirationMonth%5D=02&payload%5Bdetails%5D%5BexpirationYear%5D=2021&payload%5Bdetails%5D%5Bbin%5D=411111&payload%5Bdetails%5D%5BcardType%5D=Visa&payload%5Bdetails%5D%5BlastFour%5D=1111&payload%5Bdetails%5D%5BlastTwo%5D=11&payload%5Btype%5D=CreditCard&payload%5Bdescription%5D=ending+in+11&payload%5BbinData%5D%5Bprepaid%5D=Unknown&payload%5BbinData%5D%5Bhealthcare%5D=Unknown&payload%5BbinData%5D%5Bdebit%5D=Unknown&payload%5BbinData%5D%5BdurbinRegulated%5D=Unknown&payload%5BbinData%5D%5Bcommercial%5D=Unknown&payload%5BbinData%5D%5Bpayroll%5D=Unknown&payload%5BbinData%5D%5BissuingBank%5D=Unknown&payload%5BbinData%5D%5BcountryOfIssuance%5D=Unknown&payload%5BbinData%5D%5BproductId%5D=Unknown 500 (Internal Server Error)
(anonymous) @ VM5025:1
send @ jquery.js:8526
ajax @ jquery.js:7978
jQuery.<computed> @ jquery.js:7614
(anonymous) @ cc:111
(anonymous) @ dropin.min.js:1
setTimeout (async)
(anonymous) @ dropin.min.js:1
(anonymous) @ dropin.min.js:1
(anonymous) @ dropin.min.js:1
Promise.then (async)
t.exports @ dropin.min.js:1
(anonymous) @ dropin.min.js:1
(anonymous) @ cc:110

I may just need to abandon the use of Cashier and use Braintree's straight PHP SDK...

The problem for me is that:

Laravel - Designed to make PHP easier

Braintree - Designed to make accepting payments easier

Cashier - Designed to make putting Laravel and Braintree together easier

Using Laravel, Cashier, and Braintree - Complete mess with terribly incomplete docs and 15 tutorials that all say something different, creating a mess of code that doesn't work.

@TaylorOtwell, any thoughts?

WallyJ's avatar

Pardon my ignorance, but what do you mean "importing into app.js"? I try not to touch that file, and its 52,000 lines of code.

Please or to participate in this conversation.