微信小程序> 微信小程序搜索widget开发(PHP)

微信小程序搜索widget开发(PHP)

浏览量:4068 时间: 来源:13xs

一. 项目背景

1.公司想要为小程序开发一个 搜索widget 自定义模板. 即当用户使用微信客户端搜索小程序的时候,能达到下图中的效果 

2.微信官方:https://developers.weixin.qq.com/miniprogram/introduction/widget/custom/有详细的操作步骤, 这里不过多的赘述.本文主要记叙涉及到的两个接口的实现: (1)导入抽样数据 (2)开发后台接口

 

二. 项目实现

1. 导入抽样数据

   第一步, 获取access_token

  

public function getAccessToken(){    $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='.$this->appid.'&secret='.$this->secret;    $rs = ihttp_get($url);    $ret = json_decode($rs['content'],true);    return $ret['access_token'];}

第二步, 根据不同的业务需求组建json, 这里以汽车出行服务为例

public function setDynaMicData()    {        $access_token = $this->getAccessToken();        $url = 'http://api.weixin.qq.com/wxa/setdynamicdata?access_token='.$access_token;        $post['lifespan'] = 86400;        $post['query'] = json_encode(array('type'=>1000013));        $post['scene'] = 1;        $id = time();        $seq = 0;        $totalCount = pdo_fetchcolumn('SELECT * FROM zc2_station_line WHERE acid='.$this->uniacid);        for($count=0;$count<=$totalCount;$count+=5000)        {            $lineStations = pdo_fetchall("SELECT * FROM zc2_station_line WHERE acid={$this->uniacid} LIMIT {$count},5000");            foreach ($lineStations as $key=>$val) {                $stations[$key]['from']['city'] = $val['StartStationName'];                $stations[$key]['to']['city'] = $val['EndStationName'];            }            $attribute['count'] = count($lineStations);            $attribute['totalcount'] = $totalCount;            $attribute['id'] = "{$id}";            $attribute['seq'] = $seq;            $items['attribute'] = $attribute;            $post['data'] = '{"items":'.json_encode($stations).', "attribute":'.json_encode($attribute).'}';            $ret[] = ihttp_post($url, json_encode($post));            $seq++;        }        return $ret;    }

 

2. 开发后台接口

后台接口指的是, 你的服务器与微信服务器的消息交互接口. 当用户搜索你的小程序时, 发现本地没有相关缓存或者缓存已经过期, 则请求这个接口, 返回数据给用户;比较麻烦的是消息的交互需要用进行验证, 但是, 微信也有各种语言版本的库提供;我这里是将所有加密解密文件封装成一个类, WXBizMsgCrypt.php(将在后面贴出);

第一步, 封装加密解密工具类WXBizMsgCrypt.php(在这里下载:https://wximg.gtimg.com/shake_tv/mpwiki/cryptoDemo.zip)

第二步, 利用加密解密工具进行验证, 验证通过后就可以自己的处理业务逻辑了getWidgetData()

 

三. 项目代码及调用示例

代码示例:

WXBizMsgCrypt.php
<?php/** * 服务器与微信服务器通讯过程中的加密解密类 * 1.第三方回复加密消息给公众平台; * 2.第三方收到公众平台发送的消息,验证消息的安全性,并对消息进行解密。 * * @Author: xiewg * @since: 20190118 */class WXBizMsgCrypt{    private $token;    private $encodingAesKey;    private $appId;    public $key;    public $block_size = 32;    /**     * error code 说明.     * <ul>     *    <li>-40001: 签名验证错误</li>     *    <li>-40002: xml解析失败</li>     *    <li>-40003: sha加密生成签名失败</li>     *    <li>-40004: encodingAesKey 非法</li>     *    <li>-40005: appid 校验错误</li>     *    <li>-40006: aes 加密失败</li>     *    <li>-40007: aes 解密失败</li>     *    <li>-40008: 解密后得到的buffer非法</li>     *    <li>-40009: base64加密失败</li>     *    <li>-40010: base64解密失败</li>     *    <li>-40011: 生成xml失败</li>     * </ul>     */    public static $OK = 0;    public static $ValidateSignatureError = -40001;    public static $ParseXmlError = -40002;    public static $ComputeSignatureError = -40003;    public static $IllegalAesKey = -40004;    public static $ValidateAppidError = -40005;    public static $EncryptAESError = -40006;    public static $DecryptAESError = -40007;    public static $IllegalBuffer = -40008;    public static $EncodeBase64Error = -40009;    public static $DecodeBase64Error = -40010;    public static $GenReturnXmlError = -40011;    /**     * 构造函数     * @param $token string 公众平台上,开发者设置的token     * @param $encodingAesKey string 公众平台上,开发者设置的EncodingAESKey     * @param $appId string 公众平台的appId     */    public function __construct($token, $encodingAesKey, $appId)    {        $this->token = $token;        $this->encodingAesKey = $encodingAesKey;        $this->appId = $appId;        $this->key = base64_decode($encodingAesKey . "=");    }    /**     * 将公众平台回复用户的消息加密打包.     * <ol>     *    <li>对要发送的消息进行AES-CBC加密</li>     *    <li>生成安全签名</li>     *    <li>将消息密文和安全签名打包成xml格式</li>     * </ol>     *     * @param $replyMsg string 公众平台待回复用户的消息,xml格式的字符串     * @param $timeStamp string 时间戳,可以自己生成,也可以用URL参数的timestamp     * @param $nonce string 随机串,可以自己生成,也可以用URL参数的nonce     * @param &$encryptMsg string 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串,     *                      当return返回0时有效     *     * @return int 成功0,失败返回对应的错误码     */    public function encryptMsg($replyMsg, $timeStamp, $nonce, &$encryptMsg)    {        //加密        $array = $this->encrypt($replyMsg, $this->appId);        $ret = $array[0];        if ($ret != 0) {            return $ret;        }        if ($timeStamp == null) {            $timeStamp = time();        }        $encrypt = $array[1];        //生成安全签名        $array = $this->getSHA1($this->token, $timeStamp, $nonce, $encrypt);        $ret = $array[0];        if ($ret != 0) {            return $ret;        }        $signature = $array[1];        $encryptMsg = array('encrypt'=>$encrypt, 'msgSignature'=>$signature, 'timeStamp'=>$timeStamp, 'nonce'=>$nonce);        return self::$OK;    }    /**     * 检验消息的真实性,并且获取解密后的明文.     * <ol>     *    <li>利用收到的密文生成安全签名,进行签名验证</li>     *    <li>若验证通过,则提取xml中的加密消息</li>     *    <li>对消息进行解密</li>     * </ol>     *     * @param $msgSignature string 签名串,对应URL参数的msg_signature     * @param $timestamp string 时间戳 对应URL参数的timestamp     * @param $nonce string 随机串,对应URL参数的nonce     * @param $postData string 密文,对应POST请求的数据     * @param &$msg string 解密后的原文,当return返回0时有效     *     * @return int 成功0,失败返回对应的错误码     */    public function decryptMsg($msgSignature, $timestamp = null, $nonce, $postData, &$msg)    {        if (strlen($this->encodingAesKey) != 43) {            return self::$IllegalAesKey;        }        if ($timestamp == null) {            $timestamp = time();        }        $encrypt = $postData;        //验证安全签名        $array = $this->getSHA1($this->token, $timestamp, $nonce, $encrypt);        $ret = $array[0];        if ($ret != 0) {            return $ret;        }        $signature = $array[1];        if ($signature != $msgSignature) {            return self::$ValidateSignatureError;        }        $result = $this->decrypt($encrypt, $this->appId);        if ($result[0] != 0) {            return $result[0];        }        $msg = $result[1];        return self::$OK;    }    /**     * 用SHA1算法生成安全签名     * @param string $token 票据     * @param string $timestamp 时间戳     * @param string $nonce 随机字符串     * @param string $encrypt_msg 密文消息     * @return array     */    public function getSHA1($token, $timestamp, $nonce, $encrypt_msg)    {        //排序        try {            $array = array($encrypt_msg, $token, $timestamp, $nonce);            sort($array, SORT_STRING);            $str = implode($array);            return array(self::$OK, sha1($str));        } catch (Exception $e) {            //print $e . "";            return array(self::$ComputeSignatureError, null);        }    }    /**     * 对明文进行加密     * @param string $text 需要加密的明文     * @return string 加密后的密文     */    public function encrypt($text, $appid)    {        try {            //获得16位随机字符串,填充到明文之前            $random = $this->getRandomStr();            $text = $random . pack("N", strlen($text)) . $text . $appid;            // 网络字节序            $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);            $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');            $iv = substr($this->key, 0, 16);            //使用自定义的填充方式对明文进行补位填充            $text = $this->encode($text);            mcrypt_generic_init($module, $this->key, $iv);            //加密            $encrypted = mcrypt_generic($module, $text);            mcrypt_generic_deinit($module);            mcrypt_module_close($module);            //print(base64_encode($encrypted));            //使用BASE64对加密后的字符串进行编码            return array(self::$OK, base64_encode($encrypted));        } catch (Exception $e) {            //print $e;            return array(self::$EncryptAESError, null);        }    }    /**     * 对密文进行解密     * @param string $encrypted 需要解密的密文     * @return string 解密得到的明文     */    public function decrypt($encrypted, $appid)    {        try {            //使用BASE64对需要解密的字符串进行解码            $ciphertext_dec = base64_decode($encrypted);            $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');            $iv = substr($this->key, 0, 16);            mcrypt_generic_init($module, $this->key, $iv);            //解密            $decrypted = mdecrypt_generic($module, $ciphertext_dec);            mcrypt_generic_deinit($module);            mcrypt_module_close($module);        } catch (Exception $e) {            return array(self::$DecryptAESError, null);        }        try {            //去除补位字符            $result = $this->decode($decrypted);            //去除16位随机字符串,网络字节序和AppId            if (strlen($result) < 16)                return "";            $content = substr($result, 16, strlen($result));            $len_list = unpack("N", substr($content, 0, 4));            $xml_len = $len_list[1];            $xml_content = substr($content, 4, $xml_len);            $from_appid = substr($content, $xml_len + 4);        } catch (Exception $e) {            //print $e;            return array(self::$IllegalBuffer, null);        }        if ($from_appid != $appid)            return array(self::$ValidateAppidError, null);        return array(0, $xml_content);    }    /**     * 随机生成16位字符串     * @return string 生成的字符串     */    function getRandomStr()    {        $str = "";        $str_pol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";        $max = strlen($str_pol) - 1;        for ($i = 0; $i < 16; $i++) {            $str .= $str_pol[mt_rand(0, $max)];        }        return $str;    }    /**     * @param $text     * @return string     */    public function encode($text)    {        $block_size = $this->block_size;        $text_length = strlen($text);        //计算需要填充的位数        $amount_to_pad = $block_size - ($text_length % $block_size);        if ($amount_to_pad == 0) {            $amount_to_pad = $block_size;        }        //获得补位所用的字符        $pad_chr = chr($amount_to_pad);        $tmp = "";        for ($index = 0; $index < $amount_to_pad; $index++) {            $tmp .= $pad_chr;        }        return $text . $tmp;    }    /**     * 对解密后的明文进行补位删除     * @param $text string decrypted解密后的明文     * @return string 删除填充补位后的明文     */    public function decode($text)    {        $pad = ord(substr($text, -1));        if ($pad < 1 || $pad > 32) {            $pad = 0;        }        return substr($text, 0, (strlen($text) - $pad));    }}

 

widget.php

<?phpclass Widget{    private $uniacid = 0;    private $appid = null;    private $secret = null;    private $signature = null;    private $timestamp = null;    private $nonce = null;    private $token = null;    private $encodingAesKey = null;    public function __construct($_GPC)    {        $this->uniacid = 6;        $this->appid = 'xxxxxx';        $this->secret = 'xxxxxx';        $this->token = 'xxxxxx';        $this->encodingAesKey = 'xxxxxxx';        $this->msgSignature = $_GPC['msg_signature'];        $this->encrypt = $_GPC['__input']['Encrypt'];        $this->signature = $_GPC['signature'];        $this->timestamp = $_GPC['timestamp'];        $this->nonce = $_GPC['nonce'];    }    /**     * @Function: 小程序获取access_token     * @return string     */    public function getAccessToken()    {        $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='.$this->appid.'&secret='.$this->secret;        $rs = ihttp_get($url);        $ret = json_decode($rs['content'],true);        return $ret['access_token'];    }    /**     * @Function: 微信widget搜索,提交数据接口     * @return array     */    public function setDynaMicData()    {        $access_token = $this->getAccessToken();        $url = 'http://api.weixin.qq.com/wxa/setdynamicdata?access_token='.$access_token;        $post['lifespan'] = 86400;        $post['query'] = json_encode(array('type'=>1000013));        $post['scene'] = 1;        $id = time();        $seq = 0;        $totalCount = pdo_fetchcolumn('SELECT * FROM zc2_station_line WHERE acid='.$this->uniacid);        for($count=0;$count<=$totalCount;$count+=5000)        {            $lineStations = pdo_fetchall("SELECT * FROM zc2_station_line WHERE acid={$this->uniacid} LIMIT {$count},5000");            foreach ($lineStations as $key=>$val) {                $stations[$key]['from']['city'] = $val['StartStationName'];                $stations[$key]['to']['city'] = $val['EndStationName'];            }            $attribute['count'] = count($lineStations);            $attribute['totalcount'] = $totalCount;            $attribute['id'] = "{$id}";            $attribute['seq'] = $seq;            $items['attribute'] = $attribute;            $post['data'] = '{"items":'.json_encode($stations).', "attribute":'.json_encode($attribute).'}';            $ret[] = ihttp_post($url, json_encode($post));            $seq++;        }        return $ret;    }    /**     * 微信widget搜索,响应服务器与微信服务器消息交互接口     *     * 微信将上述请求包通过http post方式发送开发者在mp平台上配置的URL     * json格式     * POST     * http://yourdomain/youdir?timestamp=XXX&nonce=XXX&msg_signature=XXX&signature=XXX     *     * @return     */    public function getWidgetData()    {        $WXBizMsgCrypt = new WXBizMsgCrypt($this->token, $this->encodingAesKey, $this->appid);        //收到公众号平台发送的消息,进行解密操作        $rec = '';        $errCode = $WXBizMsgCrypt->decryptMsg($this->msgSignature, $this->timestamp, $this->nonce, $this->encrypt, $rec);        if ($errCode == 0)//解密成功,进一步处理业务逻辑        {            $rec = json_decode($rec,true);            $enTimeStamp = time();            $content = array();            $content['lifespan'] = 86400;//告诉微信此次数据可以缓存多久,单位为秒            $content['query'] = $rec['Query'];//微信服务器发来的query,原封不动填到这里,为json_encode之后的字符串            $content['scene'] = 1;//固定为1,表明来自搜索            $data['err_code'] = 0;//err_code中0代表正确,非0代表错误            $data['err_msg'] = 'success';//-1:无结果 -2:参数不对 -3:系统错误            $data['jump_url'] = '/pages/index/index';//跳转路径            $data['update_time'] = date('m月d日 D',$enTimeStamp);//更新时间            $data['more_description'] = '点击立即购票';//底部更多描述            //获取班次列表            $station_data_list = $this->getDepartures($content['query']);            file_put_contents('test.txt',json_encode($station_data_list).PHP_EOL,FILE_APPEND);            $data['station_data_list'] = $station_data_list;            $content['data'] = json_encode($data);            $post = array();            $post['ToUserName'] = $rec['FromUserName'];            $post['FromUserName'] = $rec['ToUserName'];            $post['CreateTime'] = $enTimeStamp;            $post['MsgType'] = 'widget_data';            $post['Content'] = json_encode($content);            $encryptMsg = '';            $nonce = '554484';            $errCode = $WXBizMsgCrypt->encryptMsg(json_encode($post), $enTimeStamp, $nonce, $encryptMsg);            if ($errCode == 0) {//加密成功                return array('Encrypt'=>$encryptMsg['encrypt'],                        'MsgSignature'=>$encryptMsg['msgSignature'],                        'TimeStamp'=>$enTimeStamp,                        'Nonce'=>$nonce);            } else {//加密失败                $ret = array('err_code'=>-3,'err_msg'=>'加密失败');                return $ret;            }        } else {//解密失败            $ret = array('err_code'=>-3,'err_msg'=>'解密失败');            return $ret;        }    }    /**     * 检验signature     * @param $signature     * @param $timestamp     * @param $nonce     * @param $token     * @return bool     */    private function checkSignature($signature,$timestamp,$nonce,$token)    {        $tmpArr = array($token, $timestamp, $nonce);        sort($tmpArr, SORT_STRING);        $tmpStr = implode( $tmpArr );        $tmpStr = sha1( $tmpStr );        if ($tmpStr == $signature ) {            return true;        } else {            return false;        }    }    /**     * 查询班次(本项目的业务逻辑)     * @param $query     * @return array     */    public function getDepartures($query)    {        file_put_contents('test.txt',$query.PHP_EOL,FILE_APPEND);//测试        $station_data_list = array();        $url = 'https://zc2api.zhongchengbus.cn/?s=Kgkx.TicketApi.departures&appid=wxapp_kgkx&nonce=73&timestamp=1548049804&signature=bd7a83fdd15a3035e90b64040eb7e518188f51f3';        $query = json_decode($query,true);        foreach ($query['station_list'] as $item)        {            file_put_contents('test.txt',"SELECT * FROM zc2_station_line WHERE sch_station_name='{$item['from_city']}' AND sch_dst_name='{$item['to_city']}' AND acid={$this->uniacid}".PHP_EOL,FILE_APPEND);//测试            $get_station_res = pdo_fetch("SELECT * FROM zc2_station_line WHERE sch_station_name='{$item['from_city']}' AND sch_dst_name='{$item['to_city']}' AND acid={$this->uniacid}");            $StartStation = $get_station_res['sch_station_code'];            $EndStation = $get_station_res['sch_dst_node'];            $SchDate = $item['date'];            file_put_contents('test.txt',json_encode($get_station_res).PHP_EOL,FILE_APPEND);            $params = array('StartStation'=>$StartStation,'EndStation'=>$EndStation,'SchDate'=>$SchDate);            $res = ihttp_post($url,$params);            file_put_contents('test.txt',json_encode($params).PHP_EOL,FILE_APPEND);            file_put_contents('test.txt',$res['content'].PHP_EOL,FILE_APPEND);            $content = json_decode($res['content'],true);            if($content['ret'] == '200')            {                foreach ($content['data'] as $val)                {                    $station_data_list_tmp['from'] = $val['SchWaitStName'];//|string|起始地|广东省汽车客运站|                    $station_data_list_tmp['from_time'] = $val['SchTime'];//|string|起飞时间|13:40|                    $station_data_list_tmp['to'] = $val['SchNodeName'];//|string|目的地|武汉路口|                    $station_data_list_tmp['to_time'] = '';//|string|到达时间|01:40|                    $station_data_list_tmp['special'] = '';//|string|跨天|+1天|                    $station_data_list_tmp['tips'] = '抢票';//|string|提示|抢票|                    $station_data_list_tmp['icon'] = '';//|string|图标|https://xxx|                    $station_data_list[] = $station_data_list_tmp;                }            }        }        return $station_data_list;    }}

 

调用示例:

调用导入数据接口:

$Widget = new Widget($_GPC);$res = $Widget->setDynaMicData();echo $res;exit();

调用后台接口:

$Widget = new Widget($_GPC);$res = $Widget->getWidgetData();echo $res;exit();

 

版权声明

即速应用倡导尊重与保护知识产权。如发现本站文章存在版权问题,烦请提供版权疑问、身份证明、版权证明、联系方式等发邮件至197452366@qq.com ,我们将及时处理。本站文章仅作分享交流用途,作者观点不等同于即速应用观点。用户与作者的任何交易与本站无关,请知悉。

产品经理

手机 : 13312967497

擅长 : 小程序流量变现

扫码领取礼包

最新资讯

热门模板

  • 头条
  • 搜狐
  • 微博
  • 百家
  • 一点资讯
  • 知乎