paypal支付开发解析(二)

在注册完成开发账户,虚拟的买家账户以及卖家账户后,即可准备开始开发,不过在此需要明白IPN与PDT是什么。

IPN(Instant Payment Notify) 即时付款传输

什么事即时付款通知,pdf文档里面说得很明白,一张图更加的有助于理解。

其中1到5这个动作过程就是即时付款通知的整个流程。详细解释如下

注意这里的IPN,是由paypal服务器主动把数据post到你指定的url页面,这里的url怎么设置?即IPN是如何启用的呢?

pdf文档里面说明了有两种方法,一是在卖家paypal登录账户后,在“用户信息”右边列表栏“即时付款通知习惯设定”点击那个“选择框” 输入通知的url地址

二是在表单中加入name为notify_url的隐藏域地址来启用IPN。

在这里我们采用第二种方法。

直观的示例代码:

// paypal发送至的$_POST数据
if(!empty($_POST)) {
$notify = $_POST;
$location = TRUE;
} else {
exit('Access Denied');
}
// 加上cmd命令 数据原样返回
$tmpAr = array_merge($_POST, array("cmd" => "_notify-validate"));
$postFieldsAr = array();
foreach ($tmpAr as $name => $value) {
$postFieldsAr[] = "$name=$value";
}
// $custom 自定义值
$customs = explode( '|', $notify['custom']);
// 订单编号
$orderid = $customs[1];
// 使用curl 发送至paypal服务器,返回VERIFIED或者是INVALID
$ppResponseAr = PPHttpPost(PAYPAL_URL, implode("&", $postFieldsAr), false);

$notifydata= array();
if( strcmp($ppResponseAr['httpResponse'], 'VERIFIED') == 0){
         // 数据验证成功,检测处理的订单数据,如订单状态是否完成,卖家商户邮箱是否一致,此次订单货币是否与表单提交一致等
if( ($notify['payment_status'] == 'Completed') && ($notify['receiver_email'] == DEFAULT_EMAIL_ADDRESS) && ($notify['mc_currency'] == 'USD') ){
$notifydata= array(
'validator'    => TRUE,
'status'    => 'completed',
'order_no'     => $orderid,
'price'     => $notify['mc_gross'] ? $notify['mc_gross'] : $notify['payment_gross'],
'trade_no'    => $notify['txn_id'],
'notify'    => 'success',
'location'    => $location
);
}
}else if( strcmp($ppResponseAr['httpResponse'], 'INVALID') == 0) {
$notifydata= array(
'validator'    => FALSE,
'notify'    => 'fail',
'location'    => $location
);
}

    /**
     * 结合discuzx的具体业务逻辑实现数据内容的更新
     */

// 订单数据paypal验证成功情况下
if($notifydata['validator']) {

$orderid = $notifydata['order_no'];
$postprice = $notifydata['price'];
$order = DB::fetch_first("SELECT o.*, m.username FROM ".DB::table('forum_order')." o LEFT JOIN ".DB::table('common_member')." m USING (uid) WHERE o.orderid='$orderid'");
if($order && floatval($postprice) == floatval($order['price']) ) {
        // 更新订单状态
if($order['status'] == 1) {
DB::query("UPDATE ".DB::table('forum_order')." SET status='2', buyer='$notifydata[trade_no]\tpaypal', paytype='paypal', confirmdate='$_G[timestamp]' WHERE orderid='$orderid'");
            // 更新用户的虚拟货币
updatemembercount($order['uid'], array($_G['setting']['creditstrans'] => $order['amount']), 1, 'AFD', $order['uid']);
updatecreditbyaction($action, $uid = 0, $extrasql = array(), $needle = '', $coef = 1, $update = 1, $fid = 0);
            // 清理过期的订单
DB::query("DELETE FROM ".DB::table('forum_order')." WHERE submitdate<'$_G[timestamp]'-60*86400");

$submitdate = dgmdate($order['submitdate']);
$confirmdate = dgmdate(TIMESTAMP);
            // 向用户发送充值成功的消息
notification_add($order['uid'], 'credit', 'addfunds_d', array(
'orderid' => $order['orderid'],
'price' => $order['price'],
'value' => $_G['setting']['extcredits'][$_G['setting']['creditstrans']]['title'].' '.$order['amount'].' '.$_G['setting']['extcredits'][$_G['setting']['creditstrans']]['unit']
), 1);
}

}

}

