在注册完成开发账户,虚拟的买家账户以及卖家账户后,即可准备开始开发,不过在此需要明白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的开发也主要就是按照此处进行修改开发的。