小程序支付

PHP之小程序支付

小程序支付,其实还是挺简单的, 把逻辑搞清楚,

前端代码看文档

https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=5

前端需要的参数,先调用 “ 预支付请求接口API” 生成订单返回参数,即可调起微信支付

在预支付订单API的时候,首先是统一下单的签名,对应好签名所需要的参数,统一下订单成功后,小程序还需签名, 小程序签名的数据切实看参数需要的是哪个, 代码注释很清楚,在这里背坑了一次

预支付订单完成,就是回调函数,回调函数切记不能有任何参数,最好单独一个文件

下面即使代码

class Pay
{
    protected function _initialize()
    {
        //微信支付参数配置(appid,商户号,支付秘钥)
        $config = array(
            'appid'         => 'xxxxxxxxxxxxxx',
            'mch_id'     => 'xxxxxxxx',
            'pay_api_key' => 'xxxxxxxxxxxxxxxxxxx',
        );
        $this->config = $config;
    }

    /**
     * 预支付请求接口(POST)
     * @author 
     * @version 
     * 
     * @param string $openid    openid
     * @param string $body      商品简单描述
     * @param string $order_sn  订单编号
     * @param string $total_fee 金额
     * @return  json的数据
     */
    public function prepay(){
        // openid JSAPI不能为空
        $openid = 'openid';
        $body = '充值';
        $order_sn = time() . '_' . $this->getNonceStr(5);
        // $total_fee = input('money') * 100; // 单位元
        $total_fee = 1; // 分

        //统一下单参数构造
        $param = array(
            'appid'             => $this->config['appid'],
            'mch_id'            => $this->config['mch_id'],
            'nonce_str'         => $this->getNonceStr(32),
            'body'              => $body,
            'out_trade_no'      => $order_sn,
            'total_fee'         => $total_fee,
            'spbill_create_ip'  => get_client_ip(),
            'notify_url'        => 'http://'.$_SERVER['HTTP_HOST'].'/api/notify/callback',
            'trade_type'        => 'JSAPI',
            'openid'            => $openid
        );

        // 统一下订单签名
        $param['sign'] = $this->makeSign($param);
        //同意下订单请求数据
        $xml = $this->arrayToXml($param);
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        $res = $this->curl_post_ssl($url, $xml);
        if(!$res){
            return $this->exit_json("Can't connect the server", 1);
        }

        $content = $this->xmlToArray($res);
        // 成功
        if ($content['return_code'] == 'SUCCESS' && $content['result_code'] == 'SUCCESS') {
            $content['ctime'] = (string)time();
            // 小程序签名,签名数据是下订单返回的数据!!!
            $params = [
                'appId'     => $this->config['appid'],
                'timeStamp' => $content['ctime'],
                'nonceStr'  => $content['nonce_str'],
                'package'   => 'prepay_id='.$content['prepay_id'],
                'signType'  => 'MD5',
            ];
            $params['paySign'] = $this->makeSign($params);
            $content['paySign'] = $params['paySign'];

            // 订单入库
            # code 数据库操作

            return $this->exit_json('订单已生成', 0, $content);

        } else if(strval($content['result_code']) == 'FAIL'){
            return $this->exit_json(strval($content['err_code_des'], 1));
        } else if(strval($content['return_code']) == 'FAIL'){
            return $this->exit_json(strval($content['return_msg']), 1 );
        } else {
            return $this->exit_json('失败', 1);
        }

    }
    
    
    /**
     * 进行支付接口(POST)
     * @author
     * @version 2018-02-03
     * 
     * @param string $prepay_id 预支付ID(调用prepay()方法之后的返回数据中获取)
     * @return  json的数据
     */
    // public function pay(){
    //     $config = $this->config;
    //     $prepay_id = I('post.prepay_id');
        
    //     $data = array(
    //         'appId'     => $config['appid'],
    //         'timeStamp' => time(),
    //         'nonceStr'  => $this->getNonceStr(),
    //         'package'   => 'prepay_id='.$prepay_id,
    //         'signType'  => 'MD5'
    //     );
        
    //     $data['paySign'] = $this->makeSign($data);
        
