From 0a10fd0194662200cfeb77fd650e2f5f8fceb1cf Mon Sep 17 00:00:00 2001 From: marco Date: Fri, 19 Dec 2025 17:20:12 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20Evitar=20cria=C3=A7=C3=A3o=20de=20fatura?= =?UTF-8?q?s=20duplicadas=20no=20webhook=20PayPal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🐛 PROBLEMA IDENTIFICADO: - Cliente pagava 1 assinatura mas recebia 2 faturas - Fatura 1: subscription_create (método confirm()) - Fatura 2: subscription_cycle (webhook PAYMENT.SALE.COMPLETED) - Ambas criadas com 8 segundos de diferença ✅ SOLUÇÃO IMPLEMENTADA: handlePaymentCompleted() agora verifica: 1. Se já existe fatura com mesmo paypal_payment_id - Evita duplicação por webhook reprocessado 2. Se já existe fatura paga HOJE para esta subscription - Evita duplicação do pagamento inicial - Webhook vem depois do confirm() 3. Só cria nova fatura se for pagamento recorrente genuíno 📊 RESULTADO: - Pagamento inicial: 1 fatura (subscription_create) - Renovação mensal/anual: 1 fatura (subscription_cycle) - Webhooks duplicados: ignorados com log 🗑️ LIMPEZA: - Removida fatura duplicada WM-2025-000002 do user_id 35 --- .../Api/SubscriptionController.php | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/backend/app/Http/Controllers/Api/SubscriptionController.php b/backend/app/Http/Controllers/Api/SubscriptionController.php index fcd35aa..7a5c934 100755 --- a/backend/app/Http/Controllers/Api/SubscriptionController.php +++ b/backend/app/Http/Controllers/Api/SubscriptionController.php @@ -843,6 +843,38 @@ private function handlePaymentCompleted(array $resource): void $subscription = Subscription::where('paypal_subscription_id', $billingAgreementId)->first(); if (!$subscription) return; + $paymentId = $resource['id'] ?? null; + + // Check if invoice already exists for this payment (avoid duplicates) + if ($paymentId) { + $existingInvoice = Invoice::where('paypal_payment_id', $paymentId) + ->orWhere('paypal_capture_id', $paymentId) + ->first(); + + if ($existingInvoice) { + Log::info('Invoice already exists for this payment', [ + 'payment_id' => $paymentId, + 'invoice_id' => $existingInvoice->id, + ]); + return; + } + } + + // Check if a paid invoice was already created for this subscription today (initial payment) + $todayInvoice = Invoice::where('subscription_id', $subscription->id) + ->where('status', Invoice::STATUS_PAID) + ->whereDate('created_at', today()) + ->first(); + + if ($todayInvoice) { + Log::info('Invoice already created today for subscription, skipping duplicate', [ + 'subscription_id' => $subscription->id, + 'existing_invoice_id' => $todayInvoice->id, + 'payment_id' => $paymentId, + ]); + return; + } + // Create invoice for recurring payment $invoice = Invoice::createForSubscription( $subscription,