From 3cbe0a7879df68affe62944901dfc2054855fbf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Falc=C3=B3n?= Date: Sat, 27 Dec 2025 13:23:05 +0000 Subject: [PATCH] fix(sync): make transaction creation idempotent (#38) ## Summary - When a transaction ID is provided during sync, check if it already exists before creating - If it exists, return the existing transaction with 200 status instead of failing with duplicate key error - Prevents duplicate transactions when sync retries occur due to network issues ## Test plan - [x] Attempt to create a transaction with a specific ID - [x] Attempt to create another transaction with the same ID - [x] Verify the second request returns the existing transaction instead of an error --- .../Sync/TransactionSyncController.php | 15 +++++++++++++++ app/Mail/Drip/FeedbackEmail.php | 2 +- app/Mail/Drip/ImportHelpEmail.php | 2 +- app/Mail/Drip/OnboardingReminderEmail.php | 2 +- app/Mail/Drip/PromoCodeEmail.php | 2 +- app/Mail/Drip/WelcomeEmail.php | 2 +- app/Mail/UserLeadInvitation.php | 2 +- 7 files changed, 21 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/Sync/TransactionSyncController.php b/app/Http/Controllers/Sync/TransactionSyncController.php index 537f585b..6022ae48 100644 --- a/app/Http/Controllers/Sync/TransactionSyncController.php +++ b/app/Http/Controllers/Sync/TransactionSyncController.php @@ -36,6 +36,21 @@ class TransactionSyncController extends Controller $labelIds = $data['label_ids'] ?? []; unset($data['label_ids']); + // If ID is provided, check if transaction already exists (idempotent create) + if (isset($data['id'])) { + $existing = Transaction::query() + ->where('id', $data['id']) + ->where('user_id', $request->user()->id) + ->first(); + + if ($existing) { + // Transaction already exists, return it as success (idempotent) + return response()->json([ + 'data' => $existing->load('labels:id,name,color'), + ], 200); + } + } + // Create transaction with provided ID if available $transaction = new Transaction([ ...$data, diff --git a/app/Mail/Drip/FeedbackEmail.php b/app/Mail/Drip/FeedbackEmail.php index 02180c7b..255a0274 100644 --- a/app/Mail/Drip/FeedbackEmail.php +++ b/app/Mail/Drip/FeedbackEmail.php @@ -58,6 +58,6 @@ class FeedbackEmail extends Mailable implements ShouldQueue */ public function middleware(): array { - return [new RateLimited('emails', releaseAfter: 1)]; + return [(new RateLimited('emails'))->releaseAfter(1)]; } } diff --git a/app/Mail/Drip/ImportHelpEmail.php b/app/Mail/Drip/ImportHelpEmail.php index fa3fe7b2..5e955090 100644 --- a/app/Mail/Drip/ImportHelpEmail.php +++ b/app/Mail/Drip/ImportHelpEmail.php @@ -58,6 +58,6 @@ class ImportHelpEmail extends Mailable implements ShouldQueue */ public function middleware(): array { - return [new RateLimited('emails', releaseAfter: 1)]; + return [(new RateLimited('emails'))->releaseAfter(1)]; } } diff --git a/app/Mail/Drip/OnboardingReminderEmail.php b/app/Mail/Drip/OnboardingReminderEmail.php index 82765651..d2256898 100644 --- a/app/Mail/Drip/OnboardingReminderEmail.php +++ b/app/Mail/Drip/OnboardingReminderEmail.php @@ -58,6 +58,6 @@ class OnboardingReminderEmail extends Mailable implements ShouldQueue */ public function middleware(): array { - return [new RateLimited('emails', releaseAfter: 1)]; + return [(new RateLimited('emails'))->releaseAfter(1)]; } } diff --git a/app/Mail/Drip/PromoCodeEmail.php b/app/Mail/Drip/PromoCodeEmail.php index d7b4cc7e..09d6b70b 100644 --- a/app/Mail/Drip/PromoCodeEmail.php +++ b/app/Mail/Drip/PromoCodeEmail.php @@ -59,6 +59,6 @@ class PromoCodeEmail extends Mailable implements ShouldQueue */ public function middleware(): array { - return [new RateLimited('emails', releaseAfter: 1)]; + return [(new RateLimited('emails'))->releaseAfter(1)]; } } diff --git a/app/Mail/Drip/WelcomeEmail.php b/app/Mail/Drip/WelcomeEmail.php index c4dee602..d994e0b5 100644 --- a/app/Mail/Drip/WelcomeEmail.php +++ b/app/Mail/Drip/WelcomeEmail.php @@ -58,6 +58,6 @@ class WelcomeEmail extends Mailable implements ShouldQueue */ public function middleware(): array { - return [new RateLimited('emails', releaseAfter: 1)]; + return [(new RateLimited('emails'))->releaseAfter(1)]; } } diff --git a/app/Mail/UserLeadInvitation.php b/app/Mail/UserLeadInvitation.php index 682d076e..349715dd 100644 --- a/app/Mail/UserLeadInvitation.php +++ b/app/Mail/UserLeadInvitation.php @@ -74,6 +74,6 @@ class UserLeadInvitation extends Mailable implements ShouldQueue */ public function middleware(): array { - return [new RateLimited('emails', releaseAfter: 1)]; + return [(new RateLimited('emails'))->releaseAfter(1)]; } }