初始化仓库
This commit is contained in:
123
vendor/w7corp/easywechat/src/OfficialAccount/AccessToken.php
vendored
Normal file
123
vendor/w7corp/easywechat/src/OfficialAccount/AccessToken.php
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace EasyWeChat\OfficialAccount;
|
||||
|
||||
use EasyWeChat\Kernel\Contracts\RefreshableAccessToken as RefreshableAccessTokenInterface;
|
||||
use EasyWeChat\Kernel\Exceptions\HttpException;
|
||||
use function intval;
|
||||
use function is_string;
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
use function json_encode;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use Psr\SimpleCache\InvalidArgumentException;
|
||||
use function sprintf;
|
||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
use Symfony\Component\Cache\Psr16Cache;
|
||||
use Symfony\Component\HttpClient\HttpClient;
|
||||
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
|
||||
class AccessToken implements RefreshableAccessTokenInterface
|
||||
{
|
||||
protected HttpClientInterface $httpClient;
|
||||
|
||||
protected CacheInterface $cache;
|
||||
|
||||
public function __construct(
|
||||
protected string $appId,
|
||||
protected string $secret,
|
||||
protected ?string $key = null,
|
||||
?CacheInterface $cache = null,
|
||||
?HttpClientInterface $httpClient = null,
|
||||
) {
|
||||
$this->httpClient = $httpClient ?? HttpClient::create(['base_uri' => 'https://api.weixin.qq.com/']);
|
||||
$this->cache = $cache ?? new Psr16Cache(new FilesystemAdapter(namespace: 'easywechat', defaultLifetime: 1500));
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return $this->key ?? $this->key = sprintf('official_account.access_token.%s.%s', $this->appId, $this->secret);
|
||||
}
|
||||
|
||||
public function setKey(string $key): static
|
||||
{
|
||||
$this->key = $key;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws HttpException
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws DecodingExceptionInterface
|
||||
* @throws RedirectionExceptionInterface
|
||||
* @throws ServerExceptionInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function getToken(): string
|
||||
{
|
||||
$token = $this->cache->get($this->getKey());
|
||||
|
||||
if ((bool) $token && is_string($token)) {
|
||||
return $token;
|
||||
}
|
||||
|
||||
return $this->refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*
|
||||
* @throws HttpException
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws DecodingExceptionInterface
|
||||
* @throws RedirectionExceptionInterface
|
||||
* @throws ServerExceptionInterface
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
#[ArrayShape(['access_token' => 'string'])]
|
||||
public function toQuery(): array
|
||||
{
|
||||
return ['access_token' => $this->getToken()];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RedirectionExceptionInterface
|
||||
* @throws DecodingExceptionInterface
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws HttpException
|
||||
* @throws TransportExceptionInterface
|
||||
* @throws ServerExceptionInterface
|
||||
*/
|
||||
public function refresh(): string
|
||||
{
|
||||
$response = $this->httpClient->request(
|
||||
'GET',
|
||||
'cgi-bin/token',
|
||||
[
|
||||
'query' => [
|
||||
'grant_type' => 'client_credential',
|
||||
'appid' => $this->appId,
|
||||
'secret' => $this->secret,
|
||||
],
|
||||
]
|
||||
)->toArray(false);
|
||||
|
||||
if (empty($response['access_token'])) {
|
||||
throw new HttpException('Failed to get access_token: '.json_encode($response, JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
|
||||
$this->cache->set($this->getKey(), $response['access_token'], intval($response['expires_in']));
|
||||
|
||||
return $response['access_token'];
|
||||
}
|
||||
}
|
||||
43
vendor/w7corp/easywechat/src/OfficialAccount/Account.php
vendored
Normal file
43
vendor/w7corp/easywechat/src/OfficialAccount/Account.php
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace EasyWeChat\OfficialAccount;
|
||||
|
||||
use EasyWeChat\OfficialAccount\Contracts\Account as AccountInterface;
|
||||
use RuntimeException;
|
||||
|
||||
class Account implements AccountInterface
|
||||
{
|
||||
public function __construct(
|
||||
protected string $appId,
|
||||
protected ?string $secret,
|
||||
protected ?string $token = null,
|
||||
protected ?string $aesKey = null
|
||||
) {
|
||||
}
|
||||
|
||||
public function getAppId(): string
|
||||
{
|
||||
return $this->appId;
|
||||
}
|
||||
|
||||
public function getSecret(): string
|
||||
{
|
||||
if (null === $this->secret) {
|
||||
throw new RuntimeException('No secret configured.');
|
||||
}
|
||||
|
||||
return $this->secret;
|
||||
}
|
||||
|
||||
public function getToken(): ?string
|
||||
{
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
public function getAesKey(): ?string
|
||||
{
|
||||
return $this->aesKey;
|
||||
}
|
||||
}
|
||||
256
vendor/w7corp/easywechat/src/OfficialAccount/Application.php
vendored
Normal file
256
vendor/w7corp/easywechat/src/OfficialAccount/Application.php
vendored
Normal file
@ -0,0 +1,256 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace EasyWeChat\OfficialAccount;
|
||||
|
||||
use function array_merge;
|
||||
use function call_user_func;
|
||||
use EasyWeChat\Kernel\Contracts\AccessToken as AccessTokenInterface;
|
||||
use EasyWeChat\Kernel\Contracts\RefreshableAccessToken as RefreshableAccessTokenInterface;
|
||||
use EasyWeChat\Kernel\Contracts\Server as ServerInterface;
|
||||
use EasyWeChat\Kernel\Encryptor;
|
||||
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
|
||||
use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
|
||||
use EasyWeChat\Kernel\HttpClient\AccessTokenAwareClient;
|
||||
use EasyWeChat\Kernel\HttpClient\AccessTokenExpiredRetryStrategy;
|
||||
use EasyWeChat\Kernel\HttpClient\RequestUtil;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use EasyWeChat\Kernel\Traits\InteractWithCache;
|
||||
use EasyWeChat\Kernel\Traits\InteractWithClient;
|
||||
use EasyWeChat\Kernel\Traits\InteractWithConfig;
|
||||
use EasyWeChat\Kernel\Traits\InteractWithHttpClient;
|
||||
use EasyWeChat\Kernel\Traits\InteractWithServerRequest;
|
||||
use EasyWeChat\OfficialAccount\Contracts\Account as AccountInterface;
|
||||
use EasyWeChat\OfficialAccount\Contracts\Application as ApplicationInterface;
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
use Overtrue\Socialite\Contracts\ProviderInterface as SocialiteProviderInterface;
|
||||
use Overtrue\Socialite\Providers\WeChat;
|
||||
use Psr\Log\LoggerAwareTrait;
|
||||
use function sprintf;
|
||||
use function str_contains;
|
||||
use Symfony\Component\HttpClient\Response\AsyncContext;
|
||||
use Symfony\Component\HttpClient\RetryableHttpClient;
|
||||
|
||||
class Application implements ApplicationInterface
|
||||
{
|
||||
use InteractWithConfig;
|
||||
use InteractWithCache;
|
||||
use InteractWithServerRequest;
|
||||
use InteractWithHttpClient;
|
||||
use InteractWithClient;
|
||||
use LoggerAwareTrait;
|
||||
|
||||
protected ?Encryptor $encryptor = null;
|
||||
|
||||
protected ?ServerInterface $server = null;
|
||||
|
||||
protected ?AccountInterface $account = null;
|
||||
|
||||
protected AccessTokenInterface|RefreshableAccessTokenInterface|null $accessToken = null;
|
||||
|
||||
protected ?JsApiTicket $ticket = null;
|
||||
|
||||
protected ?\Closure $oauthFactory = null;
|
||||
|
||||
public function getAccount(): AccountInterface
|
||||
{
|
||||
if (! $this->account) {
|
||||
$this->account = new Account(
|
||||
appId: (string) $this->config->get('app_id'), /** @phpstan-ignore-line */
|
||||
secret: (string) $this->config->get('secret'), /** @phpstan-ignore-line */
|
||||
token: (string) $this->config->get('token'), /** @phpstan-ignore-line */
|
||||
aesKey: (string) $this->config->get('aes_key'),/** @phpstan-ignore-line */
|
||||
);
|
||||
}
|
||||
|
||||
return $this->account;
|
||||
}
|
||||
|
||||
public function setAccount(AccountInterface $account): static
|
||||
{
|
||||
$this->account = $account;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
|
||||
*/
|
||||
public function getEncryptor(): Encryptor
|
||||
{
|
||||
if (! $this->encryptor) {
|
||||
$token = $this->getAccount()->getToken();
|
||||
$aesKey = $this->getAccount()->getAesKey();
|
||||
|
||||
if (empty($token) || empty($aesKey)) {
|
||||
throw new InvalidConfigException('token or aes_key cannot be empty.');
|
||||
}
|
||||
|
||||
$this->encryptor = new Encryptor(
|
||||
appId: $this->getAccount()->getAppId(),
|
||||
token: $token,
|
||||
aesKey: $aesKey,
|
||||
receiveId: $this->getAccount()->getAppId()
|
||||
);
|
||||
}
|
||||
|
||||
return $this->encryptor;
|
||||
}
|
||||
|
||||
public function setEncryptor(Encryptor $encryptor): static
|
||||
{
|
||||
$this->encryptor = $encryptor;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \ReflectionException
|
||||
* @throws InvalidArgumentException
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function getServer(): Server|ServerInterface
|
||||
{
|
||||
if (! $this->server) {
|
||||
$this->server = new Server(
|
||||
request: $this->getRequest(),
|
||||
encryptor: $this->getAccount()->getAesKey() ? $this->getEncryptor() : null
|
||||
);
|
||||
}
|
||||
|
||||
return $this->server;
|
||||
}
|
||||
|
||||
public function setServer(ServerInterface $server): static
|
||||
{
|
||||
$this->server = $server;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAccessToken(): AccessTokenInterface|RefreshableAccessTokenInterface
|
||||
{
|
||||
if (! $this->accessToken) {
|
||||
$this->accessToken = new AccessToken(
|
||||
appId: $this->getAccount()->getAppId(),
|
||||
secret: $this->getAccount()->getSecret(),
|
||||
cache: $this->getCache(),
|
||||
httpClient: $this->getHttpClient(),
|
||||
);
|
||||
}
|
||||
|
||||
return $this->accessToken;
|
||||
}
|
||||
|
||||
public function setAccessToken(AccessTokenInterface|RefreshableAccessTokenInterface $accessToken): static
|
||||
{
|
||||
$this->accessToken = $accessToken;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setOAuthFactory(callable $factory): static
|
||||
{
|
||||
$this->oauthFactory = fn (Application $app): WeChat => $factory($app);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function getOAuth(): SocialiteProviderInterface
|
||||
{
|
||||
if (! $this->oauthFactory) {
|
||||
$this->oauthFactory = fn (self $app): SocialiteProviderInterface => (new WeChat(
|
||||
[
|
||||
'client_id' => $this->getAccount()->getAppId(),
|
||||
'client_secret' => $this->getAccount()->getSecret(),
|
||||
'redirect_url' => $this->config->get('oauth.redirect_url'),
|
||||
]
|
||||
))->scopes((array) $this->config->get('oauth.scopes', ['snsapi_userinfo']));
|
||||
}
|
||||
|
||||
$provider = call_user_func($this->oauthFactory, $this);
|
||||
|
||||
if (! $provider instanceof SocialiteProviderInterface) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'The factory must return a %s instance.',
|
||||
SocialiteProviderInterface::class
|
||||
));
|
||||
}
|
||||
|
||||
return $provider;
|
||||
}
|
||||
|
||||
public function getTicket(): JsApiTicket
|
||||
{
|
||||
if (! $this->ticket) {
|
||||
$this->ticket = new JsApiTicket(
|
||||
appId: $this->getAccount()->getAppId(),
|
||||
secret: $this->getAccount()->getSecret(),
|
||||
cache: $this->getCache(),
|
||||
httpClient: $this->getClient(),
|
||||
);
|
||||
}
|
||||
|
||||
return $this->ticket;
|
||||
}
|
||||
|
||||
public function setTicket(JsApiTicket $ticket): static
|
||||
{
|
||||
$this->ticket = $ticket;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Pure]
|
||||
public function getUtils(): Utils
|
||||
{
|
||||
return new Utils($this);
|
||||
}
|
||||
|
||||
public function createClient(): AccessTokenAwareClient
|
||||
{
|
||||
$httpClient = $this->getHttpClient();
|
||||
|
||||
if ((bool) $this->config->get('http.retry', false)) {
|
||||
$httpClient = new RetryableHttpClient(
|
||||
$httpClient,
|
||||
$this->getRetryStrategy(),
|
||||
(int) $this->config->get('http.max_retries', 2) // @phpstan-ignore-line
|
||||
);
|
||||
}
|
||||
|
||||
return (new AccessTokenAwareClient(
|
||||
client: $httpClient,
|
||||
accessToken: $this->getAccessToken(),
|
||||
failureJudge: fn (Response $response) => (bool) ($response->toArray()['errcode'] ?? 0),
|
||||
throw: (bool) $this->config->get('http.throw', true),
|
||||
))->setPresets($this->config->all());
|
||||
}
|
||||
|
||||
public function getRetryStrategy(): AccessTokenExpiredRetryStrategy
|
||||
{
|
||||
$retryConfig = RequestUtil::mergeDefaultRetryOptions((array) $this->config->get('http.retry', []));
|
||||
|
||||
return (new AccessTokenExpiredRetryStrategy($retryConfig))
|
||||
->decideUsing(function (AsyncContext $context, ?string $responseContent): bool {
|
||||
return ! empty($responseContent)
|
||||
&& str_contains($responseContent, '42001')
|
||||
&& str_contains($responseContent, 'access_token expired');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
protected function getHttpClientDefaultOptions(): array
|
||||
{
|
||||
return array_merge(
|
||||
['base_uri' => 'https://api.weixin.qq.com/'],
|
||||
(array) $this->config->get('http', [])
|
||||
);
|
||||
}
|
||||
}
|
||||
15
vendor/w7corp/easywechat/src/OfficialAccount/Config.php
vendored
Normal file
15
vendor/w7corp/easywechat/src/OfficialAccount/Config.php
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace EasyWeChat\OfficialAccount;
|
||||
|
||||
class Config extends \EasyWeChat\Kernel\Config
|
||||
{
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected array $requiredKeys = [
|
||||
'app_id',
|
||||
];
|
||||
}
|
||||
16
vendor/w7corp/easywechat/src/OfficialAccount/Contracts/Account.php
vendored
Normal file
16
vendor/w7corp/easywechat/src/OfficialAccount/Contracts/Account.php
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace EasyWeChat\OfficialAccount\Contracts;
|
||||
|
||||
interface Account
|
||||
{
|
||||
public function getAppId(): string;
|
||||
|
||||
public function getSecret(): string;
|
||||
|
||||
public function getToken(): ?string;
|
||||
|
||||
public function getAesKey(): ?string;
|
||||
}
|
||||
40
vendor/w7corp/easywechat/src/OfficialAccount/Contracts/Application.php
vendored
Normal file
40
vendor/w7corp/easywechat/src/OfficialAccount/Contracts/Application.php
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace EasyWeChat\OfficialAccount\Contracts;
|
||||
|
||||
use EasyWeChat\Kernel\Contracts\AccessToken;
|
||||
use EasyWeChat\Kernel\Contracts\Config;
|
||||
use EasyWeChat\Kernel\Contracts\Server;
|
||||
use EasyWeChat\Kernel\Encryptor;
|
||||
use EasyWeChat\Kernel\HttpClient\AccessTokenAwareClient;
|
||||
use Overtrue\Socialite\Contracts\ProviderInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
|
||||
interface Application
|
||||
{
|
||||
public function getAccount(): Account;
|
||||
|
||||
public function getEncryptor(): Encryptor;
|
||||
|
||||
public function getServer(): Server;
|
||||
|
||||
public function getRequest(): ServerRequestInterface;
|
||||
|
||||
public function getClient(): AccessTokenAwareClient;
|
||||
|
||||
public function getHttpClient(): HttpClientInterface;
|
||||
|
||||
public function getConfig(): Config;
|
||||
|
||||
public function getAccessToken(): AccessToken;
|
||||
|
||||
public function getCache(): CacheInterface;
|
||||
|
||||
public function getOAuth(): ProviderInterface;
|
||||
|
||||
public function setOAuthFactory(callable $factory): static;
|
||||
}
|
||||
82
vendor/w7corp/easywechat/src/OfficialAccount/JsApiTicket.php
vendored
Normal file
82
vendor/w7corp/easywechat/src/OfficialAccount/JsApiTicket.php
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace EasyWeChat\OfficialAccount;
|
||||
|
||||
use EasyWeChat\Kernel\Exceptions\HttpException;
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
use function sprintf;
|
||||
|
||||
class JsApiTicket extends AccessToken
|
||||
{
|
||||
/**
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
|
||||
* @throws \EasyWeChat\Kernel\Exceptions\HttpException
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface
|
||||
*/
|
||||
public function getTicket(): string
|
||||
{
|
||||
$key = $this->getKey();
|
||||
$ticket = $this->cache->get($key);
|
||||
|
||||
if ((bool) $ticket && \is_string($ticket)) {
|
||||
return $ticket;
|
||||
}
|
||||
|
||||
$response = $this->httpClient->request('GET', '/cgi-bin/ticket/getticket', ['query' => ['type' => 'jsapi']])
|
||||
->toArray(false);
|
||||
|
||||
if (empty($response['ticket'])) {
|
||||
throw new HttpException('Failed to get jssdk ticket: '.\json_encode($response, JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
|
||||
$this->cache->set($key, $response['ticket'], \intval($response['expires_in']));
|
||||
|
||||
return $response['ticket'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
*
|
||||
* @throws \EasyWeChat\Kernel\Exceptions\HttpException
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
|
||||
*/
|
||||
#[ArrayShape([
|
||||
'url' => 'string',
|
||||
'nonceStr' => 'string',
|
||||
'timestamp' => 'int',
|
||||
'appId' => 'string',
|
||||
'signature' => 'string',
|
||||
])]
|
||||
public function configSignature(string $url, string $nonce, int $timestamp): array
|
||||
{
|
||||
return [
|
||||
'url' => $url,
|
||||
'nonceStr' => $nonce,
|
||||
'timestamp' => $timestamp,
|
||||
'appId' => $this->appId,
|
||||
'signature' => sha1(sprintf(
|
||||
'jsapi_ticket=%s&noncestr=%s×tamp=%s&url=%s',
|
||||
$this->getTicket(),
|
||||
$nonce,
|
||||
$timestamp,
|
||||
$url
|
||||
)),
|
||||
];
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return $this->key ?? $this->key = sprintf('official_account.jsapi_ticket.%s', $this->appId);
|
||||
}
|
||||
}
|
||||
14
vendor/w7corp/easywechat/src/OfficialAccount/Message.php
vendored
Normal file
14
vendor/w7corp/easywechat/src/OfficialAccount/Message.php
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace EasyWeChat\OfficialAccount;
|
||||
|
||||
/**
|
||||
* @property string $Event
|
||||
* @property string $MsgType
|
||||
*/
|
||||
class Message extends \EasyWeChat\Kernel\Message
|
||||
{
|
||||
//
|
||||
}
|
||||
151
vendor/w7corp/easywechat/src/OfficialAccount/Server.php
vendored
Normal file
151
vendor/w7corp/easywechat/src/OfficialAccount/Server.php
vendored
Normal file
@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace EasyWeChat\OfficialAccount;
|
||||
|
||||
use Closure;
|
||||
use EasyWeChat\Kernel\Contracts\Server as ServerInterface;
|
||||
use EasyWeChat\Kernel\Encryptor;
|
||||
use EasyWeChat\Kernel\Exceptions\BadRequestException;
|
||||
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
|
||||
use EasyWeChat\Kernel\Exceptions\RuntimeException;
|
||||
use EasyWeChat\Kernel\HttpClient\RequestUtil;
|
||||
use EasyWeChat\Kernel\ServerResponse;
|
||||
use EasyWeChat\Kernel\Traits\DecryptXmlMessage;
|
||||
use EasyWeChat\Kernel\Traits\InteractWithHandlers;
|
||||
use EasyWeChat\Kernel\Traits\RespondXmlMessage;
|
||||
use Nyholm\Psr7\Response;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Throwable;
|
||||
|
||||
class Server implements ServerInterface
|
||||
{
|
||||
use RespondXmlMessage;
|
||||
use DecryptXmlMessage;
|
||||
use InteractWithHandlers;
|
||||
|
||||
protected ServerRequestInterface $request;
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function __construct(
|
||||
?ServerRequestInterface $request = null,
|
||||
protected ?Encryptor $encryptor = null,
|
||||
) {
|
||||
$this->request = $request ?? RequestUtil::createDefaultServerRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
* @throws BadRequestException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function serve(): ResponseInterface
|
||||
{
|
||||
if ((bool) ($str = $this->request->getQueryParams()['echostr'] ?? '')) {
|
||||
return new Response(200, [], $str);
|
||||
}
|
||||
|
||||
$message = $this->getRequestMessage($this->request);
|
||||
$query = $this->request->getQueryParams();
|
||||
|
||||
if ($this->encryptor && ! empty($query['msg_signature'])) {
|
||||
$this->prepend($this->decryptRequestMessage($query));
|
||||
}
|
||||
|
||||
$response = $this->handle(new Response(200, [], 'success'), $message);
|
||||
|
||||
if (! ($response instanceof ResponseInterface)) {
|
||||
$response = $this->transformToReply($response, $message, $this->encryptor);
|
||||
}
|
||||
|
||||
return ServerResponse::make($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function addMessageListener(string $type, callable|string $handler): static
|
||||
{
|
||||
$handler = $this->makeClosure($handler);
|
||||
$this->withHandler(
|
||||
function (Message $message, Closure $next) use ($type, $handler): mixed {
|
||||
return $message->MsgType === $type ? $handler($message, $next) : $next($message);
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function addEventListener(string $event, callable|string $handler): static
|
||||
{
|
||||
$handler = $this->makeClosure($handler);
|
||||
$this->withHandler(
|
||||
function (Message $message, Closure $next) use ($event, $handler): mixed {
|
||||
return $message->Event === $event ? $handler($message, $next) : $next($message);
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,string> $query
|
||||
* @psalm-suppress PossiblyNullArgument
|
||||
*/
|
||||
protected function decryptRequestMessage(array $query): Closure
|
||||
{
|
||||
return function (Message $message, Closure $next) use ($query): mixed {
|
||||
if (! $this->encryptor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->decryptMessage(
|
||||
message: $message,
|
||||
encryptor: $this->encryptor,
|
||||
signature: $query['msg_signature'] ?? '',
|
||||
timestamp: $query['timestamp'] ?? '',
|
||||
nonce: $query['nonce'] ?? ''
|
||||
);
|
||||
|
||||
return $next($message);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BadRequestException
|
||||
*/
|
||||
public function getRequestMessage(?ServerRequestInterface $request = null): \EasyWeChat\Kernel\Message
|
||||
{
|
||||
return Message::createFromRequest($request ?? $this->request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BadRequestException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function getDecryptedMessage(?ServerRequestInterface $request = null): \EasyWeChat\Kernel\Message
|
||||
{
|
||||
$request = $request ?? $this->request;
|
||||
$message = $this->getRequestMessage($request);
|
||||
$query = $request->getQueryParams();
|
||||
|
||||
if (! $this->encryptor || empty($query['msg_signature'])) {
|
||||
return $message;
|
||||
}
|
||||
|
||||
return $this->decryptMessage(
|
||||
message: $message,
|
||||
encryptor: $this->encryptor,
|
||||
signature: $query['msg_signature'],
|
||||
timestamp: $query['timestamp'] ?? '',
|
||||
nonce: $query['nonce'] ?? ''
|
||||
);
|
||||
}
|
||||
}
|
||||
47
vendor/w7corp/easywechat/src/OfficialAccount/Utils.php
vendored
Normal file
47
vendor/w7corp/easywechat/src/OfficialAccount/Utils.php
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace EasyWeChat\OfficialAccount;
|
||||
|
||||
use EasyWeChat\Kernel\Exceptions\HttpException;
|
||||
use EasyWeChat\Kernel\Support\Str;
|
||||
use Psr\SimpleCache\InvalidArgumentException;
|
||||
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use function time;
|
||||
|
||||
class Utils
|
||||
{
|
||||
public function __construct(protected Application $app)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param array<string> $jsApiList
|
||||
* @param array<string> $openTagList
|
||||
* @param bool $debug
|
||||
* @return array<string, mixed>
|
||||
*
|
||||
* @throws HttpException
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws DecodingExceptionInterface
|
||||
* @throws RedirectionExceptionInterface
|
||||
* @throws ServerExceptionInterface
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public function buildJsSdkConfig(
|
||||
string $url,
|
||||
array $jsApiList = [],
|
||||
array $openTagList = [],
|
||||
bool $debug = false
|
||||
): array {
|
||||
return array_merge(
|
||||
compact('jsApiList', 'openTagList', 'debug'),
|
||||
$this->app->getTicket()->configSignature($url, Str::random(), time())
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user