Google pub/sub subscription data doesn't match with the app - php

I'm trying to listen for subscription changes (new and existing) of my Google Play app on the server. Here's the code I'm using. This uses the google/cloud-pubsub composer package:
$projectId = 'app-name';
$keyFile = file_get_contents(storage_path('app/app-name.json'));
$pubsub = new PubSubClient([
'projectId' => $projectId,
'keyFile' => json_decode($keyFile, true)
]);
$httpPostRequestBody = file_get_contents('php://input');
$requestData = json_decode($httpPostRequestBody, true);
info(json_encode($requestData));
$message = $pubsub->consume($requestData);
info(json_encode($message));
The code above works but the problem is that the data I get doesn't match the one I'm getting in the app side. This is a sample data:
{
"message":{
"data":"eyJ2ZXJ...",
"messageId":"16797998xxxxxxxxx",
"message_id":"1679799xxxxxxxxx",
"publishTime":"2020-12-15T02:09:23.27Z",
"publish_time":"2020-12-15T02:09:23.27Z"
},
"subscription":"projects\/app-name\/subscriptions\/test-subs"
}
If you base64_decode() the data, you'll get something like this:
{
version: "1.0",
packageName: "com.dev.app",
eventTimeMillis: "1607997631636",
subscriptionNotification: {
version: "1.0",
notificationType: 4,
purchaseToken: "kmloa....",
subscriptionId: "app_subs1"
}
}
This is where I'm expecting the purchaseToken to be the same as the one I'm getting from the client side.
Here's the code in the client-side. I'm using Expo in-app purchases to implement subscriptions:
setPurchaseListener(async ({ responseCode, results, errorCode }) => {
if (responseCode === IAPResponseCode.OK) {
const { orderId, purchaseToken, acknowledged } = results[0];
if (!acknowledged) {
await instance.post("/subscribe", {
order_id: orderId,
order_token: purchaseToken,
data: JSON.stringify(results[0]),
});
finishTransactionAsync(results[0], true);
alert(
"You're now subscribed! You can now use the full functionality of the app."
);
}
}
});
I'm expecting the purchaseToken I'm extracting from results[0] to be the same as the one the Google server is returning when it pushes the notification to the endpoint. But it doesn't.
Update
I think my main problem is that I'm assumming all the data I need will be coming from Google Pay, so I'm just relying on the data published by Google when a user subscribes in the app.
This isn't actually the one that publishes the message:
await instance.post("/subscribe")
It just updates the database with the purchase token. I can just use this to subscribe the user but there's no guarantee that the request is legitimate. Someone can just construct the necessary credentials based on an existing user and they can pretty much subscribe without paying anything. Plus this method can't be used to keep the user subscribed. So the data really has to come from Google.
Based on the answer below, I now realized that you're supposed to trigger the publish from your own server? and then you listen for that? So when I call this from the client:
await instance.post("/subscribe", {
purchaseToken
});
I actually need to publish the message containing the purchase token like so:
$pubsub = new PubSubClient([
'projectId' => $projectId,
]);
$topic = $pubsub->topic($topicName);
$message = [
'purchaseToken' => request('purchaseToken')
];
$topic->publish(['data' => $message]);
Is that what you're saying? But the only problem with this approach is how to validate if the purchase token is legitimate, and how to renew the subscription in the server? I have a field that needs to be updated each month so the user stays "subscribed" in the eyes of the server.
Maybe, I'm just overcomplicating things by using pub/sub. If there's actually an API which I could pull out data from regularly (using cron) which allows me to keep the user subscription data updated then that will also be acceptable as an answer.

