checkout & create payment

This commit is contained in:
Bayu Lukman Yusuf 2026-01-29 08:26:00 +07:00
parent d80b42c297
commit fa00ee377a
8 changed files with 396 additions and 351 deletions

View File

@ -6,6 +6,7 @@ use App\Models\Address;
use App\Models\Location;
use App\Repositories\Member\Cart\MemberCartRepository;
use App\Repositories\Member\ShippingRepository;
use App\Repositories\Member\Transaction\TransactionRepository;
use Illuminate\Http\Request;
class CheckoutController extends Controller
@ -77,6 +78,12 @@ class CheckoutController extends Controller
$location_id = session('location_id', 22);
if ($delivery_method == null || $address_id == null || $location_id == null) {
return redirect()->route('checkout.delivery');
}
$subtotal = $memberCartRepository->getSubtotal($location_id);
$total = $subtotal;
@ -97,11 +104,6 @@ class CheckoutController extends Controller
'cost' => $row['shipping_fee'],
];
});
return view('checkout.v1-delivery-1-shipping', [
'carts' => $request->user()->carts,
@ -113,8 +115,6 @@ class CheckoutController extends Controller
'shipping_list' => $shipping_list,
]);
} catch (\Exception $e) {
dd($e);
return redirect()->route('checkout.delivery')->with('error', 'Invalid checkout data');
}
}
@ -131,22 +131,73 @@ class CheckoutController extends Controller
session(['checkout_courier' => $courier]);
session(['checkout_service' => $service]);
session(['checkout_shipping_cost' => $cost]);
// session(['checkout_shipping_cost' => $cost]);
return redirect()->route('checkout.payment');
}
public function choosePayment(Request $request)
public function choosePayment(Request $request, TransactionRepository $repository, MemberCartRepository $memberCartRepository)
{
try {
// proses checkout
$address_id = session('checkout_address_id');
$courier = session('checkout_courier');
$service = session('checkout_service');
$location_id = session('location_id', 22);
$items = [];
$request->merge(['location_id' => $location_id]);
$carts = $memberCartRepository->getList($request);
if (count($carts) == 0) {
return redirect()->route('checkout.delivery')->with('error', 'No items in cart');
}
foreach ($carts as $cart) {
$items[] = [
"item_reference_id" => $cart->item_reference_id,
"qty" => $cart->qty
];
}
$data = [
"address_id" => $address_id,
"note" => "",
"courier_company" => $courier,
"courier_type" => $service,
"location_id" => $location_id,
"items" => $items,
"vouchers" => [],
"use_customer_points" => 0,
];
$item = $repository->create($data);
$notification = new \App\Notifications\Member\Transaction\OrderWaitPayment($item);
$user = auth()->user();
$user->notify($notification->delay(now()->addMinutes(1)));
// return new CheckoutResource($item);
// proses payment
$payment = $item->payments()->where("method_type",'App\Models\XenditLink')
->where("status",'PENDING')
->first();
$invoice_url = $payment ? @$payment->method->invoice_url: "";
return redirect()->to($invoice_url);
} catch (\Exception $e) {
dd($e);
return redirect()->route('checkout.delivery')->with('error', 'Invalid checkout data');
}
}

View File

@ -6,8 +6,9 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Awobaz\Compoships\Compoships;
use Carbon\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Cache;
class ItemReference extends Model
{
@ -126,9 +127,7 @@ class ItemReference extends Model
});
// Log::info([$location_id, $is_consignment]);
return $this->hasOne(Discount::class,DB::raw("discount_items.item_reference_id"))
return $this->hasOne(Discount::class, 'id', 'item_reference_id')
->leftJoin("discount_items","discounts.id","=","discount_id")
->where("discounts.type","discount")
->orderBy("discounts.created_at","desc")

View File

