231 lines
7.1 KiB
PHP
Raw Normal View History

2025-08-01 11:39:06 +08:00
<?php
// +----------------------------------------------------------------------
// | WeChatDeveloper
// +----------------------------------------------------------------------
// | 版权所有 2014~2022 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// +----------------------------------------------------------------------
// | github开源项目https://github.com/zoujingli/WeChatDeveloper
// +----------------------------------------------------------------------
namespace WeChat\Contracts;
use WeChat\Exceptions\InvalidArgumentException;
use WeChat\Exceptions\InvalidDecryptException;
use WeChat\Exceptions\InvalidResponseException;
/**
* 微信通知处理基本类
* Class BasicPushEvent
* @package WeChat\Contracts
*/
class BasicPushEvent
{
/**
* 公众号APPID
* @var string
*/
protected $appid;
/**
* 公众号推送XML内容
* @var string
*/
protected $postxml;
/**
* 公众号推送加密类型
* @var string
*/
protected $encryptType;
/**
* 公众号的推送请求参数
* @var DataArray
*/
protected $input;
/**
* 当前公众号配置对象
* @var DataArray
*/
protected $config;
/**
* 公众号推送内容对象
* @var DataArray
*/
protected $receive;
/**
* 准备回复的消息内容
* @var array
*/
protected $message;
/**
* BasicPushEvent constructor.
* @param array $options
* @throws \WeChat\Exceptions\InvalidResponseException
*/
public function __construct(array $options)
{
if (empty($options['appid'])) {
throw new InvalidArgumentException("Missing Config -- [appid]");
}
if (empty($options['appsecret'])) {
throw new InvalidArgumentException("Missing Config -- [appsecret]");
}
if (empty($options['token'])) {
throw new InvalidArgumentException("Missing Config -- [token]");
}
// 参数初始化
$this->config = new DataArray($options);
$this->input = new DataArray($_REQUEST);
$this->appid = $this->config->get('appid');
// 推送消息处理
if ($_SERVER['REQUEST_METHOD'] == "POST") {
$this->postxml = file_get_contents("php://input");
$this->encryptType = $this->input->get('encrypt_type');
if ($this->isEncrypt()) {
if (empty($options['encodingaeskey'])) {
throw new InvalidArgumentException("Missing Config -- [encodingaeskey]");
}
if (!class_exists('Prpcrypt', false)) {
require __DIR__ . '/Prpcrypt.php';
}
$prpcrypt = new \Prpcrypt($this->config->get('encodingaeskey'));
$result = Tools::xml2arr($this->postxml);
$array = $prpcrypt->decrypt($result['Encrypt']);
if (intval($array[0]) > 0) {
throw new InvalidResponseException($array[1], $array[0]);
}
list($this->postxml, $this->appid) = [$array[1], $array[2]];
}
$this->receive = new DataArray(Tools::xml2arr($this->postxml));
} elseif ($_SERVER['REQUEST_METHOD'] == "GET" && $this->checkSignature()) {
@ob_clean();
exit($this->input->get('echostr'));
} else {
throw new InvalidResponseException('Invalid interface request.', '0');
}
}
/**
* 消息是否需要加密
* @return boolean
*/
public function isEncrypt()
{
return $this->encryptType === 'aes';
}
/**
* 回复消息
* @param array $data 消息内容
* @param boolean $return 是否返回XML内容
* @param boolean $isEncrypt 是否加密内容
* @return string
* @throws \WeChat\Exceptions\InvalidDecryptException
*/
public function reply(array $data = [], $return = false, $isEncrypt = false)
{
$xml = Tools::arr2xml(empty($data) ? $this->message : $data);
if ($this->isEncrypt() || $isEncrypt) {
if (!class_exists('Prpcrypt', false)) {
require __DIR__ . '/Prpcrypt.php';
}
$prpcrypt = new \Prpcrypt($this->config->get('encodingaeskey'));
// 如果是第三方平台,加密得使用 component_appid
$component_appid = $this->config->get('component_appid');
$appid = empty($component_appid) ? $this->appid : $component_appid;
$array = $prpcrypt->encrypt($xml, $appid);
if ($array[0] > 0) throw new InvalidDecryptException('Encrypt Error.', '0');
list($timestamp, $encrypt) = [time(), $array[1]];
$nonce = rand(77, 999) * rand(605, 888) * rand(11, 99);
$tmpArr = [$this->config->get('token'), $timestamp, $nonce, $encrypt];
sort($tmpArr, SORT_STRING);
$signature = sha1(implode($tmpArr));
$format = "<xml><Encrypt><![CDATA[%s]]></Encrypt><MsgSignature><![CDATA[%s]]></MsgSignature><TimeStamp>%s</TimeStamp><Nonce><![CDATA[%s]]></Nonce></xml>";
$xml = sprintf($format, $encrypt, $signature, $timestamp, $nonce);
}
if ($return) return $xml;
@ob_clean();
echo $xml;
}
/**
* 验证来自微信服务器
* @param string $str
* @return bool
*/
private function checkSignature($str = '')
{
$nonce = $this->input->get('nonce');
$timestamp = $this->input->get('timestamp');
$msg_signature = $this->input->get('msg_signature');
$signature = empty($msg_signature) ? $this->input->get('signature') : $msg_signature;
$tmpArr = [$this->config->get('token'), $timestamp, $nonce, $str];
sort($tmpArr, SORT_STRING);
return sha1(implode($tmpArr)) === $signature;
}
/**
* 获取公众号推送对象
* @param null|string $field 指定获取字段
* @return array
*/
public function getReceive($field = null)
{
return $this->receive->get($field);
}
/**
* 获取当前微信OPENID
* @return string
*/
public function getOpenid()
{
return $this->receive->get('FromUserName');
}
/**
* 获取当前推送消息类型
* @return string
*/
public function getMsgType()
{
return $this->receive->get('MsgType');
}
/**
* 获取当前推送消息ID
* @return string
*/
public function getMsgId()
{
return $this->receive->get('MsgId');
}
/**
* 获取当前推送时间
* @return integer
*/
public function getMsgTime()
{
return $this->receive->get('CreateTime');
}
/**
* 获取当前推送公众号
* @return string
*/
public function getToOpenid()
{
return $this->receive->get('ToUserName');
}
}