drag-create-api/app/common/arw/adjfut/src/WeChat/Gzh.php
2023-06-25 08:51:24 +08:00

349 lines
9.8 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace app\common\arw\adjfut\src\WeChat;
use app\common\arw\adjfut\src\Curl;
use app\common\arw\adjfut\src\WeChat\GzhCommon;
use think\facade\Config;
use think\facade\Request;
use think\helper\Arr;
use think\Response;
class Gzh extends GzhCommon
{
/**
* 当前实例
*
* @var $this
*/
private static $instance = null;
/**
* 推送缓存 防止重复推送同样的数据
*
* @var array
* @date 2021-10-11
* @example
* @author arw
* @since 1.0.0
*/
private static $TemplateMessageInterfaceCache = [];
/**
* 初始化
*
* @date 2022-05-18
* @example
* @author arw
* @since 1.0.0
*/
public function __construct()
{
parent::__construct();
}
/**
* 实例
*
* @return self
* @date 2022-05-18
* @example
* @author arw
* @since 1.0.0
*/
public static function instance(): self
{
if (!self::$instance) {
self::$instance = new self;
}
return self::$instance;
}
/**
* 获取AccessToken
*
* @param boolean $refresh 是否强制刷新
* @return void
* @date 2020-05-29
* @example
* @author arw
* @since 1.0.0
*/
public function GetAccessToken($refresh = false)
{
return $this->_AccessToken($refresh);
}
/**
* 获取JsApiTicket
*
* @param boolean $refresh 是否强制刷新
* @return void
* @date 2020-05-29
* @example
* @author arw
* @since 1.0.0
*/
public function GetJsApiTicket($refresh = false)
{
return $this->_JsApiTicket($refresh);
}
/**
* 网页授权/获取用户基本信息
* https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
* @param array $config 配置项 [
* "scope" => "snsapi_base", 应用授权作用域,
* snsapi_base 不弹出授权页面直接跳转只能获取用户openid
* snsapi_userinfo 弹出授权页面可通过openid拿到昵称、性别、所在地。并且 即使在未关注的情况下,只要用户授权,也能获取其信息
* "state" => "", 重定向后会带上state参数开发者可以填写a-zA-Z0-9的参数值最多128字节
* "lang" => "zh_CN", 当scope等于snsapi_userinfo才生效
* 返回国家地区语言版本zh_CN 简体zh_TW 繁体en 英语
* ]
* @param callable $success 回调用户授权成功调用函数 ($userinfo,$state)
* @param callable $error 执行错误回调 ($msg)
* @return null
*/
public function authorize(array $config, callable $success, callable $error)
{
try {
$config["scope"] = Arr::get($config, "scope", "snsapi_base");
$config["state"] = Arr::get($config, "state", "");
$config["lang"] = Arr::get($config, "lang", "zh_CN");
$config["redirect_uri"] = Arr::get($config, "redirect_uri", Request::url(true));
$code = Arr::get($_GET, "code");
if (!$code) {
$url = "https://open.weixin.qq.com/connect/oauth2/authorize";
$url .= "?" . http_build_query([
"appid" => $this->appid,
"redirect_uri" => $config["redirect_uri"],
"response_type" => "code",
"scope" => $config["scope"],
"state" => $config["state"],
]);
$url .= "#wechat_redirect";
return Response::create($url, 'redirect', 302);
}
$curl = Curl::instance();
$param = [];
$param["appid"] = $this->appid;
$param["secret"] = $this->secret;
$param["code"] = $code;
$param["grant_type"] = "authorization_code";
$url = "https://api.weixin.qq.com/sns/oauth2/access_token";
$access_token = $curl->setParams($param)->get($url);
$judge = (Arr::get($access_token, "access_token") && Arr::get($access_token, "openid"));
if (!$judge || !$access_token) {
$this->log([
'action' => __FUNCTION__,
'url' => $curl->getUrl(),
'params' => $curl->getParams(),
'content' => $curl->getContent(),
'msg' => '获取access_token失败',
], 'error');
return $error("获取access_token失败");
}
if ($config["scope"] === "snsapi_userinfo") {
$curl = Curl::instance();
$url = "https://api.weixin.qq.com/sns/userinfo";
$param = [];
$param["access_token"] = Arr::get($access_token, "access_token");
$param["openid"] = Arr::get($access_token, "openid");
$param["lang"] = $config["lang"];
$user_info = $curl->setParams($param)->get($url);
$errcode = Arr::get($user_info, "errcode", false);
if ($errcode !== false) {
$this->log([
'action' => __FUNCTION__,
'url' => $curl->getUrl(),
'params' => $curl->getParams(),
'result' => $curl->getContent(),
'msg' => '获取用户信息失败',
], 'error');
return $error("获取微信用户信息失败");
}
$access_token = array_merge($access_token, $user_info);
}
return $success($access_token, $_GET["state"]);
} catch (\Throwable $th) {
$this->log([
'action' => __FUNCTION__,
'msg' => '客户端错误:' . $th->getMessage(),
'line' => $th->getLine(),
'file' => $th->getFile(),
], 'error');
return $error($th->getMessage());
}
}
/**
* 微信模板消息推送
* 微信返回errcode等于40001会更新缓存AccessToken
* 并重新推送一次
* https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Interface.html#5
* @param string $touser 推送给谁
* @param string $template_id 模板名称
* @param array $data 推送数据
* [
* "touser" => "openid",
* "template_id" => "template_id",
* "url" => "url",
* "miniprogram" => [
* "appid" => "appid",
* "pagepath" => "pagepath",
* ],
* "data" => [
* "first" => [
* "value" => "value",
* "color" => "color",
* ],
* "keyword1" => [
* "value" => "value",
* "color" => "color",
* ],
* "keyword2" => [
* "value" => "value",
* "color" => "color",
* ],
* "keyword3" => [
* "value" => "value",
* "color" => "color",
* ],
* "remark" => [
* "value" => "value",
* "color" => "color",
* ],
* ],
* ]
* @param boolean $refresh 是否刷新【 access_token 】
* @param integer $limit 调用上限(允许自动重试次数)
* @return bool
* @date 2020-05-26
* @example
* @author arw
* @since 1.1.1
*/
public function TemplateMessageInterface($touser = null, $template_id = null, array $data, $refresh = false, $limit = 3)
{
$this->log([
'action' => __FUNCTION__,
'touser' => $touser,
'template_id' => $template_id,
'data' => $data,
], 'log');
try {
$template = $this->template;
if (!$touser) {
if (isset($data["touser"])) {
$touser = $data["touser"];
}
}
if (!$template_id) {
if (isset($data["template_id"])) {
$template_id = $data["template_id"];
}
}
if (empty($touser)) {
throw new \Exception("data.touser不能为空", 1);
}
if (empty($template_id)) {
throw new \Exception("data.template_id不能为空", 1);
} else {
if (isset($template[$template_id])) {
$template_id = $template[$template_id];
}
}
$data["touser"] = $touser;
$data["template_id"] = $template_id;
$limit--;
if ($limit <= 0) {
throw new \Exception("超过调用上限", 1);
}
$access_token = $this->_AccessToken($refresh);
if (!$access_token) {
return false;
}
$appid = Arr::get($data, "miniprogram.appid");
if ($appid == 'APPID') {
Arr::set($data, 'miniprogram.appid', Config::get('xcx.appid'));
}
$param = json_encode($data);
if (!isset(self::$TemplateMessageInterfaceCache[$param])) {
self::$TemplateMessageInterfaceCache[$param] = [];
}
if (in_array($touser, self::$TemplateMessageInterfaceCache[$param])) {
throw new \Exception("重复推送", 1);
}
$url = "https://api.weixin.qq.com/cgi-bin/message/template/send";
$url .= "?access_token=" . $access_token;
$curl = Curl::instance();
$send = $curl->setData($param)->post($url);
$errcode = Arr::get($send, "errcode", false);
if ($errcode === 0) {
self::$TemplateMessageInterfaceCache[$param][] = $touser;
return true;
}
if ($errcode === 40001) {
return $this->TemplateMessageInterface($touser, $template_id, $data, true, $limit);
}
$this->log([
'action' => __FUNCTION__,
'url' => $curl->getUrl(),
'params' => $curl->getData(),
'content' => $curl->getContent(),
'msg' => '模板消息推送失败',
], 'error');
} catch (\Throwable $th) {
$this->log([
'action' => __FUNCTION__,
'msg' => '客户端错误:' . $th->getMessage(),
'line' => $th->getLine(),
'file' => $th->getFile(),
], 'error');
}
return false;
}
/**
* Jssdk授权
* @param string $url 授权url
* @return null
*/
public function JsSdk(string $url = "")
{
$jsapiTicket = $this->_JsApiTicket();
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
if (!$url) {
$url = "$protocol$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
}
$timestamp = time();
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$nonceStr = "";
for ($i = 0; $i < 16; $i++) {
$nonceStr .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
// 这里参数的顺序要按照 key 值 ASCII 码升序排序
$string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr&timestamp=$timestamp&url=$url";
$signature = sha1($string);
$signPackage = [
"appId" => $this->appid,
"nonceStr" => $nonceStr,
"timestamp" => $timestamp,
"url" => $url,
"signature" => $signature,
"rawString" => $string,
];
return $signPackage;
}
}