Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Checkout Laravel Package

vanilo/checkout

Vanilo Checkout provides a flexible, configurable checkout flow for Laravel ecommerce applications, converting carts into orders through steps for customer details, shipping, payment, and confirmation. It integrates with the Vanilo suite and can be customized to fit your store.

View on GitHub
Deep Wiki
Context7

Getting Started

Vanilo Checkout is the step-flow engine that sits between a vanilo/cart cart and a vanilo/order order. In a normal Vanilo app it arrives as a transitive dependency of vanilo/framework, so you rarely install it directly:

composer require vanilo/checkout   # only if you're assembling modules yourself
php artisan vendor:publish --tag=vanilo-checkout

Where to look first, in order:

  1. config/checkout.php — this is the source of truth for the flow. It defines the ordered steps array and the driver/implementation used to resolve the checkout. If your flow "does the wrong thing," it's almost always here.
  2. src/Contracts/Checkout.php and src/Contracts/CheckoutManager.php — the two interfaces you'll code against. The Checkout object is the state bag (cart, customer, shipping/billing address, shipping method, payment method); the CheckoutManager is the flow controller (current/next/previous step, proceed/back).
  3. src/Http/ (or the default CheckoutController) and the published views — a working reference implementation of each step.
  4. The Vanilo\Checkout\Facades\Checkout facade and the checkout middleware group — how routes reach the flow.

First use case: wire the default flow end-to-end. Register the checkout routes (the module ships a service provider that can publish/bind them), add a Checkout step controller per route, and load a cart into the checkout before rendering step one:

use Vanilo\Checkout\Facades\Checkout;

Checkout::setCart($cart);          // Cart from vanilo/cart
return view(Checkout::getCurrentStep()->getView());

Once you can walk cart → address → shipping → payment → confirm and call Checkout::createOrder() on the last step, you understand 80% of the package.

Implementation Patterns

Step-per-route (the intended pattern). Each step is a GET that renders a view and a POST that validates input, mutates the Checkout state, and calls the manager's proceed()/back(). The manager refuses to advance if a step's guard fails (e.g., no shipping method selected), so keep the step objects as the single place for "is this step satisfied?" logic instead of duplicating it in controllers.

Custom steps. Add your own step by implementing CheckoutStep (or extending the provided abstract step) and registering it in the steps array in config/checkout.php. A typical custom step is "gift message," "age verification," or "delivery date picker." Keep the step class thin: read from the Checkout bag, validate, and stash the result on the session-backed checkout. Then the rest of the flow treats it like any other step.

Cart injection at the boundary. The checkout does not own the cart. Always resolve the cart from vanilo/cart (usually the session/current cart) and setCart() at the start of the flow. If you have multiple carts (e.g., a "buy now" vs. "basket"), enforce a single active cart before entering checkout, otherwise the order created at the end won't match what the user sees.

Order creation is the hand-off point. The last step calls Checkout::createOrder(), which returns a vanilo/order Order. From there you're in vanilo/payment / gateway territory: initiate payment, attach the transaction to the order, and only then show a confirmation. Treat createOrder() as a commit — everything upstream (addresses, methods, amounts) is frozen into the order at that moment.

Headless / API flows. Because the Checkout contract is interface-driven, you can build JSON endpoints that mirror the web steps without touching views: one endpoint per step, returning the step's serialized state and validation errors. Bind your own implementation of the Checkout contract (a non-session-backed one, e.g., keyed by a token) in a service provider if you need stateless operation.

Testing. The checkout is easiest to test by resolving the contract and driving it directly:

$checkout = app(\Vanilo\Checkout\Contracts\Checkout::class);
$checkout->setCart($fakeCart);
$checkout->setShippingAddress($address);
// assert step guards / createOrder behavior

Prefer asserting on the manager's step progression and on the resulting order, not on HTTP view output.

Gotchas and Tips

  • Session-bound state. The default checkout persists to the session. That means no queue workers, no multi-tab consistency, and no "resume checkout in an email link" without extra work. If you need any of those, plan a custom Checkout implementation bound in the container.
  • Cart contract mismatch. setCart() expects a vanilo/cart cart. Passing an arbitrary Eloquent model or a "wishlist" object will blow up later when the order tries to read line items. Validate the type at the controller boundary.
  • Empty config/checkout.php steps. The manager reads the steps array at boot. If you clear the config cache after editing steps, remember to re-run php artisan config:cache in production — cached config can silently keep an old step order or a removed step.
  • Billing vs. shipping address. The default flow often copies shipping to billing when "same as shipping" is checked. If you toggle that, be explicit about whether you're clearing or reusing billingAddress, otherwise you'll ship an order with a stale address.
  • Double-submit / double order. createOrder() is not inherently idempotent. Guard the confirm step with a token, redirect-after-post, or an order-existence check keyed on the checkout identity. Users will double-click "Place order."
  • Totals drift after state changes. Changing shipping method or address can change tax/shipping amounts. Re-run the cart/order total recalculation after each mutating step, and don't cache the rendered total across steps.
  • Extension over modification. vanilo/checkout is a small, low-star internal module — fork risk is low but you should still extend via the contracts (Checkout, CheckoutManager, CheckoutStep) and container bindings rather than editing vendor/. When it breaks, reading src/ is faster than searching docs; the docs for this submodule are thin.
  • Debugging step guards. If proceed() "does nothing," log the current step object and its validation result — the manager silently refuses to advance on a failed guard. Instrument the step class rather than the controller to find the missing state.
  • Compatibility. Pin Vanilo modules to a mutually compatible set (checkout, cart, order, payment often move together). A mismatched minor version of vanilo/order is a common cause of createOrder() returning unexpected types.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource