Repository navigation
feat: customer notifications inbox API - #103
Merged
Merged
Conversation
Android pushes never worked with store-level FCM credentials and some iOS devices were rejected, because of several stacked defects: - configureFcm put the service account JSON into credentials.private_key, so no valid Firebase client was ever built from a store channel - FcmChannel required the platform-wide Firebase project to be configured - credentials were always resolved from the store, never the network app - one APNs environment per channel rejected sandbox/dev build tokens - registerDevice never reassigned a token to the latest customer and did not normalize platform casing - mail/database errors ran before push and stopped it; failures and dead tokens were never handled or logged Introduce Push\StorefrontPushChannel with isolated Firebase/APNs clients, network-first credential resolution with wrong-app and wrong-environment retries, dead token pruning, and high priority payloads. Order notifications share a StorefrontOrderNotification base class. Add a customers/unregister-device endpoint and an admin test push action.
Codecov requires full patch coverage. Cover the FCM and APNs transport send paths, PushMessage setters, APNs environment short-circuit, explicit push routes, and pruning/logging failure handling. Read device attributes with data_get so devices returned by a custom push route do not need to be Eloquent models, and drop two unreachable branches.
Storefront order notifications were already stored in the core
notifications table, but customers had no way to read them. Add
storefront/v1/notifications endpoints for the authenticated customer to
list (filter by unread/type, limit/offset), count unread, read, mark as
read, mark all as read and delete their notifications, scoped to the
storefront app (store, or network and its member stores).
Add customer notification preferences (order update pushes, promotions)
stored on the contact meta and honored by the notifications, and
broadcast new notifications in realtime on contact.{uuid} through a
broadcast channel that never fails the send.
roncodes
force-pushed
the
feat/customer-notifications-inbox
branch
from
September 26, 2026 08:14
5dfcf46 to
467a420
Compare
…asts Also drop null values from inbox item data, and read notification data through the model's array cast.
This was referenced Sep 26, 2026
Merged
roncodes
changed the base branch from
fix/push-notification-layer
to
release/v0.4.22
September 28, 2026 03:21
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a notifications inbox that storefront customers can read from the web and mobile app, built on core-api's
notificationstable. Storefront order notifications were already written there (the notifiable is the customerContact), but no public endpoint exposed them.Endpoints (
storefront/v1, authenticated with theCustomer-Tokenheader)notificationsunread(bool),type(e.g.order_completed,promotional),limit(default 25, max 100),offsetnotifications/unread-count{ "count": n }for badgesnotifications/{id}notifications/{id}/readnotifications/read-all{ "status": "OK", "updated": n }notifications/{id}notifications/preferences{ "order_updates": bool, "promotions": bool }Item shape:
{ "id": "…", "type": "order_completed", "title": "…", "body": "…", "image": null, "data": { "order_id": "order_…", "store_id": "store_…", "network_id": "network_…" }, "is_read": false, "read_at": null, "created_at": "…" }Behavior
messageand the title insubject.CustomerNotificationPresentermaps them totype/title/body, so existing history displays correctly.data.meta.notification_preferences:order_updates: falseturns off order status pushes only; the inbox copy is still written.promotions: falsesuppresses promotional notifications entirely.contact.{uuid}/contact.{public_id}(through core'sBroadcastNotificationCreated), withtypeequal to the inbox type.SafeBroadcastChannelcatches socket failures, so a broadcast error can't fail the send and cause a retried listener to push twice.Related Issue
Part of the notifications / promotions work. No tracking issue.
Type of Change
Implementation Notes
v1/NotificationController,Http/Resources/v1/CustomerNotification,Support/CustomerNotificationPresenter,Support/NotificationPreferences,Notifications/Channels/SafeBroadcastChannel.notifiable_typein (FleetOps\Models\Contact,Storefront\Models\Customer), since either can be the notifiable. Storefront filtering uses JSON paths ondata(store_id,network_id,storefront_id); a customer's rows are already narrowed by the existingnotifiablemorph index.Validation
New:
server/tests/Unit/Http/Controllers/CustomerNotificationInboxTest.php. It covers auth, store vs network scoping, filters and pagination, cross-customer isolation, read/read-all/delete, legacy rows, preferences and their effect on delivery, the realtime payload, and broadcast failure isolation.Documentation Impact
fleetbase/fleetbase.ioAPI Reference Impact
fleetbase/postmanAPI reference notes: all endpoints in the table above are new (Storefront → Notifications).
Documentation Notes
fleetbase.io, Storefront API → Notifications: the endpoints, item shape, preferences, and the realtime channel (
contact.{uuid}) for app developers.Risk
broadcastdelivery after push, database and mail. Broadcast failures are logged, not thrown.sent_count; that endpoint is replaced by campaigns in the promotions work.fleetbase/storefront-app.