• Yii2.0实现微信公众号后台开发


     接入微信
    Yii2后台配置
    
    1.在app/config/params.php中配置token参数
    
    return [
        //微信接入
        'wechat' =>[
            'token' => 'your token',
        ],
    ];
    
    2.在app/config/main.php中配置路由
    
    因为接口模块使用的RESTful API,所以需要定义路由规则。
    
    'urlManager' => [
        'enablePrettyUrl' => true,
        'enableStrictParsing' => true,
        'showScriptName' => false,
        'rules' => [
            [
                'class' => 'yii
    estUrlRule',
                'controller' => 'wechat',
                'extraPatterns' => [
                    'GET valid' => 'valid',
                ],
            ],
        ],
    ],
    
    3.在app/controllers中新建WechatController
    
    <?php
    
    namespace apicontrollers;
    
    use Yii;
    use yii
    estActiveController;
    
    class WechatController extends ActiveController
    {
    
        public $modelClass = '';
    
        public function actionValid()
        {
            $echoStr = $_GET["echostr"];
            $signature = $_GET["signature"];
            $timestamp = $_GET["timestamp"];
            $nonce = $_GET["nonce"];
            //valid signature , option
            if($this->checkSignature($signature,$timestamp,$nonce)){
                echo $echoStr;
            }
        }
    
        private function checkSignature($signature,$timestamp,$nonce)
        {
            // you must define TOKEN by yourself
            $token = Yii::$app->params['wechat']['token'];
            if (!$token) {
                echo 'TOKEN is not defined!';
            } else {
                $tmpArr = array($token, $timestamp, $nonce);
                // use SORT_STRING rule
                sort($tmpArr, SORT_STRING);
                $tmpStr = implode( $tmpArr );
                $tmpStr = sha1( $tmpStr );
    
                if( $tmpStr == $signature ){
                    return true;
                }else{
                    return false;
                }
            }
        }
    
    }
    
    微信公众号后台配置
    
    在微信公众号后台配置URL和Token,然后提交验证即可。
    
    URL:http://app.demo.com/wechats/valid
    Token:your token
    
    获取用户信息
    用户表设计
    
    CREATE TABLE `wechat_user` (
      `id` int(11) NOT NULL,
      `openid` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
      `nickname` varchar(50) COLLATE utf8_unicode_ci NOT NULL COMMENT '微信昵称',
      `sex` tinyint(4) NOT NULL COMMENT '性别',
      `headimgurl` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT '头像',
      `country` varchar(50) COLLATE utf8_unicode_ci NOT NULL COMMENT '国家',
      `province` varchar(50) COLLATE utf8_unicode_ci NOT NULL COMMENT '省份',
      `city` varchar(50) COLLATE utf8_unicode_ci NOT NULL COMMENT '城市',
      `access_token` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
      `refresh_token` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
      `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP
    ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    
    ALTER TABLE `wechat_user`
      ADD PRIMARY KEY (`id`);
    
    获取用户信息的相关接口
    
    1.用户授权接口:获取access_token、openid等;获取并保存用户资料到数据库
    
    public function actionAccesstoken()
    {
        $code = $_GET["code"];
        $state = $_GET["state"];
        $appid = Yii::$app->params['wechat']['appid'];
        $appsecret = Yii::$app->params['wechat']['appsecret'];
        $request_url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid='.$appid.'&secret='.$appsecret.'&code='.$code.'&grant_type=authorization_code';
    
        //初始化一个curl会话
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $request_url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $result = curl_exec($ch);
        curl_close($ch);
        $result = $this->response($result);
    
        //获取token和openid成功,数据解析
        $access_token = $result['access_token'];
        $refresh_token = $result['refresh_token'];
        $openid = $result['openid'];
    
        //请求微信接口,获取用户信息
        $userInfo = $this->getUserInfo($access_token,$openid);
        $user_check = WechatUser::find()->where(['openid'=>$openid])->one();
        if ($user_check) {
            //更新用户资料
        } else {
            //保存用户资料
        }
    
        //前端网页的重定向
        if ($openid) {
            return $this->redirect($state.$openid);
        } else {
            return $this->redirect($state);
        }
    }
    
    2.从微信获取用户资料
    
    public function getUserInfo($access_token,$openid)
    {
        $request_url = 'https://api.weixin.qq.com/sns/userinfo?access_token='.$access_token.'&openid='.$openid.'&lang=zh_CN';
        //初始化一个curl会话
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $request_url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $result = curl_exec($ch);
        curl_close($ch);
        $result = $this->response($result);
        return $result;
    }
    
    3.获取用户资料接口
    
    public function actionUserinfo()
    {
        if(isset($_REQUEST["openid"])){
            $openid = $_REQUEST["openid"];
            $user = WechatUser::find()->where(['openid'=>$openid])->one();
            if ($user) {
                $result['error'] = 0;
                $result['msg'] = '获取成功';
                $result['user'] = $user;
            } else {
                $result['error'] = 1;
                $result['msg'] = '没有该用户';
            }
        } else {
            $result['error'] = 1;
            $result['msg'] = 'openid为空';
        }
        return $result;
    }
    
    微信支付
    
    1.微信支付接口:打包支付数据
    
    public function actionPay(){
        if(isset($_REQUEST["uid"])&&isset($_REQUEST["oid"])&&isset($_REQUEST["totalFee"])){
            //uid、oid、totalFee
            $uid = $_REQUEST["uid"];
            $oid = $_REQUEST["oid"];
            $totalFee = $_REQUEST["totalFee"];
            $timestamp = time();
    
            //微信支付参数
            $appid = Yii::$app->params['wechat']['appid'];
            $mchid = Yii::$app->params['wechat']['mchid'];
            $key = Yii::$app->params['wechat']['key'];
            $notifyUrl = Yii::$app->params['wechat']['notifyUrl'];
    
            //支付打包
            $wx_pay = new WechatPay($mchid, $appid, $key);
            $package = $wx_pay->createJsBizPackage($uid, $totalFee, $oid, $notifyUrl, $timestamp);
            $result['error'] = 0;
            $result['msg'] = '支付打包成功';
            $result['package'] = $package;
            return $result;
        }else{
            $result['error'] = 1;
            $result['msg'] = '请求参数错误';
        }
        return $result;
    }
    
    2.接收微信发送的异步支付结果通知
    
    public function actionNotify(){
        $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
        $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);
        }
    
        //微信支付参数
        $appid = Yii::$app->params['wechat']['appid'];
        $mchid = Yii::$app->params['wechat']['mchid'];
        $key = Yii::$app->params['wechat']['key'];
        $wx_pay = new WechatPay($mchid, $appid, $key);
    
        //验证签名
        $arr = (array)$postObj;
        unset($arr['sign']);
        if ($wx_pay->getSign($arr, $key) != $postObj->sign) {
            die("签名错误");
        }
    
        //支付处理正确-判断是否已处理过支付状态
        $orders = Order::find()->where(['uid'=>$postObj->openid, 'oid'=>$postObj->out_trade_no, 'status' => 0])->all();
    
        if(count($orders) > 0){
            //更新订单状态
            foreach ($orders as $order) {
                //更新订单
                $order['status'] = 1;
                $order->update();
            }
            return '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
        } else {
            //订单状态已更新,直接返回
            return '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
        }
    }
    
    3.微信支付类 WechatPay.php
    
    <?php
    
    namespace apisdk;
    
    use Yii;
    
    class WechatPay
    {
        protected $mchid;
        protected $appid;
        protected $key;
    
        public function __construct($mchid, $appid, $key){
            $this->mchid = $mchid;
            $this->appid = $appid;
            $this->key = $key;
        }
    
        public function createJsBizPackage($openid, $totalFee, $outTradeNo, $orderName, $notifyUrl, $timestamp){
            $config = array(
                'mch_id' => $this->mchid,
                'appid' => $this->appid,
                'key' => $this->key,
            );
            $unified = array(
                'appid' => $config['appid'],
                'attach' => '支付',
                'body' => $orderName,
                'mch_id' => $config['mch_id'],
                'nonce_str' => self::createNonceStr(),
                'notify_url' => $notifyUrl,
                'openid' => $openid,
                'out_trade_no' => $outTradeNo,
                'spbill_create_ip' => '127.0.0.1',
                'total_fee' => intval($totalFee * 100),
                'trade_type' => 'JSAPI',
            );
            $unified['sign'] = self::getSign($unified, $config['key']);
            $responseXml = self::curlPost('https://api.mch.weixin.qq.com/pay/unifiedorder', self::arrayToXml($unified));
            $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA);
            if ($unifiedOrder === false) {
                die('parse xml error');
            }
            if ($unifiedOrder->return_code != 'SUCCESS') {
                die($unifiedOrder->return_msg);
            }
            if ($unifiedOrder->result_code != 'SUCCESS') {
                die($unifiedOrder->err_code);
            }
            $arr = array(
                "appId" => $config['appid'],
                "timeStamp" => $timestamp,
                "nonceStr" => self::createNonceStr(),
                "package" => "prepay_id=" . $unifiedOrder->prepay_id,
                "signType" => 'MD5',
            );
            $arr['paySign'] = self::getSign($arr, $config['key']);
            return $arr;
        }
    
        public static 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;
        }
    
    
        public static 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;
        }
    
        public static function createNonceStr($length = 16){
            $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
            $str = '';
            for ($i = 0; $i<$length; $i++){
                $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
            }
            return $str;
        }
    
        public static 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 static function getSign($params, $key){
            ksort($params, SORT_STRING);
            $unSignParaString = self::formatQueryParaMap($params, false);
            $signStr = strtoupper(md5($unSignParaString . "&key=" . $key));
            return $signStr;
        }
    
        protected static 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;
        }
    
    }
    
    获取JS-SDK的config参数
    
    根据微信公众平台开发者文档:
    
    所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用,目前Android微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题会在Android6.2中修复)。
    
    即:
    
    wx.config({
        debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
        appId: '', // 必填,公众号的唯一标识
        timestamp: , // 必填,生成签名的时间戳
        nonceStr: '', // 必填,生成签名的随机串
        signature: '',// 必填,签名,见附录1
        jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
    });
    
    1.微信支付类 WechatPay.php
    
    <?php
    
    namespace apisdk;
    
    use Yii;
    
    class WechatPay
    {
    
        public function getSignPackage($url) {
            $jsapiTicket = self::getJsApiTicket();
    
            $timestamp = time();
            $nonceStr = self::createNonceStr();
    
            // 这里参数的顺序要按照 key 值 ASCII 码升序排序
            $string = "jsapi_ticket=".$jsapiTicket."&noncestr=".$nonceStr."×tamp=".$timestamp."&url=".$url;
    
            $signature = sha1($string);
    
            $signPackage = array(
                "appId"     => $this->appid,
                "nonceStr"  => $nonceStr,
                "timestamp" => $timestamp,
                "url"       => $url,
                "signature" => $signature,
                "rawString" => $string
            );
            return $signPackage;
        }
    
        public static function getJsApiTicket() {
            //使用Redis缓存 jsapi_ticket
            $redis = Yii::$app->redis;
            $redis_ticket = $redis->get('wechat:jsapi_ticket');
            if ($redis_ticket) {
                $ticket = $redis_ticket;
            } else {
                $accessToken = self::getAccessToken();
                $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=".$accessToken;
                $res = json_decode(self::curlGet($url));
                $ticket = $res->ticket;
                if ($ticket) {
                    $redis->set('wechat:jsapi_ticket', $ticket);
                    $redis->expire('wechat:jsapi_ticket', 7000);
                }
            }
            return $ticket;
        }
    
        public static function getAccessToken() {
            //使用Redis缓存 access_token
            $redis = Yii::$app->redis;
            $redis_token = $redis->get('wechat:access_token');
            if ($redis_token) {
                $access_token = $redis_token;
            } else {
                $appid = Yii::$app->params['wechat']['appid'];
                $appsecret = Yii::$app->params['wechat']['appsecret'];
                $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=".$appid."&secret=".$appsecret;
                $res = json_decode(self::curlGet($url));
                $access_token = $res->access_token;
                if ($access_token) {
                    $redis->set('wechat:access_token', $access_token);
                    $redis->expire('wechat:access_token', 7000);
                }
            }
            return $access_token;
        }
    
        public static 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;
        }
    
        public static 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;
        }
    
        public static function createNonceStr($length = 16){
            $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
            $str = '';
            for ($i = 0; $i<$length; $i++){
                $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
            }
            return $str;
        }
    
    }
    
    2.获取config参数接口
    
    public function actionConfig(){
        if (isset($_REQUEST['url'])) {
            $url = $_REQUEST['url'];
            //微信支付参数
            $appid = Yii::$app->params['wechat']['appid'];
            $mchid = Yii::$app->params['wechat']['mchid'];
            $key = Yii::$app->params['wechat']['key'];
            $wx_pay = new WechatPay($mchid, $appid, $key);
            $package = $wx_pay->getSignPackage($url);
            $result['error'] = 0;
            $result['msg'] = '获取成功';
            $result['config'] = $package;
        } else {
            $result['error'] = 1;
            $result['msg'] = '参数错误';
        }
        return $result;
    }
  • 相关阅读:
    js 获取两个日期相差的天数--自定义方法
    C# Dictionary类型转json方法之一
    C# Obsolete(已弃用方法属性)
    css 样式计算器
    edge 浏览器自动识别电话号码解问题解决方法
    js 中止程序继续进行(break continue return throw)
    js根据等号(=)前名称获取参数值
    JS 时间格式为/Date(1332919782070)/ 转化为正常的格式
    input 内容发生改变时触发事件
    自我介绍for软件工程课程
  • 原文地址:https://www.cnblogs.com/wuheng1991/p/6210300.html
Copyright © 2020-2023  润新知