WechatService.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. <?php
  2. /**
  3. * @package merchant
  4. *
  5. * @author xaboy
  6. * @day 2020-04-20
  7. *
  8. *
  9. */
  10. namespace crmeb\services;
  11. use app\common\repositories\system\config\ConfigValueRepository;
  12. use app\common\repositories\wechat\WechatQrcodeRepository;
  13. use app\common\repositories\wechat\WechatReplyRepository;
  14. use app\common\repositories\wechat\WechatUserRepository;
  15. use crmeb\exceptions\WechatException;
  16. use EasyWeChat\Core\Exceptions\FaultException;
  17. use EasyWeChat\Core\Exceptions\InvalidArgumentException;
  18. use EasyWeChat\Core\Exceptions\RuntimeException;
  19. use EasyWeChat\Foundation\Application;
  20. use EasyWeChat\Message\Article;
  21. use EasyWeChat\Message\Image;
  22. use EasyWeChat\Message\Material;
  23. use EasyWeChat\Message\News;
  24. use EasyWeChat\Message\Text;
  25. use EasyWeChat\Message\Video;
  26. use EasyWeChat\Message\Voice;
  27. use EasyWeChat\Payment\Order;
  28. use EasyWeChat\Server\BadRequestException;
  29. use EasyWeChat\Support\Collection;
  30. use Exception;
  31. use Symfony\Component\HttpFoundation\Request;
  32. use think\facade\Log;
  33. use think\facade\Route;
  34. use think\Response;
  35. /**
  36. * Class WechatService
  37. * @package crmeb\services
  38. * @author xaboy
  39. * @day 2020-04-20
  40. */
  41. class WechatService
  42. {
  43. /**
  44. * @var Application
  45. */
  46. protected $application;
  47. /**
  48. * WechatService constructor.
  49. * @param $config
  50. */
  51. public function __construct(array $config)
  52. {
  53. $this->application = new Application($config);
  54. }
  55. /**
  56. * @return array
  57. * @author xaboy
  58. * @day 2020-04-24
  59. */
  60. public static function getConfig($wxConfig)
  61. {
  62. /** @var ConfigValueRepository $make */
  63. $make = app()->make(ConfigValueRepository::class);
  64. $wechat = $make->more([
  65. 'wechat_appid', 'wechat_appsecret', 'wechat_token', 'wechat_encodingaeskey', 'wechat_encode', 'pay_wx'
  66. ], 0);
  67. if (!empty($wxConfig['appid'])){
  68. $wechat['wechat_appid'] = $wxConfig['appid'];
  69. $wechat['wechat_appsecret'] = $wxConfig['appsecret'];
  70. }else{
  71. if ($wechat['pay_wx'] == 3) {
  72. $wechat['wechat_appid'] = "wx6ac5b448b75117d9";
  73. $wechat['wechat_appsecret'] = "fd05887f949267a75d809fd981cd64db";
  74. }
  75. }
  76. // dump($wechat);
  77. $config = [
  78. 'app_id' => trim($wechat['wechat_appid']),
  79. 'secret' => trim($wechat['wechat_appsecret']),
  80. 'token' => trim($wechat['wechat_token']),
  81. 'guzzle' => [
  82. 'timeout' => 10.0, // 超时时间(秒)
  83. 'verify' => false
  84. ],
  85. 'debug' => false,
  86. ];
  87. if ($wechat['wechat_encode'] > 0 && $wechat['wechat_encodingaeskey'])
  88. $config['aes_key'] = trim($wechat['wechat_encodingaeskey']);
  89. $payment = $make->more(['site_url', 'pay_weixin_mchid', 'pay_weixin_client_cert', 'pay_weixin_client_key', 'pay_weixin_key', 'pay_weixin_open'], 0);
  90. if (isset($payment['pay_weixin_open']) && $payment['pay_weixin_open'] == 1) {
  91. $config['payment'] = [
  92. 'merchant_id' => trim($payment['pay_weixin_mchid']),
  93. 'key' => trim($payment['pay_weixin_key']),
  94. 'cert_path' => realpath('./public' . $payment['pay_weixin_client_cert']),
  95. 'key_path' => realpath('./public' . $payment['pay_weixin_client_key']),
  96. 'notify_url' => $payment['site_url'] . Route::buildUrl('wechatNotify')->build()
  97. ];
  98. }
  99. return $config;
  100. }
  101. /**
  102. * @return self
  103. * @author xaboy
  104. * @day 2020-04-24
  105. */
  106. public static function create($wxConfig=[])
  107. {
  108. return new self(self::getConfig($wxConfig));
  109. }
  110. /**
  111. * @return Application
  112. * @author xaboy
  113. * @day 2020-04-20
  114. */
  115. public function getApplication()
  116. {
  117. return $this->application;
  118. }
  119. /**
  120. * @param \think\Request $request
  121. * @return Response
  122. * @throws BadRequestException
  123. * @throws InvalidArgumentException
  124. * @author xaboy
  125. * @day 2020-04-26
  126. */
  127. public function serve(\think\Request $request)
  128. {
  129. $this->serverRequest($request);
  130. $this->wechatEventHook();
  131. return response($this->application->server->serve()->getContent());
  132. }
  133. protected function serverRequest(\think\Request $request)
  134. {
  135. $this->application->server->setRequest(new Request($request->get(), $request->post(), [], [], [], $request->server(), $request->getContent()));
  136. }
  137. /**
  138. * @throws InvalidArgumentException
  139. * @author xaboy
  140. * @day 2020-04-20
  141. */
  142. public function wechatEventHook()
  143. {
  144. $this->application->server->setMessageHandler(function ($message) {
  145. $openId = $message->FromUserName;
  146. $userInfo = $this->getUserInfo($openId);
  147. /** @var WechatUserRepository $wechatUserRepository */
  148. $wechatUserRepository = app()->make(WechatUserRepository::class);
  149. $users = $wechatUserRepository->syncUser($openId, $userInfo, true);
  150. $response = null;
  151. /** @var WechatReplyRepository $make * */
  152. $make = app()->make(WechatReplyRepository::class);
  153. event('WechatMessage', $message);
  154. switch ($message->MsgType) {
  155. case 'event':
  156. switch (strtolower($message->Event)) {
  157. case 'subscribe':
  158. $response = $make->reply('subscribe');
  159. if (isset($message->EventKey) && $message->EventKey) {
  160. /** @var WechatQrcodeRepository $qr */
  161. $qr = app()->make(WechatQrcodeRepository::class);
  162. if ($qrInfo = $qr->ticketByQrcode($message->Ticket)) {
  163. $qrInfo->incTicket();
  164. if (strtolower($qrInfo['third_type']) == 'spread' && $users) {
  165. $spreadUid = $qrInfo['third_id'];
  166. if ($users[1]['uid'] == $spreadUid)
  167. return '自己不能推荐自己';
  168. else if ($users[1]['spread_uid'])
  169. return '已有推荐人!';
  170. try {
  171. $users[1]->setSpread($spreadUid);
  172. } catch (Exception $e) {
  173. return '绑定推荐人失败';
  174. }
  175. }
  176. }
  177. }
  178. event('WechatEventSubscribe', $message);
  179. break;
  180. case 'unsubscribe':
  181. $wechatUserRepository->unsubscribe($openId);
  182. event('WechatEventUnsubscribe', $message);
  183. break;
  184. case 'scan':
  185. $response = $make->reply('subscribe');
  186. /** @var WechatQrcodeRepository $qr */
  187. $qr = app()->make(WechatQrcodeRepository::class);
  188. if ($message->EventKey && ($qrInfo = $qr->ticketByQrcode($message->Ticket))) {
  189. $qrInfo->incTicket();
  190. if (strtolower($qrInfo['third_type']) == 'spread' && $users) {
  191. $spreadUid = $qrInfo['third_id'];
  192. if ($users[1]['uid'] == $spreadUid)
  193. return '自己不能推荐自己';
  194. else if ($users[1]['spread_uid'])
  195. return '已有推荐人!';
  196. try {
  197. $users[1]->setSpread($spreadUid);
  198. } catch (Exception $e) {
  199. return '绑定推荐人失败';
  200. }
  201. }
  202. }
  203. event('WechatEventScan', $message);
  204. break;
  205. case 'location':
  206. event('wechatEventLocation', $message);
  207. break;
  208. case 'click':
  209. $response = $make->reply($message->EventKey);
  210. break;
  211. case 'view':
  212. event('wechatEventView', $message);
  213. break;
  214. }
  215. break;
  216. case 'text':
  217. $response = $make->reply($message->Content);
  218. event('wechatMessageText', $message);
  219. break;
  220. case 'image':
  221. event('wechatMessageImage', $message);
  222. break;
  223. case 'voice':
  224. event('wechatMessageVoice', $message);
  225. break;
  226. case 'video':
  227. event('wechatMessageVideo', $message);
  228. break;
  229. case 'location':
  230. event('wechatMessageLocation', $message);
  231. break;
  232. case 'link':
  233. event('wechatMessageLink', $message);
  234. break;
  235. // ... 其它消息
  236. default:
  237. event('WechatMessageOther', $message);
  238. break;
  239. }
  240. return $response ?? false;
  241. });
  242. }
  243. /**
  244. * @param $value
  245. * @return Collection
  246. * @author xaboy
  247. * @day 2020-04-20
  248. */
  249. public function qrcodeForever($value)
  250. {
  251. return $this->application->qrcode->forever($value);
  252. }
  253. /**
  254. * @param $value
  255. * @param int $expireSeconds
  256. * @return Collection
  257. * @author xaboy
  258. * @day 2020-04-20
  259. */
  260. public function qrcodeTemp($value, $expireSeconds = 2592000)
  261. {
  262. return $this->application->qrcode->temporary($value, $expireSeconds);
  263. }
  264. /**
  265. * @param string $openid
  266. * @param string $templateId
  267. * @param array $data
  268. * @param null $url
  269. * @param null $defaultColor
  270. * @return mixed
  271. * @author xaboy
  272. * @day 2020-04-20
  273. */
  274. public function sendTemplate(string $openid, string $templateId, array $data, $url = null, $defaultColor = null)
  275. {
  276. $notice = $this->application->notice->to($openid)->template($templateId)->andData($data);
  277. if ($url !== null) $notice->url($url);
  278. if ($defaultColor !== null) $notice->defaultColor($defaultColor);
  279. return $notice->send();
  280. }
  281. /**
  282. * @param $openid
  283. * @param $out_trade_no
  284. * @param $total_fee
  285. * @param $attach
  286. * @param $body
  287. * @param string $detail
  288. * @param string $trade_type
  289. * @param array $options
  290. * @return Order
  291. * @author xaboy
  292. * @day 2020-04-20
  293. */
  294. protected function paymentOrder($openid, $out_trade_no, $total_fee, $attach, $body, $detail = '', $trade_type = 'JSAPI', $options = [])
  295. {
  296. $total_fee = bcmul($total_fee, 100, 0);
  297. $order = array_merge(compact('out_trade_no', 'total_fee', 'attach', 'body', 'detail', 'trade_type'), $options);
  298. if (!is_null($openid)) $order['openid'] = $openid;
  299. if ($order['detail'] == '') unset($order['detail']);
  300. return new Order($order);
  301. }
  302. /**
  303. * @param $openid
  304. * @param $out_trade_no
  305. * @param $total_fee
  306. * @param $attach
  307. * @param $body
  308. * @param string $detail
  309. * @param string $trade_type
  310. * @param array $options
  311. * @return Collection
  312. * @author xaboy
  313. * @day 2020-04-20
  314. */
  315. public function paymentPrepare($openid, $out_trade_no, $total_fee, $attach, $body, $detail = '', $trade_type = 'JSAPI', $options = [])
  316. {
  317. $order = $this->paymentOrder($openid, $out_trade_no, $total_fee, $attach, $body, $detail, $trade_type, $options);
  318. $result = $this->application->payment->prepare($order);
  319. if ($result->return_code == 'SUCCESS' && $result->result_code == 'SUCCESS') {
  320. return $result;
  321. } else {
  322. if ($result->return_code == 'FAIL') {
  323. throw new WechatException('微信支付错误返回:' . $result->return_msg);
  324. } else if (isset($result->err_code)) {
  325. throw new WechatException('微信支付错误返回:' . $result->err_code_des);
  326. } else {
  327. throw new WechatException('没有获取微信支付的预支付ID,请重新发起支付!');
  328. }
  329. }
  330. }
  331. /**
  332. * @param $openid
  333. * @param $out_trade_no
  334. * @param $total_fee
  335. * @param $attach
  336. * @param $body
  337. * @param string $detail
  338. * @param string $trade_type
  339. * @param array $options
  340. * @return array|string
  341. * @author xaboy
  342. * @day 2020-04-20
  343. */
  344. public function jsPay($openid, $out_trade_no, $total_fee, $attach, $body, $detail = '', $trade_type = 'JSAPI', $options = [])
  345. {
  346. $paymentPrepare = $this->paymentPrepare($openid, $out_trade_no, $total_fee, $attach, $body, $detail, $trade_type, $options);
  347. return $this->application->payment->configForJSSDKPayment($paymentPrepare->prepay_id);
  348. }
  349. /**
  350. * @param $orderNo
  351. * @param $refundNo
  352. * @param $totalFee
  353. * @param null $refundFee
  354. * @param null $opUserId
  355. * @param string $refundReason
  356. * @param string $type
  357. * @param string $refundAccount
  358. * @return Collection
  359. * @author xaboy
  360. * @day 2020-04-20
  361. */
  362. public function refund($orderNo, $refundNo, $totalFee, $refundFee = null, $opUserId = null, $refundReason = '', $type = 'out_trade_no', $refundAccount = 'REFUND_SOURCE_UNSETTLED_FUNDS')
  363. {
  364. $totalFee = floatval($totalFee);
  365. $refundFee = floatval($refundFee);
  366. return $this->application->payment->refund($orderNo, $refundNo, $totalFee, $refundFee, $opUserId, $type, $refundAccount, $refundReason);
  367. }
  368. /**
  369. * @param $orderNo
  370. * @param array $opt
  371. * @author xaboy
  372. * @day 2020-04-20
  373. */
  374. public function payOrderRefund($orderNo, array $opt)
  375. {
  376. if (!isset($opt['pay_price'])) throw new WechatException('缺少pay_price');
  377. $totalFee = floatval(bcmul($opt['pay_price'], 100, 0));
  378. $refundFee = isset($opt['refund_price']) ? floatval(bcmul($opt['refund_price'], 100, 0)) : null;
  379. $refundReason = isset($opt['desc']) ? $opt['desc'] : '';
  380. $refundNo = isset($opt['refund_id']) ? $opt['refund_id'] : $orderNo;
  381. $opUserId = isset($opt['op_user_id']) ? $opt['op_user_id'] : null;
  382. $type = isset($opt['type']) ? $opt['type'] : 'out_trade_no';
  383. /*仅针对老资金流商户使用
  384. REFUND_SOURCE_UNSETTLED_FUNDS---未结算资金退款(默认使用未结算资金退款)
  385. REFUND_SOURCE_RECHARGE_FUNDS---可用余额退款*/
  386. $refundAccount = isset($opt['refund_account']) ? $opt['refund_account'] : 'REFUND_SOURCE_UNSETTLED_FUNDS';
  387. try {
  388. $res = ($this->refund($orderNo, $refundNo, $totalFee, $refundFee, $opUserId, $refundReason, $type, $refundAccount));
  389. if ($res->return_code == 'FAIL') throw new WechatException('退款失败:' . $res->return_msg);
  390. if (isset($res->err_code)) throw new WechatException('退款失败:' . $res->err_code_des);
  391. } catch (Exception $e) {
  392. throw new WechatException($e->getMessage());
  393. }
  394. }
  395. /**
  396. * array (
  397. * 'appid' => '****',
  398. * 'attach' => 'user_recharge',
  399. * 'bank_type' => 'COMM_CREDIT',
  400. * 'cash_fee' => '1',
  401. * 'fee_type' => 'CNY',
  402. * 'is_subscribe' => 'Y',
  403. * 'mch_id' => ''*****'',
  404. * 'nonce_str' => '5ee9dac1bc302',
  405. * 'openid' => '*****',
  406. * 'out_trade_no' => ''*****'',
  407. * 'result_code' => 'SUCCESS',
  408. * 'return_code' => 'SUCCESS',
  409. * 'sign' => '51'*****'',
  410. * 'time_end' => '20200617165651',
  411. * 'total_fee' => '1',
  412. * 'trade_type' => 'JSAPI',
  413. * 'transaction_id' => ''*****'',
  414. * )
  415. *
  416. * @throws FaultException
  417. * @author xaboy
  418. * @day 2020-04-20
  419. */
  420. public function handleNotify()
  421. {
  422. $this->application->payment = new PaymentService($this->application->merchant);
  423. //TODO 微信支付
  424. return $this->application->payment->handleNotify(function ($notify, $successful) {
  425. Log::info('微信支付成功回调' . var_export($notify, 1));
  426. if (!$successful) return false;
  427. try {
  428. event('pay_success_' . $notify['attach'], ['order_sn' => $notify['out_trade_no'], 'data' => $notify]);
  429. } catch (\Exception $e) {
  430. Log::info('微信支付回调失败:' . $e->getMessage());
  431. return false;
  432. }
  433. return true;
  434. });
  435. }
  436. /**
  437. * @param string $url
  438. * @return array|string
  439. * @author xaboy
  440. * @day 2020-04-20
  441. */
  442. public function jsSdk($url)
  443. {
  444. $apiList = ['editAddress', 'openAddress', 'updateTimelineShareData', 'updateAppMessageShareData', 'onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareWeibo', 'onMenuShareQZone', 'startRecord', 'stopRecord', 'onVoiceRecordEnd', 'playVoice', 'pauseVoice', 'stopVoice', 'onVoicePlayEnd', 'uploadVoice', 'downloadVoice', 'chooseImage', 'previewImage', 'uploadImage', 'downloadImage', 'translateVoice', 'getNetworkType', 'openLocation', 'getLocation', 'hideOptionMenu', 'showOptionMenu', 'hideMenuItems', 'showMenuItems', 'hideAllNonBaseMenuItem', 'showAllNonBaseMenuItem', 'closeWindow', 'scanQRCode', 'chooseWXPay', 'openProductSpecificView', 'addCard', 'chooseCard', 'openCard'];
  445. $jsService = $this->application->js;
  446. $jsService->setUrl($url);
  447. try {
  448. return $jsService->config($apiList, false, false, false);
  449. } catch (Exception $e) {
  450. Log::info('微信参数获取失败' . $e->getMessage());
  451. return [];
  452. }
  453. }
  454. /**
  455. * 回复文本消息
  456. * @param string $content 文本内容
  457. * @return Text
  458. * @author xaboy
  459. * @day 2020-04-20
  460. */
  461. public static function textMessage($content)
  462. {
  463. return new Text(compact('content'));
  464. }
  465. /**
  466. * 回复图片消息
  467. * @param string $media_id 媒体资源 ID
  468. * @return Image
  469. * @author xaboy
  470. * @day 2020-04-20
  471. */
  472. public static function imageMessage($media_id)
  473. {
  474. return new Image(compact('media_id'));
  475. }
  476. /**
  477. * 回复视频消息
  478. * @param string $media_id 媒体资源 ID
  479. * @param string $title 标题
  480. * @param string $description 描述
  481. * @param null $thumb_media_id 封面资源 ID
  482. * @return Video
  483. * @author xaboy
  484. * @day 2020-04-20
  485. */
  486. public static function videoMessage($media_id, $title = '', $description = '...', $thumb_media_id = null)
  487. {
  488. return new Video(compact('media_id', 'title', 'description', 'thumb_media_id'));
  489. }
  490. /**
  491. * 回复声音消息
  492. * @param string $media_id 媒体资源 ID
  493. * @return Voice
  494. * @author xaboy
  495. * @day 2020-04-20
  496. */
  497. public static function voiceMessage($media_id)
  498. {
  499. return new Voice(compact('media_id'));
  500. }
  501. /**
  502. * 回复图文消息
  503. * @param string|array $title 标题
  504. * @param string $description 描述
  505. * @param string $url URL
  506. * @param string $image 图片链接
  507. * @return News|array<News>
  508. * @author xaboy
  509. * @day 2020-04-20
  510. */
  511. public static function newsMessage($title, $description = '...', $url = '', $image = '')
  512. {
  513. if (is_array($title)) {
  514. if (isset($title[0]) && is_array($title[0])) {
  515. $newsList = [];
  516. foreach ($title as $news) {
  517. $newsList[] = self::newsMessage($news);
  518. }
  519. return $newsList;
  520. } else {
  521. $data = $title;
  522. }
  523. } else {
  524. $data = compact('title', 'description', 'url', 'image');
  525. }
  526. return new News($data);
  527. }
  528. /**
  529. * 回复文章消息
  530. * @param string|array $title 标题
  531. * @param string $thumb_media_id 图文消息的封面图片素材id(必须是永久 media_ID)
  532. * @param string $source_url 图文消息的原文地址,即点击“阅读原文”后的URL
  533. * @param string $content 图文消息的具体内容,支持HTML标签,必须少于2万字符,小于1M,且此处会去除JS
  534. * @param string $author 作者
  535. * @param string $digest 图文消息的摘要,仅有单图文消息才有摘要,多图文此处为空
  536. * @param int $show_cover_pic 是否显示封面,0为false,即不显示,1为true,即显示
  537. * @param int $need_open_comment 是否打开评论,0不打开,1打开
  538. * @param int $only_fans_can_comment 是否粉丝才可评论,0所有人可评论,1粉丝才可评论
  539. * @return Article
  540. * @author xaboy
  541. * @day 2020-04-20
  542. */
  543. public static function articleMessage($title, $thumb_media_id, $source_url, $content = '', $author = '', $digest = '', $show_cover_pic = 0, $need_open_comment = 0, $only_fans_can_comment = 1)
  544. {
  545. $data = is_array($title) ? $title : compact('title', 'thumb_media_id', 'source_url', 'content', 'author', 'digest', 'show_cover_pic', 'need_open_comment', 'only_fans_can_comment');
  546. return new Article($data);
  547. }
  548. /**
  549. * 回复素材消息
  550. * @param string $type [mpnews、 mpvideo、voice、image]
  551. * @param string $media_id 素材 ID
  552. * @return Material
  553. * @author xaboy
  554. * @day 2020-04-20
  555. */
  556. public static function materialMessage($type, $media_id)
  557. {
  558. return new Material($type, $media_id);
  559. }
  560. /**
  561. * @param $to
  562. * @param $message
  563. * @return bool
  564. * @throws InvalidArgumentException
  565. * @throws RuntimeException
  566. * @author xaboy
  567. * @day 2020-04-20
  568. */
  569. public function staffTo($to, $message)
  570. {
  571. $staff = $this->application->staff;
  572. $staff = is_callable($message) ? $staff->message($message()) : $staff->message($message);
  573. $res = $staff->to($to)->send();
  574. return $res;
  575. }
  576. /**
  577. * @param $openid
  578. * @return array
  579. * @author xaboy
  580. * @day 2020-04-20
  581. */
  582. public function getUserInfo($openid)
  583. {
  584. $userService = $this->application->user;
  585. $userInfo = is_array($openid) ? $userService->batchGet($openid) : $userService->get($openid);
  586. return $userInfo->toArray();
  587. }
  588. /**
  589. * 模板消息接口
  590. * @return \EasyWeChat\Notice\Notice
  591. */
  592. public function noticeService()
  593. {
  594. return $this->application->notice;
  595. }
  596. }