I am getting weird issue of sending push notification to Android using FCM.
Goal :- Having error while sending push notification
Below is the scenario I do have function for sending push notification to Android
public static function SendMultipleNotificationAndroid($groups)
{
//your api key SERVER API KEY
$apiKey = Yii::$app->params['android_api_key'];
$url = 'https://fcm.googleapis.com/fcm/send';
$headers = array(
'Authorization:key=' . $apiKey,
'Content-Type: application/json'
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
foreach($groups as $resG){
$users = $resG['users'];
$msg = $resG['message'];
$type = $resG['notification_type'];
$notification_data = $resG['notification_data'];
$deviceTokens = [];
foreach($users as $resUser){
$deviceTokens[] = $resUser['device_token'];
//Add Friend badge count +1
Common::AddRemoveBadgeCount($resUser['user_id']);
}
if(!empty($deviceTokens)){
$fields = array(
'registration_ids' => $deviceTokens,
'priority' => 'high',
'collapse_key' => $resG['notification_type'],
'time_to_live' => 2419200,
"click_action" =>"NotificationListingActivity",
'data' => [
"title" => "ProjectName",
"body" => $resG['message'],
"action_tag" => $resG['notification_type'],
"message" => $resG['message'],
'notification_type' => $type,
'notification_data' => $notification_data,
'sound' => 'default',
]
);
//Print result
p($ch,0);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fields));
curl_exec($ch);
}
}
curl_close($ch);
}
So the issue is when I send single notification it works fine but when I send multiple notification I got error every time
<pre>Resource id #5</pre>{"multicast_id":4818908994630396118,"success":1,"failure":1,"canonical_ids":0,"results":[{"error":"NotRegistered"},{"message_id":"0:1487136045570022%c3bae3c6002e9358"}]}
<pre>Resource id #5</pre>{"multicast_id":5218359780835228544,"success":1,"failure":1,"canonical_ids":0,"results":[{"error":"NotRegistered"},{"message_id":"0:1487136046618669%c3bae3c6002e9358"}]}
As we debug the code we do have device token in our database no firewall which stops sending push notifications.
Every time I call above function I get
"error":"NotRegistered"
According to the doc its because the mobile device testing does not have your app installed anymore
If it is NotRegistered, you should remove the registration ID from
your server database because the application was uninstalled from the
device, or the client app isn't configured to receive messages.
Don't know much about php, but recently I have faced the same issue in another project and I have resolved this way :
Refere this first :
Where can I find the API KEY for Firebase Cloud Messaging?
and get updated API key as shown in below snapshot
This is a client-side (device) issue, not service-side.
Multiple scenarios can cause this:
If the client app unregisters with GCM.
If the client app is automatically unregistered, which can happen if the user uninstalls the application. For example, on iOS, if the APNS Feedback Service
reported the APNS token as invalid.
If the registration token expires (for example, Google might decide to refresh registration tokens, or the APNS token has expired for iOS devices).
If the client app is updated but the new version is not configured to receive messages.
See https://developers.google.com/cloud-messaging/http-server-ref
On app startup I check to see if the token I have stored locally matches the new token. If not then I refresh the token on my servers. I also do this in FirebaseInstanceIDService::onTokenRefresh.
The thing is firebase generates a unique device-ID for your target device when the app is run for the first time, and it will be used as the identity of the device.
If the user uninstalls the app or clears the data of the app then in that case on reinstalling or reopening the app the device-ID will differ. This will result in the ID not be identified by firebase to send the notification. This will result in the error Not Registered
I got this error when i uninstalled and reinstalled my application.
What i think is, when we reinstall application, we cant get a new fcm token every time we install.
So, we must first delete the previous instance id and then create new fcm token. Please see the code below..
Just adding the uncommented line resolved my issue..
See first comment for this solution for code :)
_firebaseRegister() {
// _firebaseMessaging.deleteInstanceID();
_firebaseMessaging.getToken().then((token) => fcmtoken = token);
}
Hope this works for you! :)
In my case, the problem was on the recipient side, not the sender. If you're sending a message to someone who has not run the app for awhile, then their device token is stale. All they need to do is restart the app.
In your case, did you ensure that when you sent multiple notifications all the devices you messaged were up and running and that the app refreshes the token on startup by calling FirebaseMessaging.getInstance().getToken()?
While searching for the "NotRegistered" issue, we found the following ...
At device end, the generation of device notification token was done by following code once, when user first time starts the app after installation.
RegToken = FirebaseInstanceId.getInstance().getToken(senderId, "FCM"); // Old code
We were using other class derived from "FirebaseMessagingService" to create / receive notifications. But the following method was missing in that class.
// New code
#Override
public void onNewToken(String token) {
Log.d(TAG, "Refreshed token: " + token);
// If you want to send messages to this application instance or
// manage this apps subscriptions on the server side, send the
// Instance ID token to your app server.
sendRegistrationToServer(token);
}
We found that, the above method was called by FCM system in device on every start of App. ( The App was not Uninstalled, still the method was giving different token every time. ) So we called our method 'sendRegistrationToServer(token);' to submit the token to our server along with the 'DeviceId' and other identification data. When we sent notification from php server on this token, it returned 'sent' instead of "NotRegistered".
This error appeared in an Android application when sending request to https://fcm.googleapis.com/fcm/send:
...
"results": [
{
"error": "NotRegistered"
}
]
While the application was working well for some time, and push notifications were delivered, then (probably after reauthorization in the app or reauthorization in Play Market) that error started to show.
If I changed push token (added "1", for instance), then I got another error:
"results": [
{
"error": "InvalidRegistration"
}
]
So, Firebase knew about the push token, but didn't deliver a notification.
I uninstalled the Android application and installed again.
My problem was different then all of the above. The Firebase message that we we're trying exceeded the maximum size of 4Kb. Very small chance this triggers an "NotRegistered". But the problem was that I took this log of a moment the app was probably not installed. So also check the size of the Firebase message.
In our case, this issue appears on iOS when we override a production installation of the app using Xcode "Run" (replaces production app with the debug version in-place).
The app still receives the same registration token, but doesn't accept messages (NotRegistered is returned from the FCM).
We've found out that uninstalling the app from the device, and performing a clean install fixes this issue.
In my case, updating the FCM token for the device worked!
I have a 100 percent solution i had fix recently this issue this
error, occurring because you are sending this notification on a device
which does does not contain your firebase setup api key For example
when you registered user that time user registered from different
firebase setup so your android token was different and your sending
request to other firebase setup for which didn't create android token
whom you trying to send notification(message) so you would have to
make sure your user android token generating from same firebase
project of which project you are using firebase api key
In my case, two cloned emulators are the reason.
I think cloned emulators have same device ID, so firebase think they are same device.
So when one device get a push message, the push token may be removed and return not NotRegistered.
If you are in the similar situation, try to remove the emulator and create new one.
A possible solution can be update user push token.
When the client app is updated (new version), old push token is changed.
I got this error after setting firebase and using app for some time.
I updated google-service.json file (look for new file in the user's personal account firebase) and reinstall app. It helps me.
For iOS, I ran into the same issue. We were only saving the token on didRegisterForRemoteNotificationsWithDeviceToken. As mentioned above, you can lose reference to the token for a variety of reasons.
To fix the "NotRegistered" error, I added the following to App Delegate:
extension AppDelegate: MessagingDelegate {
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
print("Firebase registration token: \(String(describing: fcmToken))")
//Notification setup. Token will be available anywhere
let dataDict: [String: String] = ["token": fcmToken ]
NotificationCenter.default.post(
name: Notification.Name("FCMToken"),
object: nil,
userInfo: dataDict
)
// Send token to application server.
if Auth.auth().currentUser?.uid != nil {
guard let userUid = Auth.auth().currentUser?.uid else { return }
let dataBaseRef = Database.database().reference()
let value: [String : Any] = ["pushToken" : fcmToken]
dataBaseRef.child("Your database path here").updateChildValues(value, withCompletionBlock: { (error, ref) in
if error != nil {
print("Error with storing/updating user token", error?.localizedDescription as Any)
}
})
}
}
}
didReceiveRegistrationToken is called anytime the token is updated.
After uninstalling the app in my device, the error occurred. That was because my code wasn't set up to regenerate a new fcm device token. The firebase documentation for this API recommends to call the method at app start and update your backend (or wherever you're using the tokens). I fixed it with the snippet below:
import {Platform} from 'react-native';
import messaging from '#react-native-firebase/messaging';
useEffect(() => {
requestPermission();
messaging()
.getToken()
.then(async token => {
console.log('_token=>>>', token);
if (token) {
setDeviceToken(token);
// update user realtime fdb with device token and userIdToken
const _authDriverIdRef = firebase
.database()
.ref(`users/${user.uid}`);
_authDriverIdRef.update({
fcmDeviceToken: deviceToken,
userIdToken: await user?.getIdToken(true),
});
}
});
});
const requestPermission = async () => {
return Platform.OS === 'ios' && (await messaging().requestPermission());
};
See if you have uninstalled the app and your device token is modified. Update the device token, and your error will be gone
I'm using Excel VBA to get a user's oAuth2 token for the mail.google.com scope for a native application. It works fine so I have the the user's Access Token (it refreshes if it expires) and I have the user's email address. I was using Zend SMTP to send the emails but found out there are limits to bulk sending emails this way (currently my account is locked out due to going over quota while trying to figure this out over the weekend). My client may send 2,000 emails at once and locking themselves out of their Gmail account for 24 hours is unacceptable. I think using the Gmail REST API will work, as it has higher quotas, but not finding any detailed PHP examples of how to create and send an HTML email via an HTTP request.
I know I have to create the MIME email and think I can figure that out (using this thread stackoverflow.com/questions/24940984/send-email-using-gmail-api-and-google-api-php-client) or from the Zend emails I was successfully creating. My question is what PHP code do I use to create and send the email?
The Google Dev examples all reference /userId/ (from developers.google.com/gmail/api/v1/reference/) but not sure where to get that if I just have the users token and their email address.
I assume it's something like this:
<?php
require 'GoogleAPI/src/Google/autoload.php';
extract($_POST); // use to get my client token, client's email address, the email to, cc, bcc, subject, body, etc.
//<Build the email $mime message here>
$m = new Google_Service_Gmail_Message();
$data = base64_encode($mime);
$data = str_replace(array('+','/','='),array('-','_',''),$data); // url safe
$m->setRaw($data);
$service->users_messages->send('me', $m);
?>
I don't know where to put the user's token. I know 'me' is the person that is authenticated.
I'm open to using an HTTP request as well, something like https://mywebsite.com/sendgmail.php?token=[UsersToken]&UserEmail=joe#test.com&ToEmail=toperson#xyz.com&Subject=HI&Body=Thanks for your help instead of the post method.
Just not sure how to implement that either.
I'm self taught PHP and VBA and new to the google api world so please forgive me if this is easy. I'm also looking into boxspring but trying to use the native APIs and PHP first.
EDIT:
So I've tried this using the examples but getting an error that my token is not in proper JSON format. I just want to send the token as a string. If I can't is there a way to make the string into JSON so oauth2 will accept it?:
<?php
require 'GoogleAPI/src/Google/autoload.php';
/**
* Returns an authorized API client.
* #return Google_Client the authorized client object
*/
$client = new Google_Client();
$client->setAccessToken('ya29.hwFcwk2M73vaPwNObeuwizHGjXT2y6UsAFEcDIvRAoWTM28gu2pJeK4GiMySkfAllTOQvXVMYfffff');
// Get the API client and construct the service object.
//$client = getClient();
$service = new Google_Service_Gmail($client);
// Print the labels in the user's account.
$user = 'me';
$results = $service->users_labels->listUsersLabels($user);
if (count($results->getLabels()) == 0) {
print "No labels found.\n";
} else {
print "Labels:\n";
foreach ($results->getLabels() as $label) {
printf("- %s\n", $label->getName());
}
}
?>
Error:
Fatal error: Uncaught exception 'Google_Auth_Exception' with message
'Could not json decode the token' in
/home/[my domain]/public/GoogleAPI/src/Google/Auth/OAuth2.php:179
Stack trace: #0
/home/[my domain]/public/GoogleAPI/src/Google/Client.php(215):
Google_Auth_OAuth2->setAccessToken('ya29.hwFcwk2M73...') #1
/home/[my domain]/public/labels.php(17):
Google_Client->setAccessToken('ya29.hwFcwk2M73...') #2 {main} thrown
in
/home/[my domain]/public/GoogleAPI/src/Google/Auth/OAuth2.php
on line 179
I'm sure I'm making this more difficult than it should be. I'm using the PHP example of showing a users labels, even though my final goal is to send emails.
We have an app on appstore, and with registered push notifications. They have successfully worked all the time, but we now tried to send a 'global' push, and something weird happened. This is what we have in our server-side .php file:
//Loop through tokens in tokenArray
$i = 0;
$t = 0;
foreach($tokenArray as $token)
{
$t++;
// Make notification
$msg = chr(0) . pack('n', 32) . pack('H*', $token) . pack('n', strlen($payload)) . $payload;
// Send
$result;
if($message != null)
{
$result = fwrite($fp, $msg, strlen($msg));
}
if ($result)
$i++;
}
// Close the connection to the server
fclose($fp);
if($i == 0)
{
echo 'The message was not delivered to anyone out of '.$t.'.';
}
else
{
echo 'The message was delivered to '.$i.' out of '.$t.'.';
}
The code before this has always worked, and it kind of still does. The tokenArray contains the table with tokens, as in SELECT Token FROM Tokens; from our SQL. This works.
During development, when only our own tokens were registered, it always said "The message was delivered to 4 out of 4", even though we had deleted our apps from our phones. Now we tried to send to all ≈1100 registered tokens with this code.
The message was sent, and the output was "The message was delivered to 588 out of 1194."
And we did not receive the notification ourselves!
What does that mean?
After about 5 minutes, I switched out the tokenArray with an array only containing my own tokens and sent a new push, and I received that one on my phone. I also know for a fact that the 'working' token exist in the previous 'tokenArray' which failed(I checked).
Is push notification a game of chance!? What does it mean when if($result) fails? And why did it fail over 500 times?
The certificates and .pem and .p12 etc are all working, the only thing I did different from push1 to push2 was to use another table which is a clone from the original table in my SQL-server. Table2 only have my tokens, and it worked. No other changes was made. Only SELECT Token FROM Tokens2, and later I proved that all the tokens in Tokens2 exist in Tokens
I have no idea if anyone got the push at all, or if the 'lucky' 588 of the 1200 that still has the app installed received it.
What causes this? We don't dare send another one in case half of them already received it.. Is there some limit to how fast I can send pushes at once? Or what are we doing wrong?!
Please help, thanks.
Well, I don't know php, so your code doesn't help me. However, based on your description it's likely some of the device tokens in your DB are invalid. When Apple's server gets a notification with an invalid device token it closes the socket. If you already wrote more messages after the one with the bad token, they won't reach Apple. Only after you detect that the socket was closed and open a new one your messages will reach Apple. If you don't use the enhanced notification format, it would be a good idea to start using it - this way you can get from Apple the id of the invalid message and clean your DB from invalid tokens. However, even using the enhanced format doesn't guarantee that you'll detect all the errors (unless you are willing to send the messages really slowly, and check for error responses from Apple after each message you send).
Your main loop does not take into account cases in which Apple will close socket connections. As mentioned by Eran, if you send an invalid token, Apple closes the connection at which point, any further writes using fwrite will fail. So if your 589th token is invalid, no other push will be sent to Apple.
Here's a simple fix for that that fits into your logic; this part replaces the if statement in the main loop:
if ($result) {
$i++;
} else {
fclose($fp);
// Add code here to re-open socket-connection with Apple.
}
Besides the enhanced notification format mentioned by Eran, you can also use the APNS Feedback API to query Apple for invalid tokens and purge them from your database. You can find more infomation on that here: http://bit.ly/14RPux4
There is no limit to how many push notifications you can send at once. I've sent thousands in a few seconds. The only real limitation is the connection between you and the APNS servers.