转载

PHP微信公众号后台开发(Yii2实现)

本文内容较多,包括微信接入、获取微信用户信息、微信支付、JSSDK配置参数获取等部分。如果读者对微信开发没有一个主观上的认识,那么建议读者先研读 微信公众平台开发者文档 ,然后再阅读本文,效果更佳!另外本文的分章节版本可以在 八宝粥的博客 找到。

接入微信

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; }
原文  https://segmentfault.com/a/1190000005919544
正文到此结束
Loading...