thinkphp5.1接入银联支付

银联支付,tp5.1银联支付

最近项目中要用到银联支付,所以记录一下开发过程:

1.先去银联申请接口https://open.unionpay.com/tjweb/api/dictionary?apiSvcId=453,我用的是手机网页支付(WAP支付)

2.去个人中心查看产品和测试参数

68/e2d1e7dd3a5cddad3d248872900f12.png


15/cbb467bd2e7d6db7877ec2664fe7fc.png

这些测试数据写到config.php里,稍后会用到。

    //银联支付部分配置信息(具体配置查看extend/unionpay/sdk/acp_sdk.ini)

    'unionpay_config'        => [

        'merId'              => '',//商户代码

        'configFile'         => Env::get('root_path') . 'config/acp_sdk.ini',//银联配置

    ],

3.下载银联官方实例:https://open.unionpay.com/tjweb/acproduct/list?apiSvcId=453

4.我的是放到extend里了:

b6/aa7e54f03b0922d76ad291ae52a5bd.jpg

重命名一下文件,在这些文件头部加上命名空间:namespace unionpay\sdk;

5.在application/common.php里面引入

use unionpay\sdk\SDKConfig;

use unionpay\sdk\AcpService;

然后就可以写方法调用了:

/**

 * 银联支付

 * @param array $data付款参数

 * @return string

 */

function unionpay($order){

    $SDKConfig              = new SDKConfig();

    $params                 = array(  

        //以下信息非特殊情况不需要改动

        'version'           => $SDKConfig->getSDKConfig()->version,                 //版本号

        'encoding'          => 'utf-8',                //编码方式

        'txnType'           => '01',                    //交易类型

        'txnSubType'        => '01',                 //交易子类

        'bizType'           => '000201',                //业务类型

        'frontUrl'          => $SDKConfig->getSDKConfig()->frontUrl,  //前台通知地址

        'backUrl'           => $SDKConfig->getSDKConfig()->backUrl,     //后台通知地址

        'signMethod'        => $SDKConfig->getSDKConfig()->signMethod,               //签名方法

        'channelType'       => '08',                //渠道类型,07-PC,08-手机

        'accessType'        => '0',                  //接入类型

        'currencyCode'      => '156',              //交易币种,境内商户固定156

        //TODO 以下信息需要填写

        'merId'             => $order['merId'],     //商户代码,请改自己的测试商户号,此处默认取demo演示页面传递的参数

        'orderId'           => $order["orderId"], //商户订单号,8-32位数字字母,不能含“-”或“_”,此处默认取demo演示页面传递的参数,可以自行定制规则

        'txnTime'           => date("YmdHis",$order["txnTime"]), //订单发送时间,格式为YYYYMMDDhhmmss,取北京时间,此处默认取demo演示页面传递的参数

        'txnAmt'            => $order["txnAmt"],   //交易金额,单位分,此处默认取demo演示页面传递的参数

        // 订单超时时间。

        // 超过此时间后,除网银交易外,其他交易银联系统会拒绝受理,提示超时。 跳转银行网银交易如果超时后交易成功,会自动退款,大约5个工作日金额返还到持卡人账户。

        // 此时间建议取支付时的北京时间加15分钟。

        // 超过超时时间调查询接口应答origRespCode不是A6或者00的就可以判断为失败。

        'payTimeout'        => date('YmdHis', strtotime('+15 minutes')), 

        'riskRateInfo'      => $order["riskRateInfo"],

        // 请求方保留域,

        // 透传字段,查询、通知、对账文件中均会原样出现,如有需要请启用并修改自己希望透传的数据。

        // 出现部分特殊字符时可能影响解析,请按下面建议的方式填写:

        // 1. 如果能确定内容不会出现&={}[]"'等符号时,可以直接填写数据,建议的方法如下。

        //    'reqReserved' =>'透传信息1|透传信息2|透传信息3',

        // 2. 内容可能出现&={}[]"'符号时:

        // 1) 如果需要对账文件里能显示,可将字符替换成全角&={}【】“‘字符(自己写代码,此处不演示);

        // 2) 如果对账文件没有显示要求,可做一下base64(如下)。

        //    注意控制数据长度,实际传输的数据长度不能超过1024位。

        //    查询、通知等接口解析时使用base64_decode解base64后再对数据做后续解析。

        //    'reqReserved' => base64_encode('任意格式的信息都可以'),

        //TODO 其他特殊用法请查看 special_use_purchase.php

    );

    $AcpService             = new AcpService();

    $AcpService->sign($params);

    $uri                    = $SDKConfig->getSDKConfig()->frontTransUrl;

    $html_form              = $AcpService->createAutoFormHtml($params, $uri);

    echo $html_form;

}

/**

 * 银联支付验证签名

 * @param array $data付款参数

 * @return bool 

 */

function checkUnionpaySign($data){

    $AcpService             = new AcpService();

    return  $AcpService->validate($data);

}

/**

 * 银联支付查询订单

 * @param array $data付款参数

 * @return bool

 */

