Files
portal-api/app/Http/Controllers/Api/Transaction/TransactionController.php

374 lines
15 KiB
PHP

<?php
namespace App\Http\Controllers\Api\Transaction;
use App\Enums\TransactionStatus;
use App\Http\Controllers\Controller;
use App\Http\Requests\DepositRequest;
use App\Http\Services\VNPayService;
use App\Jobs\Mail\Customer\DepositSuccessJob;
use App\Models\Customer;
use App\Models\Invoice;
use App\Models\Log;
use App\Models\Transaction;
use App\Services\ActivityLogger;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log as FacadesLog;
use Illuminate\Support\Str;
class TransactionController extends Controller
{
protected VNPayService $vnpayService;
protected ActivityLogger $activityLogger;
public function __construct(VNPayService $vnpayService, ActivityLogger $activityLogger)
{
$this->vnpayService = $vnpayService;
$this->activityLogger = $activityLogger;
}
// ponytail: VNPay auto-deposit disabled — nạp tiền hiện dùng luồng thủ công (ManualDepositController).
public function handle(DepositRequest $request)
{
return response()->json([
'error' => true,
'message' => 'Nạp tiền qua VNPay hiện không khả dụng. Vui lòng sử dụng nạp tiền thủ công.',
], 410);
// $user = $request->user();
// $transRef = time();
// $uuid = Str::uuid();
// $publicCode = 'DEP-' . now()->format('Ymd') . '-' . strtoupper(Str::random(6));
//
// $transaction = Transaction::create([
// 'uuid' => $uuid,
// 'public_code' => $publicCode,
// 'trans' => 'DEP' . $transRef,
// 'amount' => $request->amount,
// 'customer_id' => $user->id,
// 'status' => TransactionStatus::PENDING->value,
// ]);
//
// $paymentUrl = $this->vnpayService->createPaymentUrl([
// 'amount' => $request->amount,
// 'transaction_ref' => $transRef,
// 'transaction_uuid' => $uuid,
// ]);
//
// $this->activityLogger->log(
// event: 'deposit_vnpay',
// logName: 'Transaction',
// description: "Customer {$user->email} tạo yêu cầu nạp tiền VNPay: " . number_format($request->amount) . ' VNĐ - Mã: ' . $publicCode,
// subject: $transaction,
// newValues: ['public_code' => $publicCode, 'amount' => $request->amount, 'type' => 'vnpay'],
// );
//
// return response()->json([
// 'error' => false,
// 'message' => 'Tạo yêu cầu nạp tiền thành công',
// 'data' => [
// 'transaction_id' => $transaction->trans,
// 'transaction_uuid' => $uuid,
// 'transaction_code' => $publicCode,
// 'payment_url' => $paymentUrl,
// ],
// ]);
}
protected function redirectToFrontend(string $status = 'failed', ?string $transactionCode = null): \Illuminate\Http\RedirectResponse
{
$frontendUrl = config('vnpay.frontend_url', 'http://localhost:3000');
$url = $frontendUrl . '/nap-tien/return';
$params = ['status' => $status];
if ($transactionCode) {
$params['code'] = $transactionCode;
}
return redirect()->away($url . '?' . http_build_query($params));
}
// ponytail: VNPay auto-deposit disabled — nạp tiền hiện dùng luồng thủ công (ManualDepositController).
public function returnCallback(Request $request)
{
return $this->redirectToFrontend('failed');
// $data = $request->all();
//
// $vnp_TxnRef = $data['vnp_TxnRef'] ?? null;
// $vnp_Amount = $data['vnp_Amount'] ?? 0;
//
// FacadesLog::info('VNPay callback received', [
// 'vnp_TxnRef' => $vnp_TxnRef,
// 'vnp_Amount' => $vnp_Amount,
// 'user' => $request->user() ? $request->user()->id : 'not authenticated',
// ]);
//
// $transaction = Transaction::where('trans', 'DEP' . $vnp_TxnRef)->first();
//
// if (!$transaction) {
// return $this->redirectToFrontend('failed');
// }
//
// if ($transaction->status == TransactionStatus::COMPLETED->value) {
// return $this->redirectToFrontend('success', $transaction->public_code);
// }
//
// $verify = $this->vnpayService->verifyPayment($data);
//
// FacadesLog::info('VNPay callback debug', [
// 'isValid' => $verify['isValid'],
// 'isSuccess' => $verify['isSuccess'] ?? false,
// 'responseCode' => $data['vnp_ResponseCode'] ?? 'N/A',
// ]);
//
// if (!$verify['isValid']) {
// return $this->redirectToFrontend('failed', $transaction->public_code);
// }
//
// if (!$verify['isSuccess']) {
// $transaction->update(['status' => TransactionStatus::FAILED->value]);
// return $this->redirectToFrontend('failed', $transaction->public_code);
// }
//
// $originalAmount = $transaction->amount * 100;
// if ($vnp_Amount != $originalAmount) {
// $transaction->update(['status' => TransactionStatus::FAILED->value]);
// return $this->redirectToFrontend('failed', $transaction->public_code);
// }
//
// try {
// DB::beginTransaction();
//
// $transaction->update(['status' => TransactionStatus::COMPLETED->value]);
//
// $customer = Customer::find($transaction->customer_id);
//
// FacadesLog::info('Customer update debug', [
// 'customer_id' => $transaction->customer_id,
// 'customer_found' => $customer ? true : false,
// 'transaction_amount' => $transaction->amount,
// 'current_total' => $customer->total ?? 'N/A',
// 'current_credit' => $customer->credit ?? 'N/A',
// ]);
//
// $customer->total += $transaction->amount;
// $customer->credit += $transaction->amount / 100;
// $customer->save();
//
// FacadesLog::info('Customer after update', [
// 'new_total' => $customer->total,
// 'new_credit' => $customer->credit,
// ]);
//
// Log::create([
// 'agent' => 'api',
// 'client_ip' => $request->ip(),
// 'description' => 'Nạp tiền vào tài khoản: ' . number_format($transaction->amount) . ' VNĐ (+' . number_format($transaction->amount / 100) . ' credit)',
// 'customer_id' => $transaction->customer_id,
// ]);
//
// $this->activityLogger->log(
// event: 'deposit_success',
// logName: 'Transaction',
// description: "Customer {$customer->email} nạp tiền VNPay thành công: " . number_format($transaction->amount) . ' VNĐ - Mã: ' . $transaction->public_code,
// subject: $transaction,
// newValues: ['amount' => $transaction->amount, 'new_total' => $customer->total, 'public_code' => $transaction->public_code],
// );
//
// DepositSuccessJob::dispatch([
// 'customer_email' => $customer->email,
// 'customer_name' => $customer->fullname,
// 'transaction_code' => $transaction->public_code,
// 'amount' => $transaction->amount,
// 'credit' => $transaction->amount / 100,
// 'current_total' => $customer->total,
// 'current_credit' => $customer->credit,
// 'deposit_date' => now()->format('d/m/Y H:i:s'),
// ]);
//
// DB::commit();
// } catch (\Exception $e) {
// DB::rollBack();
// FacadesLog::error('Payment processing error: ' . $e->getMessage());
// return $this->redirectToFrontend('failed', $transaction->public_code);
// }
//
// return $this->redirectToFrontend('success', $transaction->public_code);
}
public function getTransactions(Request $request)
{
$user = $request->user();
$transactions = Transaction::where('customer_id', $user->id)
->orderBy('created_at', 'desc')
->get();
return response()->json([
'error' => false,
'message' => 'Lấy danh sách giao dịch thành công',
'data' => $transactions,
]);
}
public function verifyPayment(Request $request)
{
$transRef = $request->query('vnp_TxnRef');
if (!$transRef) {
return response()->json([
'error' => true,
'message' => 'Thiếu tham số vnp_TxnRef',
], 400);
}
$transaction = Transaction::where('trans', 'DEP' . $transRef)
->where('customer_id', $request->user()->id)
->first();
if (!$transaction) {
return response()->json([
'error' => true,
'message' => 'Giao dịch không tồn tại',
], 404);
}
return response()->json([
'error' => false,
'message' => 'Lấy thông tin giao dịch thành công',
'data' => [
'trans' => $transaction->trans,
'amount' => $transaction->amount,
'amount_vnp' => $transaction->amount * 100,
'status' => $transaction->status,
'status_label' => TransactionStatus::from($transaction->status)->label(),
'created_at' => $transaction->created_at,
'updated_at' => $transaction->updated_at,
],
]);
}
/**
* GET /v1/client/payment-history — combined deposit transactions + order invoice payments
*/
public function getClientPaymentHistory(Request $request): JsonResponse
{
$user = $request->user();
$deposits = Transaction::where('customer_id', $user->id)
->orderByDesc('created_at')
->get()
->map(fn(Transaction $t) => [
'type' => 'deposit',
'public_code' => $t->public_code,
'trans' => $t->trans,
'description' => $t->typeLabel(),
'direction' => $t->direction,
'amount' => (float) $t->amount,
'status' => $t->status,
'status_label' => TransactionStatus::from((int) $t->status)->label(),
'detail_key' => $t->uuid,
'created_at' => $t->created_at?->toISOString(),
]);
$invoicePayments = Invoice::with(['order.orderItems.product'])
->where('customer_id', $user->id)
->orderByDesc('created_at')
->get()
->map(fn(Invoice $inv) => [
'type' => 'order_payment',
'public_code' => $inv->invoice_number,
'trans' => null,
'description' => 'Thanh toán đơn hàng: ' . ($inv->order?->orderItems->first()?->product?->name ?? $inv->order?->order_num),
'direction' => 'debit',
'amount' => (float) $inv->total,
'status' => $inv->status,
'status_label' => \App\Enums\Invoice\InvoiceStatus::tryFrom((int) $inv->status)?->label() ?? '-',
'detail_key' => $inv->order?->order_num,
'created_at' => $inv->created_at?->toISOString(),
]);
$combined = $deposits->concat($invoicePayments)
->sortByDesc('created_at')
->values();
return response()->json([
'error' => false,
'message' => 'Lấy lịch sử thanh toán thành công',
'data' => $combined,
]);
}
public function getClientTransactionDetail(Request $request, string $uuid): JsonResponse
{
$transaction = Transaction::query()
->where('uuid', $uuid)
->where('customer_id', $request->user()->id)
->first();
if (! $transaction) {
return response()->json(['error' => true, 'message' => 'Giao dịch không tồn tại'], 404);
}
return response()->json([
'error' => false,
'data' => [
'uuid' => $transaction->uuid,
'public_code' => $transaction->public_code,
'trans' => $transaction->trans,
'type' => $transaction->type,
'type_label' => $transaction->typeLabel(),
'direction' => $transaction->direction,
'amount' => (int) $transaction->amount,
'received_amount' => $transaction->received_amount !== null ? (int) $transaction->received_amount : null,
'status' => (int) $transaction->status,
'status_label' => TransactionStatus::from((int) $transaction->status)->label(),
'has_proof' => $transaction->proof_image !== null,
'created_at' => $transaction->created_at?->toISOString(),
'updated_at' => $transaction->updated_at?->toISOString(),
],
]);
}
public function getByPublicCode(Request $request)
{
$code = $request->query('code');
if (!$code) {
return response()->json([
'error' => true,
'message' => 'Thiếu mã giao dịch',
], 400);
}
$transaction = Transaction::where('public_code', $code)
->where('customer_id', $request->user()->id)
->first();
if (!$transaction) {
return response()->json([
'error' => true,
'message' => 'Giao dịch không tồn tại',
], 404);
}
return response()->json([
'error' => false,
'message' => 'Lấy thông tin giao dịch thành công',
'data' => [
'public_code' => $transaction->public_code,
'amount' => $transaction->amount,
'amount_formatted' => number_format($transaction->amount) . ' VNĐ',
'status' => $transaction->status,
'status_label' => TransactionStatus::from($transaction->status)->label(),
'created_at' => $transaction->created_at->format('Y-m-d H:i:s'),
'paid_at' => $transaction->updated_at->format('Y-m-d H:i:s'),
],
]);
}
}