PDA

Pogčedajte punu verziju : PayPal, IPN i verifikacija


Ilija Studen
13. 11. 2005., 00:07
OK, manji problem.

Dobijem "obaveštenje" od PayPala (IPN) da je transakcija "legla" i sad treba da je verifikujem. Imam funkciju koji šalje verifikacioni zahtev i procesira odgovor ali ona definitivno ne radi kako treba pošto kao odgovor dobijem PayPal homepage (!).

Šta vi koristite? I kako?

bluesman
13. 11. 2005., 00:27
Možda da okačiš tu funkciju da vidimo, možda se dešava 404 pa 302 (redirect) - probaj da proveriš header koji se vraća? Meni liči na to.

Nisam koristio PayPal jako dugo, više od 5 godina, ne znam kako sada ide verifikacija, ali koliko se sećam sve je moglo da se strpa u 30-ak redova koda.

Ilija Studen
13. 11. 2005., 00:44
Samu funkciju sam negde "pokupio" i malo prilagodio. Klasa i test primer odakle je ovo izvučeno je prikačeno na poruku. Probao sam da vidim nešto gotovo pošto nisam imao vremena sam da se cimam (par sati proučavanja dokumentacije i eksperimentisanja), ali mi se čini da će na kraju ipak biti tako... Korisnik se redirektuje na Success stranicu, ali verifikacija ne prođe kako treba.

Pri kraju je dodato logovanje odgovora u fajl, a $this->finish_order() menja stanje porudžbine i čuva izmene u bazu podataka.

/**
* OK, verify payment
*
* @access public
* @param void
* @return null
*/
function paypal_notify() {

// Prepare
$url_parsed = parse_url('https://www.paypal.com/cgi-bin/webscr');
$ipn_data = array();
$ipn_response = '';
$post_string = '';

// Generate the post string from the $_POST vars aswell as load the
// $_POST vars into an arry so we can play with them from the calling
// script.
foreach ($_POST as $field => $value) {
$ipn_data[$field] = $value;
$post_string .= $field.'='.urlencode($value).'&';
} // foreach

// Add to post string...
$post_string .= 'cmd=_notify-validate'; // append ipn command

// Init..
$err_no = null;
$err_str = null;

// open the connection to paypal
$fp = fsockopen($url_parsed['host'], '80', $err_no, $err_str, 30);

// FSock?
if(!$fp) {
die();
} else {

// Post the data back to paypal
fputs($fp, "POST" . $url_parsed['path'] . "HTTP/1.1\r\n");
fputs($fp, "Host: " . $url_parsed['host'] . "\r\n");
fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n");
fputs($fp, "Content-length: " . strlen($post_string) . "\r\n");
fputs($fp, "Connection: close\r\n\r\n");
fputs($fp, $post_string . "\r\n\r\n");

// loop through the response from the server and append to variable
while(!feof($fp)) $ipn_response .= fgets($fp, 1024);
fclose($fp); // close connection

} // if

// Prepare for file and write it to the file...
$for_file = "Post string:\r\n\r\n$post_string\r\n\r\nResponse:\r\n\r \n$ipn_response";
@write_in_file( dirname(__FILE__) . '/lasttransaction.txt', $for_file );

// Vefied?
if (eregi("VERIFIED", $ipn_response)) {
$this->finish_order(false);
} // if

} // paypal_notify

bluesman
13. 11. 2005., 01:06
Evo šta meni pada na pamet:

fputs($fp, "Host: " . $url_parsed['host'] . "\r\n");

Ne šalješ port, možda mora port da se pošalje.

fputs($fp, "Host: " . $url_parsed['host'] . ":80\r\n");

Probao si konekciju sa portom 80.

$fp = fsockopen($url_parsed['host'], '80', $err_no, $err_str, 30);

jer je default, međutim i tu možda imaš grešku, pošto traži integer a ne string (ja sam imao problem sa tim kada sam portove čitao iz baze pa sam morao uvek intval($port) )

znači probaj sa:
$fp = fsockopen($url_parsed['host'], 80, $err_no, $err_str, 30);

Dalje, proveri da li je stvarno port 80.

Moguće je da te zbog jednog od ova 2 baca na index.

Ilija Studen
13. 11. 2005., 01:18
Opet baca na index.

Hvala u svakom slučaju. Pogledaću šta dokumentacija ima da kaže na ovu temu pa javljam ako nađem nešto pametno :D

bluesman
13. 11. 2005., 01:34
Nekada nije islo preko https, vidim da sada ide tako...

Probaj ovaj kod, sad sam to nasao na disku ...



error_reporting(E_ALL & ~E_NOTICE);

define('NO_REGISTER_GLOBALS', 1);
define('SESSION_BYPASS', 1);

$phrasegroups = array();
$specialtemplates = array();

