Skip to content

feat(events): implementa endpoint de GPS check-in HE4-48#226

Open
GustavoSimao wants to merge 1 commit into
he4rt:4.xfrom
GustavoSimao:feat/gps-checkin
Open

feat(events): implementa endpoint de GPS check-in HE4-48#226
GustavoSimao wants to merge 1 commit into
he4rt:4.xfrom
GustavoSimao:feat/gps-checkin

Conversation

@GustavoSimao
Copy link
Copy Markdown

@GustavoSimao GustavoSimao commented May 15, 2026

Description

Implements a GPS-based check-in endpoint for event attendance verification with distance validation using the Haversine formula and XP award integration. The feature validates check-in timing windows (30 minutes around event time), prevents duplicate verifications, calculates rewards based on event configuration, and tracks verification metadata per attendee.

References

Dependencies & Requirements

No new external dependencies added. The implementation uses existing Laravel framework components. Requirements:

  • Events must have status, location_lat, location_lng, gps_radius, and xp_base fields
  • Attendees must be registered for the event to perform check-in
  • Client must submit valid latitude and longitude coordinates

Contributor Summary

Contributor Lines Added Lines Removed Files Changed
GustavoSimao 535 1 15

Changes Summary

File Path Change Description
app-modules/events/src/Actions/GpsCheckinAction.php Core action orchestrating GPS verification with window validation, idempotency checks, and XP integration
app-modules/events/src/Actions/HaversineAction.php Calculates great-circle distance between coordinates in meters
app-modules/events/src/Actions/XpCalculatorAction.php Computes XP awards based on event base XP (multiplied by streak, pending HE4-47)
app-modules/events/src/Actions/UpdateCheckinAction.php Updates pivot record with verification state, timestamp, method, XP awarded
app-modules/events/src/Enums/EventStatusEnum.php New enum defining Scheduled event status
app-modules/events/src/Enums/CheckinStatusEnum.php New enum defining Verified check-in status
app-modules/events/src/Http/Controllers/GpsCheckinController.php HTTP endpoint handler delegating to GpsCheckinAction
app-modules/events/src/Http/Requests/GpsCheckinRequest.php Form request validating latitude and longitude coordinates
app-modules/events/routes/checkin-routes.php Route registration for POST /api/events/{event}/checkin with auth:sanctum middleware
app-modules/events/database/migrations/2026_05_13_112452_add_checkin_fields_to_events_table.php Adds status, location coordinates, GPS radius, and base XP fields to events table
app-modules/events/database/migrations/2026_05_13_112653_add_checkin_fields_to_events_attendees_table.php Adds check-in tracking fields (state, verified_at, verification_method, xp_awarded, streak_multiplier) to pivot
app-modules/events/src/Models/EventModel.php Updated with fillable fields, GPS coordinate casts, and expanded pivot relationships
app-modules/events/src/Models/Pivot/EventAttend.php Extended with check-in state tracking, verification metadata, and mass assignment configuration
app-modules/events/src/EventsServiceProvider.php Registers check-in routes via loadRoutesFrom
tests/Feature/GpsCheckinTest.php Feature test suite covering 8 scenarios including authorization, validation, window timing, radius constraints, and successful verification

Notes: Streak multiplier functionality (HE4-47) is pending; related values are fixed at 1 and 0 respectively, marked with TODO comments in the codebase.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 15, 2026

📝 Walkthrough

Walkthrough

This pull request adds a complete GPS-based event check-in system. It introduces database schema migrations to extend events and events_attendees tables with geolocation, status, and XP fields. New enums define event and check-in statuses. Models are updated to expose pivot columns for verification state and XP tracking. An HTTP endpoint POST /api/events/{event}/checkin validates GPS coordinates, verifies attendee registration and scheduling windows, computes great-circle distance to the event location, awards XP, and returns verification details. Supporting actions handle distance calculation (Haversine formula), XP derivation, and check-in state persistence. Comprehensive feature tests validate eight distinct scenarios covering registration, timing, radius, and conflict cases.

Suggested reviewers

  • thalesmengue
  • gvieira18
  • Clintonrocha98
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title clearly and specifically describes the main change: implementing a GPS check-in endpoint for events with the associated task identifier HE4-48.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (2)
app-modules/events/src/Models/EventModel.php (1)

266-277: ⚡ Quick win

Cast gps_radius and xp_base explicitly to integers.

Those fields are numeric business values; explicit casts prevent type drift in strict runtime paths.