function checkUnionpayOrder($data){

    $SDKConfig              = new SDKConfig();

    $params                 = array(

        //以下信息非特殊情况不需要改动

        'version'           => $SDKConfig->getSDKConfig()->version,         //版本号

        'encoding'          => 'utf-8',        //编码方式

        'signMethod'        => $SDKConfig->getSDKConfig()->signMethod,       //签名方法

        'txnType'           => '00',            //交易类型

        'txnSubType'        => '00',         //交易子类

        'bizType'           => '000000',        //业务类型

        'accessType'        => '0',          //接入类型

        'channelType'       => '07',        //渠道类型

        //TODO 以下信息需要填写

        'orderId'           => $data["orderId"], //请修改被查询的交易的订单号,8-32位数字字母,不能含“-”或“_”,此处默认取demo演示页面传递的参数

        'merId'             => $data["merId"], //商户代码,请改自己的测试商户号,此处默认取demo演示页面传递的参数

        'txnTime'           => date("YmdHis",$data["txnTime"]), //请修改被查询的交易的订单发送时间,格式为YYYYMMDDhhmmss,此处默认取demo演示页面传递的参数

    );

    $AcpService             = new AcpService();

    $AcpService->sign($params);// 签名

    $url                    = $SDKConfig->getSDKConfig()->singleQueryUrl;

    $result_arr             = $AcpService->post($params, $url);

    //没收到200应答的情况

    if(count($result_arr) <= 0) { 

        return ['status'=>0,'msg'=>'无应答'];

    }

    //应答报文验签失败

    if (!$AcpService->validate ($result_arr) ){

        return ['status'=>2,'msg'=>'应答报文验签失败'];

    }

    if ($result_arr["respCode"] == "00"){

        if ($result_arr["origRespCode"] == "00"){

            //交易成功

            //TODO

            return ['status'=>1,'msg'=>'交易成功'];

        } else if ($result_arr["origRespCode"] == "03"

                || $result_arr["origRespCode"] == "04"

                || $result_arr["origRespCode"] == "05"){

            //后续需发起交易状态查询交易确定交易状态

            //TODO

            return ['status'=>3,'msg'=>'后续需发起交易状态查询交易确定交易状态'];

        } else {

            //其他应答码做以失败处理

            //TODO

            return ['status'=>4,'msg'=>'交易失败:'. $result_arr["origRespMsg"]];

        }

    } else if ($result_arr["respCode"] == "03"

            || $result_arr["respCode"] == "04"

            || $result_arr["respCode"] == "05" ){

        //后续需发起交易状态查询交易确定交易状态

        //TODO

        return ['status'=>5,'msg'=>'处理超时,请稍微查询。'];

    } else {

        //其他应答码做以失败处理

        //TODO

        return ['status'=>6,'msg'=>'失败:'. $result_arr["respMsg"]];

    }

}


6.然后在需要的地方就可以直接调用了:

/**

* 调用银联支付

* @param int $id 订单ID

* @return html 跳转到银联支付页面或调起云闪付app

*/

    public function pay()

    {

          $id = input('id');

          $row = Db::name('order')->where('id',$id)->find();

          $uniopayData = [];

          $uniopayData['merId'] = config('app.unionpay_config.merId');

          $uniopayData['orderId'] = $row['order_id'];

          $uniopayData['txnAmt'] = $row['money'];

          $uniopayData['txnTime'] = time();

          $uniopayData['riskRateInfo'] = '{commodityName=test}';

           $tempRow = Db::name('unionpay')->where(['orderId'=>$row['order_id']])->find();

          if(!$tempRow){

               $id = Db::name('unionpay')->insertGetId($uniopayData);

               if($id){

                   unionpay($uniopayData);

              }else{

                   exit('请求失败,请联系客服');

             }

          }else{

               unionpay($uniopayData);

          }

 }


/**

* 银联支付异步回调

*/

    public function unionpayNotify(){

          $notifyData = $_POST;

           logStr("支付回调POST返回值:".json_encode($notifyData));

           //判断订单是否存在

          if(isset($notifyData['orderId'])){

               $notifyRow = Db::name('unionpay_notify')->where(['orderId'=>$notifyData['orderId']])->find();

              if(!$notifyRow){

                  $notifyData['time'] = time();

                  Db::name('unionpay_notify')->insertGetId($notifyData);

                 //验证签名

                 if (isset($_POST['signature'])){

                    $signCheck = checkUnionpaySign($_POST);

                    logStr("支付回调POST验证签名:".$signCheck);

                    //验证签名成功的话,查询一下订单,查询成功更新数据状态

                   if($signCheck){

                        $orderId = $_POST ['orderId']; //其他字段也可用类似方式获取

                        $respCode = $_POST ['respCode'];

                        logStr("支付回调POST状态码:".$respCode.($respCode == "00"));

                       //状态代码

                      if($respCode == "00"){

                           $unionpayRow = Db::name('unionpay')->where('orderId',$orderId)->find();

                          //数据存在且状态为未支付

                          if(!empty($unionpayRow) && $unionpayRow['status'] == 0){

                              //查询订单信息

                              $res = checkUnionpayOrder($unionpayRow);

                              logStr("支付回调POST查询订单:".json_encode($res));

                              if($res['status'] == 1){

                                  //更新支付状态

                                  Db::name('unionpay')->where('orderId',$orderId)->update(['status'=>1]);

                              }

                          }

                    }

                 }

            }

       }else{

                if($notifyData['respCode'] != $notifyData['respCode']){

                     $notifyData['time'] = time();

                    Db::name('unionpay_notify')->where(['orderId'=>$notifyData['orderId']])->update($notifyData);

                }

        }

      }

   }