微信开发者服务器接入指南

适用于公众号,小程序,小游戏。

也适用于公众号的接入,但是并没有什么卵用,因为公众号的对话服务,只能接收消息,不能向用户发消息,开通发消息功能需要微信认证,而个人公众号不支持微信认证。看看给用户发消息会返回什么:{"errcode":48001,"errmsg":"api unauthorized rid: 62922e5a-050daf94-1dcd13f6"},api unauthorized,就是权限不足【这个错误并非没有权限引起,而是在被动回复消息时,调用了主动回复消息的API 接口,而主动回复确实是没有权限的】。另外对公众号发文也没有卵用,因为无法获取群发文的列表,微信客服也许都不禁笑了,你想要文章列表做什么?看看我们的客服是怎么说的:

公众号技术运营专员-livia 公众号技术运营专员-livia 05-05
你好,你可以提供相关发表记录的页面截图,查看发布这里是否有文章。获取发布列表:只有通过草稿箱进行自由发布的内容才能获取,话题发布和视频发布的内容,以及已群发的内容无法通过此接口获取

参见:接入概述 | 微信开放文档

Step1

  1. 如果是公众号:在 设置与开发=> 基本配置;
  2. 如果是小游戏:在 开发管理=>开发设置=>消息推送。

设置 URL, Token, EncodingAESKey:

  • URL 是开发者用来接收微信消息和事件的接口URL。【就是服务器上的响应程序,例如: https://www.xxx.com/connect.php】
  • Token可由开发者可以任意填写,用作生成签名(该 Token 会和接口 URL 中包含的 Token 进行比对,从而验证安全性)。【以下用宏定义 TOKEN】
  • EncodingAESKey由开发者手动填写或随机生成,将用作消息体加解密密钥。【如果选用明文模式则用不上】

下载代码并放置在开发者服务器上。PHP示例代码下载:下载

Step2

