首页 > 技术文章 > TP3.2.3 接入银联支付

liubaoqing 2017-12-08 10:18 原文

TP3.2.3 接入银联支付

    项目接入银联支付的过程, 在此记录下,希望能帮助开发盆友平坑。

    银联SKD链接:https://open.unionpay.com/ajweb/product/newProDetail?proId=1&cataId=14

    首先我们先下载官方提供的SDK  ,下载好了解压选择版本 ,里面有PHP  java  .net  这里我们自然是选择PHP版本的,接入前我们先配环境,它要我们PHP

  的版本 在5.3以上,并且需开启环境的curl、openssl功能。

   然后就是它提供的测试证书了,默认在window系统是放在D:/certs ,意思是在你的电脑的D创建一个名为certs 的文件夹,然后将4个证书放进去,测试的名为

  acp_test_enc.cer  acp_test_middle.cer  acp_test_root.cer  acp_test_sign.pfx ,在创建一个名为logs文件夹D:/logs/  放支付生成的日志文件,linux中请

 修改成Linux中的路径。

  如图:

  

    你可以在assets文件夹中找到你要的证书,测试环境官方提供四个证书,生产环境官方提供三个,还个签名证书就是后缀为.pfx 的需要你去你的银联那里申请

  下载对应的你还需要可以需要解签名证书的密码 和商户号 , 这些后面都会提到的。

    在然后我们将名为SDK文件夹中的六个文件放到我们项目放到 ThinkPHP\Library\Vendor\Yunpay 的文件夹中

    

     写逻辑代码前你还得前配置好你的acp_sdk.ini 文件 ,文件里面都写的很清楚,这里就不一一解释了,这里是我的支付代码

    

