微信APP支付demo

<?php
namespace App\Controller;
use Think\Controller;
/**
 * 微信支付控制器
 */
class WxPayController extends HomeController
{

    protected $mchid; // 微信支付商户号 PartnerID 通过微信支付商户资料审核后邮件发送
    protected $appid; //公众号APPID 通过微信支付商户资料审核后邮件发送
    protected $key; //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥
    protected $notify; //微信异步回调地址
    protected $main_url; //接口访问主地址(可根据自己项目取舍)
    /**
     * @param string $openid 调用【网页授权获取用户信息】接口获取到用户在该公众号下的Openid
     * @param float $totalFee 收款总费用 单位元
     * @param string $outTradeNo 唯一的订单号
     * @param string $orderName 订单名称
     * @param string $notifyUrl 支付结果通知url 不要有问号
     * @param string $spbillIp 操作IP
     *   https://mp.weixin.qq.com/ 微信支付-开发配置-测试目录
     *   测试目录 http://mp.izhanlue.com/paytest/  最后需要斜线,(需要精确到二级或三级目录)
     * @return string
     */

    /**
     * 初始化方法
     */

    public function _initialize() {
        $this->mchid = '9999999999'; // 微信支付商户号 PartnerID 通过微信支付商户资料审核后邮件发送
        $this->appid = 'wxff9999999999'; //公众号APPID 通过微信支付商户资料审核后邮件发送
        $this->key = 'Demo9999999999';   //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥

        // 设置自定义行为
        \Think\Hook::add('paystatus', 'Home\\Behaviors\\PaystatusBehavior');
    }

    public function wxpay(){
        $uid=$_POST['uid'];
        $orderid=$_POST['orderid'];
        $where['uid']=$uid;
        $where['orderid']=$orderid;
        $order_info=M('order')->where($where)->find();
        if(empty($order_info)){
            $msg=array('code'=>'300','msg'=>'订单不存在');
            $this->ajaxReturn($msg);
        }
        $outTradeNo=$order_info['tag'].time();
        //$totalFee=$order_info['total_money']*100;
        $totalFee=$order_info['total_money']*100;
        //$outTradeNo=201801151686367077;
        $total = round(floatval($totalFee),2);
        if (!$total) {
            $total = 0;
        }
        $notifyUrl = "https://".$_SERVER['HTTP_HOST']."/App/wxpay/wx_notify";
        $orderName = "订单编号:".$order_info['orderid'];
        $spbillIp = $_SERVER['SERVER_ADDR'];
        $this->createJsBizPackage($total, $outTradeNo, $orderName, $notifyUrl, $spbillIp);
    }

