fix issue
This commit is contained in:
parent
7fbc746968
commit
79c52d0236
@ -116,50 +116,99 @@ class StripeController extends Controller
|
|||||||
{
|
{
|
||||||
$clientSessionId = $request->query('client_session_id');
|
$clientSessionId = $request->query('client_session_id');
|
||||||
|
|
||||||
$payment = Payment::where('client_session_id', $clientSessionId)
|
if (!$clientSessionId) {
|
||||||
->where('status', 'succeeded')
|
return response()->json(['error' => 'Client session ID is required'], 400);
|
||||||
->first();
|
}
|
||||||
|
|
||||||
if ($payment) {
|
$payment = Payment::where('client_session_id', $clientSessionId)->first();
|
||||||
// Si la vérification réussit, retournez le nombre de tirages.
|
|
||||||
|
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([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'drawCount' => $payment->draw_count,
|
'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([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => 'Paiement non validé.',
|
'message' => 'Payment failed or cancelled.',
|
||||||
], 404);
|
'status' => $payment->status,
|
||||||
|
], 402);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCards(Request $request)
|
public function getCards(Request $request)
|
||||||
{
|
{
|
||||||
$sessionId = $request->query('client_session_id');
|
$sessionId = $request->query('client_session_id');
|
||||||
|
|
||||||
if(!$sessionId)
|
if (!$sessionId) {
|
||||||
{
|
|
||||||
$count = $request->query('count');
|
$count = $request->query('count');
|
||||||
if($count == 1){
|
if ($count == 1) {
|
||||||
$freeCards = $this->cardRepository->draw(1);
|
$freeCards = $this->cardRepository->draw(1);
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'cards' => $freeCards
|
'cards' => $freeCards
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
return response()->json(['success' => false, 'message' => 'Client session ID is required for paid cards.'], 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Find the payment record
|
// 1. Find the payment record
|
||||||
$payment = Payment::where('client_session_id', $sessionId)->first();
|
$payment = Payment::where('client_session_id', $sessionId)->first();
|
||||||
|
|
||||||
if (!$payment) {
|
if (!$payment) {
|
||||||
|
\Log::warning('Payment record not found', ['client_session_id' => $sessionId]);
|
||||||
return response()->json(['success' => false, 'message' => 'Payment not found.'], 404);
|
return response()->json(['success' => false, 'message' => 'Payment not found.'], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. One-Time Use Check
|
// 2. One-Time Use Check - prevent double processing
|
||||||
if ($payment->status === 'processed') {
|
if ($payment->status === 'processed' && $payment->cards) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'cards' => $payment->cards,
|
'cards' => $payment->cards,
|
||||||
@ -167,30 +216,81 @@ class StripeController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Verify payment status with Stripe
|
// 3. Handle race condition - verify with Stripe if status is pending
|
||||||
if ($payment->status !== 'succeeded') {
|
if ($payment->status === 'pending' || $payment->status === 'failed') {
|
||||||
try {
|
try {
|
||||||
|
Stripe::setApiKey(env('STRIPE_SECRET_KEY'));
|
||||||
$session = Session::retrieve($sessionId);
|
$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) {
|
} catch (\Exception $e) {
|
||||||
\Log::error('Stripe session retrieval failed: ' . $e->getMessage());
|
\Log::error('Stripe session retrieval failed in getCards: ' . $e->getMessage(), [
|
||||||
return response()->json(['success' => false, 'message' => 'Validation error.'], 500);
|
'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
|
// 4. Only proceed if payment is definitely succeeded
|
||||||
$drawnCards = $this->cardRepository->draw($payment->draw_count);
|
if ($payment->status !== 'succeeded') {
|
||||||
$payment->update([
|
return response()->json(['success' => false, 'message' => 'Payment not completed.'], 402);
|
||||||
'cards' => $drawnCards,
|
}
|
||||||
'status' => 'processed',
|
|
||||||
]);
|
// 5. Draw the cards and store them atomically
|
||||||
return response()->json([
|
try {
|
||||||
'success' => true,
|
$drawnCards = $this->cardRepository->draw($payment->draw_count);
|
||||||
'cards' => $drawnCards,
|
$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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,31 +34,67 @@ class WebhookController extends Controller
|
|||||||
$session = $event->data->object;
|
$session = $event->data->object;
|
||||||
$clientSessionId = $session->metadata->client_session_id;
|
$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();
|
$payment = Payment::where('client_session_id', $clientSessionId)->first();
|
||||||
|
|
||||||
if ($payment) {
|
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') {
|
if (isset($session->metadata->type_appointment) && $session->metadata->type_appointment === 'true') {
|
||||||
$dateTimeObj = new DateTime($session->metadata->appointment_date);
|
$dateTimeObj = new DateTime($session->metadata->appointment_date);
|
||||||
$payment->update([
|
$updateData['appointment_date'] = $dateTimeObj->format('Y-m-d');
|
||||||
'status' => 'succeeded',
|
|
||||||
'appointment_date' => $dateTimeObj->format('Y-m-d')
|
|
||||||
]);
|
|
||||||
} else {
|
} else {
|
||||||
// Original logic for other payments
|
// Ensure draw_count is set correctly for card payments
|
||||||
$drawCount = $session->metadata->draw_count;
|
if (isset($session->metadata->draw_count)) {
|
||||||
$payment->update([
|
$updateData['draw_count'] = $session->metadata->draw_count;
|
||||||
'status' => 'succeeded',
|
}
|
||||||
'draw_count' => $drawCount,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$payment->update($updateData);
|
||||||
|
|
||||||
|
Log::info('Payment status updated via webhook', [
|
||||||
|
'client_session_id' => $clientSessionId,
|
||||||
|
'payment_id' => $payment->id,
|
||||||
|
'new_status' => 'succeeded'
|
||||||
|
]);
|
||||||
} else {
|
} else {
|
||||||
// Log if no matching payment record is found
|
// 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;
|
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:
|
default:
|
||||||
Log::info('Received a non-checkout.session.completed webhook event: ' . $event->type);
|
Log::info('Received unhandled webhook event', ['event_type' => $event->type]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ use Inertia\Inertia;
|
|||||||
use App\Models\Payment;
|
use App\Models\Payment;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use App\Http\Controllers\CardImportController;
|
use App\Http\Controllers\CardImportController;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
Route::get('/', function () {
|
Route::get('/', function () {
|
||||||
return Inertia::render('Landing');
|
return Inertia::render('Landing');
|
||||||
@ -47,11 +48,19 @@ Route::get('paiement', function () {
|
|||||||
Route::get('/success', function (Request $request) {
|
Route::get('/success', function (Request $request) {
|
||||||
$clientSessionId = $request->query('client_session_id');
|
$clientSessionId = $request->query('client_session_id');
|
||||||
|
|
||||||
$payment = Payment::where('client_session_id', $clientSessionId)
|
if (!$clientSessionId) {
|
||||||
->where('status', 'succeeded') // Only check for succeeded payments
|
return Inertia::render('payments/Error', ['message' => 'Invalid payment session.']);
|
||||||
->first();
|
}
|
||||||
|
|
||||||
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', [
|
return Inertia::render('payments/Success', [
|
||||||
'paymentSuccess' => true,
|
'paymentSuccess' => true,
|
||||||
'drawCount' => $payment->draw_count,
|
'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)
|
// If payment is pending, check with Stripe to handle race condition
|
||||||
$pendingPayment = Payment::where('client_session_id', $clientSessionId)->first();
|
if ($payment->status === 'pending') {
|
||||||
|
try {
|
||||||
|
\Stripe\Stripe::setApiKey(env('STRIPE_SECRET_KEY'));
|
||||||
|
$session = \Stripe\Checkout\Session::retrieve($clientSessionId);
|
||||||
|
|
||||||
if ($pendingPayment && $pendingPayment->status === 'pending') {
|
if ($session->payment_status === 'paid' && $session->status === 'complete') {
|
||||||
return Inertia::render('payments/Pending', [
|
// Payment confirmed, update our record
|
||||||
'message' => 'Payment is being processed. Please wait...',
|
$payment->update(['status' => 'succeeded']);
|
||||||
'clientSessionId' => $clientSessionId,
|
|
||||||
'paymentProvider' => $pendingPayment->payment_provider ?? 'stripe'
|
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');
|
})->name('payment.success');
|
||||||
|
|
||||||
Route::get('/rendez-vous/success', function (Request $request){
|
Route::get('/rendez-vous/success', function (Request $request){
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user