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
This commit is contained in:
parent
6f42b91585
commit
3cbe0a7879
|
|
@ -36,6 +36,21 @@ class TransactionSyncController extends Controller
|
||||||
$labelIds = $data['label_ids'] ?? [];
|
$labelIds = $data['label_ids'] ?? [];
|
||||||
unset($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
|
// Create transaction with provided ID if available
|
||||||
$transaction = new Transaction([
|
$transaction = new Transaction([
|
||||||
...$data,
|
...$data,
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,6 @@ class FeedbackEmail extends Mailable implements ShouldQueue
|
||||||
*/
|
*/
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
return [new RateLimited('emails', releaseAfter: 1)];
|
return [(new RateLimited('emails'))->releaseAfter(1)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,6 @@ class ImportHelpEmail extends Mailable implements ShouldQueue
|
||||||
*/
|
*/
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
return [new RateLimited('emails', releaseAfter: 1)];
|
return [(new RateLimited('emails'))->releaseAfter(1)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,6 @@ class OnboardingReminderEmail extends Mailable implements ShouldQueue
|
||||||
*/
|
*/
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
return [new RateLimited('emails', releaseAfter: 1)];
|
return [(new RateLimited('emails'))->releaseAfter(1)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,6 @@ class PromoCodeEmail extends Mailable implements ShouldQueue
|
||||||
*/
|
*/
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
return [new RateLimited('emails', releaseAfter: 1)];
|
return [(new RateLimited('emails'))->releaseAfter(1)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,6 @@ class WelcomeEmail extends Mailable implements ShouldQueue
|
||||||
*/
|
*/
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
return [new RateLimited('emails', releaseAfter: 1)];
|
return [(new RateLimited('emails'))->releaseAfter(1)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,6 @@ class UserLeadInvitation extends Mailable implements ShouldQueue
|
||||||
*/
|
*/
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
return [new RateLimited('emails', releaseAfter: 1)];
|
return [(new RateLimited('emails'))->releaseAfter(1)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue