Does GAE support Plivo? - php

I've been trying to implement Plivo on my GAE server but I'm getting a 500 error.
I setup Plivo by using Plivo's Github PHP Helper Library. I saved that file as plivo.php on my test server. Then I added plivosend.php with the following code
<?php
if($_POST) {
require_once 'plivo.php';
$auth_id = "auth_id";
$auth_token = "auth_token";
$p = new RestAPI($auth_id, $auth_token);
// make sure all 3 params are valid
if(!empty($_POST['send_to_name']) && !empty($_POST['send_to_number']) && !empty($_POST['sender_name'])) {
$message = 'this message doesn't matter';
$plivo_url = 'https://glacial-harbor-8656.herokuapp.com/report';
// Send message
$params = array(
'src' => '15555555555', // Sender's phone number with country code
'dst' => $_POST['send_to_number'], // Receiver's phone number with country code
'text' => $message, // Your SMS text message
'url' => $plivo_url, // The URL to which with the status of the message is sent
'method' => 'POST' // The method used to call the url
);
// Send message
$response = $p->send_message($params);
// Print the response
$message_uuid = $response['response']['message_uuid'][0];
if(!empty($message_uuid)) {
echo '{"success":1,"message_uuid":' . $message_uuid . '"}';
}
else {
// todo log this?
echo '{"success":0,"error_message":"Message failed to send."}';
}
}
else {
echo '{"success":0,"error_message":"Message failed to send. Incorrect params."}';
}
}
?>
On my test server (just my website), this sends without any problems. When I put both plivo.php and plivosend.php on GAE, I get the following 500 error:
207.58.203.50 - - [21/Sep/2015:09:58:00 -0700] "POST /plivosend.php HTTP/1.1" 500 25 - "appname/1.0.2 (iPhone; iOS 9.0; Scale/2.00)" "appname-xxx.appspot.com" ms=4 cpu_ms=3 cpm_usd=0.000003 instance=00c61b117cd04d3645448a84e24daba9991882e1 app_engine_release=1.9.26
I have no idea why... The details are extremely limited.
Does anyone have a clue? Does GAE not support Plivo?

Google App Engine restricts many functions (necessary in order to be a massively auto-scaling application platform). One restriction is outbound HTTP requests (from your PHP code to external). Read about it here, HTTP Requests and cURL Support for details and options.

Related

GuzzleHTTP headers not set properly

I'm trying to send a GET request to an api with my keys. My keys need to be in the header. I have saved my keys in an array $api and given them to Guzzle.
When I send the GET request I get a 403 Forbidden response, my best guess is that the headers aren't actually being set.
require "../vendor/autoload.php";
use GuzzleHttp\Client;
$api = array(
"X-Api-Id" => "id",
"X-Api-Secret" => "secret",
"X-Api-Key" => "key"
);
$client = new GuzzleHttp\Client([
"base_uri" => "example.com",
"timeout" => 5
]);
$response = $this->client->request("GET", "/configs/", ["verify" => false, "headers" => $api]);
var_dump($response);
The error
Fatal error: Uncaught GuzzleHttp\Exception\ClientException: Client error: GET https://api.onetap.com/configs/ resulted in a 403 Forbidden response: <!--[if IE 7]> <html class (truncated...) in C:\scaryconfigscc\vendor\guzzlehttp\guzzle\src\Exception\RequestException.php on line 113
The documentation doesn't show it much different, besides it also has default headers:
try {
$response = $client->request('GET', '/configs', [
'headers' => [
'User-Agent' => 'testing/1.0',
'Accept' => 'application/json',
'X-Api-Id' => 'id',
'X-Api-Secret' => 'secret',
'X-Api-Key' => 'key'
]
]);
} catch (ClientException $e) {
echo 'Caught exception: ' . $e->getMessage() . "\n";
}
If it doesn't work - either the credentials are wrong or it may possibly expect method POST.
You can try the following:
Use $request->getHeader() to retrieve all the headers that are being sent and check if the required headers are set.
Check $response->getHeader() and verify is the api is looking to set a cookie. In case a cookie is actually being set, you will need to set a cookiejar (https://docs.guzzlephp.org/en/stable/quickstart.html#cookies).
If both the above does not solve the problem then things get complicated. It is more of trial and error from here on. You can try one or all of the following:
Change user agent
it could be that the api is blocking your IP (so you need to try it on a different server or IP)
they could be detecting some of your use as an offending user and may be blocking the request via their firewall or at the app level. What triggers the rules is for you and them to figure out.
If it still does not work you will need to contact their support or share more details before anyone can help you.
I you receive a http response 403 Forbidden then you either not allowed to communicate with the url you request (check if you have all api keys etc generated) and/or your request is incomplete/wrong.
You may exclude Guzzle as a reason of the failure and narrow the possible causes by doing the same request with other tools. If you succeed then it means the problem is with the Guzzle, but if you don't then one of the problems described above is the real issue.
To do request with PHP's native funcion file_get_contents() use this code:
<?php
# display as a plain text instead of html, just for test
header('Content-Type: text/plain');
# full url including '/configs'
$url = 'https://www.example.com/configs/';
$headers = [
"X-Api-Id" => "id",
"X-Api-Secret" => "secret",
"X-Api-Key" => "key"
];
# optional query str
// $queryArr = [
// 'key' => 'value'
// ];
# ----------- prepare request -----------------
if (!empty($queryArr)) {
$url .= '?' . http_build_query($queryArr);
}
$opts = [];
$opts['http']['method'] = "GET";
if (!empty($headers)) {
$headersStr = '';
foreach ($headers as $header => $value) {
$headersStr .= $header . ': ' . $value . "\r\n";
}
$opts['http']['header'] = $headersStr;
$context = stream_context_create($opts);
}
# ----------- prepare request end ------------
if (isset($context)) {
$response = file_get_contents($url, false, $context);
} else {
$response = file_get_contents($url);
}
echo $response;
I checked that method with my own service and it works, all headers are send.
But if you don't believe me you can do the same by using cURL.
Type in the command line:
curl --insecure -L -H -S -v --connect-timeout 5 -H "X-Api-Id: id" -H "X-Api-Secret: secret" -H "X-Api-Key: key" "https://www.example.com/configs/"
you may need instead of curl at the beginning use the full path to the executable like C:\Downloads\curl.exe at Windows.
The options I used:
--insecure Allow insecure server connections when using SSL
-L, --location Follow redirects
-H, --header <header/#file> Pass custom header(s) to server
-S, --show-error Show error even when -s is used
-v A line starting with '>' means "header data" sent by curl, '<' means "header data"
received by curl that is hidden in normal cases, and a line starting with '*' means additional
info provided by curl.
--connect-timeout <seconds> Maximum time allowed for connection
You should firstly try the command line above without:
--insecure Allow insecure server connections when using SSL
but if you have a problem on Windows with checking certificates this is one of the workarounds (not recommended), it is better to fix that problem
for the command line curl certificate check this link, see section Certificate Verification point 4.Additionally how to fix certificate problem with php
If you run the command line curl example as above you should get something like that in the output:
* Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* Connected to www.example.com (xxx.xxx.xxx.xxx) port 80 (#0)
> GET /configs/ HTTP/1.1
> Host: www.example.com
> User-Agent: curl/7.58.0
> Accept: */*
> X-Api-Id: id
> X-Api-Secret: secret
> X-Api-Key: key
>
< HTTP/1.1 200 OK
< Connection: Keep-Alive
< Keep-Alive: timeout=5, max=100
(...)
where as previously mentioned:
-v A line starting with '>' means "header data" sent by curl, '<' means "header data"
So you can be sure and have a proof that the headers were send:
> X-Api-Id: id
> X-Api-Secret: secret
> X-Api-Key: key
There are some additional headers like:
> Accept: */*
And if you want to receive a response body as json you probably should also set this header too by adding to the command line:
-H "Accept: application/json"
but check that with the documentation of the api you request.
Once you confirm everything is working as it should by file_get_contents or cURL command line you can check everything with the Guzzle. Before you do that check if your php has curl by the command line:
php -m
you will see all the extensions in the php installed and there should be an extension named curl
Guzzle example to do a request with headers and optional query string.
I used guzzle installed by this command:
composer require "guzzlehttp/guzzle:^7.2"
Code using Guzzle:
<?php
require_once "../vendor/autoload.php";
# display as a plain text instead of html, just for test
header('Content-Type: text/plain');
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\RequestOptions;
try {
$headers = [
"X-Api-Id" => "id",
"X-Api-Secret" => "secret",
"X-Api-Key" => "key"
];
# optional query str
// $queryArr = [
// 'key' => 'value'
// ];
$method = 'GET';
$scheme = 'https';
$baseUrl = 'www.example.com';
$urlDir = '/configs/';
$baseUri = strtolower($scheme) . '://' . $baseUrl;
$clientConfigArr = [
'base_uri' => $baseUri,
RequestOptions::TIMEOUT => 5.0, // Timeout if a server does not return a response in 5 seconds.
RequestOptions::VERIFY => false, // Disable validation entirely (don't do this!).
RequestOptions::DEBUG => true,
];
# set method to default value if not set
if (empty(trim($method))) {
$method = 'GET';
}
# set location to / if not set
if (empty(trim($urlDir))) {
$urlDir = '/';
}
# add query string if query arr not empty
if (!empty($queryArr)) {
$urlDir .= '?' . http_build_query($queryArr);
}
# if headers are set
if (!empty($headers)) {
# create request with headers
$request = new Request(strtoupper(trim($method)), $urlDir, $headers);
} else {
# create request without headers
$request = new Request(strtoupper(trim($method)), $urlDir);
}
# create client and send a the requrest
$client = new Client($clientConfigArr);
$response = $client->sendRequest($request);
} catch (\GuzzleHttp\Exception\TransferException $e) {
$msg = ' ';
$msg .= \PHP_EOL;
$msg .= '[BAD] [Transfer ERR: ' . $e->getCode() . ']' . \PHP_EOL;
$msg .= 'at ' . $e->getFile() . ':' . $e->getLine() . \PHP_EOL;
if (!empty($response) && is_object($response) && method_exists($response, 'getBody')) {
$msg .= '[Response Body] ' . (string) $response->getBody() . \PHP_EOL;
}
echo "$msg";
exit(1);
} catch (\Throwable $e) {
$msg = '';
$msg .= \PHP_EOL;
$msg .= '[BAD] [ERROR Throwable]' . \PHP_EOL;
$msg .= '[Message] ' . $e->getMessage() . \PHP_EOL;
$msg .= 'at ' . $e->getFile() . ':' . $e->getLine() . \PHP_EOL;
echo "$msg";
exit(1);
}
echo '[OK] [SUCCESS]' . \PHP_EOL . \PHP_EOL;
echo $response->getBody() . \PHP_EOL;
Note that using:
RequestOptions::VERIFY => false
is the same as using --insecure for cURL command line and should be avoided, better fix the problem with the SSL certificates by following the instruction previously given. Having that problem fixed you may remove the RequestOptions::VERIFY => false from the $clientConfigArr
Guzzle usefull links:
Request
Request Options
How to check if a request or response has a specific header.
Client
Response
Exceptions
It is possible that you should not do the GET request but POST instead as Martin mentioned. If you want to make a request with different method then:
Request Methods
For the POST method you will need most likely send a body as well, you can create a POST Request and attach the body this way:
use GuzzleHttp\Psr7\Request;
$headers = ['X-Foo' => 'Bar'];
$body = 'hello!';
$request = new Request('POST', 'http://httpbin.org/post', $headers, $body);
# and send request (as in the Guzzle example)
$client = new Client($clientConfigArr);
$response = $client->sendRequest($request);
check with the api documentation if you need to pass some body content (perhaps your token) if you are going to do the POST request (or any other method)
A side note:
In the Guzzle Code example instead of require I used require_once because:
if the code from a file has already been included, it will not be included again.
source
Therefore you can use in may files as many times as you want:
require_once "../vendor/autoload.php";
and everything will work fine, in contrary to the require without once that will very often make you duplicate declaration errors.
For having windows like backslashes you may use DIRECTORY_SEPARATOR and DIR as a current dir. Using them both like this:
require_once __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
Just in case you would like to know.

Slack Events PHP Response

I'm working on an integration between Slack and Filemaker utilizing PHP. I am successful in having the code create a record in Filemaker based on the json request, and also have no trouble returning the challenge key to Slack.
However, I'm having trouble passing the header response 200 OK to Slack, while passing the challenge back. It looks like it has to be one or the other.
I've tried to move the HTTP header to different areas in the code, but haven't had any success so far.
Here is the current code:
<?php
$data = json_decode(file_get_contents('php://input'), true);
if (!isset($data["challenge"])) {
$body = $_SERVER['HTTP_X_SLACK_RETRY_REASON'];
require_once ('Filemaker.php');
//$body = file_get_contents('php://input');
$fm = new Filemaker();
$fm->setProperty('database', '');
$fm->setProperty('username', '');
$fm->setProperty('password', '');
$command = $fm->newPerformScriptCommand('PHP_RESPONSE', 'script', $body);
$result = $command->execute();
}
else {
header("Content-Type: text/plain");
header('X-PHP-Response-Code: 200', true, 200);
echo $data["challenge"];
}
?>
The result I expect is for the code to return the challenge code for Slack, while also returning an HTTP header of 200 OK.
Currently I can see I am receiving an error of "http_error" from Slack, which is what leads me to believe the problem is that the header is not being passed back successfully.
Any ideas on what is wrong, or suggestions on the right direction to proceed would be greatly appreciated.
The problem was occurring because for events slack doesn't send "challenge" as a parameter when sending events. It looks like echoing "challenge" is only needed when initially setting the URL for the events API.
I enclosed the challenge echo in a if statement that would only trigger if the challenge variable was present. After doing so the 200 OK was successfully passed.
Here is the code I used that solved the problem for me:
$data = json_decode(file_get_contents('php://input'), true);
if (isset($data["challenge"])) {
$message = [
"challenge" => $data["challenge"]
];
header('Content-Type: application/json');
echo json_encode($message);
}
The documentation is actually a bit inconsistent on this topic. It claims you can respond the challenge in plan text, but the example shows it as x-www-form-urlencoded.
To be on the safe side try returning the challenge as JSON. That works perfectly for me. You also do not need to explicitly set the HTTP 200 code.
Example code:
$message = [
"challenge" => $data["challenge"]
];
header('content-type: application/json');
echo json_encode($message);

Push notification is being sent to server but doesn't appear on cellphone

So i have this test application that i'm using to learn how to send push notifications from an external server hosted in my desktop to a virtual device or smartphone.
Then I use a simple form and input title and message and click the submit button to send the notification.
I have triple checked, the encoding of the notification is fine, and I'm able to pass the firebase token from the android app to the server, which stores it in the MySQL database.
Also the app is correctly sending the token and the server_key is also correct (used the built in copy function at firebase website)
Why isn't my notification showing? this is the source code of the script in php to send the notification to firebase:
<?php
require "init.php";
$message = $_POST['message'];
$title = $_POST['title'];
$path_to_fcm = 'https://fcm.googleapis.com/fcm/send';
$server_key = "AAAA0Tedu0Y:APA91bGF1k9LAVw90LVM9IdWludKeG1ueVo2V7HesN4CVz2KFvcwGvLkDymuHjm0IvfRvZ6wOEu5Q33pBgYDUXopvTOBSDKQJf2zFFp_22gTCrg6zxNxKyw8_0M9ciPLt3YyJOwkmNYR";
$sql = "select fcm_token from fcm_info";
$result = mysqli_query($con,$sql);
//these lines get the key that has been store (i only store one key from one app, so it takes the first value of the result
$row = mysqli_fetch_row($result);
$key = $row[0];
$header = array(
'Authorization:key=' .$server_key,
'Content-Type:application/json'
);
$field = array( 'to'=>$key,
'notification'=>array('title'=>$title,'body'=>$message)
);
$payload = json_encode($field);
//If echo $payload, It print the following:
/*
{
"to":"esM8_IMRWrw:APA91bEVOTjpC5kkqAWqWwEs7gNq5-4iUvL6fC947cfWkg1G0VhKDiuibSOr_xSNcIJ8rb4VewjNJ2Jd_0AXBdQgTbOO0FO-stmP3ymOCLGQlka3s2RQ3854UiPr_puc274hXSlQMLen",
"notification":{
"title":"asdasd",
"body":"asdasd"
}
}
So the json is properly formatted
*/
/*this generates the http url connection from my server to firebase push notification sending url. I think the error is somewhere in these lines, but i dont know where*/
$curl_session = curl_init();
curl_setopt($curl_session,CURLOPT_URL,$path_to_fcm);
curl_setopt($curl_session,CURLOPT_POST,TRUE);
curl_setopt($curl_session,CURLOPT_HTTPHEADER,$header);
curl_setopt($curl_session,CURLOPT_RETURNTRANSFER,TRUE);
curl_setopt($curl_session,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($curl_session,CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4);
curl_setopt($curl_session,CURLOPT_POSTFIELDS,$payload);
$result = curl_exec($curl_session);
curl_close($curl_session);
mysqli_close($con);
?>
Any help is appreciated, thanks in advance!
Check this link to get more information about push notifications
How to handle notification when app in background in Firebase

Postman behavior different than HTTP plugin for PHP

I want to send a HTTP post request to my node.js server. I pass a JSON object as body (content type is set to application/json).
If I send -exactly- the same JSON object in the body via Postman (https://www.getpostman.com) it works perfectly! If I use this plugin here: https://github.com/Mashape/unirest-php it is not working, I get HTTP error 500 and error message 'Cannot read property '0' of undefined' like something would be wrong with my route/ my passed JSON.. which is definitely not the case because Postman works.
How can I fix this? Is there maybe a better PHP plugin that works as it should?
I hope somebody can help me.
//HTTP Requests an Node Server. Pfade entsprechend abändern sobald live
$teamsMemberOf = Unirest\Request::get("http://localhost:3001/members-matter/mm-teams/".$result['_id']."/get-teams");
$json = $teamsMemberOf->raw_body;
$buildJson = '{"team":'.$json.'}';
//$sendJsonEncoded = json_decode($buildJson, true);
$headers = array("Accept" => "application/json");
$relevantBoxesAmount = Unirest\Request::post("http://localhost:3001/members-matter/mm-boxes/".$result['_id']."/get-relevant-boxes-amount", $headers, $buildJson);
//$relevantBoxesAmount = Unirest\Request::post("http://mockbin.com/echo?", $headers, $buildJson);
//$relevantBoxesAmount = Unirest\Request::post("http://localhost:3001/members-matter/mm-boxes/56954133596dc02d0695cb89/get-relevant-boxes-amount", $headers, $sendJsonEncoded);
echo $relevantBoxesAmount->code;
echo $relevantBoxesAmount->raw_body;

Getting a 10004 error with button type invalid using BMUpdateButton method for PayPal

I've been searching the paypal website, forums and Google in general searching for a solution on this issue. I am aware that there is another question quite similar to mine, although it does remains unanswered and I'm not allowed (yet) to post comments to ask for clarification here: what's wrong with this code to update a paypal button? I get "buttontype invalid error"
I'm relatively new using the Paypal APIs, so I'll just reproduce the steps that brought me here.
Created a hosted button of type 'add to cart' on Paypal.
Copied the generated HTML to my website.
Instead of posting directly to Paypal I just post to a PHP script (pasted below).
ini_set('track_errors', true);
$url = trim('https://api-3t.paypal.com/nvp');
$body_data = array(
'USER' => "omitted",
'PWD' => "omitted",
'SIGNATURE' => "_omitted_",
'VERSION' => "104",
'METHOD' => "BMUpdateButton",
'HOSTEDBUTTONID' => "_omitted_",
'BUTTONTYPE' => "CART",
'BUTTONCODE' => "HOSTED",
'L_BUTTONVAR0' => "amount="_new item price_",
'L_BUTTONVAR1' => "item_name="_item name_",
'L_BUTTONVAR2' => "currency_code="_new currency code_",
'L_BUTTONVAR3' => "on0=".$_POST['os0'],
'L_BUTTONVAR4' => "on1=".$_POST['on1'],
'L_BUTTONVAR5' => "on2=".$_POST['on2'],
'L_BUTTONVAR6' => "quantity=".$_POST['quantity']
);
$body_data = http_build_query($body_data, '', chr(38));
try
{
//create request and add headers
$params = array('http' => array(
'method' => "POST",
'content' => $body_data,
));
//create stream context
$ctx = stream_context_create($params);
//open the stream and send request
$fp = #fopen($url, 'r', false, $ctx);
//get response
$response = stream_get_contents($fp);
//check to see if stream is open
if ($response === false) {
throw new Exception("php error message = " . "$php_errormsg");
}
//echo $response . "<br/>";
//close the stream
fclose($fp);
//parse key from the response
$key = explode("&",$response);
//print_r ($key);
//set url to approve the transaction
$payPalURL = "https://www.paypal.com/cgi-bin/webscr" . str_replace("TOKEN", "token", htmlspecialchars(urldecode($key[0])));
If ( $key[5] == 'ACK=Success')
{
header( "Location: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=T3XNV39TEVDJG" );
}
else
{
echo $response;
echo $key[3];
echo 'ERROR Code: "' . str_replace("L_ERRORCODE0=", "", htmlspecialchars(urldecode($key[5]))) . '"<br/> Error Long Message: "' . str_replace("L_LONGMESSAGE0=", "", htmlspecialchars(urldecode($key[7]))) . '"';
}
}
catch(Exception $e)
{
echo 'Message: ||' .$e->getMessage().'||';
}
}
and basically what I'm getting is the error:
Code: "10004"
Error Long Message: "The button type specified is invalid."
The main issue is that even though I'm following whatever API instructions I'm able to get (IMHO the PayPal developers website is a mess), and I'm positively thinking I'm setting the BUTTONTYPE variable correctly, I'm still getting this error.
What I have tested:
Tested the hosted button directly, works fine. But I do need to update the price of the item dynamically.
Changed the BUTTONTYPE to something wrong. This gives me the appropiate error: 11925, but when I move it to the 'right'value I get the previous error code again.
Removed all variables except the ones required: USER, PWD, SIGNATURE, VERSION, METHOD, HOSTEDBUTTONID,BUTTONTYPE,BUTTONCODE. I get the same error.
Changed the version. Just in case the values allowed for BUTTONTYPE had changed from one to another. Same error.
So basically I'm stuck here. Do you have any ideas? My website is hosted on bluehost although I'm not allowed to 'publish' the site until this is fixed for my client.
Thanks in advance
I have answered this in this post:
https://stackoverflow.com/a/20899747/2067005
BUTTONTYPE needs BUTTONSUBTYPE to accompany it even though it says it's a optional field.

Categories