First of all - I have a really bad experience with php and pubsub because of the php PubSubClient. If your script is only waiting for push and checking the messages then remove the pubsub package and handle it with few lines of code.
Example:
$message = file_get_contents('php://input');
$message = json_decode($message, true);
if (is_array($message)) {
$message = (isset($message['message']) && isset($message['message']['data'])) ? base64_decode($message['message']['data']) : false;
if (is_string($message)) {
$message = json_decode($message, true);
if (is_array($message)) {
$type = (isset($message['type'])) ? $message['type'] : null;
$data = (isset($message['data'])) ? $message['data'] : [];
}
}
}
I'm not sure how everything works on your side but if this part publishes the message:
await instance.post("/subscribe", {
order_id: orderId,
order_token: purchaseToken,
data: JSON.stringify(results[0]),
});
It looks like it's a proxy method to publish your messages. Because payload sent with it is not like a PubSub described schema and in the final message it doesn't look like IAPQueryResponse
If I was in your situation I will check few things to debug the problem:
How I publish/read a message to/from PubSub (topic, subscription and message payload)
I will write the publish mechanism as it is described in Google PubSub publish documentation
I will check my project, topic and subscription
If everything is set-up correctly then I will compare all other message data
If the problem persist then I will try to publish to PubSub minimal amount of data - just purchaseToken at the start to check what breaks the messages
For easier debug:
Create pull subscription
When you publish a message check pull subscription messages with "View messages"
For me the problem is not directly in PubSub but in your implementation of publish/receiving of messages.
UPDATE 21-12-2020:
Flow:
Customer create/renew subscription
Publish to pubsub with authentication
PubSub transfers the message to analysis application via "push" to make your analysis.
If you need information like:
New subscribers count
Renews count
Active subscriptions count
You can create your own analysis application but if you need something more complicated then you have to pick a tool to met your needs.
You can get the messages from pubsub also with "pull" but there are few cases I've met:
Last time I've used pull pubsub returns random amount of messages - if my limit is 50 and I have more than 50 messages in the queue I'm expecting to get 50 messages but sometimes pubsub gives me less messages.
PubSub returned messages in random order - now there is an option to use ordering key but it's something new.
To implement "pull" you have to run crons or something with "push" you receive the message as soon as possible.
With "pull" you have to depend on library/package (or whatever in any language it's called) but on "push" you can handle the message with just few lines of code as my php exapmle.

Related

Is it possible to pass custom data to stripe checkout?

I'm following the documentation for Stripe Checkout with server integration: https://stripe.com/docs/payments/checkout/server
The code in the examples works fine, but the problem I'm having is being unable to track the user, or their order, when a purchase is complete.
I have a webhook setup that Stripe pings when a payment is complete. But the response session from Stripe contains no information about the product ordered except for it's name, description, and image. I could use the product name to query the database but I'd much rather an ID, or a slug, of sorts.
$app->post("/stripe-pingback", function(Request $request, Response $response, array $args) {
\Stripe\Stripe::setApiKey("xxxxx");
// You can find your endpoint's secret in your webhook settings
$endpoint_secret = 'xxxxx';
$payload = $request->getBody();
$sig_header = isset($_SERVER['HTTP_STRIPE_SIGNATURE']) ? $_SERVER['HTTP_STRIPE_SIGNATURE'] : null;
$event = null;
try {
$event = \Stripe\Webhook::constructEvent($payload, $sig_header, $endpoint_secret);
} catch(\UnexpectedValueException $e) {
// Invalid payload
http_response_code(400); // PHP 5.4 or greater
exit();
} catch(\Stripe\Error\SignatureVerification $e) {
// Invalid signature
http_response_code(400); // PHP 5.4 or greater
exit();
}
// Handle the checkout.session.completed event
if ($event->type == 'checkout.session.completed') {
$session = $event->data->object;
var_dump($session);
// Possible to get custom data from session?
$customer = $session->customer;
$customerEmail = $session->customer_email;
// Fulfill the purchase...
$this->db->insertAudioPurchase();
}
http_response_code(200); // PHP 5.4 or greater
});
Is it possible to pass an ID along with the checkout request that Stripe can ping back to allow me to lookup the order and generate a download link?
Edit: metadata now does exist on the Session object. Although you will probably need to be on a more recent API version to be able to set it.
metadata doesn't exist on the Session object yet, so you can't use that.
You can use the client_reference_id field to store some custom data in your Session. Just be aware that the contents has to be unique. Have a look here.
You can pass an array with key metadata. Stripe will return this field as you've sent when payment is complete.
You just have to pass as this format.
"metadata": {}
Although, I used it for creating user and saving cards. However, I didn't use it at the time of checkout, but it seems you can pretty much pass this with every API.
From Stripe Doc,
Updateable Stripe objects—including Account, Charge, Customer, PaymentIntent, Refund, Subscription, and Transfer—have a metadata parameter. You can use this parameter to attach key-value data to these Stripe objects.
Metadata is useful for storing additional, structured information on an object. As an example, you could store your user's full name and corresponding unique identifier from your system on a Stripe Customer object. Metadata is not used by Stripe—for example, not used to authorize or decline a charge—and won't be seen by your users unless you choose to show it to them.
checkout_session = stripe.checkout.Session.create(
customer_email='foo#bar.bar',
success_url=domain_url + 'success?session_id={CHECKOUT_SESSION_ID}',
cancel_url=domain_url + 'cancel/',
payment_method_types=['card'],
mode='subscription',
locale='pl',
line_items=[
{
'price': 'price_*****************',
'quantity': 1,
}
],
subscription_data={'metadata':{'someKeyHere': 'your session variable data'}}
)
I don't know if you've found the solution but that's the mine.
I have used payment intent data and insert into this metadata, that's one of my first help on stackoverflow.
I'm searching from two 2 days the solution, and that's it. So i want to help you.
Stripe Docs are not so clearly and useful
payment_intent_data: {
metadata: {
key1 : val1,
ke2 : val2,
key3 : val3,
}
},

FCM push notification issue: "error":"NotRegistered"

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

PHP Azure SDK Service Bus returns Malformed Response

I'm working on trace logger of sorts that pushes log message requests onto a Queue on a Service Bus, to later be picked off by a worker role which would insert them into the table store. While running on my machine, this works just fine (since I'm the only one using it), but once I put it up on a server to test, it produced the following error:
HTTP_Request2_MessageException: Malformed response: in D:\home\site\wwwroot\vendor\pear-pear.php.net\HTTP_Request2\HTTP\Request2\Adapter\Socket.php on line 1013
0 HTTP_Request2_Response->__construct('', true, Object(Net_URL2)) D:\home\site\wwwroot\vendor\pear-pear.php.net\HTTP_Request2\HTTP\Request2\Adapter\Socket.php:1013
1 HTTP_Request2_Adapter_Socket->readResponse() D:\home\site\wwwroot\vendor\pear-pear.php.net\HTTP_Request2\HTTP\Request2\Adapter\Socket.php:139
2 HTTP_Request2_Adapter_Socket->sendRequest(Object(HTTP_Request2)) D:\home\site\wwwroot\vendor\pear-pear.php.net\HTTP_Request2\HTTP\Request2.php:939
3 HTTP_Request2->send() D:\home\site\wwwroot\vendor\microsoft\windowsazure\WindowsAzure\Common\Internal\Http\HttpClient.php:262
4 WindowsAzure\Common\Internal\Http\HttpClient->send(Array, Object(WindowsAzure\Common\Internal\Http\Url)) D:\home\site\wwwroot\vendor\microsoft\windowsazure\WindowsAzure\Common\Internal\RestProxy.php:141
5 WindowsAzure\Common\Internal\RestProxy->sendContext(Object(WindowsAzure\Common\Internal\Http\HttpCallContext)) D:\home\site\wwwroot\vendor\microsoft\windowsazure\WindowsAzure\Common\Internal\ServiceRestProxy.php:86
6 WindowsAzure\Common\Internal\ServiceRestProxy->sendContext(Object(WindowsAzure\Common\Internal\Http\HttpCallContext)) D:\home\site\wwwroot\vendor\microsoft\windowsazure\WindowsAzure\ServiceBus\ServiceBusRestProxy.php:139
7 WindowsAzure\ServiceBus\ServiceBusRestProxy->sendMessage('<queuename>/mes…', Object(WindowsAzure\ServiceBus\Models\BrokeredMessage)) D:\home\site\wwwroot\vendor\microsoft\windowsazure\WindowsAzure\ServiceBus\ServiceBusRestProxy.php:155
⋮
I've seen previous posts that describe similar issues; Namely:
Windows Azure PHP Queue REST Proxy Limit (Stack Overflow)
Operations on HTTPS do not work correctly (GitHub)
That imply that this is a known issue regarding the PHP Azure Storage libraries, where there are a limited amount of HTTPS connections allowed. Before requirements were changed, I was accessing the table store directly, and ran into this same issue, and fixed it in the way the first link describes.
The problem is that the Service Bus endpoint in the connection string, unlike Table Store (etc.) connection string endpoints, MUST be 'HTTPS'. Trying to use it with 'HTTP' will return a 400 - Bad Request error.
I was wondering if anyone had any ideas on a potential workaround. Any advice would be greatly appreciated.
Thanks!
EDIT (After Gary Liu's Comment):
Here's the code I use to add items to the queue:
private function logToAzureSB($source, $msg, $severity, $machine)
{
// Gather all relevant information
$msgInfo = array(
"Severity" => $severity,
"Message" => $msg,
"Machine" => $machine,
"Source" => $source
);
// Encode it to a JSON string, and add it to a Brokered message.
$encoded = json_encode($msgInfo);
$message = new BrokeredMessage($encoded);
$message->setContentType("application/json");
// Attempt to push the message onto the Queue
try
{
$this->sbRestProxy->sendQueueMessage($this->azureQueueName, $message);
}
catch(ServiceException $e)
{
throw new \DatabaseException($e->getMessage, $e->getCode, $e->getPrevious);
}
}
Here, $this->sbRestProxy is a Service Bus REST Proxy, set up when the logging class initializes.
On the recieving end of things, here's the code on the Worker role side of this:
public override void Run()
{
// Initiates the message pump and callback is invoked for each message that is received, calling close on the client will stop the pump.
Client.OnMessage((receivedMessage) =>
{
try
{
// Pull the Message from the recieved object.
Stream stream = receivedMessage.GetBody<Stream>();
StreamReader reader = new StreamReader(stream);
string message = reader.ReadToEnd();
LoggingMessage mMsg = JsonConvert.DeserializeObject<LoggingMessage>(message);
// Create an entry with the information given.
LogEntry entry = new LogEntry(mMsg);
// Set the Logger to the appropriate table store, and insert the entry into the table.
Logger.InsertIntoLog(entry, mMsg.Service);
}
catch
{
// Handle any message processing specific exceptions here
}
});
CompletedEvent.WaitOne();
}
Where Logging Message is a simple object that basically contains the same fields as the Message Logged in PHP (Used for JSON Deserialization), LogEntry is a TableEntity which contains these fields as well, and Logger is an instance of a Table Store Logger, set up during the worker role's OnStart method.
This was a known issue with the Windows Azure PHP, which hasn't been looked at in a long time, nor has it been fixed. In the time between when I posted this and now, We ended up writing a separate API web service for logging, and had our PHP Code send JSON strings to it over cURL, which works well enough as a temporary work around. We're moving off of PHP now, so this wont be an issue for much longer anyways.

Push notification overrides previous notifications

I'm using codeigniter-gcm library on top of codeigniter to send messages to Google Cloud Messaging service. It sends the message and the message is received at the mobile device, but if I send multiple messages, only the latest message appears on the device (as if it is overriding the previous messages).
I'm seeing that I might need to create a unique notification ID, but I'm not seeing how it's done anywhere on the codeigniter-gcm documentation or Google's documentation for downstream messages.
Any idea how this should be done?
Here's my code in the codeigniter controller. It is worth mentioning that Google's response contains a different message_id for each time I send a push...
public function index() {
$this->load->library("gcm");
$this->gcm->setMessage("Test message sent on " . date("d.m.Y H:i:s"));
$this->gcm->addRecepient("*****************");
$this->gcm->setData(array(
'title' => 'my title',
'some_key' => 'some_val'
));
$this->gcm->setTtl(false);
$this->gcm->setGroup(false);
if ($this->gcm->send())
echo 'Success for all messages';
else
echo 'Some messages have errors';
print_r($this->gcm->status);
print_r($this->gcm->messagesStatuses);
}
After three exhausting days I found the solution. I'm posting it here in hope of saving someone else's time...
I had to add a parameter to the data object inside the greater JSON object, named "notId" with a unique integer value (which I chose to use a random integer from a wide range). Now why Google didn't include this in their docs? Beats me...
Here's how my JSON looks now, when it creates separate notifications instead of overriding:
{
"data": {
"some_key":"some_val",
"title":"test title",
"message":"Test message from 30.09.2015 12:57:44",
"notId":14243
},
"registration_ids":["*******"]
}
Edit:
I'm now thinking that the notId parameter is not really determined by Google, but by a plugin I use on the mobile app side.
To extend further on my environment, my mobile app is developed using Phonegap, so to get push notification I use phonegap-plugin-push which I now see in its docs that parameter name.
I'm kinda' lost now as far as explaining the situation - but happy it is no longer a problem for me :-)
You need to pass a unique ID to each notification. Once you have clicked on the notification you use that ID to remove it.
...
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mNotificationManager.cancel(SIMPLE_NOTFICATION_ID_A);
...
But I'm sure you shouldn't have so much of notifications for user at once. You should show a single notification that consolidates info about group of events like for example Gmail client does. Use Notification.Builder for that purpose.
NotificationCompat.Builder b = new NotificationCompat.Builder(c);
b.setNumber(g_push.Counter)
.setLargeIcon(BitmapFactory.decodeResource(c.getResources(), R.drawable.list_avatar))
.setSmallIcon(R.drawable.ic_stat_example)
.setAutoCancel(true)
.setContentTitle(pushCount > 1 ? c.getString(R.string.stat_messages_title) + pushCount : title)
.setContentText(pushCount > 1 ? push.ProfileID : mess)
.setWhen(g_push.Timestamp)
.setContentIntent(PendingIntent.getActivity(c, 0, it, PendingIntent.FLAG_UPDATE_CURRENT))
.setDeleteIntent(PendingIntent.getBroadcast(c, 0, new Intent(ACTION_CLEAR_NOTIFICATION), PendingIntent.FLAG_CANCEL_CURRENT))
.setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE)
.setSound(Uri.parse(prefs.getString(
SharedPreferencesID.PREFERENCE_ID_PUSH_SOUND_URI,
"android.resource://ru.mail.mailapp/raw/new_message_bells")));