chdir('./../');
require_once('./includes/init.php');
require_once('./includes/functions.php');
require_once('./includes/adminfunctions.php');
require_once('./includes/functions_subscriptions.php');

if (empty($_POST))
{
exit;
}

$query[] = 'cmd=_notify-validate';
foreach ($_POST as $key => $val)
{
$query[] = $key . '=' . urlencode ($val);
}
$query = implode('&', $query);

$used_curl = false;

if (function_exists('curl_init') AND $ch = curl_init())
{
curl_setopt($ch, CURLOPT_URL, 'http://www.paypal.com/cgi-bin/webscr');
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDSIZE, 0);
curl_setopt($ch, CURLOPT_POSTFIELDS, $query);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$result = curl_exec($ch);
curl_close($ch);
if ($result !== false)
{
$used_curl = true;
}
}
if (!$used_curl)
{

$header = "POST /cgi-bin/webscr HTTP/1.0\r\n";
$header .= "Host: www.paypal.com\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($query) . "\r\n\r\n";
$fp = fsockopen('www.paypal.com', 80, $errno, $errstr, 30);
socket_set_timeout($fp, 30);
fwrite($fp, $header . $query);
while (!feof($fp))
{
$result = fgets($fp, 1024);
if (strcmp($result, 'VERIFIED') == 0)
{
break;
}
}
fclose($fp);
}

if (($result == 'VERIFIED' OR strcmp($result, 'VERIFIED') == 0) AND !empty($_POST['item_number']) AND strtolower($_POST['business']) == strtolower($vboptions['ppemail']))
{

$item_number = explode('_', $_POST['item_number']);
$subscriptionid = intval($item_number[0]);

if (empty($item_number[1]))
{ // non vBulletin subscription
exit;
}

$userid = $DB_site->query_first("SELECT userid FROM " . TABLE_PREFIX . "user WHERE userid = " . intval($item_number[1]));

// lets check the values
if ($subscriptionid AND $userid['userid'])
{
//its a paypal payment and we have some valid ids
$sub = $DB_site->query_first("SELECT * FROM " . TABLE_PREFIX . "subscription WHERE subscriptionid = $subscriptionid");
$cost = unserialize($sub['cost']);
if ($_POST['tax'] > 0)
{
$_POST['mc_gross'] += $_POST['tax'];
}

// Check if its a payment or if its a reversal
if ($_POST['txn_type'] == 'web_accept' AND $_POST['payment_status'] == 'Completed')
{
if ($_POST['mc_gross'] == $cost[strtolower($_POST['mc_currency'])])
{
build_user_subscription($subscriptionid, $userid['userid']);
}
}
else if ($_POST['payment_status'] == 'Reversed' OR $_POST['payment_status'] == 'Refunded')
{
delete_user_subscription($subscriptionid, $userid['userid']);
}
}

// Paypal likes to get told its message has been received
if (SAPI_NAME == 'cgi' OR SAPI_NAME == 'cgi-fcgi')
{
header('Status: 200 OK');
}
else
{
header('HTTP/1.1 200 OK');
}
}

bluesman
13. 11. 2005., 01:41
Proveri da li ti vraca: HTTP/1.1 200 OK

Ako da, onda je neka frka sa parametrima
Ako ne, vidi da nije 302, to je redirekcija...

Tako ces bar znati da li saljes na pravo mesto....

Ilija Studen
13. 11. 2005., 02:32
Rešeno, hvala Blues!

Najveći problem nije bila greška u kodu ili nešto slično, već ljudska greška. Naime IPN je po defaultu isključen (stupid) što meni nije rečeno. Zato je svaki moj zahtev bio usluživan sa index stranicom.

Kod funkcije je:

/**
* OK, verify payment
*
* @access public
* @param void
* @return null
*/
function paypal_notify() {

// Prepare query
$query = array('cmd=_notify-validate');

// Fill it with POST vars
foreach ($_POST as $key => $val) {
$query[] = $key . '=' . urlencode($val);
} // foreach

// And join as string
$query = implode('&', $query);

// Init curl...
$ch = curl_init();

// Prepare curl
curl_setopt($ch, CURLOPT_URL, 'https://www.paypal.com/cgi-bin/webscr');
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
curl_setopt($ch, CURLOPT_POST, true);
//curl_setopt($ch, CURLOPT_POSTFIELDSIZE, 0);
curl_setopt($ch, CURLOPT_POSTFIELDS, $query);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

// Execute and close...
$result = curl_exec($ch);
curl_close($ch);

if($result == 'VERIFIED') {
$this->finish_order(false);
} // if

// Done here...
die();

} // paypal_notify

Mala adaptacije gore navedenog koda, curl only varijanta. Konstanta CURLOPT_POSTFIELDSIZE više ne postoji (iz nekog razloga) pa je ta linija zakomentarisana. To je manje više to, radi posao kao zmaj.