WechatService.php 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | framework
  4. // +----------------------------------------------------------------------
  5. // | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
  6. // +----------------------------------------------------------------------
  7. // | 官方网站: http://framework.thinkadmin.top
  8. // +----------------------------------------------------------------------
  9. // | 开源协议 ( https://mit-license.org )
  10. // +----------------------------------------------------------------------
  11. // | github开源项目:https://github.com/zoujingli/framework
  12. // +----------------------------------------------------------------------
  13. namespace app\wechat\service;
  14. /**
  15. * 微信处理管理
  16. * Class WechatService
  17. * @package app\wechat\service
  18. *
  19. * ----- WeOpen for Open -----
  20. * @method \WeOpen\Login login() static 第三方微信登录
  21. * @method \WeOpen\Service service() static 第三方服务
  22. *
  23. * ----- WeMini for Open -----
  24. * @method \WeMini\Code WeMiniCode() static 小程序代码管理
  25. * @method \WeMini\User WeMiniUser() static 小程序帐号管理
  26. * @method \WeMini\Basic WeMiniBasic() static 小程序基础信息
  27. * @method \WeMini\Domain WeMiniDomain() static 小程序域名管理
  28. * @method \WeMini\Tester WeMiniTester() static 小程序成员管理
  29. * @method \WeMini\Account WeMiniAccount() static 小程序账号管理
  30. *
  31. * ----- ThinkService -----
  32. * @method mixed wechat() static 第三方微信工具
  33. */
  34. class WechatService extends \We
  35. {
  36. /**
  37. * 接口类型模式
  38. * @var string
  39. */
  40. private static $type = 'WeChat';
  41. /**
  42. * 获取微信支付配置
  43. * @param array|null $options
  44. * @return array
  45. * @throws \think\Exception
  46. * @throws \think\exception\PDOException
  47. */
  48. public static function config($options = null)
  49. {
  50. if (empty($options)) $options = [
  51. // 微信功能必需参数
  52. 'appid' => self::getAppid(),
  53. 'token' => sysconf('wechat_token'),
  54. 'appsecret' => sysconf('wechat_appsecret'),
  55. 'encodingaeskey' => sysconf('wechat_encodingaeskey'),
  56. // 微信支付必要参数
  57. 'mch_id' => sysconf('wechat_mch_id'),
  58. 'mch_key' => sysconf('wechat_mch_key'),
  59. 'cache_path' => env('runtime_path') . 'wechat' . DIRECTORY_SEPARATOR,
  60. ];
  61. if (sysconf('wechat_mch_ssl_type') === 'p12') {
  62. $options['ssl_p12'] = self::_parseCertPath(sysconf('wechat_mch_ssl_p12'));
  63. } else {
  64. $options['ssl_key'] = self::_parseCertPath(sysconf('wechat_mch_ssl_key'));
  65. $options['ssl_cer'] = self::_parseCertPath(sysconf('wechat_mch_ssl_cer'));
  66. }
  67. return parent::config($options);
  68. }
  69. /**
  70. * 解析证书路径
  71. * @param string $path
  72. * @return mixed
  73. * @throws \think\Exception
  74. */
  75. private static function _parseCertPath($path)
  76. {
  77. if (preg_match('|^[a-z0-9]{16,16}\/[a-z0-9]{16,16}\.|i', $path)) {
  78. return \library\File::instance('local')->path($path, true);
  79. }
  80. return $path;
  81. }
  82. /**
  83. * 静态魔术加载方法
  84. * @param string $name 静态类名
  85. * @param array $arguments 参数集合
  86. * @return mixed
  87. * @throws \think\Exception
  88. * @throws \think\exception\PDOException
  89. */
  90. public static function __callStatic($name, $arguments)
  91. {
  92. $config = [];
  93. if (is_array($arguments) && count($arguments) > 0) {
  94. $option = array_shift($arguments);
  95. $config = is_array($option) ? $option : self::config();
  96. }
  97. if (in_array($name, ['wechat'])) {
  98. return self::instance(trim($name, '_'), 'WeChat', $config);
  99. } elseif (substr($name, 0, 6) === 'WeChat') {
  100. return self::instance(substr($name, 6), 'WeChat', $config);
  101. } elseif (substr($name, 0, 6) === 'WeMini') {
  102. return self::instance(substr($name, 6), 'WeMini', $config);
  103. } elseif (substr($name, 0, 5) === 'WePay') {
  104. return self::instance(substr($name, 5), 'WePay', $config);
  105. } elseif (substr($name, 0, 6) === 'AliPay') {
  106. return self::instance(substr($name, 6), 'AliPay', $config);
  107. }
  108. throw new \think\Exception("class {$name} not found");
  109. }
  110. /**
  111. * 接口对象实例化
  112. * @param string $name 接口名称
  113. * @param string $type 接口类型
  114. * @param array $config 微信配置
  115. * @return mixed
  116. * @throws \SoapFault
  117. * @throws \think\Exception
  118. * @throws \think\exception\PDOException
  119. */
  120. public static function instance($name, $type = 'WeChat', $config = [])
  121. {
  122. if (!in_array($type, ['WeChat', 'WeMini', 'WePay', 'AliPay'])) $type = self::$type;
  123. if (self::getType() === 'api' || in_array($type, ['WePay', 'AliPay'])) {
  124. $class = "\\{$type}\\" . ucfirst(strtolower($name));
  125. if (class_exists($class)) return new $class(empty($config) ? self::config() : $config);
  126. throw new \think\Exception("Class '{$class}' not found");
  127. }
  128. set_time_limit(3600);
  129. list($appid, $appkey) = [sysconf('wechat_thr_appid'), sysconf('wechat_thr_appkey')];
  130. $token = strtolower("{$name}-{$appid}-{$appkey}-{$type}");
  131. if (class_exists('Yar_Client')) {
  132. return new \Yar_Client(config('wechat.service_url') . "/service/api.client/yar/{$token}");
  133. }
  134. if (class_exists('SoapClient')) {
  135. $location = config('wechat.service_url') . "/service/api.client/soap/{$token}";
  136. return new \SoapClient(null, ['uri' => strtolower($name), 'location' => $location]);
  137. }
  138. throw new \think\Exception("Yar or soap extensions are not installed.");
  139. }
  140. /**
  141. * 获取微信网页JSSDK
  142. * @param string $url JS签名地址
  143. * @return array
  144. * @throws \WeChat\Exceptions\InvalidResponseException
  145. * @throws \WeChat\Exceptions\LocalCacheException
  146. * @throws \think\Exception
  147. * @throws \think\exception\PDOException
  148. */
  149. public static function getWebJssdkSign($url = null)
  150. {
  151. $url = is_null($url) ? request()->url(true) : $url;
  152. if (self::getType() === 'api') {
  153. return self::WeChatScript()->getJsSign($url);
  154. } else {
  155. return self::wechat()->jsSign($url);
  156. }
  157. }
  158. /**
  159. * 初始化进入授权
  160. * @param string $url 授权页面URL地址
  161. * @param integer $isfull 授权公众号模式
  162. * @param boolean $isRedirect 是否进行跳转
  163. * @return array
  164. * @throws \WeChat\Exceptions\InvalidResponseException
  165. * @throws \WeChat\Exceptions\LocalCacheException
  166. * @throws \think\Exception
  167. * @throws \think\exception\PDOException
  168. */
  169. public static function getWebOauthInfo($url, $isfull = 0, $isRedirect = true)
  170. {
  171. $appid = self::getAppid();
  172. list($openid, $fansinfo) = [session("{$appid}_openid"), session("{$appid}_fansinfo")];
  173. if ((empty($isfull) && !empty($openid)) || (!empty($isfull) && !empty($openid) && !empty($fansinfo))) {
  174. empty($fansinfo) || FansService::set($fansinfo);
  175. return ['openid' => $openid, 'fansinfo' => $fansinfo];
  176. }
  177. if (self::getType() === 'api') {
  178. $wechat = self::WeChatOauth();
  179. if (request()->get('state') !== $appid) {
  180. $snsapi = empty($isfull) ? 'snsapi_base' : 'snsapi_userinfo';
  181. $param = (strpos($url, '?') !== false ? '&' : '?') . 'rcode=' . encode($url);
  182. $OauthUrl = $wechat->getOauthRedirect($url . $param, $appid, $snsapi);
  183. if ($isRedirect) redirect($OauthUrl, [], 301)->send();
  184. exit("window.location.href='{$OauthUrl}'");
  185. }
  186. if (($token = $wechat->getOauthAccessToken()) && isset($token['openid'])) {
  187. session("{$appid}_openid", $openid = $token['openid']);
  188. if (empty($isfull) && request()->get('rcode')) {
  189. redirect(decode(request()->get('rcode')), [], 301)->send();
  190. }
  191. session("{$appid}_fansinfo", $fansinfo = $wechat->getUserInfo($token['access_token'], $openid));
  192. empty($fansinfo) || FansService::set($fansinfo);
  193. }
  194. redirect(decode(request()->get('rcode')), [], 301)->send();
  195. } else {
  196. $result = self::wechat()->oauth(session_id(), $url, $isfull);
  197. session("{$appid}_openid", $openid = $result['openid']);
  198. session("{$appid}_fansinfo", $fansinfo = $result['fans']);
  199. if ((empty($isfull) && !empty($openid)) || (!empty($isfull) && !empty($openid) && !empty($fansinfo))) {
  200. empty($fansinfo) || FansService::set($fansinfo);
  201. return ['openid' => $openid, 'fansinfo' => $fansinfo];
  202. }
  203. if ($isRedirect && !empty($result['url'])) {
  204. redirect($result['url'], [], 301)->send();
  205. }
  206. exit("window.location.href='{$result['url']}'");
  207. }
  208. }
  209. /**
  210. * 获取当前公众号APPID
  211. * @return bool|string
  212. * @throws \think\Exception
  213. * @throws \think\exception\PDOException
  214. */
  215. public static function getAppid()
  216. {
  217. if (self::getType() === 'api') {
  218. return sysconf('wechat_appid');
  219. } else {
  220. return sysconf('wechat_thr_appid');
  221. }
  222. }
  223. /**
  224. * 获取接口授权模式
  225. * @return string
  226. * @throws \think\Exception
  227. * @throws \think\exception\PDOException
  228. */
  229. public static function getType()
  230. {
  231. $type = strtolower(sysconf('wechat_type'));
  232. if (in_array($type, ['api', 'thr'])) return $type;
  233. throw new \think\Exception('请在后台配置微信对接授权模式');
  234. }
  235. }