微信公众号开发 – 苏demo的别样人生 https://www.libaocai.com 行走于凡尘俗世,活得别样人生 Sat, 21 Mar 2020 09:41:00 +0000 zh-CN hourly 1 https://wordpress.org/?v=6.2.4 微信支付退款 注意事项 https://www.libaocai.com/7000.html Sat, 21 Mar 2020 09:41:00 +0000 http://www.libaocai.com/?p=7000 当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,微信支付将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。

注意:

1、交易时间超过一年的订单无法提交退款

2、微信支付退款支持单笔交易分多次退款,多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。申请退款总金额不能超过订单金额。 一笔退款失败后重新提交,请不要更换退款单号,请使用原商户退款单号

3、请求频率限制:150qps,即每秒钟正常的申请退款请求次数不超过150次

    错误或无效请求频率限制:6qps,即每秒钟异常或错误的退款申请请求不超过6次

4、每个支付订单的部分退款次数不能超过50次

转载请注明:苏demo的别样人生 » 微信支付退款 注意事项

]]>
php 微信发红包类及示例应用 https://www.libaocai.com/6611.html Tue, 28 Nov 2017 02:51:29 +0000 http://www.libaocai.com/?p=6611

微信红包类

<?php

/**
 * 微信红包的类
 *
 */
class WechatRed
{

    private $mch_id       = "111111";//商户ID写死
    private $wxappid      = "22222222";//微信公众号,写死
    private $client_ip    = "119.29.80.114"; //调用红包接口的主机的IP,服务端IP,写死,即脚本文件所在的IP
    private $apikey       = "33333333";//pay的秘钥值
    private $total_num    = 1;//发放人数。固定值1,不可修改
    private $nick_name    = "微信产品中心公众号"; //红包商户名称
    private $send_name    = "微信产品中心公众号";//红包派发者名称
    private $wishing      = "祝福语"; //
    private $act_name     = "红包活动"; //活动名称
    private $remark       = "活动备注";
    private $nonce_str    = "";
    private $mch_billno   = "";
    private $re_openid    = "";//接收方的openID
    private $total_amount = 1;//红包金额,单位 分
    private $min_value    = 1;//最小金额
    private $max_value    = 1; //根据接口要求,上述3值必须一致
    private $sign         = ""; //签名在send时生成
    private $amt_type; //分裂红包参数,在sendgroup中进行定义,是常量 ALL_RAND

    //证书,在构造函数中定义,注意!
    private $apiclient_cert; //= getcwd()."/apiclient_cert.pem";
    private $apiclient_key;// = getcwd()."/apiclient_key.pem";

    //分享参数
    private $isShare       = false; //有用?似乎是无用参数,全部都不是必选和互相依赖的参数
    private $share_content = "";
    private $share_url     = "";
    private $share_imgurl  = "";

    private $wxhb_inited;

    private $api_hb_group  = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendgroupredpack";//裂变红包
    private $api_hb_single = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";

    private $error = "ok"; //init


    /**
     * WXHongBao::__construct()
     * 步骤
     * new(openid,amount)
     * setnickname
     * setsend_name
     * setwishing
     * setact_name
     * setremark
     * send()
     * @return void
     */
    public function __construct()
    {
        //好像没有什么需要构造函数做的
        $this->wxhb_inited    = false;
        $this->apiclient_cert = getcwd() . "/apiclient_cert.pem";
        $this->apiclient_key  = getcwd() . "/apiclient_key.pem";
    }

    public function err()
    {
        return $this->error;
    }

    public function error()
    {
        return $this->err();
    }

    /**
     * WXHongBao::newhb()
     * 构造新红包
     * @param mixed $toOpenId
     * @param mixed $amount 金额分
     * @return void
     */
    public function newhb($toOpenId, $amount)
    {

        if (!is_numeric($amount)) {
            $this->error = "金额参数错误";
            return;
        } elseif ($amount < 100) {
            $this->error = "金额太小";
            return;
        } elseif ($amount > 20000) {
            $this->error = "金额太大";
            return;
        }

        $this->gen_nonce_str();//构造随机字串
        $this->gen_mch_billno();//构造订单号
        $this->setOpenId($toOpenId);
        $this->setAmount($amount);
        $this->wxhb_inited = true; //标记微信红包已经初始化完毕可以发送

        //每次new 都要将分享的内容给清空掉,否则会出现残余被引用
        $this->share_content = "";
        $this->share_imgurl  = "";
        $this->share_url     = "";
    }