    //     $this->ajaxReturn($data);
    // }

    
    //微信支付回调验证
    public function notify(){
        $xml = $GLOBALS['HTTP_RAW_POST_DATA'];
        
        // 这句file_put_contents是用来查看服务器返回的XML数据 测试完可以删除了
        //file_put_contents(APP_ROOT.'/Statics/log2.txt',$res,FILE_APPEND);
        
        //将服务器返回的XML数据转化为数组
        $data = $this->xmlToArray($xml);
        // 保存微信服务器返回的签名sign
        $data_sign = $data['sign'];
        // sign不参与签名算法
        unset($data['sign']);
        $sign = $this->makeSign($data);
        
        // 判断签名是否正确  判断支付状态
        if ( ($sign===$data_sign) && ($data['return_code']=='SUCCESS') && ($data['result_code']=='SUCCESS') ) {
            $result = $data;
            //获取服务器返回的数据
            $order_sn = $data['out_trade_no'];            //订单单号
            $openid = $data['openid'];                    //付款人openID
            $total_fee = $data['total_fee'];            //付款金额
            $transaction_id = $data['transaction_id'];     //微信支付流水号
            
            //更新数据库
            $this->updateDB($order_sn,$openid,$total_fee,$transaction_id);
            
        }else{
            $result = false;
        }
        // 返回状态给微信服务器
        if ($result) {
            $str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
        }else{
            $str='<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>';
        }
        echo $str;
        return $result;
    }
// ----------------------用到的函数----------------------

    /**
     * 转化json数据,输出
     * @param  [type]  $msg   [description]
     * @param  integer $error [description]
     * @param  [type]  $data  [description]
     * @param  string  $ek    [description]
     * @return [type]         [description]
     */
    protected function exit_json($msg, $error = 1, $data = null, $ek = 'error'){
        $jsonArray = array ($ek => $error,'msg' => $msg);
        if(!empty($data))
        {
            $jsonArray['data'] = $data;
        }
        exit(json_encode($jsonArray));
    }
    /**
     * 将一个数组转换为 XML 结构的字符串
     * @author
     * @version 2018-02-03
     * 
     * @param array $arr 要转换的数组
     * @param int $level 节点层级, 1 为 Root.
     * @return string XML 结构的字符串
     */
    protected function arrayToXml($arr, $level = 1) {
        $s = $level == 1 ? "<xml>" : '';
        foreach($arr as $tagname => $value) {
            if (is_numeric($tagname)) {
                $tagname = $value['TagName'];
                unset($value['TagName']);
            }
            if(!is_array($value)) {
                $s .= "<{$tagname}>".(!is_numeric($value) ? '<![CDATA[' : '').$value.(!is_numeric($value) ? ']]>' : '')."</{$tagname}>";
            } else {
                $s .= "<{$tagname}>" . $this->array2xml($value, $level + 1)."</{$tagname}>";
            }
        }
        $s = preg_replace("/([\x01-\x08\x0b-\x0c\x0e-\x1f])+/", ' ', $s);
        return $level == 1 ? $s."</xml>" : $s;
    }
    
    /**
     * 将xml转为array
     * @author
     * @version 2018-02-03
     * 
     * @param  string   $xml xml字符串
     * @return array    转换得到的数组
     */
    protected function xmlToArray($xml){   
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $result= json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);        
        return $result;
    }
    
    /**
     * 产生随机字符串,不长于32位
     * @author
     * @version 2018-02-03
     * 
     * @param int $length
     * @return 产生的随机字符串
     */
    protected function getNonceStr($length = 32) {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";  
        $str ="";
        for ( $i = 0; $i < $length; $i++ )  {  
            $str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);  
        } 
        return $str;
    }


    /**
    * 生成签名
     * @author
     * @version 2018-02-03
     * 
     * @return 签名
     */
    protected function makeSign($data){
        //获取微信支付秘钥
        $key = $this->config['pay_api_key'];
        // 去空
        $data=array_filter($data);
        //签名步骤一:按字典序排序参数
        ksort($data);
        $string_a=http_build_query($data);
        $string_a=urldecode($string_a);
        //签名步骤二:在string后加入KEY
        //$config=$this->config;
        $string_sign_temp=$string_a."&key=".$key;
        //签名步骤三:MD5加密
        $sign = md5($string_sign_temp);
        // 签名步骤四:所有字符转为大写
        $result=strtoupper($sign);
        return $result;
    }
    
    /**
     * CURL微信支付发起请求(XML格式)
     * @author
     * @version 2018-02-03
     *
     * @return
     */
    protected function curl_post_ssl($url, $xmldata, $second=30,$aHeader=array()){
        $ch = curl_init();
        //超时时间
        curl_setopt($ch,CURLOPT_TIMEOUT,$second);
        curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1);
        //这里设置代理,如果有的话
        //curl_setopt($ch,CURLOPT_PROXY, '10.206.30.98');
        //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
        curl_setopt($ch,CURLOPT_URL,$url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,false);
        
     
        if( count($aHeader) >= 1 ){
            curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);
        }
     
        curl_setopt($ch,CURLOPT_POST, 1);
        curl_setopt($ch,CURLOPT_POSTFIELDS,$xmldata);
        $data = curl_exec($ch);
        if($data){
            curl_close($ch);
            return $data;
        }
        else { 
            $error = curl_errno($ch);
            echo "call faild, errorCode:$error\n"; 
            curl_close($ch);
            return false;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值