您的位置:首页 > 运维架构

分析Ecshop自带PayPal标准支付模块支付失败的原因 ecshop模板网 / 2014-07-06

2015-09-17 16:17 549 查看


分析Ecshop自带PayPal标准支付模块支付失败的原因


ecshop模板网 / 2014-07-06

结论 如果卖家的PayPal帐号设置了不能设置自动返回,那么支付完成后将在10秒内自动跳转到购物网站

Ecshop自带有一个PayPal标准支付模块,只需要在后台安装并设置PayPal帐号即可使用。但是这个Ecshop v2.7.3这个版本的PayPal标准支付是有bug的,当在PayPal设置自动返回后,就会出现订单在返回自己网站后明明已支付成功,却显示支付失败的问题。但只要是手动从PayPal返回的,却一切正常。

刚开始一直不知道是哪里出了问题。仔细分析了一下PayPal的标准支付流程后就可以很容易的找到问题的症结所在了。首先在自己的网站需要生成一个包含购物车信息的表单,用来提交到PayPal。在includes/modules/payment/paypal.php文件中的如下代码:

/**

* 生成支付代码

* @param array $order 订单信息

* @param array $payment 支付方式信息

*/

function get_code($order, $payment)

{

$data_order_id = $order['log_id'];

$data_amount = $order['order_amount'];

$data_return_url = return_url(basename(__FILE__, '.php'));

$data_pay_account = $payment['paypal_account'];

$currency_code = $payment['paypal_currency'];

$data_notify_url = return_url(basename(__FILE__, '.php'));

$cancel_return = $GLOBALS['ecs']->url();

$def_url = '

' . // 不能省略

"" . // 不能省略

"" . // 贝宝帐号

"" . // payment for

"" . // 订单金额

"" . // 货币

"" . // 付款后页面

"" . // 订单号

"" . // 字符集

"" . // 不要求客户提供收货地址

"" . // 付款说明

"" .

"" .

"" .

"" . // 按钮

"

";

return $def_url;

}

当该表单提交到PayPal后,客户可在PayPal平台完成支付。当客户完成支付,PayPal会立即post一个表单到购物的站点,具体的返回地址就是刚才那个表单中的notify_url的值。而客户在返回购物网站的时候有两种可能,如果卖家的PayPal帐号设置了自动返回,那么支付完成后将在10秒内自动跳转到购物网站,而这个跳转是没有post那些必要的返回信息的。另一种情况,就是卖家没有设置自动返回,这是在客户点击跳转会购物网站的页面如果用firebug查看一下,是可以看到一个post的表单的,里面包含了所以必须的信息。而Ecshop的PayPal标准支付模块的bug恰恰就是没有考虑到返回的这个差异。

再回过头来看看paypal.php的相关代码,问题就一目了然了,首先上述表单中的return_url是用户完成支付后返回网站时显示给用户看的页面,notify_url的值是用户完成支付时,PayPal用来post相关信息的地址。那么在get_code($order, $payment)这个方法中,不难发现这两个地址是相同的。

那么在继续看paypal.php中用来处理返回信息的代码。代码如下:

/**

* 响应操作

*/

function respond()

{

$payment = get_payment('paypal');

$merchant_id = $payment['paypal_account']; ///获取商户编号

// read the post from PayPal system and add 'cmd'

$req = 'cmd=_notify-validate';

foreach ($_POST as $key => $value)

{

$value = urlencode(stripslashes($value));

$req .= "&$key=$value";

}

// post back to PayPal system to validate

$header = "POST /cgi-bin/webscr HTTP/1.0\r\n";

$header .= "Content-Type: application/x-www-form-urlencoded\r\n";

$header .= "Content-Length: " . strlen($req) ."\r\n\r\n";

$fp = fsockopen ('www.paypal.com', 80, $errno, $errstr, 30);

// assign posted variables to local variables

$item_name = $_POST['item_name'];

$item_number = $_POST['item_number'];

$payment_status = $_POST['payment_status'];

$payment_amount = $_POST['mc_gross'];

$payment_currency = $_POST['mc_currency'];

$txn_id = $_POST['txn_id'];

$receiver_email = $_POST['receiver_email'];

$payer_email = $_POST['payer_email'];

$order_sn = $_POST['invoice'];

$memo = !empty($_POST['memo']) ? $_POST['memo'] : '';

$action_note = $txn_id . '(' . $GLOBALS['_LANG']['paypal_txn_id'] . ')' . $memo;

if (!$fp)

{

fclose($fp);

return false;

}

else

{

fputs($fp, $header . $req);

while (!feof($fp))

{

$res = fgets($fp, 1024);

if (strcmp($res, 'VERIFIED') == 0)

{

// check the payment_status is Completed

if ($payment_status != 'Completed' && $payment_status != 'Pending')

{

fclose($fp);

return false;

}

// check that txn_id has not been previously processed

/*$sql = "SELECT COUNT(*) FROM " . $GLOBALS['ecs']->table('order_action') . " WHERE action_note LIKE '" . mysql_like_quote($txn_id) . "%'";

if ($GLOBALS['db']->getOne($sql) > 0)

{

fclose($fp);

return false;

}*/

// check that receiver_email is your Primary PayPal email

if ($receiver_email != $merchant_id)

{

fclose($fp);

return false;

}

// check that payment_amount/payment_currency are correct

$sql = "SELECT order_amount FROM " . $GLOBALS['ecs']->table('pay_log') . " WHERE log_id = '$order_sn'";

if ($GLOBALS['db']->getOne($sql) != $payment_amount)

{

fclose($fp);

return false;

}

if ($payment['paypal_currency'] != $payment_currency)

{

fclose($fp);

return false;

}

// process payment

order_paid($order_sn, PS_PAYED, $action_note);

fclose($fp);

return true;

}

elseif (strcmp($res, 'INVALID') == 0)

{

// log for manual investigation

fclose($fp);

return false;

}

}

}

}

接下来的流程是在接到PayPal post过来的表单后,直接在拼接上cmd=_notify-validate,然后在发回PayPal的服务器进行验证,再对PayPal返回的信息进行逐行匹配,如果发现有’VERIFIED’就表示整个支付完成。所以整个流程并不复杂,当客户完成支付,PayPal会立即post一个表单到notify_url的这个地址,也就会先执行一次respond()这个方法,在这个时候其实网站后台对于订单数据的操作已经完成了。当客户在手动返回的时候又要同样有一个post表单过来,所以会重复执行一遍respond(),当然结果会显示successfully,而当客户是自动返回购物网站的,由于自动返回并没有post过来任何表单,那么拼接上cmd=_notify-validate再发回PayPal服务器验证一定是失败的,所以会显示支付失败,而实际情况是支付成功的。

至此,问题已经很清楚了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: