Laravel Jetstream Subscription Billing With Stripe Checkout and Customer Portal

Laravel Jetstream Subscription Billing With Stripe Checkout and Customer Portal

https://ift.tt/2K4IDz3


Laravel Jetstream was recently released and is a great starting point for a new project. I prefer the Livewire stack, which is used in this guide, but there is also an Inertia.js + Vue option.

I recently started a project using Jetstream since it gives you a lot of the same features as Laravel Spark, minus the subscription billing. But, I still needed billing. So, I decided to add Stripe Checkout and customer portal, offloading all of the billing front-end to Stripe.

Here’s how I did it from start to finish.



Start a new Laravel project

laravel new cool-project

cd cool-project

You’ll want to set up your preferred database at this point and ensure that your application can connect to it.



Install Jetstream

Check out the Jetstream documentation if you’re not familiar with it. Install Jetstream with the teams option.

composer require laravel/jetstream

php artisan jetstream:install livewire --teams

Publish the Jetstream views in your application so we can make a few updates later.

php artisan vendor:publish --tag=jetstream-views

And, migrate the database.

php artisan migrate

Lastly, let’s build the front-end.

npm install && npm run dev



Install Laravel Cashier

Why do we need Cashier? Because it’s still a great option to handle the Stripe webhooks for us. And, we’ll extend it to handle a custom webhook.

Install Cashier:

composer require laravel/cashier

If you don’t have a Stripe account, you’ll want to set that up and add your API keys.

Add the following to your .env file.

STRIPE_KEY=your-stripe-key
STRIPE_SECRET=your-stripe-secret
Enter fullscreen mode

Exit fullscreen mode



Team Billing

This is where I fork from the typical Cashier installation. I prefer team billing, but Cashier, by default, expects billing to be set up by user.

You can tell Cashier what model to use by adding an .env variable.

Add the following to your .env file.

CASHIER_MODEL=App\Models\Team

We also need to update the Cashier database migrations to use teams instead of users. Let’s publish the migrations to our project so we can make those updates.

php artisan vendor:publish --tag="cashier-migrations"

In migration 2019_05_03_000001_create_customer_columns.php, replace any instance of “users” with “teams”.

In migration 2019_05_03_000002_create_subscriptions_table.php, replace any instance of “user_id” with “team_id”.

And, let’s migrate the database again.

php artisan migrate

Next, you’ll add the Billable trait to your Team model.

use Laravel\Cashier\Billable;

class Team extends JetstreamTeam
{
    use Billable;
}
Enter fullscreen mode

Exit fullscreen mode

That’s it for the initial setup. Now, it’s time to add the billing front-end and handle the Stripe webhooks.



Stripe Checkout, Customer Portal and Webhooks

Let’s create a controller to handle the checkout and portal links.

php artisan make:controller StripeController

And, update the file with the following:

<?php

namespace App\Http\Controllers;

use Exception;
use Illuminate\Http\Request;

class StripeController extends Controller
{
    public function checkout(Request $request) {
        \Stripe\Stripe::setApiKey(env('STRIPE_SECRET'));
        $user = $request->user();
        $plan = $request->input('plan');

        try {
            $session = \Stripe\Checkout\Session::create([
                'customer_email' => $user->email,
                'payment_method_types' => ['card'],
                'mode' => 'subscription',
                'client_reference_id' => $user->currentTeam->id,
                'line_items' => [[
                    'price' => $plan,
                    'quantity' => 1,
                ]],
                'success_url' => route('dashboard'),
                'cancel_url' => route('billing'),
            ]);
        }
        catch (Exception $e) {
            return response()->json([
                'error' => [
                    'message' => $e->getMessage(),
                ]
            ], 400);
        }

        return response()->json(['sessionId' => $session['id']]);
    }

    public function portal(Request $request)
    {
        return $request->user()->currentTeam->redirectToBillingPortal(
            route('dashboard')
        );
    }
}

Enter fullscreen mode

Exit fullscreen mode

Let’s make a controller to handle the Stripe session.checkout.completed webhook. This is the webhook that is called after a person successfully sets up their subscription and is not handled by Cashier.

php artisan make:controller WebhookController

And, update the file with the following:

<?php

namespace App\Http\Controllers;

