KSA-ORACLE/app/Http/Controllers/StripeController.php
2025-11-17 16:50:58 +03:00

297 lines
11 KiB
PHP

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Stripe\Stripe;
use Stripe\Checkout\Session;
use Illuminate\Support\Str;
use App\Models\Payment;
use App\Repositories\CardRepositoryInterface;
class StripeController extends Controller
{
public function __construct(CardRepositoryInterface $cardRepository)
{
$this->cardRepository = $cardRepository;
}
public function createCheckoutSession(Request $request)
{
Stripe::setApiKey(env('STRIPE_SECRET_KEY'));
$count = $request->input('count');
$clientSessionId = Str::uuid();
$priceIds = [
6 => env('STRIPE_6_PRICE'),
18 => env('STRIPE_18_PRICE'),
];
if (!isset($priceIds[$count])) {
return response()->json(['error' => 'Invalid product selected.'], 400);
}
try {
$session = Session::create([
'line_items' => [[
'price' => $priceIds[$count],
'quantity' => 1,
]],
'mode' => 'payment',
'success_url' => url(env('APP_URL') . '/success?client_session_id=' . $clientSessionId),
'cancel_url' => url(env('APP_URL') . '/cancel'),
'metadata' => [
'draw_count' => $request->input('count'),
'client_session_id' => $clientSessionId,
],
]);
Payment::create([
'amount' => $session->amount_total / 100,
'currency' => $session->currency,
'stripe_session_id' => $session->id,
'client_session_id' => $clientSessionId,
'draw_count' => $count,
'status' => 'pending',
]);
return response()->json(['sessionId' => $session->id]);
} catch (\Exception $e) {
\Log::error('Stripe session creation failed: ' . $e->getMessage());
return response()->json(['error' => 'Could not create checkout session.'], 500);
}
}
public function createRendezVousSession(Request $request)
{
Stripe::setApiKey(env('STRIPE_SECRET_KEY'));
$userForm = $request->input('userForm');
$dateAppointment = $request->input('selectedDate');
$clientSessionId = Str::uuid();
$priceId = env('STRIPE_BOOKING');
try {
$session = Session::create([
'line_items' => [[
'price' => $priceId,
'quantity' => 1,
]],
'mode' => 'payment',
'success_url' => url(env('APP_URL') . '/rendez-vous/success?client_session_id=' . $clientSessionId),
'cancel_url' => url(env('APP_URL') . '/cancel'),
'metadata' => [
'client_session_id' => $clientSessionId,
'type_appointment' => true,
'appointment_date' => $dateAppointment
],
'customer_email' => $userForm["email"]
]);
Payment::create([
'amount' => $session->amount_total / 100,
'currency' => $session->currency,
'stripe_session_id' => $session->id,
'client_session_id' => $clientSessionId,
'draw_count' => 0,
'status' => 'pending',
]);
return response()->json(['sessionId' => $session->id]);
} catch (\Exception $e) {
\Log::error('Stripe session creation failed: ' . $e->getMessage());
return response()->json(['error' => 'Could not create checkout session.'], 500);
}
}
public function validatePayment(Request $request)
{
$clientSessionId = $request->query('client_session_id');
if (!$clientSessionId) {
return response()->json(['error' => 'Client session ID is required'], 400);
}
$payment = Payment::where('client_session_id', $clientSessionId)->first();
if (!$payment) {
return response()->json([
'success' => false,
'message' => 'Payment not found.',
], 404);
}
// If payment is already succeeded in our database
if ($payment->status === 'succeeded') {
return response()->json([
'success' => true,
'drawCount' => $payment->draw_count,
'cached' => true,
]);
}
// If payment is pending, check with Stripe directly to handle race condition
if ($payment->status === 'pending') {
try {
Stripe::setApiKey(env('STRIPE_SECRET_KEY'));
$session = Session::retrieve($clientSessionId);
// Check if payment is completed on Stripe side
if ($session->payment_status === 'paid' && $session->status === 'complete') {
// Update our payment record and mark as succeeded
$payment->update(['status' => 'succeeded']);
return response()->json([
'success' => true,
'drawCount' => $payment->draw_count,
'updated' => true,
]);
}
// Payment not completed yet, return pending status
return response()->json([
'success' => false,
'message' => 'Payment is still being processed.',
'status' => 'pending',
], 202);
} catch (\Exception $e) {
\Log::error('Stripe validation failed: ' . $e->getMessage(), [
'client_session_id' => $clientSessionId,
'payment_id' => $payment->id
]);
return response()->json([
'success' => false,
'message' => 'Payment validation error.',
], 500);
}
}
// Payment failed or has other status
return response()->json([
'success' => false,
'message' => 'Payment failed or cancelled.',
'status' => $payment->status,
], 402);
}
public function getCards(Request $request)
{
$sessionId = $request->query('client_session_id');
if (!$sessionId) {
$count = $request->query('count');
if ($count == 1) {
$freeCards = $this->cardRepository->draw(1);
return response()->json([
'success' => true,
'cards' => $freeCards
]);
}
return response()->json(['success' => false, 'message' => 'Client session ID is required for paid cards.'], 400);
}
// 1. Find the payment record
$payment = Payment::where('client_session_id', $sessionId)->first();
if (!$payment) {
\Log::warning('Payment record not found', ['client_session_id' => $sessionId]);
return response()->json(['success' => false, 'message' => 'Payment not found.'], 404);
}
// 2. One-Time Use Check - prevent double processing
if ($payment->status === 'processed' && $payment->cards) {
return response()->json([
'success' => true,
'cards' => $payment->cards,
'message' => 'Cards already drawn for this payment.',
]);
}
// 3. Handle race condition - verify with Stripe if status is pending
if ($payment->status === 'pending' || $payment->status === 'failed') {
try {
Stripe::setApiKey(env('STRIPE_SECRET_KEY'));
$session = Session::retrieve($sessionId);
\Log::info('Checking Stripe session status', [
'client_session_id' => $sessionId,
'stripe_status' => $session->status,
'payment_status' => $session->payment_status,
'our_status' => $payment->status
]);
if ($session->payment_status === 'paid' && $session->status === 'complete') {
// Payment confirmed, update our record
$payment->update(['status' => 'succeeded']);
\Log::info('Payment status updated to succeeded via direct Stripe check', [
'client_session_id' => $sessionId,
'payment_id' => $payment->id
]);
} else {
// Payment still pending or failed
\Log::warning('Payment not complete', [
'client_session_id' => $sessionId,
'stripe_status' => $session->status,
'payment_status' => $session->payment_status
]);
return response()->json([
'success' => false,
'message' => 'Payment is still being processed or failed. Please wait or try again.',
'status' => $session->payment_status
], 202);
}
} catch (\Exception $e) {
\Log::error('Stripe session retrieval failed in getCards: ' . $e->getMessage(), [
'client_session_id' => $sessionId,
'payment_id' => $payment->id,
'exception' => $e
]);
return response()->json(['success' => false, 'message' => 'Unable to verify payment status.'], 500);
}
}
// 4. Only proceed if payment is definitely succeeded
if ($payment->status !== 'succeeded') {
return response()->json(['success' => false, 'message' => 'Payment not completed.'], 402);
}
// 5. Draw the cards and store them atomically
try {
$drawnCards = $this->cardRepository->draw($payment->draw_count);
$payment->update([
'cards' => $drawnCards,
'status' => 'processed',
'processed_at' => now(),
]);
\Log::info('Cards drawn successfully', [
'client_session_id' => $sessionId,
'payment_id' => $payment->id,
'draw_count' => $payment->draw_count
]);
return response()->json([
'success' => true,
'cards' => $drawnCards,
]);
} catch (\Exception $e) {
\Log::error('Card drawing failed: ' . $e->getMessage(), [
'client_session_id' => $sessionId,
'payment_id' => $payment->id,
'exception' => $e
]);
return response()->json(['success' => false, 'message' => 'Failed to draw cards.'], 500);
}
}
}