// 充值成功,跳转
if($notifydata['location']) {
$url = rawurlencode('home.php?mod=spacecp&ac=credit');

dheader('location: '.$_G['siteurl'].'forum.php?mod=misc&action=paysucceed');
} else {
    // 充值失败,显示提示信息
exit($notifydata['notify']);
}

至此IPN作用完成,同时整个充值过程也就完成了,不过用户这时还停留在paypal的支付完成的页面,不会自动跳转至商户或者用户已充值记录页面。这时,PDT的作用就出现了。

PDT(Payment Data Transfer)付款数据传输

同样PDT示意图

怎样启用PDT呢,方式一需要卖家账户登录,在“用户信息”; “网站付款习惯设定”; 设置auto return 为“on”;同时在return url中设置返回的url地址;设置“Payment Data Transfer”为“on” ;点击“save”保存,这时“身份标记”就永久的显示在网页上面了。

方式二 订单或充值表单中添加hidden隐藏域

代码显示


// PDT 付款数据传输 ,DEFAULT_IDENTITY_TOKEN为刚才点击“save”生成的token值,这个值可以保存到数据库中
if( DEFAULT_IDENTITY_TOKEN){
// 获取返回的tx 交易流水号
$url = PAYPAL_URL;
$postFields =    "cmd=".urlencode("_notify-synch").
"&tx=".urlencode(htmlspecialchars($_GET["tx"])).
"&at=".urlencode(DEFAULT_IDENTITY_TOKEN);

// 请求paypal或者 付款信息明细
$ppResponseAr = PPHttpPost($url, $postFields, true);

// 不存在该交易付款信息明细
if( !$ppResponseAr['status']){
$ppResponseAr["error_msg"] = '抱歉,支付失败';

}else {
// 存在该交易
$response = $ppResponseAr['httpParsedResponseAr'];
// var_dump( $response);exit;
$tradeno = $response['txn_id'];        // paypal的交易订单

// paypal存在交易订单,且为完成状态
if( ($response['payment_status'] == 'Completed') && (urldecode($response['receiver_email']) == DEFAULT_EMAIL_ADDRESS)){

$query = DB::query("SELECT * FROM ".DB::table('forum_order')." WHERE orderid='$orderid'");

//判断数据库中是否存在该订单
if(DB::num_rows($query)) {
// 处理订单,更新用户的积分
$order = DB::fetch($query);

// 订单为未处理状态,则进行处理
if( $order['status'] == '1'){
// 设置订单
DB::query("UPDATE ".DB::table('forum_order')." SET status='2', buyer='{$tradeno}\tpaypal', paytype='paypal', confirmdate='$_G[timestamp]' WHERE orderid='$orderid'");
// 更新积分
updatemembercount($order['uid'], array($_G['setting']['creditstrans'] => $order['amount']), 1, 'AFD', $order['uid']);
updatecreditbyaction($action, $uid = 0, $extrasql = array(), $needle = '', $coef = 1, $update = 1, $fid = 0);
DB::query("DELETE FROM ".DB::table('forum_order')." WHERE submitdate<'$_G[timestamp]'-60*86400");

$submitdate = dgmdate($order['submitdate']);
$confirmdate = dgmdate(TIMESTAMP);

notification_add($order['uid'], 'credit', 'addfunds', array(
'orderid' => $order['orderid'],
'price' => $order['price'],
'value' => $_G['setting']['extcredits'][$_G['setting']['creditstrans']]['title'].' '.$order['amount'].' '.$_G['setting']['extcredits'][$_G['setting']['creditstrans']]['unit']
), 1);
}  // end

}
// 有待执行
}else if( $response['payment_status'] == 'Pending'){
$ppResponseAr['error_msg'] = '款项等待支付,请前往paypal确认接受';
}

}

}else {
$ppResponseAr["status"] = FALSE;
$ppResponseAr["error_msg"] = '正在处理,请稍后';
}

// 提示用户交易完成
showmessage( '感谢您的付款。您的交易已经完成,您可以在www.paypal.com/c2 上登录您的账户查看此次交易情况。 '.$response['payment_status'], 'home.php?mod=spacecp&ac=credit&op=log');

不过这里测试与debug调试表现的都比较简陋,其实可以在充值记录过程中,把一些状态信息都可以进行写入log文件,这样便于监测与管理。主要的逻辑结合处就是程序根据paypal支付返回的数据进行本地的业务进行处理,如充值完成后,通知店主发货,或者增加虚拟货币,积分等。discuzx把这些都api模块化了,很方便,paypal的开发也主要就是按照此处进行修改开发的。

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*