Sending HTTP request using Guzzle - php

I am trying to send a post request using Guzzle 6 http client. I am sending two requests one with content type as application/x-www-form-urlencoded (form_params in Guzzle) and the other as application/json (json in Guzzle).
I initialise the client as below (forms_params and json respectively):
$data1 = array("c1" => "a", "c2" => null)
$client = new Client();
$response = $client->post(
"http://localhost/callback",
array(
"form_params" => $data1, // send as x-www-form-urlencoded
)
);
$data2 = array("c1" => "a", "c2" => null)
$client = new Client();
$response = $client->post(
"http://localhost/callback",
array(
"json" => $data2, // send as json
)
);
The response that I receive does have not identical data/body:
Output for form_params : Data -> {"c1":"a"}
Output for json : Data -> {"c1":"a","c2":null}
I am not understanding why it does not send identical data for above requests. Could this be a bug in Guzzle? Is there any way to solve this (apart from removing nulls before sending request)?
UPDATE : As requested endpoint code (both requests are read using same code)
if ($$_SERVER["CONTENT_TYPE"] == "application/json") {
$jsonstr = file_get_contents("php://input");
$formData = json_decode($jsonstr, true);
} else {
$formData = $_POST;
}
echo "Data -> " . json_encode($formData);
UPDATE 2 : I went through the links provided in comments about this being expected behaviour in Guzzle.
But why I asked this question in first place is because I faced an issue of signature mismatch.
When I send the request, I add a header with a signature which is nothing but hash_hmac("sha256", json_encode($data), "secret_key"). So I get different signatures when sending data as json and form_params (since the data received is different in case of form_params as null values are discarded/not sent). First, I thought it might be because of a bug in Guzzle but it isn't.
Is there anyway to solve this signature issue?

As Jon Stirling and Lawrence Cherone noticed already, it's not a bug according to Guzzle's authors.
So the solution for you is to cast values to a string for form_params. It makes sense, because URL encoded format (unlike JSON) doesn't have types (all is a string). And you everyone defines own conversion rules. In PHP (using http_build_query) it works like this, skipping nulls at all.

Related

Response to json Payload in POST API not in Json Format

I have a Rest API that accepts a Json Payload, i format the Data and import this into the SQL Database. However when the payload is received the sender is waiting for a response.
They do not want a Typical HTTP 200 OK, they want a formatted Json Response with the Price etc for the submission.
Whenever i output the response to the body, it does not appear to be formatted as a Json and appears as a string, this is causing errors with the flow of the program.
i wanted to check the formatting is correct and the method.
ignoring all the handler and authorization i am typically picking up the payload via:
$payload = (file_get_contents('php://input'));
do what i need to do with the data
then respond as follow:
(of course this is wrapped in a statement of handlers but the successful segment of the code)
http_response_code(200);
$data = array(
"sub_total" => 1000,
"ExtraeChargeTotal" => 0,
"Vat" => 0,
"Total" => 1000
);
$response = json_encode($data);
echo $response ;
is this the correct way to respond? everything works how i think it should, an appears to work in postman.
however the response is a string and not in Json
To resume the comments, when json datas must been sent with PHP, it's important to force the Content-Type header by this way :
header('Content-Type: application/json; charset=utf-8');
After the header, send the datas with echo.
For exemple in this case :
http_response_code(200);
$data = array(
"sub_total" => 1000,
"ExtraeChargeTotal" => 0,
"Vat" => 0,
"Total" => 1000
);
if($response = json_encode($data)){
header('Content-Type: application/json; charset=utf-8');
echo $response ;
}

Encoding PHP POST response's JSON body into HMAC SHA256, and then into Base64

How to receive raw JSON response from HTTP POST webhook?
I am working with an API and to verify that the POST to the webhook is indeed from the appropriate company API, they suggest this method:
To allow a client to verify a webhook message has in fact come from SIGNIFYD, an X-SIGNIFYD-SEC-HMAC-SHA256 header is included in each webhook POST message. The contents of this header is the Base64 encoded output of the HMAC SHA256 encoding of the JSON body of the message, using the team's API key as the encryption key. To verify the authenticity of the webhook message, you should calculate this value yourself and verify it equals the value contained in the header.
For the test environment, the "secret" key is ABCDE instead of the "Team API key."
I am receiving it in PHP like so:
<?php
// Get relevant Signifyd custom headers to be used for verification
$header_sig_topic = $_SERVER['HTTP_X_SIGNIFYD_TOPIC'];
$header_sig_sec_hmac = $_SERVER['HTTP_X_SIGNIFYD_SEC_HMAC_SHA256'];
// Get POST body
$webhookContent = "";
$webhook = fopen('php://input' , 'r');
while (!feof($webhook)) {
$webhookContent .= fread($webhook, 4096);
}
fclose($webhook);
?>
then I am processing it into the hash like so:
<?php
$sig_ver_sha = hash_hmac('sha256', $webhookContent, $secret);
$sig_ver_hash = base64_encode( $sig_ver_sha );
?>
However, I am going wrong somewhere, because the hash I calculate is
OTc1YzExZDY2ZTE1MTVmYmJmNWNhNDRhNWMxZGIzZDk0NmM3OGE4NDU2N2JkYTJmZDJlYWI0ODRhNjlhNTdiYg==
while the header for an identical sample response header always comes with
W+D70ded8u5DG7P4BcG0u2etvAqQZvxz70Q4OXh0vlY=
I thought I was getting the JSOn body wrong somehow so I've tried every combination of json_encode and json_decode but nothing helps, my hash never matches.
I've also tried using $webhookContent = json_decode(file_get_contents('php://input'), true); to store the POST body but that just comes up empty ($_POST doesn't work either).
Am I doing something else wrong other than receiving the JSON?
The JSON that comes as the body of the test response which always comes with W+D70ded8u5DG7P4BcG0u2etvAqQZvxz70Q4OXh0vlY= as the hash key to be used for verification:
{ "analysisUrl": "https://signifyd.com/v2/cases/1/analysis",
"entriesUrl": "https://signifyd.com/v2/cases/1/entries", "notesUrl":
"https://signifyd.com/v2/cases/1/notes", "orderUrl":
"https://signifyd.com/v2/cases/1/order", "guaranteeEligible":false,
"status":"DISMISSED", "uuid":"709b9107-eda0-4cdd-bdac-a82f51a8a3f3",
"headline":"John Smith", "reviewDisposition":null, "associatedTeam":{
"teamName":"anyTeam", "teamId":26, "getAutoDismiss":true,
"getTeamDismissalDays":2 }, "orderId":"19418",
"orderDate":"2013-06-17T06:20:47-0700", "orderAmount":365.99,
"createdAt":"2013-11-05T14:23:26-0800",
"updatedAt":"2013-11-05T14:23:26-0800",
"adjustedScore":262.6666666666667, "investigationId":1,
"score":262.6666666666667, "caseId":1,
"guaranteeDisposition":"APPROVED"}
If it helps to see where I'm going wrong, an example is provided but it's in Python:
Mac sha256HMAC = javax.crypto.Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(teamAPIKEY.getBytes(), "HmacSHA256");
sha256HMAC.init(secretKey);
String encodedHMAC256 = Base64.encodeBase64String(sha256HMAC.doFinal(jsonBody.getBytes("UTF-8")));
My error was in simply not specifying the $raw_output parameter of the hash_hmac() function as true.
raw_output
When set to TRUE, outputs raw binary data. FALSE outputs lowercase hexits.
So, since I wasn't specifying $raw_output as true, I was getting hexits instead of raw binary output, which looked like this: 975c11d66e1515fbbf5ca44a5c1db3d946c78a84567bda2fd2eab484a69a57bb.
EDIT: The answer here is
<?php
$sig_ver_sha = hash_hmac('sha256', $webhookContent, $secret, true);
$sig_ver_hash = base64_encode( $sig_ver_sha );
?>

Rails send four backslashes using Net::HTTP

