FCM Push Notifications
This guide explains the Firebase Cloud Messaging (FCM) push notification system in the Unified Order Service (UOS) API. This system sends real-time notifications to mobile devices when order events occur.
What are FCM Notifications?
FCM notifications in UOS are data-only push notifications sent to mobile devices when specific order events occur. The system uses Firebase Cloud Messaging to deliver:
- Order status change notifications when orders progress through their lifecycle
- Timeslot monitoring notifications for delivery window management
- Reminder notifications for unpicked orders
- Order creation notifications when new orders are received
How It Works
1. Device Registration
Devices register their FCM tokens using the subscriptions endpoints to receive notifications for specific places.
2. Event Triggers
The system automatically sends notifications when:
- Orders are created
- Order status changes (pending → processing → picking → picked, etc.)
- Timeslots begin (2:00 PM for delivery windows)
- Reminders are needed (2:15, 2:30, 2:45 PM if orders remain unpicked)
3. Notification Delivery
All notifications use data-only payloads (no title/body) - your mobile app handles the presentation.
Event Types
Order Created (event_order_created)
Triggered when: A new order is created in the system
Payload Example:
{
"default": {
"data": {
"message_id": "msg-1725707721000-abc123def",
"order_id": "742ba33c-0c02-43e7-80b0-eb795c14dc6f",
"parent_order_id": "742ba33c-0c02-43e7-80b0-eb795c14dc6f",
"visible_id": "ORDER-20250707-1021",
"event_type": "event_order_created",
"delivery_date": "2025-07-07",
"start_time": "14:00",
"end_time": "16:00",
"place_id": "1ee68c4e-7008-4227-a92d-e1b13ac64f30"
}
}
}
Order Status Changed (event_order_status_changed)
Triggered when: Order status changes (pending → processing → picking → picked → retrieving → shipped → completed)
Payload Example:
{
"default": {
"data": {
"message_id": "msg-1725707721000-xyz789ghi",
"order_id": "742ba33c-0c02-43e7-80b0-eb795c14dc6f",
"parent_order_id": "742ba33c-0c02-43e7-80b0-eb795c14dc6f",
"visible_id": "ORDER-20250707-1021",
"event_type": "event_order_status_changed",
"delivery_date": "2025-07-07",
"start_time": "14:00",
"end_time": "16:00",
"place_id": "1ee68c4e-7008-4227-a92d-e1b13ac64f30",
"previous_status": "pending",
"new_status": "processing",
"order_number": "ORDER-20250707-1021"
}
}
}
Timeslot Started (event_timeslot_started)
Triggered when: Delivery timeslots begin (typically 2:00 PM)
Payload Example:
{
"default": {
"data": {
"message_id": "msg-1725714000000-def456ghi",
"order_id": "742ba33c-0c02-43e7-80b0-eb795c14dc6f",
"parent_order_id": "742ba33c-0c02-43e7-80b0-eb795c14dc6f",
"visible_id": "ORDER-20250707-1021",
"event_type": "event_timeslot_started",
"delivery_date": "2025-07-07",
"start_time": "14:00",
"end_time": "16:00",
"place_id": "1ee68c4e-7008-4227-a92d-e1b13ac64f30"
}
}
}
Unpicked Orders Reminder (event_unpicked_orders_reminder)
Triggered when: Orders remain unpicked after timeslot reminders (2:15, 2:30, 2:45 PM)
Payload Example:
{
"default": {
"data": {
"message_id": "msg-1725714900000-ghi789jkl",
"order_id": "742ba33c-0c02-43e7-80b0-eb795c14dc6f",
"parent_order_id": "742ba33c-0c02-43e7-80b0-eb795c14dc6f",
"visible_id": "ORDER-20250707-1021",
"event_type": "event_unpicked_orders_reminder",
"delivery_date": "2025-07-07",
"start_time": "14:00",
"end_time": "16:00",
"place_id": "1ee68c4e-7008-4227-a92d-e1b13ac64f30"
}
}
}
Notification Payload Structure
All FCM notifications use the same standardized payload structure:
Core Fields (Always Present)
| Field | Type | Description |
|---|---|---|
message_id | string | Unique message identifier |
order_id | string | Order ID (primary identifier) |
parent_order_id | string | Parent order ID (usually same as order_id) |
visible_id | string | Visible order identifier (order number, reference ID, or ID) |
event_type | string | Event type (see Event Types above) |
delivery_date | string | Delivery date in YYYY-MM-DD format |
start_time | string | Timeslot start time in HH:MM format |
end_time | string | Timeslot end time in HH:MM format |
place_id | string | Place identifier |
Event-Specific Fields
| Field | Type | Present When | Description |
|---|---|---|---|
previous_status | string | Status change events | Previous order status |
new_status | string | Status change events | New order status |
order_number | string | Status change events | Order number |
Timeslot Monitoring System
The system includes automated timeslot monitoring that runs every 15 minutes:
Monitoring Schedule
- 2:00 PM: Timeslot started notification (
event_timeslot_started) - 2:15 PM: First reminder if orders still unpicked (
event_unpicked_orders_reminder) - 2:30 PM: Second reminder if orders still unpicked (
event_unpicked_orders_reminder) - 2:45 PM: Final reminder if orders still unpicked (
event_unpicked_orders_reminder)
Monitored Statuses
Orders are considered "unpicked" when in these statuses:
pendingprocessingpicking
Once orders reach picked, retrieving, shipped, or completed, they no longer trigger reminder notifications.
Fallback Handling
The system includes robust fallback mechanisms for missing data:
Timeslot Extraction
The system attempts to extract timeslot information from multiple sources:
- Primary:
order.shipping_info.slot.start_time/end_time - Salesforce:
order.metadata.delivery_slot_start/delivery_slot_end - Drone API:
order.shipping_info.pickup_details.time_slot - Fallback:
00:00-23:59if no timeslot data available
Date Extraction
The system attempts to extract delivery dates from:
- Primary:
order.shipping_info.slot.date - Salesforce:
order.metadata.delivery_slot_start - Drone API:
order.shipping_info.slot.delivery_date - Fallback:
order.order_dateor current date
Mobile App Integration
Receiving Notifications
// React Native example
import messaging from '@react-native-firebase/messaging';
// Handle background messages
messaging().setBackgroundMessageHandler(async remoteMessage => {
const { data } = remoteMessage;
switch (data.event_type) {
case 'event_order_created':
handleOrderCreated(data);
break;
case 'event_order_status_changed':
handleStatusChange(data);
break;
case 'event_timeslot_started':
handleTimeslotStarted(data);
break;
case 'event_unpicked_orders_reminder':
handleUnpickedReminder(data);
break;
}
});
// Handle foreground messages
messaging().onMessage(async remoteMessage => {
const { data } = remoteMessage;
showInAppNotification(data);
});
Processing Notification Data
function handleOrderCreated(data) {
const {
message_id,
order_id,
delivery_date,
start_time,
end_time,
place_id
} = data;
// Show notification
showNotification(
'New Order Received',
`Order for ${delivery_date} ${start_time}-${end_time}`,
{ orderId: order_id, placeId: place_id }
);
// Update app state
refreshOrders();
}
function handleStatusChange(data) {
const {
order_id,
order_number,
previous_status,
new_status
} = data;
// Show status change notification
showNotification(
'Order Status Updated',
`Order #${order_number} is now ${new_status}`,
{ orderId: order_id }
);
// Update specific order in app
updateOrderStatus(order_id, new_status);
}
function handleTimeslotStarted(data) {
const {
delivery_date,
start_time,
end_time,
place_id
} = data;
// Show timeslot notification
showNotification(
'Delivery Window Started',
`${delivery_date} ${start_time}-${end_time}`,
{ placeId: place_id }
);
// Navigate to order management
navigateToOrders();
}
Testing Notifications
Test Order Creation
# Create order to trigger notifications
curl -X POST "https://api.uos.example.com/v1/orders" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channel_id": "7eb292fd-aa7c-4357-ba05-debb11998192",
"place_id": "1ee68c4e-7008-4227-a92d-e1b13ac64f30",
"order_number": "TEST-ORDER-001",
"shipping_info": {
"slot": {
"start_time": "14:00",
"end_time": "16:00",
"date": "2025-07-07"
}
}
}'
Test Status Changes
# Update order status to trigger status change notifications
curl -X PATCH "https://api.uos.example.com/v1/orders/ORDER_ID/status" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "processing"
}'
Monitoring and Debugging
Checking Notification Logs
The system logs all notification activities:
# Check notification logs
grep "Event notification sent" /var/log/uos/app.log
grep "FCM notification" /var/log/uos/app.log
Verifying Device Registration
# Check if devices are registered for a place
curl -X GET "https://api.uos.example.com/debug/places/PLACE_ID/subscriptions" \
-H "Authorization: Bearer YOUR_API_KEY"
Best Practices
App Development
- Handle all event types: Implement handlers for all notification types
- Validate data: Always validate notification payloads before processing
- Provide feedback: Show users what actions triggered notifications
- Cache intelligently: Cache order data locally to complement notifications
Notification Management
- Register early: Register FCM tokens as soon as permissions are granted
- Update regularly: Refresh tokens when they change
- Clean up: Remove tokens when users log out or uninstall
Error Handling
- Graceful degradation: App should work even if notifications fail
- Retry logic: Implement retry mechanisms for failed registrations
- Logging: Log notification events for debugging
Troubleshooting
Common Issues
No notifications received:
- Check device token registration
- Verify place ID is correct
- Confirm Firebase configuration
- Check notification permissions
Duplicate notifications:
- Verify single device registration per place
- Check for multiple app instances
- Confirm proper token cleanup
Missing timeslot data:
- System uses fallback values (00:00-23:59)
- Check order creation payload includes shipping_info
- Verify timeslot format matches expected structure
Delayed notifications:
- FCM delivery can be delayed by device/network conditions
- Check device battery optimization settings
- Verify app is not being killed by OS
The FCM notification system provides reliable, real-time updates to mobile applications with comprehensive fallback handling and robust error management.