FB Messenger Bot: Webhook updates not being delivered - php

i am working with Facebook Checkbox Plugin everything is working fine except facebook is not sending request to my webhook url when Confirming Opt-in.
in facebook docs it is mentioned that
After the opt-in event, we will post a webhook event to your server if the checkbox state was checked. This callback has the same format as the opt-in callback, but instead of a sender field, it has an optin object with a user_ref field.
But it is not sending any data. here is my webhook code
if (!empty($_REQUEST['hub_mode']) && $_REQUEST['hub_mode'] == 'subscribe' && $_REQUEST['hub_verify_token'] == 'verificationtoken') {
echo $_REQUEST['hub_challenge'];
}
$data = file_get_contents("php://input");
$fp = file_put_contents( PROTECH_FBB_DIR.'/data.log', $data);
i have also tried hitting my webhook manually and see if it responds. and it does work perfectly normal, so it means facebook is not posting data or maybe i am doing something wrong?
Any help would be highly appreciated. thanks

I am not php developer but have implemented the same logic in javascript, node.js. I would like to share the steps in detail and also the javascript code and hope you can figure out what you can do with it to make your life better :P
As you said, you are receiving the user_ref from the api call. That's correct. Read the documentation once again they have mentioned the user_ref will be received when user check the checkbox plugin. This user_ref is set by you and every-time the page loads this user_ref must be unique then only the checkbox plugin will render, if it is not unique the plugin wont render. And here is the complete logic behind it. You generate the user_ref, when user check the checkbox, you receive this unqiue user_ref, using this user_ref you send message to the user(you can send message to user using user_ref as many time as you want but I will suggest you rather use senderId). When you send the message to user using user_ref, the webhook api will give you a response containing senderId of the user which is actually psid we normally use in our app. This is what you need to save in your db.
Now I will put my code here how I did it.
Receiving the user_ref and sending message to user:
My payload:
function sendTextMessageRef(user_ref, messageText,md) {
var messageData = {
recipient: {
user_ref: user_ref
},
message: {
text: messageText,
metadata: md
}
};
callSendAPI(messageData);
}
function callSendAPI(messageData) {
request({
uri: 'https://graph.facebook.com/v2.6/me/messages',
qs: { access_token: PAGE_ACCESS_TOKEN },
method: 'POST',
json: messageData
}, function (error, response, body) {
if (!error && response.statusCode == 200) {
var recipientId = body.recipient_id;
var messageId = body.message_id;
if (messageId) {
console.log("Successfully sent message with id %s to recipient %s",
messageId, recipientId);
} else {
console.log("Successfully called Send API for recipient %s",
recipientId);
}
} else {
console.error("Failed calling Send API", response.statusCode, response.statusMessage, body.error);
}
});
}
Now, after sending the message, I receive a response in this json format which will include the sender id of the user:
{"sender":{"id":"xxxxxxx"},"recipient":{"id":"xxxxxWhat you are looking for is this*******"},"timestamp":1504698781373,"message":{"is_echo":true,"app_id":xxxxxxxxxxxxxxx,"metadata":"INVITATION__REPLY__qwe__2017-09-05T02xo20__xxxxxxxx__063__yes","mid":"mid.$cAAGcxxxxxxxxVxuAtJ","seq":120162,"text":":)"}}
In above received json data, the recipient.id is what you are looking for.
Here To make you understand what I did in my chat bot is first user select the checkbox plugin, I receive the call on my server, if check if it contains user_ref, if yes then I send a text message to user with a custom metadata using user_ref. When user receives the message, the webhook send me a json data in the above given format. To identify for which user_ref I have received this response, I set custom metadata which is combination of some string+user_ref. Using this I identify the sender.id of the user for which I previously sent message using user_ref. The sender.id is my pageid and recipient.id the the user id which you are trying to get and using which we generally send message to the user and is also know as psid.
Hope this helps, if you still get some issue using the above mentioned solution, then do update about it :)

Related

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

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.

How to retrieve device registration ID by using PHP (web) to avoid MismatchSenderId?

This is my code on the app on phonegap to obtain the registration ID. The registration ID is always obtained successfully.
const push = PushNotification.init({
android: {
},
browser: {
pushServiceURL: 'http://push.api.phonegap.com/v1/push'
},
ios: {
alert: "true",
badge: "true",
sound: "true"
},
windows: {}
});
push.on('registration', function ( data ) {
recordRegistrationID( data.registrationId ); //Pseudo
});
push.on('notification', function ( data ) {
// data.message,
// data.title,
// data.count,
// data.sound,
// data.image,
// data.additionalData
});
push.on('error', (e) => {
// e.message
});
Then I try to send a push notification to that device with the stored registration ID from my PHP server (example.com) by cURLing it to Firebase Cloud Messaging API, it always returns MismatchSenderId.
{"multicast_id":6660509895358893455,"success":0,"failure":1,"canonical_ids":0,"results":[{"error":"MismatchSenderId"}]}
I already tried to compile the .APK and run it on my mobile device. I always successfully get the token, but the message can never be sent to that registration ID retrieved by the PhoneGap app.
Doing more research, it seems like that because PhoneGap actually retrieves the registration ID but it ties to PhoneGap itself as a sender, so other senders won't work.
Am I correct to assume this? If this is the case, how can I obtain the registration ID using PHP instead of PhoneGap, for Firebase Cloud Messaging?
Thank you.
Doing more research, it seems like that because PhoneGap actually retrieves the registration ID but it ties to PhoneGap itself as a sender, so other senders won't work.
This seems like an odd behavior. The token generated is tied to an app instance and the Firebase project. Can you provide the link where this is mentioned? It doesn't make sense for the token associated to change to a different sender at all.
With that said, there is currently no way to get the registration token from your backend. As I mentioned above, the token is tied to the app instance, so it can only be generated on the client app side.

php paypal Server-side REST integration failure

(I'm not able to post code/errors etc because: 'You need at least 10 reputation to post more than 2 links'; and everything contains a lot of links; just spent a couple of hours on a fully detailed post).
I've read similar posts here (and other places) but they have not helped.
I'm basically following:
https://developer.paypal.com/docs/integration/direct/express-checkout/integration-jsv4/server-side-REST-integration/#set-up-your-client
but PayPal's documentation is somewhat chaotic, lacking in some crucial details and mysterious in places. Obviously written by a committee.
In a nutshell I have a PayPal payment id: "id": "PAY-0J356327TH335450NK56Y2PQ",
PayPal say return this from my create payment script. I'm not entirely sure how it should be returned: simply echo "PAY-0J356327TH335450NK56Y2PQ"; or return a json string. Tried both, did not solve anything.
Then there is (from the client side button):
onAuthorize: function(data) {
return paypal.request.post(EXECUTE_PAYMENT_URL, {
paymentID: data.paymentID,
payerID: data.payerID
}).then(function() {
// The payment is complete!
// You can now show a confirmation message to the customer
});
Which I read as it calling my execute payment script with 2 variables (paymentID and payerID), which I need to use in my script.
I've tried the obvious:
$paymentID = $_POST["paymentID"];
$payerID = $_POST["payerID"];
The not so obvious (because I'm assuming it's POSTing data although some docs seem somewhat confused on this point):
$paymentID = $_GET["paymentID"];
$payerID = $_GET["payerID"];
My final attempt of:
$ret = file_get_contents("php://input");
$ar = json_decode($ret, TRUE);
$paymentID = $ar["paymentID"];
$payerID = $ar["payerID"];
None of which made any difference.
The errors I'm getting from PayPal are:
ppxo_no_token_passed_to_payment Object ...
ppxo_unhandled_error Object ...
Error: No value passed to payment
decorate ...
I obviously have a valid access token otherwise I'd not get as far as getting a payment id.
Currently stumped; any ideas/pointers appreciated.
This really should be simple.
I had the same error message.
For me it occured when the javascript code receieved the response from my CREATE_PAYMENT_URL ajax request.
The response I was sending back from the server was not valid JSON so when it came time to execute onAuthorize data.paymentID and data.payerID were not available.
I suggests using console.log(data) just above the return statement in onAuthorize to make sure the data json object has a paymentId and payerId.
If not then it may be that your previoius CREATE_PAYMENT_URL is not returning the payment id.
The responses should be json. For example a valid create payment response:
{"id":"PAY-5T1130394T551090NLGRLACY"}
Hope that helps.

Receiving multiple webhook notification for Facebook Feed

I am facing issue with Facebook Webhook for feed while messages are working perfectly. For one post i keep on getting multiple notification from Facebook. I have already raised a bug with Facebook and their team is saying that my server is failing to send back 200 OK HTTP status. Also in their doc i have found that
"Your webhook callback should always return a 200 OK HTTP response when invoked by Facebook. Failing to do so may cause your webhook to be unsubscribed by the Messenger Platform."
My code goes like this:
<?php
$challenge = $_REQUEST['hub_challenge'];
$verify_token = $_REQUEST['hub_verify_token'];
if ($verify_token === 'password')
{
echo $challenge;
}
/*........RECEIVING INPUT FROM fACEBOOK.........*/
$input = json_decode(file_get_contents('php://input') , true);
error_log(print_r($input, true));
/*after this i am calling AI and then replying back*/
Is there any way to send back 200 OK status before calling AI in php.
For the Workaround, I have stored the notification in DB and for every notification i am checking existing data (timestamp, senderId, post) in the DB to eliminate the duplicate post.
If anyone is having better option in terms of complexity please let us know.
I a similar issue and in my case I was subscribed to my test app and actual production app. Thus 2 events being sent

Facebook graph api Friend Invitations

I have managed to send and retrieve data from facebook for invitatin using
FB.ui({
method: 'apprequests',
to: sendto,
message: 'Win Mega Prizes!',
title: 'Enter in this contest!'
},
function (response) {
if (response && response.request_ids) {
var requests = response.request_ids.join(',');
alert(requests);
window.location.href = "http://www.rmyserver/index.php?invitations="+requests+"&contest_id="+c_id+"&page_id="+page_id+"&user="+from+"&g_id="+g_id+"&sendto="+sendto+"&single=1";
} else {
alert('canceled');
}
PHP
foreach($requests as $request_id) {
// Get the request details using Graph API
echo $request_content = json_decode(file_get_contents("https://graph.facebook.com/$request_id?$app_token"), TRUE);
But i want to use single user app request , as per docs i need to use TO: filed but this doesnt work, request is sent as well i get invitation id but php code used above returns false.
Am i missing something
you can try direct url example given here https://developers.facebook.com/docs/reference/dialogs/requests/#frictionless_requests
that should work fine from php
This works for me:
FB.ui({
method:'apprequests',
message:'I want you to open up your heart a little.',
to:123456798,
// filters:['app_non_users'], // commented: let users send notifications to app users too, probably a good idea to make them revisit?
data:'' // send data what you would like to track, FB will send that back in the URL for us
// #todo send google campaign parameters
});
Moreover you are doing it wrong on PHP side, you would need to redirect the user to a URL where the invitation friend selector would be displayed. There is no way in my knowledge to directly send an invitation to users using PHP.

Categories