My rails application need to send some data to a php application, which expects a POST call.
I use the folowing code:
uri = URI.parse(apiUrl)
req = Net::HTTP::Post.new(uri.to_s, initheader = {'Content-Type' =>'application/json'})
req.basic_auth(api_key, token)
req.set_form_data({"action" => action, "data" => data})
http = Net::HTTP.new(uri.host, uri.port)
response = http.request(req)
Where data is a hash converted to json:
data = {
:key1 => val1,
:key2 => val2
}.to_json
(it is a nested hash, i.e. some values are hash as well)
My problem is that the php application receives 4 backslashes before each quotation mark:
$data_json = $_POST['data'];
error_log($data_json);
and in error log I see:
'{\\\\"key1\\\\":val1,\\\\"key2\\\\":\\\\"val2\\\\"}'
Looks like rails add one of them, but even if I remove it and replace it with the following code:
a.gsub!(/\"/, '\'')
I still get many backslashes inside the php application, hence cannot convert the string to array.
Any idea??
By using set_form_data net/http is POSTing the form as urlencoded. Its NOT posting your request body as pure JSON.
If you want to POST raw JSON you will need to follow a pattern like:
uri = URI('https://myapp.com/api/v1/resource')
req = Net::HTTP::Post.new(uri, initheader = {'Content-Type' =>'application/json'})
req.body = {param1: 'some value', param2: 'some other value'}.to_json
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
http.request(req)
end

Problems manipulating JSON data in PHP

I'm not that handy with JSON so here goes. I'm receiving Amazon SNS notifications for bouncing email addresses to a listener (in PHP 5.5) which does:
$post = #file_get_contents("php://input");
$object = json_decode($post, true);
This gives me:
Type => Notification
MessageId => #####
TopicArn => #####
Message => {
"notificationType":"Bounce",
"bounce": {
"bounceSubType":"General",
"bounceType":"Permanent",
"bouncedRecipients":[{"status":"5.3.0","action":"failed","diagnosticCode":"smtp; 554 delivery error: dd This user doesn't have a yahoo.com account (testuser#yahoo.com) [0] - mta1217.mail.bf1.yahoo.com","emailAddress":"testuser#yahoo.com"}],
"reportingMTA":"dsn; ######",
"timestamp":"2014-10-27T16:37:42.136Z",
"feedbackId":"######"
},
"mail": {
"timestamp":"2014-10-27T16:37:40.000Z",
"source":"myemail#mydomain.com",
"messageId":"######",
"destination":["testuser#yahoo.com"]
}
}
I was expecting an associative array all the way down but instead it's an array only at the top level and with JSON strings inside. I've tried everything I can think of, including json_decoding further parts of the array, but I'm struggling to access the data in a simple way. What I need is the "destination" email address which should be in $object['Message']['mail']['destination'][0].
Can anyone point out what I'm doing wrong here? Thanks.
It looks like $object['Message'] is also json encoded. Perhaps because it's using some generic container format for service call results. Try this
$post = #file_get_contents("php://input");
$object = json_decode($post, true);
//Message contains a json string
$object['Message'] = json_decode($object['Message'], true);
//Then access the structure using array notation
echo $object['Message']['mail']['destination'][0];

Trying to understand how to format this PHP POST to a Curl POST format

I am connecting with an API via curl and POSTing to it.
The API support simply says that I should form my request as so:
"If you send a POST request to the correct URL with a content-type of JSON and a body like what's below you should be off to a good start:
{
"contact": {
"email": "justin#myapi.com",
"last_name": "Johnston",
"company": "MyApi",
"first_name": "Justin"
}
}
However, I think this is for a regular PHP post maybe and not for CURL which is what I am using.
So, the curl code I have is:
//Set Curl options
curl_setopt ( $ch, CURLOPT_URL, $url );
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "first_name=" . $db->f('fname') . "&last_name=" . $db->f('lname') . "&email=" . $db->f('Email') . "&company=" . $db->f('studio_name') . "");
However, when I run the POST, the response I get back from the API says:
{"error":"Invalid parameters. Should be a hash in the form { 'contact' : { } }."}
So, I am assuming that is because my POSTFIELDS are not correct, and I understand I need to lead with "Contact", but I am not sure how to format it and haven't been able to find an example. Any idears?
Thanks very much for any help you could give! I really do appreciate it!!
Craig
To expand on the comment I posted...
I think the problem is you're sending a string as the parameter, where
it should be a JSON object. just use json_encode() to encode an
array of data and send that via POST instead of the string you're
forming
You need to create a JSON string (usually by encoding an array of data):
$data = array(
"contact" => array(
"first_name" => $db->f('fname'),
"last_name" => $db->f('lname'),
"email" => $db->f('Email'),
"company" => $db->f('studio_name')
)
);
$data = json_encode($data);
This will give you the JSON you need to post, so just change your line of code setting the parameters to this:
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
Edit: As requested in a comment, I'll attempt to explain a little more:
The POST request is sent to the server, and you can send any data you want with the request. In the example given in the question, this is valid JSON and they have specified that the body of the request should also be JSON (I'm assuming in the same format):
{
"contact": {
"email": "justin#myapi.com",
"last_name": "Johnston",
"company": "MyApi",
"first_name": "Justin"
}
}
In my solution above I've explained how to get a string containing valid JSON for an array of data; this string can be sent as the contents of the request.
The problem with what you were trying is that the whole data would have literally been sent like this:
first_name=Justin&last_name=Johnston&email=justin#myapi.com&company=MyApi
It's understandable to be confused, as is this is how you would get the data returned from an HTML form, and is quite a common way of sending variables via POST. However in this case you need to use JSON, which is why that won't work.

Categories