PHP銀聯在線接口(TP3.2)

一、 登陸銀聯自助化測試平臺(登錄地址:open.unionpay.com),登陸後,點擊個人產品,以下:點擊右方須要測試的接口,本例以 手機網頁支付(WAP支付)爲例。javascript

這裏寫圖片描述 
這裏寫圖片描述 
二、 點擊左側菜單 測試參數,便可看到測試過程所須要的參數,以下圖:點擊測試證書,下載兩個證書,一個是之後綴 .pfx 的私鑰證書,一個是後綴 .cer 的公鑰證書,將其下載後,私鑰證書文件名稱修改成 acp_test_sign.pfx 。或者你不下載的話,直接用本例子的也能夠。TP3.2例子 裏面 Public/cer 裏面已有全部證書文件。 
這裏寫圖片描述php

三、 TP3.2例子裏面已有相關代碼能夠用來測試,測試的時候請使用測試環境的參數,代碼中均有註釋。在開始以前要確保你的環境PHP版本基於5.3,需開啓curl、openssl功能,還有測試要放在線上測試,本地的虛擬域名是不行的。如遇到什麼問題,能夠參考官方的說明,本文件夾裏面有個 PHP Version SDK 是官方的文檔,參考裏面的說明就行了,他們那個例子我測試的時候也跑不起來,不知道什麼鬼緣由。 
四、 切換到生產環境,注意如下問題: 
4.1 首先根據你收到的商戶開通郵件裏面的指示,訪問網站 http://cs.cfca.com.cn/ 
下載生產證書文件: 
這裏寫圖片描述 
點擊下載後,完成下載操做後,頁面會出現下載成功的提示。下載的證書自動存放在IE中,下一步就要進行證書的導出。 
這裏寫圖片描述 
4.2 導出證書文件:打開IE瀏覽器,點擊右上角的齒輪,打開工具=》Internet選項=》內容=》證書,如圖: 
這裏寫圖片描述html

點擊證書後,找到剛剛下載的那個證書,你能夠根據名稱去辨別,商戶郵件中有標註: 
這裏寫圖片描述 
上圖中紅色標註的名稱應該跟你下載的那個名稱同樣。 
這裏寫圖片描述 
找到它,而後點擊導出:一路的下一步,在如下幾步須要注意java

這裏寫圖片描述

這裏寫圖片描述

這裏寫圖片描述

以上密碼就是 config.php 裏面生產環境要設置的那個密碼,請設置爲六位數字(僅限數字,請勿設置字母及符號)web

指定導出證書的文件名,名稱就設置爲:acp_prod_sign,並選擇目錄存放證書,點擊下一步,設置導出到桌面,完成後將在桌面看到一個 acp_prod_sign.pfx 的文件。這個就是生產環境要用到的私鑰文件,將它複製一份到證書目錄 /Public/cer 。下一步就要上傳這個證書到商戶服務網站。算法

4.2 上傳證書到商戶服務網站。登陸 https://merchant.unionpay.com/portal/login.jsp 
這裏寫圖片描述 
上傳剛剛導出的那個 acp_prod_sign.pfx 文件,點擊上傳。數據庫

下一步,啓用證書,點擊安全證書管理,啓用便可。 
這裏寫圖片描述json

下一步,下載銀聯公鑰 
這裏寫圖片描述 
解壓文件,把裏面的兩個證書一樣放到 /Public/cer 裏面。而後就去 config.php 裏面根據文件註釋切換到生產環境便可。api

這裏寫圖片描述

 

配置項數組

<?php
return array(
    //'配置項'=>'配置值'

    'UNIONPAY' => array(// 銀聯配置
        //測試環境參數
        'frontUrl' => 'https://101.231.204.80:5000/gateway/api/frontTransReq.do', //測試環境前臺交易請求地址
        //'frontUrl' => 'https://gateway.95516.com/gateway/api/frontTransReq.do', //生產環境前臺交易請求地址

        'singleQueryUrl' => 'https://101.231.204.80:5000/gateway/api/backTransReq.do', //測試環境單筆查詢請求地址
        //'singleQueryUrl' => 'https://gateway.95516.com/gateway/api/queryTrans.do', //生產環境單筆查詢請求地址

        'signCertPath' =>getcwd().'/Public/cer/acp_test_sign.pfx', //簽名證書路徑 這個證書就是你在https://open.unionpay.com/ajweb/account/testPara 上面下載的那個商戶私鑰證書 供你測試使用 
        //'signCertPath' =>getcwd().'/Public/cer/acp_prod_sign.pfx', //簽名證書路徑 這個證書就是你在 商戶開通郵件裏面叫你去 http://cs.cfca.com.cn/ 下載並把私鑰上傳至商戶服務網站並啓用的那個私鑰文件

        'signCertPwd' => '000000', //測試環境簽名證書密碼
        //'signCertPwd' => '135246', //生產環境證書籤名證書密碼 這個密碼是你在IE導出上述私鑰文件時候你本身定義的6位數字密碼 

        //'verifyCertPath' => getcwd().'/Public/cer/verify_sign_acp.cer', //測試環境驗簽證書路徑
        'verifyCertPath' => getcwd().'/Public/cer/acp_prod_verify_sign.cer', //驗簽證書路徑

        'merId' => '777290058138754', //測試商戶代碼
        //'merId' => '8024400481****', //生產環境商戶代碼 從你的商戶開通郵件裏面有


    ),
    'UNIONPAY_CONFIG'=>array(// 銀聯配置
        'version' => '5.0.0', //版本號
        'encoding' => 'GBK', //編碼方式
        'signMethod' => '01', //簽名方式
        'txnType' => '01', //交易類型
        'txnSubType' => '01', //交易子類
        'bizType' => '000201', //產品類型
        'channelType' => '07',//渠道類型
        'frontUrl' => "http://win2.qbt8.com/ase_admin/index.php/Home/ypay/pay_success", //前臺通知地址
        'backUrl' => "http://win2.qbt8.com/ase_admin/index.php/Home/ypay/notify", //後臺通知地址
        'frontFailUrl' => "http://win2.qbt8.com/ase_admin/index.php/Home/ypay/pay_fail", //失敗交易前臺跳轉地址
        'accessType' => '0', //接入類型
        'merId' => '777290058138754', //測試商戶代碼
        //'merId' => '8024400481*****', //生產環境商戶代碼
        'txnTime' => date('YmdHis'), //訂單發送時間
        'currencyCode' => '156', //交易幣種
    ),
);

?>

 

控制器

<?php
/*
|-------------------------------------------------------------|
|                       銀聯在線支付控制器
|author:shuguang  date:2016-11-16 
|-------------------------------------------------------------|
*/

namespace Home\Controller;
use Think\Controller;

class YpayController extends Controller
{
    /**
     * 支付配置
     * @var array
     */
    public $config = array();
    /**
     * 支付參數,提交到銀聯對應接口的全部參數
     * @var array
     */
    public $params = array();
    /**
     * 自動提交表單模板
     * @var string
     */
    private $formTemplate = <<<HTML
        <!DOCTYPE HTML>
        <html>
        <head>
                <meta charset="utf-8">
                <title>支付</title>
        </head>
        <body>
                <div style="text-align:center">跳轉中...</div>
                <form id="pay_form" name="pay_form" action="%s" method="post">
                        %s
                </form>
                <script type="text/javascript">
                        document.onreadystatechange = function(){
                                if(document.readyState == "complete") {
                                        document.pay_form.submit();
                                }
                        };
                </script>
        </body>
        </html>
HTML;

    public function index(){
        //前臺表單
        $this->display();
    }
    /*支付成功後 前臺通知地址*/
    public function pay_success(){

        echo "<h1>支付成功!</h1>";
    }

    /*失敗交易前臺跳轉地址*/
    public function pay_fail(){

        echo "<h1>支付失敗!</h1>";
    }