Implementing chat between webpage and android

This is my situation. I have a chat-room website. People are publicly chatting together and everyone see who is writing what. All the chats are stored in database (mysql)
Now I want to implement this chatting on Android. So when user sends a text from his phone it should be sent to the chat-room website and vice versa, meaning the Android user should see all texts which are being sent from the chat webpage.
As a result:
1: Android user should see all the texts which people send via the webpage,
2: Android user should be able to send a text to this chat-room webpage (so other users which are using the webpage to chat should see his text).
The big question is, what is the best way to achieve this?
Could this process happen in real time like XMPP?
Is GCM the only way (although it is not real time)?
If i use web services to send the messages to the web, how can i set a listener for the incoming messages?
I don't know if i am clear. Any help is appreciated. Just give the head of the string i will go to the end...
Edit: a server side question: Is there anyway to make the server do something when a specific table in MYSQL is changed (for example when a new row is added)?
The first thing that leapt into my mind was that this fits fairly well into the Pub/Sub paradigm. Clients publish chat messages to specific channels (rooms,) and also subscribe to the channels; the server subscribes to a channel and stores the data in a MySQL database.
You might try using an external real-time network like PubNub. PubNub is free for up to 1m messages (see the pricing page.) They have an Android SDK and PHP SDK (I assume you're using PHP on your server due to your use of the PHP tag.)
In your case, in your Android client, you'd subscribe to a channel:
Pubnub pubnub = new Pubnub("demo", "demo");
try {
pubnub.subscribe("my_channel", new Callback() {
//See full example for all Callback methods
#Override
public void successCallback(String channel, Object message) {
System.out.println("SUBSCRIBE : " + channel + " : "
+ message.getClass() + " : " + message.toString());
}
}
} catch (PubnubException e) {
System.out.println(e.toString());
}
(Full example here.) Then, when you want to publish a message:
Callback callback = new Callback() {
public void successCallback(String channel, Object response) {
Log.d("PUBNUB",response.toString());
}
public void errorCallback(String channel, PubnubError error) {
Log.d("PUBNUB",error.toString());
}
};
pubnub.publish("my_channel", "This is an important chat message!" , callback);
Neat! But what about your server, how does it receive these messages?
$pubnub = new Pubnub(
"demo", ## PUBLISH_KEY
"demo", ## SUBSCRIBE_KEY
"", ## SECRET_KEY
false ## SSL_ON?
);
$pubnub->subscribe(array(
'channel' => 'my_channel', ## REQUIRED Channel to Listen
'callback' => function($message) { ## REQUIRED Callback With Response
## Time to log this to MySQL!
return true; ## Keep listening (return false to stop)
}
));
I hope this helps your project. Let me know how it goes.
SHORT ANSWER
Here's link to CODETUTS
and to a SAMPLE
LONG ANSWER
For make a chat in realtime compatible with android using db like mysql you have various way. the first who come up to me is to do some api but is not the most good way cause you will have to do many request to your server. So i advice you to use technology like nodeJs and socketIO (just google them...you will find tons of example), take a look to the link i've found for you. Have a nice day. Antonio
You need websockets on the web to do this in real time, in android you need to send push notifications.
Maybe you want to check "Google Cloud Messaging for Android".

Categories