然后在 URL 内(就是 https://www.xxx.com/connect.php 文件内)放置如下函数:

include_once "wxBizMsgCrypt.php";

define("TOKEN", "XXXXXX");  //填写自己的token

if (isset($_GET['echostr'])) {          //校验服务器地址URL
    valid();
}else{
    // 业务代码
}

function valid()
{
    $echoStr = $_GET["echostr"];
    if(checkSignature()){
        header('content-type:text');
        echo $echoStr;
        exit;
    }else{
        echo $echoStr.'+++'.TOKEN;
        exit;
    }
}

function checkSignature()
{
    $signature = $_GET["signature"];
    $timestamp = $_GET["timestamp"];
    $nonce = $_GET["nonce"];

    $token = TOKEN;
    $tmpArr = array($token, $timestamp, $nonce);
    sort($tmpArr, SORT_STRING);
    $tmpStr = implode( $tmpArr );
    $tmpStr = sha1( $tmpStr );

    if( $tmpStr == $signature ){
        return true;
    }else{
        return false;
    }
}

其原理是:微信服务器向 URL 发起请求,携带了 signature, timestamp, nonce, echostr 四个参数。URL 收到后,使用 TOKEN, timestamp, nonce 组合并加密得到 tmpStr,如果 tmpStr==signature,则向微信服务器返回 echostr,否则返回 failure。加密程序在示例代码 wxBizMsgCrypt.php 中,它又引用了以下文件:

include_once "sha1.php";
include_once "xmlparse.php";
include_once "pkcs7Encoder.php";
include_once "errorCode.php";

因此服务器端至少需要以上五个文件。

Step3

回到微信后台,如果是公众号:

  1. 在 设置与开发=> 公众号开发信息,设置白名单,就是开发者服务器 IP 地址;
  2. 在 设置与开发=> 基本配置,提交 Step1 填写的信息,如果通过 Step2 代码的验证,则开通成功。

如果是小游戏:

  1. 在 开发管理=>开发设置=>开发者 ID,设置白名单,就是开发者服务器 IP 地址;
  2. 在 开发管理=>开发设置=>消息推送,提交 Step1 填写的信息,如果通过 Step2 代码的验证,则开通成功。

Step4

小游戏内转到客服对话:判断条件满足后,直接调用 onContact(),就弹出对话框:「即将进入”xxx”客服会话」;

然后,用户在会话界面提交的消息,将被转发到 URL,并在 URL 中得到处理,再回复给用户。

下面才是重点:

Step 4.1

用户发来的消息,格式如下:

// 根据实测,postStr 的格式如下:
    {
        "ToUserName": "xxx",
        "FromUserName": "xxx",
        "CreateTime": ####,
        "MsgType": "text",
        "Content": "xxx",
        "MsgId": ####
    }

PHP 获取用户消息:

$postStr = file_get_contents('php://input');
//此处推荐使用file_get_contents('php://input')获取后台post过来的数据
$postArr = json_decode($postStr,true);

if(!empty($postArr['MsgType']){
    if($postArr['MsgType'] == 'text'){   //用户发送文本消息
        // TODO
    }elseif($postArr['MsgType'] == 'image'){ //用户发送图文消息
        // TODO
    }elseif($postArr['MsgType'] == 'event' && $postArr['Event']=='user_enter_tempsession'){ //用户进入客服
        // TODO
    }else{
        exit('error');
    }
}

其中,postArr['FromUserName'],就是用户的 openid。

Step 4.2

向用户发送消息,分为两种情况:

  • 被动回复用户消息;
  • 主动向用户发送消息。

Step 4.2.1  被动回复

以下是公众号的方法,小程序/小游戏的没有试。公众号支持无限自动回复,因为无需生成 access_token,也不用开通微信认证,这是基本的接口权限。

被动回复实际上是对用户消息的即时响应,因此不需要调用微信的 API 接口。回复方式也很简单,直接 echo $xml 即可。这一点在微信开发文档 回复文本消息 | 微信开放文档 中说得语焉不详。

需要注意的是:

  • 消息的接收和回复,都是 xml 格式,为了便于处理,都进行了转 json ;
  • 回复消息里的  FromUserName 是公众号的「原始 ID(以下名为ORGID)」,而不是 「开发者ID(AppID)」;
  • 小程序/小游戏主动发送消息时,不需要 FromUserName,只需要 touser,并且要调用 API:
    https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=access_token
{
    $postStr = file_get_contents('php://input');

    if (!empty($postStr)){
        $xmlstring = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
        $postArr = json_decode(json_encode($xmlstring),true);
        $userOpenId = $postArr['FromUserName'];
        if(!empty($postArr['MsgType']) && $postArr['MsgType'] == 'text'){   //用户发送文本消息
            // 把用户的消息原样发送给用户。
            $cont = $postArr["Content"];

            // 网页接口调试时要最后执行,因可能的报错会导致后续代码不执行。 
            if($cont == "1"){
                $txt = "您好";
                $data=array(
                    "ToUserName"=>$userOpenId,
                    "FromUserName"=>ORGID,  // 
                    "CreateTime"=>time(),
                    "MsgType"=>"text",
                    "Content"=>$txt,
                );
                $xml = arrayToXml($data);

                header("Content-Type:text/xml");
                echo $xml;
            }else{

            }
        }else{
            echo "success";
            exit;
        }
    }else{
        echo "success";
        exit;
    }
}

Step 4.2.2 主动发送

以下是小游戏的方法,小程序原则上相同,公众号没有试。公众号不开通微信认证,没有主动发送消息的接口权限。

简单地说,要发文字消息,或图片,首先把目标用户(postArr['FromUserName'], openid)、消息类型(text/image)、消息(text/image)内容打成一个 json 包,然后再调用 requestAPI 发送。图片用 media_id 表示,可以通过网页访问 URL 从服务器向微信上传图片,记录并返回 media_id。getAccessToken(APPID,SECRET) 需要 APPID 和 SECRET。

注意,access_token 是一个具有时效性的口令,每次获取前要检查是否已经有,获取了新的之后还要保存。

function sendText2User($usr,$txt){
    $data=array(
        "touser"=>$usr,
        "msgtype"=>"text",
        "text"=>array("content"=>$txt)
    );
    $json = json_encode($data,JSON_UNESCAPED_UNICODE);  //php5.4+
    requestAPI($json);
}

function sendImage2User($usr,$media_id){
    $data=array(
        "touser"=>$usr,
        "msgtype"=>"image",
        "image"=>array("media_id"=>$media_id)
    );
    $json = json_encode($data,JSON_UNESCAPED_UNICODE);  //php5.4+
    requestAPI($json);
}

function requestAPI($json){
    $access_token = get_accessToken();
    /* 
     * POST发送https请求客服接口api
     */
    if(!empty($access_token)){
        $url = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=".$access_token;
        //以'json'格式发送post的https请求
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_POST, 1); // 发送一个常规的Post请求
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
        if (!empty($json)){
            curl_setopt($curl, CURLOPT_POSTFIELDS,$json);
        }
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        //curl_setopt($curl, CURLOPT_HTTPHEADER, $headers );
        $output = curl_exec($curl);
        if (curl_errno($curl)) {
            echo 'Errno'.curl_error($curl);//捕抓异常
        }
        curl_close($curl);
        if($output == 0){
            echo 'success';
            exit;
        }
    }else{
        echo "";
        exit;
    }
    
}       
/* 调用微信api,获取access_token,有效期7200s*/
function get_accessToken(){
    $token = "";
    $tokenarr = readDataFile2Array("token.txt");
    $tokentime0 = $tokenarr[1];
    $tokentime1 = gmmktime();
    if($tokentime1-$tokentime0 < 7200  && !empty($tokenarr[0])){
        $token = $tokenarr[0];
    }else{
        $token = getAccessToken(APPID,SECRET);

        $tokentime = gmmktime();
        $tokendate = date('Y-m-d H:i:s');
        writeString2File($token."\r\n".$tokentime."\r\n".$tokendate,"token.txt");
    }

    return $token;
}


function getAccessToken($appid,$secret){
    $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$appid}&secret={$secret}";
    $res = curl_get($url);
    $res = json_decode($res,1);
    if($res['errcode']!=0) throw new Exception($res['errmsg']);
    return $res['access_token'];
}