@ -0,0 +1,66 @@
<?php
namespace App\Repositories\Auth;
use App\Models\Permission;
use App\Models\Role;
use DB;
use Hash;
class RoleRepository
{
public function getList(array $params = [])
{
$limit = @$params["limit"] ? (int) @$params["limit"] : 10;
$sortColumn = @$params["sort"]["column"] ? $params["sort"]["column"] : "id";
$sortDir = @$params["sort"]["dir"] ? $params["sort"]["dir"] : "desc";
return Role::when(@$params['search'], function($query) use ($params) {
return $query->where('name', 'iLIKE', '%' . $params['search'] . '%');
})
->orderBy($sortColumn, $sortDir)
->paginate($limit);
}
public function getPermission(array $params = [])
{
return Permission::orderBy("code")->paginate(1000);
}
public function create(array $data)
{
$item = Role::create($data);
$item->permissions()->sync($data["permissions"]);
$item->save();
return $item;
}
public function update(Role $item, array $data)
{
$item->update($data);
$item->permissions()->sync($data["permissions"]);
$item->save();
return $item;
}
public function delete(Role $item)
{
$item->delete();
}
public function findBy($column, $value)
{
$item = Role::where($column, $value)->firstOrFail();
return $item;
}
public function findUserByPermission($code)
{
$roles = Role::whereHas("permissions", function($query) use ($code){
$query->where("code", $code);
})->get();
return $roles->flatMap(function($role){
return $role->users;
});
}
}

View File

@ -1,26 +0,0 @@
<?php
namespace App\Http\Controllers\Member\Transaction;
use App\Http\Controllers\Controller;
use App\Http\Requests\Member\Transaction\TransactionRequest;
use App\Http\Resources\Member\Transaction\CheckoutResource;
use App\Repositories\Member\Transaction\TransactionRepository;
use App\Notifications\Member\Transaction\OrderWaitPayment;
use Illuminate\Support\Facades\Notification;
class CheckoutController extends Controller
{
public function index(TransactionRequest $request, TransactionRepository $repository)
{
$data = $request->validated();
$item = $repository->create($data);
$notification = new OrderWaitPayment($item);
$user = auth()->user();
$user->notify($notification->delay(now()->addMinutes(1)));
return new CheckoutResource($item);
}
}

View File