    /**
     * WXHongBao::send()
     * 发出红包
     * 构造签名
     * 注意第二参数,单发时不要改动!
     * @return boolean $success
     */
    public function send($url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack", $total_num = 1)
    {

        if (!$this->wxhb_inited) {
            $this->error .= "(红包未准备好)";
            return false; //未初始化完成
        }

        $this->total_num = $total_num;

        $this->gen_Sign(); //生成签名

        //构造提交的数据
        $xml = $this->genXMLParam();


        //debug
        file_put_contents("hbxml.txt", $xml);

        //提交xml,curl
        //$url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLCERT, $this->apiclient_cert);
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLKEY, $this->apiclient_key);

        /*
        if( count($aHeader) >= 1 ){
          curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);
        }
        */
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        $data = curl_exec($ch);
        if ($data) {
            curl_close($ch);
            $rsxml = simplexml_load_string($data);
            if ($rsxml->return_code == 'SUCCESS') {
                return true;
            } else {
                $this->error = $rsxml->return_msg;
                return false;
            }

        } else {
            $this->error = curl_errno($ch);

            curl_close($ch);
            return false;
        }

    }

    /**
     * WXHongBao::sendGroup()
     * 发送裂变红包,参数为裂变数量
     * @param int $num
     * @return bool
     */
    public function sendGroup($num = 3)
    {
        $this->amt_type = "ALL_RAND";//$amt; 固定值。发送裂变红包组文档指定参数,随机
        return $this->send($this->api_hb_group, $num);
    }

    public function getApiSingle()
    {
        return $this->api_hb_single;
    }

    public function getApiGroup()
    {
        return $this->api_hb_group;
    }

    public function setNickName($nick)
    {
        $this->nick_name = $nick;
    }

    public function setSendName($name)
    {
        $this->send_name = $name;
    }

    public function setWishing($wishing)
    {
        $this->wishing = $wishing;
    }

    /**
     * WXHongBao::setActName()
     * 活动名称
     * @param mixed $act
     * @return void
     */
    public function setActName($act)
    {
        $this->act_name = $act;
    }

    public function setRemark($remark)
    {
        $this->remark = $remark;
    }

    public function setOpenId($openid)
    {
        $this->re_openid = $openid;
    }

    /**
     * WXHongBao::setAmount()
     * 设置红包金额
     * 文档有两处冲突描述
     * 一处指金额 >=1 (分钱)
     * 另一处指金额 >=100 < 20000 [1-200元]
     * 有待测试验证!
     * @param mixed $price 单位 分
     * @return void
     */
    public function setAmount($price)
    {
        $this->total_amount = $price;
        $this->min_value    = $price;
        $this->max_value    = $price;
    }

    //以下方法,为设置分裂红包时使用
    public function setHBminmax($min, $max)
    {
        $this->min_value = $min;
        $this->max_value = $max;
    }

    /**
     * 设置分享
     * @param string $img
     * @param string $url
     * @param string $content
     */
    public function setShare($img = "", $url = "", $content = "")
    {
        $this->share_content = $content;
        $this->share_imgurl  = $img;
        $this->share_url     = $url;
    }

    /**
     * 生成nonce_str
     */
    private function gen_nonce_str()
    {
        $this->nonce_str = strtoupper(md5(mt_rand() . time())); //确保不重复而已
    }

    /**
     * 生成签名
     */
    private function gen_Sign()
    {
        unset($param);
        //其实应该用key重排一次 right?
        $param["act_name"] = $this->act_name;//

        if ($this->total_num == 1) { //这些是裂变红包用不上的参数,会导致签名错误
            $param["client_ip"] = $this->client_ip;
            $param["max_value"] = $this->max_value;
            $param["min_value"] = $this->min_value;
            $param["nick_name"] = $this->nick_name;
        }

        $param["mch_billno"]   = $this->mch_billno;  //
        $param["mch_id"]       = $this->mch_id;//
        $param["nonce_str"]    = $this->nonce_str;  //
        $param["re_openid"]    = $this->re_openid;//
        $param["remark"]       = $this->remark;    //
        $param["send_name"]    = $this->send_name;//
        $param["total_amount"] = $this->total_amount;//
        $param["total_num"]    = $this->total_num;    //
        $param["wishing"]      = $this->wishing;//
        $param["wxappid"]      = $this->wxappid;//

        if ($this->share_content) $param["share_content"] = $this->share_content;
        if ($this->share_imgurl) $param["share_imgurl"] = $this->share_imgurl;
        if ($this->share_url) $param["share_url"] = $this->share_url;

        if ($this->amt_type) $param["amt_type"] = $this->amt_type; //

        ksort($param); //按照键名排序...艹,上面排了我好久

        //$sign_raw = http_build_query($param)."&key=".$this->apikey;
        $sign_raw = "";
        foreach ($param as $k => $v) {
            $sign_raw .= $k . "=" . $v . "&";
        }
        $sign_raw .= "key=" . $this->apikey;

        //file_put_contents("sign.raw",$sign_raw);//debug
        $this->sign = strtoupper(md5($sign_raw));
    }

