generated from boboko/starter
checkout process - start
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Checkout;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
use Lunar\Models\Country;
|
||||
use Modules\Core\Cart\Services\CartService;
|
||||
use Modules\Core\Checkout\Exceptions\InvalidShippingOptionException;
|
||||
use Modules\Core\Checkout\Services\CheckoutService;
|
||||
|
||||
/**
|
||||
* The checkout page itself — one page, sections (contact / billing / shipping
|
||||
* / shipping method), reviewed against boboko-core's CheckoutService. Deliberately
|
||||
* stops short of payment for this slice (see project memory): every action here
|
||||
* ends in a redirect back to `show()`, which just re-renders against whatever
|
||||
* state the cart/CheckoutService is now in — there's no client-side state to
|
||||
* keep in sync.
|
||||
*
|
||||
* Guest-only for now — the Contact section's login/register tab is UI only,
|
||||
* not wired to Modules\Core\Auth\Services\UserOtpService yet.
|
||||
*/
|
||||
class CheckoutController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly CartService $cart,
|
||||
private readonly CheckoutService $checkout,
|
||||
) {}
|
||||
|
||||
public function show(string $locale): View
|
||||
{
|
||||
$cart = $this->cart->current();
|
||||
$lines = $cart ? $this->cart->activeLines($cart) : collect();
|
||||
|
||||
$shippingAddress = $cart?->shippingAddress;
|
||||
$billingAddress = $cart?->billingAddress;
|
||||
|
||||
// Rate quoting needs a shipping address to resolve against (postcode/
|
||||
// country for ACS/BoxNow live rates, table-rate-shipping's zones) —
|
||||
// nothing to offer yet on a cart with no address saved.
|
||||
$shippingOptions = $shippingAddress
|
||||
? $this->checkout->getShippingOptions()
|
||||
: collect();
|
||||
|
||||
return view('checkout::page', [
|
||||
'cart' => $cart,
|
||||
'lines' => $lines,
|
||||
'billingAddress' => $billingAddress,
|
||||
'shippingAddress' => $shippingAddress,
|
||||
'shippingOptions' => $shippingOptions,
|
||||
'countries' => Country::orderBy('name')->get(['id', 'name']),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* One form, two CheckoutService calls — billing and shipping are always
|
||||
* both set together here. When "same as billing" is checked the shipping
|
||||
* fields are disabled client-side (so the browser never submits them) and
|
||||
* the billing values are reused for shipping too, plus delivery
|
||||
* instructions.
|
||||
*/
|
||||
public function saveAddress(string $locale, Request $request): RedirectResponse
|
||||
{
|
||||
$sameAsBilling = $request->boolean('same_as_billing');
|
||||
|
||||
$rules = [
|
||||
'contact_email' => ['required', 'email'],
|
||||
|
||||
'billing_first_name' => ['required', 'string', 'max:255'],
|
||||
'billing_last_name' => ['required', 'string', 'max:255'],
|
||||
'billing_company_name' => ['nullable', 'string', 'max:255'],
|
||||
'billing_tax_identifier' => ['nullable', 'string', 'max:255'],
|
||||
'billing_line_one' => ['required', 'string', 'max:255'],
|
||||
'billing_line_two' => ['nullable', 'string', 'max:255'],
|
||||
'billing_city' => ['required', 'string', 'max:255'],
|
||||
'billing_state' => ['nullable', 'string', 'max:255'],
|
||||
'billing_postcode' => ['required', 'string', 'max:20'],
|
||||
'billing_country_id' => ['required', 'integer', 'exists:'.(new Country)->getTable().',id'],
|
||||
'billing_contact_phone' => ['nullable', 'string', 'max:50'],
|
||||
|
||||
'shipping_delivery_instructions' => ['nullable', 'string', 'max:1000'],
|
||||
];
|
||||
|
||||
if (! $sameAsBilling) {
|
||||
$rules += [
|
||||
'shipping_first_name' => ['required', 'string', 'max:255'],
|
||||
'shipping_last_name' => ['required', 'string', 'max:255'],
|
||||
'shipping_company_name' => ['nullable', 'string', 'max:255'],
|
||||
'shipping_line_one' => ['required', 'string', 'max:255'],
|
||||
'shipping_line_two' => ['nullable', 'string', 'max:255'],
|
||||
'shipping_city' => ['required', 'string', 'max:255'],
|
||||
'shipping_state' => ['nullable', 'string', 'max:255'],
|
||||
'shipping_postcode' => ['required', 'string', 'max:20'],
|
||||
'shipping_country_id' => ['required', 'integer', 'exists:'.(new Country)->getTable().',id'],
|
||||
'shipping_contact_phone' => ['nullable', 'string', 'max:50'],
|
||||
];
|
||||
}
|
||||
|
||||
$data = $request->validate($rules);
|
||||
|
||||
$billing = [
|
||||
'first_name' => $data['billing_first_name'],
|
||||
'last_name' => $data['billing_last_name'],
|
||||
'company_name' => $data['billing_company_name'] ?? null,
|
||||
'tax_identifier' => $data['billing_tax_identifier'] ?? null,
|
||||
'line_one' => $data['billing_line_one'],
|
||||
'line_two' => $data['billing_line_two'] ?? null,
|
||||
'city' => $data['billing_city'],
|
||||
'state' => $data['billing_state'] ?? null,
|
||||
'postcode' => $data['billing_postcode'],
|
||||
'country_id' => $data['billing_country_id'],
|
||||
'contact_email' => $data['contact_email'],
|
||||
'contact_phone' => $data['billing_contact_phone'] ?? null,
|
||||
];
|
||||
|
||||
$shipping = $sameAsBilling
|
||||
? [...$billing, 'delivery_instructions' => $data['shipping_delivery_instructions'] ?? null]
|
||||
: [
|
||||
'first_name' => $data['shipping_first_name'],
|
||||
'last_name' => $data['shipping_last_name'],
|
||||
'company_name' => $data['shipping_company_name'] ?? null,
|
||||
'line_one' => $data['shipping_line_one'],
|
||||
'line_two' => $data['shipping_line_two'] ?? null,
|
||||
'city' => $data['shipping_city'],
|
||||
'state' => $data['shipping_state'] ?? null,
|
||||
'postcode' => $data['shipping_postcode'],
|
||||
'country_id' => $data['shipping_country_id'],
|
||||
'contact_email' => $data['contact_email'],
|
||||
'contact_phone' => $data['shipping_contact_phone'] ?? null,
|
||||
'delivery_instructions' => $data['shipping_delivery_instructions'] ?? null,
|
||||
];
|
||||
|
||||
$this->checkout->setBillingAddress($billing);
|
||||
$this->checkout->setShippingAddress($shipping);
|
||||
|
||||
return redirect()->route('checkout.show', $locale);
|
||||
}
|
||||
|
||||
public function selectShippingOption(string $locale, Request $request): RedirectResponse
|
||||
{
|
||||
$data = $request->validate([
|
||||
'shipping_option' => ['required', 'string'],
|
||||
]);
|
||||
|
||||
try {
|
||||
$this->checkout->selectShippingOption($data['shipping_option']);
|
||||
} catch (InvalidShippingOptionException) {
|
||||
return back()->withErrors([
|
||||
'shipping_option' => __('checkout.page.shipping_option_invalid'),
|
||||
]);
|
||||
}
|
||||
|
||||
return redirect()->route('checkout.show', $locale);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user