function curl_get($url) {
     $headers = array('User-Agent:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36');
    $oCurl = curl_init();
    if(stripos($url,"https://")!==FALSE){
        curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
    }
    curl_setopt($oCurl, CURLOPT_TIMEOUT, 20);
    curl_setopt($oCurl, CURLOPT_URL, $url);
    curl_setopt($oCurl, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );
    $sContent = curl_exec($oCurl);
    $aStatus = curl_getinfo($oCurl);
    curl_close($oCurl);
    if(intval($aStatus["http_code"])==200){
        return $sContent;
    }else{
        return false;
    }
}

function getMediaId_image($token, $filepath){
    //$url = "https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=".$token."&type=image";
    $url = "https://api.weixin.qq.com/cgi-bin/media/upload?access_token=".$token."&type=image";
    
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_HEADER, 0);    
    curl_setopt($ch, CURLOPT_NOBODY, 0);    //只取body头
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
     //发送 POST 请求
    curl_setopt($ch, CURLOPT_POST, true);     
    //全部数据使用HTTP协议中的 "POST" 操作来发送。 


    if (class_exists('\CURLFile')){
        curl_setopt($ch, CURLOPT_SAFE_UPLOAD, true);
        $data = array('media' => new \CURLFile($filepath));//
    }else{
        curl_setopt($ch,CURLOPT_SAFE_UPLOAD,false);
        $data = array('media'=>'@'.$filepath);
    }


    //$data = array('media'=>'@'.$filepath);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    $res = curl_exec( $ch );
    curl_close( $ch );
    if($res){
            $res = json_decode($res,true);
            return $res;
    }
    else return null;
}

附1  用户登录

 在用户登录小程序、小游戏的时候,就已经得到了 openid,方法如下:

在前端,用户登录时,获得用户的 code,然后发送给本小程序/小游戏服务器,请求微信得到 openid。

前端代码:

function getOpenId() {
    wx.login({
      success(res) {
        //console.log('code===', res.code)
        wx.request({
          url: 'https://www.xxx.com/getOpenId.php',
          data: {
            code: res.code
          },
          success(res1) {
            userInfo["openId"] = res1.data
            //console.log('获取成功:' + userInfo["openId"] )
            getUserInfo()
          },
          fail(res2) {
            //console.log('获取失败:' + res2)
          }
        })
      }
    })
}