    /**
     * WXHongBao::genXMLParam()
     * 生成post的参数xml数据包
     * 注意生成之前各项值要生成,尤其是Sign
     * @return $xml
     */
    public function genXMLParam()
    {

        $xml = "<xml>
      <sign>" . $this->sign . "</sign> 
      <mch_billno>" . $this->mch_billno . "</mch_billno> 
      <mch_id>" . $this->mch_id . "</mch_id> 
      <wxappid>" . $this->wxappid . "</wxappid> 
      <nick_name><![CDATA[" . $this->nick_name . "]]></nick_name> 
      <send_name><![CDATA[" . $this->send_name . "]]></send_name> 
      <re_openid>" . $this->re_openid . "</re_openid> 
      <total_amount>" . $this->total_amount . "</total_amount> 
      <min_value>" . $this->min_value . "</min_value> 
      <max_value>" . $this->max_value . "</max_value> 
      <total_num>" . $this->total_num . "</total_num> 
      <wishing><![CDATA[" . $this->wishing . "]]></wishing> 
      <client_ip><![CDATA[" . $this->client_ip . "]]></client_ip> 
      <act_name><![CDATA[" . $this->act_name . "]]></act_name> 
      <remark><![CDATA[" . $this->remark . "]]></remark>       
      <nonce_str>" . $this->nonce_str . "</nonce_str>
      ";


        if ($this->share_content) $xml .= "<share_content><![CDATA[" . $this->share_content . "]]></share_content>
    ";
        if ($this->share_imgurl) $xml .= "<share_imgurl><![CDATA[" . $this->share_imgurl . "]]></share_imgurl>
    ";
        if ($this->share_url) $xml .= "<share_url><![CDATA[" . $this->share_url . "]]></share_url>
    ";
        if ($this->amt_type) $xml .= "<amt_type><![CDATA[" . $this->amt_type . "]]></amt_type>
    ";

        $xml .= "</xml>";

        return $xml;
    }

    /**
     * WXHongBao::gen_mch_billno()
     * 商户订单号(每个订单号必须唯一)
     * 组成: mch_id+yyyymmdd+10位一天内不能重复的数字。
     * 接口根据商户订单号支持重入, 如出现超时可再调用。
     * @return void
     */
    private function gen_mch_billno()
    {
        //生成一个长度10,的阿拉伯数字随机字符串
        $rnd_num = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
        $rndstr  = "";
        while (strlen($rndstr) < 10) {
            $rndstr .= $rnd_num[array_rand($rnd_num)];
        }

        $this->mch_billno = $this->mch_id . date("Ymd") . $rndstr;
    }
}

示例应用

$toOpenId = 'asdasdasd'; //接收红包的用户的微信OpenId
$hb = new WechatRed();

$hb->newhb($toOpenId,1000); //新建一个10元的红包,第二参数单位是 分,注意取值范围 1-200元
//以下若干项可选操作,不指定则使用class脚本顶部的预设值
$hb->setNickName("土豪有限公司");
$hb->setSendName("土豪");
$hb->setWishing("恭喜发财");
$hb->setActName("发钱活动");
$hb->setRemark("任性一把");

//发送红包

if(!$hb->send()){ //发送错误

echo $hb->err();

}else{

echo "红包发送成功";

}

 

 

转载请注明:苏demo的别样人生 » php 微信发红包类及示例应用

]]>
微信公众号获取用户地理位置 https://www.libaocai.com/6588.html Fri, 17 Nov 2017 08:57:30 +0000 http://www.libaocai.com/?p=6588

微信公众开放平台 接口文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140841

获取用户地理位置(需要用户点击同意)

需要在微信公众平台,接口权限中开通上报地理位置接口。

在用户点击同意后,会上报用户当前的地理位置信息,第三方在收到地理位置上报信息之后,只需要回复success表明收到即可,是不允许回复消息给粉丝的。

推送XML数据包示例:

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[LOCATION]]></Event>
<Latitude>23.137466</Latitude>
<Longitude>113.352425</Longitude>
<Precision>119.385040</Precision>
</xml>

参数说明:

参数 说明
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType 消息类型,event
Event 事件类型,LOCATION
Latitude 地理位置纬度
Longitude 地理位置经度
Precision 地理位置精度

开发者响应微信发送的数据信息:

 