    /*生產支付參數 提交支付 */
    function usespay(){
        $this->config = C('UNIONPAY');//從配置裏讀取 
        $config = C('UNIONPAY_CONFIG');
        $config['certId']  = $this->getSignCertId(); //證書ID
        $config['orderId'] = mt_rand(111111111,999999999);//訂單號 自定義 
        $config['txnAmt']  = I("post.money")*100; //交易金額,單位分

        $this->params = $config;
        // $_SESSION['ceshi']=$config;

        /* 如下是本身的業務邏輯操做 生產支付記錄到本地數據庫 
        $money = I("post.money");;
        $user_id = $this->user_id;
        $OrderId = $config['orderId'];//生成隨機訂單號
        $pay_type = "銀聯";//支付方式 1餘額 2支付寶
        $pay_fee = M('handfee')->find(2);
        if ($pay_fee['type'] == 1){
            $fee=$pay_fee['rate']*$money;
        }else {
            $fee=$pay_fee['fee'];
        }
        //訂單表數據

        $order = array(
                "order_id"=>$OrderId,
                "uid"=>$user_id,
                "pay_mode"=>1,
                "pay_channels"=>2,
                "fee"=>$fee,
                "status"=>0,//待審覈
                "beizhu"=>"銀聯在線充值",
                "ent_money"=>$money-$fee,
                "time"=>time(),
                "sub_time"=>time(),
                "pay_money"=>$money,
                "pay_type"=>$pay_type,//1餘額支付 2支付寶支付
                //"type"=>2
        );*/

        //$Ord=M('pay');
        //$Ord->add($order);

        $html = $this->createPostForm();//構建自動提交HTML表單
        echo $html;
    }
    function ceshi(){
        dump($_SESSION);
    }

