完美利用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 相关文章推荐
投票管理程序
Oct 09 PHP
从Web查询数据库之PHP与MySQL篇
Sep 25 PHP
php 8小时时间差的解决方法小结
Dec 22 PHP
10个实用的PHP代码片段
Sep 02 PHP
php中导出数据到excel时数字变为科学计数的解决方法
Feb 03 PHP
php程序员应具有的7种能力小结
Nov 27 PHP
使用PHP编写发红包程序
Jul 22 PHP
php正则表达式获取内容所有链接
Jul 24 PHP
YII视图整合kindeditor扩展的方法
Jul 13 PHP
Laravel的throttle中间件失效问题解决方法
Oct 09 PHP
Linux平台PHP5.4设置FPM线程数量的方法
Nov 09 PHP
PHP获取当前执行php文件名的代码
Mar 02 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
php中函数的形参与实参的问题说明
2010/09/01 PHP
php sybase_fetch_array使用方法
2014/04/15 PHP
php 实现进制相互转换
2016/04/07 PHP
php解决约瑟夫环算法实例分析
2019/09/30 PHP
php获取是星期几的的一些常用姿势
2019/12/15 PHP
javascript文件中引用依赖的js文件的方法
2014/03/17 Javascript
借助JavaScript脚本判断浏览器Flash Player信息的方法
2014/07/09 Javascript
理解javascript中的回调函数(callback)
2014/09/02 Javascript
JQuery判断checkbox是否选中及其它复选框操作方法合集
2015/06/01 Javascript
分析js闭包引起的事件注册问题
2016/03/29 Javascript
Js得到radiobuttonlist选中值的两种方法(推荐)
2016/08/25 Javascript
js实现tab切换效果
2017/02/16 Javascript
jQuery plugin animsition使用小结
2017/09/14 jQuery
js用类封装pop弹窗组件
2017/10/08 Javascript
Angular中支持SCSS的方法
2017/11/18 Javascript
React如何实现浏览器打印部分内容详析
2019/05/19 Javascript
解决Vue中的生命周期beforeDestory不触发的问题
2020/07/21 Javascript
[01:14]2019完美世界城市挑战赛(秋季赛)全国总决赛精彩花絮
2020/01/08 DOTA
Python使用Scrapy保存控制台信息到文本解析
2017/12/27 Python
python爬虫爬取网页表格数据
2018/03/07 Python
Python中GeoJson和bokeh-1的使用讲解
2019/01/03 Python
Pandas读写CSV文件的方法示例
2019/03/27 Python
python利用datetime模块计算程序运行时间问题
2020/02/20 Python
使用CSS3代码绘制可爱的Hello Kitty猫
2016/08/03 HTML / CSS
图解CSS3制作圆环形进度条的实例教程
2016/05/26 HTML / CSS
雅高酒店中国:Accorhotels.com China
2018/03/26 全球购物
罗马尼亚在线杂货店:Pilulka.ro
2019/09/28 全球购物
七年级音乐教学反思
2014/01/26 职场文书
《维生素c的故事》教学反思
2014/02/18 职场文书
医生个人自我剖析材料
2014/10/08 职场文书
2015初中团支部工作总结
2015/07/21 职场文书
公司出差管理制度范本
2015/08/05 职场文书
《棉鞋里的阳光》教学反思
2016/02/20 职场文书
vue项目多环境配置(.env)的实现
2021/07/21 Vue.js
Python+Matplotlib+LaTeX玩转数学公式
2022/02/24 Python
豆瓣2021评分最高动画剧集-豆瓣评分最高的动画剧集2021
2022/03/18 日漫