use App\Models\Team;
use Illuminate\Support\Facades\DB;
use Laravel\Cashier\Http\Controllers\WebhookController as CashierController;

class WebhookController extends CashierController
{
    public function handleCheckoutSessionCompleted(array $payload)
    {
        $data = $payload['data']['object'];
        $team = Team::findOrFail($data['client_reference_id']);

        DB::transaction(function () use ($data, $team) {
            $team->update(['stripe_id' => $data['customer']]);

            $team->subscriptions()->create([
                'name' => 'default',
                'stripe_id' => $data['subscription'],
                'stripe_status' => 'active'
            ]);
        });

        return $this->successMethod();
    }
}

Enter fullscreen mode

Exit fullscreen mode

One gotcha is that we need to add an exception in the CSRF middlware for the Stripe webhooks.

Update the VerifyCsrfToken.php class with the following:

class VerifyCsrfToken extends Middleware
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = [
        'stripe/*'
    ];
}
Enter fullscreen mode

Exit fullscreen mode

Another gotcha is that we need to make sure the “stripe_id” column is fillable on the Team model.

Update the Team model with the following:

class Team extends JetstreamTeam
{
    ...

    protected $fillable = [
        'name',
        'personal_team',
        'stripe_id'
    ];

    ...
}
Enter fullscreen mode

Exit fullscreen mode

Finally, add the routes for these new controller actions.

Add the following to the routes file:

Route::middleware(['auth:sanctum', 'verified'])->group(function () {
    Route::post('/stripe/checkout', [StripeController::class, 'checkout'])->name('stripe.checkout');
    Route::get('/stripe/portal', [StripeController::class, 'portal'])->name('stripe.portal');
});

Route::post(
    'stripe/webhook',
    '\App\Http\Controllers\WebhookController@handleWebhook'
);
Enter fullscreen mode

Exit fullscreen mode



Jetstream Billing View

We still need to give the user a way to access this new team billing functionality. Let’s add Stripe.js and create the Jetstream-flavored billing view. This view is a good start and works, but it could be slicker.

Add Stripe.js to resources/views/layouts/app.blade.php in the <head> section.

<script src="https://js.stripe.com/v3/"></script>
Enter fullscreen mode

Exit fullscreen mode

Create a new view at resources/views/billing.blade.php and update with the following:

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
             ()
        </h2>
    </x-slot>

    <div>
        <div class="max-w-7xl mx-auto py-10 sm:px-6 lg:px-8">
            <div class="mt-10 sm:mt-0">
                <x-jet-action-section>
                    <x-slot name="title">
                        
                    </x-slot>

                    <x-slot name="description">
                        
                    </x-slot>

                    <x-slot name="content">
                        <p></p>

                        @if (Auth::user()->currentTeam->subscribed('default'))
                            <div class="mt-6">
                                <a class="btn" href="">
                                    
                                </a>
                            </div>
                        @else
                            <div class="mt-4">
                                <button data-plan="price_XXX" class="btn checkout-btn">
                                    
                                </button>
                            </div>
                            <div class="mt-4">
                                <button data-plan="price_XXX" class="btn checkout-btn">
                                    
                                </button>
                            </div>
                        @endif
                    </x-slot>
                </x-jet-action-section>
             </div>
        </div>
    </div>
</x-app-layout>

<script>
var handleFetchResult = function(result) {
    if (!result.ok) {
        return result.json().then(function(json) {
            if (json.error && json.error.message) {
                throw new Error(result.url + ' ' + result.status + ' ' + json.error.message);
            }
        });
    }
    return result.json();
};

var createCheckoutSession = function(plan) {
    return fetch("/stripe/checkout", {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            plan: plan
        })
    }).then(handleFetchResult);
};

var stripe = Stripe('');
var checkoutBtns = document.getElementsByClassName('checkout-btn');

for (var i = 0; i < checkoutBtns.length; i++) {
    checkoutBtns[i].addEventListener("click", function(e) {
        var plan = e.target.getAttribute("data-plan");

        createCheckoutSession(plan).then(function(data) {
        stripe
            .redirectToCheckout({
                sessionId: data.sessionId
            })
            .then(handleResult);
        });
    })
}
</script>


Enter fullscreen mode

Exit fullscreen mode

You’ll want to update the “Subscribe” buttons’ data-plan attributes with your own Stripe product price IDs.

Let’s add a route to make this billing view work. Add the following to your routes file.

Route::middleware(['auth:sanctum', 'verified'])->group(function () {
    Route::get('/billing', function() {
        return view('billing');
    })->name('billing');
});
Enter fullscreen mode

Exit fullscreen mode

Finally (finally!), we’ll add a “Billing” link to the Jetstream navigation dropdown.

In navigation-dropdown.blade.php, add the following in the “Team Management” section.

<!-- Team Billing -->
<x-jet-dropdown-link href="">
    
</x-jet-dropdown-link>
Enter fullscreen mode

Exit fullscreen mode



That’s it! 🎉

This was super long, but I hope it helped someone. Laravel Jetstream is a great starting point and Stripe checkeout and customer portal make it relatively easy to add subscription and billing management.

programming

via Laravel News Links https://ift.tt/2dvygAJ

December 13, 2020 at 08:45PM

Crater Invoice 4.0.0

Crater Invoice 4.0.0

https://craterapp.com


Complete Invoicing Solution

Crater is free for life with all of the below features catered
towards the freelance and small business community.

Invoices

Create and send professional
invoices to clients and save time
for what really matters.

Estimate

Create a quick and detailed
quote including prices, discounts,
inventory and more.

Track Payment

Easily keep a detailed note of
your transactions and never lose
a payment history.

Expenses

Keep track of your spending on various services with our easy to use expense tracking.

Reports

Get detailed reports on your
invoices with one or various
clients.

Taxes

Input different tax
types & calculate them
as simple or compound tax per-item or directly on invoice total.

Send your invoices to the clients instantly, track your payments
or check a detailed client history, Crater App for iOS and Android
let you manage everything from your phone.

Professional Invoice Templates

Choose between three specially designed templates
for creating invoices and estimates.

programming

via Laravel News Links https://ift.tt/2dvygAJ

December 13, 2020 at 08:45PM

Drug Reverses Age-Related Mental Decline Within Days

Drug Reverses Age-Related Mental Decline Within Days

https://ift.tt/2IJayUB

The University of California San Francisco issued this glowing announcement of some new research:
Just a few doses of an experimental drug can reverse age-related declines in memory and mental flexibility in mice, according to a new study by UC San Francisco scientists. The drug, called ISRIB, has already been shown in laboratory studies to restore memory function months after traumatic brain injury, reverse cognitive impairments in Down Syndrome, prevent noise-related hearing loss, fight certain types of prostate cancer, and even enhance cognition in healthy animals. In the new study, published Dec. 1, 2020, in the open-access journal eLife, researchers showed rapid restoration of youthful cognitive abilities in aged mice, accompanied by a rejuvenation of brain and immune cells that could help explain improvements in brain function. "ISRIB’s extremely rapid effects show for the first time that a significant component of age-related cognitive losses may be caused by a kind of reversible physiological "blockage" rather than more permanent degradation," said Susanna Rosi, PhD, Lewis and Ruth Cozen Chair II and professor in the departments of Neurological Surgery and of Physical Therapy and Rehabilitation Science. "The data suggest that the aged brain has not permanently lost essential cognitive capacities, as was commonly assumed, but rather that these cognitive resources are still there but have been somehow blocked, trapped by a vicious cycle of cellular stress," added Peter Walter, PhD, a professor in the UCSF Department of Biochemistry and Biophysics and a Howard Hughes Medical Institute investigator. "Our work with ISRIB demonstrates a way to break that cycle and restore cognitive abilities that had become walled off over time…." "We’ve seen how ISRIB restores cognition in animals with traumatic brain injury, which in many ways is like a sped-up version of age-related cognitive decline," said Rosi, who is director of neurocognitive research in the UCSF Brain and Spinal Injury Center and a member of the UCSF Weill Institute for Neurosciences. "It may seem like a crazy idea, but asking whether the drug could reverse symptoms of aging itself was just a logical next step." Forbes also reports that "In all studies, the researchers have observed no serious side effects."


Read more of this story at Slashdot.

geeky

via Slashdot https://slashdot.org/

December 12, 2020 at 07:03PM

Fantastic Four Finally Entering the Marvel Cinematic Universe and More Huge News

Fantastic Four Finally Entering the Marvel Cinematic Universe and More Huge News

https://ift.tt/3m6DALu