//银联充值操作
    public function pay()
    {
        header ( 'Content-type:text/html;charset=utf-8' );
        Vendor('Yunpay.acp_service');

        //前台通知地址
        $frontUrl = "http://".I("server.HTTP_HOST")."/Assets/rechargedetail";
        //后台通知地址
        $backUrl = "http://".I("server.HTTP_HOST");
        $params = array(
            //以下信息非特殊情况不需要改动
            'version' => \com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->version,          //版本号
            'encoding' => 'utf-8',                                                              //编码方式
            'txnType' => '01',                                                                  //交易类型
            'txnSubType' => '01',                                                              //交易子类
            'bizType' => '000201',                                                              //业务类型
            'frontUrl' =>  $frontUrl,                                                          //前台通知地址
            'backUrl' =>   $backUrl,                                                          //后台通知地址
            'signMethod' => \com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->signMethod,    //签名方法
            'channelType' => '08',                                                             //渠道类型,07-PC,08-手机
            'accessType' => '0',                                                                //接入类型
            'currencyCode' => '156',    
             // 超过超时时间调查询接口应答origRespCode不是A6或者00的就可以判断为失败。
               'payTimeout' => date('YmdHis', strtotime('+15 minutes'))                         //订单发送时间
        );
        $txnAmt = I('post.txnAmt');    //交易金额
        $orderId = I('post.orderId');  //商户订单号

        //加入商户参数
        $params['txnAmt'] = $txnAmt*100;
        $params['merId'] = C('Yunpay.merId');    //商户号
        $params['orderId'] = $orderId;
        $params['txnTime'] = date('YmdHis');

        //商品描述,可空
        $body = trim(I('post.WIDbody'));
        $ud = session('users.uid');
        $data = array(
                'uid'=>$ud,                                  //用户id
                'win_code'=>$orderId,                     //商户订单号
                'winsubject'=>I('post.WIDsubject'),          //订单名称
                'wintotal_amount'=>$txnAmt,                //付款金额
                'winbody'=>I('post.WIDbody'),             //商品描述
                'state'=>'yl',                              //支付方式
                'status'=>'0',                              //是否支付
                'ordertime'=>time()                          //交易时间
            );
        M("pay_record")->add($data); // 保存交易信息
        \com\unionpay\acp\sdk\AcpService::sign ( $params );
        $uri = \com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->frontTransUrl;
        $html_form = \com\unionpay\acp\sdk\AcpService::createAutoFormHtml( $params, $uri );
        echo $html_form;
    
    }

    这里我曾试过删除里面的命名空间用 new的方式去写,这样可以简洁代码,但是后面异步的时候报错,客服说不能删除命名空间,这样会导致方法名重复,所以还是乖乖的用demo

  中提供的方法。

    异步方法--------

    

  1     //银联充值异步
  2     public function xxx()
  3     {    
  4         Vendor('Yunpay.acp_service');
  5         $logger = \com\unionpay\acp\sdk\LogUtil::getLogger();
  6         $logger->LogInfo("receive back notify: " . \com\unionpay\acp\sdk\createLinkString ( $_POST, false, true ));
  7         if (isset ( $_POST ['signature'] )) {
  8                 // echo \com\unionpay\acp\sdk\AcpService::validate ( $_POST ) ? '验签成功' : '验签失败';
  9                 $respCode = I('post.respCode');
 10                 $orderId = I('post.orderId');         // 商户订单号
 11                 $total_amount = I('post.settleAmt'); //订单金额
 12                 $trade_no = I('post.queryId');         // queryId 银联唯一标识一笔交易
 13 
 14                 //判断respCode=00、A6后,对涉及资金类的交易,请再发起查询接口查询,确定交易成功后更新数据库。
 15                 if( $respCode=='00' ){
 16                      $this->unionpay($orderId,$total_amount,$trade_no);
 17                 }else{
 18                      $res = $this->confirmpay($orderId,'1');
 19                         if( $res == 'Successful' ){
 20                             $this->unionpay($orderId,$total_amount,$trade_no);
 21                      } else {
 22                             echo '交易失败';
 23                      }
 24                 }
 25 
 26         } else {
 27             echo '签名为空';
 28         }
 29 
 30 
 31     }
 32 
 33 
 34     //银联充值
 35     public function unionpay($orderId,$total_amount,$trade_no)
 36     {
 37         $per = M("pay_record")->where('win_code='.$orderId)->find(); //查找该订单
 38         if( $per['status']=='1' ){
 39             echo '已充值';
 40             return;
 41         }
 42         $Pay = M("pay");
 43         // 在Pay模型中启动事务
 44         $Pay->startTrans();            
 45         // 进行相关的业务逻辑操作
 46         $res = $Pay->where('uid='.$per['uid'])->setInc('money',$total_amount/100);
 47         //数据组合
 48         $data = array(
 49                 'alipay_number'=>$trade_no,                  //银联唯一标识
 50                 'status'=>'1',                              //交易状态
 51                 'paytime'=>time()                          //交易时间
 52             );
 53         M("pay_record")->where('win_code='.$orderId)->save($data); // 修改交易信息
 54 
 55         if (!empty($res)){
 56           // 提交事务
 57           $Pay->commit();
 58         }else{
 59           // 事务回滚
 60           $Pay->rollback();
 61         }
 62     }
 63 
 64 
 65     //确定是否充值操作
 66     public function confirmpay($orderId,$L)
 67     {
 68         header ( 'Content-type:text/html;charset=utf-8' );
 69          Vendor('Yunpay.acp_service');
 70          $params = array(
 71             //以下信息非特殊情况不需要改动
 72             'version' => \com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->version,          //版本号
 73             'encoding' => 'utf-8',          //编码方式
 74             'signMethod' => \com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->signMethod,          //签名方法
 75             'txnType' => '00',              //交易类型
 76             'txnSubType' => '00',          //交易子类
 77             'bizType' => '000000',          //业务类型
 78             'accessType' => '0',          //接入类型
 79             'channelType' => '07',          //渠道类型
 80         );
 81         if($L == '0'){
 82             $time = M("order_pay")->where('win_code = "'.$orderId.'"')->find()['addtime'];
 83         }else{
 84             $time = M("pay_record")->where('win_code = "'.$orderId.'"')->find()['ordertime'];
 85         }
 86         $params['merId'] = C('Yunpay.merId');       //商户号
 87         $params['orderId'] = $orderId;               //交易的订单号
 88         $params['txnTime'] = date('YmdHis',$time); //订单发送时间
 89 
 90         \com\unionpay\acp\sdk\AcpService::sign ( $params ); // 签名
 91         $url = \com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->singleQueryUrl;
 92 
 93         $result_arr = \com\unionpay\acp\sdk\AcpService::post ( $params, $url);
 94         if(count($result_arr)<=0) { //没收到200应答的情况
 95             return 'No200';
 96         }
 97         if (!\com\unionpay\acp\sdk\AcpService::validate ($result_arr) ){
 98             return "应答报文验签失败";
 99         }
100         if ($result_arr["respCode"] == "00"){
101             if ($result_arr["origRespCode"] == "00"){
102                 //交易成功
103                 //TODO
104                 return "Successful";
105             } else if ($result_arr["origRespCode"] == "03"
106                     || $result_arr["origRespCode"] == "04"
107                     || $result_arr["origRespCode"] == "05"){
108                 //后续需发起交易状态查询交易确定交易状态
109                 //TODO
110                 return "交易处理中,请稍微查询";
111             } else {
112                 //其他应答码做以失败处理
113                 //TODO
114                 return "交易失败:" . $result_arr["origRespMsg"];
115             }
116         } else if ($result_arr["respCode"] == "03"
117                 || $result_arr["respCode"] == "04"
118                 || $result_arr["respCode"] == "05" ){
119             //后续需发起交易状态查询交易确定交易状态
120             //TODO
121             return "处理超时,请稍微查询";
122         } else {
123             //其他应答码做以失败处理
124             //TODO
125             return "失败:" . $result_arr["respMsg"];
126         }
127 
128     }

    这里第一个方法xxx 中的respCode等于00 就是支付成功 ,如果没有需要根据你生成的订单号在次查询在结果。这里客服说这种失败不好模拟,就不说了,但是这操作方法还是

  建议写下,以防万一 。

    最后说明下几个参数 queryId  银联唯一标识,需要保存, 还有银联支付是按 '分' 做单位的 所以支付跳转前  假如是1元,你得乘以100,它才可以识别为1元,要不然就是0.01元

  然后异步到你的时候,如果你是元的单位在除于100,如果是分就不用了。

    搞定收工 , 祝大家早日成为大牛

  

    

 

 

    

  

  

  

 

推荐阅读