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

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

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

公司业务量可能是小点,去年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;
        }
    }

 

版权声明:本文为hanybblog原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/hanybblog/p/6899143.html