It’s official. The Fantastic Four are joining the Marvel Cinematic Universe. Jon Watts (Spider-Man Homecoming) will direct.

The news came at the end of a long, densely packed Marvel Studios presentation during Disney’s 2020 Investor’s Day event. We’ll have much more soon.

This story is developing…

geeky,Tech

via Gizmodo https://gizmodo.com

December 10, 2020 at 08:57PM

Laravel ajax datatables tutorial

Laravel ajax datatables tutorial

https://ift.tt/2W0mggE


jQuery datatable is very popular for listing data in web application with various interactive features like live-searching, pagination, sorting and more. If you want to use jQuery datatables in your laravel application then here I’m going to show you how to use jQuery-datatables in laravel and how to work for laravel datatables ajax, laravel datatables server-side, datatables customization and more. In this tutorial post, I will use a popular laravel package for datatables named yajra/laravel-datatables for making our task easier. Let’s start.

 

Laravel yajra/laravel-datatables ajax steps

  1. Install the yajra/laravel-datatables package.
  2. Define routes.
  3. Make a controller for datatable.
  4. Make view and add Js libraries.
  5. Code for jQuery datatables.

 

1. Install the yajra/laravel-datatables package

First, we have to Install the yajra/laravel-datatables package by composer command.

composer require yajra/laravel-datatables-oracle:"~9.0"

 

2. Define routes

Now let’s define these two routes in the web.phpp route file. You can change the route endpoints according to your application.

Route::get('api/customers','CustomerController@data');
Route::get('customers','CustomerController@index');

 

3. Make a controller for datatable

Now make a controller to handle the view and feed data to our jQuery datatables. Run the artisan command below to make a controller.

php artisan make:controller CustomerController

Do code in the controller:

<?php namespace App\Http\Controllers;

use App\Customer;

class CustomerController extends Controller
{

    public function index()
    {
        return view('customers');
    }

    public function data()
    {
        $customers = Customer::all();
        return datatables()->of($customers)->toJson();
    }
}

Note: Look from the index method we are just returning a simple view customers which we’ll create later and from data method we are returning JSON data for feed it to jQuery datatables. Here we have just used datatables()->of($data)->toJson(); you can find more option from yajra/laravel-datatables documentation.

 

With help of the datatables()->of() method from the yajra/larave-datatables package, we will get the formated JSON in example.com/api/customers endpoint look like below. So we don’t need to do anything about data formation.