if($postObj->Event == 'LOCATION'){
    $Latitude = $postObj->Latitude;
    $Longitude= $postObj->Longitude;
    //返回给用户
    $contentStr = "我们收到您上报的地理位置:\n\n 纬度是:{$Latitude} \n,经度是{$Longitude}";
    $resultStr = sprintf($textTpl,$fromUsername,$toUsername,$time,'text',$contentStr);
    echo $resultStr;
}

 

 

 

 

 

转载请注明:苏demo的别样人生 » 微信公众号获取用户地理位置

]]>
微信公众号生成带参数的二维码 https://www.libaocai.com/6586.html Fri, 17 Nov 2017 08:10:44 +0000 http://www.libaocai.com/?p=6586

在微信公众号公众平台接口文档中,有一个接口是为了满足用户渠道推广分析及功能等场景的需要而开放的接口就是带参数二维码的接口。

目前有两种二维码类型:

1.临时二维码

有过期时间,最长30天(2592000s),能够生成较多数量,主要用于账号绑定等不要求二维码永久保存的业务场景。

2.永久二维码

无过期时间,数量较少(最多10万个),适用于账号绑定、用户来源统计等业务场景。

 

当用户扫描带有场景值的二维码时,会推送下面两种事件:

1.当用户位关注公众号时,用户可以关注公众号,关注后微信将场景值关注事件推送给开发者;

2.当用户已经关注过公众号时,在用户扫描二维码,会自动进入会话界面,微信也会将场景值扫描时间推送给开发者。

 

操作步骤

1.创建二维码ticket

每次创建二维码ticket需要提供开发者自行设定的参数scene_id.

临时二维码

http请求方式: POST
URL: https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKENPOST数据格式:json
POST数据例子:{“expire_seconds”: 604800, “action_name”: “QR_SCENE”, “action_info”: {“scene”: {“scene_id”: 123}}}

或者也可以使用以下POST数据创建字符串形式的二维码参数:
{“expire_seconds”: 604800, “action_name”: “QR_STR_SCENE”, “action_info”: {“scene”: {“scene_str”: “test”}}}

永久二维码

http请求方式: POST
URL: https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKENPOST数据格式:json
POST数据例子:{“action_name”: “QR_LIMIT_SCENE”, “action_info”: {“scene”: {“scene_id”: 123}}}

或者也可以使用以下POST数据创建字符串形式的二维码参数:
{“action_name”: “QR_LIMIT_STR_SCENE”, “action_info”: {“scene”: {“scene_str”: “test”}}}

参数说明

参数 说明
expire_seconds 该二维码有效时间,以秒为单位。 最大不超过2592000(即30天),此字段如果不填,则默认有效期为30秒。
action_name 二维码类型,QR_SCENE为临时的整型参数值,QR_STR_SCENE为临时的字符串参数值,QR_LIMIT_SCENE为永久的整型参数值,QR_LIMIT_STR_SCENE为永久的字符串参数值
action_info 二维码详细信息
scene_id 场景值ID,临时二维码时为32位非0整型,永久二维码时最大值为100000(目前参数只支持1–100000)
scene_str 场景值ID(字符串形式的ID),字符串类型,长度限制为1到64

返回说明

正确的Json返回结果:

 

{“ticket”:”gQH47joAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL2taZ2Z3TVRtNzJXV1Brb3ZhYmJJAAIEZ23sUwMEmm

3sUw==”,”expire_seconds”:60,”url”:”http:\/\/weixin.qq.com\/q\/kZgfwMTm72WWPkovabbI”}

参数 说明
ticket 获取的二维码ticket,凭借此ticket可以在有效时间内换取二维码。
expire_seconds 该二维码有效时间,以秒为单位。 最大不超过2592000(即30天)。
url 二维码图片解析后的地址,开发者可根据该地址自行生成需要的二维码图片

 

2.通过ticket获取二维码

请求说明

HTTP GET请求(请使用https协议)https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET提醒:TICKET记得进行UrlEncode

返回说明

ticket正确情况下,http 返回码是200,是一张图片,可以直接展示或者下载。

HTTP头(示例)如下:
Accept-Ranges:bytes
Cache-control:max-age=604800
Connection:keep-alive
Content-Length:28026
Content-Type:image/jpg
Date:Wed, 16 Oct 2013 06:37:10 GMT
Expires:Wed, 23 Oct 2013 14:37:10 +0800
Server:nginx/1.4.1

错误情况下(如ticket非法)返回HTTP错误码404。