@ -0,0 +1,149 @@
<?php
namespace App\Repositories\Member\Voucher;
use App\Models\Voucher;
use App\Models\Customer;
use App\Models\User;
use App\Models\Cart;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
use Carbon\Carbon;
class VoucherRepository
{
public function getList($request)
{
$user_id = auth()->user()->id;
$customer = Customer::where("user_id", $user_id)->first();
$model = Voucher::where('customer_id', (int) @$customer->id)
->where('used_at', null)
->where('expired_at','>=',Carbon::now())
->orderBy('created_at','desc')->paginate($request->limit);
return $model;
}
public function getValidByItems($carts, $customer_id)
{
$item_reference_ids = $carts->pluck("item_reference_id")->toArray();
// collect voucher ids valids
if (count($item_reference_ids) == 0){
$item_reference_ids[] = 0;
}
$item_ids = implode(',', $item_reference_ids);
$ids = DB::select("SELECT distinct vouchers.id from vouchers
left join voucher_events on vouchers.voucher_event_id = voucher_events.id
left join voucher_items on voucher_items.voucher_event_id = voucher_events.id
where used_at is null and
(expired_at > now() or expired_at is null) and
customer_id = ? and (
redeem_point > 0 or
( vouchers.voucher_event_id is null and vouchers.item_reference_id is null) or
( vouchers.voucher_event_id is not null and voucher_items.item_reference_id is null) or
( vouchers.item_reference_id is not null and vouchers.item_reference_id in ($item_ids)) or
( vouchers.voucher_event_id is not null and voucher_items.item_reference_id in ($item_ids))
)",[$customer_id]);
$ids = collect($ids)->pluck("id")->toArray();
return $ids;
}
public function getSum($params)
{
$user_id = auth()->user()->id;
$customer = Customer::where("user_id", $user_id)->firstOrFail();
$voucher_ids = $params["voucher_ids"];
// collect item_reference_ids
$cart_ids = @$params["cart_ids"] ?? [];
$carts = Cart::where("user_id", $user_id)
->where(function($query) use ($cart_ids){
if (count($cart_ids)){
$query->whereIn("id", $cart_ids);
}
})
->get();
$ids = $this->getValidByItems($carts, $customer->id);
return Voucher::where('customer_id', (int) @$customer->id)
->where('used_at', null)
->whereIn('id', $ids)
->whereIn('id', $ids)
->orderBy('created_at','desc')
->get();
}
public function getValid($params)
{
$user_id = auth()->user()->id;
$customer = Customer::where("user_id", $user_id)->first();
$limit = $params["limit"] ?? 10;
// collect item_reference_ids
$cart_ids = @$params["cart_ids"] ?? [];
$carts = Cart::where("user_id", $user_id)
->where(function($query) use ($cart_ids){
if (count($cart_ids)){
$query->whereIn("id", $cart_ids);
}
})
->get();
$ids = $this->getValidByItems($carts, $customer->id);
return Voucher::where('customer_id', (int) @$customer->id)
->where('used_at', null)
->orderBy('created_at','desc')->paginate($limit)
->transform(function ($voucher, $key) use ($ids) {
$voucher->is_valid = in_array($voucher->id, $ids);
return $voucher;
});
}
public function detail($id)
{
$model = Voucher::findOrFail($id);
return $model;
}
public function send($id, $data)
{
$model = DB::transaction(function () use ($id, $data) {
$customer = Customer::where("user_id", $data['customer_id'])->firstOrFail();
$model = Voucher::findOrFail($id);
\Log::info("VOUCHER SEND FROM ".$model->customer_id." TO ".$customer->id);
$model->customer_id = $customer->id;
$model->save();
return $model;
});
return $model;
}
public function phoneCheck($data)
{
$model = DB::transaction(function () use ($data) {
$model = Customer::where('phone', $data['phone'])->first();
if ($model->user_id == null) {
throw ValidationException::withMessages([
"phone" => "User dari phone number belum terdaftar"
]);
}
$model = User::findOrfail($model->user_id);
return $model;
});
return $model;
}
}

24
app/ThirdParty/Xendit/Xendit.php vendored Normal file
View File

@ -0,0 +1,24 @@
<?php
namespace App\ThirdParty\Xendit;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Cache;
class Xendit
{
public function createPaymentLink($payload){
$url = "https://api.xendit.co/v2/invoices";
$key = env("XENDIT_PRIVATE_KEY");
$res = Http::withBasicAuth($key, "")
->withBody(json_encode($payload), 'application/json')
->post($url);
if ($res->status() == 200)
return $res->json();
return null;
}
}

View File

@ -37,9 +37,7 @@
</h1>
<form action="{{ route('checkout.shipping.process') }}" method="post">
@csrf
<input type="hidden" name="delivery_method" value="{{ $delivery_method }}">
<input type="hidden" name="address_id" value="{{ $address_id }}">
@if ($delivery_method == 'shipping')
@foreach ($shipping_list as $shipping)
<div class="form-check mb-3">
@ -100,360 +98,137 @@
@section('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
// Delivery method selection
const deliveryOption = document.getElementById('deliveryOption');
const pickupOption = document.getElementById('pickupOption');
const deliveryOptions = document.getElementById('deliveryOptions');
const pickupOptions = document.getElementById('pickupOptions');
const shippingAddress = document.getElementById('shippingAddress');
const continueButton = document.getElementById('continueButton');
// Shipping option selection handler
const shippingRadios = document.querySelectorAll('input[name="shipping_option"]');
console.log('Found shipping radios:', shippingRadios.length);
// Handle delivery method change
deliveryOption.addEventListener('change', function() {
if (this.checked) {
deliveryOptions.style.display = 'block';
pickupOptions.style.display = 'none';
resetPickupSelection();
// Update button text for delivery
document.getElementById('continueButtonText').textContent =
'{{ __('checkout.continue_to_shipping') }}';
// Update hidden input values
document.getElementById('deliveryMethodInput').value = 'delivery';
// Update address ID from selected address
const addressSelect = document.getElementById('addressSelect');
if (addressSelect && addressSelect.value) {
document.getElementById('addressIdInput').value = addressSelect.value;
}
// Show shipping address step and shipping row
const shippingAddressStep = document.getElementById('shippingAddressStep');
const shippingRow = document.getElementById('shipping-row');
if (shippingAddressStep) {
console.log("Showing shipping address step");
shippingAddressStep.style.visibility = 'visible';
shippingAddressStep.style.height = 'auto';
shippingAddressStep.style.overflow = 'visible';
shippingAddressStep.style.margin = '';
shippingAddressStep.style.padding = '';
}
if (shippingRow) {
console.log("Showing shipping row");
shippingRow.style.visibility = 'visible';
shippingRow.style.height = 'auto';
shippingRow.style.overflow = 'visible';
shippingRow.style.margin = '';
shippingRow.style.padding = '';
} else {
console.log("Shipping row not found");
}
}
});
pickupOption.addEventListener('change', function() {
if (this.checked) {
deliveryOptions.style.display = 'none';
pickupOptions.style.display = 'block';
resetDeliverySelection();
// Update button text for pickup
document.getElementById('continueButtonText').textContent =
'{{ __('checkout.continue_to_payment') }}';
// Update hidden input values
document.getElementById('deliveryMethodInput').value = 'pickup';
document.getElementById('addressIdInput').value = '';
// Hide shipping address step and shipping row
const shippingAddressStep = document.getElementById('shippingAddressStep');
const shippingRow = document.getElementById('shipping-row');
console.log("Elements found:", shippingAddressStep, shippingRow);
if (shippingAddressStep) {
console.log("Hiding shipping address step");
shippingAddressStep.style.visibility = 'hidden';
shippingAddressStep.style.height = '0';
shippingAddressStep.style.overflow = 'hidden';
shippingAddressStep.style.margin = '0';
shippingAddressStep.style.padding = '0';
}
if (shippingRow) {
console.log("Hiding shipping row");
shippingRow.style.visibility = 'hidden';
shippingRow.style.height = '0';
shippingRow.style.overflow = 'hidden';
shippingRow.style.margin = '0';
shippingRow.style.padding = '0';
} else {
console.log("Shipping row not found");
}
}
});
// Address selection handler
const addressSelect = document.getElementById('addressSelect');
if (addressSelect) {
// Auto-populate form with first selected address on page load
function populateAddressForm() {
const selectedOption = addressSelect.options[addressSelect.selectedIndex];
if (selectedOption && selectedOption.value) {
// Populate all shipping address form fields
document.getElementById('firstName').value = selectedOption.dataset.firstName || '';
document.getElementById('lastName').value = selectedOption.dataset.lastName || '';
document.getElementById('address').value = selectedOption.dataset.address || '';
document.getElementById('city').value = selectedOption.dataset.city || '';
document.getElementById('state').value = selectedOption.dataset.state || '';
document.getElementById('zip').value = selectedOption.dataset.postcode || '';
document.getElementById('phone').value = selectedOption.dataset.phone || '';
document.getElementById('postcode').value = selectedOption.dataset.postcode || '';
// Update order summary with postcode
if (selectedOption.dataset.postcode) {
updateOrderSummaryWithShipping();
}
}
}
// Populate on page load
populateAddressForm();
// Set initial address ID
if (addressSelect.value) {
document.getElementById('addressIdInput').value = addressSelect.value;
}
addressSelect.addEventListener('change', function() {
const selectedOption = this.options[this.selectedIndex];
if (this.value === 'new' || this.value === '') {
// Clear form fields for new address
document.getElementById('firstName').value = '';
document.getElementById('lastName').value = '';
document.getElementById('address').value = '';
document.getElementById('city').value = '';
document.getElementById('state').value = '';
document.getElementById('zip').value = '';
document.getElementById('phone').value = '';
document.getElementById('postcode').value = '';
// Clear address ID input
document.getElementById('addressIdInput').value = '';
} else {
// Populate form fields with selected address
document.getElementById('firstName').value = selectedOption.dataset.firstName || '';
document.getElementById('lastName').value = selectedOption.dataset.lastName || '';
document.getElementById('address').value = selectedOption.dataset.address || '';
document.getElementById('city').value = selectedOption.dataset.city || '';
document.getElementById('state').value = selectedOption.dataset.state || '';
document.getElementById('zip').value = selectedOption.dataset.postcode || '';
document.getElementById('phone').value = selectedOption.dataset.phone || '';
document.getElementById('postcode').value = selectedOption.dataset.postcode || '';
// Update address ID input
document.getElementById('addressIdInput').value = this.value;
// Update order summary with postcode
if (selectedOption.dataset.postcode) {
updateOrderSummaryWithShipping();
}
}
});
}
// Auto-update order summary when postcode changes
document.getElementById('postcode').addEventListener('input', function() {
if (this.value) {
updateOrderSummaryWithShipping();
}
});
// Store selection for pickup
const storeRadios = document.querySelectorAll('input[name="store"]');
storeRadios.forEach(radio => {
shippingRadios.forEach((radio, index) => {
console.log(`Radio ${index}:`, radio.value, radio.checked);
radio.addEventListener('change', function() {
console.log('Shipping option changed:', this.value, this.checked);
if (this.checked) {
updateOrderSummaryForPickup();
updateOrderSummaryWithShippingCost(this.value);
}
});
});
// Form validation before continue
continueButton.addEventListener('click', function(e) {
const selectedMethod = document.querySelector('input[name="deliveryMethod"]:checked').value;
function updateOrderSummaryWithShippingCost(shippingValue) {
console.log('updateOrderSummaryWithShippingCost called with:', shippingValue);
if (selectedMethod === 'delivery') {
if (!validateDeliveryForm()) {
e.preventDefault();
return;
}
} else if (selectedMethod === 'pickup') {
if (!validatePickupForm()) {
e.preventDefault();
return;
}
}
});
function resetDeliverySelection() {
resetShippingCalculation();
}
function resetPickupSelection() {
document.querySelectorAll('input[name="store"]').forEach(radio => {
radio.checked = false;
});
resetShippingCalculation();
}
function resetShippingCalculation() {
// Reset order summary to original state
const shippingElement = document.querySelector('[data-shipping-cost]');
if (shippingElement) {
shippingElement.style.display = 'none';
// Parse the shipping value: courier|service|cost
const parts = shippingValue.split('|');
console.log('Parsed parts:', parts);
if (parts.length !== 3) {
console.log('Invalid shipping value format');
return;
}
const totalElement = document.getElementById('cart-estimated-total');
if (totalElement && window.originalTotal) {
totalElement.textContent = `Rp ${window.originalTotal}`;
}
}
function updateOrderSummaryWithShipping() {
// Simulate shipping cost calculation based on postcode
const shippingCost = calculateShippingCost(document.getElementById('postcode').value);
const courier = parts[0];
const service = parts[1];
const cost = parseInt(parts[2]);
console.log('Extracted cost:', cost);
// Update order summary
const subtotalElement = document.getElementById('cart-subtotal');
console.log('Subtotal element:', subtotalElement);
if (!subtotalElement) {
console.log('Subtotal element not found');
return;
}
const currentSubtotal = parseFloat(subtotalElement.textContent.replace(/[^\d]/g, ''));
const newTotal = currentSubtotal + shippingCost;
console.log('Current subtotal:', currentSubtotal);
const newTotal = currentSubtotal + cost;
console.log('New total:', newTotal);
// Store original total
if (!window.originalTotal) {
window.originalTotal = subtotalElement.textContent;
console.log('Stored original total:', window.originalTotal);
}
// Update total
const totalElement = document.getElementById('cart-estimated-total');
totalElement.textContent = `Rp ${number_format(newTotal, 0, ',', '.')}`;
console.log('Total element:', totalElement);
if (totalElement) {
totalElement.textContent = `Rp ${number_format(newTotal, 0, ',', '.')}`;
console.log('Updated total to:', totalElement.textContent);
}
// Show shipping cost in summary
showShippingCost(shippingCost);
}
function updateOrderSummaryForPickup() {
// Pickup is usually free
const subtotalElement = document.getElementById('cart-subtotal');
const currentSubtotal = parseFloat(subtotalElement.textContent.replace(/[^\d]/g, ''));
// Store original total
if (!window.originalTotal) {
window.originalTotal = subtotalElement.textContent;
}
// Update total (same as subtotal for pickup)
const totalElement = document.getElementById('cart-estimated-total');
totalElement.textContent = subtotalElement.textContent;
// Show free shipping
showShippingCost(0, true);
}
function calculateShippingCost(postcode) {
// Simple shipping cost calculation based on postcode
// In real implementation, this would call an API
const jakartaPostcodes = ['10000', '10110', '10220', '10310', '10410'];
const bandungPostcodes = ['40111', '40112', '40113', '40114', '40115'];
if (jakartaPostcodes.includes(postcode)) {
return 15000; // Jakarta: Rp 15,000
} else if (bandungPostcodes.includes(postcode)) {
return 25000; // Bandung: Rp 25,000
} else {
return 35000; // Other areas: Rp 35,000
}
showShippingCost(cost);
}
function showShippingCost(cost, isFree = false) {
// Find or create shipping cost element in order summary
let shippingElement = document.querySelector('[data-shipping-cost]');
console.log('showShippingCost called with:', cost, isFree);
if (!shippingElement) {
const orderSummary = document.querySelector('.list-unstyled');
const shippingLi = document.createElement('li');
shippingLi.className = 'd-flex justify-content-between';
shippingLi.setAttribute('data-shipping-cost', '');
shippingLi.innerHTML = `
<span>{{ __('checkout.shipping') }}:</span>
<span class="text-dark-emphasis fw-medium" id="shipping-cost">Rp 0</span>
`;
orderSummary.appendChild(shippingLi);
shippingElement = shippingLi;
// Use existing shipping row in order summary
const shippingRow = document.getElementById('shipping-row');
console.log('Shipping row element:', shippingRow);
if (!shippingRow) {
console.log('Shipping row not found');
return;
}
const costElement = shippingRow.querySelector('span:last-child');
console.log('Cost element:', costElement);
if (!costElement) {
console.log('Cost element not found');
return;
}
const costElement = document.getElementById('shipping-cost');
if (isFree) {
costElement.textContent = '{{ __('checkout.free') }}';
costElement.className = 'text-success fw-medium';
console.log('Set to free shipping');
} else {
costElement.textContent = `Rp ${number_format(cost, 0, ',', '.')}`;
costElement.className = 'text-dark-emphasis fw-medium';
console.log('Set shipping cost to:', costElement.textContent);
}
shippingElement.style.display = 'flex';
// Ensure the shipping row is visible
shippingRow.style.display = 'flex';
console.log('Made shipping row visible');
}
function validateDeliveryForm() {
const postcode = document.getElementById('postcode').value;
const firstName = document.getElementById('firstName').value;
const address = document.getElementById('address').value;
const city = document.getElementById('city').value;
const phone = document.getElementById('phone').value;
if (!postcode) {
alert('{{ __('checkout.enter_postcode') }}');
return false;
}
if (!firstName || !address || !city || !phone) {
alert('{{ __('checkout.fill_required_fields') }}');
return false;
}
return true;
// Initialize shipping cost on page load for checked option
const checkedShippingRadio = document.querySelector('input[name="shipping_option"]:checked');
console.log('Checked shipping radio on load:', checkedShippingRadio);
if (checkedShippingRadio) {
console.log('Initializing with value:', checkedShippingRadio.value);
updateOrderSummaryWithShippingCost(checkedShippingRadio.value);
}
function validatePickupForm() {
const selectedStore = document.querySelector('input[name="store"]:checked');
// Number formatting helper
function number_format(number, decimals, dec_point, thousands_sep) {
number = (number + '').replace(/[^0-9+\-Ee.]/g, '');
var n = !isFinite(+number) ? 0 : +number;
var prec = !isFinite(+decimals) ? 0 : Math.abs(decimals);
var sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep;
var dec = (typeof dec_point === 'undefined') ? '.' : dec_point;
if (!selectedStore) {
alert('{{ __('checkout.select_store') }}');
return false;
var s = '';
var toFixedFix = function(n, prec) {
var k = Math.pow(10, prec);
return '' + Math.round(n * k) / k;
};
s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
if (thousands_sep) {
var re = /(-?\d+)(\d{3})/;
while (re.test(s[0])) {
s[0] = s[0].replace(re, '$1' + sep + '$2');
}
return true;
}
// Number formatting helper
function number_format(number, decimals, dec_point, thousands_sep) {
number = (number + '').replace(/[^0-9+\-Ee.]/g, '');
var n = !isFinite(+number) ? 0 : +number;
var prec = !isFinite(+decimals) ? 0 : Math.abs(decimals);
var sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep;
var dec = (typeof dec_point === 'undefined') ? '.' : dec_point;
var s = '';
var toFixedFix = function(n, prec) {
var k = Math.pow(10, prec);
return '' + Math.round(n * k) / k;
};
s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
if (thousands_sep) {
var re = /(-?\d+)(\d{3})/;
while (re.test(s[0])) {
s[0] = s[0].replace(re, '$1' + sep + '$2');
}
}
if ((s[1] || '').length < prec) {
s[1] = s[1] || '';
s[1] += new Array(prec - s[1].length + 1).join('0');
}
return s.join(dec);
if ((s[1] || '').length < prec) {
s[1] = s[1] || '';
s[1] += new Array(prec - s[1].length + 1).join('0');
}
return s.join(dec);
}
});
</script>
@endsection

View File

@ -8,6 +8,13 @@
<div class="container py-5">
<div class="row pt-1 pt-sm-3 pt-lg-4 pb-2 pb-md-3 pb-lg-4 pb-xl-5">
{{-- show error message --}}
@if(session('error'))
<div class="alert alert-danger">
{{ session('error') }}
</div>
@endif
<!-- Delivery info (Step 1) -->
<div class="col-lg-8 col-xl-7 mb-5 mb-lg-0">
<div class="d-flex flex-column gap-5 pe-lg-4 pe-xl-0">