General.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. <?php
  2. namespace App\libs\wechat\auth\Gateways;
  3. use Illuminate\Support\Facades\Redis;
  4. use App\libs\helpers\Curl;
  5. use Illuminate\Support\Facades\DB;
  6. class General extends Base
  7. {
  8. private $appId;
  9. private $secret;
  10. public function __construct()
  11. {
  12. parent::__construct();
  13. $this->appId = $this->config['appid'];
  14. $this->secret = $this->config['secret'];
  15. }
  16. /**
  17. * @link 第一步 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
  18. * @desc 获取授权地址
  19. * @param string $scope 授权类型snsapi_base|snsapi_userinfo
  20. * @param string|null $redirect 授权回跳地址
  21. * @return string
  22. */
  23. public function authUrl(string $scope = 'snsapi_base', string $redirect = null): string
  24. {
  25. $redirect = $redirect ? $redirect : "https://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
  26. $query = http_build_query([
  27. 'appid' => $this->appId,
  28. 'redirect_uri' => $redirect,
  29. 'response_type' => 'code',
  30. 'scope' => $scope
  31. ]);
  32. return "https://open.weixin.qq.com/connect/oauth2/authorize?{$query}#wechat_redirect";
  33. }
  34. /**
  35. * @link 第二步 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
  36. * @desc 使用授权code换取openid
  37. * @param string $code
  38. * @return array
  39. */
  40. public function code2Openid(string $code): array
  41. {
  42. $url = 'https://api.weixin.qq.com/sns/oauth2/access_token';
  43. $res = Curl::requestCurl($url, 'GET', [], http_build_query([
  44. 'appid' => $this->appId,
  45. 'secret' => $this->secret,
  46. 'code' => $code,
  47. 'grant_type' => 'authorization_code'
  48. ]));
  49. return $res ? json_decode($res, true) : [];
  50. }
  51. /**
  52. * @link 第四步 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
  53. * @desc 获取用户信息
  54. * @param string $accessToken
  55. * @param string $openid
  56. * @return bool|mixed|string
  57. */
  58. public function userSimpleInfo(string $accessToken, string $openid): array
  59. {
  60. $url = 'https://api.weixin.qq.com/sns/userinfo';
  61. $res = Curl::requestCurl($url, 'GET', [], http_build_query([
  62. 'access_token' => $accessToken,
  63. 'openid' => $openid,
  64. 'lang' => 'zh_CN',
  65. ]));
  66. return $res ? json_decode($res, true) : [];
  67. }
  68. /**
  69. * @link https://developers.weixin.qq.com/doc/offiaccount/User_Management/Get_users_basic_information_UnionID.html#UinonId
  70. * @desc 获取用户详细信息
  71. * @param string $openid
  72. * @return array
  73. */
  74. public function userDetailInfo(string $openid): array
  75. {
  76. $url = 'https://api.weixin.qq.com/cgi-bin/user/info';
  77. $res = Curl::requestCurl($url, 'GET', [], http_build_query([
  78. 'access_token' => $this->accessToken(),
  79. 'openid' => $openid,
  80. 'lang' => 'zh_CN',
  81. ]));
  82. return $res ? json_decode($res, true) : [];
  83. }
  84. /**
  85. * @link 附录1-JS-SDK使用权限签名算法 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html
  86. * @desc 获取 JSAPI TICKET
  87. * @param int $repeatCnt
  88. * @return string
  89. */
  90. public function jsApiTicket(&$repeatCnt = 1): string
  91. {
  92. $drive = $this->config['token_drive'] ?? 'redis';
  93. $key = 'wechat_' . $this->appId . '_jsApiTicket';
  94. switch ($drive) {
  95. case 'redis':
  96. $redis = Redis::instance();
  97. $redis->select(15);
  98. $ticket = $redis->get($key);
  99. break;
  100. case 'db':
  101. default:
  102. $ticket = Db::table('configs')->where('c_key', $key)->where('c_expired > ' . time())->value('c_value');
  103. break;
  104. }
  105. if ($ticket) {
  106. return $ticket;
  107. }
  108. $url = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket';
  109. $res = Curl::requestCurl($url, 'GET', [], http_build_query([
  110. 'access_token' => $this->accessToken(),
  111. 'type' => 'jsapi'
  112. ]));
  113. $data = json_decode($res, true);
  114. if (isset($data['ticket'])) {
  115. $ticket = $data['ticket'];
  116. switch ($drive) {
  117. case 'redis':
  118. $redis->setex($key, 7000, $ticket);
  119. break;
  120. case 'db':
  121. default:
  122. Db::table('configs')
  123. ->where('c_key', $key)
  124. ->update([
  125. 'c_value' => $ticket,
  126. 'c_expired' => time() + 7000,
  127. 'c_updated' => date('Y-m-d H:i:s')
  128. ]);
  129. break;
  130. }
  131. return $ticket;
  132. } else {
  133. if ($repeatCnt <= 3) {
  134. $repeatCnt++;
  135. return $this->jsApiTicket($repeatCnt);
  136. } else {
  137. return $data['errcode'];
  138. }
  139. }
  140. }
  141. /**
  142. * @link https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
  143. * @desc 获取全局唯一接口调用凭据
  144. * @param int $repeatCnt
  145. * @return string
  146. */
  147. public function accessToken(&$repeatCnt = 1)
  148. {
  149. $drive = $this->config['token_drive'] ?? 'redis';
  150. $key = 'wechat_' . $this->appId . '_accessToken';
  151. switch ($drive) {
  152. case 'redis':
  153. $redis = Redis::instance();
  154. $redis->select(15);
  155. $token = $redis->get($key);
  156. break;
  157. case 'db':
  158. default:
  159. $token = Db::table('configs')->where('c_key',$key)->where('c_expired',' > ',time())->value('c_value');
  160. break;
  161. }
  162. if ($token) {
  163. return $token;
  164. }
  165. $url = 'https://api.weixin.qq.com/cgi-bin/token';
  166. $res = Curl::requestCurl($url, 'GET', [], http_build_query([
  167. 'grant_type' => 'client_credential',
  168. 'appid' => $this->appId,
  169. 'secret' => $this->secret
  170. ]));
  171. $data = json_decode($res, true);
  172. if (isset($data['access_token'])) {
  173. $token = $data['access_token'];
  174. switch ($drive) {
  175. case 'redis':
  176. $redis->setex($key, 7000, $token);
  177. break;
  178. case 'db':
  179. default:
  180. Db::table('configs')
  181. ->where('c_key', $key)
  182. ->update([
  183. 'c_value' => $token,
  184. 'c_expired' => time() + 7000,
  185. 'c_updated' => date('Y-m-d H:i:s')
  186. ]);
  187. break;
  188. }
  189. return $token;
  190. } else {
  191. if ($repeatCnt <= 3) {
  192. $repeatCnt++;
  193. return $this->accessToken($repeatCnt);
  194. } else {
  195. return $data['errcode'];
  196. }
  197. }
  198. }
  199. }