web-mooc/app/Http/Controllers/student/VAPaymentController.php
Baghiz Zuhdi Adzin 1f8efdccc4 pembayaran
2026-01-21 15:34:54 +07:00

329 lines
11 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Http\Controllers\student;
use App\Events\PaymentCompleted;
use App\Http\Controllers\Controller;
use App\Models\CartItem;
use App\Models\Payment_history;
use App\Models\VAPayment;
use App\Notifications\CoursePurchaseCreated;
use App\Models\Course;
use App\Models\Enrollment;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Carbon\Carbon;
class VAPaymentController extends Controller
{
public function store(Request $request)
{
$request->validate([
'item_type' => 'required|string',
]);
$payment_details = Session::get('payment_details');
if (!$payment_details) {
return back()->with('error', 'Payment session not found.');
}
$item_id_arr = collect($payment_details['items'])->pluck('id')->toArray();
// BTN REQUIREMENT
$noTest = str_pad(random_int(0, 99999), 5, '0', STR_PAD_LEFT);
$nama = auth()->user()->name;
$tagihan = $payment_details['payable_amount'];
$expiredAt = Carbon::now()->addHour();
DB::beginTransaction();
try {
$response = $this->generateDummyVA(
$noTest,
$tagihan,
$nama,
$expiredAt->format('Y-m-d H:i:s')
);
$btnResponse = json_decode($response, true);
Log::info('BTN VA Response', $btnResponse ?? []);
$vaNumber = $btnResponse['BTNVirtualAccount'];
if (!$vaNumber) {
throw new \Exception('BTN VA failed: ' . $response);
}
$vaPayment = VAPayment::create([
'user_id' => auth()->id(),
'item_type' => $request->item_type,
'items' => json_encode($item_id_arr),
'tax' => $payment_details['tax'],
'total_amount' => $tagihan,
'coupon' => $payment_details['coupon'] ?? null,
'va_number' => $vaNumber,
'status' => 0,
'expired_at' => $expiredAt
]);
if ($request->item_type === 'course') {
// Hapus dari cart
CartItem::whereIn('course_id', $item_id_arr)
->where('user_id', auth()->id())
->delete();
$courses = Course::whereIn('id', $item_id_arr)->get();
auth()->user()->notify(
new CoursePurchaseCreated($vaPayment, $courses)
);
}
DB::commit();
return redirect()
->route('payment.va.show', $vaPayment->id)
->with('success', 'Virtual Account berhasil dibuat.');
} catch (\Throwable $e) {
DB::rollBack();
Log::error('VA Payment Error', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return back()->with('error', 'Gagal membuat Virtual Account: ' . $e->getMessage());
}
}
private function generateVAFast($notest, $tagihan, $nama, $tgl_terakhirbayar)
{
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => 'https://neosidata.unesa.ac.id/btn_v2/create',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
'credential' => '$1$bCE4xLfB$U7t8ux0g4iflFaCpcLqaB.',
'noid' => $notest,
'nama' => $nama,
'tagihan' => $tagihan,
'flag' => 'F',
'expired_date' => $tgl_terakhirbayar,
'deskripsi' => 'Pembayaran Fast Track',
],
]);
$response = curl_exec($curl);
curl_close($curl);
return $response;
}
//untuk testing
private function generateDummyVA($notest, $tagihan, $nama, $tgl_terakhirbayar)
{
// Prefix BTN: 9422 + 5 digit random + 8 digit noTest (total 17 digit)
// Format: 9422 + XXXXX + 00000 + noTest (5 digit)
// Buat angka random 5 digit untuk middle part
$middle = str_pad(random_int(0, 99999), 5, '0', STR_PAD_LEFT);
// Pad noTest dengan leading zeros jika kurang dari 5 digit
$paddedNoTest = str_pad($notest, 5, '0', STR_PAD_LEFT);
// Generate VA number (17 digit)
$vaNumber = '9422' . $middle . '0' . $paddedNoTest; // 4 + 5 + 1 + 5 = 15 digit
// Tambah 2 digit random untuk genapin 17 digit
$vaNumber .= str_pad(random_int(0, 99), 2, '0', STR_PAD_LEFT);
// Format response seperti aslinya
$dummyResponse = [
'BTNVirtualAccount' => $vaNumber,
'status' => '00',
'message' => 'Success',
'data' => [
'va' => $vaNumber,
'nama' => $nama,
'tagihan' => $tagihan,
'expired_date' => $tgl_terakhirbayar,
'bank' => 'BTN',
'deskripsi' => 'Pembayaran Fast Track'
],
'timestamp' => now()->toISOString()
];
Log::info('DUMMY VA GENERATED', [
'va_number' => $vaNumber,
'notest' => $notest,
'padded_notest' => $paddedNoTest,
'total_digits' => strlen($vaNumber)
]);
return json_encode($dummyResponse);
}
public function show($id)
{
$vaPayment = VAPayment::where('id', $id)
->where('user_id', auth()->user()->id)
->firstOrFail();
return view('payment.va_show', compact('vaPayment'));
}
public function checkPaymentApi($id)
{
Log::info('CHECK PAYMENT STARTED');
$payment = VAPayment::where('id', $id)
->where('user_id', auth()->id())
->firstOrFail();
if ($payment->status == 1) {
return response()->json([
'status' => 1,
'message' => 'Already paid'
]);
}
if (now()->greaterThan($payment->expired_at)) {
$payment->update(['status' => 2]);
return response()->json([
'status' => 2,
'message' => 'Expired'
]);
}
try {
$fullVa = $payment->va_number;
$noidcek = substr($fullVa, 5);
$url = "https://neosidata.unesa.ac.id/api-va-narkoba/{$noidcek}/{$noidcek}";
$response = Http::timeout(15)
->withOptions(['verify' => false])
->get($url);
if (!$response->successful()) {
return response()->json([
'status' => 0,
'message' => 'BTN API error'
]);
}
$data = $response->json();
$isPaid = is_numeric($data['terbayar'] ?? 0)
&& floatval($data['terbayar']) >= floatval($data['tagihan'] ?? 0);
if (!$isPaid) {
return response()->json([
'status' => 0,
'message' => 'Unpaid'
]);
}
DB::beginTransaction();
// 1⃣ Update VA Payment
$payment->update([
'status' => 1,
'paid_at' => now()
]);
// 2⃣ Insert ke payment_history
if ($payment->item_type === 'course') {
$items = json_decode($payment->items);
foreach ($items as $courseId) {
$course = Course::findOrFail($courseId);
$amount = $course->discount_flag
? $course->discounted_price
: $course->price;
// Coupon
$discount = 0;
if ($payment->coupon) {
$coupon = Coupon::where('code', $payment->coupon)->first();
if ($coupon) {
$discount = $amount * ($coupon->discount / 100);
}
}
$finalAmount = $amount - $discount;
$tax = (get_settings('course_selling_tax') / 100) * $finalAmount;
$history = [
'invoice' => Str::random(20),
'user_id' => $payment->user_id,
'payment_type' => 'virtual_account',
'course_id' => $course->id,
'amount' => $finalAmount,
'tax' => $tax,
'coupon' => $payment->coupon,
];
// Revenue split
if (get_course_creator_id($course->id)->role === 'admin') {
$history['admin_revenue'] = $finalAmount;
} else {
$history['instructor_revenue'] =
$finalAmount * (get_settings('instructor_revenue') / 100);
$history['admin_revenue'] =
$finalAmount - $history['instructor_revenue'];
}
// Payment_history::insert($history);
// // 3⃣ Enrollment
// Enrollment::insert([
// 'user_id' => $payment->user_id,
// 'course_id' => $course->id,
// 'enrollment_type' => 'paid',
// 'entry_date' => time(),
// 'expiry_date' => $course->expiry_period > 0
// ? strtotime('+' . ($course->expiry_period * 30) . ' days')
// : null,
// 'created_at' => now(),
// 'updated_at' => now(),
// ]);
}
}
DB::commit();
// 4⃣ Event & Notification
event(new PaymentCompleted($payment));
return response()->json([
'status' => 1,
'message' => 'Payment successful'
]);
} catch (\Throwable $e) {
DB::rollBack();
Log::error('VA CHECK ERROR', [
'payment_id' => $payment->id,
'error' => $e->getMessage()
]);
return response()->json([
'status' => 0,
'message' => 'Check failed'
], 500);
}
}
}