示例php代码 (临时二维码)

    $url      = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" . $access_token;//获取ticket access_token 详见微信开发文档获取方法
    $scene_id = 3000;//定义的场景值 3000,业务员二维码
    $post     = '{"expire_seconds": 604800, "action_name": "QR_SCENE", "action_info": {"scene": {"scene_id": ' . $scene_id . '}}}';
    $res      = curl_post($url, $post);
    if (!empty($res)) {
        $res       = json_decode($res, true);
        $ticket    = $res['ticket'];
        $url       = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" . urlencode($ticket);
        $file      = file_get_contents($url);
        $file_name = time() . mt_rand(1000, 9999) . '.jpg';
        file_put_contents(ROOT_PATH . 'data/cache/' . $file_name, $file);
        $qrcode_url = 'https://www.libaocai.com/data/cache/' . $file_name;

        echo '获取到的openid是:'.$_SESSION['openid'].'<br/>';

        echo '获取到的二维码是:<br/>';
        echo '<img src="' . $qrcode_url . '"/>';
    }
    else{
        echo '<pre>';
        print_r($res);
        exit;
    }

 

3.微信扫码事件消息处理

switch($postObj->MsgType){
    case 'event':
        if($postObj->Event == 'subscribe'){//首次关注,关注事件
            $scene_id = $postObj ->EventKey;
            $sale_id = str_replace('qrscene_','',$scene_id);
            $contentStr = "hello,您是第一次来到XXX,欢迎您!会员注册,戳这里https://www.libaocai.com/?u=".$sale_id;
            $resultStr = sprintf($textTpl,$fromUsername,$toUsername,$time,'text',$contentStr);
            echo $resultStr;
        }
        else if($postObj->Event == 'SCAN'){//关注过,扫描事件
            $scene_id= $postObj ->EventKey;//scene_id
            //检测是否是3000
            $contentStr = "您真牛!找到我们的业务员推广链接入口!scence_id:".$scene_id;

            $resultStr = sprintf($textTpl,$fromUsername,$toUsername,$time,'text',$contentStr);
            echo $resultStr;
        }
}

ps:这里后续还会有补充。

转载请注明:苏demo的别样人生 » 微信公众号生成带参数的二维码

]]>
微信公众号通过关键词回复图文消息 https://www.libaocai.com/6584.html Fri, 17 Nov 2017 06:11:21 +0000 http://www.libaocai.com/?p=6584

打开部分微信公众号,可以收到微信公众号的推送消息,这些消息有纯文本的 也有图文的,其中图文的还有单条和多条的。

这里我们先做图文消息推送的一些接口准备工作。

找到为微信公众号开发文档,查看接口文档,

文档地址:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140543

回复图文消息的XML代码:

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[news]]></MsgType>
<ArticleCount>2</ArticleCount>
<Articles>
<item>
<Title><![CDATA[title1]]></Title> 
<Description><![CDATA[description1]]></Description>
<PicUrl><![CDATA[picurl]]></PicUrl>
<Url><![CDATA[url]]></Url>
</item>
<item>
<Title><![CDATA[title]]></Title>
<Description><![CDATA[description]]></Description>
<PicUrl><![CDATA[picurl]]></PicUrl>
<Url><![CDATA[url]]></Url>
</item>
</Articles>
</xml>

参数说明

参数 是否必须 说明
ToUserName 接收方帐号(收到的OpenID)
FromUserName 开发者微信号
CreateTime 消息创建时间 (整型)
MsgType news
ArticleCount 图文消息个数,限制为8条以内
Articles 多条图文消息信息,默认第一个item为大图,注意,如果图文数超过8,则将会无响应
Title 图文消息标题
Description 图文消息描述
PicUrl 图片链接,支持JPG、PNG格式,较好的效果为大图360*200,小图200*200
Url 点击图文消息跳转链接

补充说明:

微信服务器在将用户的消息发给公众号的开发者服务器地址(开发者中心处配置)后,微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次,如果在调试中,发现用户无法收到响应的消息,可以检查是否消息处理超时。关于重试的消息排重,有msgid的消息推荐使用msgid排重。事件类型消息推荐使用FromUserName + CreateTime 排重。

假如服务器无法保证在五秒内处理并回复,必须做出下述回复,这样微信服务器才不会对此作任何处理,并且不会发起重试(这种情况下,可以使用客服消息接口进行异步回复),否则,将出现严重的错误提示。详见下面说明:

1、直接回复success(推荐方式)

2、直接回复空串(指字节长度为0的空字符串,而不是XML结构体中content字段的内容为空)

一旦遇到以下情况,微信都会在公众号会话中,向用户下发系统提示“该公众号暂时无法提供服务,请稍后再试”:

1、开发者在5秒内未回复任何内容2、开发者回复了异常数据,比如JSON数据等

另外,请注意,回复图片(不支持gif动图)等多媒体消息时需要预先通过素材管理接口上传临时素材到微信服务器,可以使用素材管理中的临时素材,也可以使用永久素材。

 

发送图文消息,限制数为10,超过10条,则会无响应。

首条图文消息的大图的最佳尺寸是大图360*200,小图200*200,图片建议使用JPG/PNG格式,尽量不要使用GIF格式。

通过关键词来触发自动回复图文消息给用户,需要指定MsgType为text。

设置消息XML模板

<?php
$tmp_arr = array(
    'text' => <<<XML
<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[%s]]></Content>
<FuncFlag>0</FuncFlag>
</xml>
XML
,
    'single_news' => <<<XML
<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[news]]></MsgType>
<ArticleCount>1</ArticleCount>
<Articles>
<item>
<Title><![CDATA[%s]]></Title>
<Description><![CDATA[%s]]></Description>
<PicUrl><![CDATA[%s]]></PicUrl>
<Url><![CDATA[%s]]></Url>
</item>
</Articles>
</xml>
XML
,
    'multi_news' => <<<XML
<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[news]]></MsgType>
<ArticleCount>4</ArticleCount>
<Articles>
<item>
<Title><![CDATA[%s]]></Title>
<Description><![CDATA[%s]]></Description>
<PicUrl><![CDATA[%s]]></PicUrl>
<Url><![CDATA[%s]]></Url>
</item>
<item>
<Title><![CDATA[%s]]></Title>
<Description><![CDATA[%s]]></Description>
<PicUrl><![CDATA[%s]]></PicUrl>
<Url><![CDATA[%s]]></Url>
</item>
<item>
<Title><![CDATA[%s]]></Title>
<Description><![CDATA[%s]]></Description>
<PicUrl><![CDATA[%s]]></PicUrl>
<Url><![CDATA[%s]]></Url>
</item>
<item>
<Title><![CDATA[%s]]></Title>
<Description><![CDATA[%s]]></Description>
<PicUrl><![CDATA[%s]]></PicUrl>
<Url><![CDATA[%s]]></Url>
</item>
</Articles>
</xml>
XML
);

单条图文测试代码

switch($postObj->MsgType){
    case 'text':
        if($keyword == '单条'){
            $news         = M('news')->where('status=1')->order('rand()')->limit(1)->find();//获取单条图文消息
            $title        = $news['title'];
            $description  = $news['intro'];
            $PicUrl       = $news['thumb'];//注意图片的尺寸和格式,可以代码处理也可以使用第三方组件支持(详见博客)
            $Url          = get_news_url($news['id']);//获取图文消息的链接地址

            $resultStr = sprintf($tmp_arr['single_news'],$fromUsername,$toUsername,$time,$title,$description,$PicUrl,$Url);
            echo $resultStr;
        }
}

 

多条图文测试代码

if($keyword == '多条'){

    $news = M('news')->where('status=1')->order('rand()')->limit(5)->find();
    foreach($news as $key=>$val){
        $news[$key]['url'] = get_news_url($val['id']);
    }
    $resultStr =$this->replyNews($postObj,$news);

}

附上多条图文消息的处理xml结构函数

//回复图文消息
public function replyNews($obj,$newsArr)
{
    //判断是否为数组类型
    if(!is_array($newsArr))
    {
        return;
    }
    // 判断数组是否为空数组
    if(!$newsArr)
    {
        return;
    }
    $itemStr = "";
    //定义item模板
    $itemXml = "<item>
          <Title><![CDATA[%s]]></Title> 
          <Description><![CDATA[%s]]></Description>
          <PicUrl><![CDATA[%s]]></PicUrl>
          <Url><![CDATA[%s]]></Url>
        </item>";
    foreach($newsArr as $item)
    {
        $itemStr .= sprintf($itemXml,$item['title'],$item['intro'],$item['thumb'],$item['url']);
    }
    $replyXml = "<xml>
          <ToUserName><![CDATA[%s]]></ToUserName>
          <FromUserName><![CDATA[%s]]></FromUserName>
          <CreateTime>%s</CreateTime>
          <MsgType><![CDATA[news]]></MsgType>
          <ArticleCount>".count($newsArr)."</ArticleCount>
          <Articles>".$itemStr."</Articles>
        </xml>";
    return sprintf($replyXml,$obj->FromUserName,$obj->ToUserName,time());
}

注意多条相对单条的,组合xml结构的方式是Articles里是多条记录内容。

 

 

转载请注明:苏demo的别样人生 » 微信公众号通过关键词回复图文消息

]]>
微信公众号删除自定义菜单 https://www.libaocai.com/6579.html Thu, 16 Nov 2017 05:10:15 +0000 http://www.libaocai.com/?p=6579

删除接口:https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN

成功返回示例

{“errcode”:0,”errmsg”:”ok”}

注释:在个性化菜单的时候,调用此接口会删除默认菜单及全部个性化菜单。

获取access_token ,请详见微信公众号创建自定义菜单文档