    protected function createJsBizPackage($totalFee, $outTradeNo, $orderName, $notifyUrl, $spbillIp) {
        $config = array(
            'mch_id' => $this->mchid,
            'appid' => $this->appid,
            'key' => $this->key,
        );
        $unified = array(
            'appid' => $config['appid'],
            'body' => $orderName,
            'mch_id' => $config['mch_id'],
            'nonce_str' => $this->createNonceStr(),
            'notify_url' => $notifyUrl,
            'out_trade_no' => $outTradeNo,
            'spbill_create_ip' => $spbillIp,
            'total_fee' => intval($totalFee * 1), //单位 转为分
            'trade_type' => 'APP',
        );
        $unified['sign'] = $this->getSign($unified, $config['key']); //第一次签名
        //print_R($this->arrayToXml($unified));
        $responseXml = $this->curlPost('https://api.mch.weixin.qq.com/pay/unifiedorder', $this->arrayToXml($unified));
        $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA);
        //print_r($unifiedOrder);die;
        if ($unifiedOrder === false) {
            $this->error('parse xml error');
            die('parse xml error');
        }
        if ($unifiedOrder->return_code != 'SUCCESS') {
            $this->error($unifiedOrder->return_msg);
            die($unifiedOrder->return_msg);
        }
        if ($unifiedOrder->result_code != 'SUCCESS') {
            $this->error($unifiedOrder->err_code);
            die($unifiedOrder->err_code);
        }
        //$unifiedOrder->trade_type 交易类型 调用接口提交的交易类型,取值如下:JSAPI,NATIVE,APP
        //$unifiedOrder->prepay_id 预支付交易会话标识 微信生成的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时
        //$unifiedOrder->code_url 二维码链接 trade_type为NATIVE是有返回,可将该参数值生成二维码展示出来进行扫码支付
        //该数组内容是需要二次签名返回给客服端的,必须有时间戳,键名命名不能用驼峰法,不能有下划线或其他任何符号
        //而且只能是微信要求的这个参数参与签名,其余的不能参与签名,否则客服端会报签名错误
        //以下是标准命名,已在线上测试通过
        /*$nonce_str = $this->toArray($unifiedOrder->nonce_str);
        print_R($nonce_str);die;
        $prepay_id = $this->toArray($unifiedOrder->prepay_id);*/
        $nonce_str = $unifiedOrder->nonce_str;
        $nonce_str = (String)$nonce_str;
        $prepay_id = $unifiedOrder->prepay_id;
        $prepay_id = (String)$prepay_id;
        $arr = array(
            "appid" => $config['appid'],
            "partnerid" => $config['mch_id'],
            "timestamp" => time(),
            "noncestr" => $nonce_str,
            "prepayid" => $prepay_id,
            "package" => 'Sign=WXPay',
        );
        $arr['sign'] = $this->getSign($arr, $config['key']); //第二次签名
        $arr['outtradeno'] = $unified['out_trade_no']; //返回商户订单号
        //print_R($arr);die;
        $this->ajaxReturn($arr) ;
    }


    public static function toArray($simplexml_obj, $array_tags=array(), $strip_white=1)
    {
        if( $simplexml_obj )
        {
            if( count($simplexml_obj)==0 ){
                return $strip_white?trim((string)$simplexml_obj):(string)$simplexml_obj;
            }

            $attr = array();
            foreach ($simplexml_obj as $k=>$val) {
                if( !empty($array_tags) && in_array($k, $array_tags) ) {
                    $attr[] = self::toArray($val, $array_tags, $strip_white);
                }else{
                    $attr[$k] = self::toArray($val, $array_tags, $strip_white);
                }
            }
            return $attr;
        }

        return false;
    }
    /**
     * 生成随机字符串
     * @param type $length 设置随机长度,默认32位
     */
    protected function createNonceStr($length = 32) {
        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        $str = '';
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * 获取签名
     * @param type $params 签名的对象
     * @param type $key 签名key  |_mC`3^+z!kDlN>U~5cB{fcWSqZt|>
    /V-P|
     */
    protected function getSign($params, $key) {
        ksort($params, SORT_STRING);
        $unSignParaString = $this->formatQueryParaMap($params, false);
        $signStr = strtoupper(md5($unSignParaString . "&key=" . $key));
        return $signStr;
    }

    /**
     * 签名规则
     * @param type $paraMap
     * @param type $urlEncode
     * @return type
     */
    private function formatQueryParaMap($paraMap, $urlEncode = false) {
        $buff = "";
        ksort($paraMap);
        foreach ($paraMap as $k => $v) {
            if (null != $v && "null" != $v) {
                if ($urlEncode) {
                    $v = urlencode($v);
                }
                $buff .= $k . "=" . $v . "&";
            }
        }
        $reqPar = '';
        if (strlen($buff) > 0) {
            $reqPar = substr($buff, 0, strlen($buff) - 1);
        }
        return $reqPar;
    }

    /**
     * curl post
     * @param type $url
     * @param type $postData
     * @param type $options
     * @return type
     */
    protected function curlPost($url = '', $postData = '', $options = array()) {
        if (is_array($postData)) {
            $postData = http_build_query($postData);
        }
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数
        if (!empty($options)) {
            curl_setopt_array($ch, $options);
        }
        //https请求 不验证证书和host
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        $data = curl_exec($ch);
        curl_close($ch);
        return $data;
    }
    /**
     * curl get
     * @param string $url
     * @param array $options
     * @return mixed
     */
    protected function curlGet($url = '', $options = array()) {
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        if (!empty($options)) {
            curl_setopt_array($ch, $options);
        }
        //https请求 不验证证书和host
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        $data = curl_exec($ch);
        curl_close($ch);
        return $data;
    }

    /**
     * 数组转化为XML
     * @param type $arr
     * @return string
     */
    protected function arrayToXml($arr) {
        $xml = "<xml>";
        foreach ($arr as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
        }
        $xml .= "</xml>";
        return $xml;
    }

    public function logResult($postStr){
        $world = date("Y-m-d h:i:s",time());
        $file  = $_SERVER['DOCUMENT_ROOT'].'/log/Wxpay_app_notify.txt';
        date_default_timezone_set("PRC");
        $fp = fopen($file,"a");
        flock($fp, LOCK_EX) ;
        fwrite($fp,"执行日期:".print_r($postStr,true)."\n".$world."\n");
        flock($fp, LOCK_UN);
        fclose($fp);
    }

    function xmlToArray($xml){

        //禁止引用外部xml实体

        libxml_disable_entity_loader(true);

        $xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);

        $val = json_decode(json_encode($xmlstring),true);

        return $val;

    }


    /**
     * 微信回调地址
     * @return type
     * 通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒
     */
    public function wx_notify(){
        //echo "123";die;
        $config = array(
            'mch_id' => '999999999',
            'appid' => 'wxff99999999999',
            'key' => 'Demo99999999999999',
        );

        $postStr = $GLOBALS["HTTP_RAW_POST_DATA"]; //获取微信返回的数据
        //$this->logResult($postStr);
        $this->logResult($postStr);
        $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); //转化数据,进行验签
        if ($postObj === false) {
            die('parse xml error');
        }
        if ($postObj->return_code != 'SUCCESS') {
            die($postObj->return_msg);
        }
        if ($postObj->result_code != 'SUCCESS') {
            die($postObj->err_code);
        }
        $arr = (array) $postObj;
        unset($arr['sign']);
        if ($this->getSign($arr, $config['key']) == $postObj->sign) {
            if ($postObj->return_code == "FAIL") {
                //此处应该更新一下订单状态,商户自行增删操作;失败
                echo '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[通信出错]]></return_msg></xml>'; //通知微信支付失败;设置返回码
                return $postObj;
            } else if ($postObj->result_code == "FAIL") {
                //此处应该更新一下订单状态,商户自行增删操作;失败
                echo '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[业务出错]]></return_msg></xml>'; //通知微信支付失败;设置返回码
                return $postObj;
            } else {
                $tag=substr($arr['out_trade_no'],0,strlen($arr['out_trade_no'])-10);
                $where['tag']=substr($arr['out_trade_no'],0,strlen($arr['out_trade_no'])-10);
                //$where['tag']=$arr['out_trade_no'];
                $order=M("order");
                $re=$order->where($where)->find();
                if($re['status'] == -1){

                    // 监听用户支付状态
                    \Think\Hook::listen('paystatus',$tag);


                    $order->status=1;
                    $order->ispay=2;
                    $order->wx_transaction_id=$arr['transaction_id'];
                    $order->time_pay = $arr['time_end'];
                    $order->payway=4;
                    $order->where($where)->save();



                    //改变销量
                    $data=M('shoplist')->where($where)->getField('goodid,num');
                    foreach ($data as $k=>$v)
                    {
                        $whereid['id']=array('eq',$k);
                        $sale=M('document')->where($whereid)->getField('sale');
                        $set['sale']=intval($v)+intval($sale);
                        M('document')->where($where)->setField($set);
                    }
                }
                echo exit('<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>');
                //此处应该更新一下订单状态,商户自行增删操作;操作成功
                //这里就可以做自己数据库订单判断及操作了
                //这里可以选择直接sql操作数据库,也可以根据自己需要,模拟自己的加密算法去请求自己的接口

            }
        } else {
            echo '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>'; //通知微信支付失败;设置返回码
            return $postObj;
        }
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值