243 lines
8.6 KiB
PHP
243 lines
8.6 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\Telegram;
|
|
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\Storage;
|
|
use Illuminate\Support\Str;
|
|
|
|
class ManualDepositController extends Controller
|
|
{
|
|
public function __construct(protected ActivityLogger $activityLogger) {}
|
|
/**
|
|
* GET /v1/transaction/manual-deposit/pending
|
|
* Kiểm tra xem user có GD manual đang chờ duyệt (đã submit proof) không.
|
|
*/
|
|
public function pending(Request $request): JsonResponse
|
|
{
|
|
$user = $request->user();
|
|
|
|
$transaction = Transaction::where('customer_id', $user->id)
|
|
->where('type', 'manual')
|
|
->where('status', TransactionStatus::PENDING->value)
|
|
->whereNotNull('proof_image')
|
|
->latest()
|
|
->first();
|
|
|
|
if (!$transaction) {
|
|
return response()->json(['error' => false, 'data' => ['has_pending' => false]]);
|
|
}
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'data' => [
|
|
'has_pending' => true,
|
|
'transaction' => [
|
|
'public_code' => $transaction->public_code,
|
|
'amount' => $transaction->amount,
|
|
'created_at' => $transaction->created_at?->toISOString(),
|
|
],
|
|
],
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* POST /v1/transaction/manual-deposit/prepare
|
|
* Tạo transaction PENDING và trả về public_code để show trong QR / nội dung CK.
|
|
* Client gọi khi chuyển sang bước chọn ngân hàng.
|
|
*/
|
|
public function prepare(DepositRequest $request): JsonResponse
|
|
{
|
|
$user = $request->user();
|
|
|
|
// Chặn nếu đã có GD đã submit đang chờ admin duyệt
|
|
$submitted = Transaction::where('customer_id', $user->id)
|
|
->where('type', 'manual')
|
|
->where('status', TransactionStatus::PENDING->value)
|
|
->whereNotNull('proof_image')
|
|
->exists();
|
|
|
|
if ($submitted) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => 'Bạn đang có yêu cầu nạp tiền chờ duyệt. Vui lòng chờ admin xác nhận trước khi tạo yêu cầu mới.',
|
|
], 422);
|
|
}
|
|
|
|
// Huỷ transaction prepare cũ (chưa có proof) nếu còn tồn tại
|
|
$old = Transaction::where('customer_id', $user->id)
|
|
->where('type', 'manual')
|
|
->where('status', TransactionStatus::PENDING->value)
|
|
->whereNull('proof_image')
|
|
->latest()
|
|
->first();
|
|
|
|
if ($old) {
|
|
DB::transaction(function () use ($old, $user) {
|
|
$user->total_pending = max(0, $user->total_pending - $old->amount);
|
|
$user->save();
|
|
$old->forceDelete();
|
|
});
|
|
}
|
|
|
|
$uuid = Str::uuid()->toString();
|
|
$publicCode = 'MAN-' . now()->format('Ymd') . '-' . strtoupper(Str::random(6));
|
|
|
|
DB::beginTransaction();
|
|
try {
|
|
Transaction::create([
|
|
'uuid' => $uuid,
|
|
'public_code' => $publicCode,
|
|
'trans' => 'MAN' . time(),
|
|
'amount' => $request->amount,
|
|
'customer_id' => $user->id,
|
|
'type' => 'manual',
|
|
'status' => TransactionStatus::PENDING->value,
|
|
]);
|
|
|
|
$user->total_pending += $request->amount;
|
|
$user->save();
|
|
|
|
DB::commit();
|
|
} catch (\Exception) {
|
|
DB::rollBack();
|
|
return response()->json(['error' => true, 'message' => 'Có lỗi xảy ra, vui lòng thử lại'], 500);
|
|
}
|
|
|
|
$this->activityLogger->log(
|
|
event: 'deposit_prepare',
|
|
logName: 'Transaction',
|
|
description: "Customer {$user->email} tạo yêu cầu nạp tiền thủ công: " . number_format($request->amount) . ' VNĐ - Mã: ' . $publicCode,
|
|
newValues: ['public_code' => $publicCode, 'amount' => $request->amount, 'type' => 'manual'],
|
|
);
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'data' => [
|
|
'uuid' => $uuid,
|
|
'public_code' => $publicCode,
|
|
],
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* DELETE /v1/transaction/manual-deposit/{uuid}/cancel
|
|
* Huỷ transaction đang prepare (chưa có proof, chưa submit).
|
|
* Client gọi khi user quay lại step 1 để đổi số tiền.
|
|
*/
|
|
public function cancel(Request $request, string $uuid): JsonResponse
|
|
{
|
|
$user = $request->user();
|
|
|
|
$transaction = Transaction::where('uuid', $uuid)
|
|
->where('customer_id', $user->id)
|
|
->where('type', 'manual')
|
|
->where('status', TransactionStatus::PENDING->value)
|
|
->whereNull('proof_image')
|
|
->firstOrFail();
|
|
|
|
$amount = $transaction->amount;
|
|
$publicCode = $transaction->public_code;
|
|
|
|
DB::transaction(function () use ($transaction, $user) {
|
|
$user->total_pending = max(0, $user->total_pending - $transaction->amount);
|
|
$user->save();
|
|
$transaction->forceDelete();
|
|
});
|
|
|
|
$this->activityLogger->log(
|
|
event: 'deposit_cancel',
|
|
logName: 'Transaction',
|
|
description: "Customer {$user->email} huỷ yêu cầu nạp tiền thủ công: " . number_format($amount) . ' VNĐ - Mã: ' . $publicCode,
|
|
newValues: ['public_code' => $publicCode, 'amount' => $amount],
|
|
);
|
|
|
|
return response()->json(['error' => false, 'message' => 'Đã huỷ yêu cầu']);
|
|
}
|
|
|
|
/**
|
|
* POST /v1/transaction/manual-deposit/confirm
|
|
* Attach proof image vào transaction đã prepare rồi đánh dấu đã submit.
|
|
* Client gọi sau khi upload proof ở step 3.
|
|
*/
|
|
public function confirm(Request $request): JsonResponse
|
|
{
|
|
$request->validate([
|
|
'uuid' => 'required|string|uuid',
|
|
'proof_image' => 'required|string|max:500',
|
|
]);
|
|
|
|
$user = $request->user();
|
|
|
|
$transaction = Transaction::where('uuid', $request->uuid)
|
|
->where('customer_id', $user->id)
|
|
->where('type', 'manual')
|
|
->where('status', TransactionStatus::PENDING->value)
|
|
->whereNull('proof_image')
|
|
->first();
|
|
|
|
if (!$transaction) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => 'Giao dịch không hợp lệ hoặc đã được xử lý',
|
|
], 422);
|
|
}
|
|
|
|
$transaction->update(['proof_image' => $request->proof_image]);
|
|
|
|
try {
|
|
(new Telegram())->SendManualDepositPendingApproval($user, $transaction);
|
|
} catch (\Exception) {
|
|
// Không để lỗi Telegram ảnh hưởng response
|
|
}
|
|
|
|
Log::create([
|
|
'agent' => 'api',
|
|
'client_ip' => $request->ip(),
|
|
'description' => 'Gửi yêu cầu nạp tiền thủ công: ' . number_format($transaction->amount) . ' VNĐ (chờ duyệt)',
|
|
'customer_id' => $user->id,
|
|
]);
|
|
|
|
$this->activityLogger->log(
|
|
event: 'deposit_confirm',
|
|
logName: 'Transaction',
|
|
description: "Customer {$user->email} nộp chứng từ nạp tiền thủ công: " . number_format($transaction->amount) . ' VNĐ - Mã: ' . $transaction->public_code,
|
|
newValues: ['public_code' => $transaction->public_code, 'amount' => $transaction->amount, 'has_proof' => true],
|
|
);
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'message' => 'Yêu cầu nạp tiền đã được ghi nhận, vui lòng chờ admin xác nhận',
|
|
'data' => ['public_code' => $transaction->public_code],
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* POST /v1/transaction/manual-deposit/upload-proof
|
|
* Upload ảnh chứng từ, trả về path để dùng ở bước confirm.
|
|
*/
|
|
public function uploadProof(Request $request): JsonResponse
|
|
{
|
|
$request->validate([
|
|
'file' => 'required|image|mimes:jpeg,jpg,png|max:5120',
|
|
]);
|
|
|
|
$path = $request->file('file')->store('deposit-proofs', 'public');
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'url' => Storage::disk('public')->url($path),
|
|
'path' => $path,
|
|
]);
|
|
}
|
|
}
|