转载请注明:苏demo的别样人生 » 微信公众号删除自定义菜单

]]>
微信自定义菜单的click事件推送 https://www.libaocai.com/6577.html Thu, 16 Nov 2017 04:01:02 +0000 http://www.libaocai.com/?p=6577

前面我们实践了微信公众号自定义创建菜单的开发,

示例:

$menu = ‘{

“button”:[

{

“type” : “click”,

“name”:  “济南美食”,

“key” : “V1001_JINAN_FOOD”

},

{

“type” :”click”,

“name”:”济南美景”,

“key” :”V1002_JINAN_SENERY”

},

{

“name” :”商务合作”,

“sub_button”:[

{

“type”:”view”,

“name”:”联系我们”,

“url”: “http://tools.libaocai.com/about”

}

]

}

]

}’;

里面的V1001_JINAN_FOOD 和V1002_JINAN_SENERY 是我们定义的事件key。

在自定义菜单创建生效以后,用户点击济南美景、济南美食,触发的click事件,会推送给开发者,开发者就会接收到来自腾讯服务器推送过来的click事件,根据实际情况进行开发处理。

click事件xml代码:

<xml><ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[FromUser]]></FromUserName> <CreateTime>123456789</CreateTime> <MsgType><![CDATA[event]]></MsgType> <Event><![CDATA[CLICK]]></Event> <EventKey><![CDATA[EVENTKEY]]></EventKey> </xml>

其中的EVENT 就是事件类型,例如CLICK,

EVENTKey 就是事件KEY值,与自定义菜单接口中的KEY值对应。

在wx_sample.php我们需要对接收的消息进行处理,主要是function responseMsg.

例如

public function responseMsg()
{
    $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];

    if (!empty($postStr)){
        libxml_disable_entity_loader(true);
        $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
        $fromUsername = $postObj->FromUserName;
        $toUsername = $postObj->ToUserName;
        $keyword = trim($postObj->Content);
        $time = time();
        $textTpl = "<xml><ToUserName><![CDATA[%s]]></ToUserName>
                            <FromUserName><![CDATA[%s]]></FromUserName>
                            <CreateTime>%s</CreateTime>
                            <MsgType><![CDATA[text]]></MsgType>
                            <Content><![CDATA[%s]]></Content>
                            <FuncFlag>0</FuncFlag>
                            </xml>";
        if($postObj->MsgType=='event'){
            if($postObj->Event == 'CLICK'){
                if($postObj->EventKey == 'V1001_JINAN_FOOD '){

                    $contentStr = "济南美食";
                    $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $contentStr);
                    echo $resultStr;
                }
                else if($postObj->EventKey == 'V1002_JINAN_SENERY  '){

                    $contentStr = "济南美景";
                    $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $contentStr);
                    echo $resultStr;
                }
            }
        }

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

补充说明

这里只是简单的处理回复单文本消息。

转载请注明:苏demo的别样人生 » 微信自定义菜单的click事件推送

]]>
创建自定义菜单 https://www.libaocai.com/6575.html Thu, 16 Nov 2017 03:45:26 +0000 http://www.libaocai.com/?p=6575

操作步骤

1.获取access_token

接口:https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

appid 微信公众号的APPID

secret 微信公众号的appsecret

常用方式通过php curl请求获取返回值,返回值是json格式,进行json处理即可获取access_token。

2.根据上面的获取的access_token编写代码创建自定义菜单

创建菜单的接口:https://api.weixin.qq.com/cgi-bin/menu/create?access_tokeen=ACCESS_TOKEN

菜单示例:

$menu = ‘{

“button”:[

{

“type” : “click”,

“name”:  “济南美食”,

“key” : “V1001_JINAN_FOOD”

},

{

“type” :”click”,

“name”:”济南美景”,

“key” :”V1002_JINAN_SENERY”

},

{

“name” :”商务合作”,

“sub_button”:[

{

“type”:”view”,

“name”:”联系我们”,

“url”: “http://tools.libaocai.com/about”

}

]

}

]

}’;

 

里用http_request函数 ,将参数发送给创建菜单的接口地址。

示例:

function httpRequest($user,$data= null){

$ch = curl_init();

curl_setopt($ch,CURLOPT_URL,$url);

curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);

curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);

if(!empty($data)){

curl_setopt($ch,CURLOPT_POST,1);

curl_setopt($ch,CURL_POSTFIELDS,$data);

}

curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);

$out  =curl_exec($ch);

curl_close($ch);

return $output;

}

coding完成以后,运营,然后到微信公众号对话窗口即可查看菜单是否生效。

 