    function usernotify(){// 付款後返回商家


    }
    function notify(){//後臺通知路徑
        /*付款後業務邏輯代碼  */
        $orderId = $_POST ['orderId']; //其餘字段也可用相似方式獲取
        $respCode = $_POST ['respCode']; //判斷respCode=00或A6便可認爲交易成功
        if ($respCode=='00'||$respCode=='A6'){

            /*經過寫入文件的方式記錄返回的訂單號等 */
            $str = "--------- ".date('Y-m-d H:i:s')." ---------";
            $str .= "orderId:".$orderId."\r\n";
            $str .= "respCode:".$respCode."\r\n";
            $str .= "--------- END -----------"."\r\n";
            file_put_contents('unionpay_notify_log.log', $str);


            /* 如下是支付成功後的數據庫操做 請根據須要自行操做 
            $order['status']=1;
            $order['check_time']=time();

            M('pay')->where(array('order_id'=>$orderId))->save($order); 
            $order_info = M('pay')->where(array('order_id'=>$orderId))->find();  
            $log['user_id']=$order_info['uid'];
            $log['user_money']=$order_info['pay_money'];
            $log['change_time']=time();
            $log['desc']="銀聯在線充值";
            M('account_log')->add($log);
            M('users')->where('user_id='.$order_info['uid'])->setInc('user_money',$order_info['ent_money']);
            */
        }
    }
    function unionpayfail(){

    }
    /*  function orderPay($orderinfo,$state){
         $filename = 'Log/yapy';        
         file_put_contents($filename.'/'.$orderinfo['orderId'].'.txt', json_encode($_POST), FILE_APPEND);
         //$order = D('order');
         //$payment = D('payment');
         //$where['order_sn'] = array('in', array($orderinfo['orderId']));
         //$orinfo = $order->where($where)->find();
 
         $rs = $payment->where($where)->find();
         if (empty($rs) && $orinfo['order_state'] < 2 ) {
             $where1['udb_user.user_id'] = array('eq', session('id'));
             $userinfo1 = json_decode(req_api("api_key", C("API_KEY"), C('USER_API') ."user/api/GetSomeuser/", array('where' => json_encode($where1))), true);
             $data1['order_state'] = (int) $state;
             //$orderwhere['order_sn'] = array('in', array($orderinfo['orderId']));
 
             //$order->where($orderwhere)->save($data1);
 
             if($orinfo['balance'] >0 && $orinfo['isblance'] == 1){
                 if($userinfo1[0]['balance']-$orinfo['balance']>=0){
                     $total1 = $total1-$data['balance'];
                     $istrue = req_api("api_key", C("API_KEY"), C('USER_API') . "user/api/removeBalance/", array('user' =>session('id'),'count'=>$orinfo['balance'],'type'=>'d'));
                     //$this->BanlanceRecord(2,$orinfo['balance'],'購物消費',session('id'));
                 }
             }
             if ($orinfo['jindou'] >0 && $orinfo['isjindou'] == 1) {
                 if($userinfo1[0]['user_wealth']-$orinfo['jindou']>=0){
                     $istrue = req_api("api_key", C("API_KEY"), C('USER_API') . "user/api/AddJindou/", array('user' =>session('id'),'count'=>$orinfo['jindou'],'type'=>'d'));
                     $this->ChangeRecord(2,$orinfo['jindou'],'購物抵消',session('id'));
                     $total1 = $total1-($orinfo['jindou']/100);
                 }
             }
             $data['order_sn'] = $orderinfo['orderId'];
             $data['buyer_id'] = $orderinfo['certId'];
             $data['buyer_user'] = '銀聯支付';
             $data['is_success'] = 'T';
             $data['notify_time'] = substr($orderinfo['txnTime'],0,4)."-".substr($orderinfo['txnTime'],4,2).'-'.substr($orderinfo['txnTime'],6,2).' '.substr($orderinfo['txnTime'],8,2).':'.substr($orderinfo['txnTime'],10,2).':'.substr($orderinfo['txnTime'],12,2);
             $data['trade_no'] = $orderinfo['queryId'];
             $data['seller_id'] = $orderinfo['merId'];
             $data['total_fee'] = $orderinfo['txnAmt']*100;
             $data['sign'] = $orderinfo['signature'];
             $data['user_id'] = $orinfo['user_id'];
             $data['order_state'] = (int) $state;
             $data['status'] = 0;
 
             $payment->data($data)->filter('strip_tags')->add();
         }
 
         $record = A('Shop/Orderrecord');
         $shuju['order_state'] = (string) $state;
         $shuju['action_user_id'] = session('id');
         $shuju['action_descrption'] = $type.'支付寶付款' . $orinfo['payable_total'];
         $record->ChangeOrderRecords($orinfo['_id'], $shuju);
         $orderrecord = A('Shop/Order');
         $orderrecord->CashMoneyRecord(2, $orinfo['payable_total'], '購物消費--訂單(' . $orderinfo['out_trade_no'] . ')', session('id'));
         layout(false);
         $this->assign('orderinfo', $orinfo);
         $this->display('Order:PaySuccess6');
     } */

