PHP手機網頁接入支付寶心得(一)自主拼接參數並簽名訪問

很久沒接支付寶了,不小心來了個項目,需要接入支付寶,翻了下整個硬盤,以前寫的代碼找不到了,只能重新從支付寶的DEMO開始,本來就知道支付寶demo爛,但沒想到爛到這程度。花了一整天,終於搞掂,記錄一下,以作參考。

工作步驟:

  1. 肯定得先找到文檔和並下載DEMO:
    手機網頁文檔首頁:https://docs.open.alipay.com/203
    網頁支付DEMO:https://docs.open.alipay.com/54/106682/
    由於只懂PHP,所以只能下載PHP版的demo了。
    demo壓縮包信息
    下載完了一看,1.57M,比我的整個站點還大,其中還包括了一整個lotusphp框架,還有一大堆根本上完全用不到的lib,果斷還是按照以前的經驗,自己拼接接口直接對接支付,demo僅用作研究和類推。
  2. 把上面的所有文件解壓到根目錄下面的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大概長這樣:

  3. 哎,不容易,才完成兩步,接下來跑一下看看是什麼卵樣吧,我的本地測試網址是w.com,改過hosts了,方便些。

    經過1,2,3次點擊,我擦,哎呦不錯哦,居然可以了。但是,對於一個處女座的程序猿來說,這種不知根知底的,而且爛的一b的代碼,絕對不是我要的。只不過是生成訪問參數然後提交網關進行支付的功能,按照經驗來看,最多不會超過100行代碼就能搞掂。

  4. 好了,那麼開始動手自己拼裝參數並訪問接口了。先找到簽名函數,原來的簽名方法如下:

    然並卵,我只需要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);
    }
    

     

  5. 然後,就剩下參數的拼裝了,先由一個充值界面提交充值金額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("'","&apos;",$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就會立即跳轉到支付寶支付界面了
    	}
  6. 加上業務代碼,基本上支付流程就完成了:

        //充值界面
    	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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章