Wechat subscription notification development (tutorial for beginners) wechat engine

Micro engine replication available

Micro engine access bypasses login verification, please check https://blog.csdn.net/fuchto/article/details/118190252

In this article, wechat account:: create ($ID); Functions such as bypass the login verification can not be used directly, and an error will be reported

You need to trace the source to the parameter call in the corresponding method

The first step is to configure the official account to select the corresponding template.

Development of this feature must be a} service number

Official documents https://developers.weixin.qq.com/doc/offiaccount/Subscription_Messages/intro.html

Set up official account number related domain name js domain name authorized domain name

Step 2: create a database


SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for ims_template_log
-- ----------------------------
DROP TABLE IF EXISTS `ims_template_log`;
CREATE TABLE `ims_template_log`  (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `openid` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'user openid',
  `template_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Template id',
  `uniacid` int(11) NULL DEFAULT NULL COMMENT 'official account id',
  `status` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Subscription Status ',
  `number` int(11) NULL DEFAULT 1 COMMENT 'Number of consents',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 61 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Table structure for ims_template_msg
-- ----------------------------
DROP TABLE IF EXISTS `ims_template_msg`;
CREATE TABLE `ims_template_msg`  (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `uniacid` int(11) NULL DEFAULT NULL COMMENT 'official account id',
  `head_title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Page title',
  `headimage` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Avatar address',
  `content` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Show copywriting',
  `bg_image` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Background map',
  `button_text` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Button content',
  `herad_media_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'head portrait media_id',
  `bg_media_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Background map media_id',
  `template` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT 'Template content',
  `createtime` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Table structure for ims_template_sendLog
-- ----------------------------
DROP TABLE IF EXISTS `ims_template_sendLog`;
CREATE TABLE `ims_template_sendLog`  (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `msg_id` int(11) NULL DEFAULT NULL COMMENT 'news id templat_msg',
  `uniacid` int(11) NULL DEFAULT NULL,
  `sum` int(11) NULL DEFAULT NULL COMMENT 'Push number',
  `success` int(11) NULL DEFAULT NULL COMMENT 'Number of successful push',
  `start_time` int(11) NULL DEFAULT NULL COMMENT 'Start data',
  `end_time` int(11) NULL DEFAULT NULL COMMENT 'End time',
  `status` int(2) NULL DEFAULT NULL COMMENT 'Push status: 0=Pushing, 1=Push complete',
  `title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Push template title',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 18 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

SET FOREIGN_KEY_CHECKS = 1;

template_ The template format in MSG is

[
    {
        "name":"Template name",
        "type":"Jump type",
        "appid":"Applet appid",
        "pages":"Applet path",
        "jump_url":"Jump link",
        "template_id":"Template id",
        "Template id":{
            "Template parameters key":"Template parameters value",
            "Template parameters key":"Template parameters value",
            "Template parameters key":"Template parameters value"
        }
    },
    {
        
        "name":"Template name",
        "type":"Jump type",
        "appid":"Applet appid",
        "pages":"Applet path",
        "jump_url":"Jump link",
        "template_id":"Template id",
        "Template id":{
            "Template parameters key":"Template parameters value",
            "Template parameters key":"Template parameters value",
            "Template parameters key":"Template parameters value"
        }
    }
]

Generate corresponding authorization page link

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

If the prompt "this link cannot be accessed", please check whether the parameter is filled in incorrectly and whether you have the authorization scope permission corresponding to the scope parameter.

parameterIs it necessaryexplain
appidyesThe only sign of official account number
redirect_uriyesThe callback link address redirected after authorization. Please use urlEncode to process the link
response_typeyesReturn type, please fill in code
scopeyesApplication authorization scope, snsapi_base (do not pop up the authorization page, jump directly, only get the user openid), snsapi_userinfo (pop up the authorization page. You can get the nickname, gender and location through openid. In addition, even if you don't pay attention, you can get the information as long as the user is authorized)
statenoAfter the reset, the state parameter will be brought. The developer can fill in the parameter value of a-zA-Z0-9, up to 128 bytes
#wechat_redirectyesThis parameter must be taken when directly opening or redirecting page 302

Official website

https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html 

Step 2: exchange code for web access authorization_ token

First of all, please note that the code here is in exchange for a special web page authorization access_ Token, and access in basic support_ The token (the access_token is used to call other interfaces) is different. The official account can get the web page authorization access_ through the following interfaces. token. If the scope of web page authorization is snsapi_base, the authorized access of the web page is obtained in this step_ While token, openid and snsapi are also obtained_ This is the end of the base web authorization process.

Special attention: due to the secret of the official account and the access_ obtained The security level of tokens is very high. They must only be saved on the server and are not allowed to be passed to the client. Subsequent refresh access_token and access_ The steps of obtaining user information through token must also be initiated from the server.

After obtaining the code, request the following link to obtain access_ token: https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

Parameter description

parameterIs it necessaryexplain
appidyesThe only sign of official account number
secretyesappsecret of the official account
codeyesFill in the code parameters obtained in step 1
grant_typeyesFill in as authorization_code

Micro engine code

$id = $_GPC['uniacid'];
    $code = $_GPC['code'];
    $account_api = WeAccount::create($id);
    // Get user openid
    $OauthInfo = $account_api->getOauthInfo($code);

    $openid = $OauthInfo['openid'];
    // Wechat jsapi_ticket
    $Ticket = $account_api->getJsApiTicket();
    // generation timestamp 
    $time = TIMESTAMP;
    // Random string
    $noncestr = mt_rand(10000,99999);
    // Get current page
    $thisUrl = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].'?'.$_SERVER['QUERY_STRING'];
    // Splicing signature
    $str = "jsapi_ticket={$Ticket}&noncestr={$noncestr}&timestamp={$time}&url={$thisUrl}";
    //  Generate signature
    $signature=sha1($str);

    $info = pdo_get('template_msg',['uniacid'=>$id,'id'=>$_GPC['id']]);
   

    $template_info = json_decode($info['template'],true);
    // Get all set templates
    // $templateList = getTemplateList($id);
    $templateId = '';

    foreach ($template_info as $key => $value){
        $templateId .=trim($value['template_id']).',';
    }

    // foreach ($templateList['data'] as $key => $value){
    //     $templateId .=$value['priTmplId'].',';
    // }

    $templateId = substr($templateId,0,strlen($templateId)-1);

    template('platform/template_home');




//Get private template list
function getTemplateList($uniacid){
    $account_api = WeAccount::create($uniacid);
    $token = $account_api->getAccessToken();
    $url  = "https://api.weixin.qq.com/wxaapi/newtmpl/gettemplate?access_token={$token}";
    $response = ihttp_get($url);
    $data = json_decode($response['content'],true);
    return $data;
}

Use open tags in html files to pull up the authorization page

The first step is to import the js file

Introduce the following JS files on the page that needs to call the JS interface: http://res.wx.qq.com/open/js/jweixin-1.6.0.js (supports https)

To further improve the service stability, when the above resources are inaccessible, you can access them instead: http://res2.wx.qq.com/open/js/jweixin-1.6.0.js (supports https)

Note: AMD/CMD standard module loading method is supported

<script src="http://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>

Step 2 configure corresponding parameters

$(document).ready(function() {
		wx.config({
			debug: false, // When the debugging mode is enabled, the return values of all APIs called will be alert ed on the client. To view the incoming parameters, you can open them on the pc side. The parameter information will be printed through the log and will only be printed on the pc side.
			appId: 'appid', // Required, the only sign of official account.
			timestamp: '{$time}', // Required, time stamp to generate signature
			nonceStr: '{$noncestr}', // Required, generate random string of signature
			signature: '{$signature}',// Required, signature
			jsApiList: ['addEventListener'], // Required, list of JS interfaces to be used
			openTagList :['wx-open-subscribe'] // Required open label
		});
	})
	wx.ready(function () {
		// Config information validation will execute the ready method. All interface calls must be obtained after the config interface gets the result. Config is an asynchronous operation of the client. Therefore, if the relevant interface is required when the page is loaded, the relevant interface must be put in the ready function to ensure the correct execution. For the interface that is called only when triggered by the user, it can be called directly without putting it in the ready function
		console.log('Validation succeeded');

	});
	wx.error(function (res) {
		// If the verification of config information fails, the error function will be executed. For example, the verification fails due to the expiration of the signature. The specific error information can be viewed in the debug mode of config or in the returned res parameter. For SPA, the signature can be updated here
		console.log('Validation failed');
	});

Step 3 use open Tags

{$templateId} is the obtained template id. multiple template IDs are separated by {buttons in the development tab are displayed only on the mobile phone and not on the pc

<wx-open-subscribe template="{$templateId}" id="subscribe-btn">
					<script type="text/wxtag-template" slot="style">
						<style>
							.subscribe-btn {
								border: none;
								padding: 10px;
								border-radius: 5px;
								color: #ff605e;
								background-color: #fff;
								font-size: 16px;
							}
						</style>
					</script>
					<script type="text/wxtag-template">
						<button class="subscribe-btn">
							This is the button display text
						</button>
					</script>
				</wx-open-subscribe>

Step 4: submit data to the backend for receiving

var btn = document.getElementById('subscribe-btn');
	//  Loading succeeded
	btn.addEventListener('success', function (e) {
		console.log('success', e.detail);
		if(e.detail.errMsg == "subscribe:ok"){
			console.log('agree');
			console.log(e.detail.subscribeDetails);
			$.ajax({
				type : 'post',
				url : "url",
				async:false,
				data : {
					uniacid : "{$id}",
					openid : "{$openid}",
					template : e.detail.subscribeDetails,
				},
				dataType : 'json',
				success:function(datas){
					console.log(datas);
					if(datas.code == 1){
						alert(datas.msg);
						//   WeixinJSBridge.call('closeWindow');
					}else{
						alert(datas.msg);
					}

				}
			})
		}else{
			console.log('disagree');
		}
	});
	// Loading failed
	btn.addEventListener('error',function (e) {
		alert("Loading failed");
		console.log('fail', e.detail);
	});

Note that in authorization, users can send messages several times if they subscribe several times

Unlike a one-time subscription message, it can only be sent once no matter how many times the user agrees

If the user refuses to receive this message in the sent message, the current user will not send it successfully no matter how many times it is sent to the current user

If the user clicks to receive this message in the sent message, the user clicks the authorization button once on the authorization page, and the number of times to cancel or allow will be increased by one. In the authorization page, the template message allowed outside will not appear in the authorization list

Send message

        

 $uniacid = $_GPC['uniacid'];
    if(empty($uniacid)){
        echo("uniacid official account id Cannot be empty");
        die;
    }
    if(empty($_GPC['id'])){
        echo("news id Cannot be empty");
        die;
    }
    if(empty($_GPC['template_id'])){
        echo("Template id Cannot be empty");
        die;
    }
    $user = pdo_getall("template_log",['uniacid'=>$uniacid,'template_id'=>$_GPC['template_id'],'status'=>'accept','number >'=> 0]);
    $template_info = pdo_get("template_msg",['uniacid'=>$uniacid,'id'=>$_GPC['id']]);
    if($user){
        // Total number
        $count = count($user);
        $i = 0;
        $send_data = [
            'sum' => $count,
            'msg_id' => $template_info['id'],
            'uniacid' => $uniacid,
            'start_time' => TIMESTAMP,
            'status' => 0
        ];
        $template = json_decode($template_info['template'],true);
        foreach ($template as $k => $v){
            if($_GPC['template_id'] == $v['template_id']){
                $send_data['title'] = $v['name'];
            }
        }
        pdo_insert('template_sendLog',$send_data);
        $sendedid = pdo_insertid();
        foreach ($user as $key => $value){

            $status = send_template_msg($uniacid,$template,$value);
            if($status === true){
                $i = $i + 1;
                pdo_update("template_log",['number' => $value['number'] - 1],['id'=>$value['id']]);
            }
        }
        $sended = [
            'status' => 1,
            'success' => $i,
            'end_time' => TIMESTAMP,
        ];
        pdo_update('template_sendLog',$sended,['id'=> $sendedid]);
        echo 'Send complete';
    }else{
        echo "No eligible users";
    }


function send_template_msg($uniacid,$template,$user){
    $account_api = WeAccount::create($uniacid);
    $token = $account_api->getAccessToken();
    $url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/bizsend?access_token={$token}";

    $data['touser'] = $user['openid'];
    $data['template_id'] = $user['template_id'];
    foreach ($template as $key => $value){
        if($value['template_id'] == $user['template_id']){
            if($value['type'] == 1){
                $data['page'] = ($value['jump_url']);
            }elseif($value['type'] == 2){
                $data['miniprogram']['appid'] = $value['appid'];
                $data['miniprogram']['pagepath'] = $value['pages'];
            }
            foreach ($value[$value['template_id']] as $k => $v){
                $data['data'][$k]['value'] = $v;
            }
        }

    }

    $response = ihttp_post($url,json_encode($data));
    $result = json_decode($response['content'],true);


//    In one case, a message has been sent to the user. When the user chooses to refuse to receive such a message in the template, an error 43101 will be reported when sending the message next time. Therefore, set the number of times this message can be sent to 0
//  When the user selects to receive this template message in the received template, it will be permanently received. When selecting access, it will not select the option of this template, so in add_ If the data submitted in the log method and the message data permanently received is consent, the number of times the current template can be sent + 1
    if($result['errcode'] == 43101){
        pdo_update('ims_template_log',['number' => 0],['id'=>$user['id']]);
    }
    if($result['errcode'] == 0){
        return true;
    }else{
        var_dump($result);
        return false;
    }


}

Please contact me if you have any questions

Added by mj99 on Mon, 17 Jan 2022 07:25:35 +0200