OfficialNotify.php 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. <?php
  2. namespace App\Http\Controllers\Api\Wechat;
  3. use App\Http\Controllers\Controller;
  4. use App\Models\Api\Personnel\EmployeeOpenid as EmployeeOpenidModel;
  5. use App\Facades\Servers\Logs\Log;
  6. use App\Servers\Wechat\Official;
  7. class OfficialNotify extends Controller
  8. {
  9. /**
  10. * 公众号关注回调 - 自动绑定用户公众号OpenID
  11. * @author 唐远望
  12. * @version 1.0
  13. * @date 2026-03-10
  14. */
  15. public function callback(EmployeeOpenidModel $EmployeeOpenidModel)
  16. {
  17. // 1. 处理微信服务器验证(GET请求)
  18. if ($_SERVER['REQUEST_METHOD'] === 'GET') {
  19. return $this->checkSignature();
  20. }
  21. // 2. 处理微信事件推送(POST请求)
  22. if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  23. return $this->handleEvent($EmployeeOpenidModel);
  24. }
  25. return 'success';
  26. }
  27. /**
  28. * 处理微信事件
  29. */
  30. private function handleEvent($EmployeeOpenidModel)
  31. {
  32. // 获取微信推送的原始数据
  33. $xmlData = file_get_contents('php://input');
  34. $xml = simplexml_load_string($xmlData, 'SimpleXMLElement', LIBXML_NOCDATA);
  35. if (!$xml) {
  36. return 'success';
  37. }
  38. // 提取关键信息
  39. $fromUsername = (string)$xml->FromUserName; // 用户的公众号OpenID
  40. $toUsername = (string)$xml->ToUserName; // 公众号原始ID
  41. $event = (string)$xml->Event; // 事件类型
  42. $eventKey = (string)$xml->EventKey; // 事件KEY值(扫码关注时会有)
  43. // 记录日志,方便调试
  44. Log::info('wechat_subscribe_info', '微信关注事件', [
  45. 'FromUserName' => $fromUsername,
  46. 'ToUserName' => $toUsername,
  47. 'event' => $event,
  48. 'eventKey' => $eventKey
  49. ]);
  50. // 处理关注事件
  51. if ($event == 'subscribe') {
  52. try {
  53. // 尝试获取用户UnionID
  54. $Official = new Official();
  55. $official_user_info = $Official->getApp()->user->get($fromUsername);
  56. $unionid = isset($official_user_info['unionid']) ? $official_user_info['unionid'] : '';
  57. if ($unionid) {
  58. // 1. 有UnionID,直接绑定公众号OpenID
  59. $user_open_data = $EmployeeOpenidModel->where(['unionid' => $unionid])->first();
  60. if ($user_open_data) {
  61. $user_open_data->official_openid = $fromUsername;
  62. $user_open_data->save();
  63. Log::info('wechat_subscribe_success', '绑定成功', ['unionid' => $unionid, 'openid' => $fromUsername]);
  64. }
  65. } else {
  66. Log::info('wechat_subscribe_error', '获取UnionID失败', [
  67. 'data' => $fromUsername,
  68. 'request_data' => $official_user_info
  69. ]);
  70. }
  71. } catch (\Exception $e) {
  72. Log::error('wechat_subscribe_exception', '处理异常', [
  73. 'error' => $e->getMessage()
  74. ]);
  75. }
  76. }
  77. // 重要:必须返回success
  78. return 'success';
  79. }
  80. /**
  81. * 验证服务器地址有效性
  82. */
  83. private function checkSignature()
  84. {
  85. $signature = $_GET["signature"] ?? '';
  86. $timestamp = $_GET["timestamp"] ?? '';
  87. $nonce = $_GET["nonce"] ?? '';
  88. $echostr = $_GET["echostr"] ?? '';
  89. if (!$signature || !$timestamp || !$nonce) {
  90. return 'Invalid request';
  91. }
  92. $token = config('wechat.openplat.token', 'your_token_here');
  93. $tmpArr = [$token, $timestamp, $nonce];
  94. sort($tmpArr, SORT_STRING);
  95. $tmpStr = sha1(implode($tmpArr));
  96. if ($tmpStr == $signature) {
  97. return $echostr; // 验证成功,返回echostr
  98. } else {
  99. return 'Invalid signature';
  100. }
  101. }
  102. }