Suggested patch
     protected function casts(): array
     {
         return [
             'active' => 'boolean',
             'event_at' => 'datetime',
             'start_at' => 'datetime',
             'end_at' => 'datetime',
             'event_type' => EventTypeEnum::class,
             'status' => EventStatusEnum::class,
             'location_lat' => 'float',
             'location_lng' => 'float',
+            'gps_radius' => 'integer',
+            'xp_base' => 'integer',
         ];
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app-modules/events/src/Models/EventModel.php` around lines 266 - 277, The
casts() method currently returns type casts for event fields but misses explicit
integer casts for the numeric business fields; update the array returned by
casts() (in the EventModel::casts method) to include 'gps_radius' => 'integer'
and 'xp_base' => 'integer' so those attributes are always cast to ints at
runtime.
tests/Feature/GpsCheckinTest.php (1)

189-234: ⚡ Quick win

Assert persistence side effects in the success scenario.

This test currently validates only response payload. Add assertions for persisted pivot values and character XP to protect the critical contract.

Proposed test hardening
 it('retorna 200 quando dentro do raio e janela válida', function (): void {
@@
     $response->assertJsonStructure([
         'state',
         'verification_method',
         'verified_at',
         'xp_awarded',
         'streak_multiplier',
         'streak_current',
     ]);
+
+    $pivot = $event->attendees()->where('user_id', $user->id)->first()->pivot;
+    expect($pivot->state)->toBe(CheckinStatusEnum::Verified);
+    expect($pivot->xp_awarded)->toBe(100);
+    expect($pivot->verification_method)->toBe('gps');
+    expect($pivot->verified_at)->not->toBeNull();
+
+    $user->character->refresh();
+    expect($user->character->experience)->toBe(100);
 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Feature/GpsCheckinTest.php` around lines 189 - 234, Add assertions that
verify persisted side effects after the successful checkin: assert the
event-user pivot (via the EventModel->attendees() pivot for $event and $user)
was updated to the verified state and contains verification_method 'gps' and
xp_awarded 100 (or the fields your pivot uses), and assert the Character for
$user had its XP increased by xp_base (e.g., 100) and any streak fields
(streak_current/streak_multiplier) were updated accordingly; use existing
symbols EventModel, attendees(), CheckinStatusEnum, Character and the
$event/$user fixtures to locate where to insert these DB/persisted assertions
after the $response assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@app-modules/events/database/migrations/2026_05_13_112452_add_checkin_fields_to_events_table.php`:
- Around line 11-21: The migration defines new columns in the up() method but
lacks a down() method to reverse those changes; add a down() implementation that
uses Schema::table('events', function (Blueprint $table) { ... }) to drop the
added columns (status, location_lat, location_lng, gps_radius, xp_base) so the
migration is reversible—implement the down() method in the same migration class
and call $table->dropColumn([...]) (or individual dropColumn calls) to remove
those fields.

In
`@app-modules/events/database/migrations/2026_05_13_112653_add_checkin_fields_to_events_attendees_table.php`:
- Around line 11-21: The migration adds five nullable columns in up() on the
events_attendees table but lacks a down() to reverse it; add a public function
down(): void that calls Schema::table('events_attendees', function (Blueprint
$table) {
$table->dropColumn(['state','verified_at','verification_method','xp_awarded','streak_multiplier']);
}) so the migration is reversible; reference the existing up() method and the
column names to ensure the down() drops exactly those columns.

In `@app-modules/events/src/Actions/GpsCheckinAction.php`:
- Around line 36-40: GpsCheckinAction assumes event schedule and GPS fields
exist (eventModel->start_at, end_at, radius, latitude, longitude) causing 500s
for nullable schema; before performing time and radius checks in
GpsCheckinAction, guard these nullable fields by validating/presence-checking
eventModel->start_at and eventModel->end_at and the GPS fields (radius,
latitude, longitude) and return a controlled domain error (e.g.,
response()->json(['error'=>'event_incomplete'], 400)) if any required field is
missing; update the conditional blocks that reference start_at/end_at and the
radius/distance calculation to first check for null and short-circuit with the
domain error so downstream ->copy(), ->subMinutes(), and geo math never run on
null.
- Around line 44-50: Wrap the verification/state-check and the subsequent writes
into a database transaction and lock the pivot row to prevent TOCTOU races: in
GpsCheckinAction (where $pivot is checked at the state check around the existing
pivot->state === CheckinStatusEnum::Verified and later updated at the write
block around lines 69-72), start a DB transaction, reload the pivot with a
"select ... for update" (or use the ORM's lockForUpdate on the pivot record),
re-check $pivot->state inside the transaction, and only then perform the state
change, set verified_at and xp_awarded and persist the changes (and commit);
ensure any XP-award side-effects (balance updates, notifications) occur inside
the same transaction or are idempotent/compensated to avoid double-awarding on
concurrent requests or failures.

In `@app-modules/events/src/Http/Requests/GpsCheckinRequest.php`:
- Around line 20-23: The validation currently returns only 'numeric' for lat/lng
in the rules() method of GpsCheckinRequest, which permits impossible
coordinates; update the rules to constrain latitude to -90..90 and longitude to
-180..180 (e.g., add 'between:-90,90' for 'lat' and 'between:-180,180' for
'lng', or use 'min'/'max' equivalents) so invalid GPS values are rejected.

---

Nitpick comments:
In `@app-modules/events/src/Models/EventModel.php`:
- Around line 266-277: The casts() method currently returns type casts for event
fields but misses explicit integer casts for the numeric business fields; update
the array returned by casts() (in the EventModel::casts method) to include
'gps_radius' => 'integer' and 'xp_base' => 'integer' so those attributes are
always cast to ints at runtime.

In `@tests/Feature/GpsCheckinTest.php`:
- Around line 189-234: Add assertions that verify persisted side effects after
the successful checkin: assert the event-user pivot (via the
EventModel->attendees() pivot for $event and $user) was updated to the verified
state and contains verification_method 'gps' and xp_awarded 100 (or the fields
your pivot uses), and assert the Character for $user had its XP increased by
xp_base (e.g., 100) and any streak fields (streak_current/streak_multiplier)
were updated accordingly; use existing symbols EventModel, attendees(),
CheckinStatusEnum, Character and the $event/$user fixtures to locate where to
insert these DB/persisted assertions after the $response assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 0a9d63fe-c9c0-4ee5-8ff9-dcb68de9acd4

📥 Commits

Reviewing files that changed from the base of the PR and between 0c624a7 and 21423dc.

📒 Files selected for processing (15)
  • app-modules/events/database/migrations/2026_05_13_112452_add_checkin_fields_to_events_table.php
  • app-modules/events/database/migrations/2026_05_13_112653_add_checkin_fields_to_events_attendees_table.php
  • app-modules/events/routes/checkin-routes.php
  • app-modules/events/src/Actions/GpsCheckinAction.php
  • app-modules/events/src/Actions/HaversineAction.php
  • app-modules/events/src/Actions/UpdateCheckinAction.php
  • app-modules/events/src/Actions/XpCalculatorAction.php
  • app-modules/events/src/Enums/CheckinStatusEnum.php
  • app-modules/events/src/Enums/EventStatusEnum.php
  • app-modules/events/src/EventsServiceProvider.php
  • app-modules/events/src/Http/Controllers/GpsCheckinController.php
  • app-modules/events/src/Http/Requests/GpsCheckinRequest.php
  • app-modules/events/src/Models/EventModel.php
  • app-modules/events/src/Models/Pivot/EventAttend.php
  • tests/Feature/GpsCheckinTest.php

Comment on lines +11 to +21
public function up(): void
{
Schema::table('events', function (Blueprint $table): void {
$table->string('status')->nullable();
$table->decimal('location_lat', 10, 7)->nullable();
$table->decimal('location_lng', 10, 7)->nullable();
$table->integer('gps_radius')->nullable();
$table->integer('xp_base')->nullable();
});
}
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add a down() rollback for the new columns.

This migration is not reversible right now, which is risky for rollback scenarios.

Suggested patch
 return new class extends Migration
 {
     public function up(): void
     {
         Schema::table('events', function (Blueprint $table): void {
             $table->string('status')->nullable();
             $table->decimal('location_lat', 10, 7)->nullable();
             $table->decimal('location_lng', 10, 7)->nullable();
             $table->integer('gps_radius')->nullable();
             $table->integer('xp_base')->nullable();
         });
     }
+
+    public function down(): void
+    {
+        Schema::table('events', function (Blueprint $table): void {
+            $table->dropColumn([
+                'status',
+                'location_lat',
+                'location_lng',
+                'gps_radius',
+                'xp_base',
+            ]);
+        });
+    }
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public function up(): void
{
Schema::table('events', function (Blueprint $table): void {
$table->string('status')->nullable();
$table->decimal('location_lat', 10, 7)->nullable();
$table->decimal('location_lng', 10, 7)->nullable();
$table->integer('gps_radius')->nullable();
$table->integer('xp_base')->nullable();
});
}
};
public function up(): void
{
Schema::table('events', function (Blueprint $table): void {
$table->string('status')->nullable();
$table->decimal('location_lat', 10, 7)->nullable();
$table->decimal('location_lng', 10, 7)->nullable();
$table->integer('gps_radius')->nullable();
$table->integer('xp_base')->nullable();
});
}
public function down(): void
{
Schema::table('events', function (Blueprint $table): void {
$table->dropColumn([
'status',
'location_lat',
'location_lng',
'gps_radius',
'xp_base',
]);
});
}
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app-modules/events/database/migrations/2026_05_13_112452_add_checkin_fields_to_events_table.php`
around lines 11 - 21, The migration defines new columns in the up() method but
lacks a down() method to reverse those changes; add a down() implementation that
uses Schema::table('events', function (Blueprint $table) { ... }) to drop the
added columns (status, location_lat, location_lng, gps_radius, xp_base) so the
migration is reversible—implement the down() method in the same migration class
and call $table->dropColumn([...]) (or individual dropColumn calls) to remove
those fields.

Comment on lines +11 to +21
public function up(): void
{
Schema::table('events_attendees', function (Blueprint $table): void {
$table->string('state')->nullable();
$table->timestamp('verified_at')->nullable();
$table->string('verification_method')->nullable();
$table->integer('xp_awarded')->nullable();
$table->float('streak_multiplier')->nullable();
});
}
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add a down() method to keep the migration reversible.

Without rollback logic, reverting a failed deploy is harder than necessary.

Suggested patch
 return new class extends Migration
 {
     public function up(): void
     {
         Schema::table('events_attendees', function (Blueprint $table): void {
             $table->string('state')->nullable();
             $table->timestamp('verified_at')->nullable();
             $table->string('verification_method')->nullable();
             $table->integer('xp_awarded')->nullable();
             $table->float('streak_multiplier')->nullable();
         });
     }
+
+    public function down(): void
+    {
+        Schema::table('events_attendees', function (Blueprint $table): void {
+            $table->dropColumn([
+                'state',
+                'verified_at',
+                'verification_method',
+                'xp_awarded',
+                'streak_multiplier',
+            ]);
+        });
+    }
 };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app-modules/events/database/migrations/2026_05_13_112653_add_checkin_fields_to_events_attendees_table.php`
around lines 11 - 21, The migration adds five nullable columns in up() on the
events_attendees table but lacks a down() to reverse it; add a public function
down(): void that calls Schema::table('events_attendees', function (Blueprint
$table) {
$table->dropColumn(['state','verified_at','verification_method','xp_awarded','streak_multiplier']);
}) so the migration is reversible; reference the existing up() method and the
column names to ensure the down() drops exactly those columns.

Comment on lines +36 to +40
if (now()->lt($eventModel->start_at->copy()->subMinutes(30))) {
return response()->json(['error' => 'event_not_started'], 400);
}

if (now()->gt($eventModel->end_at->copy()->addMinutes(30))) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard nullable event fields before time/radius checks.

Line 36, Line 40, and Lines 55-59 assume event schedule/GPS fields are always present. With nullable schema fields, incomplete event setup can cause 500s instead of a controlled domain error.

Proposed fix
     public function execute(EventModel $eventModel, float $latitude, float $longitude): JsonResponse
     {
+        if (
+            !$eventModel->start_at
+            || !$eventModel->end_at
+            || $eventModel->location_lat === null
+            || $eventModel->location_lng === null
+            || $eventModel->gps_radius === null
+        ) {
+            return response()->json(['error' => 'event_not_configured_for_checkin'], 400);
+        }
+
         $pivot = $eventModel->attendees()->where('user_id', auth()->user()->id)->first()?->pivot;

Also applies to: 52-59

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app-modules/events/src/Actions/GpsCheckinAction.php` around lines 36 - 40,
GpsCheckinAction assumes event schedule and GPS fields exist
(eventModel->start_at, end_at, radius, latitude, longitude) causing 500s for
nullable schema; before performing time and radius checks in GpsCheckinAction,
guard these nullable fields by validating/presence-checking eventModel->start_at
and eventModel->end_at and the GPS fields (radius, latitude, longitude) and
return a controlled domain error (e.g.,
response()->json(['error'=>'event_incomplete'], 400)) if any required field is
missing; update the conditional blocks that reference start_at/end_at and the
radius/distance calculation to first check for null and short-circuit with the
domain error so downstream ->copy(), ->subMinutes(), and geo math never run on
null.

Comment on lines +44 to +50
if ($pivot->state === CheckinStatusEnum::Verified) {
return response()->json([
'state' => $pivot->state,
'verified_at' => $pivot->verified_at,
'xp_awarded' => $pivot->xp_awarded,
], 409);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

Make verification and XP awarding atomic/race-safe.

There is a TOCTOU gap between Line 44 (state check) and Lines 69-72 (writes). Concurrent requests can both pass and award XP twice; failures after pivot update can leave inconsistent data.

Proposed fix
+use Illuminate\Support\Facades\DB;
+
     public function execute(EventModel $eventModel, float $latitude, float $longitude): JsonResponse
     {
-        $pivot = $eventModel->attendees()->where('user_id', auth()->user()->id)->first()?->pivot;
+        $user = auth()->user();
+
+        return DB::transaction(function () use ($eventModel, $latitude, $longitude, $user): JsonResponse {
+            $pivot = $eventModel->attendees()
+                ->where('user_id', $user->id)
+                ->lockForUpdate()
+                ->first()?->pivot;
 
-        if (!$pivot) {
-            return response()->json(['error' => 'not_registered'], 403);
-        }
+            if (!$pivot) {
+                return response()->json(['error' => 'not_registered'], 403);
+            }
 
-        if ($pivot->state === CheckinStatusEnum::Verified) {
-            return response()->json([
-                'state' => $pivot->state,
-                'verified_at' => $pivot->verified_at,
-                'xp_awarded' => $pivot->xp_awarded,
-            ], 409);
-        }
+            if ($pivot->state === CheckinStatusEnum::Verified) {
+                return response()->json([
+                    'state' => $pivot->state,
+                    'verified_at' => $pivot->verified_at,
+                    'xp_awarded' => $pivot->xp_awarded,
+                ], 409);
+            }
 
-        $xpAwarded = $this->xpCalculatorAction->execute($eventModel);
-        $this->updateCheckinAction->execute($pivot, $xpAwarded);
-        auth()->user()->character->increment('experience', $xpAwarded);
+            $xpAwarded = $this->xpCalculatorAction->execute($eventModel);
+            $this->updateCheckinAction->execute($pivot, $xpAwarded);
+
+            if (!$user->character) {
+                return response()->json(['error' => 'character_not_found'], 409);
+            }
+            $user->character->increment('experience', $xpAwarded);
 
-        return response()->json([
-            // ...
-        ]);
+            return response()->json([
+                // ...
+            ]);
+        });
     }

Also applies to: 67-72

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app-modules/events/src/Actions/GpsCheckinAction.php` around lines 44 - 50,
Wrap the verification/state-check and the subsequent writes into a database
transaction and lock the pivot row to prevent TOCTOU races: in GpsCheckinAction
(where $pivot is checked at the state check around the existing pivot->state ===
CheckinStatusEnum::Verified and later updated at the write block around lines
69-72), start a DB transaction, reload the pivot with a "select ... for update"
(or use the ORM's lockForUpdate on the pivot record), re-check $pivot->state
inside the transaction, and only then perform the state change, set verified_at
and xp_awarded and persist the changes (and commit); ensure any XP-award
side-effects (balance updates, notifications) occur inside the same transaction
or are idempotent/compensated to avoid double-awarding on concurrent requests or
failures.

Comment on lines +20 to +23
return [
'lat' => ['required', 'numeric'],
'lng' => ['required', 'numeric'],
];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Enforce geographic bounds for latitude/longitude.

numeric allows invalid coordinates; add range checks to reject impossible GPS values.

Suggested patch
     public function rules(): array
     {
         return [
-            'lat' => ['required', 'numeric'],
-            'lng' => ['required', 'numeric'],
+            'lat' => ['required', 'numeric', 'between:-90,90'],
+            'lng' => ['required', 'numeric', 'between:-180,180'],
         ];
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return [
'lat' => ['required', 'numeric'],
'lng' => ['required', 'numeric'],
];
return [
'lat' => ['required', 'numeric', 'between:-90,90'],
'lng' => ['required', 'numeric', 'between:-180,180'],
];
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app-modules/events/src/Http/Requests/GpsCheckinRequest.php` around lines 20 -
23, The validation currently returns only 'numeric' for lat/lng in the rules()
method of GpsCheckinRequest, which permits impossible coordinates; update the
rules to constrain latitude to -90..90 and longitude to -180..180 (e.g., add
'between:-90,90' for 'lat' and 'between:-180,180' for 'lng', or use 'min'/'max'
equivalents) so invalid GPS values are rejected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant