How do I troubleshoot Curl in PHP? - php

I have never used Curl but I am trying to complete a self api project at my job just to gain some experience.
And I am stuck on the first step... I want to authenticate with an api.
So I am running this code and I expect to see a Success 200 response with my access token, etc but I get nothing.
No error, no feedback, the page just opens up blank
I have tired to use CURLINFO_HEADER_OUT from this page What Steps do you Take to Troubleshoot Problems with PHP cURL? but still I got a blank page
Anyway thank you to anyone in advantage for some tips
<?php
const TOKEN_ENDPOINT = 'xxxxxx';
const GRANT_TYPE = 'xxxxx';
const CLIENTID = 'xxxxxxxxxxx';
const CLIENTSECRET = 'xxxxxxxxxxxxxx';
const USERNAME = 'xxxxxxxxxxxxxxxxx';
const PASSWORD = 'xxxxxxxxxxxxxxx';
$clientCredentials = base64_encode(CLIENTID . ':' . CLIENTSECRET);
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => TOKEN_ENDPOINT,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS =>'grant_type=' . GRANT_TYPE . '&username=' . USERNAME . '&password=' . PASSWORD ,
CURLOPT_HTTPHEADER => array(
'Content-Type: application/x-www-form-urlencoded',
'Accept: application/json',
'Authorization: Basic ' . $clientCredentials
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response ;
?>

To check curl error, the best way is to use curl_error function
$response = curl_exec($curl);
if (curl_errno($curl)) {
$error_msg = curl_error($curl);
}
curl_close($curl);
echo $response ;
See the description of libcurl error codes here
See the description of PHP curl_errno() function here
See the description of PHP curl_error() function here

When all else fails, i do this (code snippet from my ApiHelper curl wrapper). Use your own logger or evidence printing mechanism. Most every time the answer to the puzzle is in the printed stuff :
// we simply stream curlopt debug info to a temporary
// file, so we can log it out later (when helper is set
// to verbose)
$st = microtime(true);
$verbiage = null;
if ($this->verbose) {
// write out the curl debug stuff
curl_setopt($ch , CURLINFO_HEADER_OUT , false);
curl_setopt($ch , CURLOPT_VERBOSE , true);
$verbiage = fopen('php://temp' , 'w+');
curl_setopt($ch , CURLOPT_STDERR , $verbiage);
}
$resp = curl_exec($ch);
$end = microtime(true); // get as float
$delta = 1000.0 * ($end - $st); // treat as float
$this->roundTripInMs = sprintf("%.2f" , $delta);
$this->getInstanceLogger()->debug("WS call : round trip took " . sprintf("%.2f" , $delta) . " ms.");
if ($this->verbose) {
// rewind and log the verbose output
rewind($verbiage);
$verboseLog = stream_get_contents($verbiage);
$this->getInstanceLogger()->info("Verbose cURL : \n$verboseLog");
fclose($verbiage);
}
$this->curlinfo = curl_getinfo($ch);
$this->verbose && $this->getInstanceLogger()->info("cURL info : \n" . json_encode($this->curlinfo , PEHR_PRETTY_JSON));
$this->httpStatus = curl_getinfo($ch , CURLINFO_RESPONSE_CODE);
if (!$resp) {
if ($this->verbose) $this->getInstanceLogger()->error("CURL request to [$url] failed\n" . json_encode($this->curlinfo , PEHR_PRETTY_JSON));
return 0;
}
curl_close($ch);
if ($resp && $this->verbose) $this->getInstanceLogger()->debug("Received json \n" . $resp);

Related

Fetch all products on shopify store using shopify's API & PHP

Currently i can fetch as per shopify's API limit of 250 products per call and echo this out. I have done some research and found that i need to paginate the request on the overall count of products [5000 products / 250 products per page = 20 pages] in the store.
I want get all products in shopify
so I tried to solved.
but i can not get all products.
the result is always 'error.....'.what is problem?
$pages = ceil($products_cnt->count/250); // Count products / 250
for($i = 0; $i < $pages; $i++){
$api_url = 'https://apikey:password#store.myshopify.com';
$get_url = $api_url . '/admin/products.json?limit=250&page='.($i+1);
$products_content = #file_get_contents( $get_url );
if (!empty($products_all)) {
print_r('ok');
} else {
print_r('error.....');
}
$products_json = json_decode( $products_content, true );
$products = $products_json['products'];
I guess you have a problem with Shopify API rate limit. But to be sure of this need to check the response from the Shopify API. For the HTTP request better to use the curl or some HTTP client package for example the Guzzle.
Try instead of the #file_get_contents($get_url) use this code:
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch,CURLOPT_CONNECTTIMEOUT, 30);
$products_content = curl_exec($ch);
if(curl_errno($ch)){
print_r('Curl error.' . curl_error($ch));
}
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if(in_array($status_code, [200, 201])){
print_r('ok');
} else {
print_r(
'Shopify API error. ' .
'HTTP Code: ' . curl_getinfo($ch, CURLINFO_HTTP_CODE) . '; '
'Error: ' . $products_content
);
}
curl_close($ch);
The Pagination method you are trying to use has been deprecated. Shopify introduced new cursor based pagination from API version 2019-07. To read more about Cursor based pagination, head over to Shopify Docs for Cursor based Pagination. It is better if you use some PHP library that offers rate limiting and other things. However, a sample implementation using cURL would look something like below. Check code comments for details.
<?php
// username and password for API
$username = "";
$password = "";
$nextPage = NULL;
$curl = curl_init();
// set result limit and Basic auth
curl_setopt_array(
$curl,
array(
CURLOPT_URL => "https://store-name.myshopify.com/admin/api/2020-07/products.json?limit=50",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_USERPWD => $username . ":" . $password
)
);
// call back function to parse Headers and get Next Page Link
curl_setopt(
$curl,
CURLOPT_HEADERFUNCTION,
function($curl, $header) use (&$nextPage) {
$len = strlen($header);
$header = explode(':', $header, 2);
if (count($header) < 2) // ignore invalid headers
return $len;
if (trim($header[0]) === "Link" && strpos($header[1], 'next') !== false) {
$links = explode(',', $header[1], 2);
$link = count($links) === 2 ? $links[1] : $links[0];
if (preg_match('/<(.*?)>/', $link, $match) === 1) $nextPage = $match[1];
}
return $len;
}
);
// First request
$response = curl_exec($curl);
if (curl_errno($curl)) {
$error_msg = curl_error($curl);
print_r($error_msg);
}
$parsedResponse = json_decode($response);
$result = $parsedResponse->products;
// generate new requests till next page is available
while ($nextPage !== NULL) {
curl_setopt($curl, CURLOPT_URL, $nextPage);
$parsedResponse->products = [];
$nextPage = NULL;
$response = curl_exec($curl);
$parsedResponse = json_decode($response);
if (curl_errno($curl)) {
$error_msg = curl_error($curl);
} else {
$result = array_merge($result, $parsedResponse->products);
sleep(2);
}
};
echo "Products Count: ";
echo count($result);
curl_close($curl);
Response Headers parsing function by Geoffrey

Generate Public API Key from USAePay Rest API Returning Error

I have been trying to generate a Public API key for my USAePay account to use with a USAePay Client JS iFrame using PHP.
The CURL request keeps returning error code 80: Transaction type not allowed from this source.
I have tried with and without a pin and in both test mode using sandbox.usaepay. . . and in regular mode without the sandbox in the URL (changing the SourceKey in the console appropriately).
Is it possible that there is something wrong with my PHP code, or am I missing some other setting?
$seed = "abcdefghijklmnop";
$apikey = "mysourcekeyfromconsole";
$apipin = "1234";
$prehash = $apikey . $seed . $apipin;
$apihash = "s2/" . $seed . "/" . hash("sha256",$prehash);
$authKey = base64_encode($apikey . ':' . $apihash);
//print("Authorization: Basic " . $authKey);
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://sandbox.usaepay.com/api/v2/publickey",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET"
]);
curl_setopt($curl, CURLOPT_HTTPHEADER, [
"Content-Type: application.json",
"Authorization: Basic " . $authKey
]);
$response = curl_exec($curl);
$error = curl_error($curl);
curl_close($curl);
It seems that this does not work from the sandbox, only from "https://usaepay.com/api/v2/publickey". I don't know why it did not work initially when I tried that, unless I changed some other code or setting. Now, though, when I try to change my code to sandbox, it returns an error, and without sandbox, it returns the public key.

Google Translate API v2, v3 PHP

I just started using the BING translate API to do a small volume of translations into most of their supported languages and that works pretty well.
There is a GitHub project that has simple PHP code for making the API call to Microsoft. You mostly just need the API key, and it can be customized pretty easily.
Text-Translation-API-V3-PHP
// NOTE: Be sure to uncomment the following line in your php.ini file.
// ;extension=php_openssl.dll
// **********************************************
// *** Update or verify the following values. ***
// **********************************************
// Replace the subscriptionKey string value with your valid subscription key.
$key = 'ENTER KEY HERE';
$host = "https://api.cognitive.microsofttranslator.com";
$path = "/translate?api-version=3.0";
// Translate to German and Italian.
$params = "&to=de&to=it";
$text = "Hello, world!";
if (!function_exists('com_create_guid')) {
function com_create_guid() {
return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
mt_rand( 0, 0xffff ),
mt_rand( 0, 0x0fff ) | 0x4000,
mt_rand( 0, 0x3fff ) | 0x8000,
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
);
}
}
function Translate ($host, $path, $key, $params, $content) {
$headers = "Content-type: application/json\r\n" .
"Content-length: " . strlen($content) . "\r\n" .
"Ocp-Apim-Subscription-Key: $key\r\n" .
"X-ClientTraceId: " . com_create_guid() . "\r\n";
// NOTE: Use the key 'http' even if you are making an HTTPS request. See:
// http://php.net/manual/en/function.stream-context-create.php
$options = array (
'http' => array (
'header' => $headers,
'method' => 'POST',
'content' => $content
)
);
$context = stream_context_create ($options);
$result = file_get_contents ($host . $path . $params, false, $context);
return $result;
}
$requestBody = array (
array (
'Text' => $text,
),
);
$content = json_encode($requestBody);
$result = Translate ($host, $path, $key, $params, $content);
// Note: We convert result, which is JSON, to and from an object so we can pretty-print it.
// We want to avoid escaping any Unicode characters that result contains. See:
// http://php.net/manual/en/function.json-encode.php
$json = json_encode(json_decode($result), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
echo $json;
I also have a Google Cloud account and am looking for something similar for a few languages that Google supports that BING does not. For v2, it is not too hard to call Google to make the return the translations.
I found this GitHub project that seems to work for v2 API calls with an API key, but unfortunately I think that is a fee-for-service program now ?
google-cloud-php-translate
That also seems to work pretty well if you have an API key. If you are using v3, they apparently updated the libraries and support. You can make a CURL call from the command line and they have some of that documented on their website, but I am looking for a way to make the call using a PHP file.
require 'vendor/autoload.php';
use Google\Cloud\Translate\TranslateClient;
$translate = new TranslateClient([
'key' => 'APIKEY'
]);
// Translate text from english to french.
$result = $translate->translate('Hello world!', [
'target' => 'fr'
]);
echo $result['text'] . "\n";
// Detect the language of a string.
$result = $translate->detectLanguage('Greetings from Michigan!');
echo $result['languageCode'] . "\n";
// Get the languages supported for translation specifically for your target language.
$languages = $translate->localizedLanguages([
'target' => 'en'
]);
foreach ($languages as $language) {
echo $language['name'] . "\n";
echo $language['code'] . "\n";
}
// Get all languages supported for translation.
$languages = $translate->languages();
foreach ($languages as $language) {
echo $language . "\n";
}
Not sure that is even possible, but the best I can come up with is something like this based upon the command line CURL, but the authentication is wrong and fails. I do have the .json file for my Project/Service credentials. The ${PROJECT_ID} I presume is the project ID for the account, and Bearer $(gcloud auth application-default print-access-token) I am not sure about. There are some instructions about how to obtain that through the CLI, but is there a way to get that via a PHP file ? Like I say, the v2 version works fine.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://translation.googleapis.com/v3beta1/projects/${PROJECT_ID}/locations/global:detectLanguage");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n mimeType: 'text/plain',\n content: 'Omnia Gallia est divisa in tres partes'\n}");
curl_setopt($ch, CURLOPT_POST, 1);
$headers = array();
$headers[] = 'Authorization: Bearer $(gcloud auth application-default print-access-token)';
$headers[] = 'Content-Type: application/json';
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$result = curl_exec($ch);
if (curl_errno($ch)) {
echo 'Error:' . curl_error($ch);
} else {
echo $result;
}
curl_close ($ch);
There might be clues here, but it talks about exporting the path for the credentials file and running PHP scripts from the command line instead of from a server.
Google Cloud Translate API Samples
I have an irrational dislike for using client libraries, so I didn't install the Google PHP library. From what I can tell, the only way to get the authentication to work is to actually go through the whole Oauth2 process. I think the PHP library handles some of that for you, but this code should work as a standalone solution.
First, make sure you've got your Google Cloud Platform account set up, then create a project, then enable the Translation API, after that, create and configure an API key before creating and configuring an OAuth 2.0 client (make sure you enter the correct redirect url). Nothin' to it! ;-)
If you succeed in getting all of that squared away, you should be good to go!
The page effectively redirects the user to the same page they were just on, but includes the results of a GET request in the url. The response contains a code, that can be used to make another GET request in order to retrieve the access token, and once you've got the access token, you can make a POST request to perform the actual translation.
<?php
$clientId = "{your client id}";
$clientSecret = "{your client secret}";
$clientRedirectURL = "{your redirect URL}";
$login_url = 'https://accounts.google.com/o/oauth2/v2/auth?scope=' . urlencode('https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/cloud-translation') . '&redirect_uri=' . urlencode($clientRedirectURL) . '&response_type=code&client_id=' . $clientId . '&access_type=online';
if (!isset($_GET['code'])){
header("location: $login_url");
} else {
$code = filter_var($_GET['code'], FILTER_SANITIZE_STRING);
$curlGet = '?client_id=' . $clientId . '&redirect_uri=' . $clientRedirectURL . '&client_secret=' . $clientSecret . '&code='. $code . '&grant_type=authorization_code';
$url = 'https://www.googleapis.com/oauth2/v4/token' . $curlGet;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
$data = curl_exec($ch);
$data = json_decode($data, true);
curl_close($ch);
$accessToken = $data['access_token'];
$apiKey = "{your api key}";
$projectID = "{your project id}";
$target = "https://translation.googleapis.com/v3/projects/$projectID:translateText?key=$apiKey";
$headers = array(
"Content-Type: application/json; charset=utf-8",
"Authorization: Bearer " . $accessToken,
"x-goog-encode-response-if-executable: base64",
"Accept-language: en-US,en;q=0.9,es;q=0.8"
);
$requestBody = array();
$requestBody['sourceLanguageCode'] = "en";
$requestBody['targetLanguageCode'] = "pt";
$requestBody['contents'] = array("So, I guess this thing works?");
$requestBody['mimeType'] = "text/plain";
$ch = curl_init($target);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($requestBody));
$data = curl_exec($ch);
curl_close($ch);
echo $data;
}
Also, I found this tutorial quite helpful.
This probably won't evaluate:
'Authorization: Bearer $(gcloud auth application-default print-access-token)'
It's rather:
// $cmd = 'gcloud auth application-default login';
$cmd = 'gcloud auth application-default print-access-token';
$token = shell_exec($cmd);
besides it probably should be a service account.
It seems google/cloud replaces google/cloud-translate. For Translate, you could edit the translate-v2.json or add translate-v3beta1.json; but v3beta1 has a whole other REST API than v2 ...

PHP CURL POST method is giving 500 response code

Hi I know that there are similar questions already in there but I have ruled them out.
So I am trying to make a simple POST method with curl and keep getting error 500. Have I missed something ?
// Get cURL resource
$curl = curl_init();
//POST request body
$post_data = array(
'subscription_uuid' => $subscription_uuid,
'merchant' => $merchant_id
);
echo "JSON in POSTFIELDS:" . json_encode($post_data, JSON_PRETTY_PRINT) . "\n";
// Set Headers, endpoint and option to output response as string
curl_setopt_array($curl, array(
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_URL => 'https://subscriptions-jwt.fortumo.io/subscriptions/cancel',
CURLOPT_POST => 1,
CURLOPT_HTTPHEADER => array(
'Content Type: application/json' ,
'Authorization: Bearer' . " " . $jwt
),
CURLOPT_POSTFIELDS => json_encode($post_data)
));
// Send the request & save response
$unsubscribe_response = curl_exec($curl);
$statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
//Request URL:
echo "Unsubscribe Request URL:\n" . curl_getinfo($curl, CURLINFO_EFFECTIVE_URL) . "\n";
echo "Error Code:" . $statusCode . "\n";
And here is the response I get from that code block(simplified this using echo-s):
JSON in POSTFIELDS:{
"subscription_uuid": "<<MY-TOKEN>>",
"merchant": "<<MY-TOKEN>>"
}
Unsubscribe Request URL:
https://subscriptions-jwt.fortumo.io/subscriptions/cancel
Error Code:500
Wierd thing is that using exactly same set of headers, JSON postfields and URL in a tool such as Advanced REST client everything works fine and I get 200 response with no problem.
Something is wrong with my code. Can anyone please spot the issue? Thanks in advance!

PHP client side Digest authentication with qop=auth-int

Does anyone know any PHP library for connecting remote servers that use Digest authentication method with qop=auth-int?
Or, if not, now should I build the A2 for the result? It says in RFC 2617 that I need to use an entity body, but what is this? I am just sending a GET request, it does not have any body at all.
Thanks in advance.
I was looking for the answer to your question too, for requests other than GET.
Perheps you could use something like:
$entityBody = file_get_contents('php://input');
I have not tested if it works, but looking at the answer to this question it would make sence to me that it should.
Note that this stream can only be read once [1] so if you need it again elsewhere you should reuse the $entityBody variable.
From my answer of a similar question:
I implemented the client-side Digest Authentication in PHP with cURL recently. Here's the full code:
<?php
error_reporting(E_ALL);
ini_set( 'display_errors','1');
$url = "https://api.example.com/ws.asmx/do";
$username = "username";
$password = "pwd";
$post_data = array(
'fieldname1' => 'value1',
'fieldname2' => 'value2'
);
$options = array(
CURLOPT_URL => $url,
CURLOPT_HEADER => true,
CURLOPT_VERBOSE => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_SSL_VERIFYPEER => false, // for https
CURLOPT_USERPWD => $username . ":" . $password,
CURLOPT_HTTPAUTH => CURLAUTH_DIGEST,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($post_data)
);
$ch = curl_init();
curl_setopt_array( $ch, $options );
try {
$raw_response = curl_exec( $ch );
// validate CURL status
if(curl_errno($ch))
throw new Exception(curl_error($ch), 500);
// validate HTTP status code (user/password credential issues)
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status_code != 200)
throw new Exception("Response with Status Code [" . $status_code . "].", 500);
} catch(Exception $ex) {
if ($ch != null) curl_close($ch);
throw new Exception($ex);
}
if ($ch != null) curl_close($ch);
echo "raw response: " . $raw_response;
?>
If you're doing a GET request, you shouldn't need auth-int. If your service requires it, you can assume an empty entity-body (thus you can just do md5("") for this part of the A2 hash).

Categories