很久沒接支付寶了,不小心來了個項目,需要接入支付寶,翻了下整個硬盤,以前寫的代碼找不到了,只能重新從支付寶的DEMO開始,本來就知道支付寶demo爛,但沒想到爛到這程度。花了一整天,終於搞掂,記錄一下,以作參考。
工作步驟:
- 肯定得先找到文檔和並下載DEMO:
手機網頁文檔首頁:https://docs.open.alipay.com/203
網頁支付DEMO:https://docs.open.alipay.com/54/106682/
由於只懂PHP,所以只能下載PHP版的demo了。
下載完了一看,1.57M,比我的整個站點還大,其中還包括了一整個lotusphp框架,還有一大堆根本上完全用不到的lib,果斷還是按照以前的經驗,自己拼接接口直接對接支付,demo僅用作研究和類推。 - 把上面的所有文件解壓到根目錄下面的ali目錄內,不着急運行,先打開config.php看看有什麼東東,爲了減少版面,已經簡單重新排版過:
<?php $config = array ( 'app_id' => "", //應用ID,您的APPID。 'charset' => "UTF-8",//編碼格式 'sign_type'=>"RSA2",//簽名方式 'alipay_public_key' => "",//支付寶公鑰,貌似大家的都一樣 'merchant_private_key' => "", //商戶私鑰,這東西和商戶公鑰,得下載ali的簽名工具來離線生成,生成後還要提交到支付寶開放平臺 //支付寶網關 'gatewayUrl' => "https://openapi.alipay.com/gateway.do", //異步通知地址 'notify_url' => "http://工程公網訪問地址/alipay.trade.wap.pay-PHP-UTF-8/notify_url.php", //同步跳轉 'return_url' => "http://mitsein.com/alipay.trade.wap.pay-PHP-UTF-8/return_url.php", );
然後我們支看看支付寶開放平臺裏面,咱們的應用情況吧:
如上圖,其中
A、方框3就是所謂的appid了,
B、方框2可以看到應用的公鑰和支付寶公鑰,不需要手機短信
C、方框1需要手機驗證碼,先下載支付寶的RSA簽名驗籤工具,16.66M,可謂很不小了,畢竟還內含了一個不知道完整與否的JRE,傳送口:https://docs.open.alipay.com/291/106097,
然後,得出兩堆亂78糟的東西,演示用,所以把部份內容塗掉了:
這兩堆亂78糟的東西,都得複製粘貼到開放平臺的《接口加簽方式》那裏,需要手機驗證。
然後,商戶公鑰在網站中用不到,我只需要把商戶私鑰和支付寶公鑰弄到config.php裏面就行了。然後我的config.php大概長這樣: -
哎,不容易,才完成兩步,接下來跑一下看看是什麼卵樣吧,我的本地測試網址是w.com,改過hosts了,方便些。
經過1,2,3次點擊,我擦,哎呦不錯哦,居然可以了。但是,對於一個處女座的程序猿來說,這種不知根知底的,而且爛的一b的代碼,絕對不是我要的。只不過是生成訪問參數然後提交網關進行支付的功能,按照經驗來看,最多不會超過100行代碼就能搞掂。 -
好了,那麼開始動手自己拼裝參數並訪問接口了。先找到簽名函數,原來的簽名方法如下:
然並卵,我只需要RSA2和文本方式的密鑰簽名而已,並不需要RSA和文件方式,所以改爲這樣,簡潔多了吧:<?php /** * 支付寶RSA簽名 OPENSSL_ALGO_SHA256 * @param $data 待簽名數據 * @param $prikey 私鑰字符串 * return 簽名結果 */ function rsaSign($data, $prikey) { $res = "-----BEGIN RSA PRIVATE KEY-----\n" . wordwrap($prikey, 64, "\n", true) . "\n-----END RSA PRIVATE KEY-----"; openssl_sign($data, $sign,$res,OPENSSL_ALGO_SHA256); return base64_encode($sign); }
-
然後,就剩下參數的拼裝了,先由一個充值界面提交充值金額coin,入庫以後獲得新記錄的ID
就到真正的拼裝了,沒什麼技巧好說的,只保留一些必填的字段就行,選填的基本上不想理。<?php /** * @brief 拼裝支付寶參數並生成訪問表單 * * @param [in] $id 訂單id * @param [in] $coin 訂單金額 * @return 生成表單返回訪問支付寶的html * * @details Details */ function alipay($id,$coin){ $c=$GLOBALS['config']['alipay'];//配置文件,前面有提到了 //接口參數 選填的就不理了 $d['app_id'] =$c['appid']; $d['method'] ='alipay.trade.wap.pay'; $d['return_url']=$c['return_url']; $d['notify_url']=$c['notify_url']; $d['charset'] =$c['charset']; $d['sign_type'] =$c['signType']; $d['version'] ='1.0'; $d['timestamp'] =date('Y-m-d H:i:s'); //業務參數 不理可選的 $od['subject'] ="充值現金幣{$coin}元"; //商品標題 $od['out_trade_no'] =$id; //訂單號 $od['timeout_express'] ='10m'; //10分鐘內支付完成,否則關閉交易 $od['total_amount'] =$coin; //總金額 $od['product_code'] ='QUICK_WAP_WAY'; //銷售產品碼 $od['passback_params'] ='alipay'; //回傳參數,用來判斷是否支付寶支付的 //$od['body'] =''; //商品描述 $d['biz_content']=json_encode($od); //如果參數又多又亂,排序前還得做兩個動作:trim了以後,除去空值數據 ksort($d);//按key排序 $signStr=''; foreach($d as $k=>$v){ $signStr.="&{$k}={$v}";//這裏只能用循環,嘗試用http_build_query會導致訪問失敗 } $signStr=substr($signStr,1); $d['sign']=rsaSign($signStr,$c['prikey']); //生成簽名 上一步那裏很詳細了,其實3行代碼 //生成訪問表單 //試過用get的方式訪問,支付寶不認。有可能是sign參數超過了256位導致的。 $html="<form id='form1' action='{$c['url']}?charset={$c['charset']}' method='POST'>"; foreach($d as $k=>$v){ $v = str_replace("'","'",$v);//單引號會導致 $html.= "<input type='hidden' name='{$k}' value='{$v}'/>\n"; } $html.="<input type='submit' value='ok' style='display:none;''></form> <script>document.getElementById('form1').submit();</script>"; return $html;//直接echo就會立即跳轉到支付寶支付界面了 }
-
加上業務代碼,基本上支付流程就完成了:
//充值界面 function charge(){ $a=isset($_GET['a'])?$_GET['a']:''; $u=$_SESSION['u']; if($a=='do'){//提交過來以後的處理 $type=isset($_POST['type'])?$_POST['type']:''; $coin=isset($_POST['zcb'])&&is_numeric($_POST['zcb'])?$_POST['zcb']:0; if(!$type)$this->ajaxReturn(array(0,'充值類型錯誤1')); if($type!='alipay'&&$type!='wxpay')$this->ajaxReturn(array(0,'充值類型錯誤2')); if(!$coin)$this->ajaxReturn(array(0,'充值金額錯誤')); $d=['uid'=>$u['user_id'],'uname'=>$u['uname'],'type'=>'charge','status'=>0,'coin'=>$coin,'zcb'=>$coin,'from_uname'=>$type]; $r=M('income'); $id=$r->add($d);//入庫 $pay=D('Pay'); $formHtml=$pay->$type($id,$coin);//$type='alipay' 調用支付寶支付 $this->ajaxReturn(array(1,'正在跳轉到支付網關,請稍候',$formHtml)); } $this->assign('webtitle',gettext('充值')); $this->display(); }
部份HTML如下:
<div id='formdiv' style='display:none'></div> <script> $('#form').on('submit',function(){ var d=$(this).form2json(); if(!d.zcb.is_numeric())return die('充值金額填寫錯誤'); layer.load(2); $.post(this.action,d,function(r){ layer.closeAll(); layer.msg(r[1],function(){ layer.closeAll(); if(r[0]){//充值金額已經提交成功 $('#formdiv').html(r[2]);//上一步生成的formHTML放到隱藏元素裏 $('form1').submit();//然後直接提交 } }); },'json'); return false; }); </script>
先到這了,寫個文章寫了兩個小時,累成狗了,明天再搞return和notify。