转载请注明:苏demo的别样人生 » 创建自定义菜单

]]>
微信网页授权获取用户基本信息 https://www.libaocai.com/6572.html Thu, 16 Nov 2017 02:55:00 +0000 http://www.libaocai.com/?p=6572

基本原理:

通过开发者服务器生成接口链接,将链接发送给关注公众号的粉丝,粉丝用户点击链接,会把链接的参数发送给微信服务器,

微信服务器会生成一个code并带到链接里面的redrect_url ,这个URL 就是第三方网页,第三方网页根据获取的code值,通过另外

的接口url请求微信服务器获取access_token,通过access_token 再通过腾讯的URL接口获取用户的OPENID,最后通过OPENID获取用户的基本信息。

相关流程图:

操作步骤

1.通过官方接口生成链接

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

注意参数设置

appid 微信公众号的appid

redirect_uri 第三方页面URL

scope 应用授权作用域 ,分为两种 snsapi_base(不弹出页面授权,直接条全,只能获取哦难怪乎openid) snsapi_userinfo (弹出授权页面,可以通过openid获取用户昵称、性别、所在地, 并且,即使用户未关注公众号,只要点击了授权,也能获取其信息.

如果用户点击了确定授权,会将code参数自动带到redirect_uri后面。

2.注意在微信公众号管理中心,设置OAuth2.0网页授权的回调页面域名信息;

3.获取access_token.

通过上面拿到的code信息,请求官方接口,获取access_token.

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

appid 微信公众号的appid

secret 微信公众号的AppSecret

code 上面获取的code值

请求成功,返回值示例:

{

“access_token” : “ACCESS_TOKEN”,

“expires_in” : 7200,

“refresh_token” : “REFRESH_TOKEN”,

“openid”: “OPENID”,

“scope”: “SCOPE”

}

 

4.获取用户基本信息(需啊哟scope为snsapi_userinfo)

接口:https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN

请求结果返回示例:

{

“openid” : “OPENID”,

“nickname”:NICKNAME,

“sex”:”1″,

“province”:”PROVINCE”,

“city” : “CITY”,

“country”:”COUNTRY”,

“headimgurl”: “HEADIMGURL”,

“privilege”:[

“PRIVILEGE1”

“PRIVILEGE2”

],

“unionid” : “UNIONID”

}

补充说明

redirect_uri 尽量使用https 来确保授权code安全性

通过code获取网页授权的access_token和基础支持中的access_token不同

网页授权接口连接额参数排序必须正确,不能更换,否则,会出错。

 

 

 

 

 

 

转载请注明:苏demo的别样人生 » 微信网页授权获取用户基本信息

]]>
微信公众号 被动回复用户消息函数responseMsg() https://www.libaocai.com/6569.html Thu, 16 Nov 2017 02:23:58 +0000 http://www.libaocai.com/?p=6569

responseMsg函数 ,用于返回消息给微信用户。

该函数存在于微信公共号开发平台提供的示例demo中。

应用示例 (开发者模式下
当微信用户关注某一个公众号后,会对服务器发送一个post关注请求,微信服务器会把这个请求以xml格式发送给开发者服务器,开发服务器则接收到xml结构数据消息,进行处理,并再以xml格式返回给微信SERVER.

函数responseMsg()就是在开发者服务器文件wx_sample.php里面,用于处理开发者服务器接收到的xml消息。

1.参数接收

$postStr = $GLOBALS[‘HTTP_RAW_POST_DATA’];

2处理消息

if(!empty($postStr)){

…….

}

else{

echo ”;

exit;

}

 

函数libxml_disable_entity_loader(true) 防xml内部被注入。

$postObj=simplexml_load_string($postStr,’SimpleXMLElement’,LIBXML_NOCDATA); 将传递过来的消息对象化。

同样可以写为$postObj = simplexml_load_string($postStr);

3.消息内容

$fromUsername = $postObj->FromUserName; //获取微信用户名

$toUsername  = $postObj->ToUserName;//获取开发者用户名

$keyword = trim($postObj->Content);//获取用户发送的消息内容

$time = time();

$textTpl      = "<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[%s]]></MsgType>
<Content><![CDATA[%s]]></Content>
<FuncFlag>0</FuncFlag>
</xml>";

4.发送消息

if (!empty($keyword)) {
    $msgType    = "text";//消息类型
    $contentStr = "Welcome to wechat world!";//消息具体内容
    $resultStr  = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
    echo $resultStr;
} else {
    echo "Input something...";
}

springf() 将$textTpl 里 百分号 %s 替换成我们定义的内容。

补充说明

如果微信用户发送的消息为空,需要返回的消息为

echo "Input something...";

提示用户输入关键词。

 

 

转载请注明:苏demo的别样人生 » 微信公众号 被动回复用户消息函数responseMsg()

]]>