fix issue

This commit is contained in:
Nyavokevin 2025-11-17 16:50:58 +03:00
parent 7fbc746968
commit 79c52d0236
3 changed files with 245 additions and 55 deletions

View File

@ -116,50 +116,99 @@ class StripeController extends Controller
{
$clientSessionId = $request->query('client_session_id');
$payment = Payment::where('client_session_id', $clientSessionId)
->where('status', 'succeeded')
->first();
if (!$clientSessionId) {
return response()->json(['error' => 'Client session ID is required'], 400);
}
if ($payment) {
// Si la vérification réussit, retournez le nombre de tirages.
$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,
]);
}
// Si la vérification échoue, retournez une erreur.
// 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' => 'Paiement non validé.',
], 404);
'message' => 'Payment failed or cancelled.',
'status' => $payment->status,
], 402);
}
public function getCards(Request $request)
{
$sessionId = $request->query('client_session_id');
if(!$sessionId)
{
if (!$sessionId) {
$count = $request->query('count');
if($count == 1){
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
if ($payment->status === 'processed') {
// 2. One-Time Use Check - prevent double processing
if ($payment->status === 'processed' && $payment->cards) {
return response()->json([
'success' => true,
'cards' => $payment->cards,
@ -167,30 +216,81 @@ class StripeController extends Controller
]);
}
// 3. Verify payment status with Stripe
if ($payment->status !== 'succeeded') {
// 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);
if ($session->payment_status !== 'paid' || $session->status !== 'complete') {
return response()->json(['success' => false, 'message' => 'Payment not complete.'], 402);
\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);
}
$payment->update(['status' => 'succeeded']);
} catch (\Exception $e) {
\Log::error('Stripe session retrieval failed: ' . $e->getMessage());
return response()->json(['success' => false, 'message' => 'Validation error.'], 500);
\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. Securely draw the cards and store them
$drawnCards = $this->cardRepository->draw($payment->draw_count);
$payment->update([
'cards' => $drawnCards,
'status' => 'processed',
]);
return response()->json([
'success' => true,
'cards' => $drawnCards,
]);
// 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);
}
}
}

View File

@ -34,31 +34,67 @@ class WebhookController extends Controller
$session = $event->data->object;
$clientSessionId = $session->metadata->client_session_id;
Log::info('Processing checkout.session.completed webhook', [
'client_session_id' => $clientSessionId,
'session_id' => $session->id,
'payment_status' => $session->payment_status,
'status' => $session->status
]);
$payment = Payment::where('client_session_id', $clientSessionId)->first();
if ($payment) {
// Update the payment status to succeeded regardless of current status
// This ensures webhook updates work even if user already processed the payment
$updateData = ['status' => 'succeeded'];
if (isset($session->metadata->type_appointment) && $session->metadata->type_appointment === 'true') {
$dateTimeObj = new DateTime($session->metadata->appointment_date);
$payment->update([
'status' => 'succeeded',
'appointment_date' => $dateTimeObj->format('Y-m-d')
]);
$updateData['appointment_date'] = $dateTimeObj->format('Y-m-d');
} else {
// Original logic for other payments
$drawCount = $session->metadata->draw_count;
$payment->update([
'status' => 'succeeded',
'draw_count' => $drawCount,
]);
// Ensure draw_count is set correctly for card payments
if (isset($session->metadata->draw_count)) {
$updateData['draw_count'] = $session->metadata->draw_count;
}
}
$payment->update($updateData);
Log::info('Payment status updated via webhook', [
'client_session_id' => $clientSessionId,
'payment_id' => $payment->id,
'new_status' => 'succeeded'
]);
} else {
// Log if no matching payment record is found
Log::warning('No pending payment record found for client_session_id: ' . $clientSessionId);
Log::warning('No payment record found for webhook processing', [
'client_session_id' => $clientSessionId,
'stripe_session_id' => $session->id
]);
}
break;
case 'checkout.session.async_payment_failed':
case 'checkout.session.expired':
$session = $event->data->object;
$clientSessionId = $session->metadata->client_session_id ?? null;
if ($clientSessionId) {
$payment = Payment::where('client_session_id', $clientSessionId)->first();
if ($payment) {
$payment->update(['status' => 'failed']);
Log::info('Payment marked as failed via webhook', [
'client_session_id' => $clientSessionId,
'payment_id' => $payment->id,
'event_type' => $event->type
]);
}
}
break;
default:
Log::info('Received a non-checkout.session.completed webhook event: ' . $event->type);
Log::info('Received unhandled webhook event', ['event_type' => $event->type]);
break;
}
} catch (\Exception $e) {

View File

@ -5,6 +5,7 @@ use Inertia\Inertia;
use App\Models\Payment;
use Illuminate\Http\Request;
use App\Http\Controllers\CardImportController;
use Illuminate\Support\Facades\Log;
Route::get('/', function () {
return Inertia::render('Landing');
@ -47,11 +48,19 @@ Route::get('paiement', function () {
Route::get('/success', function (Request $request) {
$clientSessionId = $request->query('client_session_id');
$payment = Payment::where('client_session_id', $clientSessionId)
->where('status', 'succeeded') // Only check for succeeded payments
->first();
if (!$clientSessionId) {
return Inertia::render('payments/Error', ['message' => 'Invalid payment session.']);
}
if ($payment) {
$payment = Payment::where('client_session_id', $clientSessionId)->first();
if (!$payment) {
Log::warning('Payment record not found on success page', ['client_session_id' => $clientSessionId]);
return Inertia::render('payments/Error', ['message' => 'Payment record not found.']);
}
// If payment is already succeeded in our database
if ($payment->status === 'succeeded') {
return Inertia::render('payments/Success', [
'paymentSuccess' => true,
'drawCount' => $payment->draw_count,
@ -59,18 +68,63 @@ Route::get('/success', function (Request $request) {
]);
}
// If payment not found as succeeded, check if it's pending (especially for Wise)
$pendingPayment = Payment::where('client_session_id', $clientSessionId)->first();
if ($pendingPayment && $pendingPayment->status === 'pending') {
return Inertia::render('payments/Pending', [
'message' => 'Payment is being processed. Please wait...',
'clientSessionId' => $clientSessionId,
'paymentProvider' => $pendingPayment->payment_provider ?? 'stripe'
// If payment is pending, check with Stripe to handle race condition
if ($payment->status === 'pending') {
try {
\Stripe\Stripe::setApiKey(env('STRIPE_SECRET_KEY'));
$session = \Stripe\Checkout\Session::retrieve($clientSessionId);
if ($session->payment_status === 'paid' && $session->status === 'complete') {
// Payment confirmed, update our record
$payment->update(['status' => 'succeeded']);
Log::info('Payment status updated on success page via direct Stripe check', [
'client_session_id' => $clientSessionId,
'payment_id' => $payment->id
]);
return Inertia::render('payments/Success', [
'paymentSuccess' => true,
'drawCount' => $payment->draw_count,
'paymentProvider' => $payment->payment_provider ?? 'stripe'
]);
}
// Payment still pending, show pending page
return Inertia::render('payments/Pending', [
'message' => 'Payment is still being processed. Please wait...',
'clientSessionId' => $clientSessionId,
'paymentProvider' => $payment->payment_provider ?? 'stripe'
]);
} catch (\Exception $e) {
Log::error('Stripe session check failed on success page: ' . $e->getMessage(), [
'client_session_id' => $clientSessionId,
'payment_id' => $payment->id
]);
// Show pending page on Stripe check failure
return Inertia::render('payments/Pending', [
'message' => 'Verifying payment status. Please wait...',
'clientSessionId' => $clientSessionId,
'paymentProvider' => $payment->payment_provider ?? 'stripe'
]);
}
}
// Payment failed or cancelled
if ($payment->status === 'failed') {
return Inertia::render('payments/Error', [
'message' => 'Payment failed. Please try again.',
'paymentProvider' => $payment->payment_provider ?? 'stripe'
]);
}
return Inertia::render('payments/Error', ['message' => 'Payment validation failed.']);
// Any other status
return Inertia::render('payments/Error', [
'message' => 'Payment status: ' . $payment->status,
'paymentProvider' => $payment->payment_provider ?? 'stripe'
]);
})->name('payment.success');
Route::get('/rendez-vous/success', function (Request $request){