Photo by Mein Deal on Unsplash

Laravel Shopr: How to add shipping to your checkout

Mattias Persson
3 min readJul 12, 2019

Laravel Shopr is a package for integrating e-commerce into your Laravel app. It gives you a shopping cart with discount coupons and a full checkout flow, just to mention a few of the useful features included.
In this guide we won’t discuss how to install or get started with the package, since it’s covered in detail in the documentation.

Conceptual overview

Adding shipping alternatives to your Shopr checkout is actually very simple. We’ll treat the shipping options as a Shoppable just like the other purchasable models, which makes it easy to add it to the cart and order. This is very similar to how discount coupons are handled.

Basically, the flow will look like this:

  1. On the checkout page, the customer can select one of the available shipping options.
  2. Before making the request to pay for the cart and convert it to an order, we’ll add the selected shipping option to the cart as a regular item.
  3. In the confirmation templates, we can easily customize how the shipping option is displayed.

That’s it!

Adding shipping options to your checkout

First of all, we create a migration for the shipping_options table.

Schema::create('shipping_methods', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->text('description')->nullable();
$table->integer('sortorder')->default(0);
$table->decimal('price', 9, 2)->default(0);

// This column allows us to add a
// "free for orders worth x or more"-feature.
// More on that further down.
$table->decimal('free_level', 9, 2)->nullable();

$table->timestamps();
$table->softDeletes();
});

And the model, which extends the Shoppable class instead of the default Model class.

<?phpnamespace App\Models;use Happypixels\Shopr\Models\Shoppable;
use Illuminate\Database\Eloquent\SoftDeletes;
class ShippingMethod extends Shoppable
{
use SoftDeletes;
}

Next, we’ll create an endpoint for retrieving the available shipping options on the checkout page. This will be called to present the options to the customer.

<?phpnamespace App\Http\Controllers;use App\Models\ShippingMethod;class ShippingMethodController extends Controller
{
public function index()
{
return ShippingMethod::orderBy('sortorder', 'ASC')->get();
}
}

Don’t forget to also add a route for it.

You then present a few radio buttons or similar which allows the customer to select their desired shipping method.
In this example we’ll use Stripe to process the payments, so we want to add the shipping option to the cart after retrieving the Stripe token but before making the charge request. For more details on the Stripe checkout flow, check out the demo implementation.

methods: {
charge (token) {
window.axios.post('shopr/cart/items', {
shoppable_type: 'App\\Models\\ShippingMethod',
shoppable_id: this.userData.options.shipping.id
}).then(response => {
// Make the charge request to process the payment.
})
}
}

We have now added the selected shipping method to our order. The price is automatically added to the order total and everything should work. However, we want to exclude it from the order items table in the confirmation template. This is done by filtering the order items by their shoppable_type (the model).

// The order-confirmation blade template as configured in config/shopr.php.@foreach($order->items->where('shoppable_type', '!=', 'App\Models\ShippingMethod') as $item)
// Print order row.
@endforeach

To make it more testable and keep the logics away from your template, you might want to use custom models for your order and add a `printableItems` method or something similar to it which filters out the shipping option row. Then, just call the `$order->printableItems` in the confirmation template. To access the shipping method, add another method which does the opposite (only returns the shipping method).
Read more about how to use custom models for your orders in the documentation.

All done!

Bonus: using a dynamic price for a shipping option

Imagine you want a shipping method to be free when placing an order worth $100 or more. This can be achieved by setting the free_level-column in the database to 100, then modifying the getPrice method on the ShippingMethod model to check the cart value before returning the price:

<?phpnamespace App\Models;use Happypixels\Shopr\Contracts\Cart;
use Happypixels\Shopr\Models\Shoppable;
use Illuminate\Database\Eloquent\SoftDeletes;
class ShippingMethod extends Shoppable
{
use SoftDeletes;
protected $casts = [
'price' => 'float',
'free_level' => 'float',
];
public function getPrice()
{
$cartValue = app(Cart::class)->total();
if ($this->free_level !== null && $cartValue >= $this->free_level) {
return 0;
}
return $this->price;
}
}

Don’t forget to make sure your frontend displays the correct price for the shipping method as well.

--

--