Merged in izzati-feat-recall-FPX (pull request #102)
Izzati feat recall FPX
This commit is contained in:
@@ -0,0 +1,112 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use App\Models\Resit;
|
||||||
|
use App\Services\BillplzService;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use App\Models\Payment;
|
||||||
|
use App\Http\Controllers\ApiController;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\App;
|
||||||
|
|
||||||
|
class CheckBillplzPayments extends Command
|
||||||
|
{
|
||||||
|
protected $signature = 'billplz:semak
|
||||||
|
{--date= : Date in YYYY-MM-DD format}
|
||||||
|
{--preview : Preview pending resits only}';
|
||||||
|
protected $description = 'Check Billplz payment status and update database';
|
||||||
|
|
||||||
|
protected $billplzService;
|
||||||
|
|
||||||
|
public function __construct(BillplzService $billplzService)
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->billplzService = $billplzService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$date = $this->option('date') ?? now()->toDateString();
|
||||||
|
$preview = $this->option('preview');
|
||||||
|
|
||||||
|
// Validate date
|
||||||
|
try {
|
||||||
|
$dateObj = Carbon::createFromFormat('Y-m-d', $date);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->error("Invalid date format. Use YYYY-MM-DD.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->info("Retrying Billplz check for date: {$dateObj->toDateString()}");
|
||||||
|
|
||||||
|
$date = $dateObj->toDateString();
|
||||||
|
|
||||||
|
$pendingResits = DB::table('resit as r')
|
||||||
|
->join('payment as p', 'p.resit_id', '=', 'r.id')
|
||||||
|
->join(DB::raw('(
|
||||||
|
SELECT user_id, total_payment, MIN(id) AS min_id
|
||||||
|
FROM payment
|
||||||
|
GROUP BY user_id, total_payment
|
||||||
|
) as unique_payment'), 'p.id', '=', 'unique_payment.min_id')
|
||||||
|
->whereNull('r.status')
|
||||||
|
->where('p.status', 0)
|
||||||
|
->whereNotNull('r.billplz_id')
|
||||||
|
->whereDate('r.created_at', $date)
|
||||||
|
->where('r.billplz_id', 'NOT LIKE', '%CDM%')
|
||||||
|
->select('r.*', 'p.id as payment_id', 'p.total_payment')
|
||||||
|
->distinct()
|
||||||
|
->get();
|
||||||
|
|
||||||
|
if ($pendingResits->isEmpty()) {
|
||||||
|
$this->info('Tiada Resit belum dibayar ditemui.');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($preview) {
|
||||||
|
$this->info("Preview mode aktif. Senarai Resit:");
|
||||||
|
foreach ($pendingResits as $resit) {
|
||||||
|
$this->line("Resit ID: {$resit->id} | Billplz ID: {$resit->billplz_id} | Jumlah: RM{$resit->total_payment}");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($pendingResits as $resit) {
|
||||||
|
$this->info("Semak Resit ID: {$resit->id}");
|
||||||
|
|
||||||
|
$billData = $this->billplzService->testGetBill($resit->billplz_id);
|
||||||
|
|
||||||
|
if (isset($billData['error'])) {
|
||||||
|
$this->error("❌ Gagal semak Billplz untuk Resit {$resit->id}: {$billData['details']}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($billData['paid']) && $billData['paid'] === true) {
|
||||||
|
// Simulate Request object
|
||||||
|
$data = [
|
||||||
|
'billplz' => [
|
||||||
|
'id' => $resit->billplz_id,
|
||||||
|
'paid' => 'true',
|
||||||
|
'paid_at' => $billData['paid_at'] ?? now()->toDateTimeString(),
|
||||||
|
]
|
||||||
|
];
|
||||||
|
// dd($data);
|
||||||
|
|
||||||
|
$apiController = App::make(ApiController::class);
|
||||||
|
$apiController->processPaidResit($data);
|
||||||
|
// dd($fakeRequest);
|
||||||
|
|
||||||
|
|
||||||
|
$this->info("✅ Resit ID {$resit->id} handled via processPaidResit.");
|
||||||
|
}else {
|
||||||
|
$this->info("ℹ️ Resit ID {$resit->id} masih belum dibayar.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->info('Semakan semula Billplz selesai.');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -39,6 +39,8 @@ use Billplz\Client;
|
|||||||
use Http\Client\Common\HttpMethodsClient;
|
use Http\Client\Common\HttpMethodsClient;
|
||||||
use Http\Adapter\Guzzle7\Client as GuzzleHttpClient;
|
use Http\Adapter\Guzzle7\Client as GuzzleHttpClient;
|
||||||
use Http\Message\MessageFactory\GuzzleMessageFactory;
|
use Http\Message\MessageFactory\GuzzleMessageFactory;
|
||||||
|
use App\Models\LogUser;
|
||||||
|
use App\Jobs\WhatsappPembidaFPXPaid;
|
||||||
|
|
||||||
class ApiController extends Controller
|
class ApiController extends Controller
|
||||||
{
|
{
|
||||||
@@ -644,5 +646,70 @@ class ApiController extends Controller
|
|||||||
return view('pembida.feePaymentResult', compact('data', 'result', 'success'));
|
return view('pembida.feePaymentResult', compact('data', 'result', 'success'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// BillplzService.php atau ApiController.php
|
||||||
|
public function processPaidResit(array $data)
|
||||||
|
{
|
||||||
|
$currentdate = Carbon::now();
|
||||||
|
$currentdate = $currentdate->toDateTimeString();
|
||||||
|
$loguser = new LogUser;
|
||||||
|
$loguser->user_id = User::where('name','System')->first()->id;
|
||||||
|
$loguser->date = $currentdate;
|
||||||
|
$loguser->username = 'System';
|
||||||
|
$loguser->nama_user = 'System';
|
||||||
|
$loguser->norujukan = $data['billplz']['id'];
|
||||||
|
$loguser->nota = 'Callback Resit Billplz Telah Bayar : Billplz_id -'.$data['billplz']['id'];
|
||||||
|
$loguser->save();
|
||||||
|
|
||||||
|
$resit = Resit::where('billplz_id', '=', $data['billplz']['id'])->first();
|
||||||
|
|
||||||
|
if (!$resit) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$resit->status = 'PAID';
|
||||||
|
$resit->paid_at = $data['billplz']['paid_at'];
|
||||||
|
$resit->save();
|
||||||
|
|
||||||
|
$resitoriginal = Resit::where('user_id', $resit->user_id)
|
||||||
|
->where('lelong_id', $resit->lelong_id)
|
||||||
|
->where('resit_type', 2)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$payment = Payment::where('lelong_id', $resit->lelong_id)
|
||||||
|
->where('user_id', $resit->user_id)
|
||||||
|
->where('resit_id', $resit->id)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
foreach ($payment as $index) {
|
||||||
|
$index->status = 2;
|
||||||
|
$index->remark = "BERJAYA BAYARAN ONLINE";
|
||||||
|
$index->update();
|
||||||
|
|
||||||
|
$updateTagBid = LelongBid::where('norujukan', $index->norujukan)->first();
|
||||||
|
if ($updateTagBid) {
|
||||||
|
$updateTagBid->status_payment = 2;
|
||||||
|
$updateTagBid->save();
|
||||||
|
|
||||||
|
$updateTagBidder = LelongBidder::where('lelongbid_id', $updateTagBid->id)
|
||||||
|
->where('user_id', $resit->user_id)->first();
|
||||||
|
if ($updateTagBidder) {
|
||||||
|
$updateTagBidder->status_payment = 2;
|
||||||
|
$updateTagBidder->payment_id = $index->id;
|
||||||
|
$updateTagBidder->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event(new PaymentNotify('PAID', $resit->user_id, $payment));
|
||||||
|
//notifikasi wasap kepada pembida//
|
||||||
|
WhatsappPembidaFPXPaid::dispatch($resit->user_id,
|
||||||
|
$resit->id,
|
||||||
|
$resit->total_payment);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,19 +24,26 @@ use App\Models\Invoice;
|
|||||||
use App\Models\ScanJual;
|
use App\Models\ScanJual;
|
||||||
use App\Services\ResitPhotoService;
|
use App\Services\ResitPhotoService;
|
||||||
use Symfony\Component\Console\Descriptor\Descriptor;
|
use Symfony\Component\Console\Descriptor\Descriptor;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use App\Services\BillplzService;
|
||||||
|
use App\Http\Controllers\ApiController;
|
||||||
|
|
||||||
|
|
||||||
class PembidaController extends Controller
|
class PembidaController extends Controller
|
||||||
{
|
{
|
||||||
private $resitPhotoService;
|
private $resitPhotoService;
|
||||||
|
protected $billplzService;
|
||||||
|
protected $ResitSemakanService;
|
||||||
/**
|
/**
|
||||||
* Create a new controller instance.
|
* Create a new controller instance.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct(ResitPhotoService $resitPhotoService)
|
public function __construct(ResitPhotoService $resitPhotoService,BillplzService $billplzService)
|
||||||
{
|
{
|
||||||
$this->middleware('auth');
|
$this->middleware('auth');
|
||||||
$this->resitPhotoService = $resitPhotoService;
|
$this->resitPhotoService = $resitPhotoService;
|
||||||
|
$this->billplzService = $billplzService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1371,5 +1378,167 @@ class PembidaController extends Controller
|
|||||||
//return view('pembida.statusserahanbypembida',compact('listwinner','lelong','arrayinfo','cawanganbidder'));
|
//return view('pembida.statusserahanbypembida',compact('listwinner','lelong','arrayinfo','cawanganbidder'));
|
||||||
return view('pembida.statusserahanbypembida',compact('arraysms','arraysls','listwinner','lelong','cawanganbidder'));
|
return view('pembida.statusserahanbypembida',compact('arraysms','arraysls','listwinner','lelong','cawanganbidder'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function semakanBillplz(Request $request, BillplzService $billplzService)
|
||||||
|
{
|
||||||
|
$date = $request->input('date') ?? now()->toDateString();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$dateObj = Carbon::createFromFormat('Y-m-d', $date);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return back()->with('error', 'Tarikh tidak sah. Guna format YYYY-MM-DD.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$date = $dateObj->toDateString();
|
||||||
|
|
||||||
|
$allPendingResits = DB::table('resit as r')
|
||||||
|
->join('payment as p', 'p.resit_id', '=', 'r.id')
|
||||||
|
->join(DB::raw('(
|
||||||
|
SELECT user_id, total_payment, MIN(id) AS min_id
|
||||||
|
FROM payment
|
||||||
|
GROUP BY user_id, total_payment
|
||||||
|
) as unique_payment'), 'p.id', '=', 'unique_payment.min_id')
|
||||||
|
->join('users as u', 'p.user_id', '=', 'u.id')
|
||||||
|
->join('cawangan as c', 'p.kodcaw', '=', 'c.caw_code') // JOIN ikut kodcaw
|
||||||
|
// Elakkan resit yang ada CDM payment pada hari yang sama
|
||||||
|
->whereNotExists(function ($query) use ($date) {
|
||||||
|
$query->select(DB::raw(1))
|
||||||
|
->from('resit as r2')
|
||||||
|
->join('payment as p2', 'p2.resit_id', '=', 'r2.id')
|
||||||
|
->whereRaw('p2.user_id = p.user_id')
|
||||||
|
->whereRaw('p2.total_payment = p.total_payment')
|
||||||
|
->whereDate('r2.created_at', $date)
|
||||||
|
->where('r2.billplz_id', 'LIKE', '%CDM%');
|
||||||
|
})
|
||||||
|
->whereNull('r.status')
|
||||||
|
->where('p.status', 0)
|
||||||
|
->whereNotNull('r.billplz_id')
|
||||||
|
->where('r.billplz_id', 'NOT LIKE', '%CDM%')
|
||||||
|
->whereDate('r.created_at', $date)
|
||||||
|
->select(
|
||||||
|
'r.*',
|
||||||
|
'p.id as payment_id',
|
||||||
|
'p.total_payment',
|
||||||
|
'p.user_id',
|
||||||
|
'u.name as payer_name',
|
||||||
|
'c.caw_name as cawangan_nama' // Tambah nama cawangan
|
||||||
|
)
|
||||||
|
->distinct()
|
||||||
|
->get();
|
||||||
|
|
||||||
|
// Kumpul mengikut billplz_id dan user_id
|
||||||
|
$groupedResitsUnpaid = $allPendingResits
|
||||||
|
->groupBy('billplz_id') // Group mengikut billplz_id
|
||||||
|
->map(function ($items) {
|
||||||
|
return (object) [
|
||||||
|
'billplz_id' => $items->first()->billplz_id,
|
||||||
|
'jumlah_bayaran' => $items->sum('total_payment'),
|
||||||
|
'payer_name' => $items->first()->payer_name,
|
||||||
|
'cawangan_nama' => $items->first()->cawangan_nama,
|
||||||
|
'resit_id' => $items->first()->id,
|
||||||
|
'created_at' => $items->first()->created_at,
|
||||||
|
'user_id' => $items->first()->user_id,
|
||||||
|
];
|
||||||
|
})
|
||||||
|
->values(); // Reset index
|
||||||
|
|
||||||
|
|
||||||
|
$billplzWithCdmResits = DB::table('resit as r')
|
||||||
|
->join('payment as p', 'p.resit_id', '=', 'r.id')
|
||||||
|
->join(DB::raw('(
|
||||||
|
SELECT user_id, total_payment, MIN(id) AS min_id
|
||||||
|
FROM payment
|
||||||
|
GROUP BY user_id, total_payment
|
||||||
|
) as unique_payment'), 'p.id', '=', 'unique_payment.min_id')
|
||||||
|
->join('users as u', 'p.user_id', '=', 'u.id')
|
||||||
|
->join('cawangan as c', 'p.kodcaw', '=', 'c.caw_code')
|
||||||
|
->whereNull('r.status')
|
||||||
|
->where('p.status', 0)
|
||||||
|
->whereNotNull('r.billplz_id')
|
||||||
|
->where('r.billplz_id', 'NOT LIKE', '%CDM%')
|
||||||
|
->whereDate('r.created_at', $date)
|
||||||
|
->whereExists(function ($query) use ($date) {
|
||||||
|
$query->select(DB::raw(1))
|
||||||
|
->from('resit as r2')
|
||||||
|
->join('payment as p2', 'p2.resit_id', '=', 'r2.id')
|
||||||
|
->join('users as u2', 'p2.user_id', '=', 'u2.id')
|
||||||
|
->whereRaw('u2.name = u.name') // banding nama
|
||||||
|
->whereRaw('p2.total_payment = p.total_payment') // dan jumlah
|
||||||
|
->whereDate('r2.created_at', $date)
|
||||||
|
->where('r2.billplz_id', 'LIKE', '%CDM%');
|
||||||
|
})
|
||||||
|
->select('r.*', 'p.id as payment_id', 'p.total_payment', 'u.name as payer_name', 'c.caw_name as cawangan_nama')
|
||||||
|
->distinct()
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$groupedResits = $billplzWithCdmResits
|
||||||
|
->groupBy('billplz_id')
|
||||||
|
->map(function ($items) {
|
||||||
|
return (object) [
|
||||||
|
'billplz_id' => $items->first()->billplz_id,
|
||||||
|
'jumlah_bayaran' => $items->sum('total_payment'),
|
||||||
|
'payer_name' => $items->first()->payer_name,
|
||||||
|
'cawangan_nama' => $items->first()->cawangan_nama,
|
||||||
|
'resit_id' => $items->first()->id,
|
||||||
|
'created_at' => $items->first()->created_at,
|
||||||
|
'user_id' => $items->first()->user_id,
|
||||||
|
];
|
||||||
|
})->values();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Semak status Billplz untuk setiap resit
|
||||||
|
$paidResits = collect();
|
||||||
|
$unpaidResits = collect();
|
||||||
|
$processingResits = collect();
|
||||||
|
|
||||||
|
foreach ($allPendingResits as $resit) {
|
||||||
|
$billData = $billplzService->testGetBill($resit->billplz_id);
|
||||||
|
|
||||||
|
$isPaid = isset($billData['paid']) && $billData['paid'] === true;
|
||||||
|
$isProcessing = isset($billData['state']) && in_array($billData['state'], ['pending', 'processing']) && isset($billData['paid_at']);
|
||||||
|
|
||||||
|
if ($isPaid) {
|
||||||
|
$resit->paid_at = $billData['paid_at'] ?? now();
|
||||||
|
$paidResits->push($resit);
|
||||||
|
} elseif ($isProcessing) {
|
||||||
|
$resit->paid_at = $billData['paid_at'];
|
||||||
|
$resit->state = $billData['state'];
|
||||||
|
$processingResits->push($resit);
|
||||||
|
} else {
|
||||||
|
$unpaidResits->push($resit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('pembida.semakanbillplz', compact(
|
||||||
|
'paidResits',
|
||||||
|
'unpaidResits',
|
||||||
|
'billplzWithCdmResits',
|
||||||
|
'processingResits',
|
||||||
|
'groupedResits',
|
||||||
|
'groupedResitsUnpaid',
|
||||||
|
'date'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function prosesBillplz(Request $request)
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'billplz' => [
|
||||||
|
'id' => $request->billplz_id,
|
||||||
|
'resit_id' => $request->resit_id,
|
||||||
|
'paid' => 'true',
|
||||||
|
'paid_at' => $request->paid_at ?? now()->toDateTimeString(),
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$apiController = app(ApiController::class);
|
||||||
|
$apiController->processPaidResit($data);
|
||||||
|
|
||||||
|
return redirect()->back()->with('success', 'Resit telah diproses.');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\LogUser;
|
||||||
|
|
||||||
|
class WhatsappPembidaFPXPaid implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public $userId, $resitId, $amount;
|
||||||
|
|
||||||
|
public function __construct($userId, $resitId, $amount)
|
||||||
|
{
|
||||||
|
$this->userId = $userId;
|
||||||
|
$this->resitId = $resitId;
|
||||||
|
$this->amount = $amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
$dt = Carbon::now();
|
||||||
|
$user = User::find($this->userId);
|
||||||
|
if (!$user || !$user->notelbimbit) return;
|
||||||
|
|
||||||
|
$message = "Assalamualaikum {$user->name}, bayaran FPX anda sebanyak RM " . number_format($this->amount, 2) . " telah berjaya direkodkan. Resit ID: {$this->resitId}. Terima kasih.";
|
||||||
|
|
||||||
|
$mobile_no = '6' . $user->notelbimbit;
|
||||||
|
if (config('app.env') != 'production') {
|
||||||
|
$mobile_no = config('app.admin_phone_number');
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = Http::get(config('app.wabot_api'), [
|
||||||
|
'number' => $mobile_no,
|
||||||
|
'type' => 'text',
|
||||||
|
'message' => $message,
|
||||||
|
'instance_id' => config('app.wabot_instances_id'),
|
||||||
|
'access_token' => config('app.wabot_token_keys')
|
||||||
|
]);
|
||||||
|
|
||||||
|
$loguser = new LogUser;
|
||||||
|
$loguser->date = $dt;
|
||||||
|
$loguser->username = 'Operasi';
|
||||||
|
$loguser->nama_user = 'System';
|
||||||
|
$loguser->nota = 'Notifikasi Pembayaran Billplz Berjaya';
|
||||||
|
$loguser->save();
|
||||||
|
|
||||||
|
Log::info("Notifikasi Pembayaran Billplz Berjaya: " . $response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
use Billplz\Client as BillplzClient;
|
||||||
|
use Http\Client\Common\HttpMethodsClient;
|
||||||
|
use GuzzleHttp\Client as GuzzleHttpClient;
|
||||||
|
use Http\Message\MessageFactory\GuzzleMessageFactory;
|
||||||
|
use Laravie\Codex\Exceptions\NotFoundException;
|
||||||
|
|
||||||
|
class BillplzService
|
||||||
|
{
|
||||||
|
protected $client;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
// Buat HttpMethodsClient dengan GuzzleHttpClient dan GuzzleMessageFactory
|
||||||
|
$http = new HttpMethodsClient(
|
||||||
|
new GuzzleHttpClient(), // Guzzle HTTP client
|
||||||
|
new GuzzleMessageFactory() // Guzzle message factory untuk request dan response
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get API key dan base URL dari environment
|
||||||
|
$apiKey = env('BILLPLZ_API_KEY');
|
||||||
|
$baseUrl = env('BILLPLZ_API_URL', 'https://api.billplz.com'); // Default production URL
|
||||||
|
|
||||||
|
// Mencipta client Billplz dengan adapter HTTP
|
||||||
|
$this->client = new BillplzClient($http, $apiKey, $baseUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetBill(string $billId)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Minta details bill berdasarkan ID
|
||||||
|
$response = $this->client->bill()->get($billId);
|
||||||
|
|
||||||
|
// Pastikan respons berjaya
|
||||||
|
if ($response->isSuccessful()) {
|
||||||
|
return $response->toArray(); // Kembalikan hasil dalam bentuk array
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['error' => 'Request failed', 'details' => $response->toArray()];
|
||||||
|
} catch (NotFoundException $e) {
|
||||||
|
return ['error' => 'Bill not found', 'details' => $e->getMessage()];
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return ['error' => 'Unexpected error', 'details' => $e->getMessage()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,4 +30,9 @@ return [
|
|||||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'billplz' => [
|
||||||
|
'key' => env('BILLPLZ_API_KEY'),
|
||||||
|
'sandbox' => env('BILLPLZ_SANDBOX', false),
|
||||||
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -0,0 +1,176 @@
|
|||||||
|
@extends('layouts.app')
|
||||||
|
@section('content')
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row ">
|
||||||
|
<div class="col">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header header-style">
|
||||||
|
<a href="javascript:window.history.go(-1);" class="header-style-icon">
|
||||||
|
<i class="fas fa-arrow-left"></i>
|
||||||
|
</a>
|
||||||
|
<div class="header-style-title">Semakan Billplz FPX</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
@if ($message = Session::get('success'))
|
||||||
|
<div class="alert alert-success">
|
||||||
|
<p>{{ $message }}</p>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<form method="GET" action="{{ route('pembida.semakanbillplz') }}" class="mb-4">
|
||||||
|
<div class="form-inline">
|
||||||
|
<label for="date" class="mr-2">Pilih Tarikh:</label>
|
||||||
|
<input type="date" name="date" id="date" class="form-control mr-2" value="{{ $date }}">
|
||||||
|
<button type="submit" class="btn btn-primary">Semak</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<h4>✅ Resit Telah Dibayar (PAID)</h4>
|
||||||
|
@if($paidResits->isEmpty())
|
||||||
|
<p>Tiada resit yang telah dibayar.</p>
|
||||||
|
@else
|
||||||
|
<table class="table table-bordered text-center">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Nama Pembayar</th>
|
||||||
|
<th>ID Pembayar</th>
|
||||||
|
<th>Cawangan</th>
|
||||||
|
<th>Resit ID</th>
|
||||||
|
<th>Billplz ID</th>
|
||||||
|
<th>Jumlah (RM)</th>
|
||||||
|
<th>Dibayar Pada</th>
|
||||||
|
<th>Tindakan</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach($paidResits as $resit)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $resit->payer_name ?? '-' }}</td>
|
||||||
|
<td>{{ $resit->user_id }}</td>
|
||||||
|
<td>{{ $resit->cawangan_nama ?? '-' }}</td>
|
||||||
|
<td>{{ $resit->id }}</td>
|
||||||
|
<td>{{ $resit->billplz_id }}</td>
|
||||||
|
<td>{{ number_format($resit->total_payment, 2) }}</td>
|
||||||
|
<td>{{ \Carbon\Carbon::parse($resit->paid_at)->format('d/m/Y H:i') }}</td>
|
||||||
|
<td>
|
||||||
|
<form method="POST" action="{{ route('pembida.prosesBillplz', ['resit_id' => $resit->id, 'billplz_id' => $resit->billplz_id]) }}">
|
||||||
|
@csrf
|
||||||
|
<input type="hidden" name="billplz_id" value="{{ $resit->billplz_id }}">
|
||||||
|
<input type="hidden" name="paid_at" value="{{ \Carbon\Carbon::parse($resit->paid_at)->toDateTimeString() }}">
|
||||||
|
<button type="submit" class="btn btn-sm btn-success">Proses</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h4>❌ Resit Belum Dibayar (UNPAID)</h4>
|
||||||
|
@if($unpaidResits->isEmpty())
|
||||||
|
<p>Semua resit telah dibayar.</p>
|
||||||
|
@else
|
||||||
|
<table class="table table-bordered text-center">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Nama Pembayar</th>
|
||||||
|
<th>ID Pembayar</th>
|
||||||
|
<th>Cawangan</th>
|
||||||
|
<th>Resit ID</th>
|
||||||
|
<th>Billplz ID</th>
|
||||||
|
<th>Jumlah (RM)</th>
|
||||||
|
<th>Tarikh Dicipta</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach ($groupedResitsUnpaid as $resit)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $resit->payer_name ?? '-' }}</td>
|
||||||
|
<td>{{ $resit->user_id }}</td>
|
||||||
|
<td>{{ $resit->cawangan_nama ?? '-' }}</td>
|
||||||
|
<td>{{ $resit->resit_id }}</td>
|
||||||
|
<td>{{ $resit->billplz_id }}</td>
|
||||||
|
<td>{{ number_format($resit->jumlah_bayaran, 2) }}</td>
|
||||||
|
<td>{{ \Carbon\Carbon::parse($resit->created_at)->format('d/m/Y H:i') }}</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<h4>🔄 Resit Billplz yang Telah Dibayar Semula Melalui CDM</h4>
|
||||||
|
@if($billplzWithCdmResits->isEmpty())
|
||||||
|
<p>Tiada bayaran semula melalui CDM ditemui.</p>
|
||||||
|
@else
|
||||||
|
<table class="table table-bordered text-center">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Nama Pembayar</th>
|
||||||
|
<th>ID Pembayar</th>
|
||||||
|
<th>Cawangan</th>
|
||||||
|
<th>Resit ID</th>
|
||||||
|
<th>Billplz ID</th>
|
||||||
|
<th>Jumlah (RM)</th>
|
||||||
|
<th>Tarikh Dicipta</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach ($groupedResits as $resit)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $resit->payer_name }}</td>
|
||||||
|
<td>{{ $resit->user_id }}</td>
|
||||||
|
<td>{{ $resit->cawangan_nama }}</td>
|
||||||
|
<td>{{ $resit->resit_id }}</td>
|
||||||
|
<td>{{ $resit->billplz_id }}</td>
|
||||||
|
<td>{{ number_format($resit->jumlah_bayaran, 2) }}</td>
|
||||||
|
<td>{{ \Carbon\Carbon::parse($resit->created_at)->format('d/m/Y H:i') }}</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<h4>🕒 Resit Telah Dibayar Tetapi Dalam Proses (PENDING)</h4>
|
||||||
|
@if($processingResits->isEmpty())
|
||||||
|
<p>Tiada resit yang belum direlease.</p>
|
||||||
|
@else
|
||||||
|
<table class="table table-bordered text-center">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Nama Pembayar</th>
|
||||||
|
<th>ID Pembayar</th>
|
||||||
|
<th>Cawangan</th>
|
||||||
|
<th>Resit ID</th>
|
||||||
|
<th>Billplz ID</th>
|
||||||
|
<th>Jumlah (RM)</th>
|
||||||
|
<th>Tarikh Dibayar</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach($processingResits as $resit)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $resit->payer_name }}</td>
|
||||||
|
<td>{{ $resit->user_id }}</td>
|
||||||
|
<td>{{ $resit->cawangan_nama ?? '-' }}</td>
|
||||||
|
<td>{{ $resit->id }}</td>
|
||||||
|
<td>{{ $resit->billplz_id }}</td>
|
||||||
|
<td>{{ number_format($resit->total_payment, 2) }}</td>
|
||||||
|
<td>{{ \Carbon\Carbon::parse($resit->paid_at)->format('d/m/Y H:i') }}</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
@@ -13,6 +13,8 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@hasrole('operasi')
|
@hasrole('operasi')
|
||||||
<a href="{{ route('pembida.statuspembayaranbypembida') }}" class="btn btn-success mb-2">Status Pembayaran Mengikut Nama Pembida</a>
|
<a href="{{ route('pembida.statuspembayaranbypembida') }}" class="btn btn-success mb-2">Status Pembayaran Mengikut Nama Pembida</a>
|
||||||
|
<a href="{{ route('pembida.semakanbillplz') }}" class="btn btn-success mb-2">Semakan BILLPLZ</a>
|
||||||
|
|
||||||
@endhasrole
|
@endhasrole
|
||||||
@hasrole('account')
|
@hasrole('account')
|
||||||
<a href="{{ route('account.statuspembayaranbypembida') }}" class="btn btn-success mb-2">Status Pembayaran Mengikut Nama Pembida</a>
|
<a href="{{ route('account.statuspembayaranbypembida') }}" class="btn btn-success mb-2">Status Pembayaran Mengikut Nama Pembida</a>
|
||||||
|
|||||||
@@ -364,6 +364,9 @@ Route::group(['middleware' => ['permission:pengesahanpembida'],'prefix'=>'pembid
|
|||||||
Route::get('/statuspembayaranbypembida',['as'=>'pembida.statuspembayaranbypembida','uses'=>'PembidaController@statuspembayaranbypembida']);
|
Route::get('/statuspembayaranbypembida',['as'=>'pembida.statuspembayaranbypembida','uses'=>'PembidaController@statuspembayaranbypembida']);
|
||||||
Route::get('/statusserahanbidaan',['as'=>'pembida.statusserahanbidaan','uses'=>'PembidaController@statusserahanbidaan']);
|
Route::get('/statusserahanbidaan',['as'=>'pembida.statusserahanbidaan','uses'=>'PembidaController@statusserahanbidaan']);
|
||||||
Route::get('/statusserahanbypembida',['as'=>'pembida.statusserahanbypembida','uses'=>'PembidaController@statusserahanbypembida']);
|
Route::get('/statusserahanbypembida',['as'=>'pembida.statusserahanbypembida','uses'=>'PembidaController@statusserahanbypembida']);
|
||||||
|
Route::get('/semakanbillplz',['as'=>'pembida.semakanbillplz','uses'=>'PembidaController@semakanbillplz']);
|
||||||
|
Route::post('/semakanbillplz/proses',['as'=>'pembida.prosesBillplz','uses'=>'PembidaController@prosesBillplz']);
|
||||||
|
|
||||||
|
|
||||||
// Route::get('/pemenangkedua',['as'=>'pembida.pemenangkedua','uses'=>'PembidaController@pemenangkedua']);
|
// Route::get('/pemenangkedua',['as'=>'pembida.pemenangkedua','uses'=>'PembidaController@pemenangkedua']);
|
||||||
// Route::get('/senaraipemenangkedua/{lelong_id}/{kodcaw}',['as'=>'pembida.senaraipemenangkedua','uses'=>'PembidaController@senaraipemenangkedua']);
|
// Route::get('/senaraipemenangkedua/{lelong_id}/{kodcaw}',['as'=>'pembida.senaraipemenangkedua','uses'=>'PembidaController@senaraipemenangkedua']);
|
||||||
|
|||||||
Reference in New Issue
Block a user