Modul Wp3 Update
Modul Wp3 Update
MODUL
PROGRAMMING III
Segala puji dan syukur kami panjatkan ke hadirat Tuhan Yang Maha Esa, karena berkat rahmat-
Nya penulisan modul Mata Kuliah Web Programming III ini dapat terselesaikan dengan baik.
Modul ini disusun untuk memenuhi kebutuhan mahasiswa dalam mata kuliah Web
Programming III, yang disajikan dalam bentuk praktikum dan diharapkan dapat membekali
mahasiswa dalam memahami pembuatan web menggunakan Framework Laravel.
Modul Web Programming III ini membahas materi yang dibatasi hingga pembuatan halaman
Administrator (Back-End). Di akhir perkuliahan, mahasiswa diharapkan mampu
mengimplementasikan materi yang telah dipelajari dalam bentuk final project yang harus
dipresentasikan sebagai syarat kelulusan mata kuliah Web Programming II. Teknik penyajian
dalam modul ini dilakukan secara terpadu dan sistematis.
Kami menyadari bahwa modul ini masih memiliki banyak kekurangan. Oleh karena itu, kami
dengan lapang dada menerima segala masukan dan kritik yang konstruktif dari berbagai pihak
demi kesempurnaan modul ini di masa yang akan datang. Semoga modul ini dapat bermanfaat
bagi para pembaca.
Tim Penulis
DAFTAR ISI
COVER.................................................................................................................................................. i
KATA PENGANTAR .......................................................................................................................... ii
DAFTAR ISI ........................................................................................................................................iii
Minggu Ke-1 Instalasi & Konfigurasi Pada Laravel ............................................................................ 1
Minggu Ke-2 Mengenal Controller, Function, Route & View .............................................................. 5
Minggu Ke-3 Manajemen Basis Data: Konfigurasi Databases, Migration, dan Seeders ..................... 32
Minggu Ke-4 CRUD (Create, Read, Update, Delete) ......................................................................... 48
Minggu Ke-5 Authenticate (Logika Login) & Pengujian Unit ............................................................ 52
Minggu Ke-6 Template HTML, CSS, Bootstrap, dan JavaScript ....................................................... 59
Minggu Ke-7 Implementasi DataTable dan Form dengan Template................................................... 66
Minggu Ke-9 Manajemen Data Master ............................................................................................... 73
Minggu Ke-10 Data Join Tabel Part 1 .................................................... Error! Bookmark not defined.
Minggu Ke-11 Data Join Tabel Part 2 .................................................... Error! Bookmark not defined.
Minggu Ke-12 Laporan Data Master ...................................................... Error! Bookmark not defined.
Minggu Ke-13 Persentasi Tugas Kelompok...................................................................................... 117
Minggu Ke-14 Persentasi Tugas Kelompok...................................................................................... 118
Minggu Ke-15 Persentasi Tugas Kelompok...................................................................................... 119
Minggu Ke-1
Pengenalan Web Frontend Development
Sebelum memulai materi pada Web Programming III, silakan lakukan pembaruan Portofolio,
yaitu dengan menambahkan unit unjuk kerja kompetensi yang telah dikerjakan pada Web
Programming II di setiap pertemuannya. Dengan demikian, Portofolio yang Anda miliki akan
menjadi lebih lengkap, detail, dan sempurna sesuai dengan standar Impentasi Unit Kompetensi
Software Development.
✓ Web Programming II berfocus pad halaman backend
✓ Web Programming III berfocus pada halaman frontend
Berikut kami tampilkan kembali salah satu skema dari Lembaga Sertifikasi Profesi (LSP)
Universitas Bina Sarana Informatika, yaitu skema Programmer pada Tabel 1.1 dan skema
Analis Program pada Tabel 1.2
Tabel 1.1
Skema Programmer
Sebelumnya, pada Modul Web Programming II, kita telah menggunakan template Matrix
Admin. Pada Modul Web Programming III kali ini, kita akan beralih menggunakan template
E-Shop. E-Shop adalah template website e-commerce gratis yang responsif, sehingga dapat
diakses dengan baik di berbagai perangkat. Template ini dapat diunduh melalui tautan berikut:
https://bit.ly/LaravelWebPro3
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Produk;
Dimana kita diminta untuk membuat folder v_beranda dengan file index.blade.php dengan
script sebagai berikut:
@extends('v_layouts.app')
@section('content')
<!-- template -->
<!-- end template-->
@endsection
// Frontend
Route::get('/beranda', [BerandaController::class, 'index'])->name('beranda');
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\BerandaController;
use App\Http\Controllers\LoginController;
use App\Http\Controllers\UserController;
use App\Http\Controllers\KategoriController;
use App\Http\Controllers\ProdukController;
Route::get('/', function () {
// return view('welcome');
return redirect()->route('beranda');
// Frontend
Route::get('/beranda', [BerandaController::class, 'index'])->name('beranda');
Banner
Footer
Kita ubah direktori gambar ke Folder banner yang sesuai dengan tema, misalnya sebagai
contoh yakni penjualan jajanan tradisional sebagi toko makanan khas Indonesia. Bisa di
dowonload https://bit.ly/LaravelWebPro3 dengan folder Template\banner.rar & ekstrak
dengan direktori public\frontend\banner sebagai berikut:
Catatan:
Gunakan ukuran gambar banner yang sesuai dengan ukuran asli gambar banner yang
disediakan dalam template.
Tampilan produk merupakan elemen utama dalam e-commerce yang berperan penting dalam
memberikan pengalaman pengguna (UX) yang baik. Pengguna harus dapat dengan mudah
melihat daftar produk, mendapatkan informasi yang jelas, serta melakukan pencarian dan filter
sesuai kebutuhan mereka. Oleh karena itu, desain antarmuka harus responsif, menarik, dan
intuitif agar meningkatkan kenyamanan serta kemudahan dalam berbelanja.
Pada materi ini, kita akan mempelajari cara menampilkan daftar produk dengan tata letak yang
menarik, serta memastikan tampilan responsif agar dapat diakses dengan baik di berbagai
perangkat. Implementasi ini akan menjadi dasar dalam membangun pengalaman pengguna
yang lebih interaktif dan fungsional pada website e-commerce.
1. Tambahkan data produk melalui backend minimal 8 record misalnya 7 record dengan status
Publis dan 1 status Blok
<a href="#">
<button class="main-btn quick-view"><i class="fa fa-search-
plus"></i> Detail Produk</button>
</a>
<img src="{{ asset('storage/img-produk/thumb_md_' . $row->foto) }}"
alt="">
</div>
<div class="product-body">
<h3 class="product-price"> Rp. {{ number_format($row->harga, 0, ',',
'.') }} <span
class="product-old-price">{{ $row->kategori->nama_kategori
}}</span></h3>
6. Berikut hasil perulangan fungsi index() yang ada pada BerandaController.php, dimana
produk yang ditampilkan hanya 6 produk terbaru saja.
@extends('v_layouts.app')
@section('content')
<!-- template -->
<div class="product-btns">
<form action="#" method="post"
style="display: inline-block;">
@csrf
<button type="submit" class="primary-btn add-to-cart"><i class="fa
fa-shopping-cart"></i>
Pesan</button>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- /Product Details -->
9. Saat diklik salah satu produk dengan aksi Detail Produk maka akan tampilan ke halaman
detail produk. Namun, banner tetap tampil. Kita dapat menyembunyikannya banner, dimana
banner hanya akan muncul pada halaman beranda saja.
b. Kategori a. Banner
@endif
a. Banner
b. Kategori, cari komentar <!-- category nav --> kemudian berikut perubahan pada blok kode
<!-- category nav --> sampai <!-- /category nav -->
@php
$kategori = DB::table('kategori')->orderBy('nama_kategori', 'asc')->get();
@endphp
@if (request()->segment(1) == '' || request()->segment(1) == 'beranda')
<!-- category nav -->
<div class="category-nav">
<span class="category-header">Kategori <i class="fa fa-list"></i></span>
<ul class="category-list">
</div>
@else
<div class="category-nav show-on-click">
<span class="category-header">Kategori <i class="fa fa-list"></i></span>
<ul class="category-list">
@foreach ($kategori as $row)
<li><a href="{{ route('produk.kategori', $row->id) }}">{{ $row->nama_kategori
}}</a></li>
@endforeach
</ul>
</div>
<!-- /category nav -->
@endif
Ubah menjadi
filter kategori
Lakukan pada pencarian yakni Filter By Brand kemudian ganti menjadi Filter Kategori
dengan script berikut:
<!-- aside widget -->
<div class="aside">
<h3 class="aside-title">Filter Kategori</h3>
Sehingga untuk melakukan filter berdasarkan kategori dapat dilakukkan selain dari Menu
Header dan kali ini kita akan sesuaikan halaman sidebar:
</div>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must
come *after* these tags -->
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<!-- HEADER -->
<header>
<!-- top Header -->
<div id="top-header">
<div class="container">
<div class="pull-left">
<span>Selamat datang di toko online</span>
</div>
</div>
</div>
<!-- /top Header -->
</ul>
</div>
<!-- menu nav -->
</div>
</div>
<!-- /container -->
</div>
<!-- /NAVIGATION -->
</body>
</html>
API Google bekerja dengan cara mengirimkan permintaan (request) dari aplikasi ke server
Google, dan server Google akan mengirimkan respons (response) yang berisi data atau hasil
dari layanan yang diminta. Misalnya, Google Maps API dapat digunakan untuk menampilkan
peta di dalam aplikasi, atau Google Drive API dapat dimanfaatkan untuk memungkinkan
pengguna menyimpan dan mengakses file mereka di Google Drive langsung dari aplikasi.
Google Client ID dan Client Secret adalah dua komponen penting yang diberikan oleh Google
Developers Console untuk mengidentifikasi dan mengamankan aplikasi saat berinteraksi
dengan layanan Google. Client ID berfungsi sebagai identifier unik yang digunakan untuk
mengenali aplikasi yang meminta akses ke data pengguna melalui OAuth 2.0. Sedangkan
Client Secret adalah kode rahasia yang bertindak sebagai bukti bahwa permintaan yang
dikirim ke Google berasal dari aplikasi yang sah. Keduanya digunakan bersama dalam proses
autentikasi dan otorisasi, seperti memungkinkan pengguna login dengan akun Google atau
mengakses data pengguna dari layanan Google seperti Gmail atau Google Drive.
Proses kerja dimulai ketika aplikasi mengarahkan pengguna ke halaman login Google dengan
menyertakan Client ID. Setelah pengguna berhasil login dan memberikan izin, Google akan
mengirimkan kode otorisasi ke aplikasi melalui Authorized Redirect URI. Selanjutnya, aplikasi
menukar kode otorisasi tersebut dengan access token menggunakan Client ID dan Client
Secret. Access token inilah yang memungkinkan aplikasi untuk mengakses data pengguna dari
layanan Google.
2. Pilih My Project
6. Kali ini, project telah berganti ke project yang dituju, yaitu tokoonline-laravel3. Kemudian
klik menu pada sebelah kiri project.
16. Klik Web client 1 untuk mendapatkan informasi seperti Client ID & Client Secrets
<?php
namespace App\Models;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $table = "customer";
protected $fillable = [
'user_id',
'google_id',
'google_token',
'alamat',
'pos',
];
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Customer;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Laravel\Socialite\Facades\Socialite;
use Illuminate\Support\Facades\Hash;
if (!$registeredUser) {
// Buat user baru
$user = User::create([
//API Google
Route::get('/auth/redirect', [CustomerController::class, 'redirect'])-
>name('auth.redirect');
25. Sehingga pada saat klik tombol Login meminta akun gmail, pilih gmail masing-masing.
Pastikan gmail belum pernah digunakan pada tabel user.
Klik Lanjut
Sebagai contoh, akun Gmail [email protected] berhasil tersimpan pada tabel user dengan
detail sebagai berikut: id = 3, nama = Sopian Aji, email = [email protected], role = 2, dan
password yang telah terenkripsi. Selanjutnya, periksa tabel customer untuk memastikan
apakah id = 3 sudah ditambahkan ke dalam tabel tersebut. Dan pada tabel customer sudah
berhasil tersimpan dengan baik.
Manajemen akun pengguna mencakup pengelolaan dari dua sisi, yaitu frontend dan backend.
Pada sisi frontend, pengguna umum dapat mengakses dan menggunakan aplikasi, sementara
pada sisi backend, pengelola aplikasi dapat memantau serta mengelola data pelanggan. Dengan
demikian, pengelolaan akun dilakukan secara dua arah.
Menjadi:
c. Halaman dalam keadaan sudah login, dengan tombol Keluar yang dapat digunakan untuk
keluar dari aplikasi.
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-body">
<div class="table-responsive">
<table id="zero_config" class="table table-striped table-bordered">
<thead>
<tr>
<th>No</th>
<th>Nama</th>
<th>Email</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@foreach ($index as $row)
<tr>
<td> {{ $loop->iteration }} </td>
<td> {{$row->user->nama}} </td>
<td> {{$row->user->email}} </td>
<td>
<a href="#" title="Ubah Data">
<button type="button" class="btn btn-warning btn-
sm"><i class="fas fa-eye"></i> Detail</button>
</a>
<a href="#" title="Ubah Data">
<button type="button" class="btn btn-cyan btn-
sm"><i class="far fa-edit"></i> Ubah</button>
</a>
</div>
</div>
</div>
</div>
Silakan jadikan aksi seperti Detail, Ubah, dan Hapus sebagai tugas mandiri untuk dilengkapi,
sehingga halaman backend dapat mengelola data dengan lebih baik.
Sebelum masuk ke sesi praktik, penting untuk memahami dua konsep utama dalam Laravel
yang sering digunakan untuk mengelola rute dan keamanan aplikasi, yaitu Route Group dan
Middleware
Route Group adalah fitur di Laravel yang memungkinkan kita mengelompokkan beberapa
rute dengan konfigurasi yang sama, seperti prefix, middleware, atau namespace. Dengan
Route Group, kode menjadi lebih rapi dan mudah dikelola, terutama saat bekerja dengan
banyak rute.
Misalnya, jika kita memiliki beberapa rute dalam halaman admin, kita bisa
mengelompokkannya dalam satu grup dengan prefix /admin, sehingga semua rute di dalamnya
otomatis memiliki awalan admin/ tanpa perlu menulisnya satu per satu.
Middleware adalah lapisan keamanan dalam Laravel yang digunakan untuk memproses
permintaan (request) sebelum mencapai aplikasi. Middleware sering digunakan untuk
autentikasi, otorisasi, log aktivitas, atau pengolahan data sebelum dikirim ke aplikasi.
Contoh umum penggunaan Middleware adalah memastikan bahwa hanya pengguna yang
sudah login yang dapat mengakses halaman admin. Laravel menyediakan middleware bawaan
seperti auth, tetapi kita juga bisa membuat middleware sendiri sesuai kebutuhan.
Setelah memahami konsep ini, kita akan langsung masuk ke praktik bagaimana menerapkan
Route Group dan Middleware dalam Laravel untuk meningkatkan efisiensi dan keamanan
aplikasi.
if ($request->email != $customer->user->email) {
$rules['email'] = 'required|max:255|email|unique:customer';
}
if ($request->alamat != $customer->alamat) {
$rules['alamat'] = 'required';
}
if ($request->pos != $customer->pos) {
$rules['pos'] = 'required';
}
$customer->user->update($validatedData);
$customer->update([
'alamat' => $request->input('alamat'),
'pos' => $request->input('pos'),
]);
return redirect()->route('customer.akun', $id)->with('success', 'Data berhasil
diperbarui');
}
Jangan lupa untuk menambahkan folder img-customer pada direktori storage/app/public/ img-
customer
7. Kemudian kita buatkan tampilan viewnya, yakni buat folder dengan nama v_customer
dengan direktori sebagai berikut resources/views/v_customer/edit.blade.php
@extends('v_layouts.app')
@section('content')
<!-- template -->
<div class="row">
<div class="col-md-12">
<div class=" billing-details">
<div class="section-title">
<h3 class="title"> {{$judul}} </h3>
</div>
<div class="row">
<div class="col-md-12">
<!-- msgError -->
@if(session()->has('success'))
<div class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-
label="Close"><span aria-hidden="true">×</span></button>
<strong>{{ session('success') }}</strong>
</div>
@endif
<!-- end msgError -->
<!-- msgError -->
@if(session()->has('msgError'))
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-
label="Close"><span aria-hidden="true">×</span></button>
<strong>{{ session('msgError') }}</strong>
</div>
@endif
<!-- end msgError -->
</div>
<form action="{{ route('customer.akun, $edit->user->id) }}" method="post"
enctype="multipart/form-data">
@method('put')
@csrf
<div class="form-group">
<label>Nama</label>
<input type="text" name="nama" value="{{ old('nama', $edit-
>user->nama) }}" class="form-control @error('nama') is-invalid @enderror"
placeholder="Masukkan Nama">
@error('nama')
<span class="invalid-feedback alert-danger" role="alert">
{{ $message }}
</span>
@enderror
</div>
<div class="form-group">
<label>Email</label>
<input type="text" name="email" value="{{ old('email', $edit-
>user->email) }}" class="form-control @error('email') is-invalid @enderror"
placeholder="Masukkan Email">
@error('email')
<span class="invalid-feedback alert-danger" role="alert">
{{ $message }}
</span>
@enderror
</div>
<div class="form-group">
<label>HP</label>
<input type="text" onkeypress="return hanyaAngka(event)"
name="hp" value="{{ old('hp', $edit->user->hp) }}" class="form-control @error('hp') is-
invalid @enderror" placeholder="Masukkan Nomor HP">
@error('hp')
<span class="invalid-feedback alert-danger" role="alert">
{{ $message }}
</span>
@enderror
</div>
<div class="form-group">
<label>Alamat</label><br>
<textarea name="alamat" class="form-control @error('alamat')
is-invalid @enderror">{{ old('alamat',$edit->alamat) }}</textarea>
@error('alamat')
<span class="invalid-feedback alert-danger" role="alert">
{{ $message }}
<div class="form-group">
<label>Kode Pos</label>
<input type="text" name="pos" value="{{ old('pos', $edit->pos)
}}" class="form-control @error('pos') is-invalid @enderror" placeholder="Masukkan Nomor
Resi">
@error('pos')
<span class="invalid-feedback alert-danger" role="alert">
{{ $message }}
</span>
@enderror
</div>
</div>
<br>
<div class="col-md-12">
<br>
<div class="pull-left">
<button type="submit" class="primary-btn">Simpan</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
Kali ini kita akan menggunakan group, sehingga kita dapat mengelompokkan route yang
terkait dengan customer menjadi sebagai berikut
Keranjang belanja (shopping cart) adalah fitur penting dalam aplikasi e-commerce yang
memungkinkan pengguna menyimpan produk yang ingin dibeli sebelum melakukan transaksi.
Manajemen keranjang belanja mencakup berbagai aspek, seperti menambah, mengupdate, dan
menghapus produk dalam keranjang, serta menghitung total harga secara otomatis.
Manajemen keranjang belanja yang baik akan meningkatkan pengalaman pengguna serta
memastikan proses transaksi berjalan dengan lancar. Sekarang, mari kita masuk ke pembahasan
lebih lanjut mengenai implementasi fitur ini
1. Pertama, yang akan kita lakukan adalah membuat tabel order dan order_item untuk
menyimpan data proses transaksi. Membuat tabel order sebagai barikut:
php artisan make:migration create_order_table
Model Order:
class Order extends Model
{
public $timestamps = true;
protected $table = "order";
protected $fillable = [
'customer_id',
'total_harga',
'status',
'noresi',
'kurir',
'layanan_ongkir',
'biaya_ongkir',
'estimasi_ongkir',
'total_berat',
'alamat',
'pos'
];
Model OrderItem:
class OrderItem extends Model
{
public $timestamps = true;
protected $table = "order_item";
protected $fillable = ['order_id', 'produk_id', 'quantity', 'harga'];
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\Customer;
use App\Models\Produk;
use App\Models\Order;
use App\Models\OrderItem;
$order = Order::firstOrCreate(
['customer_id' => $customer->id, 'status' => 'pending'],
['total_harga' => 0]
);
$orderItem = OrderItem::firstOrCreate(
['order_id' => $order->id, 'produk_id' => $produk->id],
['quantity' => 1, 'harga' => $produk->harga]
);
if (!$orderItem->wasRecentlyCreated) {
$orderItem->quantity++;
$orderItem->save();
}
$order->total_harga += $produk->harga;
$order->save();
5. Pada function viewCart kita diminta untuk membuat file cart.blade.php dengan direktori
resources/views/v_order/cart.blade.php
@extends('v_layouts.app')
@section('content')
<!-- template -->
<div class="col-md-12">
<div class="order-summary clearfix">
<div class="section-title">
<p>KERANJANG</p>
<h3 class="title">Keranjang Belanja</h3>
</div>
<!-- msgSuccess -->
@if(session()->has('success'))
<div class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-
label="Close"><span aria-hidden="true">×</span></button>
<strong>{{ session('success') }}</strong>
</div>
@endif
<!-- end msgSuccess -->
<!-- msgError -->
@if(session()->has('error'))
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-
label="Close"><span aria-hidden="true">×</span></button>
<strong>{{ session('error') }}</strong>
</div>
@endif
<!-- end msgError -->
@if($order && $order->orderItems->count() > 0)
<table class="shopping-cart-table table">
<thead>
<tr>
<th>Produk</th>
<th></th>
<th class="text-center">Harga</th>
<th class="text-center">Quantity</th>
<th class="text-center">Total</th>
<th class="text-right"></th>
</tr>
</thead>
<tbody>
@php
$totalHarga = 0;
$totalBerat = 0;
6. Kemudian, tambahkan tautan pada tampilan produk di semua aksi "Pesan" agar pengguna
dapat melakukan pemesanan. Misalnya pada halaman beranda pada direktori
resources/views/v_beranda/index.blade.php
7. Saat customer melakukan pemesanan namun belum login, maka akan diarahkan ke halaman
login dengan opsi untuk masuk menggunakan akun Gmail. Namun, jika kondisi sudah login
saat klik pesan maka akan masuk ke keranjang belanja
Pada pertemuan ini, kita akan membahas topik yang sangat menarik dan penting, terutama bagi
kalian yang berkecimpung dalam pengembangan aplikasi e-commerce atau sistem yang
membutuhkan informasi ongkos kirim. Kita akan mempelajari Integrasi API RajaOngkir untuk
Menghitung Ongkos Kirim. Situs resmi RajaOngkir di https://rajaongkir.com.
RajaOngkir adalah salah satu layanan populer di Indonesia yang menyediakan data ongkos
kirim dari berbagai ekspedisi pengiriman, seperti JNE, TIKI, POS Indonesia, dan masih banyak
lagi lebih detail dapat dilihat pada tautan https://rajaongkir.com/dokumentasi. Dengan
mengintegrasikan API RajaOngkir, kita bisa mendapatkan informasi ongkir secara real-time,
mulai dari tarif pengiriman, estimasi waktu pengiriman, hingga tracking resi.
RAJAONGKIR_API_KEY=your_api_key
RAJAONGKIR_BASE_URL=https://api.rajaongkir.com/starter
2. Secara default, Laravel sudah memiliki dependency yang dibutuhkan untuk melakukan
HTTP request, yaitu Laravel HTTP Client (berbasis Guzzle). Namun, jika ada masalah
dengan Http::get(), maka kita bisa memastikan Guzzle HTTP Client sudah terinstal dengan
menjalankan perintah berikut:
composer require guzzlehttp/guzzle
Route::get('/rajaongkir_list1', function () {
$response = Http::withHeaders([
'key' => '794a5d197b9cb469ae958ed043ccf921'
])->get('https://api.rajaongkir.com/starter/province'); //bisa ganti dengan 'province'
atau 'city'
// $statusCode = $response->json()['rajaongkir']['status']['code'];
// $provisi = $response->json()['rajaongkir']['results'];
dd($response->json());
});
Route::get('/rajaongkir_list2', function () {
$response = Http::withHeaders([
'key' => '794a5d197b9cb469ae958ed043ccf921'
])->get('https://api.rajaongkir.com/starter/province'); //bisa ganti dengan 'province'
atau 'city'
return $response->json();
});
4. Setelah API Key dapat berjalan dengan baik, kita akan menjalankan aplikasi menggunakan
form sederhana tanpa template. Buatlah controller dengan nama RajaOngkirController.php.
php artisan make:controller RajaOngkirController
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
return response()->json($response->json());
}
return response()->json($response->json());
}
$response = Http::withHeaders([
'key' => env('RAJAONGKIR_API_KEY')
])->post(env('RAJAONGKIR_BASE_URL') . '/cost', [
'origin' => $origin,
'destination' => $destination,
'weight' => $weight,
'courier' => $courier,
]);
6.
# API RajaOngkir
RAJAONGKIR_API_KEY=794a5d197b9cb469ae958ed043ccf921
RAJAONGKIR_BASE_URL=https://api.rajaongkir.com/starter
<head>
<title>Cek Ongkir</title>
<meta name="csrf-token" content="{{ csrf_token() }}">
</head>
<body>
<form id="ongkirForm">
<select name="province" id="province">
<option value="">Pilih Provinsi</option>
</select>
<select name="city" id="city">
<option value="">Pilih Kota</option>
</select>
<input type="number" name="weight" id="weight" placeholder="Berat (gram)">
<select name="courier" id="courier">
<option value="">Pilih Kurir</option>
<option value="jne">JNE</option>
<option value="tiki">TIKI</option>
<option value="pos">POS Indonesia</option>
</select>
<button type="submit">Cek Ongkir</button>
</form>
<div id="result"></div>
<script>
document.addEventListener('DOMContentLoaded', function() {
fetch('/provinces')
.then(response => response.json())
.then(data => {
console.log('Provinces data:', data);
if (data.rajaongkir.status.code === 200) {
let provinces = data.rajaongkir.results;
let provinceSelect = document.getElementById('province');
provinces.forEach(province => {
let option = document.createElement('option');
document.getElementById('province').addEventListener('change', function() {
let provinceId = this.value;
fetch(`/cities?province_id=${provinceId}`)
.then(response => response.json())
.then(data => {
console.log('Cities data:', data);
if (data.rajaongkir.status.code === 200) {
let cities = data.rajaongkir.results;
let citySelect = document.getElementById('city');
citySelect.innerHTML = '';
cities.forEach(city => {
let option = document.createElement('option');
option.value = city.city_id;
option.textContent = city.city_name;
citySelect.appendChild(option);
});
} else {
console.error('Failed to fetch cities',
data.rajaongkir.status.description);
}
})
.catch(error => {
console.error('Error fetching cities:', error);
});
});
document.getElementById('ongkirForm').addEventListener('submit',
function(event) {
event.preventDefault();
let origin = 501; // Gnati Kode kota asal
let destination = document.getElementById('city').value;
let weight = document.getElementById('weight').value;
let courier = document.getElementById('courier').value;
fetch('/cost', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-
token"]').getAttribute('content')
},
body: JSON.stringify({
origin: origin,
destination: destination,
weight: weight,
courier: courier
})
})
.then(response => response.json())
.then(data => {
console.log('Cost data:', data);
if (data.rajaongkir.status.code === 200) {
let result = data.rajaongkir.results[0].costs;
</html>
Pada pertemuan ini, kita akan menyempurnakan materi dari pertemuan sebelumnya, yaitu
keranjang belanja (pertemuan 6) dan penghitungan ongkir (pertemuan 7). Dengan demikian,
kita dapat memproses pesanan dengan lebih lengkap, termasuk menambah dan menghapus
produk yang akan dipesan, memilih jasa pengiriman sesuai keinginan, serta melihat riwayat
pesanan yang telah dilakukan.
Pembahasan ini akan membantu kita memahami alur pemesanan dan checkout produk,
sehingga sistem yang dibangun dapat berfungsi dengan baik dalam mengelola transaksi
pelanggan.
1. Buka kembali dan kita update script pada file cart.blade.php dengan direktori
resources/views/v_order/cart.blade.php:
@extends('v_layouts.app')
@section('content')
<!-- template -->
<div class="col-md-12">
<div class="order-summary clearfix">
<div class="section-title">
<p>KERANJANG</p>
<h3 class="title">Keranjang Belanja</h3>
</div>
<!-- msgSuccess -->
@if(session()->has('success'))
<div class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-
label="Close"><span aria-hidden="true">×</span></button>
<strong>{{ session('success') }}</strong>
</div>
@endif
<!-- end msgSuccess -->
<!-- msgError -->
@if(session()->has('error'))
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-
label="Close"><span aria-hidden="true">×</span></button>
<strong>{{ session('error') }}</strong>
</div>
@endif
<!-- end msgError -->
@if($order && $order->orderItems->count() > 0)
<table class="shopping-cart-table table">
<thead>
<tr>
<th>Produk</th>
<th></th>
<th class="text-center">Harga</th>
<th class="text-center">Quantity</th>
<th class="text-center">Total</th>
<th class="text-right"></th>
</tr>
</thead>
<tbody>
@php
$totalHarga = 0;
$totalBerat = 0;
@endphp
});
});
3. sehingga kita perlu menambahkan function updateCart dan removeFromCart dari pada
controller yakni OrderController, dan berikut script lengkap pada OrderController:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\Customer;
use App\Models\Produk;
use App\Models\Order;
use App\Models\OrderItem;
$order = Order::firstOrCreate(
['customer_id' => $customer->id, 'status' => 'pending'],
['total_harga' => 0]
);
$orderItem = OrderItem::firstOrCreate(
['order_id' => $order->id, 'produk_id' => $produk->id],
['quantity' => 1, 'harga' => $produk->harga]
);
$order->total_harga += $produk->harga;
$order->save();
if ($order) {
$orderItem = OrderItem::where('order_id', $order->id)->where('produk_id', $id)-
>first();
if ($orderItem) {
$order->total_harga -= $orderItem->harga * $orderItem->quantity;
$orderItem->delete();
Klik tombol Update untuk memperbarui Total, sedangkan tombol x digunakan untuk
menghapus data, jika kita cek pada database data sudah masuk di tabel order dan order_item
Menjadi
<form action="{{ route('order.select-shipping') }}" method="post">
@csrf
<input type="hidden" name="total_price" value="{{ $totalHarga }}">
<input type="hidden" name="total_weight" value="{{ $totalBerat }}">
<div class="pull-right">
<button class="primary-btn">Pilih Pengiriman</button>
</div>
</form>
5. Sehingga kita update kembali routes\web.php, pada group route untuk is.customer:
Route::post('select-shipping', [OrderController::class, 'selectShipping'])-
>name('order.select-shipping');
Route::post('update-ongkir', [OrderController::class, 'updateOngkir'])-
>name('order.update-ongkir');
Route::get('select-payment', [OrderController::class, 'selectPayment'])-
>name('order.selectpayment');
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\Customer;
use App\Models\Produk;
use App\Models\Order;
use App\Models\OrderItem;
$order = Order::firstOrCreate(
['customer_id' => $customer->id, 'status' => 'pending'],
['total_harga' => 0]
);
$orderItem = OrderItem::firstOrCreate(
['order_id' => $order->id, 'produk_id' => $produk->id],
['quantity' => 1, 'harga' => $produk->harga]
);
if (!$orderItem->wasRecentlyCreated) {
$orderItem->quantity++;
$orderItem->save();
}
$order->total_harga += $produk->harga;
$order->save();
if ($order) {
$orderItem = OrderItem::where('order_id', $order->id)->where('produk_id', $id)-
>first();
if ($orderItem) {
$order->total_harga -= $orderItem->harga * $orderItem->quantity;
$orderItem->delete();
if ($order->total_harga <= 0) {
$order->delete();
} else {
$order->save();
}
}
}
return redirect()->route('order.cart')->with('success', 'Produk berhasil dihapus
dari keranjang');
}
if ($order) {
// Simpan data ongkir ke dalam order
$order->kurir = $request->input('kurir');
$order->layanan_ongkir = $request->input('layanan_ongkir');
$order->biaya_ongkir = $request->input('biaya_ongkir');
$order->estimasi_ongkir = $request->input('estimasi_ongkir');
$order->total_berat = $request->input('total_berat');
$order->alamat = $request->input('alamat') . ', <br>' . $request-
>input('city_name') . ', <br>' . $request->input('province_name');
$order->pos = $request->input('pos');
$order->save();
// Simpan ke session flash agar bisa diakses di halaman tujuan
return redirect()->route('order.selectpayment')
->with('origin', $origin)
->with('originName', $originName);
}
✓ CSRF Token adalah sebuah string unik yang dibuat oleh server untuk melindungi aplikasi web dari
serangan CSRF.
✓ Token ini harus dikirim bersama setiap permintaan yang memodifikasi data di server supaya server
bisa memverifikasi bahwa permintaan tersebut benar-benar berasal dari pengguna yang sah dan
bukan dari pihak luar yang mencoba menyerang.
✓ Dengan menempatkan token ini di dalam elemen <meta>, JavaScript pada halaman bisa membaca
token ini dan menyertakannya dalam header HTTP (misalnya X-CSRF-TOKEN) pada setiap
permintaan AJAX.
✓ Sintaks {{ csrf_token() }} biasanya digunakan di framework seperti Laravel untuk menampilkan
token CSRF yang di-generate server ke halaman HTML.
8. Untuk view dari function selectShipping maka kita tambahkan pada file
select_shipping.blade.php didirektori resources/views/v_order/select_shipping.blade.php
sebagai berikut:
@extends('v_layouts.app')
@section('content')
<!-- template -->
<div class="col-md-12">
<div class="order-summary clearfix">
<div class="section-title">
<p>PENGIRIMAN</p>
<h3 class="title">Pilih Pengiriman</h3>
</div>
<form id="shippingForm">
<!-- Kota Asal -->
<input type="hidden" id="city_origin" name="city_origin" value="">
<input type="hidden" id="city_origin_name" name="city_origin_name" value="">
<!-- /Kota Asal -->
<div class="form-group">
<label for="province">Provinsi Tujuan:</label>
<select name="province" id="province" class="input">
<option value="">Pilih Provinsi Tujuan</option>
<!-- Data Provinsi Tujuan akan dimuat dengan JavaScript -->
</select>
</div>
<div class="form-group">
<label for="city">Kota Tujuan:</label>
<select name="city" id="city" class="input">
<option value="">Pilih Kota Tujuan</option>
<!-- Data Kota Tujuan akan dimuat dengan JavaScript -->
</select>
</div>
<input type="hidden" name="weight" id="weight" value="{{ $totalBerat }}">
<input type="hidden" name="province_name" id="province_name">
<br>
<div id="result">
<table class="shopping-cart-table table">
<thead>
<tr>
<th>Layanan</th>
<th>Biaya</th>
<th>Estimasi Pengiriman</th>
<th>Total Berat</th>
<th>Total Harga</th>
<th>Bayar</th>
</tr>
</thead>
<tbody id="shippingResults">
<!-- Hasil dari pencarian akan dimuat di sini -->
</tbody>
</table>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const originCityCode = 115; //ganti disini untuk kode kota asal
const originCityName = 'Depok'; //ganti disini untuk nama kota asal
document.getElementById('city_origin').value = originCityCode;
document.getElementById('city_origin_name').value = originCityName;
// Load provinces
fetch('/provinces')
.then(response => response.json())
.then(data => {
if (data.rajaongkir.status.code === 200) {
let provinces = data.rajaongkir.results;
let provinceSelect = document.getElementById('province');
provinces.forEach(province => {
let option = document.createElement('option');
option.value = province.province_id;
option.textContent = province.province;
provinceSelect.appendChild(option);
fetch(`/cities?province_id=${provinceId}`)
.then(response => response.json())
.then(data => {
if (data.rajaongkir.status.code === 200) {
let cities = data.rajaongkir.results;
let citySelect = document.getElementById('city');
citySelect.innerHTML = '<option value="">Pilih Kota
Tujuan</option>'; // Clear previous options
cities.forEach(city => {
let option = document.createElement('option');
option.value = city.city_id;
option.textContent = city.city_name;
citySelect.appendChild(option);
});
} else {
console.error('Failed to fetch cities',
data.rajaongkir.status.description);
}
})
.catch(error => {
console.error('Error fetching cities:', error);
});
});
document.getElementById('city').addEventListener('change', function() {
let cityName = this.options[this.selectedIndex].text;
document.getElementById('city_name').value = cityName;
});
fetch('/cost', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-
token"]').getAttribute('content')
},
body: JSON.stringify({
origin: origin,
destination: destination,
weight: weight,
courier: courier
})
})
.then(response => response.json())
.then(data => {
if (data.rajaongkir.status.code === 200) {
let result = data.rajaongkir.results[0].costs;
let shippingResults = document.getElementById('shippingResults');
shippingResults.innerHTML = ''; // Clear previous results
result.forEach(cost => {
let row = document.createElement('tr');
row.innerHTML = `
<td>${cost.service}</td>
<td>${cost.cost[0].value} Rupiah</td>
<td>${cost.cost[0].etd} hari</td>
<td>${weight} Gram</td>
<td>Rp. {{ number_format($totalHarga, 0, ',', '.') }}</td>
<td>
<form action="{{ route('order.update-ongkir') }}"
method="post">
@csrf
<input type="hidden" name="province"
value="${document.getElementById('province').value}">
<input type="hidden" name="city"
value="${document.getElementById('city').value}">
<input type="hidden" name="province_name"
value="${document.getElementById('province_name').value}">
<input type="hidden" name="city_name"
value="${document.getElementById('city_name').value}">
<input type="hidden" name="kurir"
value="${courier}">
<input type="hidden" name="alamat"
value="${alamat}">
<input type="hidden" name="pos" value="${kodePos}">
<input type="hidden" name="layanan_ongkir"
value="${cost.service}">
<input type="hidden" name="biaya_ongkir"
value="${cost.cost[0].value}">
<input type="hidden" name="estimasi_ongkir"
value="${cost.cost[0].etd}">
<input type="hidden" name="total_berat"
value="${weight}">
<input type="hidden" name="city_origin"
value="${origin}">
<input type="hidden" name="city_origin_name"
value="${originName}">
<button type="submit" class="primary-btn">Pilih
Pengiriman</button>
</form>
</td>
`;
shippingResults.appendChild(row);
});
Pada baris script ini kita bisa mengganti dengan kota asal yang kita inginkan:
<script>
document.addEventListener('DOMContentLoaded', function() {
const originCityCode = 115; //ganti disini untuk kode kota asal
const originCityName = 'Depok'; //ganti disini untuk nama kota asal
document.getElementById('city_origin').value = originCityCode;
document.getElementById('city_origin_name').value = originCityName;
//script berikutnya >>
</script>
Jika berhasil, akan muncul halaman select-shipping seperti gambar di bawah ini. Isi data seperti
Provinsi Tujuan, Kota Tujuan, Kurir, Alamat, dan Kode Pos, kemudian klik tombol Cek
Ongkir untuk mendapatkan pilihan pengiriman yang diinginkan:
</tr>
</thead>
<tbody>
@php
$totalHarga = 0;
$totalBerat = 0;
@endphp
@foreach($order->orderItems as $item)
@php
$totalHarga += $item->harga * $item->quantity;
$totalBerat += $item->produk->berat * $item->quantity;
@endphp
<tr>
<td class="thumb"><img src="{{ asset('storage/img-produk/thumb_sm_' .
$item->produk->foto) }}" alt=""></td>
<td class="details">
<a>{{ $item->produk->nama_produk }}</a>
<ul>
<li><span>Berat: {{ $item->produk->berat }} Gram</span></li>
</ul>
<ul>
<li><span>Stok: {{ $item->produk->stok }} Gram</span></li>
</ul>
</td>
<td class="price text-center"><strong>Rp. {{ number_format($item-
>harga, 0, ',', '.') }}</strong></td>
<td class="qty text-center">
<a> {{ $item->quantity }} </a>
</td>
</tr>
@endforeach
</tbody>
<tfoot>
<tr>
<th class="empty" colspan="3"></th>
<th>SUBTOTAL</th>
<th colspan="2" class="sub-total">Rp. {{ number_format($totalHarga, 0,
',', '.') }}</th>
</tr>
<tr>
<th class="empty" colspan="3"></th>
<th>Ongkos Kirim</th>
<td colspan="2">
Rp. {{ number_format($order->biaya_ongkir, 0, ',', '.') }} <br>
{{ $order->kurir.'.'.$order->layanan_ongkir.' *estimasi '. $order-
>estimasi_ongkir.' Hari'}}
@if(session('origin'))
<p>Kota asal: {{ $originName }} </p>
@endif
</td>
</tr>
<tr>
<th class="empty" colspan="3"></th>
<th>TOTAL BAYAR</th>
<th colspan="2" class="total">Rp. {{ number_format($totalHarga +
$order->biaya_ongkir, 0, ',', '.') }}</th>
</tr>
</tfoot>
</table>
@endsection
10. kemudian kita tambahkan relasi customer di model User sebagai berikut:
public function customer()
{
return $this->hasOne(Customer::class);
}
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $table = "user";
protected $fillable = [
'nama',
'email',
'role',
'status',
'password',
'hp',
'foto',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
Payment Gateway adalah sebuah layanan yang memfasilitasi proses pembayaran online dengan
menghubungkan antara merchant (penjual) dan pembeli melalui berbagai metode pembayaran.
Payment Gateway berfungsi sebagai perantara yang memastikan transaksi berlangsung aman,
cepat, dan efisien. Layanan ini sangat penting dalam e-commerce dan bisnis online karena
memungkinkan pembayaran dilakukan secara digital.
Midtrans adalah salah satu penyedia Payment Gateway terkemuka di Indonesia yang
memungkinkan bisnis untuk menerima pembayaran online dengan berbagai metode. Midtrans
didirikan pada tahun 2015 dan merupakan bagian dari Gojek Group. Layanan ini dirancang
untuk memudahkan merchant dalam mengelola transaksi pembayaran dengan fitur-fitur yang
lengkap dan integrasi yang mudah. Midtrans menawarkan berbagai fitur yang membuatnya
menjadi pilihan populer bagi banyak bisnis online, di antaranya:
1. Multi-Channel Payment: Midtrans mendukung berbagai metode pembayaran, termasuk
transfer bank, kartu kredit/debit, e-wallet (seperti GoPay, OVO, Dana, ShopeePay), dan
gerai retail (seperti Alfamart, Indomaret).
2. One-Click Checkout: Memungkinkan pelanggan untuk menyimpan informasi pembayaran
mereka sehingga transaksi berikutnya dapat dilakukan dengan satu klik.
3. Recurring Payment: Cocok untuk bisnis yang menawarkan layanan berlangganan, seperti
membership atau subscription.
4. Fraud Detection: Midtrans dilengkapi dengan sistem deteksi penipuan yang canggih untuk
melindungi merchant dan pelanggan dari transaksi yang mencurigakan.
5. Customizable Checkout Page: Merchant dapat menyesuaikan halaman pembayaran sesuai
dengan brand mereka.
6. Real-Time Reporting: Merchant dapat memantau transaksi secara real-time melalui
dashboard Midtrans.
Jika berhasil login maka akan ditampilkan kehalaman dashboard midtrans sebagai
berikut:
4. Pada Pengaturan -> Access Keys disinilah kita mendapat Akses Key
6. Demikian beberapa pengaturan yang dapat kita lakukan pada Midtrans. Selanjutnya, kita
kembali ke proyek. Pertama, kita install library Midtrans terlebih dahulu melalui terminal:
composer require midtrans/midtrans-php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\OrderController;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
return [
'merchant_id' => env('MIDTRANS_MERCHANT_ID'),
'client_key' => env('MIDTRANS_CLIENT_KEY'),
'server_key' => env('MIDTRANS_SERVER_KEY'),
'is_production' => env('MIDTRANS_IS_PRODUCTION'),
'is_sanitized' => env('MIDTRANS_IS_SANITIZED'),
'is_3ds' => env('MIDTRANS_IS_3DS'),
];
</tr>
</thead>
<tbody>
@php
$totalHarga = 0;
$totalBerat = 0;
@endphp
@foreach($order->orderItems as $item)
@php
$totalHarga += $item->harga * $item->quantity;
$totalBerat += $item->produk->berat * $item->quantity;
@endphp
</tr>
@endforeach
</tbody>
<tfoot>
<tr>
<th class="empty" colspan="3"></th>
<th>SUBTOTAL</th>
<th colspan="2" class="sub-total">Rp. {{ number_format($totalHarga, 0,
',', '.') }}</th>
</tr>
<tr>
<th class="empty" colspan="3"></th>
<th>Ongkos Kirim</th>
<td colspan="2">
Rp. {{ number_format($order->biaya_ongkir, 0, ',', '.') }} <br>
{{ $order->kurir.'.'.$order->layanan_ongkir.' *estimasi '. $order-
>estimasi_ongkir.' Hari'}}
@if(session('origin'))
<p>Kota asal: {{ $originName }} </p>
@endif
</td>
</tr>
<tr>
<th class="empty" colspan="3"></th>
<th>TOTAL BAYAR</th>
<th colspan="2" class="total">Rp. {{ number_format($totalHarga +
$order->biaya_ongkir, 0, ',', '.') }}</th>
</tr>
</tfoot>
</table>
@endsection
10. Demikian juga kita update pada Controller.php yakni pada OrderController.php, dimana
kita akan update script function selectPayment, kemudian kita tambahkan function callback
& function complete. Untuk function complete terdapat 2 yakni:
public function complete() // Untuk kondisi local
untuk kondisi lokal atau ketika proyek belum memiliki domain. Fungsi ini digunakan saat
pengujian secara lokal.
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\Customer;
use App\Models\Produk;
use App\Models\Order;
use App\Models\OrderItem;
use Midtrans\Snap;
use Midtrans\Config;
$order = Order::firstOrCreate(
['customer_id' => $customer->id, 'status' => 'pending'],
['total_harga' => 0]
);
$orderItem = OrderItem::firstOrCreate(
if (!$orderItem->wasRecentlyCreated) {
$orderItem->quantity++;
$orderItem->save();
}
$order->total_harga += $produk->harga;
$order->save();
if ($order) {
$orderItem = OrderItem::where('order_id', $order->id)->where('produk_id', $id)-
>first();
if ($order->total_harga <= 0) {
$order->delete();
} else {
$order->save();
}
}
}
return redirect()->route('order.cart')->with('success', 'Produk berhasil dihapus
dari keranjang');
}
if ($order) {
// Simpan data ongkir ke dalam order
$order->kurir = $request->input('kurir');
$order->layanan_ongkir = $request->input('layanan_ongkir');
$order->biaya_ongkir = $request->input('biaya_ongkir');
$order->estimasi_ongkir = $request->input('estimasi_ongkir');
$order->total_berat = $request->input('total_berat');
$order->alamat = $request->input('alamat') . ', <br>' . $request-
>input('city_name') . ', <br>' . $request->input('province_name');
$order->pos = $request->input('pos');
$order->save();
// Simpan ke session flash agar bisa diakses di halaman tujuan
return redirect()->route('order.selectpayment')
->with('origin', $origin)
->with('originName', $originName);
}
if (!$order) {
return redirect()->route('order.cart')->with('error', 'Keranjang belanja
kosong.');
}
// Midtrans configuration
Config::$serverKey = config('midtrans.server_key');
Config::$isProduction = false;
Config::$isSanitized = true;
Config::$is3ds = true;
$params = [
'transaction_details' => [
'order_id' => $orderId,
'gross_amount' => (int) $grossAmount, // Pastikan gross_amount adalah
integer
],
'customer_details' => [
'first_name' => $customer->nama,
'email' => $customer->email,
'phone' => $customer->hp,
],
];
$snapToken = Snap::getSnapToken($params);
return view('v_order.select_payment', [
'order' => $order,
'origin' => $origin,
'originName' => $originName,
'snapToken' => $snapToken,
]);
}
if ($order) {
// Update status order menjadi 'Paid'
$order->status = 'Paid';
$order->save();
}
12. Berikutnya pada view kita belum menambahakan view history.blade.php pada direktori
resources/views/v_order/history.blade.php sebagai berikut:
@extends('v_layouts.app')
@section('content')
<!-- template -->
@section('content')
<div class="col-md-12">
<div class="order-summary clearfix">
<div class="section-title">
<p>HISTORY</p>
<h3 class="title">HISTORY PESANAN</h3>
</div>
<!-- msgSuccess -->
@if(session()->has('success'))
<div class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-
label="Close"><span aria-hidden="true">×</span></button>
<strong>{{ session('success') }}</strong>
</div>
@endif
<!-- end msgSuccess -->
@if($orders->count() > 0)
<table class="shopping-cart-table table">
<thead>
<tr>
<th>ID Pesanan</th>
<th>Tanggal</th>
</table>
@else
<p>Anda belum memiliki riwayat pesanan.</p>
@endif
</div>
</div>
@endsection
<div class="col-md-12">
<div class="order-summary clearfix">
<div class="section-title">
<h3 class="title">Order Review</h3>
</div>
@if($orders->count() > 0)
<table class="shopping-cart-table table">
<thead>
<tr>
<th>ID Pesanan</th>
<th>Tanggal</th>
<th>Total Harga</th>
<th>Status</th>
<th>Detail</th>
</tr>
</thead>
<tbody>
@foreach($order->orderItems as $item)
<tr>
<td>{{ $item->produk->nama_produk }}</td>
<td>{{ $item->quantity }}</td>
<td>Rp. {{ number_format($item->harga, 0, ',', '.') }}</td>
<td>Rp. {{ number_format($item->harga * $item->quantity, 0, ',', '.')
}}</td>
</tr>
@endforeach
</tbody>
</table>
<div class="pull-right">
<button class="primary-btn">Place Order</button>
</div>
@else
<p>Anda belum memiliki riwayat pesanan.</p>
@endif
</div>
</div>
<!-- end template-->
@endsection
table tr td {
padding: 6px;
font-weight: normal;
border: 1px solid #ccc;
}
table th {
border: 1px solid #ccc;
}
</style>
<table>
<tr>
<td align="left">
<img src="{{ asset('image/logo.png') }}" width="8%">
</td>
</tr>
<tr>
<td align="left">
<h2>Detail Pesanan #{{ $order->id }}</h2>
<strong>Tanggal:</strong> {{ $order->created_at->format('d M Y H:i') }}
</td>
</tr>
</table>
<p></p>
<table>
<tr>
<td align="left" style="border: hidden;">
<h5>Pelanggan</h5>
<address>
Nama: {{ $order->customer->user->nama }}<br>
Email: {{ $order->customer->email }}<br>
Hp: {{ $order->customer->hp }}<br>
Alamat: <br>{!! $order->alamat !!} <br>
Kode Pos: {{ $order->pos }}
</address>
</td>
<td align="right" style="border: hidden;">
<h5>Ongkos Kirim</h5>
<address>
@if ($order->noresi)
No.Resi: {{ $order->noresi }} <br>
@else
No. Resi <<dalam proses>> <br>
@endif
Kurir: {{ $order->kurir }}<br>
Layanan: {{ $order->layanan_ongkir }}<br>
Estimasi: {{ $order->estimasi_ongkir }} Hari<br>
Berat: {{ $order->total_berat }} Gram<br>
</address>
</td>
</tr>
</table>
<p></p>
<table>
</tr>
</thead>
<tbody>
@php
$totalHarga = 0;
$totalBerat = 0;
@endphp
@foreach ($order->orderItems as $item)
@php
$totalHarga += $item->harga * $item->quantity;
$totalBerat += $item->produk->berat * $item->quantity;
@endphp
<tr>
<td> {{ $loop->iteration }}</td>
<td class="details">
<a>{{ $item->produk->nama_produk }} #{{ $item->produk->kategori-
>nama_kategori }}</a>
<ul>
<li><span>Berat: {{ $item->produk->berat }} Gram</span></li>
<li><span>Stok: {{ $item->produk->stok }} Gram</span></li>
</ul>
</td>
<td class="price text-center">Rp. {{ number_format($item->harga, 0, ',', '.')
}}</td>
<td class="qty text-center">
<a> {{ $item->quantity }} </a>
</td>
<td class="total text-center">Rp. {{ number_format($item->harga * $item-
>quantity, 0, ',', '.') }}</td>
</tr>
@endforeach
</tbody>
<tfoot>
<tr>
<th class="empty" colspan="3"></th>
<td>Subtotal</td>
<td colspan="2">Rp. {{ number_format($totalHarga, 0, ',', '.') }}</td>
</tr>
<tr>
<th class="empty" colspan="3"></th>
<td>Ongkos Kirim</td>
<td colspan="2">
Rp. {{ number_format($order->biaya_ongkir, 0, ',', '.') }}
</td>
</tr>
<tr>
<th class="empty" colspan="3"></th>
<td><b>Total Bayar</b></td>
<td colspan="2" class="total"> <b>Rp.
{{ number_format($totalHarga + $order->biaya_ongkir, 0, ',', '.')
}}</b> </td>
</tr>
</tfoot>
</table>
<script>
window.onload = function() {
function printStruk() {
window.print();
}
</script>
14. Ketikan kita klik tombol Bayar Sekarang maka kita sudah berhasil menggunakan Snap
Token pada midtrans
Sebagai contoh kita akan melakukan pembayaran melalui Kredit/debit Card, dokumentasi
pada https://docs.midtrans.com/docs/snap-interactive-demo, sedangkan untuk pembayaran
lainnya yakni pada https://simulator.sandbox.midtrans.com/ sebagi berikut:
4. Paste Virtual
account number dan
klik Inquire
4. Pembayaran
Sukses dibayarkan
Kembali ke halaman website tunggu hingga status menjadi Payment Successfull, jika sudah
klik tombol OK untuk menuju kehalaman berikutnya
Kali ini, kita dapat mengaktifkan aksi Keranjang dan History maka kita dapat menambahkan
script yakni pada app.blade.php
</a>
</li>
<!-- /Cart -->
Presentasi Tugas Kelompok yakni mempresentasikan hasil dari project akhir yang
dikerjakan secara berkelompok dengan Indikator Penilaian:
1. Ketepatan waktu pengumpulan tugas
2. Kelengkapan makalah project
3. Dapat menjelaskan, menampilkan dan membuktikan hasil project akhir secara
berkelompok
Presentasi Tugas Kelompok yakni mempresentasikan hasil dari project akhir yang
dikerjakan secara berkelompok dengan Indikator Penilaian:
1. Ketepatan waktu pengumpulan tugas
2. Kelengkapan makalah project
3. Dapat menjelaskan, menampilkan dan membuktikan hasil project akhir secara
berkelompok
Presentasi Tugas Kelompok yakni mempresentasikan hasil dari project akhir yang
dikerjakan secara berkelompok dengan Indikator Penilaian:
1. Ketepatan waktu pengumpulan tugas
2. Kelengkapan makalah project
3. Dapat menjelaskan, menampilkan dan membuktikan hasil project akhir secara
berkelompok