{
  "draw": 0,
  "recordsTotal": 20,
  "recordsFiltered": 20,
  "data": [
    {
      "id": 1,
      "name": "Kenya",
      "email": "Cheyenne.Nolan94@hotmail.com",
      "phone": "910-845479",
      "dob": "1982-10-22"
    },
    {
      "id": 2,
      "name": "Madisen",
      "email": "Soledad.Schumm23@yahoo.com",
      "phone": "910-450685",
      "dob": "1984-09-19"
    },

    ...
    ...
}

 

4. Make view and add Js libraries.

Make a view customers.blade.php inside resources/views directory and add js dependencies. jQuery datatables need the jQuery library. So we have to add it first then datatables CSS and Js file. Let’s do that.

Note: You can add CSS, Js library files inside your master layout file, here I’m adding these into customers.blade.php file for tutorial purpose.

@extends('layouts.app')
@section('content')
    <div class="container">
        <div class="row">
            <div class="col-md-8">
                <h3>Customers</h3>
                <hr>
                <table id="customers" class="table table-bordered table-condensed table-striped" >
                    <thead>
                    <tr>
                        <th>ID</th>
                        <th>Name</th>
                        <th>Phone</th>
                        <th>DOB</th>
                    </tr>
                    </thead>

                </table>


                <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
                <link href="https://cdn.datatables.net/1.10.12/css/jquery.dataTables.min.css" rel="stylesheet">
                <script src="https://cdn.datatables.net/1.10.12/js/jquery.dataTables.min.js"></script>

                <!-- our script will be here -->
            </div>
        </div>
    </div>
@endsection 

 

5. Code for jQuery datatables

Now add the code given below at the end of customers.blade.php file for showing data in interactive jQuery datatable. For laravel datatables ajax, set the ajax value of our API endpoint for fetching the customers data and set value true for yajra/laravel-datatables server-side.

$(document).ready(function() {
    $.noConflict();
    
    $('#customers').DataTable({
        ajax: '',
        serverSide: true,
        processing: true,
        columns: [
            {data: 'id', name: 'id'},
            {data: 'name', name: 'name'},
            {data: 'phone', name: 'phone'},
            {data: 'dob', name: 'dob'}
        ]
    });
})

 

Output Laravel-Datatable

Browse example.com/customers and you will get the result looks like given output screenshot.

laravel-yajra-datatables.png

 

Laravel Datatables customization

 

Add a custom column to the datatables

To add a custom column into yajra/laravel-datatables API data use addColumn('coumn_name',callback) method. Suppose we need another column for action like edit, delete button.

public function data()
{
    $customers = Customer::all();
    return datatables()->of($customers)
        ->addColumn('action', function ($row) {
            $html = '<a href="#" class="btn btn-xs btn-secondary">Edit</a> ';
            $html .= '<button data-rowid="'.$row->id.'" class="btn btn-xs btn-danger">Del</button>';
            return $html;
        })->toJson();
}

In HTML markup table add the column

<th>Action</th>

Add the column in script’s columns array.

{data: 'action', name: 'action'},

datatables-addcolumn.png

 

Disable searching & sorting for a specific column

To disable searching and sorting for specific column add orderable and searchable false.

{data: 'action', name: 'action', orderable: false, searchable: false},

 

Add custom ajax loader image to datatables

To add custom ajax loader image to jQuery datatables add the image with oLanguage into the script.

oLanguage: {
    sProcessing: "<img src='image_url_here'>"
},

 

Datatables Pre-sorted data with a specific column

To show datatables data with pre-sorted with a specific column, add the aaSorting property.

aaSorting: [[0, "desc"]],

Note: Here 0 refers to the first column of the datatables.

 

Hope this tutorial post help you to integrate jQuery datatables with yajra/laravel-datatables package. If you find this post helpful to you then please share this post with others so that they will get helped.

programming

via Laravel News Links https://ift.tt/2dvygAJ

December 10, 2020 at 08:06PM

Huge Star Wars Updates: Andor Teaser, Obi-Wan Timeline, Lando Show, and The Acolyte

Huge Star Wars Updates: Andor Teaser, Obi-Wan Timeline, Lando Show, and The Acolyte

https://ift.tt/3gHlLSb


Ewan McGregor is suiting up as Obi-Wan very soon.
Photo: Lucasfilm

Star Wars and Disney+ will continue to get cozy even when The Mandalorain isn’t around. At Disney’s 2020 Investor’s Day event, significant updates were given on the other Star Wars official shows we knew were coming to Disney+.

First up, Andor will be released in 2022 and began filming two weeks ago. Here’s a sizzle reel:

The Obi-Wan Kenobi show takes place ten years after Revenge of the Sith and will bring back Hayden Christensen as Darth Vader. Footage was shown, even though it won’t start shooting until next year, but it wasn’t online. Only this bit was revealed.

G/O Media may get a commission

Also Justin Simien is doing a Lando show and Leslye Headland’s show is called The Acolyte and takes place during the High Republic.

This story is developing…


For more, make sure you’re following us on our Instagram @io9dotcom.

geeky,Tech

via Gizmodo https://gizmodo.com

December 10, 2020 at 08:09PM

Loki’s First Trailer Brings the Asgardian Trickster Back Into the Fold

Loki’s First Trailer Brings the Asgardian Trickster Back Into the Fold

https://ift.tt/37QzTV7


Glorious indeed.
Screenshot: Marvel Studios

Trailer FrenzyA special place to find the newest trailers for movies and TV shows you’re craving.

The god of tricks is getting into the crime game, with a suitably wild trailer for his new series.

Starring Tom Hiddleston reprising his role as the Thor breakout, the series is set after Loki inadvertently gains access to the Tesseract, flinging him into the path of a mysterious investigative team known as the TVA.

Loki hits Disney+ in May 2021.


For more, make sure you’re following us on our Instagram @io9dotcom.

G/O Media may get a commission

geeky,Tech

via Gizmodo https://gizmodo.com

December 10, 2020 at 08:09PM

The Remington 870: A Timeless Pump-Action Shotgun

The Remington 870: A Timeless Pump-Action Shotgun

https://ift.tt/3gxMY9Q

One would have to argue long and hard to dispute that the classic Remington 870 pump-action shotgun is not, perhaps, the greatest slide-action smooth-bore shotgun of all time. Selling well into the millions, the Remington 870 has been delivered in a large variety of models from basic shotguns to magnums with numerous unique features that vary among them. The basic Remington 870 was first produced in 1950 as the Wingmaster model available in several gauges including 12, 16, or 20 gauge in barrel lengths of 26, 28, or 30 inches. These were 5 shotguns with various fixed-choke barrels including open bore, improved cylinder, modified, and full choke. Barrels were available in plain or vent-ribbed for an extra fee.

As mentioned, there have been so many models it is difficult to account for them all. These named pump-action shotguns included the Wingmaster, Express, Youth, Synthetic, Deer, Magnum, Express Magnum, Turkey, Express Combo, Laminate, Express Junior, Home Defense, Tactical, Express Super Magnum, Super Magnum Turkey, Super Slug, Special Purpose Marine Magnum, Sportsman, Small Gauge (28 and .410 gauge), Special Purpose, Magnum Duck, SPS-T Turkey, Special Purpose Deer, Lightweight, Special Field, Police, Blackhawk, Riot, Trap, and an unaccounted number of tribute and anniversary guns. See what I mean?

Remington 870 shotguns have seen action by law enforcement, military, and civilian use all these years. It is a sturdy design utilizing twin pump bars to insure smooth action for ejecting a spent shell and loading a fresh one. Shotshells are inserted under the receiver frame through a spring hinged loading gate into an under the barrel magazine hidden under the forearm stock. Plain sights are just a bead at the muzzle, though other models such as deer slug guns used other types of sights.

Wood furniture on the Remington 870s was everything from homely looking hardwood stocks to oiled walnut as well as highly lacquered walnut with pressed or cut checkering. Metal finishing was usually a nice polished bluing to a more utilitarian matte bead blasted dark blue or black finish depending on the model. Marine models were either parkerized or finished in a matte nickel type finish to resist salt rusting.

Barrels came in several lengths as mentioned – plain or with a raised sighting rib. Trap and skeet versions often had special raised ribs for sport shooting. Finishes were typically blued or matte. Remington much later in production introduced their unique RemChoke system which allowed the shooter to screw in various choke tubes from IC, Mod, Full, and a variety of specialty choke tubes such as Super Full or Turkey Full. This made the Remington 870 even more popular. The Remington 870 is a universal shotgun for all types of hunting from small game, upland birds, waterfowl, deer, and big game. It is a great home defense shotgun and truck gun. If you don’t have one, then it should certainly be on your buy list.

The post The Remington 870: A Timeless Pump-Action Shotgun appeared first on AllOutdoor.com.

guns

via All Outdoor https://ift.tt/2yaNKUu

December 10, 2020 at 01:19PM

Immediately After Moving To Texas, Elon Musk Announces Tesla AR-15

Immediately After Moving To Texas, Elon Musk Announces Tesla AR-15

https://ift.tt/36Zdndq


AUSTIN, TX—After years of fighting lame California politicians who want to lock everyone in their homes so they can’t go to space or build cool stuff, Tesla CEO Elon Musk announced he was moving to a land flowing with milk and honey called Texas. Almost immediately after the move, he announced a new product the whole world has been waiting for: the Tesla AR-15.

“Um… h-howdy, I think,” said Elon, expertly taking on the local dialect. “Y’all want a Tesla AR-15? You got it, partner!” 

The new firearm will look similar to a standard AR-15 but will in fact be a battery-powered railgun capable of firing 3 million rounds per minute. It will also feature a fingerprint sensor, Bluetooth capability, heat-seeking ammunition, and a chainsaw bayonet, to name a few.

“We really wanted to re-think self-defense,” said Musk. “This new firearm will be the go-to rifle for our SpaceX volunteers when they encounter hostile aliens. It’s also a celebration of the Second Amendment. If any federal authorities try to confiscate it, the rifle will automatically connect to the internet and file a lawsuit for you!”

The Federal Government has requested that Tesla at least remove the AI-powered “self-driving” feature on the firearm before selling it. 


fun

via The Babylon Bee https://babylonbee.com

December 9, 2020 at 02:03PM