• PHP 第三方支付


    以前事情比较繁忙,压根都没有时间去整理最近的工作。

    最近稍微轻松点,就把自己在公司处理的支付业务拿出来,留个纪念,顺道回顾下以前自己支付的知识。

    俗话说实践是检验整理的唯一标准,东西做的是否能用,只能去实际检验。

    公司业务量可能是小点,去年9月初上线的东西,到现在充值也就是,我现查吧。。。。。。。。。。。。

    这种级别的充值量,可能确实小了点,不过最起码说明,代码处理的业务逻辑,基本上没有什么大的问题。

    行了,废话不说了,直接看以下介绍吧。

    支付的逻辑需要三步:

    (1)去合作的第三方拿到  商家号和秘钥 。

    (2)自己项目中集成第三方的SDK,完成支付。

    (3)第三方的异步通知,自己接收通知,修改订单状态。

    知道RSA加密的可以忽略:

    秘钥:RSA加密,需要生成,公钥publicKey 和私钥originKey

    规则: 私钥加密,公钥解密。 知道双方的公钥和私钥,就互相验证了。

    代码逻辑 【加密方式自己查看SDK,我们用的是RSA加密,所以就以它为例子】:

     (1)页面写好提交的方法,然后准备提交。

     
    //js获取页面数据,然后submit 建议:最好表单去提交
    function sub(){
    var
    pay_money = $("#pay_money").val(); var bank_code = $("input[name='bank']:checked").val(); $.ajax({ url:'/recharge/pay_data', type:'post', data:{'pay_money':pay_money,'bank_code':bank_code}, dataType:'json', async: false, success:function(e){ document.getElementById('order_no_number').value= e.order_no; if(e.sign !='' && e.sign_type !=''){
    //js动态创建表单,然后提交
    var form = $("<form method='post' id='myform'></form>"); form.attr({"action":"跳转地址"}); form.attr({"target":"_blank"}); for (arg in e) { var input = $("<input type='hidden'>"); input.attr({"name":arg}); input.val(e[arg]); form.append(input); } $("#form_hidden").append(form); $("#myform").submit(); } } })
    }

    (2)服务端,得按照SDK所需参数,配置好各种必须的参数,提供页面数据提交的参数。

     public function pay_data(){
            //银行代码和充值金额
            $bank_code = $_POST['bank_code'];
            $pay_money = $_POST['pay_money'];
    
            //充值数据
            $uid = $this->session->userdata('uid');
            if(!empty($uid) && !empty($bank_code) && !empty($pay_money)){
                $hkd = $pay_money; //港币(测试完成,放开)
                $hk_rate = $this->comm_func->getHkRate();//汇率,港币兑人民币
                $order_amount = $pay_money * $hk_rate; //人民币 = 港币 * 汇率
                $data['merchant_code'] = $this->config->item('merchant_code'); //配置中获取-商户号
                $data['service_type'] = 'direct_pay';
                $data['interface_version'] = 'V3.0';
                $data['input_charset'] = 'UTF-8';
                $data['notify_url'] = $this->config->item('web_notify_url');  //配置中获取-异步通知地址
                $data['return_url'] = $this->config->item('web_return_url');  //配置中获取-支付完成后的跳转地址
                $data['client_ip'] = $_SERVER["REMOTE_ADDR"];
                $data['order_no']   = self::get_orderno($uid);
                $data['order_time'] = date('Y-m-d H:i:s');
                $data['order_amount']  = sprintf('%01.2f',$order_amount);
                $data['product_name']  = '港币充值testpay';
                $data['bank_code'] = $bank_code;
                $sign_type ="RSA-S";
                $sign_str = $this->doSign($data,$this->config->item('origPriKey'));//配置中获取-RSA加密的私钥
                $pay_type = 1;
                //插入订单记录
                $sql = "INSERT INTO tb_pay (`uid`,`order_no`,`order_time`,`order_amount`,`hkd`,`rate`,`pay_type`,`bank_code`) VALUES('{$uid}','{$data['order_no']}','{$data['order_time']}','{$data['order_amount']}','{$hkd}','{$hk_rate}','{$pay_type}','{$data['bank_code']}')";
                try {
                    $this->db->query($sql);
                    if ($this->db->affected_rows() <=0) {
                        self::write_log('sql未起效:'.$sql,'web_pay_err');
                    }
                } catch (Exception $e) {
                    self::write_log('sql报错:'.$e,'web_pay_err');
                }
                //支付请求日志
                self::write_log('返回数据:uid='.$uid.' data='.json_encode($data)."sign_str=".$sign_str,'web_get_paydata');
                $data['sign'] = $sign_str;
                $data['sign_type'] = $sign_type;
                echo json_encode($data);
            }
        }

    (3)服务端,处理异步通知--这个是重点,做好各种处理措施。

    /*
         * 服务器通知notify_url
         */
        public function notify_url(){
            $notifyStr = json_encode($_POST);
            self::write_log($notifyStr,'web_notify_pay');//记录日志
            $signArr = $_POST;
            unset($signArr['sign'],$signArr['sign_type']);
            $dinpaySign = base64_decode($_POST["sign"]);
            $sign = $this->CheckSign($signArr,$dinpaySign,$this->config->item('PublicPriKey'));   //获取验证的公钥
    
            if(isset($_POST['order_no']) && isset($_POST['sign']) && $sign)
            {
    
                $DB_LOCK = $this->load->database('lock_tb', TRUE);
                $order_no = isset($_POST['order_no'])?$_POST['order_no']:'';
                $trade_no = isset($_POST['trade_no'])?$_POST['trade_no']:'';
                $trade_time = isset($_POST['trade_time'])?$_POST['trade_time']:'';
                $trade_status = isset($_POST['trade_status'])?$_POST['trade_status']:'';
                //$bank_code = isset($_POST['bank_code'])?$_POST['bank_code']:'';
                $bank_seq_no = isset($_POST['bank_seq_no'])?$_POST['bank_seq_no']:'';
                $trade_amount = isset($_POST['order_amount'])?$_POST['order_amount']:'';

    //-----开启事物处理
    $DB_LOCK->trans_start();
    //mysql行级锁,处理完成,释放锁
    $query = $DB_LOCK->query("SELECT * FROM `tb_pay` WHERE order_no = '{$_POST['order_no']}' FOR UPDATE"); $row = $query->row_array(); if (isset($row) && !empty($row)) { //有且还未返回状态 更新 if (isset($row['trade_status']) && $row['trade_status']==0) { $trade_status_field = ($trade_status == 'SUCCESS')?1:2; $upSql = "UPDATE tb_pay SET `trade_time` = '{$trade_time}',`trade_no` = '{$trade_no}',`trade_amount` = '{$trade_amount}',`trade_status`='{$trade_status_field}',`bank_seq_no`='{$bank_seq_no}',`back_time`='".date('Y-m-d H:i:s')."' WHERE `order_no` = '{$order_no}'"; try { $DB_LOCK->query($upSql); if ($DB_LOCK->affected_rows() <=0) { self::write_log('sql未起效:'.$upSql,'web_pay_err'); }else{ $this->update_money($row['uid'],$row['hkd']); } } catch (Exception $e) { self::write_log('sql报错:'.$e,'web_pay_err'); } } //已有返回状态 if (isset($row['trade_status']) && $row['trade_status']!=0) { $trade_status_field = ($trade_status == 'SUCCESS')?1:2; if ($trade_status_field == $row['trade_status'] && $trade_amount == $row['trade_amount']) { //重复发送 self::write_log('重复异步通知:'.$notifyStr,'web_pay_err'); }else { //两次数据不一致 self::write_log('重要bug:与已有数据冲突:'.$notifyStr,'web_pay_err'); } } }else { //无此记录 self::write_log('订单库中无此记录:'.$notifyStr,'web_pay_err'); } $DB_LOCK->trans_complete();
    //----事物结束,处理失败,回滚业务
    echo 'SUCCESS'; //没有问题,打印success,处理成功 }else { //验签失败 self::write_log('验签失败:'.$notifyStr.'sign:'.$sign,'web_pay_err'); } }

    代码所用的一些方法:

         /*生成订单号
         * @uid
         */
        public static function get_orderno($id)
        {
            $rand = date('ymd').substr(time(),-5).substr(microtime(),2,7);
            $fix = substr(md5($id),0,7);
            return $rand.$fix;
        }
    
        /**
         * sign
         * */
        public function doSign($inA,$origPriKey)
        {
            if(empty($inA))
                return false;
            $signStr = $this->doSort($inA);
            $priKey = openssl_get_privatekey($origPriKey);
            openssl_sign($signStr,$sign_info,$priKey,OPENSSL_ALGO_MD5);
            $sign = base64_encode($sign_info);
            return $sign;
        }
        /*
         * sort
         */
        private function doSort($param)
        {
            $signStr = '';
            ksort($param);
            foreach($param as $k => $v){
                $signStr .= $k."=".$v."&";
            }
            $signStr = rtrim($signStr,'&');
            self::write_log("web_send_signStr::".$signStr,'web_send_pay_signStr');
            return $signStr;
        }
    
        /*
         * 验证notify中的签名
         */
        public function CheckSign($inA,$dinpaySign,$pubKey){
            if(empty($inA))
                return false;
            $param = $inA;
            $signStr = '';
            ksort($param);
            foreach($param as $k => $v){
                $signStr .= $k."=".$v."&";
            }
            $signStr = rtrim($signStr,'&');
          
            $priKey = openssl_get_publickey($pubKey);
            $sign = openssl_verify($signStr,$dinpaySign,$priKey,OPENSSL_ALGO_MD5);
            if($sign){
                return true;
            }else{
                return false;
            }
        }
  • 相关阅读:
    2014阿里巴巴面试题哈尔滨
    Hadoop分布式集群配置
    Ubuntu上搭建Hadoop环境(单机模式+伪分布模式)
    安装VMware Workstation提示the msi failed的解决办法
    Scalaz(35)- Free :运算-Trampoline,say NO to StackOverflowError
    Scalaz(34)- Free :算法-Interpretation
    Scalaz(33)- Free :算式-Monadic Programming
    Scalaz(32)- Free :lift
    Scalaz(31)- Free :自由数据结构-算式和算法的关注分离
    Scalaz(30)- Free :Natural Tranformation ~>
  • 原文地址:https://www.cnblogs.com/hanybblog/p/6899143.html
Copyright © 2020-2023  润新知