    /**
     * 構建自動提交HTML表單
     * @return string
     */
    public function createPostForm()
    {

        $this->params['signature'] = $this->sign();

        $input = '';
        foreach($this->params as $key => $item) {
            $input .= "\t\t<input type=\"hidden\" name=\"{$key}\" value=\"{$item}\">\n";
        }
        return sprintf($this->formTemplate, $this->config['frontUrl'], $input);
    }
    /**
     * 驗證簽名
     * 驗籤規則:
     * 除signature域以外的全部項目都必須參加驗籤
     * 根據key值按照字典排序,而後用&拼接key=value形式待驗簽字符串;
     * 而後對待驗簽字符串使用sha1算法作摘要;
     * 用銀聯公鑰對摘要和簽名信息作驗籤操做
     *
     * @throws \Exception
     * @return bool
     */
    public function verifySign()
    {
        $publicKey = $this->getVerifyPublicKey();
        $verifyArr = $this->filterBeforSign();
        ksort($verifyArr);
        $verifyStr = $this->arrayToString($verifyArr);
        $verifySha1 = sha1($verifyStr);
        $signature = base64_decode($this->params['signature']);
        $result = openssl_verify($verifySha1, $signature, $publicKey);
        if($result === -1) {
            // throw new \Exception('Verify Error:'.openssl_error_string());
            echo 'Verify Error:'.openssl_error_string();
        }
        return $result === 1 ? true : false;
    }
    /**
     * 取簽名證書ID(SN)
     * @return string
     */
    public function getSignCertId()
    {

        return $this->getCertIdPfx($this->config['signCertPath']);
    }
    /**
     * 簽名數據
     * 簽名規則:
     * 除signature域以外的全部項目都必須參加簽名
     * 根據key值按照字典排序,而後用&拼接key=value形式待簽名字符串;
     * 而後對待簽名字符串使用sha1算法作摘要;
     * 用銀聯頒發的私鑰對摘要作RSA簽名操做
     * 簽名結果用base64編碼後放在signature域
     *
     * @throws \InvalidArgumentException
     * @return multitype|string
     */
    private function sign() {
        $signData = $this->filterBeforSign();
        ksort($signData);
        $signQueryString = $this->arrayToString($signData);
        if($this->params['signMethod'] == 01) {
            //簽名以前先用sha1處理
            //echo $signQueryString;exit;
            $datasha1 = sha1($signQueryString);
            $signed = $this->rsaSign($datasha1);
        } else {
            //throw new \InvalidArgumentException('Nonsupport Sign Method');
            echo 'Nonsupport Sign Method';
        }
        return $signed;
    }
    /**
     * 數組轉換成字符串
     * @param array $arr
     * @return string
     */
    private function arrayToString($arr)
    {
        $str = '';
        foreach($arr as $key => $value) {
            $str .= $key.'='.$value.'&';
        }
        return substr($str, 0, strlen($str) - 1);
    }
    /**
     * 過濾待簽名數據
     * signature域不參加簽名
     *
     * @return array
     */
    private function filterBeforSign()
    {
        $tmp = $this->params;
        unset($tmp['signature']);
        return $tmp;
    }
    /**
     * RSA簽名數據,並base64編碼
     * @param string $data 待簽名數據
     * @return mixed
     */
    private function rsaSign($data)
    {
        $privatekey = $this->getSignPrivateKey();
        $result = openssl_sign($data, $signature, $privatekey);
        if($result) {
            return base64_encode($signature);
        }
        return false;
    }
    /**
     * 取.pfx格式證書ID(SN)
     * @return string
     */
    private function getCertIdPfx($path)
    {
        $data = fopen($path);
        $pkcs12certdata = file_get_contents($path);
        openssl_pkcs12_read($pkcs12certdata, $certs, $this->config['signCertPwd']);
        $x509data = $certs['cert'];
        openssl_x509_read($x509data);
        $certdata = openssl_x509_parse($x509data);
        return $certdata['serialNumber'];
    }
    /**
     * 取.cer格式證書ID(SN)
     * @return string
     */
    private function getCertIdCer($path)
    {
        $x509data = file_get_contents($path);
        openssl_x509_read($x509data);
        $certdata = openssl_x509_parse($x509data);
        return $certdata['serialNumber'];
    }
    /**
     * 取簽名證書私鑰
     * @return resource
     */
    private function getSignPrivateKey()
    {
        $pkcs12 = file_get_contents($this->config['signCertPath']);
        openssl_pkcs12_read($pkcs12, $certs, $this->config['signCertPwd']);
        return $certs['pkey'];
    }
    /**
     * 取驗證簽名證書
     * @throws \InvalidArgumentException
     * @return string
     */
    private function getVerifyPublicKey()
    {
        //先判斷配置的驗簽證書是否銀聯返回指定的證書是否一致
        if($this->getCertIdCer($this->config['verifyCertPath']) != $this->params['certId']) {
            // throw new \InvalidArgumentException('Verify sign cert is incorrect');
            echo 'Verify sign cert is incorrect';
        }
        return file_get_contents($this->config['verifyCertPath']);
    }
}

 

view

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
    <title>銀聯支付測試</title>
</head>
<body>
<h1>銀聯支付測試</h1>
<form action="{:U('Ypay/usespay')}" method="post">
    支付金額:<input type="text" name="money" value="0.1" />
    <input type="submit" value="肯定支付" />
</form>
</body>
</html>

 

最後再把證書文件下載就好了

相關文章
相關標籤/搜索