function getUserInfo(){
  let exportJson = {};
  let sysInfo = wx.getSystemInfoSync();
  //获取微信界面大小
  let width = sysInfo.screenWidth;
  let height = sysInfo.screenHeight;
  wx.getSetting({
    success(res) {
     //console.log(res.authSetting);
      if (res.authSetting["scope.userInfo"]) {
       //console.log("用户已授权");
        wx.getUserInfo({
          success(res) {
           //console.log(res);
            exportJson.userInfo = res.userInfo;
            //此时可进行登录操作

            userInfo["nickName"] = res.userInfo.nickName
            userInfo["avatarUrl"] = res.userInfo.avatarUrl
            userInfo["language"] = res.userInfo.language
            userInfo["gender"] = res.userInfo.gender
            userInfo["country"] = res.userInfo.country
            userInfo["province"] = res.userInfo.province

            recordLogin()
          }
        });
      } else {
       //console.log("用户未授权");
        let button = wx.createUserInfoButton({
          type: 'text',
          text: '',
          style: {
            left: 0,
            top: 0,
            width: width,
            height: height,
            backgroundColor: '#00000000',//最后两位为透明度
            color: '#ffffff',
            fontSize: 20,
            textAlign: "center",
            lineHeight: height,
          }
        });
        button.onTap((res) => {
          if (res.userInfo) {
           //console.log("用户授权:", res);
            exportJson.userInfo = res.userInfo;
            //此时可进行登录操作
            button.destroy();

            userInfo["nickName"] = res.userInfo.nickName
            userInfo["avatarUrl"] = res.userInfo.avatarUrl
            userInfo["language"] = res.userInfo.language
            userInfo["gender"] = res.userInfo.gender
            userInfo["country"] = res.userInfo.country
            userInfo["province"] = res.userInfo.province

            recordLogin()
          } else {
            //console.log("用户拒绝授权:", res);
            // TODO
          }
        });
      }
    }
  })
}

function recordLogin(){
  var curTime = new Date();
  var loginTime = util.formatTime(curTime);

  wx.request({
    url: 'https://XXX/userLogin.php', //仅为示例,并非真实的接口地址
    data: {
      openId: userInfo["openId"],
      nickName: userInfo["nickName"],
      avatarUrl: userInfo["avatarUrl"],
      language: userInfo["language"],
      gender: userInfo["gender"],
      country: userInfo["country"],
      province: userInfo["province"],
      loginTime: loginTime,
    },
    header: {
      'content-type': 'application/json' // 默认值
    },
    success(res) {
      //console.log(res.data)
      // TODO
    }
  })
}

后端代码:即前端里的 https://www.xxx.com/getOpenId.php

function getOpenid() {
    $code = $_GET['code'];//小程序传来的code值

    $appid = 'XXX';//小程序的appid
    $appSecret = 'XXX';// 小程序的$appSecret
    $wxUrl = 'https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code';
    $getUrl = sprintf($wxUrl, $appid, $appSecret, $code);//把appid,appsecret,code拼接到url里
    $result = curl_get($getUrl);//请求拼接好的url
    $wxResult = json_decode($result, true);
    if (empty($wxResult)) {
        echo '获取openid时异常,微信内部错误';
    } else {
        $loginFail = array_key_exists('errcode', $wxResult);
        if ($loginFail) {//请求失败
            var_dump($wxResult);
        } else {//请求成功
            $openid = $wxResult['openid'];
            //echo "获取openid成功成功:" . $openid;
            echo $openid;
        }
    }
}

附2  关于口令的使用

上述过程中使用了多种口令:

  1. 开通接口:仅需要 TOKEN;
  2. 向用户被动回复消息:不需调用 API 接口,仅需要 ORGID;
  3. 向用户主动发消息:需要 access_token;
  4. 上传图片,需要 access_token;
  5. 获取 access_token,需要 APPID, APPSECRET;【注意,access_token 是一个具有时效性的口令,每次获取前要检查是否已经有,获取了新的之后还要保存。】
  6. 获取用户 openid,需要 APPID, APPSECRET。

附3  关于接口调试

微信提供了在线接口调试:微信公众平台接口调试工具

要点:

  1. ToUserName,就是 ORGID;
  2. FromUserName,可以用个人的 openid;
  3. CreateTime,MsgId,都填1;
  4. 数据类型:公众号应选 xml;其他可选 json,但最终 php 用的都是 json 解析;
  5. 检查问题后,返回调试信息,其中的内容来自:requestAPI($json)中的 echo 'success'; ,其中的空格将自动替换为 +。其实直接 echo $output; 更有用。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值