Using PHP with Google serial notification (IPN) - php

I'm trying to figure out how to get this working with PHP. I have a working IPN for paypal that is less than 20 lines of code to get the data I need. I have tried reading the Google docs but they are either way too specific or way too general. There is some sample code, which is about 1300 lines in 5 files and I can't make sense of it. I just need a handful of vars back from a completed transaction, nothing more. Is it possible to do this with a few lines of code (and I mean without 1300 lines worth of "include" files) or is Google Checkout's process really that bulky?

Here's a bit of code I started. Not yet finished.
It works perfectly.
All you need to do is take the data Google sends back and this code writes to a file and use it to insert into your sales table send notification of payment received to customer and so on.
The trick is that when Google sends you a post you must call back with and Authorization header or it will not take it in consideration.
function post2google($url, $timeout = 30, $port = 80, $buffer = 128) {
$mid = "123456789";
$mky = "qwertyuiop";
$aut = base64_encode($mid . ":" . $mky);
$arr = parse_url($url);
$ssl = "";
if($arr['scheme'] == "https") $ssl = "ssl://";
$post = "POST " . $arr['path'] . " HTTP/1.1\r\n";
$post .= "Host: " . $arr['host'] . "\r\n";
$post .= "Authorization: Basic " . $aut . "\r\n";
$post .= "Content-Type: application/xml; charset=UTF-8\r\n";
$post .= "Accept: application/xml; charset=UTF-8\r\n";
$post .= "Content-Length: " . strlen($arr['query']) . "\r\n";
$post .= "Connection: Close\r\n";
$post .= "\r\n";
$post .= $arr['query'];
$f = fsockopen($ssl . $arr['host'], $port, $errno, $errstr, $timeout);
if(!$f)
return $errstr . " (" . $errno . ")";
else{
fputs($f, $post);
while(!feof($f)) { $echo .= #fgets($f, $buffer); }
fclose($f);
return $echo;
}
}
$re = post2google("https://checkout.google.com/api/checkout/v2/reportsForm/Merchant/123456789?_type=notification-history-request&serial-number=" . $_REQUEST['serial-number'], 3, 443);
$re = str_replace("&", "\n", $re) . "\n\n--\n\n";
file_put_contents("gpn.txt", $re, FILE_APPEND);

I've gotten it to work, and here's the skeleton of my code that can be used to handle HTTP notifications/responses. This was obviously derived from tntu's example above. (Thanks!)
//incoming data is in the var $_POST['serial-number']
//"send" the response to acknowledge the serial number that google talks about all over but never explains how
echo "_type=notification-acknowledgment&serial-number=".$_POST['serial-number'];
//now we need to call google's server and ask for this transaction's data:
//you'll need to change your merchant id in the $url and $mid vars, and your merchant key in the $mky var
$url = "https://sandbox.google.com/checkout/api/checkout/v2/reportsForm/Merchant/1234567890?_type=notification-history-request&serial-number=" . $_REQUEST['serial-number'];
$mid = "1234567890";
$mky = "ABCDEFGHIJK";
$aut = base64_encode($mid . ":" . $mky);
$arr = parse_url($url);
$ssl = "";
if($arr['scheme'] == "https") $ssl = "ssl://";
$post = "POST " . $arr['path'] . " HTTP/1.1\r\n";
$post .= "Host: " . $arr['host'] . "\r\n";
$post .= "Authorization: Basic " . $aut . "\r\n";
$post .= "Content-Type: application/xml; charset=UTF-8\r\n";
$post .= "Accept: application/xml; charset=UTF-8\r\n";
$post .= "Content-Length: " . strlen($arr['query']) . "\r\n";
$post .= "Connection: Close\r\n";
$post .= "\r\n";
$post .= $arr['query'];
//now we actually make the request by opening a socket and calling Google's server
$f = fsockopen($ssl . $arr['host'], 443, $errno, $errstr, 30);
if(!$f){
//something failed in the opening of the socket, we didn't contact google at all, you can do whatever you want here such as emailing yourself about it and what you were trying to send, etc
#mail("troubleshooting#yourdomain.com","Google IPN - HTTP ERROR ",$errstr . " (" . $errno . ")\n\n\n".$arr['query']);
}else{
//the socket was opened, send the request for the order data:
fputs($f, $post); // you're sending
while(!feof($f)) { $response .= #fgets($f, 128); } //google replies and you store it in $response
fclose($f); //close the socket, we're done talking to google's server
$spl=strpos($response,"_type="); //parse the type because parse_str won't catch it
if ($spl!==false){
$spl2=strpos($response,"&",$spl);
$ordertype=substr($response,($spl+6),($spl2-$spl)-6);
}//$ordertype will tell you which type of notification is being sent, new-order-notification, risk-information-notification, etc
$subresponse=substr($response,$spl2+1); //put the rest of it into an array for easy access
parse_str($subresponse,$order);//you can now access google's response in $order[] vars
//IMPORTANT: dots in Google's field names are replaced by underscore, for example:
// $order['google-order-number'] and $order['buyer-billing-address_address1'] NOT $order['buyer-billing-address.address1']
//order field names are shown here:
//https://developers.google.com/checkout/developer/Google_Checkout_HTML_API_Notification_API#order_summary
//this is the point where you will want to use the data contained in $order[] to create a new record in your database or whatever.
//NOTE: be sure to store and check for duplicates using the google-order-number because you will get multiple notifications from google regarding the same order
if (strtoupper($order['order-summary_financial-order-state']) == "CHARGEABLE"){
//CHARGEABLE is what indicates it is safe to create a login for the user (if you are delivering digital goods)
// insert into db, and/or email user with key or download url
}
}

Related

Getting an invalid response from the ConnectWise API when using multipart/form-data POST request

I am getting an error when using the ConnectWise API to upload a document to an existing ticket, and I believe the issue is with how I performing the curl request.
I have tried messing around with the curl options based on what I've found on Google, but nothing seems to work.
Here is the POST request
function post_ticket_attachment ( $ticket_id, $file_attachment ) {
$url = "https://api-na.myconnectwise.net/v4_6_release/apis/3.0/system/documents";
$filename = $file_attachment['name'];
$filedata = $file_attachment['tmp_name'];
$filesize = $file_attachment['size'];
$fields = array (
"recordId" => $ticket_id,
"recordType" => "Ticket",
"title" => $file_attachment['name']
);
$filenames = array($filedata);
$files = array();
foreach( $filenames as $f ){
$files[$f] = file_get_contents($f);
}
$ch = curl_init();
$boundary = uniqid();
$delimiter = '-------------' . $boundary;
$post_data = build_data_files($boundary, $fields, $files);
curl_setopt_array($ch, array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => $post_data,
CURLOPT_HTTPHEADER => array(
"Authorization: basic " . $connect_wise_auth,
"Content-Type: multipart/form-data; boundary=" . $delimiter,
"Content-Length: " . strlen($post_data)
),
));
$response = curl_exec($ch);
console_log ($response);
}
And here is how I'm building the post data:
function build_data_files($boundary, $fields, $files){
$data = '';
$eol = "\r\n";
$delimiter = '-------------' . $boundary;
foreach ($fields as $name => $content) {
$data .= "--" . $delimiter . $eol
. 'Content-Disposition: form-data; name="' . $name . "\"".$eol.$eol
. $content . $eol;
}
foreach ($files as $name => $content) {
$data .= "--" . $delimiter . $eol
. 'Content-Disposition: form-data; name="' . $name . '"; filename="' . $name . '"' . $eol
. 'Content-Transfer-Encoding: binary'.$eol
;
$data .= $eol;
$data .= $content . $eol;
}
$data .= "--" . $delimiter . "--".$eol;
return $data;
}
I'm expecting the request to post correctly, but instead I'm getting a response from ConnectWise stating, "Cannot route. Codebase/company is invalid."
I just had this problem and was because I forget to add the username and password to the basic authorization. Looks like that you are adding it but I can't see the what is containing so, just to clarify, the username is the company+publickey and the password is the private key, while encoded in base64.
From the official documentation (you may be to login in to be able to see it)
It is recommended to create API Members versus using API Keys tied to
a specific member. Authorization: Basic
base64(companyid+publickey:privatekey) (Authorization: Basic
Y29tcGFueWlkK3B1YmxpY2tleTpwcml2YXRla2V5)
Once you add that information properly the API will be able to check that your user is correct and you belong to that company.
Please note that this is a very specific problem with a very specific API, so probably if you find more problems in the future you will have more luck asking directly on their forums.
Sorry not a full answer (I cannot comment yet)
I was getting this same error when trying to lookup a company by its identifier. It turned out that I was not using the correct URI.
try using fiddler or something to examine exactly what you are posting to see if there is something not 100% right.
My issue was I had a forward slash here: company/companies/?conditions
It should have been company/companies?conditions
I didn't notice at first as I was using variables to build the URI so it was not very clear.
I also find that using this https://github.com/christaylorcodes/ConnectWiseManageAPI is helpful as I can see how the request should be formatted (by watching with fiddler when I post). Then you can make sure your post is behaving the same.

Why does fsockget timeout in this example when using single quotes for some of the text?

I'm testing Salesforce's WebToLead form using this simple example http://wiki.developerforce.com/index.php/Simple_Web2Lead_Implementation.
When I changed the double quotes (used to concatenate the $header values) into single quotes, I keep getting the following error:
Fatal error: Maximum execution time of 60 seconds exceeded in C:\wamp\www\test.php on line 23
When I change them back to double quotes, everything works fine. What am I missing?
Here is a simplified version you can use if you have a Salesforce developer account:
<?php
//do quality checks on the incoming data here.
//then bundle the request and send it to Salesforce.com
$req = "&lead_source=". urlencode("test");
$req .= "&first_name=" . urlencode("first name test");
$req .= "&debug=" . urlencode("1");
$req .= "&oid=" . urlencode("<your oid>");
$req .= "&retURL=" . "";
$req .= "&debugEmail=" . urlencode("<your email>");
$header = 'POST /servlet/servlet.WebToLead?encoding=UTF-8 HTTP/1.0\r\n';
$header .= 'Content-Type: application/x-www-form-urlencoded\r\n';
$header .= 'Host: www.salesforce.com\r\n';
$header .= 'Content-Length: ' . strlen($req) . '\r\n\r\n';
$fp = fsockopen ('www.salesforce.com', 80, $errno, $errstr, 30);
if (!$fp) {
echo "No connection made";
} else {
fputs ($fp, $header . $req);
while (!feof($fp)) {
$res = fgets ($fp, 1024); // error is thrown here
echo $res;
}
}
fclose($fp);
?>
Well, '\r\n\r\n' won't be evaluated into the actual characters, while "\r\n\r\n" will.
Since they aren't being evaluated properly, the header will be improperly formed, which would explain your timeout.

Paypal IPN cannot get verified

I am trying to implement Paypal IPN but it never reaches the url I've set. I've written a script to log visits to this url and all I get are my visits.
How long does it take for Paypal to sent the notification?
EDIT
IPNs suddenly started to come but now I can't verify...Here is the code:
$url = 'https://www.paypal.com/cgi-bin/webscr';
$postdata = '';
foreach ($_POST as $i => $v) {
$postdata .= $i . '=' . urlencode($v) . '&';
}
$postdata .= 'cmd=_notify-validate';
$web = parse_url($url);
if ($web['scheme'] == 'https') {
$web['port'] = 443;
$ssl = 'ssl://';
} else {
$web['port'] = 80;
$ssl = '';
}
$fp = #fsockopen($ssl . $web['host'], $web['port'], $errnum, $errstr, 30);
if (!$fp) {
echo $errnum . ': ' . $errstr;
} else {
fputs($fp, "POST " . $web['path'] . " HTTP/1.1\r\n");
fputs($fp, "Host: " . $web['host'] . "\r\n");
fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n");
fputs($fp, "Content-length: " . strlen($postdata) . "\r\n");
fputs($fp, "Connection: close\r\n\r\n");
fputs($fp, $postdata . "\r\n\r\n");
while (!feof($fp)) {
$info[] = #fgets($fp, 1024);
}
fclose($fp);
$info = implode(',', $info);
if (eregi('VERIFIED', $info)) {
} else {
}
}
I already commented above. But I'm pretty sure the html encoded & is messing up your callback.
There's big difference between URL encoding and HTML encoding.
Change this '&' to this '&'. & is a url/post character used to separate different sets of key/value pairs. By changing it to &, you made your whole callback a single value.
Also, just some advice, but I would ditch this
if (eregi('VERIFIED', $info)) {} else {}
and replace it with this
if (preg_match('/VERIFIED/', $info)) {} else {}
eregi is depreciated.
http://php.net/manual/en/function.eregi.php

fsockopen soap request

I am trying to send a SOAP message to a service using php.
I want to do it with fsockopen, here's is the code :
<?php
$fp = #fsockopen("ssl://xmlpropp.worldspan.com", 443, $errno, $errstr);
if (!is_resource($fp)) {
die('fsockopen call failed with error number ' . $errno . '.' . $errstr);
}
$soap_out = "POST /xmlts HTTP/1.1\r\n";
$soap_out .= "Host: 212.127.18.11:8800\r\n";
//$soap_out .= "User-Agent: MySOAPisOKGuys \r\n";
$soap_out .= "Content-Type: text/xml; charset='utf-8'\r\n";
$soap_out .= "Content-Length: 999\r\n\r\n";
$soap_put .= "Connection: close\r\n";
$soap_out .= "SOAPAction:\r\n";
$soap_out .= '
Worldspan
This is a test
';
if(!fputs($fp, $soap_out, strlen($soap_out)))
echo "could not write";
echo "<xmp>".$soap_out."</xmp>";
echo "--------------------<br>";
while (!feof($fp))
{
$soap_in .= fgets($fp, 100);
}
echo "<xmp>$soap_in</xmp>";
fclose($fp);
echo "ok";
the above code just hangs . if i remove the while it types ok, so i suppose the problem is at $soap_in .= fgets($fp, 100)
Any ideas of what is happening
Its not just a matter of opening a socket then writing 'POST....' to it - you need a full HTTP stack to parse the possible responses (e.g. what different encodings? Partial Content?). Use cURL.
The reason its currently failing is probably because the remote system is configured to use keepalives - which would again be solved by using a proper HTTP stack.
C.
I recommend, use curl for soap actions. http://www.zimbra.com/forums/developers/9890-solved-simple-soap-admin-example-php.html#post52586

How to use twitter api with "http basic auth"?

How to use twitter api with "http basic auth"?
I think I should use the "consumer key"! because twitter gave you limit rate of requests per hour, how can they count my requests if I didn't use my consumer key?
Whenever you want to use HTTP basic auth with anything, if you want to ignore the actual implementation and HTTP headers, just use cURL. Here's a simple example in PHP, cURL is available in other languages too:
<?php
$ch = curl_init();
// Sets the URL cURL will open
curl_setopt($ch, CURLOPT_URL, 'http://twitter.com/statuses/user_timeline.xml?screen_name=al3x');
// Here's the HTTP auth
// The 3rd argument is your Twitter username and password joined with a colon
curl_setopt($ch, CURLOPT_USERPWD, 'username:password');
// Makes curl_exec() return server response
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Lately the Twitter API expects an Expect header. It's a mystery
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
// And here's the result XML
$twitter_xml = curl_exec($ch);
curl_close($ch);
?>
And then $twitter_xml will contain the XML of al3x's public timeline. As far as rate limiting goes, ceejayoz already answered that pretty well.
Authenticated API GET requests are counted against your user account's tally.
Unauthenticated API GET requests (permitted by some methods) are counted against your IP address's tally.
POST requests are not rate limited.
More details are available in the Twitter docs.
I would think that they count the requests from the same IP - but I haven't double checked.
Even dynamic IP address will be static for a session and, in the absence of any other identifying information, the only thing that will distinguish you from other users.
As other posters have said - there are better methods if you've got a key.
$twitter = file_get_content("http://user:password#twitter.com/blabla");
more about native HTTP Wrapper support in PHP
I recently wrote some PHP to post to Twitter
This is the working part of it:
$message = 'A new revision (#' . $data['revision'] . ') was commited by ' . $data['author'] . ': ' . $data['message'] . "";
$message = substr($message, 0, 140);
$content = 'status=' . urlencode($message);
$packetString = "POST /statuses/update.xml HTTP/1.1\r\n";
$packetString .= "Authorization: Basic " . base64_encode($username . ":" . $password) . "\r\n";
$packetString .= "Content-Length:" . strlen($content) . "\r\n";
$packetString .= "HOST: twitter.com\r\n";
$packetString .= "\r\n" . $content . "\r\n";
$sock = fsockopen('twitter.com', 80);
fwrite($sock, $packetString);
//This is some logging, to a local file so I can monitor local what's going on
$response = fread($sock, 10240);
fwrite($fh, $packetString . "\r\n\r\n\r\n" . trim($response) . "\r\n\r\n\r\nD:\r\n" . $d);
fclose($fh);
You can see it in action here: http://twitter.com/fmsvn using a callback from our SVN server I am posting the SVN messages to the projects Twitter Feed.

Categories