国内流行的内容管理系统(CMS)多端全媒体解决方案 https://www.dedebiz.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

202 lines
6.4KB

  1. <?php
  2. namespace WeChat\Contracts;
  3. if (!defined('DEDEINC')) exit ('dedebiz');
  4. use WeChat\Exceptions\InvalidArgumentException;
  5. use WeChat\Exceptions\InvalidDecryptException;
  6. use WeChat\Exceptions\InvalidResponseException;
  7. /**
  8. * 微信通知处理基本类
  9. * Class BasicPushEvent
  10. * @package WeChat\Contracts
  11. */
  12. class BasicPushEvent
  13. {
  14. /**
  15. * 公众号APPID
  16. * @var string
  17. */
  18. protected $appid;
  19. /**
  20. * 公众号推送XML内容
  21. * @var string
  22. */
  23. protected $postxml;
  24. /**
  25. * 公众号推送加密类型
  26. * @var string
  27. */
  28. protected $encryptType;
  29. /**
  30. * 公众号的推送请求参数
  31. * @var DataArray
  32. */
  33. protected $input;
  34. /**
  35. * 当前公众号配置对象
  36. * @var DataArray
  37. */
  38. protected $config;
  39. /**
  40. * 公众号推送内容对象
  41. * @var DataArray
  42. */
  43. protected $receive;
  44. /**
  45. * 准备回复的消息内容
  46. * @var array
  47. */
  48. protected $message;
  49. /**
  50. * BasicPushEvent constructor.
  51. * @param array $options
  52. * @throws \WeChat\Exceptions\InvalidResponseException
  53. */
  54. public function __construct(array $options)
  55. {
  56. if (empty($options['appid'])) {
  57. throw new InvalidArgumentException("Missing Config -- [appid]");
  58. }
  59. if (empty($options['appsecret'])) {
  60. throw new InvalidArgumentException("Missing Config -- [appsecret]");
  61. }
  62. if (empty($options['token'])) {
  63. throw new InvalidArgumentException("Missing Config -- [token]");
  64. }
  65. //参数初始化
  66. $this->config = new DataArray($options);
  67. $this->input = new DataArray($_REQUEST);
  68. $this->appid = $this->config->get('appid');
  69. //推送消息处理
  70. if ($_SERVER['REQUEST_METHOD'] == "POST") {
  71. $this->postxml = file_get_contents("php://input");
  72. $this->encryptType = $this->input->get('encrypt_type');
  73. if ($this->isEncrypt()) {
  74. if (empty($options['encodingaeskey'])) {
  75. throw new InvalidArgumentException("Missing Config -- [encodingaeskey]");
  76. }
  77. if (!class_exists('Prpcrypt', false)) {
  78. require __DIR__.'/Prpcrypt.php';
  79. }
  80. $prpcrypt = new \Prpcrypt($this->config->get('encodingaeskey'));
  81. $result = Tools::xml2arr($this->postxml);
  82. $array = $prpcrypt->decrypt($result['Encrypt']);
  83. if (intval($array[0]) > 0) {
  84. throw new InvalidResponseException($array[1], $array[0]);
  85. }
  86. list($this->postxml, $this->appid) = [$array[1], $array[2]];
  87. }
  88. $this->receive = new DataArray(Tools::xml2arr($this->postxml));
  89. } elseif ($_SERVER['REQUEST_METHOD'] == "GET" && $this->checkSignature()) {
  90. @ob_clean();
  91. exit($this->input->get('echostr'));
  92. } else {
  93. throw new InvalidResponseException('Invalid interface request.', '0');
  94. }
  95. }
  96. /**
  97. * 消息是否需要加密
  98. * @return boolean
  99. */
  100. public function isEncrypt()
  101. {
  102. return $this->encryptType === 'aes';
  103. }
  104. /**
  105. * 回复消息
  106. * @param array $data 消息内容
  107. * @param boolean $return 是否返回XML内容
  108. * @param boolean $isEncrypt 是否加密内容
  109. * @return string
  110. * @throws \WeChat\Exceptions\InvalidDecryptException
  111. */
  112. public function reply(array $data = [], $return = false, $isEncrypt = false)
  113. {
  114. $xml = Tools::arr2xml(empty($data) ? $this->message : $data);
  115. if ($this->isEncrypt() || $isEncrypt) {
  116. if (!class_exists('Prpcrypt', false)) {
  117. require __DIR__.'/Prpcrypt.php';
  118. }
  119. $prpcrypt = new \Prpcrypt($this->config->get('encodingaeskey'));
  120. //如果是第三方平台,加密得使用 component_appid
  121. $component_appid = $this->config->get('component_appid');
  122. $appid = empty($component_appid) ? $this->appid : $component_appid;
  123. $array = $prpcrypt->encrypt($xml, $appid);
  124. if ($array[0] > 0) throw new InvalidDecryptException('Encrypt Error.', '0');
  125. list($timestamp, $encrypt) = [time(), $array[1]];
  126. $nonce = rand(77, 999) * rand(605, 888) * rand(11, 99);
  127. $tmpArr = [$this->config->get('token'), $timestamp, $nonce, $encrypt];
  128. sort($tmpArr, SORT_STRING);
  129. $signature = sha1(implode($tmpArr));
  130. $format = "<xml><Encrypt><![CDATA[%s]]></Encrypt><MsgSignature><![CDATA[%s]]></MsgSignature><TimeStamp>%s</TimeStamp><Nonce><![CDATA[%s]]></Nonce></xml>";
  131. $xml = sprintf($format, $encrypt, $signature, $timestamp, $nonce);
  132. }
  133. if ($return) return $xml;
  134. @ob_clean();
  135. echo $xml;
  136. }
  137. /**
  138. * 验证来自微信服务器
  139. * @param string $str
  140. * @return bool
  141. */
  142. private function checkSignature($str = '')
  143. {
  144. $nonce = $this->input->get('nonce');
  145. $timestamp = $this->input->get('timestamp');
  146. $msg_signature = $this->input->get('msg_signature');
  147. $signature = empty($msg_signature) ? $this->input->get('signature') : $msg_signature;
  148. $tmpArr = [$this->config->get('token'), $timestamp, $nonce, $str];
  149. sort($tmpArr, SORT_STRING);
  150. return sha1(implode($tmpArr)) === $signature;
  151. }
  152. /**
  153. * 获取公众号推送对象
  154. * @param null|string $field 指定获取字段
  155. * @return array
  156. */
  157. public function getReceive($field = null)
  158. {
  159. return $this->receive->get($field);
  160. }
  161. /**
  162. * 获取当前微信OPENID
  163. * @return string
  164. */
  165. public function getOpenid()
  166. {
  167. return $this->receive->get('FromUserName');
  168. }
  169. /**
  170. * 获取当前推送消息类型
  171. * @return string
  172. */
  173. public function getMsgType()
  174. {
  175. return $this->receive->get('MsgType');
  176. }
  177. /**
  178. * 获取当前推送消息ID
  179. * @return string
  180. */
  181. public function getMsgId()
  182. {
  183. return $this->receive->get('MsgId');
  184. }
  185. /**
  186. * 获取当前推送时间
  187. * @return integer
  188. */
  189. public function getMsgTime()
  190. {
  191. return $this->receive->get('CreateTime');
  192. }
  193. /**
  194. * 获取当前推送公众号
  195. * @return string
  196. */
  197. public function getToOpenid()
  198. {
  199. return $this->receive->get('ToUserName');
  200. }
  201. }
  202. ?>