完美利用Yii2微信后台开发的系列总结


Posted in PHP onJuly 18, 2016

网上有很多关于YII2.0微信开发教程,但是太过复杂凌乱,所以今天在这里给大家整理总结利用Yii2微信后台开发的系列了,给需要的小伙伴们参考。

一:接入微信

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\rest\UrlRule',
  'controller' => 'wechat',
  'extraPatterns' => [
  'GET valid' => 'valid',
  ],
 ],
 ],
],

3.在app/controllers中新建WechatController

<?php

namespace api\controllers;

use Yii;
use yii\rest\ActiveController;

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 api\sdk;

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 api\sdk;

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;
}

以上就是利用Yii2微信后台开发全部过程及示例代码,希望本文对大家基于php的微信公众平台开发有所帮助。

PHP 相关文章推荐
PHP中的加密功能
Oct 09 PHP
基于文本的搜索
Oct 09 PHP
php基础知识:类与对象(3) 构造函数和析构函数
Dec 13 PHP
php access 数据连接与读取保存编辑数据的实现代码
May 12 PHP
解析PHP中intval()等int转换时的意外异常情况
Jun 21 PHP
php smarty模板引擎的6个小技巧
Apr 24 PHP
CodeIgniter安全相关设置汇总
Jul 03 PHP
PHP中基本HTTP认证技巧分析
Mar 16 PHP
PHP.vs.JAVA
Apr 29 PHP
PHP入门教程之数学运算技巧总结
Sep 11 PHP
php-fpm重启导致的程序执行中断问题详解
Apr 29 PHP
PHP pthreads v3下worker和pool的使用方法示例
Feb 21 PHP
PHP中set_include_path()函数相关用法分析
Jul 18 #PHP
PHP中spl_autoload_register()函数用法实例详解
Jul 18 #PHP
详谈PHP程序Laravel 5框架的优化技巧
Jul 18 #PHP
3种方法轻松处理php开发中emoji表情的问题
Jul 18 #PHP
PHP生成图像验证码的方法小结(2种方法)
Jul 18 #PHP
Yii2中DropDownList简单用法示例
Jul 18 #PHP
Yii2使用dropdownlist实现地区三级联动功能的方法
Jul 18 #PHP
You might like
mac下Apache + MySql + PHP搭建网站开发环境
2014/06/02 PHP
PHP多个文件上传到服务器实例
2014/10/29 PHP
Joomla语言翻译类Jtext用法分析
2016/05/05 PHP
JavaScript中Math对象使用说明
2008/01/16 Javascript
javascript radio 联动效果
2009/03/04 Javascript
JS实现在页面随时自定义背景颜色的方法
2015/02/27 Javascript
遮罩层点击按钮弹出并且具有拖动和关闭效果(两种方法)
2015/08/20 Javascript
限制复选框最多选择项的实现代码
2016/05/30 Javascript
jQuery动态生成不规则表格(前后端)
2017/02/21 Javascript
jstree单选功能的实现方法
2017/06/07 Javascript
JS和jQuery通过this获取html标签中的属性值(实例代码)
2017/09/11 jQuery
微信小程序之GET请求的实例详解
2017/09/29 Javascript
ES6 javascript中class类的get与set用法实例分析
2017/10/30 Javascript
jQuery实现表单动态添加与删除数据操作示例
2018/07/03 jQuery
JavaScript对象属性操作实例解析
2020/02/04 Javascript
JS实现简易图片自动轮播
2020/10/16 Javascript
python解析发往本机的数据包示例 (解析数据包)
2014/01/16 Python
跟老齐学Python之有容乃大的list(2)
2014/09/15 Python
python采集百度百科的方法
2015/06/05 Python
在Linux系统上通过uWSGI配置Nginx+Python环境的教程
2015/12/25 Python
python中abs&amp;map&amp;reduce简介
2018/02/20 Python
Python利用递归实现文件的复制方法
2018/10/27 Python
python实现从wind导入数据
2019/12/03 Python
Python 统计位数为偶数的数字代码详解
2020/03/15 Python
python小程序基于Jupyter实现天气查询的方法
2020/03/27 Python
解析python 类方法、对象方法、静态方法
2020/08/15 Python
Miller Harris官网:英国小众香水品牌
2020/09/24 全球购物
教师实习自我鉴定
2013/12/14 职场文书
考核工作实施方案
2014/03/30 职场文书
幼儿园家长评语大全
2014/04/16 职场文书
小学教师师德演讲稿
2014/05/06 职场文书
2014国庆节幼儿园亲子活动方案
2014/09/16 职场文书
公民代理授权委托书
2014/09/24 职场文书
2015元旦文艺汇演主持稿(开场白+结束语)
2014/12/14 职场文书
2015年评职称个人工作总结
2015/10/15 职场文书
毕业生自荐求职信书写的技巧
2019/08/26 职场文书