chore: 添加静态资源文件和版本配置文件
添加大量图片、字体、音频等静态资源文件,包括用户界面图片、模板文件和安装界面素材 添加系统版本配置文件(.version)和常量定义文件(.constant) 更新供应商依赖文件,包括Symfony VarDumper组件和相关许可证文件 添加验证码配置文件(captcha.php)和.gitignore规则
This commit is contained in:
245
crmeb/services/wechat/BaseApplication.php
Normal file
245
crmeb/services/wechat/BaseApplication.php
Normal file
@@ -0,0 +1,245 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace crmeb\services\wechat;
|
||||
|
||||
use think\helper\Str;
|
||||
use EasyWeChat\Work\Application;
|
||||
use Symfony\Component\HttpFoundation\HeaderBag;
|
||||
use EasyWeChat\Pay\Application as PayApplication;
|
||||
use EasyWeChat\MiniApp\Application as MiniAppApplication;
|
||||
use crmeb\services\wechat\contract\BaseApplicationInterface;
|
||||
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
|
||||
use EasyWeChat\OfficialAccount\Application as OfficialAccountApplication;
|
||||
use think\Request;
|
||||
|
||||
/**
|
||||
* Class BaseApplication
|
||||
* @package crmeb\services\wechat
|
||||
*/
|
||||
abstract class BaseApplication implements BaseApplicationInterface
|
||||
{
|
||||
|
||||
//app端
|
||||
const APP = 'app';
|
||||
//h5端、公众端
|
||||
const WEB = 'web';
|
||||
//小程序端
|
||||
const MINI = 'mini';
|
||||
//开发平台
|
||||
const OPEN = 'open';
|
||||
//pc端
|
||||
const PC = 'pc';
|
||||
|
||||
const BASE_URL = 'https://api.weixin.qq.com';
|
||||
|
||||
/**
|
||||
* 访问端
|
||||
* @var string
|
||||
*/
|
||||
protected string $accessEnd = '';
|
||||
|
||||
/**
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $name;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static array $property = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $pushMessageHandler;
|
||||
|
||||
/**
|
||||
* Debug
|
||||
* @var bool
|
||||
*/
|
||||
protected bool $debug = true;
|
||||
|
||||
/**
|
||||
* 设置消息处理类
|
||||
* @param string $handler
|
||||
* @return $this
|
||||
*/
|
||||
public function setPushMessageHandler(string $handler): static
|
||||
{
|
||||
$this->pushMessageHandler = $handler;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置访问端
|
||||
* @param string $accessEnd
|
||||
* @return $this
|
||||
*/
|
||||
public function setAccessEnd(string $accessEnd)
|
||||
{
|
||||
if (in_array($accessEnd, [self::APP, self::WEB, self::MINI])) {
|
||||
$this->accessEnd = $accessEnd;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动获取访问端
|
||||
* @param Request $request
|
||||
* @return string
|
||||
*/
|
||||
public function getAuthAccessEnd(Request $request): string
|
||||
{
|
||||
if (!$this->accessEnd) {
|
||||
try {
|
||||
if ($request->isApp()) {
|
||||
$this->accessEnd = self::APP;
|
||||
} else if ($request->isPc()) {
|
||||
$this->accessEnd = self::PC;
|
||||
} else if ($request->isWechat() || $request->isH5()) {
|
||||
$this->accessEnd = self::WEB;
|
||||
} else if ($request->isRoutine()) {
|
||||
$this->accessEnd = self::MINI;
|
||||
} else {
|
||||
$this->accessEnd = self::WEB;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->accessEnd = self::WEB;
|
||||
}
|
||||
}
|
||||
return $this->accessEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录错误日志
|
||||
* @param \Throwable $e
|
||||
*/
|
||||
protected static function error(\Throwable $e)
|
||||
{
|
||||
static::instance()->debug && response_log_write([
|
||||
'error' => $e->getMessage(),
|
||||
'line' => $e->getLine(),
|
||||
'file' => $e->getFile(),
|
||||
'trace' => $e->getTrace()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求日志
|
||||
* @param string $message
|
||||
* @param $request
|
||||
* @param $response
|
||||
*/
|
||||
protected static function logger(string $message, $request, $response)
|
||||
{
|
||||
$debug = static::instance()->debug;
|
||||
|
||||
if ($debug) {
|
||||
if (is_object($response) && method_exists($response, 'toArray')) {
|
||||
$response = $response->toArray();
|
||||
}
|
||||
|
||||
response_log_write([
|
||||
'message' => $message,
|
||||
'request' => $request,
|
||||
'response' => $response
|
||||
], 'info');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置request
|
||||
* @param PayApplication|OfficialAccountApplication|MiniAppApplication|Application $application
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
protected function setRequest(PayApplication|OfficialAccountApplication|MiniAppApplication|Application $application)
|
||||
{
|
||||
$request = request();
|
||||
$symfonyRequest = new SymfonyRequest($request->get(), $request->post(), [], $request->cookie(), [], $request->server(), $request->getContent());
|
||||
$symfonyRequest->headers = new HeaderBag($request->header());
|
||||
$application->setRequestFromSymfonyRequest($symfonyRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置日志
|
||||
* @param MiniAppApplication|OfficialAccountApplication $loggerAwareTrait
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
protected function setLogger(MiniAppApplication|OfficialAccountApplication $loggerAwareTrait)
|
||||
{
|
||||
$loggerAwareTrait->setLogger(app()->log);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置缓存
|
||||
* @param $withCache Application|OfficialAccountApplication|MiniAppApplication|
|
||||
* @return void
|
||||
*/
|
||||
protected function setCache($withCache)
|
||||
{
|
||||
$withCache->setCache(app()->cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置http请求
|
||||
* @param $application Application|OfficialAccountApplication|MiniAppApplication|PayApplication
|
||||
* @param string $baseUrl
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2024/1/25
|
||||
*/
|
||||
protected function setHttpClient($application, string $baseUrl = self::BASE_URL)
|
||||
{
|
||||
$application->setHttpClient(new CustomHttpClient($baseUrl));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求驱动
|
||||
* @param string $name
|
||||
* @return string
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function getClientClassName(string $name)
|
||||
{
|
||||
$class = Str::studly($name) . 'Client';
|
||||
$className = '\\crmeb\\services\\wechat\\client\\' . $this->name . '\\' . $class;
|
||||
|
||||
return $className;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param $arguments
|
||||
* @return mixed|object
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
$className = $this->getClientClassName($name);
|
||||
if (class_exists($className)) {
|
||||
$client = $this->application()->getClient();
|
||||
return new $className($client);
|
||||
} else {
|
||||
throw new WechatException('请求类不存在');
|
||||
}
|
||||
}
|
||||
}
|
||||
167
crmeb/services/wechat/CustomHttpClient.php
Normal file
167
crmeb/services/wechat/CustomHttpClient.php
Normal file
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat;
|
||||
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseStreamInterface;
|
||||
use Symfony\Component\HttpClient\NativeHttpClient;
|
||||
use Symfony\Component\HttpClient\Response\AsyncContext;
|
||||
use Swoole\Coroutine\Http\Client;
|
||||
|
||||
/**
|
||||
* 携程curl请求
|
||||
* Class CustomHttpClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2024/1/24
|
||||
* @package crmeb\services\wechat
|
||||
*/
|
||||
class CustomHttpClient implements HttpClientInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $baseUrl = '';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected int $timeout = 3;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $httpConfig = [];
|
||||
|
||||
/**
|
||||
* CustomHttpClient constructor.
|
||||
* @param string $baseUrl
|
||||
* @param array $httpConfig
|
||||
*/
|
||||
public function __construct(string $baseUrl, array $httpConfig = [])
|
||||
{
|
||||
$this->baseUrl = $baseUrl;
|
||||
$this->httpConfig = $httpConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起请求
|
||||
* @param string $method
|
||||
* @param string $url
|
||||
* @param array $options
|
||||
* @return ResponseInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2024/1/24
|
||||
*/
|
||||
public function request(string $method, string $url, array $options = []): ResponseInterface
|
||||
{
|
||||
// 合并传入的选项和默认选项
|
||||
$options = array_merge(self::OPTIONS_DEFAULTS, $this->httpConfig, $options);
|
||||
|
||||
$ssl = strstr($this->baseUrl, 'https://') !== false;
|
||||
$baseUrl = str_replace(['https://', 'http://', '/'], '', $this->baseUrl);
|
||||
|
||||
// 使用 Swoole Client 完成请求
|
||||
$client = new Client($baseUrl, $ssl ? 443 : 80, $ssl);
|
||||
|
||||
$client->set([
|
||||
'timeout' => !empty($options['timeout']) ? $options['timeout'] : $this->timeout,
|
||||
'ssl_cert_file' => $options['cert'] ?? null,
|
||||
'ssl_key_file' => $options['ssl_key'] ?? null
|
||||
]);
|
||||
|
||||
$client->setMethod($method);
|
||||
$headers = [];
|
||||
// 设置请求头
|
||||
foreach ($options['headers'] as $key => $value) {
|
||||
if (is_string($key)) {
|
||||
$values = [];
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $item) {
|
||||
[$type, $val] = strstr($item, ':') !== false ? explode(':', $item) : [null, null];
|
||||
if ($type && $val) {
|
||||
$values[] = $val;
|
||||
}
|
||||
}
|
||||
$headers[$key] = implode(',', $values);
|
||||
} else {
|
||||
$values[] = $value;
|
||||
$headers[$key] = implode(',', $values);
|
||||
}
|
||||
} else {
|
||||
[$type, $val] = strstr($value, ':') !== false ? explode(':', $value) : [null, null];
|
||||
$values[] = $val;
|
||||
$headers[$type] = implode(',', $values);
|
||||
}
|
||||
}
|
||||
|
||||
$client->setHeaders($headers);
|
||||
|
||||
if (!empty($options['query'])) {
|
||||
$url = $url . (strstr($url, '?') !== false ? '&' : '?') . http_build_query($options['query']);
|
||||
}
|
||||
if (!empty($options['json'])) {
|
||||
$client->setData($options['json']);
|
||||
} else if (!empty($options['body'])) {
|
||||
$client->setData($options['body']);
|
||||
}
|
||||
|
||||
// 发起请求
|
||||
$client->execute('/' . $url);
|
||||
|
||||
// 创建响应对象
|
||||
$response = new SwooleResponse($client);
|
||||
|
||||
// 关闭客户端连接
|
||||
$client->close();
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param iterable|ResponseInterface $responses
|
||||
* @param float|null $timeout
|
||||
* @return ResponseStreamInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2024/1/25
|
||||
*/
|
||||
public function stream(iterable|ResponseInterface $responses, float $timeout = null): ResponseStreamInterface
|
||||
{
|
||||
// 创建 ResponseStreamInterface 对象并进行流式处理
|
||||
$stream = new AsyncContext($responses, $this->httpClient, $timeout);
|
||||
|
||||
// 返回流对象
|
||||
return $stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @return $this
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2024/1/25
|
||||
*/
|
||||
public function withOptions(array $options): static
|
||||
{
|
||||
$this->httpConfig = array_merge(self::OPTIONS_DEFAULTS, $options);
|
||||
if (!empty($this->httpConfig['base_uri']) && $this->httpConfig['base_uri'] !== $this->baseUrl) {
|
||||
$this->baseUrl = $this->httpConfig['base_uri'];
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
259
crmeb/services/wechat/DefaultConfig.php
Normal file
259
crmeb/services/wechat/DefaultConfig.php
Normal file
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace crmeb\services\wechat;
|
||||
|
||||
use think\facade\Config;
|
||||
|
||||
/**
|
||||
* 默认配置
|
||||
* Class DefaultConfig
|
||||
* @package crmeb\services\wechat
|
||||
*/
|
||||
class DefaultConfig
|
||||
{
|
||||
//小程序appid
|
||||
const MINI_APPID = 'mini.appid';
|
||||
//公众号appid
|
||||
const OFFICIAL_APPID = 'official.appid';
|
||||
//开放平台appid
|
||||
const APP_APPID = 'app.appid';
|
||||
//开放平台网页端appid
|
||||
const WEB_APPID = 'web.appid';
|
||||
//企业微信id
|
||||
const WORK_CORP_ID = 'work.corp_id';
|
||||
//商户id
|
||||
const PAY_MCHID = 'pay.mchid';
|
||||
//系统配置域名地址,携带,格式:http://www.a.com
|
||||
const COMMENT_URL = 'comment.url';
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
const WECHAT_CONFIG = [
|
||||
//请求响应日志
|
||||
'logger' => true,
|
||||
//公用
|
||||
'comment' => [
|
||||
'url' => [
|
||||
'key' => 'site_url'
|
||||
],
|
||||
],
|
||||
//小程序配置
|
||||
'mini' => [
|
||||
'appid' => [
|
||||
'key' => 'routine_appId'
|
||||
],
|
||||
'secret' => [
|
||||
'key' => 'routine_appsecret'
|
||||
],
|
||||
'token' => [
|
||||
'key' => 'wechat_token'
|
||||
],
|
||||
'key' => [
|
||||
'key' => 'wechat_encodingaeskey'
|
||||
],
|
||||
'notifyUrl' => [
|
||||
//必须携带斜杠开头
|
||||
'value' => '/api/pay/notify/routine'
|
||||
],
|
||||
],
|
||||
//公众号配置
|
||||
'official' => [
|
||||
'appid' => [
|
||||
'key' => 'wechat_appid'
|
||||
],
|
||||
'secret' => [
|
||||
'key' => 'wechat_appsecret'
|
||||
],
|
||||
'token' => [
|
||||
'key' => 'wechat_token'
|
||||
],
|
||||
'key' => [
|
||||
'key' => 'wechat_encodingaeskey'
|
||||
],
|
||||
'encode' => [
|
||||
'key' => 'wechat_encode'
|
||||
],
|
||||
],
|
||||
//开放平台APP
|
||||
'app' => [
|
||||
'appid' => [
|
||||
'key' => 'wechat_app_appid'
|
||||
],
|
||||
'secret' => [
|
||||
'key' => 'wechat_app_appsecret'
|
||||
],
|
||||
'token' => [
|
||||
'key' => 'wechat_openapp_app_token'
|
||||
],
|
||||
'key' => [
|
||||
'key' => 'wechat_openapp_app_aes_key'
|
||||
],
|
||||
'notifyUrl' => [
|
||||
//必须携带斜杠开头
|
||||
'value' => '/api/pay/notify/app'
|
||||
],
|
||||
],
|
||||
//开放平台网页应用
|
||||
'web' => [
|
||||
'appid' => [
|
||||
'key' => 'wechat_open_app_id'
|
||||
],
|
||||
'secret' => [
|
||||
'key' => 'wechat_open_app_secret'
|
||||
],
|
||||
'token' => [
|
||||
'key' => 'wechat_open_app_token'
|
||||
],
|
||||
'key' => [
|
||||
'key' => 'wechat_open_app_aes_key'
|
||||
],
|
||||
],
|
||||
//企业微信
|
||||
'work' => [
|
||||
'corp_id' => [
|
||||
'key' => 'wechat_work_corpid'
|
||||
],
|
||||
'token' => [
|
||||
'key' => 'wechat_work_token'
|
||||
],
|
||||
'key' => [
|
||||
'key' => 'wechat_work_aes_key'
|
||||
],
|
||||
],
|
||||
//支付
|
||||
'pay' => [
|
||||
//商户号
|
||||
'mchid' => [
|
||||
'key' => 'pay_weixin_mchid'
|
||||
],
|
||||
//小程序商户号
|
||||
'routine_mchid' => [
|
||||
'key' => 'pay_routine_mchid'
|
||||
],
|
||||
//支付key
|
||||
'key' => [
|
||||
'key' => 'pay_weixin_key'
|
||||
],
|
||||
//证书
|
||||
'client_cert' => [
|
||||
'key' => 'pay_weixin_client_cert'
|
||||
],
|
||||
//证书
|
||||
'client_key' => [
|
||||
'key' => 'pay_weixin_client_key'
|
||||
],
|
||||
'notifyUrl' => [
|
||||
//支付回调,必须携带斜杠开头
|
||||
'value' => '/api/pay/notify/wechat'
|
||||
],
|
||||
'refundUrl' => [
|
||||
//退款回调,必须携带斜杠开头
|
||||
'value' => '/api/pay/refund/wechat'
|
||||
],
|
||||
],
|
||||
//v3支付新增配置,证书和商户号使用v2支付配置的证书
|
||||
'v3_pay' => [
|
||||
'key' => [
|
||||
//默认使用value值,没有值使用eb_system_config配置中的key的值
|
||||
'key' => 'v3_pay_weixin_key',
|
||||
//配置值
|
||||
'value' => '',
|
||||
],
|
||||
'serial_no' => [
|
||||
//默认使用value值,没有值使用eb_system_config配置中的key的值
|
||||
'key' => 'pay_weixin_serial_no',
|
||||
//配置值
|
||||
'value' => '',
|
||||
],
|
||||
'pay_type' => [
|
||||
//默认使用value值,没有值使用eb_system_config配置中的key的值
|
||||
'key' => 'pay_wechat_type',
|
||||
//配置值
|
||||
'value' => '',
|
||||
],
|
||||
'public_key'=>[
|
||||
//默认使用value值,没有值使用eb_system_config配置中的key的值
|
||||
'key' => 'v3_pay_public_key',
|
||||
//配置值
|
||||
'value' => '',
|
||||
],
|
||||
'public_pem'=>[
|
||||
//默认使用value值,没有值使用eb_system_config配置中的key的值
|
||||
'key' => 'v3_pay_public_pem',
|
||||
//配置值
|
||||
'value' => '',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* 获取配置,如果配置为数组则使用value的值,如果没有值返回key
|
||||
* @param string $key
|
||||
* @return array|mixed|string[]|null|bool
|
||||
*/
|
||||
public static function value(string $key)
|
||||
{
|
||||
$config = [];
|
||||
if (Config::has('wechat')) {
|
||||
$config = Config::get('wechat', []);
|
||||
}
|
||||
$config = array_merge(self::WECHAT_CONFIG, $config);
|
||||
|
||||
$key = explode('.', $key);
|
||||
$value = null;
|
||||
foreach ($key as $k) {
|
||||
if ($value) {
|
||||
$value = $value[$k] ?? null;
|
||||
} else {
|
||||
$value = $config[$k] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
return $value['value'] ?? null;
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @return mixed|null
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/18
|
||||
*/
|
||||
public static function key(string $key)
|
||||
{
|
||||
$config = [];
|
||||
if (Config::has('wechat')) {
|
||||
$config = Config::get('wechat', []);
|
||||
}
|
||||
$config = array_merge(self::WECHAT_CONFIG, $config);
|
||||
$key = explode('.', $key);
|
||||
$value = null;
|
||||
foreach ($key as $k) {
|
||||
if ($value) {
|
||||
$value = $value[$k] ?? null;
|
||||
} else {
|
||||
$value = $config[$k] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
$value = $value['key'] ?? null;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
140
crmeb/services/wechat/ErrorMessage.php
Normal file
140
crmeb/services/wechat/ErrorMessage.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace crmeb\services\wechat;
|
||||
|
||||
|
||||
use crmeb\utils\ApiErrorCode;
|
||||
|
||||
/**
|
||||
* 错误消息处理
|
||||
* Class ErrorMessage
|
||||
* @package crmeb\services\wechat
|
||||
*/
|
||||
class ErrorMessage
|
||||
{
|
||||
|
||||
const MSG_CODE = [
|
||||
'1' => '未创建直播间',
|
||||
'1003' => '商品id不存在',
|
||||
'47001' => '入参格式不符合规范',
|
||||
'200002' => '入参错误',
|
||||
'300001' => '禁止创建/更新商品 或 禁止编辑&更新房间',
|
||||
'300002' => '名称长度不符合规则',
|
||||
'300006' => '图片上传失败',
|
||||
'300022' => '此房间号不存在',
|
||||
'300023' => '房间状态 拦截',
|
||||
'300024' => '商品不存在',
|
||||
'300025' => '商品审核未通过',
|
||||
'300026' => '房间商品数量已经满额',
|
||||
'300027' => '导入商品失败',
|
||||
'300028' => '房间名称违规',
|
||||
'300029' => '主播昵称违规',
|
||||
'300030' => '主播微信号不合法',
|
||||
'300031' => '直播间封面图不合规',
|
||||
'300032' => '直播间分享图违规',
|
||||
'300033' => '添加商品超过直播间上限',
|
||||
'300034' => '主播微信昵称长度不符合要求',
|
||||
'300035' => '主播微信号不存在',
|
||||
'300036' => '主播微信号未实名认证',
|
||||
'300037' => '购物直播频道封面图不合规',
|
||||
'300038' => '未在小程序管理后台配置客服',
|
||||
'9410000' => '直播间列表为空',
|
||||
'9410001' => '获取房间失败',
|
||||
'9410002' => '获取商品失败',
|
||||
'9410003' => '获取回放失败',
|
||||
'300003' => '价格输入不合规',
|
||||
'300004' => '商品名称存在违规违法内容',
|
||||
'300005' => '商品图片存在违规违法内容',
|
||||
'300007' => '线上小程序版本不存在该链接',
|
||||
'300008' => '添加商品失败',
|
||||
'300009' => '商品审核撤回失败',
|
||||
'300010' => '商品审核状态不对',
|
||||
'300011' => '操作非法',
|
||||
'300012' => '没有提审额度',
|
||||
'300013' => '提审失败',
|
||||
'300014' => '审核中,无法删除',
|
||||
'300017' => '商品未提审',
|
||||
'300018' => '图片尺寸不符合要求',
|
||||
'300021' => '商品添加成功,审核失败',
|
||||
'40001' => 'AppSecret错误或者AppSecret不属于这个小程序,请确认AppSecret 的正确性',
|
||||
'40002' => '请确保grant_type字段值为client_credential',
|
||||
'40013' => '不合法的AppID,请检查AppID的正确性,避免异常字符,注意大小写',
|
||||
'40125' => '小程序配置无效,请检查配置',
|
||||
'40164' => 'IP地址不在白名单中,请检查设置',
|
||||
'41002' => '缺少appid参数',
|
||||
'41004' => '缺少secret参数',
|
||||
'43104' => 'appid与openid不匹配',
|
||||
'48001' => '微信接口暂无权限,请先去获取',
|
||||
'-1' => '系统错误',
|
||||
];
|
||||
|
||||
const WORK_ERROR_MESSAGE = [
|
||||
40098 => '成员尚未实名认证',
|
||||
40068 => '不合法的标签/标签组ID'
|
||||
];
|
||||
|
||||
/**
|
||||
* 处理返回错误信息友好提示
|
||||
* @param string $message
|
||||
* @return array|mixed|string
|
||||
*/
|
||||
public static function getMessage(string $message)
|
||||
{
|
||||
if (strstr($message, 'Request AccessToken fail') !== false || strstr($message, 'Request access_token fail') !== false) {
|
||||
$message = str_replace('Request AccessToken fail. response:', '', $message);
|
||||
$message = str_replace('Request access_token fail:', '', $message);
|
||||
$message = trim($message);
|
||||
$message = json_decode($message, true) ?: [];
|
||||
$errcode = $message['errcode'] ?? false;
|
||||
if ($errcode) {
|
||||
$message = ApiErrorCode::ERROR_WECHAT_MESSAGE[$errcode] ?? $message;
|
||||
}
|
||||
}
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析错误
|
||||
* @param \Throwable $e
|
||||
* @return array|mixed|string
|
||||
*/
|
||||
public static function getValidMessgae(\Throwable $e)
|
||||
{
|
||||
$message = '';
|
||||
if (!isset(self::MSG_CODE[$e->getCode()]) && (strstr($e->getMessage(), 'Request AccessToken fail') !== false || strstr($e->getMessage(), 'Request access_token fail') !== false)) {
|
||||
$message = str_replace('Request AccessToken fail. response:', '', $e->getMessage());
|
||||
$message = str_replace('Request access_token fail:', '', $message);
|
||||
$message = trim($message);
|
||||
$message = json_decode($message, true) ?: [];
|
||||
$errcode = $message['errcode'] ?? false;
|
||||
if ($errcode) {
|
||||
$message = self::MSG_CODE[$errcode] ?? $message;
|
||||
}
|
||||
}
|
||||
return $message ?: self::MSG_CODE[$e->getCode()] ?? $e->getMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取企业微信错误提示
|
||||
* @param int $errcode
|
||||
* @param string|null $message
|
||||
* @return string|null
|
||||
*/
|
||||
public static function getWorkMessage(int $errcode, string $message = null)
|
||||
{
|
||||
if (isset(self::WORK_ERROR_MESSAGE[$errcode])) {
|
||||
return self::WORK_ERROR_MESSAGE[$errcode];
|
||||
} else {
|
||||
return $message;
|
||||
}
|
||||
}
|
||||
}
|
||||
197
crmeb/services/wechat/Messages.php
Normal file
197
crmeb/services/wechat/Messages.php
Normal file
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace crmeb\services\wechat;
|
||||
|
||||
|
||||
use crmeb\services\wechat\message\Article;
|
||||
use crmeb\services\wechat\message\Image;
|
||||
use crmeb\services\wechat\message\Media;
|
||||
use crmeb\services\wechat\message\News;
|
||||
use crmeb\services\wechat\message\NewsItem;
|
||||
use crmeb\services\wechat\message\Text;
|
||||
use crmeb\services\wechat\message\TextCard;
|
||||
use crmeb\services\wechat\message\Transfer;
|
||||
use crmeb\services\wechat\message\Video;
|
||||
use crmeb\services\wechat\message\Voice;
|
||||
|
||||
/**
|
||||
* 消息回复模板
|
||||
* Class Messages
|
||||
* @package crmeb\services\wechat
|
||||
*/
|
||||
class Messages
|
||||
{
|
||||
/**
|
||||
* 回复文本消息
|
||||
* @param string $content 文本内容
|
||||
* @return Text
|
||||
*/
|
||||
public static function textMessage(string $content = ''): Text
|
||||
{
|
||||
$content = str_replace("\\n", "\n", $content); //返回数据处理换行符
|
||||
$content = str_replace("\\r", "\r", $content); //返回数据处理换行符
|
||||
return new Text($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复图片消息
|
||||
* @param string $mediaId 媒体资源 ID
|
||||
* @return Image
|
||||
*/
|
||||
public static function imageMessage(string $mediaId): Image
|
||||
{
|
||||
return new Image($mediaId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复视频消息
|
||||
* @param string $mediaId 媒体资源 ID
|
||||
* @param string $title 标题
|
||||
* @param string $description 描述
|
||||
* @param null $thumb_media_id 封面资源 ID
|
||||
* @return Video
|
||||
*/
|
||||
public static function videoMessage(string $mediaId, string $title = '', string $description = '...', $thumb_media_id = null): Video
|
||||
{
|
||||
return new Video($mediaId, compact('title', 'description', 'thumb_media_id'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复声音消息
|
||||
* @param string $mediaId 媒体资源 ID
|
||||
* @return Voice
|
||||
*/
|
||||
public static function voiceMessage(string $mediaId): Voice
|
||||
{
|
||||
return new Voice($mediaId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复图文消息
|
||||
* @param array|string $title
|
||||
* @param string $description
|
||||
* @param string $url
|
||||
* @param string $image
|
||||
* @return News
|
||||
*/
|
||||
public static function newsMessage(array|string $title, string $description = '...', string $url = '', string $image = ''): News
|
||||
{
|
||||
if (is_array($title)) {
|
||||
$items = [
|
||||
new NewsItem([
|
||||
'title' => $title['title'],
|
||||
'description' => $title['description'],
|
||||
'url' => $title['url'],
|
||||
'image' => $title['image']
|
||||
])
|
||||
];
|
||||
} else {
|
||||
$items = [
|
||||
new NewsItem([
|
||||
'title' => $title,
|
||||
'description' => $description,
|
||||
'url' => $url,
|
||||
'image' => $image
|
||||
])
|
||||
];
|
||||
}
|
||||
return new News($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新闻消息类型
|
||||
* @param string|array $title
|
||||
* @param string $description
|
||||
* @param string $url
|
||||
* @param string $image
|
||||
* @return News
|
||||
*/
|
||||
public static function newMessage(string|array $title, string $description = '...', string $url = '', string $image = ''): News
|
||||
{
|
||||
if (is_array($title)) {
|
||||
$items = [
|
||||
new NewsItem([
|
||||
'title' => $title['title'],
|
||||
'description' => $title['description'],
|
||||
'url' => $title['url'],
|
||||
'image' => $title['image']
|
||||
])
|
||||
];
|
||||
} else {
|
||||
$items = [
|
||||
new NewsItem([
|
||||
'title' => $title,
|
||||
'description' => $description,
|
||||
'url' => $url,
|
||||
'image' => $image
|
||||
])
|
||||
];
|
||||
}
|
||||
return new News($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复文章消息
|
||||
* @param string|array $title 标题
|
||||
* @param string $thumb_media_id 图文消息的封面图片素材id(必须是永久 media_ID)
|
||||
* @param string $source_url 图文消息的原文地址,即点击“阅读原文”后的URL
|
||||
* @param string $content 图文消息的具体内容,支持HTML标签,必须少于2万字符,小于1M,且此处会去除JS
|
||||
* @param string $author 作者
|
||||
* @param string $digest 图文消息的摘要,仅有单图文消息才有摘要,多图文此处为空
|
||||
* @param int $show_cover_pic 是否显示封面,0为false,即不显示,1为true,即显示
|
||||
* @param int $need_open_comment 是否打开评论,0不打开,1打开
|
||||
* @param int $only_fans_can_comment 是否粉丝才可评论,0所有人可评论,1粉丝才可评论
|
||||
* @return Article
|
||||
*/
|
||||
public static function articleMessage(array|string $title, string $thumb_media_id, string $source_url, string $content = '', string $author = '', string $digest = '', int $show_cover_pic = 0, int $need_open_comment = 0, int $only_fans_can_comment = 1): Article
|
||||
{
|
||||
$data = is_array($title) ? $title : compact('title', 'thumb_media_id', 'source_url', 'content', 'author', 'digest', 'show_cover_pic', 'need_open_comment', 'only_fans_can_comment');
|
||||
return new Article($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复素材消息
|
||||
* @param string $mediaId
|
||||
* @param string|null $type mpnews、 mpvideo、voice、image
|
||||
* @return Media
|
||||
*/
|
||||
public static function materialMessage(string $mediaId, string|null $type = null): Media
|
||||
{
|
||||
return new Media($mediaId, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 多客服消息转发
|
||||
* @param string|null $account
|
||||
* @return Transfer
|
||||
*/
|
||||
public static function transfer(string $account = null): Transfer
|
||||
{
|
||||
return new Transfer($account);
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息模板
|
||||
* @param string $title
|
||||
* @param string $description
|
||||
* @param string $url
|
||||
* @return TextCard
|
||||
*/
|
||||
public static function TextCardMessage(string $title, string $description, string $url): TextCard
|
||||
{
|
||||
return new TextCard([
|
||||
'title' => $title,
|
||||
'description' => $description,
|
||||
'url' => $url
|
||||
]);
|
||||
}
|
||||
}
|
||||
698
crmeb/services/wechat/MiniProgram.php
Normal file
698
crmeb/services/wechat/MiniProgram.php
Normal file
@@ -0,0 +1,698 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace crmeb\services\wechat;
|
||||
|
||||
|
||||
use crmeb\services\wechat\client\miniprogram\MediaCheckClient;
|
||||
use EasyWeChat\Kernel\Exceptions\BadRequestException;
|
||||
use EasyWeChat\Kernel\Exceptions\RuntimeException;
|
||||
use EasyWeChat\MiniApp\Application;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
use crmeb\services\wechat\config\MiniProgramConfig;
|
||||
use crmeb\services\wechat\client\miniprogram\AuthClient;
|
||||
use crmeb\services\wechat\client\miniprogram\LiveClient;
|
||||
use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
|
||||
use crmeb\services\wechat\client\miniprogram\OrderClient;
|
||||
use crmeb\services\wechat\client\official\MaterialClient;
|
||||
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
|
||||
use crmeb\services\wechat\client\miniprogram\SubscribeClient;
|
||||
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Log;
|
||||
|
||||
|
||||
/**
|
||||
* 小程序服务
|
||||
* Class MiniProgram
|
||||
* @package crmeb\services\wechat
|
||||
* @method AuthClient auth() 授权
|
||||
* @method LiveClient live() 直播
|
||||
* @method MaterialClient material() 素材
|
||||
* @method OrderClient order() 订单
|
||||
* @method SubscribeClient subscribe() 订阅消息
|
||||
* @method MediaCheckClient media_check() 敏感词
|
||||
*/
|
||||
class MiniProgram extends BaseApplication
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $name = 'miniprogram';
|
||||
|
||||
/**
|
||||
* @var MiniProgramConfig
|
||||
*/
|
||||
protected MiniProgramConfig $config;
|
||||
|
||||
/**
|
||||
* @var Application
|
||||
*/
|
||||
protected $application;
|
||||
|
||||
/**
|
||||
* MiniProgram constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->debug = DefaultConfig::value('logger');
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @return Application
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function application(): Application
|
||||
{
|
||||
if (!$this->application) {
|
||||
$this->application = new Application($this->config->all());
|
||||
$this->setHttpClient($this->application);
|
||||
$this->setRequest($this->application);
|
||||
$this->setLogger($this->application);
|
||||
$this->setCache($this->application);
|
||||
}
|
||||
return $this->application;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \think\Response
|
||||
* @throws InvalidArgumentException
|
||||
* @throws BadRequestException
|
||||
* @throws RuntimeException
|
||||
* @throws \ReflectionException
|
||||
* @throws \Throwable
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/10/7
|
||||
*/
|
||||
public static function serve(): \think\Response
|
||||
{
|
||||
$make = self::instance();
|
||||
$server = $make->application()->getServer();
|
||||
$server->with($make->pushMessageHandler);
|
||||
$response = $server->serve();
|
||||
return response($response->getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MiniProgram
|
||||
*/
|
||||
public static function instance(): static
|
||||
{
|
||||
$instance = app()->make(self::class);
|
||||
$instance->config = app()->make(MiniProgramConfig::class);
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得用户信息 根据code 获取session_key
|
||||
* @param string $code
|
||||
* @return Response|ResponseInterface
|
||||
*/
|
||||
public static function getUserInfo(string $code)
|
||||
{
|
||||
try {
|
||||
$response = self::instance()->auth()->session($code);
|
||||
|
||||
self::logger('获得用户信息 根据code 获取session_key', compact('code'), $response);
|
||||
|
||||
return $response;
|
||||
} catch (\Throwable $e) {
|
||||
throw new WechatException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密数据
|
||||
* @param string $sessionKey
|
||||
* @param string $iv
|
||||
* @param string $encryptData
|
||||
* @return array
|
||||
*/
|
||||
public static function decryptData(string $sessionKey, string $iv, string $encryptData)
|
||||
{
|
||||
$response = self::instance()->auth()->decryptData($sessionKey, $iv, $encryptData);
|
||||
|
||||
self::logger('解密数据', compact('sessionKey', 'iv', 'encryptData'), $response);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取小程序码:适用于需要的码数量极多,或仅临时使用的业务场景
|
||||
* @param string $scene
|
||||
* @param string $path
|
||||
* @param int $width
|
||||
* @return Response|string
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function appCodeUnlimit(string $scene, string $path = '', int $width = 0)
|
||||
{
|
||||
$optional = [
|
||||
'page' => $path,
|
||||
'width' => $width
|
||||
];
|
||||
if (!$optional['page']) {
|
||||
unset($optional['page']);
|
||||
}
|
||||
if (!$optional['width']) {
|
||||
unset($optional['width']);
|
||||
}
|
||||
try {
|
||||
$response = self::instance()->auth()->getUnlimit($scene, $optional)->getContent();
|
||||
$res = json_decode($response);
|
||||
if (isset($res->errcode) && $res->errcode == 40001) {
|
||||
self::instance()->application()->getAccessToken()->refresh();
|
||||
$response = self::instance()->auth()->getUnlimit($scene, $optional)->getContent();
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('获取小程序码错误:' . $e->getMessage());
|
||||
$response = [];
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取短链
|
||||
* @param string $page
|
||||
* @param string $query
|
||||
* @return Response|string|ResponseInterface
|
||||
* User: liusl
|
||||
* DateTime: 2024/11/23 下午2:54
|
||||
*/
|
||||
public static function generateUrlLink(string $page, string $query)
|
||||
{
|
||||
try {
|
||||
return self::instance()->auth()->generateUrlLink($page, $query);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('获取小程序码错误:' . $e->getMessage());
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送订阅消息
|
||||
* @param string $touser
|
||||
* @param string $templateId
|
||||
* @param array $data
|
||||
* @param string $link
|
||||
* @return Response|ResponseInterface
|
||||
* @throws InvalidArgumentException
|
||||
* @throws InvalidConfigException
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function sendSubscribeTemlate(string $touser, string $templateId, array $data, string $link = '')
|
||||
{
|
||||
$response = self::instance()->subscribe()->send([
|
||||
'template_id' => $templateId,
|
||||
'touser' => $touser,
|
||||
'page' => $link,
|
||||
'data' => $data
|
||||
]);
|
||||
|
||||
self::logger('发送订阅消息', compact('templateId', 'touser', 'link', 'data'), $response);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加订阅消息模版
|
||||
* @param string $tid
|
||||
* @param array $kidList
|
||||
* @param string $sceneDesc
|
||||
* @return mixed
|
||||
*/
|
||||
public static function addSubscribeTemplate(string $tid, array $kidList, string $sceneDesc = '')
|
||||
{
|
||||
try {
|
||||
$res = self::instance()->subscribe()->addTemplate($tid, $kidList, $sceneDesc);
|
||||
|
||||
self::logger('添加订阅消息模版', compact('tid', 'kidList', 'sceneDesc'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['priTmplId'])) {
|
||||
return $res['priTmplId'];
|
||||
} else {
|
||||
throw new WechatException($res['errmsg']);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
throw new WechatException($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除订阅消息
|
||||
* @param string $templateId
|
||||
* @return Response|ResponseInterface
|
||||
*/
|
||||
public static function delSubscribeTemplate(string $templateId)
|
||||
{
|
||||
try {
|
||||
$response = self::instance()->subscribe()->deleteTemplate($templateId);
|
||||
|
||||
self::logger('删除订阅消息', compact('templateId'), $response);
|
||||
|
||||
return $response;
|
||||
} catch (\Throwable $e) {
|
||||
throw new WechatException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模版标题的关键词列表
|
||||
* @param string $tid
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getSubscribeTemplateKeyWords(string $tid)
|
||||
{
|
||||
try {
|
||||
$res = self::instance()->subscribe()->getTemplateKeywords($tid);
|
||||
|
||||
self::logger('获取模版标题的关键词列表', compact('tid'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['data'])) {
|
||||
return $res['data'];
|
||||
} else {
|
||||
throw new WechatException($res['errmsg']);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
throw new WechatException($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取直播列表
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @return array
|
||||
*/
|
||||
public static function getLiveInfo(int $page = 1, int $limit = 10)
|
||||
{
|
||||
try {
|
||||
$res = self::instance()->live()->getRooms($page, $limit);
|
||||
|
||||
// self::logger('获取直播列表', compact('page', 'limit'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['room_info']) && $res['room_info']) {
|
||||
return $res['room_info'];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取直播回放
|
||||
* @param int $room_id
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getLivePlayback(int $room_id, int $page = 1, int $limit = 10)
|
||||
{
|
||||
try {
|
||||
$res = self::instance()->live()->getPlaybacks($room_id, $page, $limit);
|
||||
|
||||
self::logger('获取直播回放', compact('room_id', 'page', 'limit'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['live_replay'])) {
|
||||
return $res['live_replay'];
|
||||
} else {
|
||||
throw new WechatException($res['errmsg']);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
throw new WechatException(ErrorMessage::getValidMessgae($e));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建直播间
|
||||
* @param array $data
|
||||
* @return mixed
|
||||
*/
|
||||
public static function createLiveRoom(array $data)
|
||||
{
|
||||
try {
|
||||
$res = self::instance()->live()->createRoom($data);
|
||||
|
||||
self::logger('创建直播间', compact('data'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['roomId'])) {
|
||||
return $res;
|
||||
} else {
|
||||
throw new WechatException($res['errmsg']);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
throw new WechatException(ErrorMessage::getValidMessgae($e));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直播间添加商品
|
||||
* @param int $roomId
|
||||
* @param $ids
|
||||
* @return bool
|
||||
*/
|
||||
public static function roomAddGoods(int $roomId, $ids)
|
||||
{
|
||||
try {
|
||||
$res = self::instance()->live()->roomAddGoods($roomId, $ids);
|
||||
|
||||
self::logger('直播间添加商品', compact('roomId', 'ids'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0) {
|
||||
return true;
|
||||
} else {
|
||||
throw new WechatException($res['errmsg']);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
throw new WechatException(ErrorMessage::getValidMessgae($e));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商品列表
|
||||
* @param int $status
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getGoodsList(int $status = 2, int $page = 1, int $limit = 10)
|
||||
{
|
||||
try {
|
||||
$res = self::instance()->live()->getGoodsList($status, $page, $limit);
|
||||
|
||||
self::logger('获取商品列表', compact('status', 'page', 'limit'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['goods'])) {
|
||||
return $res['goods'];
|
||||
} else {
|
||||
throw new WechatException($res['errmsg']);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
throw new WechatException(ErrorMessage::getValidMessgae($e));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商品详情
|
||||
* @param $goods_ids
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getGooodsInfo($goods_ids)
|
||||
{
|
||||
try {
|
||||
$res = self::instance()->live()->getGooodsInfo($goods_ids);
|
||||
|
||||
self::logger('获取商品详情', compact('goods_ids'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['goods'])) {
|
||||
return $res['goods'];
|
||||
} else {
|
||||
throw new WechatException($res['errmsg']);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
throw new WechatException(ErrorMessage::getValidMessgae($e));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加商品
|
||||
* @param string $coverImgUrl
|
||||
* @param string $name
|
||||
* @param int $priceType
|
||||
* @param string $url
|
||||
* @param $price
|
||||
* @param string $price2
|
||||
* @return mixed
|
||||
*/
|
||||
public static function addGoods(string $coverImgUrl, string $name, int $priceType, string $url, $price, $price2 = '')
|
||||
{
|
||||
try {
|
||||
$res = self::instance()->live()->addGoods($coverImgUrl, $name, $priceType, $url, $price, $price2);
|
||||
|
||||
self::logger('添加商品', compact('coverImgUrl', 'name', 'priceType', 'url', 'price', 'price2'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['goodsId'])) {
|
||||
return $res;
|
||||
} else {
|
||||
throw new WechatException($res['errmsg']);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
throw new WechatException(ErrorMessage::getValidMessgae($e));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 商品撤回审核
|
||||
* @param int $goodsId
|
||||
* @param $auditId
|
||||
* @return bool
|
||||
*/
|
||||
public static function resetauditGoods(int $goodsId, $auditId)
|
||||
{
|
||||
try {
|
||||
$res = self::instance()->live()->resetauditGoods($goodsId, $auditId);
|
||||
|
||||
self::logger('商品撤回审核', compact('goodsId', 'auditId'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0) {
|
||||
return true;
|
||||
} else {
|
||||
throw new WechatException($res['errmsg']);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
throw new WechatException(ErrorMessage::getValidMessgae($e));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 商品重新提交审核
|
||||
* @param int $goodsId
|
||||
* @return mixed
|
||||
*/
|
||||
public static function auditGoods(int $goodsId)
|
||||
{
|
||||
try {
|
||||
$res = self::instance()->live()->auditGoods($goodsId);
|
||||
|
||||
self::logger('商品重新提交审核', compact('goodsId'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['auditId'])) {
|
||||
return $res['auditId'];
|
||||
} else {
|
||||
throw new WechatException($res['errmsg']);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
throw new WechatException(ErrorMessage::getValidMessgae($e));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除商品
|
||||
* @param int $goodsId
|
||||
* @return bool
|
||||
*/
|
||||
public static function deleteGoods(int $goodsId)
|
||||
{
|
||||
try {
|
||||
$res = self::instance()->live()->deleteGoods($goodsId);
|
||||
|
||||
self::logger('删除商品', compact('goodsId'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0) {
|
||||
return true;
|
||||
} else {
|
||||
throw new WechatException($res['errmsg']);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
throw new WechatException(ErrorMessage::getValidMessgae($e));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新商品
|
||||
* @param int $goodsId
|
||||
* @param string $coverImgUrl
|
||||
* @param string $name
|
||||
* @param int $priceType
|
||||
* @param string $url
|
||||
* @param $price
|
||||
* @param string $price2
|
||||
* @return bool
|
||||
*/
|
||||
public static function updateGoods(int $goodsId, string $coverImgUrl, string $name, int $priceType, string $url, $price, $price2 = '')
|
||||
{
|
||||
try {
|
||||
$res = self::instance()->live()->updateGoods($goodsId, $coverImgUrl, $name, $priceType, $url, $price, $price2);
|
||||
|
||||
self::logger('更新商品', compact('goodsId', 'coverImgUrl', 'name', 'priceType', 'url', 'price', 'price2'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0) {
|
||||
return true;
|
||||
} else {
|
||||
throw new WechatException($res['errmsg']);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
throw new WechatException(ErrorMessage::getValidMessgae($e));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取成员列表
|
||||
* @param int $role
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @param string $keyword
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getRoleList(int $role = 2, int $page = 0, int $limit = 30, string $keyword = '')
|
||||
{
|
||||
try {
|
||||
$res = self::instance()->live()->getRoleList($role, $page, $limit, $keyword);
|
||||
|
||||
self::logger('获取成员列表', compact('role', 'page', 'limit', 'keyword'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['list'])) {
|
||||
return $res['list'];
|
||||
} else {
|
||||
throw new WechatException($res['errmsg']);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
throw new WechatException(ErrorMessage::getValidMessgae($e));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序临时素材上传
|
||||
* @param string $path
|
||||
* @param string $type
|
||||
* @return WechatResponse
|
||||
* @throws TransportExceptionInterface
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws RedirectionExceptionInterface
|
||||
* @throws ServerExceptionInterface
|
||||
*/
|
||||
public static function temporaryUpload(string $path, string $type = 'image')
|
||||
{
|
||||
$response = self::instance()->material()->upload($type, $path);
|
||||
|
||||
self::logger('小程序-临时素材上传', compact('path', 'type'), $response);
|
||||
|
||||
|
||||
return new WechatResponse($response);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 上传订单
|
||||
* @param string $out_trade_no 订单号(商城订单好)
|
||||
* @param int $logistics_type 物流模式,发货方式枚举值:1、实体物流配送采用快递公司进行实体物流配送形式 2、同城配送 3、虚拟商品,虚拟商品,例如话费充值,点卡等,无实体配送形式 4、用户自提
|
||||
* @param array $shipping_list 物流信息列表,发货物流单列表,支持统一发货(单个物流单)和分拆发货(多个物流单)两种模式,多重性: [1, 10]
|
||||
* @param string $payer_openid 支付者,支付者信息
|
||||
* @param string $path
|
||||
* @param int $delivery_mode 发货模式,发货模式枚举值:1、UNIFIED_DELIVERY(统一发货)2、SPLIT_DELIVERY(分拆发货) 示例值: UNIFIED_DELIVERY
|
||||
* @param bool $is_all_delivered 分拆发货模式时必填,用于标识分拆发货模式下是否已全部发货完成,只有全部发货完成的情况下才会向用户推送发货完成通知。示例值: true/false
|
||||
* @return array
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function shippingByTradeNo(string $out_trade_no, int $logistics_type, array $shipping_list, string $payer_openid, string $path, int $delivery_mode = 1, bool $is_all_delivered = true)
|
||||
{
|
||||
return self::instance()->order()->shippingByTradeNo($out_trade_no, $logistics_type, $shipping_list, $payer_openid, $path, $delivery_mode, $is_all_delivered);
|
||||
}
|
||||
|
||||
/**
|
||||
* 合单
|
||||
* @param string $out_trade_no
|
||||
* @param int $logistics_type
|
||||
* @param array $sub_orders
|
||||
* @param string $payer_openid
|
||||
* @param int $delivery_mode
|
||||
* @param bool $is_all_delivered
|
||||
* @return array
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function combinedShippingByTradeNo(string $out_trade_no, int $logistics_type, array $sub_orders, string $payer_openid, int $delivery_mode = 2, bool $is_all_delivered = false)
|
||||
{
|
||||
return self::instance()->order()->combinedShippingByTradeNo($out_trade_no, $logistics_type, $sub_orders, $payer_openid, $delivery_mode, $is_all_delivered);
|
||||
}
|
||||
|
||||
/**
|
||||
* 签收通知
|
||||
* @param string $merchant_trade_no
|
||||
* @param string $received_time
|
||||
* @return array
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function notifyConfirmByTradeNo(string $merchant_trade_no, string $received_time)
|
||||
{
|
||||
return self::instance()->order()->notifyConfirmByTradeNo($merchant_trade_no, $received_time);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否开通
|
||||
* @return bool
|
||||
* @throws HttpException
|
||||
* @date 2023/05/17
|
||||
* @author yyw
|
||||
*/
|
||||
public static function isManaged()
|
||||
{
|
||||
return self::instance()->order()->checkManaged();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置小修跳转路径
|
||||
* @param $path
|
||||
* @return array
|
||||
* @throws HttpException|TransportExceptionInterface
|
||||
*/
|
||||
public static function setMesJumpPathAndCheck($path)
|
||||
{
|
||||
return self::instance()->order()->setMesJumpPathAndCheck($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 敏感词验证
|
||||
* @param string $content
|
||||
* @param int $scene
|
||||
* @param string $openId
|
||||
* @return true
|
||||
* User: liusl
|
||||
* DateTime: 2024/11/26 下午4:29
|
||||
*/
|
||||
public static function msgSecCheck(string $content)
|
||||
{
|
||||
// try{
|
||||
//scene 场景枚举值(1 资料;2 评论;3 论坛;4 社交日志)
|
||||
$res = self::instance()->media_check()->msgSecCheck($content);
|
||||
var_dump($res);
|
||||
if($res->result['label'] == 100) {
|
||||
return true;
|
||||
} else {
|
||||
throw new ValidateException('内容包含:【'.$res->result['label'].'】无法发布');
|
||||
}
|
||||
// } catch (\Exception $e) {
|
||||
// Log::error('敏感词检查:' . $e->getMessage());
|
||||
// throw new WechatException(ErrorMessage::getMessage($e->getMessage()));
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
713
crmeb/services/wechat/OfficialAccount.php
Normal file
713
crmeb/services/wechat/OfficialAccount.php
Normal file
@@ -0,0 +1,713 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace crmeb\services\wechat;
|
||||
|
||||
use crmeb\services\wechat\client\official\CardClient;
|
||||
use crmeb\services\wechat\client\official\MaterialClient;
|
||||
use crmeb\services\wechat\client\official\MediaClient;
|
||||
use crmeb\services\wechat\client\official\QrCodeClient;
|
||||
use crmeb\services\wechat\client\official\StaffClient;
|
||||
use crmeb\services\wechat\client\official\TemplateClient;
|
||||
use crmeb\services\wechat\config\OfficialAccountConfig;
|
||||
use crmeb\services\wechat\config\OpenAppConfig;
|
||||
use crmeb\services\wechat\config\OpenWebConfig;
|
||||
use crmeb\services\wechat\client\official\UserClient;
|
||||
use crmeb\services\wechat\message\MessageInterface;
|
||||
use EasyWeChat\Kernel\Exceptions\BadRequestException;
|
||||
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
|
||||
use EasyWeChat\Kernel\Exceptions\RuntimeException;
|
||||
use EasyWeChat\OfficialAccount\Application;
|
||||
use ReflectionException;
|
||||
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 公众号服务
|
||||
* Class OfficialAccount
|
||||
* @package crmeb\services\wechat
|
||||
* @method UserClient user() 用户
|
||||
* @method CardClient card() 卡卷
|
||||
* @method TemplateClient template() 模板消息
|
||||
* @method MaterialClient material() 永久素材管理
|
||||
* @method MediaClient media() 临时素材管理
|
||||
* @method StaffClient staff() 消息
|
||||
* @method QrCodeClient qrCode() 二维码
|
||||
*/
|
||||
class OfficialAccount extends BaseApplication
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $name = 'official';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $application;
|
||||
|
||||
/**
|
||||
* OfficialAccount constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->debug = DefaultConfig::value('logger');
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @return Application
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function application(): Application
|
||||
{
|
||||
$request = request();
|
||||
|
||||
$config = match ($accessEnd = $this->getAuthAccessEnd($request)) {
|
||||
self::APP => app()->make(OpenAppConfig::class)->all(),
|
||||
self::PC => app()->make(OpenWebConfig::class)->all(),
|
||||
default => app()->make(OfficialAccountConfig::class)->all(),
|
||||
};
|
||||
|
||||
if (!isset($this->application[$accessEnd])) {
|
||||
$this->application[$accessEnd] = new Application($config);
|
||||
$this->setHttpClient($this->application[$accessEnd]);
|
||||
$this->setRequest($this->application[$accessEnd]);
|
||||
$this->setLogger($this->application[$accessEnd]);
|
||||
$this->setCache($this->application[$accessEnd]);
|
||||
}
|
||||
return $this->application[$accessEnd];
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务端
|
||||
* @return \think\Response
|
||||
* @throws BadRequestException
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ReflectionException
|
||||
* @throws RuntimeException
|
||||
* @throws Throwable
|
||||
*/
|
||||
public static function serve(): \think\Response
|
||||
{
|
||||
$make = self::instance();
|
||||
$server = $make->application()->getServer();
|
||||
$server->with($make->pushMessageHandler);
|
||||
$response = $server->serve();
|
||||
return response($response->getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OfficialAccount
|
||||
*/
|
||||
public static function instance(): static
|
||||
{
|
||||
return app()->make(self::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取js的SDK
|
||||
* @param string $url
|
||||
* @return array
|
||||
*/
|
||||
public static function jsSdk(string $url = ''): array
|
||||
{
|
||||
$apiList = ['openAddress', 'updateTimelineShareData', 'updateAppMessageShareData',
|
||||
'onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ',
|
||||
'onMenuShareWeibo', 'onMenuShareQZone', 'startRecord', 'stopRecord',
|
||||
'onVoiceRecordEnd', 'playVoice', 'pauseVoice', 'stopVoice',
|
||||
'onVoicePlayEnd', 'uploadVoice', 'downloadVoice', 'chooseImage',
|
||||
'previewImage', 'uploadImage', 'downloadImage', 'translateVoice',
|
||||
'getNetworkType', 'openLocation', 'getLocation', 'hideOptionMenu',
|
||||
'showOptionMenu', 'hideMenuItems', 'showMenuItems', 'hideAllNonBaseMenuItem',
|
||||
'showAllNonBaseMenuItem', 'closeWindow', 'scanQRCode', 'chooseWXPay',
|
||||
'openProductSpecificView', 'addCard', 'chooseCard', 'openCard','requestMerchantTransfer'];
|
||||
|
||||
try {
|
||||
return self::retryWithTokenRefresh(function() use ($url, $apiList) {
|
||||
return self::instance()->application()->getUtils()->buildJsSdkConfig(
|
||||
url: $url,
|
||||
jsApiList: $apiList,
|
||||
openTagList: [],
|
||||
debug: false
|
||||
);
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
self::error($e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信用户信息
|
||||
* @param $openid
|
||||
* @return array|Response|mixed|ResponseInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public static function getUserInfo($openid)
|
||||
{
|
||||
try {
|
||||
$userInfo = self::retryWithTokenRefresh(function() use ($openid) {
|
||||
$userService = self::instance()->user();
|
||||
|
||||
if (is_array($openid)) {
|
||||
$res = $userService->select($openid);
|
||||
if (isset($res['user_info_list'])) {
|
||||
return $res['user_info_list'];
|
||||
} else {
|
||||
throw new WechatException($res['errmsg'] ?? '获取微信粉丝信息失败');
|
||||
}
|
||||
} else {
|
||||
$userInfo = $userService->get($openid);
|
||||
return is_object($userInfo) ? $userInfo->toArray() : $userInfo;
|
||||
}
|
||||
});
|
||||
} catch (Throwable $e) {
|
||||
throw new WechatException(ErrorMessage::getMessage($e->getMessage()));
|
||||
}
|
||||
|
||||
self::logger('获取微信用户信息', compact('openid'), $userInfo);
|
||||
|
||||
return $userInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员卡列表
|
||||
* @param int $offset
|
||||
* @param int $count
|
||||
* @param string $statusList
|
||||
* @return mixed
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function getCardList(int $offset = 0, int $count = 10, string $statusList = 'CARD_STATUS_VERIFY_OK')
|
||||
{
|
||||
try {
|
||||
$res = self::instance()->card()->list($offset, $count, $statusList);
|
||||
|
||||
self::logger('获取会员卡列表', compact('offset', 'count', 'statusList'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['card_id_list'])) {
|
||||
return $res['card_id_list'];
|
||||
} else {
|
||||
throw new WechatException($res['errmsg']);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw new WechatException(ErrorMessage::getMessage($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取卡券颜色
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function getCardColors()
|
||||
{
|
||||
try {
|
||||
|
||||
$response = self::instance()->card()->colors();
|
||||
|
||||
self::logger('获取卡券颜色', [], $response);
|
||||
|
||||
return $response;
|
||||
} catch (\Exception $e) {
|
||||
throw new WechatException(ErrorMessage::getMessage($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建卡券
|
||||
* @param string $cardType
|
||||
* @param array $baseInfo
|
||||
* @param array $especial
|
||||
* @param array $advancedInfo
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function createCard(string $cardType, array $baseInfo, array $especial = [], array $advancedInfo = []): ResponseInterface|Response
|
||||
{
|
||||
try {
|
||||
$res = self::instance()->card()->create($cardType, array_merge(['base_info' => $baseInfo, 'advanced_info' => $advancedInfo], $especial));
|
||||
|
||||
self::logger('创建卡券', compact('cardType', 'baseInfo', 'especial', 'advancedInfo'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['card_id'])) {
|
||||
return $res;
|
||||
} else {
|
||||
throw new WechatException($res['errmsg']);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw new WechatException(ErrorMessage::getMessage($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $cardId
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public static function getCard(int $cardId): ResponseInterface|Response
|
||||
{
|
||||
try {
|
||||
$res = self::retryWithTokenRefresh(function() use ($cardId) {
|
||||
return self::instance()->card()->get($cardId);
|
||||
});
|
||||
|
||||
self::logger('获取卡券信息', compact('cardId'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0) {
|
||||
return $res;
|
||||
} else {
|
||||
throw new WechatException($res['errmsg']);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw new WechatException(ErrorMessage::getMessage($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改卡券
|
||||
* @param string $cardId
|
||||
* @param string $type
|
||||
* @param array $baseInfo
|
||||
* @param array $especial
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function updateCard(string $cardId, string $type, array $baseInfo = [], array $especial = []): ResponseInterface|Response
|
||||
{
|
||||
try {
|
||||
$res = self::retryWithTokenRefresh(function() use ($cardId, $type, $baseInfo, $especial) {
|
||||
return self::instance()->card()->update($cardId, $type, array_merge(['base_info' => $baseInfo], $especial));
|
||||
});
|
||||
|
||||
self::logger('修改卡券', compact('cardId', 'type', 'baseInfo', 'especial'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0) {
|
||||
return $res;
|
||||
} else {
|
||||
throw new WechatException($res['errmsg']);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw new WechatException(ErrorMessage::getMessage($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取领卡券二维码
|
||||
* @param string $card_id 卡券ID
|
||||
* @param string $outer_id 生成二维码标识参数
|
||||
* @param string $code 自动移code
|
||||
* @param int $expire_time
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function getCardQRCode(string $card_id, string $outer_id, string $code = '', int $expire_time = 1800): ResponseInterface|Response
|
||||
{
|
||||
$data = [
|
||||
'action_name' => 'QR_CARD',
|
||||
'expire_seconds' => $expire_time,
|
||||
'action_info' => [
|
||||
'card' => [
|
||||
'card_id' => $card_id,
|
||||
'is_unique_code' => false,
|
||||
'outer_id' => $outer_id
|
||||
]
|
||||
]
|
||||
];
|
||||
if ($code) $data['action_info']['card']['code'] = $code;
|
||||
try {
|
||||
$res = self::instance()->card()->createQrCode($data);
|
||||
|
||||
self::logger('获取领卡券二维码', compact('data'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['url'])) {
|
||||
return $res;
|
||||
} else {
|
||||
throw new WechatException($res['errmsg']);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw new WechatException(ErrorMessage::getMessage($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置会员卡激活字段
|
||||
* @param string $cardId
|
||||
* @param array $requiredForm
|
||||
* @param array $optionalForm
|
||||
* @return mixed
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function cardActivateUserForm(string $cardId, array $requiredForm = [], array $optionalForm = []): mixed
|
||||
{
|
||||
try {
|
||||
$res = self::instance()->card()->setActivationForm($cardId, array_merge($requiredForm, $optionalForm));
|
||||
|
||||
self::logger('设置会员卡激活字段', compact('cardId', 'requiredForm', 'optionalForm'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0) {
|
||||
return $res;
|
||||
} else {
|
||||
throw new WechatException($res['errmsg']);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw new WechatException(ErrorMessage::getMessage($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 会员卡激活
|
||||
* @param string $card_id
|
||||
* @param string $code
|
||||
* @param string $membership_number
|
||||
* @return mixed
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function cardActivate(string $card_id, string $code, string $membership_number = ''): mixed
|
||||
{
|
||||
$info = [
|
||||
'membership_number' => $membership_number ? $membership_number : $code, //会员卡编号,由开发者填入,作为序列号显示在用户的卡包里。可与Code码保持等值。
|
||||
'code' => $code, //创建会员卡时获取的初始code。
|
||||
'activate_begin_time' => '', //激活后的有效起始时间。若不填写默认以创建时的 data_info 为准。Unix时间戳格式
|
||||
'activate_end_time' => '', //激活后的有效截至时间。若不填写默认以创建时的 data_info 为准。Unix时间戳格式。
|
||||
'init_bonus' => '0', //初始积分,不填为0。
|
||||
'init_balance' => '0', //初始余额,不填为0。
|
||||
];
|
||||
try {
|
||||
$res = self::instance()->card()->activate($info);
|
||||
|
||||
self::logger('会员卡激活', compact('info'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['url'])) {
|
||||
return $res;
|
||||
} else {
|
||||
throw new WechatException($res['errmsg']);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw new WechatException(ErrorMessage::getMessage($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员信息
|
||||
* @param string $cardId
|
||||
* @param string $code
|
||||
* @return mixed
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function getMemberCardUser(string $cardId, string $code): mixed
|
||||
{
|
||||
try {
|
||||
$res = self::instance()->card()->getUser($cardId, $code);
|
||||
|
||||
self::logger('获取会员信息', compact('cardId', 'code'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['user_info'])) {
|
||||
return $res;
|
||||
} else {
|
||||
throw new WechatException($res['errmsg']);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw new WechatException(ErrorMessage::getMessage($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新会员信息
|
||||
* @param array $data
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function updateMemberCardUser(array $data): ResponseInterface|Response
|
||||
{
|
||||
try {
|
||||
$res = self::instance()->card()->updateUser($data);
|
||||
|
||||
self::logger('更新会员信息', compact('data'), $res);
|
||||
|
||||
if (isset($res['errcode']) && $res['errcode'] == 0 && isset($res['user_info'])) {
|
||||
return $res;
|
||||
} else {
|
||||
throw new WechatException($res['errmsg']);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw new WechatException(ErrorMessage::getMessage($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置模版消息行业
|
||||
* @param int $industryOne
|
||||
* @param int $industryTwo
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public static function setIndustry(int $industryOne, int $industryTwo): ResponseInterface|Response
|
||||
{
|
||||
$response = self::instance()->template()->setIndustry($industryOne, $industryTwo);
|
||||
|
||||
self::logger('设置模版消息行业', compact('industryOne', 'industryTwo'), $response);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得添加模版ID
|
||||
* @param $templateIdShort
|
||||
* @param $keywordList
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function addTemplateId($templateIdShort, $keywordList): ResponseInterface|Response
|
||||
{
|
||||
try {
|
||||
$response = self::instance()->template()->addTemplate($templateIdShort, $keywordList);
|
||||
|
||||
self::logger('获得添加模版ID', compact('templateIdShort'), $response);
|
||||
|
||||
return $response;
|
||||
} catch (\Exception $e) {
|
||||
throw new WechatException(ErrorMessage::getMessage($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模板列表
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function getPrivateTemplates(): ResponseInterface|Response
|
||||
{
|
||||
try {
|
||||
$response = self::instance()->template()->getPrivateTemplates();
|
||||
|
||||
self::logger('获取模板列表', [], $response);
|
||||
|
||||
return $response;
|
||||
} catch (\Exception $e) {
|
||||
throw new WechatException(ErrorMessage::getMessage($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据模版ID删除模版
|
||||
* @param string $templateId
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function deleleTemplate(string $templateId): ResponseInterface|Response
|
||||
{
|
||||
try {
|
||||
return self::instance()->template()->deletePrivateTemplate($templateId);
|
||||
} catch (\Exception $e) {
|
||||
throw new WechatException(ErrorMessage::getMessage($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取行业
|
||||
* @return Response|ResponseInterface
|
||||
*/
|
||||
public static function getIndustry(): ResponseInterface|Response
|
||||
{
|
||||
try {
|
||||
$response = self::instance()->template()->getIndustry();
|
||||
|
||||
self::logger('获取行业', [], $response);
|
||||
|
||||
return $response;
|
||||
} catch (Throwable $e) {
|
||||
throw new WechatException(ErrorMessage::getMessage($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送模板消息
|
||||
* @param string $openid
|
||||
* @param string $templateId
|
||||
* @param array $data
|
||||
* @param string|null $url
|
||||
* @param string|null $defaultColor
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function sendTemplate(string $openid, string $templateId, array $data, string $url = null, string $defaultColor = null): ResponseInterface|Response
|
||||
{
|
||||
$response = self::instance()->template()->send([
|
||||
'touser' => $openid,
|
||||
'template_id' => $templateId,
|
||||
'data' => $data,
|
||||
'url' => $url
|
||||
]);
|
||||
|
||||
self::logger('发送模板消息', compact('openid', 'templateId', 'data', 'url'), $response);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 静默授权-使用code获取用户授权信息
|
||||
* @param string|null $code
|
||||
* @return array
|
||||
*/
|
||||
public static function tokenFromCode(string $code = null): array
|
||||
{
|
||||
$code = $code ?: request()->param('code');
|
||||
if (!$code) {
|
||||
throw new WechatException('无效CODE');
|
||||
}
|
||||
|
||||
try {
|
||||
$response = self::instance()->application()->getOauth()->userFromCode($code);
|
||||
|
||||
self::logger('静默授权-使用code获取用户授权信息', compact('code'), $response);
|
||||
|
||||
return $response->getTokenResponse();
|
||||
} catch (Throwable $e) {
|
||||
throw new WechatException('授权失败' . $e->getMessage() . 'line' . $e->getLine());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用code获取用户授权信息
|
||||
* @param string|null $code
|
||||
* @return array
|
||||
*/
|
||||
public static function userFromCode(string $code = null): array
|
||||
{
|
||||
$code = $code ?: request()->param('code');
|
||||
if (!$code) {
|
||||
throw new WechatException('无效CODE');
|
||||
}
|
||||
try {
|
||||
$response = self::instance()->application()->getOauth()->userFromCode($code);
|
||||
|
||||
self::logger('使用code获取用户授权信息', compact('code'), $response);
|
||||
|
||||
return $response->getRaw();
|
||||
} catch (Throwable $e) {
|
||||
throw new WechatException('授权失败' . $e->getMessage() . 'line' . $e->getLine());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 临时素材上传
|
||||
* @param string $path
|
||||
* @return WechatResponse
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws RedirectionExceptionInterface
|
||||
* @throws ServerExceptionInterface
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function uploadImage(string $path): WechatResponse
|
||||
{
|
||||
$response = self::instance()->media()->uploadImage($path);
|
||||
|
||||
self::logger('素材管理-上传附件', compact('path'), $response);
|
||||
|
||||
return new WechatResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 永久素材上传
|
||||
* @param string $path
|
||||
* @param string $type
|
||||
* @return WechatResponse
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws RedirectionExceptionInterface
|
||||
* @throws ServerExceptionInterface
|
||||
*/
|
||||
public static function temporaryUpload(string $path, string $type = 'image'): WechatResponse
|
||||
{
|
||||
$response = self::retryWithTokenRefresh(function() use ($path, $type) {
|
||||
return self::instance()->material()->upload($type, $path);
|
||||
});
|
||||
|
||||
self::logger('临时素材上传', compact('path', 'type'), $response);
|
||||
|
||||
return new WechatResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息回执
|
||||
* @param MessageInterface|string $message
|
||||
* @param string $to
|
||||
* @param string $account
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public static function staffSend(MessageInterface|string $message, string $to, string $account = '')
|
||||
{
|
||||
return self::retryWithTokenRefresh(function() use ($message, $to, $account) {
|
||||
return self::instance()->staff()->send($message, $to, $account);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用的access_token刷新重试方法
|
||||
* @param callable $callback 需要重试的回调函数
|
||||
* @param int $maxRetries 最大重试次数
|
||||
* @return mixed
|
||||
* @throws WechatException
|
||||
*/
|
||||
protected static function retryWithTokenRefresh(callable $callback, int $maxRetries = 1)
|
||||
{
|
||||
$attempts = 0;
|
||||
|
||||
while ($attempts <= $maxRetries) {
|
||||
try {
|
||||
return $callback();
|
||||
} catch (Throwable $e) {
|
||||
$attempts++;
|
||||
|
||||
// 检查是否是access_token过期错误
|
||||
$errorMessage = $e->getMessage();
|
||||
$isTokenError = strpos($errorMessage, 'invalid credential') !== false ||
|
||||
strpos($errorMessage, 'access_token is invalid') !== false ||
|
||||
(method_exists($e, 'getCode') && $e->getCode() == 40001);
|
||||
|
||||
// 如果是token错误且还有重试机会,则刷新token并重试
|
||||
if ($isTokenError && $attempts <= $maxRetries) {
|
||||
try {
|
||||
self::instance()->application()->getAccessToken()->refresh();
|
||||
continue; // 继续下一次循环重试
|
||||
} catch (Throwable $refreshException) {
|
||||
// 刷新token失败,抛出原始异常
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
// 不是token错误或已达到最大重试次数,抛出异常
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
throw new WechatException('重试次数已达上限');
|
||||
}
|
||||
}
|
||||
834
crmeb/services/wechat/Payment.php
Normal file
834
crmeb/services/wechat/Payment.php
Normal file
@@ -0,0 +1,834 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace crmeb\services\wechat;
|
||||
|
||||
use app\services\kefu\UserServices;
|
||||
use app\services\wechat\WechatUserServices;
|
||||
use crmeb\services\wechat\util\XML;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
use think\exception\ValidateException;
|
||||
use Throwable;
|
||||
use think\Response;
|
||||
use ReflectionException;
|
||||
use think\facade\Event;
|
||||
use EasyWeChat\Pay\Application;
|
||||
use crmeb\exceptions\PayException;
|
||||
use crmeb\services\wechat\v3pay\PayClient;
|
||||
use crmeb\services\wechat\config\OpenAppConfig;
|
||||
use crmeb\services\wechat\config\OpenWebConfig;
|
||||
use crmeb\services\wechat\config\PaymentConfig;
|
||||
use crmeb\services\wechat\config\V3PaymentConfig;
|
||||
use crmeb\services\wechat\client\pay\OrderClient;
|
||||
use EasyWeChat\Kernel\Exceptions\RuntimeException;
|
||||
use crmeb\services\wechat\config\MiniProgramConfig;
|
||||
use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
|
||||
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
|
||||
|
||||
/**
|
||||
* 微信支付
|
||||
* Class Payment
|
||||
* @package crmeb\services\wechat
|
||||
*/
|
||||
class Payment extends BaseApplication
|
||||
{
|
||||
|
||||
const BASE_PAY_URL = 'https://api.mch.weixin.qq.com';
|
||||
|
||||
/**
|
||||
* @var PaymentConfig
|
||||
*/
|
||||
protected PaymentConfig $config;
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
protected V3PaymentConfig $v3Config;
|
||||
|
||||
/**
|
||||
* @var PayClient
|
||||
*/
|
||||
protected PayClient $payClient;
|
||||
|
||||
/**
|
||||
* 是否v3支付
|
||||
* @var bool
|
||||
*/
|
||||
public bool $isV3PAy = true;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $application = [];
|
||||
|
||||
/**
|
||||
* Payment constructor.
|
||||
* @param PaymentConfig $config
|
||||
* @param V3PaymentConfig $v3Config
|
||||
* @param PayClient $payClient
|
||||
*/
|
||||
public function __construct(PaymentConfig $config, V3PaymentConfig $v3Config, PayClient $payClient)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->v3Config = $v3Config;
|
||||
$this->payClient = $payClient;
|
||||
$this->isV3PAy = $this->v3Config->isV3PAy;
|
||||
$this->debug = !!DefaultConfig::value('logger');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Payment
|
||||
*/
|
||||
public static function instance(): static
|
||||
{
|
||||
return app()->make(static::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Application
|
||||
* @throws InvalidArgumentException
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2022/10/11
|
||||
*/
|
||||
public function application(): Application
|
||||
{
|
||||
$request = request();
|
||||
$config = $this->config->all();
|
||||
switch ($accessEnd = $this->getAuthAccessEnd($request)) {
|
||||
case self::APP:
|
||||
/** @var OpenAppConfig $make */
|
||||
$make = app()->make(OpenAppConfig::class);
|
||||
$config['app_id'] = $make->appId;
|
||||
$config['notify_url'] = trim($make->getConfig(DefaultConfig::COMMENT_URL)) . DefaultConfig::value('app.notifyUrl');
|
||||
break;
|
||||
case self::PC:
|
||||
/** @var OpenWebConfig $make */
|
||||
$make = app()->make(OpenWebConfig::class);
|
||||
$config['app_id'] = $make->appId;
|
||||
break;
|
||||
case self::MINI:
|
||||
/** @var MiniProgramConfig $make */
|
||||
$make = app()->make(MiniProgramConfig::class);
|
||||
$config['app_id'] = $make->appId;
|
||||
$config['notify_url'] = trim($make->getConfig(DefaultConfig::COMMENT_URL)) . DefaultConfig::value('mini.notifyUrl');
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isset($this->application[$accessEnd])) {
|
||||
$this->application[$accessEnd] = new Application($config);
|
||||
$this->setHttpClient($this->application[$accessEnd], self::BASE_PAY_URL);
|
||||
$this->setRequest($this->application[$accessEnd]);
|
||||
}
|
||||
|
||||
return $this->application[$accessEnd];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 发起订单支付接口入口
|
||||
* @return OrderClient
|
||||
* @throws InvalidArgumentException
|
||||
* @throws InvalidConfigException
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/10/10
|
||||
*/
|
||||
public function order()
|
||||
{
|
||||
$api = $this->application()->getClient();
|
||||
$config = $this->application()->getConfig();
|
||||
return new OrderClient($api, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 付款码支付
|
||||
* @param string $authCode
|
||||
* @param string $outTradeNo
|
||||
* @param string $totalFee
|
||||
* @param string $attach
|
||||
* @param string $body
|
||||
* @param string $detail
|
||||
* @return array
|
||||
* @throws InvalidArgumentException
|
||||
* @throws InvalidConfigException
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function microPay(string $authCode, string $outTradeNo, string $totalFee, string $attach, string $body, string $detail = '')
|
||||
{
|
||||
$totalFee = bcmul($totalFee, 100, 0);
|
||||
$response = self::instance()->order()->pay([
|
||||
'auth_code' => $authCode,
|
||||
'out_trade_no' => $outTradeNo,
|
||||
'total_fee' => (int)$totalFee,
|
||||
'attach' => $attach,
|
||||
'body' => $body,
|
||||
'detail' => $detail
|
||||
]);
|
||||
$response = $response->toArray();
|
||||
|
||||
self::logger('付款码支付', compact('authCode', 'outTradeNo', 'totalFee', 'attach', 'body', 'detail'), $response);
|
||||
|
||||
//下单成功
|
||||
if ($response['return_code'] === 'SUCCESS') {
|
||||
//扫码付款直接支付成功
|
||||
if ($response['result_code'] === 'SUCCESS' && $response['trade_type'] === 'MICROPAY') {
|
||||
return [
|
||||
'paid' => 1,
|
||||
'message' => '支付成功',
|
||||
'payInfo' => $response,
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'paid' => 0,
|
||||
'message' => $response['err_code_des'],
|
||||
'payInfo' => $response
|
||||
];
|
||||
}
|
||||
} else {
|
||||
throw new PayException($response['return_msg']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销订单
|
||||
* @param string $outTradeNo
|
||||
* @return bool
|
||||
* @throws InvalidArgumentException
|
||||
* @throws InvalidConfigException
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function reverseOrder(string $outTradeNo): bool
|
||||
{
|
||||
$response = self::instance()->order()->reverseByOutTradeNumber($outTradeNo);
|
||||
$response = $response->toArray();
|
||||
self::logger('撤销订单', compact('outTradeNo'), $response);
|
||||
|
||||
if ($response['return_code'] === 'SUCCESS') {
|
||||
return true;
|
||||
} else {
|
||||
throw new PayException($response['return_msg']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询订单支付状态
|
||||
* @param string $outTradeNo
|
||||
* @return array
|
||||
* @throws InvalidArgumentException
|
||||
* @throws InvalidConfigException
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function queryOrder(string $outTradeNo)
|
||||
{
|
||||
$response = self::instance()->order()->queryByOutTradeNumber($outTradeNo);
|
||||
$response = $response->toArray();
|
||||
|
||||
self::logger('查询订单支付状态', compact('outTradeNo'), $response);
|
||||
|
||||
if ($response['return_code'] === 'SUCCESS') {
|
||||
if ($response['result_code'] === 'SUCCESS') {
|
||||
return [
|
||||
'paid' => 1,
|
||||
'out_trade_no' => $outTradeNo,
|
||||
'payInfo' => $response
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'paid' => 0,
|
||||
'out_trade_no' => $outTradeNo,
|
||||
'payInfo' => $response
|
||||
];
|
||||
}
|
||||
} else {
|
||||
throw new PayException($response['return_msg']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 企业付款到零钱
|
||||
* @param string $openid openid
|
||||
* @param string $orderId 订单号
|
||||
* @param string $amount 金额
|
||||
* @param string $desc 说明
|
||||
* @param string $type 类型
|
||||
* @return bool
|
||||
* @throws InvalidArgumentException
|
||||
* @throws TransportExceptionInterface
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException|InvalidConfigException
|
||||
*/
|
||||
public static function merchantPay(string $channel_type, int $uid, string $orderId, string $amount, string $desc, string $user_name = '', array $transferDetailList = [])
|
||||
{
|
||||
/** @var WechatUserServices $wechatServices */
|
||||
$wechatServices = app()->make(WechatUserServices::class);
|
||||
$typeMap = [
|
||||
'routine' => Payment::MINI,
|
||||
'wechat' => Payment::WEB,
|
||||
'app' => Payment::APP
|
||||
];
|
||||
|
||||
$channelType = $channel_type;
|
||||
$openid = '';
|
||||
$type = null;
|
||||
|
||||
if (isset($typeMap[$channelType])) {
|
||||
$openid = $wechatServices->getWechatOpenid($uid, $channelType);
|
||||
$type = $typeMap[$channelType];
|
||||
}
|
||||
if (!$openid) {
|
||||
throw new PayException('该用户暂不支持企业付款到零钱,请手动转账');
|
||||
}
|
||||
$application = self::instance()->setAccessEnd($type)->application();
|
||||
$config = $application->getConfig();
|
||||
if (empty($config['certificate'])) {
|
||||
throw new PayException('企业付款到零钱需要支付cert证书,检测到您没有上传!');
|
||||
}
|
||||
if (empty($config['private_key'])) {
|
||||
throw new PayException('企业付款到零钱需要支付key证书,检测到您没有上传!');
|
||||
}
|
||||
if (self::instance()->isV3PAy) {
|
||||
if (sys_config('v3_pay_public_key') != '') {
|
||||
$transfer_scene_id = sys_config('pay_weixin_scene_id');
|
||||
if (!$transfer_scene_id) {
|
||||
throw new PayException('请配置微信v3新支付场景ID!');
|
||||
}
|
||||
$user_name = $user_name ?: app()->make(UserServices::class)->value(['uid' => $uid], 'nickname');
|
||||
//v3新支付
|
||||
$res = self::instance()->payClient->setType($type)->transferBills(
|
||||
outBatchNo: $orderId,
|
||||
amount: $amount,
|
||||
openid: $openid,
|
||||
userName: $user_name,
|
||||
remark: $desc,
|
||||
perception: $desc,
|
||||
transferDetailList: $transferDetailList,
|
||||
transfer_scene_id: $transfer_scene_id
|
||||
);
|
||||
self::logger('商家转账到零钱', compact('orderId', 'amount', 'openid', 'desc', 'user_name', 'transferDetailList', 'transfer_scene_id'), $res);
|
||||
} else {
|
||||
//v3支付使用发起商家转账API
|
||||
$res = self::instance()->payClient->setType($type)->batches(
|
||||
outBatchNo: $orderId,
|
||||
amount: $amount,
|
||||
batchName: $desc,
|
||||
remark: $desc,
|
||||
transferDetailList: [
|
||||
[
|
||||
'out_detail_no' => $orderId,
|
||||
'transfer_amount' => $amount,
|
||||
'transfer_remark' => $desc,
|
||||
'openid' => $openid
|
||||
]
|
||||
]
|
||||
);
|
||||
}
|
||||
return $res;
|
||||
|
||||
} else {
|
||||
$merchantPayData = [
|
||||
'partner_trade_no' => $orderId, //随机字符串作为订单号,跟红包和支付一个概念。
|
||||
'openid' => $openid, //收款人的openid
|
||||
'check_name' => 'NO_CHECK', //文档中有三种校验实名的方法 NO_CHECK OPTION_CHECK FORCE_CHECK
|
||||
'amount' => (int)bcmul($amount, '100', 0), //单位为分
|
||||
'desc' => $desc,
|
||||
'spbill_create_ip' => request()->ip(), //发起交易的IP地址
|
||||
];
|
||||
$result = self::instance()->order()->toBalance($merchantPayData);
|
||||
$result = $result->toArray();
|
||||
|
||||
self::logger('企业付款到零钱', compact('merchantPayData'), $result);
|
||||
|
||||
if ($result['return_code'] == 'SUCCESS' && $result['result_code'] != 'FAIL') {
|
||||
return true;
|
||||
} else {
|
||||
throw new PayException(($result['return_msg'] ?? '支付失败') . ':' . ($result['err_code_des'] ?? '发起企业支付到零钱失败'));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成支付订单对象
|
||||
* @param $openid
|
||||
* @param $out_trade_no
|
||||
* @param $total_fee
|
||||
* @param $attach
|
||||
* @param $body
|
||||
* @param string $detail
|
||||
* @param string $trade_type
|
||||
* @param array $options
|
||||
* @return \EasyWeChat\Kernel\HttpClient\Response
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function paymentOrder($openid, $out_trade_no, $total_fee, $attach, $body, $detail = '', $trade_type = 'JSAPI', array $options = [])
|
||||
{
|
||||
$total_fee = bcmul($total_fee, 100, 0);
|
||||
$order = array_merge(compact('out_trade_no', 'total_fee', 'attach', 'body', 'detail', 'trade_type'), $options);
|
||||
if (!is_null($openid)) $order['openid'] = $openid;
|
||||
if ($order['detail'] == '') unset($order['detail']);
|
||||
$result = self::instance()->order()->unify($order);
|
||||
$result = $result->toArray();
|
||||
self::logger('生成支付订单对象', compact('order'), $result);
|
||||
|
||||
if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
|
||||
return $result;
|
||||
} else {
|
||||
if ($result['return_code'] == 'FAIL') {
|
||||
throw new PayException('微信支付错误返回:' . $result['return_msg']);
|
||||
} else if (isset($result['err_code'])) {
|
||||
throw new PayException('微信支付错误返回:' . $result['err_code_des']);
|
||||
} else {
|
||||
throw new PayException('没有获取微信支付的预支付ID,请重新发起支付!');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成支付订单对象(小程序商户号支付时)
|
||||
* @param $openid
|
||||
* @param $out_trade_no
|
||||
* @param $total_fee
|
||||
* @param $attach
|
||||
* @param $body
|
||||
* @param string $detail
|
||||
* @param string $trade_type
|
||||
* @param array $options
|
||||
* @return \EasyWeChat\Kernel\HttpClient\Response|ResponseInterface
|
||||
* @throws InvalidArgumentException
|
||||
* @throws InvalidConfigException
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function paymentMiniOrder($openid, $out_trade_no, $total_fee, $attach, $body, $detail = '', $trade_type = 'JSAPI', array $options = [])
|
||||
{
|
||||
$total_fee = bcmul($total_fee, 100, 0);
|
||||
$order = array_merge(compact('out_trade_no', 'total_fee', 'attach', 'body', 'detail', 'trade_type'), $options);
|
||||
if (!is_null($openid)) $order['openid'] = $openid;
|
||||
if ($order['detail'] == '') unset($order['detail']);
|
||||
$order['spbill_create_ip'] = request()->ip();
|
||||
$result = self::instance()->order()->createorder($order);
|
||||
$result = $result->toArray();
|
||||
self::logger('生成支付订单对象', compact('order'), $result);
|
||||
if ($result['errcode'] == '0') {
|
||||
return $result;
|
||||
} else {
|
||||
throw new PayException('微信支付错误返回:' . $result['errmsg']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获得jsSdk支付参数
|
||||
* @param $openid
|
||||
* @param $out_trade_no
|
||||
* @param $total_fee
|
||||
* @param $attach
|
||||
* @param $body
|
||||
* @param string $detail
|
||||
* @param string $trade_type
|
||||
* @param array $options
|
||||
* @return array
|
||||
* @throws InvalidArgumentException
|
||||
* @throws InvalidConfigException|TransportExceptionInterface
|
||||
*/
|
||||
public static function jsPay($openid, $out_trade_no, $total_fee, $attach, $body, $detail = '', $trade_type = 'JSAPI', $options = [])
|
||||
{
|
||||
$pay = self::instance();
|
||||
$paymentPrepare = self::paymentOrder($openid, $out_trade_no, $total_fee, $attach, $body, $detail, $trade_type, $options);
|
||||
return $pay->application()
|
||||
->getUtils()
|
||||
->buildSdkConfig($paymentPrepare['prepay_id'], $pay->application()->getConfig()->get('app_id'), 'MD5');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得jsSdk支付参数(小程序商户号支付时)
|
||||
* @param $openid
|
||||
* @param $out_trade_no
|
||||
* @param $total_fee
|
||||
* @param $attach
|
||||
* @param $body
|
||||
* @param string $detail
|
||||
* @param string $trade_type
|
||||
* @param array $options
|
||||
* @return array
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function miniPay($openid, $out_trade_no, $total_fee, $attach, $body, $detail = '', $trade_type = 'JSAPI', $options = [])
|
||||
{
|
||||
$paymentPrepare = self::paymentMiniOrder($openid, $out_trade_no, $total_fee, $attach, $body, $detail, $trade_type, $options);
|
||||
$paymentPrepare['payment_params']['timestamp'] = $paymentPrepare['payment_params']['timeStamp'];
|
||||
return $paymentPrepare['payment_params'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得APP付参数
|
||||
* @param $openid
|
||||
* @param $out_trade_no
|
||||
* @param $total_fee
|
||||
* @param $attach
|
||||
* @param $body
|
||||
* @param string $detail
|
||||
* @param string $trade_type
|
||||
* @param array $options
|
||||
* @return array|string
|
||||
* @throws InvalidArgumentException
|
||||
* @throws InvalidConfigException
|
||||
* @throws TransportExceptionInterface
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
*/
|
||||
public static function appPay($openid, $out_trade_no, $total_fee, $attach, $body, $detail = '', $trade_type = 'APP', $options = [])
|
||||
{
|
||||
if (self::instance()->isV3PAy) {
|
||||
return self::instance()->payClient->appPay($out_trade_no, $total_fee, $body, $attach);
|
||||
} else {
|
||||
$paymentPrepare = self::paymentOrder($openid, $out_trade_no, $total_fee, $attach, $body, $detail, $trade_type, $options);
|
||||
return self::instance()->application()->getUtils()->buildAppConfig($paymentPrepare['prepay_id'], self::instance()->application()->getConfig()->get('app_id'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* v3 jspay 支付
|
||||
* @return PayClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/10/10
|
||||
*/
|
||||
public function payClient()
|
||||
{
|
||||
return $this->payClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得native支付参数
|
||||
* @param $openid
|
||||
* @param $out_trade_no
|
||||
* @param $total_fee
|
||||
* @param $attach
|
||||
* @param $body
|
||||
* @param string $detail
|
||||
* @param string $trade_type
|
||||
* @param array $options
|
||||
* @return array
|
||||
* @throws TransportExceptionInterface
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
*/
|
||||
public static function nativePay($openid, $out_trade_no, $total_fee, $attach, $body, $detail = '', $trade_type = 'NATIVE', $options = [])
|
||||
{
|
||||
$instance = self::instance();
|
||||
|
||||
if ($instance->isV3PAy) {
|
||||
$data = $instance->payClient->nativePay($out_trade_no, $total_fee, $body, $attach);
|
||||
$res['code_url'] = $data['code_url'];
|
||||
$res['invalid'] = time() + 60;
|
||||
$res['logo'] = [];
|
||||
return $res;
|
||||
}
|
||||
|
||||
$data = $instance->setAccessEnd(self::WEB)->paymentOrder($openid, $out_trade_no, $total_fee, $attach, $body, $detail, $trade_type, $options);
|
||||
if ($data) {
|
||||
$res['code_url'] = $data['code_url'];
|
||||
$res['invalid'] = time() + 60;
|
||||
$res['logo'] = [];
|
||||
} else $res = [];
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用商户订单号退款
|
||||
* @param $orderNo
|
||||
* @param $refundNo
|
||||
* @param $totalFee
|
||||
* @param null $refundFee
|
||||
* @param null $opUserId
|
||||
* @param string $refundReason
|
||||
* @param string $type
|
||||
* @param string $refundAccount
|
||||
* @return \EasyWeChat\Kernel\HttpClient\Response
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public function refund($orderNo, $refundNo, $totalFee, $refundFee = null, $opUserId = null, string $refundReason = '', string $type = 'out_trade_no', string $refundAccount = 'REFUND_SOURCE_UNSETTLED_FUNDS')
|
||||
{
|
||||
$totalFee = floatval($totalFee);
|
||||
$refundFee = floatval($refundFee);
|
||||
if ($type == 'out_trade_no') {
|
||||
$result = $this->order()->byOutTradeNumber($orderNo, $refundNo, $totalFee, $refundFee, [
|
||||
'refund_account' => $refundAccount,
|
||||
'notify_url' => $this->config->refundUrl,
|
||||
'refund_desc' => $refundReason
|
||||
]);
|
||||
} else {
|
||||
$result = $this->order()->byTransactionId($orderNo, $refundNo, $totalFee, $refundFee, [
|
||||
'refund_account' => $refundAccount,
|
||||
'notify_url' => $this->config->refundUrl,
|
||||
'refund_desc' => $refundReason
|
||||
]);
|
||||
}
|
||||
|
||||
self::logger('使用商户订单号退款', compact('orderNo', 'refundNo', 'totalFee', 'refundFee', 'opUserId', 'refundReason', 'type', 'refundAccount'), $result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序商户退款
|
||||
* @param $orderNo //微信支付单号
|
||||
* @param $refundNo //微信退款单号
|
||||
* @param $totalFee
|
||||
* @param null $refundFee
|
||||
* @param array $opt
|
||||
* @return \EasyWeChat\Kernel\HttpClient\Response|ResponseInterface
|
||||
* @throws InvalidArgumentException
|
||||
* @throws InvalidConfigException
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public function miniRefund($orderNo, $refundNo, $totalFee, $refundFee = null, array $opt = [])
|
||||
{
|
||||
$totalFee = floatval($totalFee);
|
||||
$refundFee = floatval($refundFee);
|
||||
|
||||
$order = [
|
||||
'openid' => $opt['open_id'],
|
||||
'trade_no' => $opt['routine_order_id'],
|
||||
'transaction_id' => $orderNo,
|
||||
'refund_no' => $refundNo,
|
||||
'total_amount' => $totalFee,
|
||||
'refund_amount' => $refundFee,
|
||||
];
|
||||
$result = $this->order()->refundorder($order);
|
||||
|
||||
self::logger('使用商户订单号退款', compact('orderNo', 'refundNo', 'totalFee', 'refundFee', 'opt'), $result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款
|
||||
* @param $orderNo
|
||||
* @param array $opt
|
||||
* @return bool
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException|TransportExceptionInterface
|
||||
*/
|
||||
public function payOrderRefund($orderNo, array $opt)
|
||||
{
|
||||
if (isset($opt['pay_routine_open']) && $opt['pay_routine_open']) {
|
||||
return $this->payMiniOrderRefund($orderNo, $opt);
|
||||
}
|
||||
if (!isset($opt['pay_price'])) {
|
||||
throw new PayException('缺少pay_price');
|
||||
}
|
||||
$certPath = $this->config->certPath;
|
||||
if (!$certPath) {
|
||||
throw new PayException('请上传支付证书cert');
|
||||
}
|
||||
$keyPath = $this->config->keyPath;
|
||||
if (!$keyPath) {
|
||||
throw new PayException('请上传支付证书key');
|
||||
}
|
||||
if (!is_file($certPath)) {
|
||||
throw new PayException('支付证书cert不存在');
|
||||
}
|
||||
if (!is_file($keyPath)) {
|
||||
throw new PayException('支付证书key不存在');
|
||||
}
|
||||
|
||||
if ($this->isV3PAy) {
|
||||
return $this->payClient->refund($orderNo, $opt);
|
||||
}
|
||||
|
||||
$totalFee = floatval(bcmul($opt['pay_price'], 100, 0));
|
||||
$refundFee = isset($opt['refund_price']) ? floatval(bcmul($opt['refund_price'], 100, 0)) : null;
|
||||
$refundReason = $opt['desc'] ?? '';
|
||||
$refundNo = $opt['refund_id'] ?? $orderNo;
|
||||
$opUserId = $opt['op_user_id'] ?? null;
|
||||
$type = $opt['type'] ?? 'out_trade_no';
|
||||
/*仅针对老资金流商户使用
|
||||
REFUND_SOURCE_UNSETTLED_FUNDS---未结算资金退款(默认使用未结算资金退款)
|
||||
REFUND_SOURCE_RECHARGE_FUNDS---可用余额退款*/
|
||||
$refundAccount = $opt['refund_account'] ?? 'REFUND_SOURCE_UNSETTLED_FUNDS';
|
||||
try {
|
||||
$res = $this->refund($orderNo, $refundNo, $totalFee, $refundFee, $opUserId, $refundReason, $type, $refundAccount);
|
||||
$res = $res->toArray();
|
||||
if ($res['return_code'] == 'FAIL') {
|
||||
throw new PayException('退款失败:' . $res['return_msg']);
|
||||
}
|
||||
if (isset($res['err_code'])) {
|
||||
throw new PayException('退款失败:' . $res['err_code_des']);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
|
||||
self::error($e);
|
||||
|
||||
throw new PayException($e->getMessage());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序商户退款
|
||||
* @param $orderNo
|
||||
* @param array $opt
|
||||
* @return bool
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public function payMiniOrderRefund($orderNo, array $opt)
|
||||
{
|
||||
if (!isset($opt['pay_price'])) {
|
||||
throw new PayException('缺少pay_price');
|
||||
}
|
||||
if (!isset($opt['routine_order_id'])) {
|
||||
throw new PayException('缺少订单单号');
|
||||
}
|
||||
$totalFee = floatval(bcmul($opt['pay_price'], 100, 0));
|
||||
$refundFee = isset($opt['refund_price']) ? floatval(bcmul($opt['refund_price'], 100, 0)) : null;
|
||||
$refundNo = $opt['refund_no'];
|
||||
try {
|
||||
$result = $this->miniRefund($orderNo, $refundNo, $totalFee, $refundFee, $opt);
|
||||
$result = $result->toArray();
|
||||
if ($result['errcode'] == '0') {
|
||||
return true;
|
||||
} else {
|
||||
throw new PayException('退款失败:' . $result['errmsg']);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
|
||||
self::error($e);
|
||||
|
||||
throw new PayException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 商家转账通知
|
||||
* @return Response
|
||||
* User: liusl
|
||||
* DateTime: 2025/2/14 下午2:53
|
||||
*/
|
||||
public function handleMchNotify()
|
||||
{
|
||||
$response = $this->payClient->handleNotify(function ($notify, $success) {
|
||||
self::logger('商家转账成功回调接口', [], $notify);
|
||||
if (isset($notify['transfer_bill_no']) && $success) {
|
||||
$res = Event::until('pay.mchNotify', [$notify]);
|
||||
if ($res) {
|
||||
return $res;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return response($response->getContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信支付成功回调接口
|
||||
* @return Response
|
||||
* @throws InvalidArgumentException
|
||||
* @throws RuntimeException
|
||||
* @throws ReflectionException
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function handleNotify()
|
||||
{
|
||||
if ($this->isV3PAy) {
|
||||
$response = $this->payClient->handleNotify(function ($notify, $success) {
|
||||
|
||||
self::logger('微信支付成功回调接口', [], $notify);
|
||||
if (isset($notify['out_trade_no']) && $success) {
|
||||
$res = Event::until('pay.notify', [$notify]);
|
||||
if ($res) {
|
||||
return $res;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
return response($response->getContent());
|
||||
} else {
|
||||
$message = $this->application()->getServer()->getRequestMessage();
|
||||
$message = $message->toArray();
|
||||
if ($this->checkSignV2($message)) {
|
||||
self::logger('微信支付成功回调接口', [], $message);
|
||||
|
||||
Event::until('pay.notify', [$message]);
|
||||
return response(XML::build(['return_code' => 'success', 'return_message' => 'OK']), 200, [], 'xml');
|
||||
} else {
|
||||
return response(XML::build(['return_code' => 'fail', 'return_message' => 'FAIL']), 500, [], 'xml');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验签名
|
||||
* @param $message
|
||||
* @return bool
|
||||
*/
|
||||
public function checkSignV2($message)
|
||||
{
|
||||
$sign = $message['sign'];
|
||||
unset($message['sign']);
|
||||
|
||||
if ($this->generate_sign($message, $this->config->key) !== $sign) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $attributes
|
||||
* @param $key
|
||||
* @param $encryptMethod
|
||||
* @return string
|
||||
*/
|
||||
function generate_sign(array $attributes, $key, $encryptMethod = 'md5')
|
||||
{
|
||||
ksort($attributes);
|
||||
|
||||
$attributes['key'] = $key;
|
||||
|
||||
return strtoupper(call_user_func_array($encryptMethod, [urldecode(http_build_query($attributes))]));
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款结果通知
|
||||
* @return Response
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ReflectionException
|
||||
* @throws RuntimeException
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function handleRefundedNotify()
|
||||
{
|
||||
$response = $this->application()->getServer()->handleRefunded(function ($message, \Closure $next) {
|
||||
|
||||
self::logger('退款结果通知', [], compact('message'));
|
||||
|
||||
Event::until('pay.refunded.notify', [$message]);
|
||||
|
||||
return $next($message);
|
||||
});
|
||||
|
||||
return response($response->serve());
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否时微信付款二维码值
|
||||
* @param string $authCode
|
||||
* @return bool
|
||||
*/
|
||||
public static function isWechatAuthCode(string $authCode)
|
||||
{
|
||||
return preg_match('/^[0-9]{18}$/', $authCode) && in_array(substr($authCode, 0, 2), ['10', '11', '12', '13', '14', '15']);
|
||||
}
|
||||
}
|
||||
163
crmeb/services/wechat/SwooleResponse.php
Normal file
163
crmeb/services/wechat/SwooleResponse.php
Normal file
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat;
|
||||
|
||||
use Symfony\Component\HttpClient\Exception\ServerException;
|
||||
use Symfony\Component\HttpClient\Exception\ClientException;
|
||||
use Symfony\Component\HttpClient\Exception\RedirectionException;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
use Swoole\Coroutine\Http\Client;
|
||||
|
||||
/**
|
||||
* 携程请求返回资源
|
||||
* Class SwooleResponse
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2024/1/25
|
||||
* @package crmeb\services\wechat
|
||||
*/
|
||||
class SwooleResponse implements ResponseInterface
|
||||
{
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
private $swooleResponse;
|
||||
|
||||
/**
|
||||
* SwooleResponse constructor.
|
||||
* @param Client $swooleResponse
|
||||
*/
|
||||
public function __construct(Client $swooleResponse)
|
||||
{
|
||||
$this->swooleResponse = $swooleResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $throw
|
||||
* @return string
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2024/1/25
|
||||
*/
|
||||
public function getContent(bool $throw = true): string
|
||||
{
|
||||
if ($throw) {
|
||||
$this->checkStatusCode();
|
||||
}
|
||||
|
||||
return $this->swooleResponse->getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2024/1/25
|
||||
*/
|
||||
public function getStatusCode(): int
|
||||
{
|
||||
return $this->swooleResponse->getStatusCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $throw
|
||||
* @return array
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2024/1/25
|
||||
*/
|
||||
public function getHeaders(bool $throw = true): array
|
||||
{
|
||||
if ($throw) {
|
||||
$this->checkStatusCode();
|
||||
}
|
||||
|
||||
$headers = [];
|
||||
foreach ($this->swooleResponse->getHeaders() as $name => $value) {
|
||||
$headers[strtolower($name)][] = $value;
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $throw
|
||||
* @return array
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2024/1/25
|
||||
*/
|
||||
public function toArray(bool $throw = true): array
|
||||
{
|
||||
$content = $this->getContent($throw);
|
||||
if ($content === '' && $throw) {
|
||||
throw new \InvalidArgumentException('The content is not valid JSON.');
|
||||
}
|
||||
|
||||
return json_decode($content, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2024/1/25
|
||||
*/
|
||||
public function cancel(): void
|
||||
{
|
||||
$this->swooleResponse->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $type
|
||||
* @return mixed
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2024/1/24
|
||||
*/
|
||||
public function getInfo(string $type = null): mixed
|
||||
{
|
||||
if ($type === null || $type === 'http_code') {
|
||||
return $this->getStatusCode();
|
||||
}
|
||||
|
||||
$httpResponse = [
|
||||
'http_code' => $this->getStatusCode(),
|
||||
'response_headers' => $this->swooleResponse->getHeaders(),
|
||||
'url' => $this->swooleResponse->host,
|
||||
];
|
||||
|
||||
return $type ? $httpResponse[$type] ?? null : $httpResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2024/1/25
|
||||
*/
|
||||
private function checkStatusCode()
|
||||
{
|
||||
$code = $this->getInfo('http_code');
|
||||
|
||||
if (500 <= $code) {
|
||||
throw new ServerException($this);
|
||||
}
|
||||
|
||||
if (400 <= $code) {
|
||||
throw new ClientException($this);
|
||||
}
|
||||
|
||||
if (300 <= $code) {
|
||||
throw new RedirectionException($this);
|
||||
}
|
||||
}
|
||||
}
|
||||
35
crmeb/services/wechat/WechatException.php
Normal file
35
crmeb/services/wechat/WechatException.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace crmeb\services\wechat;
|
||||
|
||||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 微信错误统一处理
|
||||
* Class WechatException
|
||||
* @package crmeb\services\wechat
|
||||
*/
|
||||
class WechatException extends \RuntimeException
|
||||
{
|
||||
|
||||
/**
|
||||
* WechatException constructor.
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
* @param Throwable|null $previous
|
||||
*/
|
||||
public function __construct($message = "", $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
||||
129
crmeb/services/wechat/WechatResponse.php
Normal file
129
crmeb/services/wechat/WechatResponse.php
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace crmeb\services\wechat;
|
||||
|
||||
use think\Collection;
|
||||
use Throwable;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
|
||||
|
||||
/**
|
||||
* 微信错误
|
||||
* Class WechatResponse
|
||||
* @package crmeb\services\wechat
|
||||
*/
|
||||
class WechatResponse extends Collection
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Throwable
|
||||
*/
|
||||
protected Throwable $e;
|
||||
|
||||
/**
|
||||
* 是否抛出默认错误
|
||||
* @var bool
|
||||
*/
|
||||
protected bool $error = true;
|
||||
|
||||
/**
|
||||
* 数据转换
|
||||
* @var array
|
||||
*/
|
||||
protected $items = [];
|
||||
|
||||
/**
|
||||
* WechatResponse constructor.
|
||||
* @param $items
|
||||
*/
|
||||
public function __construct($items = [])
|
||||
{
|
||||
$this->items = is_object($items) && method_exists($items, 'toArray') ? $items->toArray() : $items;
|
||||
$this->wechatError();
|
||||
}
|
||||
|
||||
/**
|
||||
* 错误统一处理
|
||||
*/
|
||||
public function wechatError()
|
||||
{
|
||||
if (!$this->error) {
|
||||
return;
|
||||
}
|
||||
if (isset($this->items['errcode']) && 0 !== $this->items['errcode']) {
|
||||
throw new WechatException(
|
||||
ErrorMessage::getWorkMessage(
|
||||
$this->items['errcode'] ?? 0,
|
||||
$this->items['errmsg'] ?? null
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $boole
|
||||
* @return $this
|
||||
*/
|
||||
public function serError(bool $boole)
|
||||
{
|
||||
$this->error = $boole;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 正确处理
|
||||
* @param callable $then
|
||||
* @param bool $error
|
||||
* @return $this
|
||||
*/
|
||||
public function then(callable $then, bool $error = null)
|
||||
{
|
||||
$error = $error || $this->error;
|
||||
if (0 !== $this->items['errcode'] && $error) {
|
||||
throw new WechatException($this->items['errmsg']);
|
||||
}
|
||||
try {
|
||||
$this->response = $then($this->items);
|
||||
} catch (Throwable $e) {
|
||||
$this->e = $e;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 异常处理
|
||||
* @param callable $catch
|
||||
* @return $this
|
||||
*/
|
||||
public function catch(callable $catch)
|
||||
{
|
||||
|
||||
if (!$this->e) {
|
||||
$this->e = new WechatException('success');
|
||||
}
|
||||
|
||||
$catch($this->e, $this->items);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取返回值
|
||||
* @return string
|
||||
*/
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
}
|
||||
1057
crmeb/services/wechat/Work.php
Normal file
1057
crmeb/services/wechat/Work.php
Normal file
File diff suppressed because it is too large
Load Diff
80
crmeb/services/wechat/client/BaseClient.php
Normal file
80
crmeb/services/wechat/client/BaseClient.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client;
|
||||
|
||||
use EasyWeChat\Kernel\Form\File;
|
||||
use EasyWeChat\Kernel\Form\Form;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use EasyWeChat\Kernel\HttpClient\AccessTokenAwareClient;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
|
||||
/**
|
||||
* 基础接口请求
|
||||
* Class BaseClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/18
|
||||
* @package crmeb\services\wechat\client
|
||||
*/
|
||||
abstract class BaseClient
|
||||
{
|
||||
/**
|
||||
* UserClient constructor.
|
||||
* @param AccessTokenAwareClient $api
|
||||
*/
|
||||
public function __construct(protected AccessTokenAwareClient $api)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
* @param string $url
|
||||
* @param array $files
|
||||
* @param array $form
|
||||
* @param array $query
|
||||
* @return Response
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function httpUpload(string $url, array $files = [], array $form = [], array $query = [])
|
||||
{
|
||||
$form['media'] = File::fromPath($files['media']);
|
||||
$options = Form::create($form)->toArray();
|
||||
return $this->api->request(
|
||||
'POST',
|
||||
$url.'?'.http_build_query($query),
|
||||
$options
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $mediaId
|
||||
* @param string $uri
|
||||
* @return Response
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
protected function getResources(string $mediaId, string $uri)
|
||||
{
|
||||
return $this->api->request('GET', $uri, [
|
||||
'query' => [
|
||||
'media_id' => $mediaId,
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
160
crmeb/services/wechat/client/miniprogram/AuthClient.php
Normal file
160
crmeb/services/wechat/client/miniprogram/AuthClient.php
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client\miniprogram;
|
||||
|
||||
|
||||
use crmeb\services\wechat\client\BaseClient;
|
||||
use crmeb\services\wechat\config\MiniProgramConfig;
|
||||
use crmeb\services\wechat\util\AES;
|
||||
use crmeb\services\wechat\WechatException;
|
||||
use EasyWeChat\Kernel\HttpClient\AccessTokenAwareClient;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
use think\annotation\Inject;
|
||||
|
||||
/**
|
||||
* 授权
|
||||
* Class AuthClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
* @package crmeb\services\wechat\client\miniprogram
|
||||
*/
|
||||
class AuthClient extends BaseClient
|
||||
{
|
||||
|
||||
/**
|
||||
* @var MiniProgramConfig
|
||||
*/
|
||||
protected MiniProgramConfig $config;
|
||||
|
||||
/**
|
||||
* AuthClient constructor.
|
||||
* @param AccessTokenAwareClient $api
|
||||
*/
|
||||
public function __construct(AccessTokenAwareClient $api)
|
||||
{
|
||||
parent::__construct($api);
|
||||
$this->config = app()->make(MiniProgramConfig::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用code换取用户信息
|
||||
* @param string $code
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function session(string $code): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'appid' => $this->config->appId,
|
||||
'secret' => $this->config->secret,
|
||||
'js_code' => $code,
|
||||
'grant_type' => 'authorization_code',
|
||||
];
|
||||
|
||||
return $this->api->get('sns/jscode2session', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密数据
|
||||
* @param string $sessionKey
|
||||
* @param string $iv
|
||||
* @param string $encrypted
|
||||
* @return array
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function decryptData(string $sessionKey, string $iv, string $encrypted): array
|
||||
{
|
||||
$decrypted = AES::decrypt(
|
||||
base64_decode($encrypted, false),
|
||||
base64_decode($sessionKey, false),
|
||||
base64_decode($iv, false)
|
||||
);
|
||||
|
||||
$decrypted = json_decode($decrypted, true);
|
||||
|
||||
if (!$decrypted) {
|
||||
throw new WechatException('The given payload is invalid.');
|
||||
}
|
||||
|
||||
return $decrypted;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取小程序码:适用于需要的码数量极多,或仅临时使用的业务场景
|
||||
* @param string $scene
|
||||
* @param array $optional
|
||||
* @return Response
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function getUnlimit(string $scene, array $optional = []): Response
|
||||
{
|
||||
$params = array_merge([
|
||||
'scene' => $scene,
|
||||
], $optional);
|
||||
|
||||
return $this->getStream('wxa/getwxacodeunlimit', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流文件
|
||||
* @param string $endpoint
|
||||
* @param array $params
|
||||
* @return Response
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
protected function getStream(string $endpoint, array $params): Response
|
||||
{
|
||||
return $this->api->request('POST', $endpoint, ['json' => $params]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取小程序短链
|
||||
* @param string $path 通过 URL Link 进入的小程序页面路径,必须是已经发布的小程序存在的页面,不可携带 query 。path 为空时会跳转小程序主页
|
||||
* @param string $query 通过 URL Link 进入小程序时的query,最大1024个字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~%
|
||||
* @param int $expire_type 默认值0.小程序 URL Link 失效类型,失效时间:0,失效间隔天数:1
|
||||
* @param int $expire_time 到期失效的 URL Link 的失效时间,为 Unix 时间戳。生成的到期失效 URL Link 在该时间前有效。最长有效期为30天。expire_type 为 0 必填
|
||||
* @param int $expire_interval 到期失效的URL Link的失效间隔天数。生成的到期失效URL Link在该间隔时间到达前有效。最长间隔天数为30天。expire_type 为 1 必填
|
||||
* @throws TransportExceptionInterface User: liusl
|
||||
* DateTime: 2024/11/23 下午2:46
|
||||
*/
|
||||
public function generateUrlLink(string $path, string $query, int $expire_type = 0, int $expire_time = 30 * 24 * 3600, int $expire_interval = 29)
|
||||
{
|
||||
$params = [
|
||||
'path' => $path,
|
||||
'query' => $query,
|
||||
'expire_type' => $expire_type,
|
||||
];
|
||||
if ($expire_type == 0) {
|
||||
$params['expire_time'] = time() + $expire_time;
|
||||
} else {
|
||||
$params['expire_interval'] = $expire_interval;
|
||||
}
|
||||
return $this->api->postJson('wxa/generate_urllink', $params);
|
||||
|
||||
}
|
||||
}
|
||||
304
crmeb/services/wechat/client/miniprogram/LiveClient.php
Normal file
304
crmeb/services/wechat/client/miniprogram/LiveClient.php
Normal file
@@ -0,0 +1,304 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client\miniprogram;
|
||||
|
||||
|
||||
use crmeb\services\wechat\client\BaseClient;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
|
||||
/**
|
||||
* 直播
|
||||
* Class LiveClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
* @package crmeb\services\wechat\client\miniprogram
|
||||
*/
|
||||
class LiveClient extends BaseClient
|
||||
{
|
||||
/**
|
||||
* 添加直播间参数
|
||||
* @var array
|
||||
*/
|
||||
protected array $createData = [
|
||||
'name' => '', // 房间名字
|
||||
'coverImg' => '', // 通过 uploadfile 上传,填写 mediaID
|
||||
'startTime' => 0, // 开始时间
|
||||
'endTime' => 0, // 结束时间
|
||||
'anchorName' => '', // 主播昵称
|
||||
'anchorWechat' => '', // 主播微信号
|
||||
'shareImg' => '', //通过 uploadfile 上传,填写 mediaID
|
||||
'feedsImg' => '', //通过 uploadfile 上传,填写 mediaID
|
||||
'isFeedsPublic' => 1, // 是否开启官方收录,1 开启,0 关闭
|
||||
'type' => 1, // 直播类型,1 推流 0 手机直播
|
||||
'screenType' => 0, // 1:横屏 0:竖屏
|
||||
'closeLike' => 0, // 是否 关闭点赞 1 关闭
|
||||
'closeGoods' => 0, // 是否 关闭商品货架,1:关闭
|
||||
'closeComment' => 0, // 是否开启评论,1:关闭
|
||||
'closeReplay' => 1, // 是否关闭回放 1 关闭
|
||||
'closeShare' => 0, // 是否关闭分享 1 关闭
|
||||
'closeKf' => 0 // 是否关闭客服,1 关闭
|
||||
];
|
||||
|
||||
/**
|
||||
* 获取直播房间
|
||||
* @param int $start
|
||||
* @param int $limit
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function getRooms(int $start = 0, int $limit = 10): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'start' => $start,
|
||||
'limit' => $limit,
|
||||
];
|
||||
|
||||
return $this->api->postJson('wxa/business/getliveinfo', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取直播回放
|
||||
* @param int $roomId
|
||||
* @param int $start
|
||||
* @param int $limit
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function getPlaybacks(int $roomId, int $start = 0, int $limit = 10): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'action' => 'get_replay',
|
||||
'room_id' => $roomId,
|
||||
'start' => $start,
|
||||
'limit' => $limit,
|
||||
];
|
||||
|
||||
return $this->api->postJson('wxa/business/getliveinfo', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建直播间
|
||||
* @param array $data
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function createRoom(array $data): ResponseInterface|Response
|
||||
{
|
||||
$params = array_merge($this->createData, $data);
|
||||
return $this->api->postJson('wxaapi/broadcast/room/create', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 直播间导入商品
|
||||
* @param int $room_id
|
||||
* @param $ids
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function roomAddGoods(int $room_id, $ids): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'ids' => $ids,
|
||||
'roomId' => $room_id
|
||||
];
|
||||
return $this->api->postJson('wxaapi/broadcast/room/addgoods', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商品列表信息
|
||||
* @param int $status
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function getGoodsList(int $status, int $page = 0, int $limit = 30): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'offset' => $page * $limit,
|
||||
'limit' => $limit,
|
||||
'status' => $status
|
||||
];
|
||||
return $this->api->get('wxaapi/broadcast/goods/getapproved', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商品详情
|
||||
* @param array $ids
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function getGooodsInfo(array $ids): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'goods_ids' => $ids
|
||||
];
|
||||
return $this->api->get('wxa/business/getgoodswarehouse', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 商品添加并审核
|
||||
* @param string $coverImgUrl
|
||||
* @param string $name
|
||||
* @param int $priceType
|
||||
* @param string $url
|
||||
* @param string $price
|
||||
* @param string $price2
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function addGoods(string $coverImgUrl, string $name, int $priceType, string $url, string $price, string $price2 = ''): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'goodsInfo' => [
|
||||
'coverImgUrl' => $coverImgUrl,
|
||||
'name' => $name,
|
||||
'priceType' => $priceType,
|
||||
'price' => $price,
|
||||
'url' => $url
|
||||
]
|
||||
];
|
||||
if ($priceType != 1) $params['goodsInfo']['price2'] = $price2;
|
||||
return $this->api->postJson('wxaapi/broadcast/goods/add', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤回审核
|
||||
* @param int $goodsId
|
||||
* @param int $auditId
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function resetauditGoods(int $goodsId, int $auditId): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'goodsId' => $goodsId,
|
||||
'auditId' => $auditId
|
||||
];
|
||||
return $this->api->postJson('wxaapi/broadcast/goods/resetaudit', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新提交审核
|
||||
* @param int $goodsId
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function auditGoods(int $goodsId): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'goodsId' => $goodsId
|
||||
];
|
||||
return $this->api->postJson('wxaapi/broadcast/goods/autdit', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除商品
|
||||
* @param int $goodsId
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function deleteGoods(int $goodsId): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'goodsId' => $goodsId
|
||||
];
|
||||
return $this->api->postJson('wxaapi/broadcast/goods/delete', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新商品
|
||||
* @param int $goodsId
|
||||
* @param string $coverImgUrl
|
||||
* @param string $name
|
||||
* @param int $priceType
|
||||
* @param string $url
|
||||
* @param string $price
|
||||
* @param string $price2
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function updateGoods(int $goodsId, string $coverImgUrl, string $name, int $priceType, string $url, string $price, string $price2 = ''): ResponseInterface|Response
|
||||
{
|
||||
$params = ['goodsInfo' => [
|
||||
'goodsId' => $goodsId,
|
||||
'coverImgUrl' => $coverImgUrl,
|
||||
'name' => $name,
|
||||
'priceType' => $priceType,
|
||||
'price' => $price,
|
||||
'url' => $url
|
||||
]];
|
||||
if ($priceType != 1) $params['goodsInfo']['price2'] = $price2;
|
||||
return $this->api->postJson('wxaapi/broadcast/goods/update', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取成员列表
|
||||
* @param int $role
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @param string $keyword
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function getRoleList(int $role = 2, int $page = 0, int $limit = 30, string $keyword = ''): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'role' => $role,
|
||||
'offset' => $page * $limit,
|
||||
'limit' => $limit,
|
||||
'keyword' => $keyword
|
||||
];
|
||||
return $this->api->get('wxaapi/broadcast/role/getrolelist', $params);
|
||||
}
|
||||
}
|
||||
62
crmeb/services/wechat/client/miniprogram/MaterialClient.php
Normal file
62
crmeb/services/wechat/client/miniprogram/MaterialClient.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client\miniprogram;
|
||||
|
||||
|
||||
use crmeb\services\wechat\client\BaseClient;
|
||||
use crmeb\services\wechat\WechatException;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
|
||||
/**
|
||||
* 附件
|
||||
* Class MaterialClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/26
|
||||
* @package crmeb\services\wechat\client\miniprogram
|
||||
*/
|
||||
class MaterialClient extends BaseClient
|
||||
{
|
||||
|
||||
/**
|
||||
* 附件类型
|
||||
* @var array|string[]
|
||||
*/
|
||||
protected array $allowTypes = ['image', 'voice', 'video', 'thumb'];
|
||||
|
||||
/**
|
||||
* 上传
|
||||
* @param string $type
|
||||
* @param string $path
|
||||
* @return Response
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function upload(string $type, string $path): Response
|
||||
{
|
||||
if (!file_exists($path) || !is_readable($path)) {
|
||||
throw new WechatException(sprintf("File does not exist, or the file is unreadable: '%s'", $path));
|
||||
}
|
||||
|
||||
if (!in_array($type, $this->allowTypes, true)) {
|
||||
throw new WechatException(sprintf("Unsupported media type: '%s'", $type));
|
||||
}
|
||||
|
||||
return $this->httpUpload('cgi-bin/media/upload', ['media' => $path], ['type' => $type]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client\miniprogram;
|
||||
|
||||
|
||||
use crmeb\services\wechat\client\BaseClient;
|
||||
use crmeb\services\wechat\config\MiniProgramConfig;
|
||||
use crmeb\services\wechat\util\AES;
|
||||
use crmeb\services\wechat\WechatException;
|
||||
use EasyWeChat\Kernel\HttpClient\AccessTokenAwareClient;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
use think\annotation\Inject;
|
||||
use think\exception\ValidateException;
|
||||
|
||||
|
||||
class MediaCheckClient extends BaseClient
|
||||
{
|
||||
|
||||
const MSG_API = 'wxa/msg_sec_check';
|
||||
const MEDIA_API = 'wxa/media_check_async';
|
||||
const LABEL = [
|
||||
100 => '正常',
|
||||
10001 => '广告',
|
||||
20001 => '时政',
|
||||
20002 => '色情',
|
||||
20003 => '辱骂',
|
||||
20006 => '违法犯罪',
|
||||
20008 => '欺诈',
|
||||
20012 => '低俗',
|
||||
20013 => '版权',
|
||||
21000 => '其他'
|
||||
];
|
||||
//scene 场景枚举值(1 资料;2 评论;3 论坛;4 社交日志)
|
||||
|
||||
/**
|
||||
* 文本敏感词检测
|
||||
* @param $content
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* User: liusl
|
||||
* DateTime: 2024/11/26 下午5:28
|
||||
*/
|
||||
public function msgSecCheck($content)
|
||||
{
|
||||
$params = [
|
||||
'content' => $content
|
||||
];
|
||||
return $this->api->postJson(self::MSG_API, $params);
|
||||
|
||||
}
|
||||
}
|
||||
424
crmeb/services/wechat/client/miniprogram/OrderClient.php
Normal file
424
crmeb/services/wechat/client/miniprogram/OrderClient.php
Normal file
@@ -0,0 +1,424 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client\miniprogram;
|
||||
|
||||
|
||||
use Redis;
|
||||
use crmeb\exceptions\AdminException;
|
||||
use crmeb\services\wechat\config\MiniProgramConfig;
|
||||
use crmeb\services\wechat\config\PaymentConfig;
|
||||
use crmeb\services\wechat\util\Helper;
|
||||
use crmeb\services\wechat\WechatException;
|
||||
use crmeb\services\wechat\WechatResponse;
|
||||
use EasyWeChat\Kernel\HttpClient\AccessTokenAwareClient;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface as ResponseInterfaceAlias;
|
||||
use think\facade\Cache;
|
||||
|
||||
/**
|
||||
* 订单管理
|
||||
* Class OrderClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
* @package crmeb\services\wechat\client\miniprogram
|
||||
*/
|
||||
class OrderClient
|
||||
{
|
||||
const redis_prefix = 'mini_order';
|
||||
|
||||
const express_company = 'ZTO'; // 默认发货快递公司为(中通快递)
|
||||
|
||||
/**
|
||||
* @var PaymentConfig
|
||||
*/
|
||||
protected PaymentConfig $config;
|
||||
|
||||
/**
|
||||
* @var MiniProgramConfig
|
||||
*/
|
||||
protected MiniProgramConfig $miniProgramConfig;
|
||||
|
||||
/**
|
||||
* @var Redis
|
||||
*/
|
||||
protected Redis $redis;
|
||||
|
||||
/**
|
||||
* UserClient constructor.
|
||||
* @param AccessTokenAwareClient $api
|
||||
*/
|
||||
public function __construct(protected AccessTokenAwareClient $api)
|
||||
{
|
||||
$this->config = app()->make(PaymentConfig::class);
|
||||
$this->miniProgramConfig = app()->make(MiniProgramConfig::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理联系人
|
||||
* @param array $contact
|
||||
* @return array
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
protected function handleContact(array $contact = []): array
|
||||
{
|
||||
if (isset($contact)) {
|
||||
if (isset($contact['consignor_contact']) && $contact['consignor_contact']) {
|
||||
$contact['consignor_contact'] = Helper::encryptTel($contact['consignor_contact']);
|
||||
}
|
||||
if (isset($contact['receiver_contact']) && $contact['receiver_contact']) {
|
||||
$contact['receiver_contact'] = Helper::encryptTel($contact['receiver_contact']);
|
||||
}
|
||||
}
|
||||
return $contact;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传订单
|
||||
* @param string $out_trade_no
|
||||
* @param int $logistics_type
|
||||
* @param array $shipping_list
|
||||
* @param string $payer_openid
|
||||
* @param $path
|
||||
* @param int $delivery_mode
|
||||
* @param bool $is_all_delivered
|
||||
* @return mixed
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws RedirectionExceptionInterface
|
||||
* @throws ServerExceptionInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function shippingByTradeNo(string $out_trade_no, int $logistics_type, array $shipping_list, string $payer_openid, $path, int $delivery_mode = 1, bool $is_all_delivered = true)
|
||||
{
|
||||
if (!$this->checkManaged()) {
|
||||
throw new AdminException('开通小程序订单管理服务后重试');
|
||||
}
|
||||
$params = [
|
||||
'order_key' => [
|
||||
'order_number_type' => 2,
|
||||
'mchid' => $this->config->mchId,
|
||||
'transaction_id' => $out_trade_no
|
||||
],
|
||||
'logistics_type' => $logistics_type,
|
||||
'delivery_mode' => $delivery_mode,
|
||||
'upload_time' => date(DATE_RFC3339),
|
||||
'payer' => [
|
||||
'openid' => $payer_openid
|
||||
]
|
||||
];
|
||||
|
||||
if ($delivery_mode == 2) {
|
||||
$params['is_all_delivered'] = $is_all_delivered;
|
||||
}
|
||||
|
||||
foreach ($shipping_list as $shipping) {
|
||||
$contact = $this->handleContact($shipping['contact'] ?? []);
|
||||
$params['shipping_list'][] = [
|
||||
'tracking_no' => $shipping['tracking_no'] ?? '',
|
||||
'express_company' => $shipping['express_company'] ?? '',
|
||||
'item_desc' => $shipping['item_desc'],
|
||||
'contact' => $contact
|
||||
];
|
||||
}
|
||||
return $this->shipping($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 合单
|
||||
* @param string $out_trade_no
|
||||
* @param int $logistics_type
|
||||
* @param array $sub_orders
|
||||
* @param string $payer_openid
|
||||
* @param int $delivery_mode
|
||||
* @param bool $is_all_delivered
|
||||
* @return mixed
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws RedirectionExceptionInterface
|
||||
* @throws ServerExceptionInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function combinedShippingByTradeNo(string $out_trade_no, int $logistics_type, array $sub_orders, string $payer_openid, int $delivery_mode = 2, bool $is_all_delivered = false)
|
||||
{
|
||||
if (!$this->checkManaged()) {
|
||||
throw new WechatException('开通小程序订单管理服务后重试');
|
||||
}
|
||||
$params = [
|
||||
'order_key' => [
|
||||
'order_number_type' => 1,
|
||||
'mchid' => $this->config->mchId,
|
||||
'out_trade_no' => $out_trade_no,
|
||||
],
|
||||
'upload_time' => date(DATE_RFC3339),
|
||||
'payer' => [
|
||||
'openid' => $payer_openid
|
||||
]
|
||||
];
|
||||
|
||||
foreach ($sub_orders as $order) {
|
||||
$sub_order = [
|
||||
'order_key' => [
|
||||
'order_number_type' => 1,
|
||||
'mchid' => $this->config->mchId,
|
||||
'out_trade_no' => $order['out_trade_no'],
|
||||
'logistics_type' => $logistics_type,
|
||||
],
|
||||
'delivery_mode' => $delivery_mode,
|
||||
'is_all_delivered' => $is_all_delivered
|
||||
];
|
||||
foreach ($sub_orders['shipping_list'] as $shipping) {
|
||||
$contact = $this->handleContact($shipping['contact'] ?? []);
|
||||
$sub_order['shipping_list'][] = [
|
||||
'tracking_no' => $shipping['tracking_no'] ?? '',
|
||||
'express_company' => isset($shipping['express_company']) ? $this->getDelivery($shipping['express_company']) : '',
|
||||
'item_desc' => $shipping['item_desc'],
|
||||
'contact' => $contact
|
||||
];
|
||||
}
|
||||
$params['sub_orders'][] = $sub_order;
|
||||
}
|
||||
|
||||
return $this->resultHandle($this->api->postJson('wxa/sec/order/upload_combined_shipping_info', $params));
|
||||
}
|
||||
|
||||
/**
|
||||
* 签收通知
|
||||
* @param string $merchant_trade_no
|
||||
* @param string $received_time
|
||||
* @return mixed
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws RedirectionExceptionInterface
|
||||
* @throws ServerExceptionInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function notifyConfirmByTradeNo(string $merchant_trade_no, string $received_time)
|
||||
{
|
||||
$params = [
|
||||
'merchant_id' => $this->config->mchId,
|
||||
'merchant_trade_no' => $merchant_trade_no,
|
||||
'received_time' => $received_time
|
||||
];
|
||||
return $this->resultHandle($this->api->postJson('wxa/sec/order/notify_confirm_receive', $params));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置小修跳转路径
|
||||
* @param $path
|
||||
* @return mixed
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws RedirectionExceptionInterface
|
||||
* @throws ServerExceptionInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function setMesJumpPathAndCheck($path)
|
||||
{
|
||||
if (!$this->checkManaged()) {
|
||||
throw new WechatException('开通小程序订单管理服务后重试');
|
||||
}
|
||||
|
||||
$params = [
|
||||
'path' => $path
|
||||
];
|
||||
return $this->resultHandle($this->api->postJson('wxa/sec/order/set_msg_jump_path', $params));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $params
|
||||
* @return mixed
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws RedirectionExceptionInterface
|
||||
* @throws ServerExceptionInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function shipping($params)
|
||||
{
|
||||
return $this->resultHandle($this->api->postJson('wxa/sec/order/upload_shipping_info', $params));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return object|Redis|null
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
protected function getRedis()
|
||||
{
|
||||
if (empty($this->redis)) {
|
||||
$this->redis = Cache::store('redis')->handler();
|
||||
}
|
||||
return $this->redis;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询小程序是否已开通发货信息管理服务
|
||||
* @return mixed
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws RedirectionExceptionInterface
|
||||
* @throws ServerExceptionInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function isManaged()
|
||||
{
|
||||
$params = [
|
||||
'appid' => $this->miniProgramConfig->appId
|
||||
];
|
||||
return $this->resultHandle($this->api->postJson('wxa/sec/order/is_trade_managed', $params));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response|ResponseInterfaceAlias $result
|
||||
* @return mixed
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws RedirectionExceptionInterface
|
||||
* @throws ServerExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
private function resultHandle(Response|ResponseInterfaceAlias $result)
|
||||
{
|
||||
return new WechatResponse($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取运力id列表get_delivery_list
|
||||
* @return mixed
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws RedirectionExceptionInterface
|
||||
* @throws ServerExceptionInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function getDeliveryList()
|
||||
{
|
||||
return $this->resultHandle($this->api->postJson('cgi-bin/express/delivery/open_msg/get_delivery_list', []));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置物流列表
|
||||
* @return array
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws RedirectionExceptionInterface
|
||||
* @throws ServerExceptionInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function setDeliveryList()
|
||||
{
|
||||
$list = $this->getDeliveryList();
|
||||
if ($list) {
|
||||
$key = self::redis_prefix . '_delivery_list';
|
||||
$date = array_column($list['delivery_list'], 'delivery_id', 'delivery_name');
|
||||
// 创建缓存
|
||||
$this->getRedis()->hMSet($key, $date);
|
||||
|
||||
return $date;
|
||||
} else {
|
||||
throw new WechatException('物流公司列表异常');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function checkManaged()
|
||||
{
|
||||
$key = self::redis_prefix . '_is_trade_managed';
|
||||
if ($this->getRedis()->exists($key)) {
|
||||
return true;
|
||||
} else {
|
||||
return $this->setManaged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function setManaged()
|
||||
{
|
||||
try {
|
||||
$res = $this->isManaged();
|
||||
if ($res['is_trade_managed']) {
|
||||
$key = self::redis_prefix . '_is_trade_managed';
|
||||
$this->getRedis()->set($key, $res['is_trade_managed']);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (\Throwable) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取物流列表
|
||||
* @param $company_name
|
||||
* @return mixed|string
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws RedirectionExceptionInterface
|
||||
* @throws ServerExceptionInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function getDelivery($company_name)
|
||||
{
|
||||
$key = self::redis_prefix . '_delivery_list';
|
||||
if (!$this->getRedis()->exists($key)) {
|
||||
$date = $this->setDeliveryList();
|
||||
$express_company = $date[$company_name] ?? '';
|
||||
} else {
|
||||
$express_company = $this->getRedis()->hMGet($key, [$company_name])[$company_name] ?? '';
|
||||
}
|
||||
if (empty($express_company)) {
|
||||
$express_company = self::express_company;
|
||||
}
|
||||
|
||||
return $express_company;
|
||||
}
|
||||
}
|
||||
158
crmeb/services/wechat/client/miniprogram/SubscribeClient.php
Normal file
158
crmeb/services/wechat/client/miniprogram/SubscribeClient.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client\miniprogram;
|
||||
|
||||
|
||||
use crmeb\services\wechat\client\BaseClient;
|
||||
use crmeb\services\wechat\WechatException;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
|
||||
class SubscribeClient extends BaseClient
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $message = [
|
||||
'touser' => '',
|
||||
'template_id' => '',
|
||||
'page' => '',
|
||||
'data' => [],
|
||||
'miniprogram_state' => 'formal',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected array $required = ['touser', 'template_id', 'data'];
|
||||
|
||||
/**
|
||||
* 添加订阅消息模版
|
||||
* @param string $tid
|
||||
* @param array $kidList
|
||||
* @param string|null $sceneDesc
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function addTemplate(string $tid, array $kidList, string $sceneDesc = null): ResponseInterface|Response
|
||||
{
|
||||
$sceneDesc = $sceneDesc ?? '';
|
||||
$data = compact('tid', 'kidList', 'sceneDesc');
|
||||
|
||||
return $this->api->post('wxaapi/newtmpl/addtemplate', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除模板消息
|
||||
* @param string $id
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function deleteTemplate(string $id): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->post('wxaapi/newtmpl/deltemplate', ['priTmplId' => $id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模版标题的关键词列表
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
* @param string $tid
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public function getTemplateKeywords(string $tid): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->get('wxaapi/newtmpl/getpubtemplatekeywords', compact('tid'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
* @param array $data
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function send(array $data = []): ResponseInterface|Response
|
||||
{
|
||||
$params = $this->formatMessage($data);
|
||||
|
||||
$this->restoreMessage();
|
||||
|
||||
return $this->api->postJson('cgi-bin/message/subscribe/send', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return array
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
protected function formatMessage(array $data = []): array
|
||||
{
|
||||
$params = array_merge($this->message, $data);
|
||||
|
||||
foreach ($params as $key => $value) {
|
||||
if (in_array($key, $this->required, true) && empty($value) && empty($this->message[$key])) {
|
||||
throw new WechatException(sprintf('Attribute "%s" can not be empty!', $key));
|
||||
}
|
||||
|
||||
$params[$key] = empty($value) ? $this->message[$key] : $value;
|
||||
}
|
||||
|
||||
foreach ($params['data'] as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
if (\array_key_exists('value', $value)) {
|
||||
$params['data'][$key] = ['value' => $value['value']];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (count($value) >= 1) {
|
||||
$value = [
|
||||
'value' => $value[0],
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$value = [
|
||||
'value' => strval($value),
|
||||
];
|
||||
}
|
||||
|
||||
$params['data'][$key] = $value;
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore message.
|
||||
*/
|
||||
protected function restoreMessage()
|
||||
{
|
||||
$this->message = (new \ReflectionClass(static::class))->getDefaultProperties()['message'];
|
||||
}
|
||||
}
|
||||
226
crmeb/services/wechat/client/official/CardClient.php
Normal file
226
crmeb/services/wechat/client/official/CardClient.php
Normal file
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client\official;
|
||||
|
||||
|
||||
use crmeb\services\wechat\client\BaseClient;
|
||||
use EasyWeChat\Kernel\HttpClient\AccessTokenAwareClient;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
|
||||
/**
|
||||
* 卡片
|
||||
* Class CardClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/18
|
||||
* @package crmeb\services\wechat\client\official
|
||||
*/
|
||||
class CardClient extends BaseClient
|
||||
{
|
||||
const STATUS_CONTENT = 'CARD_STATUS_VERIFY_OK';
|
||||
|
||||
/**
|
||||
* 获取会员卡列表
|
||||
* @param int $offset
|
||||
* @param int $count
|
||||
* @param string $statusList
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function list(int $offset = 0, int $count = 10, string $statusList = self::STATUS_CONTENT): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'offset' => $offset,
|
||||
'count' => $count,
|
||||
'status_list' => $statusList,
|
||||
];
|
||||
|
||||
return $this->api->postJson('card/batchget', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取颜色
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function colors(): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->get('card/getcolors');
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建卡卷
|
||||
* @param string $cardType
|
||||
* @param array $attributes
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function create(string $cardType = 'member_card', array $attributes = []): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'card' => [
|
||||
'card_type' => strtoupper($cardType),
|
||||
strtolower($cardType) => $attributes,
|
||||
],
|
||||
];
|
||||
|
||||
return $this->api->postJson('card/create', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取卡卷信息
|
||||
* @param string $cardId
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function get(string $cardId): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'card_id' => $cardId,
|
||||
];
|
||||
|
||||
return $this->api->postJson('card/get', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改卡卷
|
||||
* @param string $cardId
|
||||
* @param string $type
|
||||
* @param array $attributes
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function update(string $cardId, string $type, array $attributes = []): ResponseInterface|Response
|
||||
{
|
||||
$card = [];
|
||||
$card['card_id'] = $cardId;
|
||||
$card[strtolower($type)] = $attributes;
|
||||
|
||||
return $this->api->postJson('card/update', $card);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除卡卷
|
||||
* @param string $cardId
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function delete(string $cardId): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'card_id' => $cardId,
|
||||
];
|
||||
|
||||
return $this->api->postJson('card/delete', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建二维码.
|
||||
* @param array $cards
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function createQrCode(array $cards): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->postJson('card/qrcode/create', $cards);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置开卡字段接口.
|
||||
* @param string $cardId
|
||||
* @param array $settings
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function setActivationForm(string $cardId, array $settings): ResponseInterface|Response
|
||||
{
|
||||
$params = array_merge(['card_id' => $cardId], $settings);
|
||||
|
||||
return $this->api->postJson('card/membercard/activateuserform/set', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 会员卡接口激活.
|
||||
* @param array $info
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function activate(array $info = []): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->postJson('card/membercard/activate', $info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拉取会员信息接口.
|
||||
* @param string $cardId
|
||||
* @param string $code
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function getUser(string $cardId, string $code): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'card_id' => $cardId,
|
||||
'code' => $code,
|
||||
];
|
||||
|
||||
return $this->api->postJson('card/membercard/userinfo/get', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新会员信息.
|
||||
* @param array $params
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function updateUser(array $params = []): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->postJson('card/membercard/updateuser', $params);
|
||||
}
|
||||
}
|
||||
98
crmeb/services/wechat/client/official/MaterialClient.php
Normal file
98
crmeb/services/wechat/client/official/MaterialClient.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client\official;
|
||||
|
||||
|
||||
use crmeb\services\wechat\client\BaseClient;
|
||||
use crmeb\services\wechat\WechatException;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
|
||||
/**
|
||||
* 永久素材上传-附件
|
||||
* Class MaterialClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/18
|
||||
* @package crmeb\services\wechat\client\official
|
||||
*/
|
||||
class MaterialClient extends BaseClient
|
||||
{
|
||||
|
||||
/**
|
||||
* 上传图片
|
||||
* @param string $path
|
||||
* @return Response
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function uploadImage(string $path): Response
|
||||
{
|
||||
return $this->upload('image', $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传音频
|
||||
* @param string $path
|
||||
* @return Response
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function uploadVoice(string $path): Response
|
||||
{
|
||||
return $this->upload('voice', $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
* @param string $type
|
||||
* @param string $path
|
||||
* @param array $form
|
||||
* @return Response
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function upload(string $type, string $path, array $form = []): Response
|
||||
{
|
||||
if (!file_exists($path) || !is_readable($path)) {
|
||||
throw new WechatException(sprintf('File does not exist, or the file is unreadable: "%s"', $path));
|
||||
}
|
||||
|
||||
$form['type'] = $type;
|
||||
|
||||
return $this->httpUpload($this->getApiByType($type), ['media' => $path], $form);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取接口
|
||||
* @param string $type
|
||||
* @return string
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function getApiByType(string $type): string
|
||||
{
|
||||
return match ($type) {
|
||||
'news_image' => 'cgi-bin/media/uploadimg',
|
||||
default => 'cgi-bin/material/add_material'
|
||||
};
|
||||
}
|
||||
}
|
||||
61
crmeb/services/wechat/client/official/MediaClient.php
Normal file
61
crmeb/services/wechat/client/official/MediaClient.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client\official;
|
||||
|
||||
|
||||
use crmeb\services\wechat\client\BaseClient;
|
||||
use crmeb\services\wechat\WechatException;
|
||||
|
||||
class MediaClient extends BaseClient
|
||||
{
|
||||
/**
|
||||
* Allow media type.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $allowTypes = ['image', 'voice', 'video', 'thumb'];
|
||||
|
||||
public function uploadImage($path)
|
||||
{
|
||||
return $this->upload('image', $path);
|
||||
}
|
||||
|
||||
public function uploadVideo($path)
|
||||
{
|
||||
return $this->upload('video', $path);
|
||||
}
|
||||
|
||||
public function uploadVoice($path)
|
||||
{
|
||||
return $this->upload('voice', $path);
|
||||
}
|
||||
|
||||
public function uploadThumb($path)
|
||||
{
|
||||
return $this->upload('thumb', $path);
|
||||
}
|
||||
|
||||
public function upload(string $type, string $path)
|
||||
{
|
||||
if (!file_exists($path) || !is_readable($path)) {
|
||||
throw new WechatException(sprintf("File does not exist, or the file is unreadable: '%s'", $path));
|
||||
}
|
||||
|
||||
if (!in_array($type, $this->allowTypes, true)) {
|
||||
throw new WechatException(sprintf("Unsupported media type: '%s'", $type));
|
||||
}
|
||||
|
||||
return $this->httpUpload('cgi-bin/media/upload', ['media' => $path], ['type' => $type]);
|
||||
}
|
||||
}
|
||||
131
crmeb/services/wechat/client/official/QrCodeClient.php
Normal file
131
crmeb/services/wechat/client/official/QrCodeClient.php
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client\official;
|
||||
|
||||
|
||||
use crmeb\services\wechat\client\BaseClient;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
|
||||
/**
|
||||
* 二维码
|
||||
* Class QrCodeClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/18
|
||||
* @package crmeb\services\wechat\client\official
|
||||
*/
|
||||
class QrCodeClient extends BaseClient
|
||||
{
|
||||
public const DAY = 86400;
|
||||
public const SCENE_MAX_VALUE = 100000;
|
||||
public const SCENE_QR_CARD = 'QR_CARD';
|
||||
public const SCENE_QR_TEMPORARY = 'QR_SCENE';
|
||||
public const SCENE_QR_TEMPORARY_STR = 'QR_STR_SCENE';
|
||||
public const SCENE_QR_FOREVER = 'QR_LIMIT_SCENE';
|
||||
public const SCENE_QR_FOREVER_STR = 'QR_LIMIT_STR_SCENE';
|
||||
|
||||
/**
|
||||
* 获取url
|
||||
* @param string $ticket
|
||||
* @return string
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function url(string $ticket): string
|
||||
{
|
||||
return sprintf('https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=%s', urlencode($ticket));
|
||||
}
|
||||
|
||||
/**
|
||||
* createForeverQrcode
|
||||
* @param string|int $sceneValue
|
||||
* @return array|mixed[]
|
||||
* @throws TransportExceptionInterface
|
||||
* @throws \EasyWeChat\Kernel\Exceptions\BadResponseException
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface
|
||||
*/
|
||||
public function forever(string|int $sceneValue): array
|
||||
{
|
||||
if (is_int($sceneValue) && $sceneValue > 0 && $sceneValue < self::SCENE_MAX_VALUE) {
|
||||
$type = self::SCENE_QR_FOREVER;
|
||||
$sceneKey = 'scene_id';
|
||||
} else {
|
||||
$type = self::SCENE_QR_FOREVER_STR;
|
||||
$sceneKey = 'scene_str';
|
||||
}
|
||||
$scene = [$sceneKey => $sceneValue];
|
||||
|
||||
return $this->create($type, $scene, false)->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 临时二维码生成
|
||||
* @param int|string $sceneValue
|
||||
* @param int|null $expireSeconds
|
||||
* @return array|mixed[]
|
||||
* @throws TransportExceptionInterface
|
||||
* @throws \EasyWeChat\Kernel\Exceptions\BadResponseException
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface
|
||||
*/
|
||||
public function temporary(int|string $sceneValue, ?int $expireSeconds = null): array
|
||||
{
|
||||
if (is_int($sceneValue) && $sceneValue > 0) {
|
||||
$type = self::SCENE_QR_TEMPORARY;
|
||||
$sceneKey = 'scene_id';
|
||||
} else {
|
||||
$type = self::SCENE_QR_TEMPORARY_STR;
|
||||
$sceneKey = 'scene_str';
|
||||
}
|
||||
$scene = [$sceneKey => $sceneValue];
|
||||
|
||||
return $this->create($type, $scene, true, $expireSeconds)->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建二维码
|
||||
* @param $actionName
|
||||
* @param $actionInfo
|
||||
* @param bool $temporary
|
||||
* @param null $expireSeconds
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
protected function create($actionName, $actionInfo, bool $temporary = true, $expireSeconds = null): ResponseInterface|Response
|
||||
{
|
||||
null !== $expireSeconds || $expireSeconds = 7 * self::DAY;
|
||||
|
||||
$params = [
|
||||
'action_name' => $actionName,
|
||||
'action_info' => ['scene' => $actionInfo],
|
||||
];
|
||||
|
||||
if ($temporary) {
|
||||
$params['expire_seconds'] = min($expireSeconds, 30 * self::DAY);
|
||||
}
|
||||
|
||||
return $this->api->postJson('cgi-bin/qrcode/create', $params);
|
||||
}
|
||||
}
|
||||
67
crmeb/services/wechat/client/official/StaffClient.php
Normal file
67
crmeb/services/wechat/client/official/StaffClient.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client\official;
|
||||
|
||||
|
||||
use crmeb\services\wechat\client\BaseClient;
|
||||
use crmeb\services\wechat\message\MessageInterface;
|
||||
use crmeb\services\wechat\message\Raw;
|
||||
use EasyWeChat\Kernel\HttpClient\AccessTokenAwareClient;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
|
||||
/**
|
||||
* 消息
|
||||
* Class StaffClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
* @package crmeb\services\wechat\client\offical
|
||||
*/
|
||||
class StaffClient extends BaseClient
|
||||
{
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
* @param MessageInterface|string $message
|
||||
* @param string $to
|
||||
* @param string $account
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function send(MessageInterface|string $message, string $to, string $account = ''): ResponseInterface|Response
|
||||
{
|
||||
if (empty($message)) {
|
||||
throw new \RuntimeException('No message to send.');
|
||||
}
|
||||
|
||||
if ($message instanceof Raw) {
|
||||
$message = json_decode($message->get('content'), true);
|
||||
} else {
|
||||
$prepends = [
|
||||
'touser' => $to,
|
||||
];
|
||||
if ($account) {
|
||||
$prepends['customservice'] = ['kf_account' => $account];
|
||||
}
|
||||
$message = $message->transformForJsonRequest($prepends);
|
||||
}
|
||||
|
||||
return $this->api->postJson('cgi-bin/message/custom/send', $message);
|
||||
}
|
||||
}
|
||||
215
crmeb/services/wechat/client/official/TemplateClient.php
Normal file
215
crmeb/services/wechat/client/official/TemplateClient.php
Normal file
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client\official;
|
||||
|
||||
|
||||
use crmeb\services\wechat\client\BaseClient;
|
||||
use crmeb\services\wechat\WechatException;
|
||||
use EasyWeChat\Kernel\HttpClient\AccessTokenAwareClient;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
|
||||
/**
|
||||
* 模板消息
|
||||
* Class TemplateClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/18
|
||||
* @package crmeb\services\wechat\client\official
|
||||
*/
|
||||
class TemplateClient extends BaseClient
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $message = [
|
||||
'touser' => '',
|
||||
'template_id' => '',
|
||||
'url' => '',
|
||||
'data' => [],
|
||||
'miniprogram' => '',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array|string[]
|
||||
*/
|
||||
protected array $required = ['touser', 'template_id'];
|
||||
|
||||
/**
|
||||
* 设置行业
|
||||
* @param string $industryOne
|
||||
* @param string $industryTwo
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function setIndustry(string $industryOne, string $industryTwo): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'industry_id1' => $industryOne,
|
||||
'industry_id2' => $industryTwo,
|
||||
];
|
||||
|
||||
return $this->api->postJson('cgi-bin/template/api_set_industry', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加模板消息
|
||||
* @param int $shortId
|
||||
* @param array $keywordList
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function addTemplate($shortId, $keywordList): ResponseInterface|Response
|
||||
{
|
||||
$params = ['template_id_short' => $shortId, 'keyword_name_list' => $keywordList];
|
||||
|
||||
return $this->api->postJson('cgi-bin/template/api_add_template', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模板消息
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function getPrivateTemplates(): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->postJson('cgi-bin/template/get_all_private_template');
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除模板
|
||||
* @param string $templateId
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function deletePrivateTemplate(string $templateId): ResponseInterface|Response
|
||||
{
|
||||
$params = ['template_id' => $templateId];
|
||||
|
||||
return $this->api->postJson('cgi-bin/template/del_private_template', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取行业
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function getIndustry()
|
||||
{
|
||||
return $this->api->postJson('cgi-bin/template/get_industry');
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送模板消息
|
||||
* @param array $data
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function send(array $data = [])
|
||||
{
|
||||
$params = $this->formatMessage($data);
|
||||
|
||||
$this->restoreMessage();
|
||||
|
||||
return $this->api->postJson('cgi-bin/message/template/send', $params);
|
||||
}
|
||||
|
||||
protected function restoreMessage()
|
||||
{
|
||||
$this->message = (new \ReflectionClass(static::class))->getDefaultProperties()['message'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取
|
||||
* @param array $data
|
||||
* @return array
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
protected function formatMessage(array $data = [])
|
||||
{
|
||||
$params = array_merge($this->message, $data);
|
||||
|
||||
foreach ($params as $key => $value) {
|
||||
if (in_array($key, $this->required, true) && empty($value) && empty($this->message[$key])) {
|
||||
throw new WechatException(sprintf('Attribute "%s" can not be empty!', $key));
|
||||
}
|
||||
|
||||
$params[$key] = empty($value) ? $this->message[$key] : $value;
|
||||
}
|
||||
|
||||
$params['data'] = $this->formatData($params['data'] ?? []);
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取data
|
||||
* @param array $data
|
||||
* @return array
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
protected function formatData(array $data)
|
||||
{
|
||||
$formatted = [];
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
if (\array_key_exists('value', $value)) {
|
||||
$formatted[$key] = $value;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (count($value) >= 2) {
|
||||
$value = [
|
||||
'value' => $value[0],
|
||||
'color' => $value[1],
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$value = [
|
||||
'value' => strval($value),
|
||||
];
|
||||
}
|
||||
|
||||
$formatted[$key] = $value;
|
||||
}
|
||||
|
||||
return $formatted;
|
||||
}
|
||||
}
|
||||
113
crmeb/services/wechat/client/official/UserClient.php
Normal file
113
crmeb/services/wechat/client/official/UserClient.php
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client\official;
|
||||
|
||||
use crmeb\services\wechat\client\BaseClient;
|
||||
use EasyWeChat\Kernel\HttpClient\AccessTokenAwareClient;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
|
||||
/**
|
||||
* 用户接口
|
||||
* Class UserClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
* @package crmeb\services\wechat\offical\user
|
||||
*/
|
||||
class UserClient extends BaseClient
|
||||
{
|
||||
/**
|
||||
* 获取单个用户
|
||||
* @param string $openid
|
||||
* @param string $lang
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function get(string $openid, string $lang = 'zh_CN'): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'openid' => $openid,
|
||||
'lang' => $lang,
|
||||
];
|
||||
|
||||
return $this->api->get('cgi-bin/user/info', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建菜单
|
||||
* @param array $buttons
|
||||
* @param array $matchRule
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function menuCreate(array $buttons, array $matchRule = []): ResponseInterface|Response
|
||||
{
|
||||
if (!empty($matchRule)) {
|
||||
return $this->api->postJson('cgi-bin/menu/addconditional', [
|
||||
'button' => $buttons,
|
||||
'matchrule' => $matchRule,
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->api->postJson('cgi-bin/menu/create', ['button' => $buttons]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量获取用户
|
||||
* @param string|null $nextOpenId
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function list(string $nextOpenId = null): ResponseInterface|Response
|
||||
{
|
||||
$params = ['next_openid' => $nextOpenId];
|
||||
|
||||
return $this->api->get('cgi-bin/user/get', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索用户
|
||||
* @param array $openids
|
||||
* @param string $lang
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function select(array $openids, string $lang = 'zh_CN'): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->postJson('cgi-bin/user/info/batchget', [
|
||||
'user_list' => array_map(function ($openid) use ($lang) {
|
||||
return [
|
||||
'openid' => $openid,
|
||||
'lang' => $lang,
|
||||
];
|
||||
}, $openids),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
339
crmeb/services/wechat/client/pay/OrderClient.php
Normal file
339
crmeb/services/wechat/client/pay/OrderClient.php
Normal file
@@ -0,0 +1,339 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client\pay;
|
||||
|
||||
use crmeb\services\wechat\util\Helper;
|
||||
use crmeb\services\wechat\util\XML;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
use think\helper\Arr;
|
||||
use EasyWeChat\Pay\Client;
|
||||
use EasyWeChat\Kernel\Contracts\Config;
|
||||
|
||||
/**
|
||||
* 创建订单
|
||||
* Class OrderClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/26
|
||||
* @package crmeb\services\wechat\client\pay
|
||||
*/
|
||||
class OrderClient
|
||||
{
|
||||
|
||||
/**
|
||||
* OrderClient constructor.
|
||||
* @param Client $api
|
||||
* @param Config $config
|
||||
*/
|
||||
public function __construct(protected Client $api, protected Config $config)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序创建订单
|
||||
* @param array $params
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function createorder(array $params): ResponseInterface|Response
|
||||
{
|
||||
$data = [
|
||||
'openid' => $params['openid'],//支付者的openid
|
||||
'combine_trade_no' => $params['out_trade_no'],//商家合单支付总交易单号
|
||||
'expire_time' => time() + 7000,
|
||||
'sub_orders' => [
|
||||
[
|
||||
'mchid' => $this->config->get('mch_id'),
|
||||
'amount' => (int)$params['total_fee'],
|
||||
'trade_no' => $params['out_trade_no'],
|
||||
'description' => $params['body'],
|
||||
]
|
||||
],
|
||||
];
|
||||
return $this->api->postJson('shop/pay/createorder', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序退款
|
||||
* @param array $params
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function refundorder(array $params): ResponseInterface|Response
|
||||
{
|
||||
$data = [
|
||||
'openid' => $params['openid'],
|
||||
'mchid' => $this->config->get('mch_id'),
|
||||
'trade_no' => $params['trade_no'],
|
||||
'transaction_id' => $params['transaction_id'],
|
||||
'refund_no' => $params['refund_no'],
|
||||
'total_amount' => (int)$params['total_amount'],
|
||||
'refund_amount' => (int)$params['refund_amount'],
|
||||
];
|
||||
return $this->api->postJson('shop/pay/refundorder', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 付款码支付
|
||||
* @param array $params
|
||||
* @return ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function pay(array $params): ResponseInterface
|
||||
{
|
||||
$params['appid'] = $this->config->get('app_id');
|
||||
|
||||
return $this->api->request('post', 'pay/micropay', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下单
|
||||
* @param array $params
|
||||
* @return ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function unify(array $params): ResponseInterface
|
||||
{
|
||||
if (empty($params['spbill_create_ip'])) {
|
||||
$params['spbill_create_ip'] = request()->ip();
|
||||
}
|
||||
|
||||
$params['notify_url'] = $params['notify_url'] ?? $this->config->get('notify_url');
|
||||
|
||||
return $this->request('pay/unifiedorder', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起请求
|
||||
* @param string $endpoint
|
||||
* @param array $params
|
||||
* @param string $method
|
||||
* @param array $options
|
||||
* @return Response
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
protected function request(string $endpoint, array $params = [], string $method = 'post', array $options = []): ResponseInterface
|
||||
{
|
||||
$base = [
|
||||
'appid' => $this->config->get('app_id'),
|
||||
'mch_id' => $this->config->get('mch_id'),
|
||||
'nonce_str' => uniqid(),
|
||||
];
|
||||
|
||||
$params = array_filter(array_filter(array_merge($base, [], $params)), 'strlen');
|
||||
|
||||
$secretKey = $this->config->get('v2_secret_key');
|
||||
|
||||
$encryptMethod = Helper::getEncryptMethod(Arr::get($params, 'sign_type', 'MD5'), $secretKey);
|
||||
|
||||
$params['sign'] = Helper::generateSign($params, $secretKey, $encryptMethod);
|
||||
|
||||
$options = array_merge([
|
||||
'body' => XML::build($params),
|
||||
], $options);
|
||||
|
||||
return $this->api->request(strtoupper($method), $endpoint, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 商户订单号退款
|
||||
* @param string $number
|
||||
* @param string $refundNumber
|
||||
* @param int $totalFee
|
||||
* @param int $refundFee
|
||||
* @param array $optional
|
||||
* @return Response
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function byOutTradeNumber(string $number, string $refundNumber, int $totalFee, int $refundFee, array $optional = []): Response
|
||||
{
|
||||
return $this->refund($refundNumber, $totalFee, $refundFee, array_merge($optional, ['out_trade_no' => $number]));
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付回执单号退款
|
||||
* @param string $transactionId
|
||||
* @param string $refundNumber
|
||||
* @param int $totalFee
|
||||
* @param int $refundFee
|
||||
* @param array $optional
|
||||
* @return Response
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function byTransactionId(string $transactionId, string $refundNumber, int $totalFee, int $refundFee, array $optional = []): Response
|
||||
{
|
||||
return $this->refund($refundNumber, $totalFee, $refundFee, array_merge($optional, ['transaction_id' => $transactionId]));
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款
|
||||
* @param string $refundNumber
|
||||
* @param int $totalFee
|
||||
* @param int $refundFee
|
||||
* @param array $optional
|
||||
* @return Response
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
protected function refund(string $refundNumber, int $totalFee, int $refundFee, array $optional = []): Response
|
||||
{
|
||||
$params = array_merge([
|
||||
'out_refund_no' => $refundNumber,
|
||||
'total_fee' => $totalFee,
|
||||
'refund_fee' => $refundFee,
|
||||
'appid' => $this->config->get('app_id'),
|
||||
], $optional);
|
||||
|
||||
return $this->safeRequest('secapi/pay/refund', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销订单
|
||||
* @param string $outTradeNumber
|
||||
* @return Response
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function reverseByOutTradeNumber(string $outTradeNumber): Response
|
||||
{
|
||||
return $this->reverse($outTradeNumber, 'out_trade_no');
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销订单
|
||||
* @param string $number
|
||||
* @param string $type
|
||||
* @return Response
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
protected function reverse(string $number, string $type): Response
|
||||
{
|
||||
$params = [
|
||||
'appid' => $this->config->get('app_id'),
|
||||
$type => $number,
|
||||
];
|
||||
|
||||
return $this->safeRequest('secapi/pay/reverse', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询退款订单
|
||||
* @param string $number
|
||||
* @return Response
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function queryByOutTradeNumber(string $number): Response
|
||||
{
|
||||
return $this->query([
|
||||
'out_trade_no' => $number,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询订单
|
||||
* @param array $params
|
||||
* @return Response
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
protected function query(array $params): Response
|
||||
{
|
||||
$params['appid'] = $this->config->get('app_id');
|
||||
|
||||
return $this->request('pay/orderquery', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 企业付款到零钱
|
||||
* @param array $params
|
||||
* @return Response
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function toBalance(array $params): Response
|
||||
{
|
||||
$base = [
|
||||
'mch_id' => null,
|
||||
'mchid' => $this->config->get('mch_id'),
|
||||
'mch_appid' => $this->config->get('app_id'),
|
||||
];
|
||||
|
||||
if (empty($params['spbill_create_ip'])) {
|
||||
$params['spbill_create_ip'] = request()->ip();
|
||||
}
|
||||
|
||||
return $this->safeRequest('mmpaymkttransfers/promotion/transfers', array_merge($base, $params));
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款请求
|
||||
* @param string $endpoint
|
||||
* @param array $params
|
||||
* @param string $method
|
||||
* @param array $options
|
||||
* @return Response
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
protected function safeRequest(string $endpoint, array $params, string $method = 'post', array $options = []): Response
|
||||
{
|
||||
$options = array_merge([
|
||||
'cert' => $this->config->get('certificate'),
|
||||
'ssl_key' => $this->config->get('private_key'),
|
||||
], $options);
|
||||
|
||||
return $this->request($endpoint, $params, $method, $options);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
133
crmeb/services/wechat/client/work/ContactWayClient.php
Normal file
133
crmeb/services/wechat/client/work/ContactWayClient.php
Normal file
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client\work;
|
||||
|
||||
|
||||
use crmeb\services\wechat\client\BaseClient;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
|
||||
/**
|
||||
* 联系客服
|
||||
* Class ContactWayClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/19
|
||||
* @package crmeb\services\wechat\client\work
|
||||
*/
|
||||
class ContactWayClient extends BaseClient
|
||||
{
|
||||
|
||||
/**
|
||||
* 配置客户联系「联系我」方式.
|
||||
* @param int $type
|
||||
* @param int $scene
|
||||
* @param array $config
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function create(int $type, int $scene, array $config = []): ResponseInterface|Response
|
||||
{
|
||||
$params = array_merge([
|
||||
'type' => $type,
|
||||
'scene' => $scene,
|
||||
], $config);
|
||||
|
||||
return $this->api->postJson('cgi-bin/externalcontact/add_contact_way', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新企业已配置的「联系我」方式.
|
||||
* @param string $configId
|
||||
* @param array $config
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function update(string $configId, array $config = []): ResponseInterface|Response
|
||||
{
|
||||
$params = array_merge([
|
||||
'config_id' => $configId,
|
||||
], $config);
|
||||
|
||||
return $this->api->postJson('cgi-bin/externalcontact/update_contact_way', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除企业已配置的「联系我」方式.
|
||||
* @param string $configId
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function delete(string $configId): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->postJson('cgi-bin/externalcontact/del_contact_way', [
|
||||
'config_id' => $configId,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取企业已配置的「联系我」列表,注意,该接口仅可获取2021年7月10日以后创建的「联系我」
|
||||
* @param string $cursor 分页查询使用的游标,为上次请求返回的 next_cursor
|
||||
* @param int $limit 每次查询的分页大小,默认为100条,最多支持1000条
|
||||
* @param int|null $startTime 「联系我」创建起始时间戳, 不传默认为90天前
|
||||
* @param int|null $endTime 「联系我」创建结束时间戳, 不传默认为当前时间
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function list(string $cursor = '', int $limit = 100, int $startTime = null, int $endTime = null): ResponseInterface|Response
|
||||
{
|
||||
$data = [
|
||||
'cursor' => $cursor,
|
||||
'limit' => $limit,
|
||||
];
|
||||
if ($startTime) {
|
||||
$data['start_time'] = $startTime;
|
||||
}
|
||||
if ($endTime) {
|
||||
$data['end_time'] = $endTime;
|
||||
}
|
||||
return $this->api->postJson('cgi-bin/externalcontact/list_contact_way', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束临时会话
|
||||
* @param string $userId 企业成员的user_id
|
||||
* @param string $externalUserId 客户的外部联系人user_id
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function closeTempChat(string $userId, string $externalUserId): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->postJson('cgi-bin/externalcontact/close_temp_chat', [
|
||||
'userid' => $userId,
|
||||
'external_userid' => $externalUserId
|
||||
]);
|
||||
}
|
||||
}
|
||||
101
crmeb/services/wechat/client/work/DepartmentClient.php
Normal file
101
crmeb/services/wechat/client/work/DepartmentClient.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client\work;
|
||||
|
||||
|
||||
use crmeb\services\wechat\client\BaseClient;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
|
||||
/**
|
||||
* 部门
|
||||
* Class DepartmentClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
* @package crmeb\services\wechat\client\work
|
||||
*/
|
||||
class DepartmentClient extends BaseClient
|
||||
{
|
||||
|
||||
/**
|
||||
* 获取部门信息
|
||||
* @param int $id
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function get(int $id): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->get('cgi-bin/department/get', compact('id'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门列表
|
||||
* @param int|null $id
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function list(?int $id = null): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->get('cgi-bin/department/list', compact('id'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取子部门ID列表
|
||||
* @param int|null $id
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function simpleList(?int $id = null): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->get('cgi-bin/department/simplelist', compact('id'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取成员ID列表
|
||||
* @param int $limit
|
||||
* @param string $cursor
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function getUserListIds(int $limit = 0, string $cursor = ''): ResponseInterface|Response
|
||||
{
|
||||
$data = [];
|
||||
|
||||
if ($limit) {
|
||||
$data['limit'] = $limit;
|
||||
}
|
||||
|
||||
if ($cursor) {
|
||||
$data['cursor'] = $cursor;
|
||||
}
|
||||
|
||||
return $this->api->postJson('cgi-bin/user/list_id', $data);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
222
crmeb/services/wechat/client/work/ExternalContactClient.php
Normal file
222
crmeb/services/wechat/client/work/ExternalContactClient.php
Normal file
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client\work;
|
||||
|
||||
|
||||
use crmeb\services\wechat\client\BaseClient;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
|
||||
/**
|
||||
* 外部联系
|
||||
* Class ExternalContactClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
* @package crmeb\services\wechat\client\work
|
||||
*/
|
||||
class ExternalContactClient extends BaseClient
|
||||
{
|
||||
|
||||
/**
|
||||
* 获取外部联系人详情
|
||||
* @param string $externalUserId
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function get(string $externalUserId): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->get('cgi-bin/externalcontact/get', [
|
||||
'external_userid' => $externalUserId,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量获取客户详情
|
||||
* @param array $userIdList
|
||||
* @param string $cursor
|
||||
* @param int $limit
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function batchGet(array $userIdList, string $cursor = '', int $limit = 100): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->postJson('cgi-bin/externalcontact/batch/get_by_user', [
|
||||
'userid_list' => $userIdList,
|
||||
'cursor' => $cursor,
|
||||
'limit' => $limit,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取企业标签库
|
||||
* @param array $tagIds
|
||||
* @param array $groupIds
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function getCorpTags(array $tagIds = [], array $groupIds = []): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'tag_id' => $tagIds,
|
||||
'group_id' => $groupIds
|
||||
];
|
||||
|
||||
return $this->api->postJson('cgi-bin/externalcontact/get_corp_tag_list', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加企业客户标签
|
||||
* @param array $params
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function addCorpTag(array $params): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->postJson('cgi-bin/externalcontact/add_corp_tag', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑企业客户标签
|
||||
* @param string $id
|
||||
* @param string|null $name
|
||||
* @param int|null $order
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function updateCorpTag(string $id, ?string $name = null, ?int $order = null): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
"id" => $id
|
||||
];
|
||||
|
||||
if (!\is_null($name)) {
|
||||
$params['name'] = $name;
|
||||
}
|
||||
|
||||
if (!\is_null($order)) {
|
||||
$params['order'] = $order;
|
||||
}
|
||||
|
||||
return $this->api->postJson('cgi-bin/externalcontact/edit_corp_tag', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除企业客户标签
|
||||
* @param array $tagId
|
||||
* @param array $groupId
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function deleteCorpTag(array $tagId, array $groupId): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
"tag_id" => $tagId,
|
||||
"group_id" => $groupId,
|
||||
];
|
||||
|
||||
return $this->api->postJson('cgi-bin/externalcontact/del_corp_tag', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑客户企业标签
|
||||
* @param array $params
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function markTags(array $params): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->postJson('cgi-bin/externalcontact/mark_tag', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户群列表
|
||||
* @param array $params
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function getGroupChats(array $params): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->postJson('cgi-bin/externalcontact/groupchat/list', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户群详情
|
||||
* @param string $chatId
|
||||
* @param int $needName
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function getGroupChat(string $chatId, int $needName = 0): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'chat_id' => $chatId,
|
||||
'need_name' => $needName,
|
||||
];
|
||||
|
||||
return $this->api->postJson('cgi-bin/externalcontact/groupchat/get', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取「群聊数据统计」数据. (按自然日聚合的方式)
|
||||
* @param int $dayBeginTime
|
||||
* @param int $dayEndTime
|
||||
* @param array $userIds
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function groupChatStatisticGroupByDay(int $dayBeginTime, int $dayEndTime, array $userIds = []): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'day_begin_time' => $dayBeginTime,
|
||||
'day_end_time' => $dayEndTime,
|
||||
'owner_filter' => [
|
||||
'userid_list' => $userIds
|
||||
]
|
||||
];
|
||||
return $this->api->postJson('cgi-bin/externalcontact/groupchat/statistic_group_by_day', $params);
|
||||
}
|
||||
|
||||
}
|
||||
121
crmeb/services/wechat/client/work/GroupChatClient.php
Normal file
121
crmeb/services/wechat/client/work/GroupChatClient.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client\work;
|
||||
|
||||
|
||||
use crmeb\services\wechat\client\BaseClient;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
|
||||
/**
|
||||
* 客户群聊配置
|
||||
* Class GroupChatClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
* @package crmeb\services\wechat\client\work
|
||||
*/
|
||||
class GroupChatClient extends BaseClient
|
||||
{
|
||||
/**
|
||||
* 更新客户群进群方式配置
|
||||
* @param string $configId
|
||||
* @param string $roomBaseName
|
||||
* @param array $chatIdList
|
||||
* @param string $state
|
||||
* @param int $autoCreateRoom
|
||||
* @param int $roomBaseId
|
||||
* @param string|null $remark
|
||||
* @param int $scene
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function updateJoinWay(string $configId, string $roomBaseName, array $chatIdList, string $state, int $autoCreateRoom = 1, int $roomBaseId = 1, string $remark = null, int $scene = 2): ResponseInterface|Response
|
||||
{
|
||||
$data = [
|
||||
'config_id' => $configId,
|
||||
'scene' => $scene,
|
||||
'remark' => $remark,
|
||||
'auto_create_room' => $autoCreateRoom,
|
||||
'room_base_name' => $roomBaseName,
|
||||
'room_base_id' => $roomBaseId,
|
||||
'chat_id_list' => $chatIdList,
|
||||
'state' => $state,
|
||||
];
|
||||
return $this->api->postJson('cgi-bin/externalcontact/groupchat/update_join_way', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置客户群进群方式
|
||||
* @param string $roomName
|
||||
* @param array $chatIdList
|
||||
* @param string $state
|
||||
* @param int $autoCreateRoom
|
||||
* @param int $roomBaseId
|
||||
* @param string|null $remark
|
||||
* @param int $scene
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function addJoinWay(string $roomName, array $chatIdList, string $state, int $autoCreateRoom = 1, int $roomBaseId = 1, string $remark = null, int $scene = 2): ResponseInterface|Response
|
||||
{
|
||||
$data = [
|
||||
'scene' => $scene,
|
||||
'remark' => $remark,
|
||||
'chat_id_list' => $chatIdList,
|
||||
'auto_create_room' => $autoCreateRoom,
|
||||
'room_base_name' => $roomName,
|
||||
'room_base_id' => $roomBaseId,
|
||||
'state' => $state
|
||||
];
|
||||
|
||||
return $this->api->postJson('cgi-bin/externalcontact/groupchat/add_join_way', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户群进群方式配置
|
||||
* @param string $configId
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function getJoinWay(string $configId): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->postJson('cgi-bin/externalcontact/groupchat/get_join_way', ['config_id' => $configId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除客户群进群方式配置
|
||||
* @param string $configId
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function deleteJoinWay(string $configId): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->postJson('cgi-bin/externalcontact/groupchat/del_join_way', ['config_id' => $configId]);
|
||||
}
|
||||
|
||||
}
|
||||
85
crmeb/services/wechat/client/work/MediaClient.php
Normal file
85
crmeb/services/wechat/client/work/MediaClient.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client\work;
|
||||
|
||||
|
||||
use crmeb\services\wechat\client\BaseClient;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
|
||||
/**
|
||||
* 附件管理
|
||||
* Class MediaClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
* @package crmeb\services\wechat\client\work
|
||||
*/
|
||||
class MediaClient extends BaseClient
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param string $path
|
||||
* @param array $form
|
||||
* @return Response
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function upload(string $type, string $path, array $form = []): Response
|
||||
{
|
||||
$files = [
|
||||
'media' => $path,
|
||||
];
|
||||
|
||||
return $this->httpUpload('cgi-bin/media/upload', $files, $form, compact('type'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传附件资源
|
||||
* @param string $path
|
||||
* @param string $mediaType
|
||||
* @param string $attachmentType
|
||||
* @return Response
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function uploadAttachment(string $path, string $mediaType, string $attachmentType): Response
|
||||
{
|
||||
$query = [
|
||||
'media_type' => $mediaType,
|
||||
'attachment_type' => $attachmentType,
|
||||
];
|
||||
|
||||
return $this->httpUpload('cgi-bin/media/upload_attachment', ['media' => $path], $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取临时素材
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
* @param string $mediaId
|
||||
* @return Response
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public function get(string $mediaId): Response
|
||||
{
|
||||
return $this->getResources($mediaId, 'cgi-bin/media/get');
|
||||
}
|
||||
}
|
||||
225
crmeb/services/wechat/client/work/MessageClient.php
Normal file
225
crmeb/services/wechat/client/work/MessageClient.php
Normal file
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client\work;
|
||||
|
||||
|
||||
use crmeb\services\wechat\client\BaseClient;
|
||||
use crmeb\services\wechat\WechatException;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
|
||||
/**
|
||||
* 消息发送
|
||||
* Class MessageClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/10/8
|
||||
* @package crmeb\services\wechat\client\work
|
||||
*/
|
||||
class MessageClient extends BaseClient
|
||||
{
|
||||
|
||||
/**
|
||||
* Required attributes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $required = ['content', 'title', 'url', 'pic_media_id', 'appid', 'page'];
|
||||
|
||||
/**
|
||||
* @var array|string[]
|
||||
*/
|
||||
protected array $textMessage = [
|
||||
'content' => '',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $imageMessage = [
|
||||
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array|string[]
|
||||
*/
|
||||
protected array $linkMessage = [
|
||||
'title' => '',
|
||||
'picurl' => '',
|
||||
'desc' => '',
|
||||
'url' => '',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array|string[]
|
||||
*/
|
||||
protected array $miniprogramMessage = [
|
||||
'title' => '',
|
||||
'pic_media_id' => '',
|
||||
'appid' => '',
|
||||
'page' => '',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param array $message
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function send(array $message): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->postJson('cgi-bin/message/send', $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加企业群发消息模板
|
||||
* @param array $msg
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function submit(array $msg): ResponseInterface|Response
|
||||
{
|
||||
$params = $this->formatMessage($msg);
|
||||
|
||||
return $this->api->postJson('cgi-bin/externalcontact/add_msg_template', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return array
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/26
|
||||
*/
|
||||
protected function formatMessage(array $data = [])
|
||||
{
|
||||
$params = $data;
|
||||
|
||||
if (!empty($params['text'])) {
|
||||
$params['text'] = $this->formatFields($params['text'], $this->textMessage);
|
||||
}
|
||||
|
||||
if (!empty($params['image'])) {
|
||||
$params['image'] = $this->formatFields($params['image'], $this->imageMessage);
|
||||
}
|
||||
|
||||
if (!empty($params['link'])) {
|
||||
$params['link'] = $this->formatFields($params['link'], $this->linkMessage);
|
||||
}
|
||||
|
||||
if (!empty($params['miniprogram'])) {
|
||||
$params['miniprogram'] = $this->formatFields($params['miniprogram'], $this->miniprogramMessage);
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @param array $default
|
||||
* @return array
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/26
|
||||
*/
|
||||
protected function formatFields(array $data = [], array $default = [])
|
||||
{
|
||||
$params = array_merge($default, $data);
|
||||
foreach ($params as $key => $value) {
|
||||
if (in_array($key, $this->required, true) && empty($value) && empty($default[$key])) {
|
||||
throw new WechatException(sprintf('Attribute "%s" can not be empty!', $key));
|
||||
}
|
||||
|
||||
$params[$key] = empty($value) ? $default[$key] : $value;
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $welcomeCode
|
||||
* @param array $msg
|
||||
* @return ResponseInterface|Response
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/26
|
||||
*/
|
||||
public function sendWelcome(string $welcomeCode, array $msg): ResponseInterface|Response
|
||||
{
|
||||
$formattedMsg = $this->formatMessage($msg);
|
||||
|
||||
$params = array_merge($formattedMsg, [
|
||||
'welcome_code' => $welcomeCode,
|
||||
]);
|
||||
|
||||
return $this->api->postJson('cgi-bin/externalcontact/send_welcome_msg', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群发成员发送任务列表
|
||||
* @param string $msgId 群发消息的id,通过获取群发记录列表接口返回
|
||||
* @param int|null $limit 返回的最大记录数,整型,最大值1000,默认值500,超过最大值时取默认值
|
||||
* @param string|null $cursor 用于分页查询的游标,字符串类型,由上一次调用返回,首次调用可不填
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function getGroupmsgTask(string $msgId, ?int $limit = null, ?string $cursor = null): ResponseInterface|Response
|
||||
{
|
||||
$data = [
|
||||
'msgid' => $msgId,
|
||||
'limit' => $limit,
|
||||
'cursor' => $cursor,
|
||||
];
|
||||
$writableData = array_filter($data, function (string $key) use ($data) {
|
||||
return !is_null($data[$key]);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
return $this->api->postJson('cgi-bin/externalcontact/get_groupmsg_task', $writableData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取企业群发成员执行结果
|
||||
* @param string $msgId 群发消息的id,通过获取群发记录列表接口返回
|
||||
* @param string $userid 发送成员userid,通过获取群发成员发送任务列表接口返回
|
||||
* @param int|null $limit 返回的最大记录数,整型,最大值1000,默认值500,超过最大值时取默认值
|
||||
* @param string|null $cursor 用于分页查询的游标,字符串类型,由上一次调用返回,首次调用可不填
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function getGroupmsgSendResult(string $msgId, string $userid, ?int $limit = null, ?string $cursor = null): ResponseInterface|Response
|
||||
{
|
||||
$data = [
|
||||
'msgid' => $msgId,
|
||||
'userid' => $userid,
|
||||
'limit' => $limit,
|
||||
'cursor' => $cursor,
|
||||
];
|
||||
$writableData = array_filter($data, function (string $key) use ($data) {
|
||||
return !is_null($data[$key]);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
return $this->api->postJson('cgi-bin/externalcontact/get_groupmsg_send_result', $writableData);
|
||||
}
|
||||
}
|
||||
164
crmeb/services/wechat/client/work/MomentClient.php
Normal file
164
crmeb/services/wechat/client/work/MomentClient.php
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client\work;
|
||||
|
||||
|
||||
use crmeb\services\wechat\client\BaseClient;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
|
||||
/**
|
||||
* 朋友圈
|
||||
* Class MomentClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
* @package crmeb\services\wechat\client\work
|
||||
*/
|
||||
class MomentClient extends BaseClient
|
||||
{
|
||||
|
||||
/**
|
||||
* 创建发表任务
|
||||
* @param array $param
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function createTask(array $param): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->postJson('cgi-bin/externalcontact/add_moment_task', $param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务创建结果
|
||||
* @param string $jobId
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function getTask(string $jobId): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->get('cgi-bin/externalcontact/get_moment_task_result', ['jobid' => $jobId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取企业全部的发表列表
|
||||
* @param array $params
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function list(array $params): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->postJson('cgi-bin/externalcontact/get_moment_list', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户朋友圈企业发表的列表
|
||||
* @param string $momentId
|
||||
* @param string $cursor
|
||||
* @param int $limit
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function getTasks(string $momentId, string $cursor = '', int $limit = 500): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'moment_id' => $momentId,
|
||||
'cursor' => $cursor,
|
||||
'limit' => $limit
|
||||
];
|
||||
|
||||
return $this->api->postJson('cgi-bin/externalcontact/get_moment_task', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户朋友圈发表时选择的可见范围
|
||||
* @param string $momentId
|
||||
* @param string $userId
|
||||
* @param string $cursor
|
||||
* @param int $limit
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function getCustomers(string $momentId, string $userId, string $cursor, int $limit): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'moment_id' => $momentId,
|
||||
'userid' => $userId,
|
||||
'cursor' => $cursor,
|
||||
'limit' => $limit
|
||||
];
|
||||
|
||||
return $this->api->postJson('cgi-bin/externalcontact/get_moment_customer_list', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户朋友圈发表后的可见客户列表
|
||||
* @param string $momentId
|
||||
* @param string $userId
|
||||
* @param string $cursor
|
||||
* @param int $limit
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function getSendResult(string $momentId, string $userId, string $cursor, int $limit): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'moment_id' => $momentId,
|
||||
'userid' => $userId,
|
||||
'cursor' => $cursor,
|
||||
'limit' => $limit
|
||||
];
|
||||
|
||||
return $this->api->postJson('cgi-bin/externalcontact/get_moment_send_result', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户朋友圈的互动数据
|
||||
* @param string $momentId
|
||||
* @param string $userId
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function getComments(string $momentId, string $userId): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'moment_id' => $momentId,
|
||||
'userid' => $userId
|
||||
];
|
||||
|
||||
return $this->api->postJson('cgi-bin/externalcontact/get_moment_comments', $params);
|
||||
}
|
||||
}
|
||||
86
crmeb/services/wechat/client/work/UserClient.php
Normal file
86
crmeb/services/wechat/client/work/UserClient.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\client\work;
|
||||
|
||||
|
||||
use crmeb\services\wechat\client\BaseClient;
|
||||
use EasyWeChat\Kernel\HttpClient\Response;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
|
||||
/**
|
||||
* 用户
|
||||
* Class UserClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/18
|
||||
* @package crmeb\services\wechat\client\work
|
||||
*/
|
||||
class UserClient extends BaseClient
|
||||
{
|
||||
|
||||
/**
|
||||
* 获取部门成员详细信息
|
||||
* @param int $departmentId
|
||||
* @param bool $fetchChild
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function getDetailedDepartmentUsers(int $departmentId, bool $fetchChild = false): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'department_id' => $departmentId,
|
||||
'fetch_child' => (int)$fetchChild,
|
||||
];
|
||||
|
||||
return $this->api->get('cgi-bin/user/list', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取通讯录成员详情
|
||||
* @param string $userId
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function get(string $userId): ResponseInterface|Response
|
||||
{
|
||||
return $this->api->get('cgi-bin/user/get', ['userid' => $userId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* userid转openid
|
||||
* @param string $userId
|
||||
* @param int|null $agentId
|
||||
* @return Response|ResponseInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public function userIdToOpenid(string $userId, int $agentId = null): ResponseInterface|Response
|
||||
{
|
||||
$params = [
|
||||
'userid' => $userId,
|
||||
'agentid' => $agentId,
|
||||
];
|
||||
|
||||
return $this->api->postJson('cgi-bin/user/convert_to_openid', $params);
|
||||
}
|
||||
}
|
||||
79
crmeb/services/wechat/config/HttpCommonConfig.php
Normal file
79
crmeb/services/wechat/config/HttpCommonConfig.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace crmeb\services\wechat\config;
|
||||
|
||||
use crmeb\services\wechat\contract\ConfigHandlerInterface;
|
||||
use crmeb\services\wechat\contract\ServeConfigInterface;
|
||||
use crmeb\services\wechat\DefaultConfig;
|
||||
|
||||
/**
|
||||
* Http请求配置
|
||||
* Class HttpCommonConfig
|
||||
* @package crmeb\services\wechat\config
|
||||
*/
|
||||
class HttpCommonConfig implements ConfigHandlerInterface
|
||||
{
|
||||
/**
|
||||
* @var bool[]
|
||||
*/
|
||||
protected array $config = [
|
||||
'verify' => false,
|
||||
'timeout' => 5
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $serve;
|
||||
|
||||
/**
|
||||
* @param string $serve
|
||||
* @return $this
|
||||
*/
|
||||
public function setServe(string $serve): static
|
||||
{
|
||||
$this->serve = $serve;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务端实例
|
||||
* @return ServeConfigInterface
|
||||
*/
|
||||
public function getServe()
|
||||
{
|
||||
return app()->make($this->serve);
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接获取配置
|
||||
* @param string $key
|
||||
* @param null $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConfig(string $key, $default = null)
|
||||
{
|
||||
if ($value = DefaultConfig::value($key)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return $this->getServe()->getConfig(DefaultConfig::key($key), $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function all(): array
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
}
|
||||
22
crmeb/services/wechat/config/MicroMerchantConfig.php
Normal file
22
crmeb/services/wechat/config/MicroMerchantConfig.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace crmeb\services\wechat\config;
|
||||
|
||||
/**
|
||||
* 小微商户
|
||||
* Class MicroMerchantConfig
|
||||
* @package crmeb\services\wechat\config
|
||||
*/
|
||||
class MicroMerchantConfig
|
||||
{
|
||||
|
||||
}
|
||||
127
crmeb/services/wechat/config/MiniProgramConfig.php
Normal file
127
crmeb/services/wechat/config/MiniProgramConfig.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace crmeb\services\wechat\config;
|
||||
|
||||
use crmeb\services\wechat\contract\ConfigHandlerInterface;
|
||||
use crmeb\services\wechat\DefaultConfig;
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
|
||||
/**
|
||||
* 小程序配置
|
||||
* Class MiniProgramConfig
|
||||
* @package crmeb\services\wechat\config
|
||||
*/
|
||||
class MiniProgramConfig implements ConfigHandlerInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* APPid
|
||||
* @var string
|
||||
*/
|
||||
public string $appId;
|
||||
|
||||
/**
|
||||
* APPsecret
|
||||
* @var string
|
||||
*/
|
||||
public string $secret;
|
||||
|
||||
/**
|
||||
* Token
|
||||
* @var string
|
||||
*/
|
||||
public string $token;
|
||||
|
||||
/**
|
||||
* EncodingAESKey
|
||||
* @var string
|
||||
*/
|
||||
public string $aesKey;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $responseType = 'array';
|
||||
|
||||
/**
|
||||
* http配置
|
||||
* @var HttpCommonConfig
|
||||
*/
|
||||
protected HttpCommonConfig $httpConfig;
|
||||
|
||||
/**
|
||||
* 是否初始化过
|
||||
* @var bool
|
||||
*/
|
||||
protected bool $init = false;
|
||||
|
||||
/**
|
||||
* MiniProgramConfig constructor.
|
||||
* @param HttpCommonConfig $commonConfig
|
||||
*/
|
||||
public function __construct(HttpCommonConfig $commonConfig)
|
||||
{
|
||||
$this->httpConfig = $commonConfig;
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
if ($this->init) {
|
||||
return;
|
||||
}
|
||||
$this->init = true;
|
||||
$this->appId = $this->httpConfig->getConfig(DefaultConfig::MINI_APPID, '');
|
||||
$this->secret = $this->httpConfig->getConfig('mini.secret', '');
|
||||
$this->token = $this->httpConfig->getConfig('mini.token', '');
|
||||
$this->aesKey = $this->httpConfig->getConfig('mini.key', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
* @param string $key
|
||||
* @param null $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConfig(string $key, $default = null)
|
||||
{
|
||||
return $this->httpConfig->getConfig($key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* 全部
|
||||
* @return array
|
||||
*/
|
||||
#[ArrayShape([
|
||||
'app_id' => "string",
|
||||
'secret' => "string",
|
||||
'response_type' => "string",
|
||||
'log' => "array",
|
||||
'http' => "bool[]"
|
||||
])]
|
||||
#[Pure]
|
||||
public function all(): array
|
||||
{
|
||||
return [
|
||||
'app_id' => $this->appId,
|
||||
'secret' => $this->secret,
|
||||
'token' => $this->token,
|
||||
'aes_key' => $this->aesKey,
|
||||
'response_type' => $this->responseType,
|
||||
'http' => $this->httpConfig->all()
|
||||
];
|
||||
}
|
||||
}
|
||||
116
crmeb/services/wechat/config/OfficialAccountConfig.php
Normal file
116
crmeb/services/wechat/config/OfficialAccountConfig.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace crmeb\services\wechat\config;
|
||||
|
||||
use crmeb\services\wechat\contract\ConfigHandlerInterface;
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
|
||||
/**
|
||||
* 公众号配置
|
||||
* Class OfficialAccountConfig
|
||||
* @package crmeb\services\wechat\config
|
||||
*/
|
||||
class OfficialAccountConfig implements ConfigHandlerInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* AppID
|
||||
* @var string
|
||||
*/
|
||||
public string $appId;
|
||||
|
||||
/**
|
||||
* AppSecret
|
||||
* @var string
|
||||
*/
|
||||
public string $secret;
|
||||
|
||||
/**
|
||||
* Token
|
||||
* @var string
|
||||
*/
|
||||
public string $token;
|
||||
|
||||
/**
|
||||
* EncodingAESKey
|
||||
* @var string
|
||||
*/
|
||||
public string $aesKey;
|
||||
|
||||
/**
|
||||
* 指定 API 调用返回结果的类型
|
||||
* @var string
|
||||
*/
|
||||
protected string $responseType = 'array';
|
||||
|
||||
/**
|
||||
* @var HttpCommonConfig
|
||||
*/
|
||||
protected HttpCommonConfig $httpConfig;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected bool $init = false;
|
||||
|
||||
/**
|
||||
* OfficialAccountConfig constructor.
|
||||
* @param HttpCommonConfig $commonConfig
|
||||
*/
|
||||
public function __construct(HttpCommonConfig $commonConfig)
|
||||
{
|
||||
$this->httpConfig = $commonConfig;
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
if ($this->init) {
|
||||
return;
|
||||
}
|
||||
$this->init = true;
|
||||
$this->appId = $this->httpConfig->getConfig('official.appid', '');
|
||||
$this->secret = $this->httpConfig->getConfig('official.secret', '');
|
||||
$this->token = $this->httpConfig->getConfig('official.token', '');
|
||||
$this->aesKey = ($this->httpConfig->getConfig('official.encode', -1) > 0 ? $this->httpConfig->getConfig('official.key', '') : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有配置
|
||||
* @return array
|
||||
*/
|
||||
#[ArrayShape([
|
||||
'app_id' => "string",
|
||||
'secret' => "string",
|
||||
'token' => "string",
|
||||
'aes_key' => "string",
|
||||
'response_type' => "string",
|
||||
'log' => "array",
|
||||
'http' => "bool[]"
|
||||
])]
|
||||
#[Pure]
|
||||
public function all(): array
|
||||
{
|
||||
return [
|
||||
'app_id' => $this->appId,
|
||||
'secret' => $this->secret,
|
||||
'token' => $this->token,
|
||||
'aes_key' => $this->aesKey,
|
||||
'response_type' => $this->responseType,
|
||||
'http' => $this->httpConfig->all()
|
||||
];
|
||||
}
|
||||
}
|
||||
36
crmeb/services/wechat/config/OpenAppConfig.php
Normal file
36
crmeb/services/wechat/config/OpenAppConfig.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace crmeb\services\wechat\config;
|
||||
|
||||
/**
|
||||
* 开放平台APP配置
|
||||
* Class OpenAppConfig
|
||||
* @package crmeb\services\wechat\config
|
||||
*/
|
||||
class OpenAppConfig extends OpenWebConfig
|
||||
{
|
||||
|
||||
/**
|
||||
* OpenAppConfig constructor.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
if ($this->init) {
|
||||
return;
|
||||
}
|
||||
$this->init = true;
|
||||
$this->appId = $this->config->getConfig('app.appid', '');
|
||||
$this->secret = $this->config->getConfig('app.secret', '');
|
||||
$this->token = $this->config->getConfig('app.token', '');
|
||||
$this->aesKey = $this->config->getConfig('app.key', '');
|
||||
}
|
||||
}
|
||||
116
crmeb/services/wechat/config/OpenWebConfig.php
Normal file
116
crmeb/services/wechat/config/OpenWebConfig.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace crmeb\services\wechat\config;
|
||||
|
||||
|
||||
use crmeb\services\wechat\contract\ConfigHandlerInterface;
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
|
||||
/**
|
||||
* 开放平台网页端配置
|
||||
* Class OpenWebConfig
|
||||
* @package crmeb\services\wechat\config
|
||||
*/
|
||||
class OpenWebConfig implements ConfigHandlerInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Appid
|
||||
* @var string
|
||||
*/
|
||||
public string $appId;
|
||||
|
||||
/**
|
||||
* Appsecret
|
||||
* @var string
|
||||
*/
|
||||
public string $secret;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public string $token;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public string $aesKey;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected bool $init = false;
|
||||
|
||||
/**
|
||||
* @var HttpCommonConfig
|
||||
*/
|
||||
protected HttpCommonConfig $config;
|
||||
|
||||
/**
|
||||
* OpenWebConfig constructor.
|
||||
* @param HttpCommonConfig $config
|
||||
*/
|
||||
public function __construct(HttpCommonConfig $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* OpenWebConfig constructor.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
if ($this->init) {
|
||||
return;
|
||||
}
|
||||
$this->init = true;
|
||||
$this->appId = $this->config->getConfig('web.appid', '');
|
||||
$this->secret = $this->config->getConfig('web.secret', '');
|
||||
$this->token = $this->config->getConfig('web.token', '');
|
||||
$this->aesKey = $this->config->getConfig('web.key', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
* @param string $key
|
||||
* @param null $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConfig(string $key, $default = null)
|
||||
{
|
||||
return $this->config->getConfig($key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
#[ArrayShape([
|
||||
'app_id' => "string",
|
||||
'secret' => "string",
|
||||
'token' => "string",
|
||||
'aes_key' => "string",
|
||||
'http' => "bool[]"
|
||||
])]
|
||||
#[Pure]
|
||||
public function all(): array
|
||||
{
|
||||
return [
|
||||
'app_id' => $this->appId,
|
||||
'secret' => $this->secret,
|
||||
'token' => $this->token,
|
||||
'aes_key' => $this->aesKey,
|
||||
'http' => $this->config->all()
|
||||
];
|
||||
}
|
||||
}
|
||||
159
crmeb/services/wechat/config/PaymentConfig.php
Normal file
159
crmeb/services/wechat/config/PaymentConfig.php
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace crmeb\services\wechat\config;
|
||||
|
||||
use crmeb\services\wechat\contract\ConfigHandlerInterface;
|
||||
use crmeb\services\wechat\DefaultConfig;
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
|
||||
/**
|
||||
* 支付配置
|
||||
* Class PaymentConfig
|
||||
* @package crmeb\services\wechat\config
|
||||
*/
|
||||
class PaymentConfig implements ConfigHandlerInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* appid
|
||||
* @var string
|
||||
*/
|
||||
public string $appId;
|
||||
|
||||
/**
|
||||
* 商户密钥
|
||||
* @var string
|
||||
*/
|
||||
public string $mchId;
|
||||
|
||||
/**
|
||||
* 小程序商户号
|
||||
* @var string
|
||||
*/
|
||||
public string $routineMchId;
|
||||
|
||||
/**
|
||||
* API密钥
|
||||
* @var string
|
||||
*/
|
||||
public string $key;
|
||||
|
||||
/**
|
||||
* 证书cert
|
||||
* @var string
|
||||
*/
|
||||
public string $certPath;
|
||||
|
||||
/**
|
||||
* 证书key
|
||||
* @var string
|
||||
*/
|
||||
public string $keyPath;
|
||||
|
||||
/**
|
||||
* 支付异步回调地址
|
||||
* @var string
|
||||
*/
|
||||
public string $notifyUrl;
|
||||
|
||||
/**
|
||||
* 退款异步通知
|
||||
* @var string
|
||||
*/
|
||||
public string $refundUrl;
|
||||
|
||||
/**
|
||||
* @var HttpCommonConfig
|
||||
*/
|
||||
protected HttpCommonConfig $httpConfig;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected bool $init = false;
|
||||
|
||||
/**
|
||||
* PaymentConfig constructor.
|
||||
* @param HttpCommonConfig $commonConfig
|
||||
*/
|
||||
public function __construct(HttpCommonConfig $commonConfig)
|
||||
{
|
||||
$this->httpConfig = $commonConfig;
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/18
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
if ($this->init) {
|
||||
return;
|
||||
}
|
||||
$this->init = true;
|
||||
$this->appId = $this->httpConfig->getConfig(DefaultConfig::OFFICIAL_APPID, '');
|
||||
$this->mchId = $this->httpConfig->getConfig(DefaultConfig::PAY_MCHID, '');
|
||||
$this->routineMchId = $this->httpConfig->getConfig('pay.routine_mchid', '');
|
||||
$this->key = $this->httpConfig->getConfig('pay.key', '');
|
||||
|
||||
$certPath = env('RECEPTACLE_ENABLE', false) ? env('RECEPTACLE_PAYCERT', '') : $this->httpConfig->getConfig('pay.client_cert', '');
|
||||
$keyPath = env('RECEPTACLE_ENABLE', false) ? env('RECEPTACLE_PAYKEY', '') : $this->httpConfig->getConfig('pay.client_key', '');
|
||||
|
||||
$this->certPath = str_replace('//', '/', public_path() . $certPath);
|
||||
$this->keyPath = str_replace('//', '/', public_path() . $keyPath);
|
||||
$this->notifyUrl = trim($this->httpConfig->getConfig(DefaultConfig::COMMENT_URL)) . DefaultConfig::value('pay.notifyUrl');
|
||||
$this->refundUrl = trim($this->httpConfig->getConfig(DefaultConfig::COMMENT_URL)) . DefaultConfig::value('pay.refundUrl');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
* @param string $key
|
||||
* @param null $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConfig(string $key, $default = null)
|
||||
{
|
||||
return $this->httpConfig->getConfig($key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* 全部配置
|
||||
* @return array
|
||||
*/
|
||||
#[Pure]
|
||||
#[ArrayShape([
|
||||
'app_id' => "string",
|
||||
'mch_id' => "string",
|
||||
'v2_secret_key' => "string",
|
||||
'private_key' => "string",
|
||||
'certificate' => "string",
|
||||
'notify_url' => "string",
|
||||
'log' => "array",
|
||||
'http' => "bool[]"
|
||||
])]
|
||||
public function all(): array
|
||||
{
|
||||
return [
|
||||
'app_id' => $this->appId,
|
||||
'mch_id' => $this->mchId,
|
||||
'v2_secret_key' => $this->key,
|
||||
'private_key' => $this->keyPath,
|
||||
'certificate' => $this->certPath,
|
||||
'notify_url' => $this->notifyUrl,
|
||||
'http' => $this->httpConfig->all()
|
||||
];
|
||||
}
|
||||
}
|
||||
201
crmeb/services/wechat/config/V3PaymentConfig.php
Normal file
201
crmeb/services/wechat/config/V3PaymentConfig.php
Normal file
@@ -0,0 +1,201 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\config;
|
||||
|
||||
|
||||
use crmeb\services\wechat\contract\ConfigHandlerInterface;
|
||||
use crmeb\services\wechat\DefaultConfig;
|
||||
|
||||
/**
|
||||
* Class V3PaymentConfig
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2022/9/30
|
||||
* @package crmeb\services\wechat\config
|
||||
*/
|
||||
class V3PaymentConfig implements ConfigHandlerInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* appid
|
||||
* @var string
|
||||
*/
|
||||
public string $appId;
|
||||
|
||||
/**
|
||||
* 商户密钥
|
||||
* @var string
|
||||
*/
|
||||
public string $mchId;
|
||||
|
||||
/**
|
||||
* API密钥
|
||||
* @var string
|
||||
*/
|
||||
public string $key;
|
||||
|
||||
/**
|
||||
* 证书序列号
|
||||
* @var string
|
||||
*/
|
||||
public string $serialNo;
|
||||
|
||||
/**
|
||||
* 证书cert
|
||||
* @var string
|
||||
*/
|
||||
public string $certPath;
|
||||
|
||||
/**
|
||||
* 证书key
|
||||
* @var string
|
||||
*/
|
||||
public string $keyPath;
|
||||
|
||||
/**
|
||||
* 支付异步回调地址
|
||||
* @var string
|
||||
*/
|
||||
public string $notifyUrl;
|
||||
|
||||
/**
|
||||
* 退款异步通知
|
||||
* @var string
|
||||
*/
|
||||
public string $refundUrl;
|
||||
|
||||
/**
|
||||
* 小程序appid
|
||||
* @var string
|
||||
*/
|
||||
public string $miniprogAppid;
|
||||
|
||||
/**
|
||||
* 应用appid
|
||||
* @var string
|
||||
*/
|
||||
public string $appAppid;
|
||||
|
||||
/**
|
||||
* 微信公众号appid
|
||||
* @var string
|
||||
*/
|
||||
public string $wechatAppid;
|
||||
|
||||
/**
|
||||
* v3支付公钥
|
||||
* @var string
|
||||
*/
|
||||
public string $publicKey;
|
||||
|
||||
/**
|
||||
* v3支付公钥证书
|
||||
* @var string
|
||||
*/
|
||||
public string $publicPem;
|
||||
|
||||
/**
|
||||
* web appid
|
||||
* @var string
|
||||
*/
|
||||
public string $webAppid;
|
||||
|
||||
/**
|
||||
* 是否v3支付
|
||||
* @var bool
|
||||
*/
|
||||
public bool $isV3PAy = true;
|
||||
|
||||
/**
|
||||
* @var HttpCommonConfig
|
||||
*/
|
||||
protected HttpCommonConfig $httpConfig;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected bool $init = false;
|
||||
|
||||
/**
|
||||
* V3PaymentConfig constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->httpConfig = app()->make(HttpCommonConfig::class);
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
if ($this->init) {
|
||||
return;
|
||||
}
|
||||
$this->init = true;
|
||||
$this->appId = $this->httpConfig->getConfig(DefaultConfig::OFFICIAL_APPID, '');
|
||||
$this->mchId = $this->httpConfig->getConfig(DefaultConfig::PAY_MCHID, '');
|
||||
$this->serialNo = $this->httpConfig->getConfig('v3_pay.serial_no', '');
|
||||
$this->key = $this->httpConfig->getConfig('v3_pay.key', '');
|
||||
$this->isV3PAy = !!$this->httpConfig->getConfig('v3_pay.pay_type', false);
|
||||
$this->certPath = str_replace('//', '/', public_path() . $this->httpConfig->getConfig('pay.client_cert', ''));
|
||||
$this->keyPath = str_replace('//', '/', public_path() . $this->httpConfig->getConfig('pay.client_key', ''));
|
||||
$this->notifyUrl = trim($this->httpConfig->getConfig(DefaultConfig::COMMENT_URL)) . DefaultConfig::value('pay.notifyUrl');
|
||||
$this->refundUrl = trim($this->httpConfig->getConfig(DefaultConfig::COMMENT_URL)) . DefaultConfig::value('pay.refundUrl');
|
||||
$this->appAppid = $this->httpConfig->getConfig(DefaultConfig::APP_APPID, '');
|
||||
$this->webAppid = $this->httpConfig->getConfig(DefaultConfig::WEB_APPID, '');
|
||||
$this->wechatAppid = $this->httpConfig->getConfig(DefaultConfig::OFFICIAL_APPID, '');
|
||||
$this->miniprogAppid = $this->httpConfig->getConfig(DefaultConfig::MINI_APPID, '');
|
||||
$this->publicKey = $this->httpConfig->getConfig('v3_pay.public_key', '');
|
||||
$this->publicPem = str_replace('//', '/', public_path() . $this->httpConfig->getConfig('v3_pay.public_pem', ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2022/9/30
|
||||
*/
|
||||
public function all(): array
|
||||
{
|
||||
return [
|
||||
'app_id' => $this->appId,
|
||||
'serial_no' => $this->serialNo,
|
||||
'mch_id' => $this->mchId,
|
||||
'key' => $this->key,
|
||||
'cert_path' => $this->certPath,
|
||||
'key_path' => $this->keyPath,
|
||||
'notify_url' => $this->notifyUrl,
|
||||
'http' => $this->httpConfig->all(),
|
||||
'other' => [
|
||||
'wechat' => [
|
||||
'appid' => $this->wechatAppid,
|
||||
],
|
||||
'web' => [
|
||||
'appid' => $this->webAppid,
|
||||
],
|
||||
'app' => [
|
||||
'appid' => $this->appAppid,
|
||||
],
|
||||
'miniprog' => [
|
||||
'appid' => $this->miniprogAppid,
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
162
crmeb/services/wechat/config/WorkConfig.php
Normal file
162
crmeb/services/wechat/config/WorkConfig.php
Normal file
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace crmeb\services\wechat\config;
|
||||
|
||||
|
||||
use crmeb\services\wechat\contract\ConfigHandlerInterface;
|
||||
use crmeb\services\wechat\contract\WorkAppConfigHandlerInterface;
|
||||
use crmeb\services\wechat\DefaultConfig;
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
|
||||
/**
|
||||
* 企业微信配置
|
||||
* Class WorkConfig
|
||||
* @package crmeb\services\wechat\config
|
||||
*/
|
||||
class WorkConfig implements ConfigHandlerInterface
|
||||
{
|
||||
|
||||
//应用
|
||||
const TYPE_APP = 'app';
|
||||
//客户联系
|
||||
const TYPE_USER = 'user';
|
||||
//通讯录同步
|
||||
const TYPE_ADDRESS = 'address';
|
||||
//客服
|
||||
const TYPE_KEFU = 'kefu';
|
||||
//审批
|
||||
const TYPE_APPROVE = 'approve';
|
||||
//会议室
|
||||
const TYPE_MEETING = 'meeting';
|
||||
//自建应用
|
||||
const TYPE_USER_APP = 'build';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public string $corpId;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public string $token;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public string $aesKey;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $responseType = 'array';
|
||||
|
||||
/**
|
||||
* @var HttpCommonConfig
|
||||
*/
|
||||
protected HttpCommonConfig $httpConfig;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected bool $init = false;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $handler;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $appConfig;
|
||||
|
||||
/**
|
||||
* WorkConfig constructor.
|
||||
* @param HttpCommonConfig $commonConfig
|
||||
*/
|
||||
public function __construct(HttpCommonConfig $commonConfig)
|
||||
{
|
||||
$this->httpConfig = $commonConfig;
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/18
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
if ($this->init) {
|
||||
return;
|
||||
}
|
||||
$this->init = true;
|
||||
$this->corpId = $this->httpConfig->getConfig(DefaultConfig::WORK_CORP_ID, '');
|
||||
$this->token = $this->httpConfig->getConfig('work.token', '');
|
||||
$this->aesKey = $this->httpConfig->getConfig('work.key', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全部值
|
||||
* @return array
|
||||
*/
|
||||
#[ArrayShape([
|
||||
'corp_id' => "string",
|
||||
'token' => "string",
|
||||
'aes_key' => "string",
|
||||
'response_type' => "string",
|
||||
'log' => "array",
|
||||
'http' => "bool[]"
|
||||
])]
|
||||
#[Pure]
|
||||
public function all(): array
|
||||
{
|
||||
return [
|
||||
'corp_id' => $this->corpId,
|
||||
'token' => $this->token,
|
||||
'aes_key' => $this->aesKey,
|
||||
'response_type' => $this->responseType,
|
||||
'http' => $this->httpConfig->all()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用配置
|
||||
* @param string $type
|
||||
* @return array
|
||||
*/
|
||||
public function getAppConfig(string $type): array
|
||||
{
|
||||
if (!isset($this->appConfig[$type])) {
|
||||
/** @var WorkAppConfigHandlerInterface $make */
|
||||
$make = app()->make($this->handler);
|
||||
if (!$this->corpId) {
|
||||
$this->init();
|
||||
}
|
||||
$this->appConfig[$type] = $make->getAppConfig($this->corpId, $type);
|
||||
}
|
||||
return $this->appConfig[$type];
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置
|
||||
* @param string $handler
|
||||
* @return $this
|
||||
*/
|
||||
public function setHandler(string $handler): self
|
||||
{
|
||||
$this->handler = $handler;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
41
crmeb/services/wechat/contract/BaseApplicationInterface.php
Normal file
41
crmeb/services/wechat/contract/BaseApplicationInterface.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace crmeb\services\wechat\contract;
|
||||
|
||||
use EasyWeChat\Pay\Contracts\Application;
|
||||
use EasyWeChat\MiniApp\Contracts\Application as MiniAppApplication;
|
||||
use EasyWeChat\OfficialAccount\Contracts\Application as ApplicationInterface;
|
||||
use EasyWeChat\Work\Contracts\Application as WorkApplication;
|
||||
|
||||
/**
|
||||
* Interface BaseApplicationInterface
|
||||
* @package crmeb\services\wechat\contract
|
||||
*/
|
||||
interface BaseApplicationInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return static
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public static function instance(): static;
|
||||
|
||||
/**
|
||||
* @return ApplicationInterface|Application|MiniAppApplication|WorkApplication
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public function application(): ApplicationInterface|Application|MiniAppApplication|WorkApplication;
|
||||
}
|
||||
27
crmeb/services/wechat/contract/ConfigHandlerInterface.php
Normal file
27
crmeb/services/wechat/contract/ConfigHandlerInterface.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace crmeb\services\wechat\contract;
|
||||
|
||||
/**
|
||||
* 配置
|
||||
* Interface ConfigHandlerInterface
|
||||
* @package crmeb\services\wechat\contract
|
||||
*/
|
||||
interface ConfigHandlerInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* 获取全部
|
||||
* @return array
|
||||
*/
|
||||
public function all(): array;
|
||||
}
|
||||
28
crmeb/services/wechat/contract/ServeConfigInterface.php
Normal file
28
crmeb/services/wechat/contract/ServeConfigInterface.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace crmeb\services\wechat\contract;
|
||||
|
||||
/**
|
||||
* Interface ServeConfigInterface
|
||||
* @package crmeb\services\wechat\contract
|
||||
*/
|
||||
interface ServeConfigInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param null $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConfig(string $key, $default = null);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace crmeb\services\wechat\contract;
|
||||
|
||||
/**
|
||||
* 企业微信获取应用配置
|
||||
* Interface WorkAppConfigHandlerInterface
|
||||
* @package crmeb\services\wechat\contract
|
||||
*/
|
||||
interface WorkAppConfigHandlerInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* 获取应用配置
|
||||
* @param string $corpId
|
||||
* @param string $type 应用标识
|
||||
* @return array
|
||||
*/
|
||||
public function getAppConfig(string $corpId, string $type): array;
|
||||
|
||||
}
|
||||
58
crmeb/services/wechat/message/Article.php
Normal file
58
crmeb/services/wechat/message/Article.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\message;
|
||||
|
||||
|
||||
class Article extends Message
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $type = 'mpnews';
|
||||
|
||||
/**
|
||||
* Properties.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $properties = [
|
||||
'thumb_media_id',
|
||||
'author',
|
||||
'title',
|
||||
'content',
|
||||
'digest',
|
||||
'source_url',
|
||||
'show_cover',
|
||||
];
|
||||
|
||||
/**
|
||||
* Aliases of attribute.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $jsonAliases = [
|
||||
'content_source_url' => 'source_url',
|
||||
'show_cover_pic' => 'show_cover',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $required = [
|
||||
'thumb_media_id',
|
||||
'title',
|
||||
'content',
|
||||
'show_cover',
|
||||
];
|
||||
}
|
||||
249
crmeb/services/wechat/message/HasAttributes.php
Normal file
249
crmeb/services/wechat/message/HasAttributes.php
Normal file
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\message;
|
||||
|
||||
|
||||
use crmeb\services\wechat\WechatException;
|
||||
use EasyWeChat\Kernel\Support\Arr;
|
||||
use think\helper\Str;
|
||||
|
||||
trait HasAttributes
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $attributes = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected bool $snakeable = true;
|
||||
|
||||
/**
|
||||
* Set Attributes.
|
||||
*
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return HasAttributes
|
||||
*/
|
||||
public function setAttributes(array $attributes = []): self
|
||||
{
|
||||
$this->attributes = $attributes;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set attribute.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param string $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAttribute(string $attribute, string $value)
|
||||
{
|
||||
Arr::set($this->attributes, $attribute, $value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attribute.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed|null $default
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAttribute(string $attribute, mixed $default = null)
|
||||
{
|
||||
return Arr::get($this->attributes, $attribute, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $attribute
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isRequired(string $attribute)
|
||||
{
|
||||
return in_array($attribute, $this->getRequired(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|mixed
|
||||
*/
|
||||
public function getRequired(): mixed
|
||||
{
|
||||
return property_exists($this, 'required') ? $this->required : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set attribute.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function with(string $attribute, mixed $value)
|
||||
{
|
||||
$this->snakeable && $attribute = Str::snake($attribute);
|
||||
|
||||
$this->setAttribute($attribute, $value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override parent set() method.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function set(string $attribute, mixed $value)
|
||||
{
|
||||
$this->setAttribute($attribute, $value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override parent get() method.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed|null $default
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $attribute, mixed $default = null)
|
||||
{
|
||||
return $this->getAttribute($attribute, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has(string $key)
|
||||
{
|
||||
return Arr::has($this->attributes, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function merge(array $attributes)
|
||||
{
|
||||
$this->attributes = array_merge($this->attributes, $attributes);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|string $keys
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function only(array|string $keys)
|
||||
{
|
||||
return array_intersect_key($this->attributes, array_flip((array)$keys));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all items.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
$this->checkRequiredAttributes();
|
||||
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic call.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $args
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
if (0 === stripos($method, 'with')) {
|
||||
return $this->with(substr($method, 4), array_shift($args));
|
||||
}
|
||||
|
||||
throw new \BadMethodCallException(sprintf('Method "%s" does not exists.', $method));
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic get.
|
||||
*
|
||||
* @param string $property
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($property)
|
||||
{
|
||||
return $this->get($property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic set.
|
||||
*
|
||||
* @param string $property
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function __set($property, $value)
|
||||
{
|
||||
return $this->with($property, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not an data exists by key.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($key)
|
||||
{
|
||||
return isset($this->attributes[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check required attributes.
|
||||
*
|
||||
*/
|
||||
protected function checkRequiredAttributes()
|
||||
{
|
||||
foreach ($this->getRequired() as $attribute) {
|
||||
if (is_null($this->get($attribute))) {
|
||||
throw new WechatException(sprintf('"%s" cannot be empty.', $attribute));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
crmeb/services/wechat/message/Image.php
Normal file
31
crmeb/services/wechat/message/Image.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\message;
|
||||
|
||||
/**
|
||||
* 图片
|
||||
* Class Image
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
* @package crmeb\services\wechat\message
|
||||
*/
|
||||
class Image extends Media
|
||||
{
|
||||
/**
|
||||
* 消息类型
|
||||
* @var string
|
||||
*/
|
||||
protected string $type = 'image';
|
||||
}
|
||||
69
crmeb/services/wechat/message/Media.php
Normal file
69
crmeb/services/wechat/message/Media.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\message;
|
||||
|
||||
|
||||
use think\helper\Str;
|
||||
|
||||
class Media extends Message
|
||||
{
|
||||
/**
|
||||
* Properties.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $properties = ['media_id'];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $required = [
|
||||
'media_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* MaterialClient constructor.
|
||||
*
|
||||
* @param string $mediaId
|
||||
* @param string|null $type
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct(string $mediaId, string $type = null, array $attributes = [])
|
||||
{
|
||||
parent::__construct(array_merge(['media_id' => $mediaId], $attributes));
|
||||
|
||||
!empty($type) && $this->setType($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
public function getMediaId(): string
|
||||
{
|
||||
$this->checkRequiredAttributes();
|
||||
|
||||
return $this->get('media_id');
|
||||
}
|
||||
|
||||
public function toXmlArray()
|
||||
{
|
||||
return [
|
||||
Str::studly($this->getType()) => [
|
||||
'MediaId' => $this->get('media_id'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
217
crmeb/services/wechat/message/Message.php
Normal file
217
crmeb/services/wechat/message/Message.php
Normal file
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\message;
|
||||
|
||||
|
||||
|
||||
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
|
||||
use EasyWeChat\Kernel\Support\XML;
|
||||
|
||||
/**
|
||||
* 消息
|
||||
* Class Message
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
* @package crmeb\services\wechat\message
|
||||
*/
|
||||
class Message implements MessageInterface
|
||||
{
|
||||
use HasAttributes;
|
||||
|
||||
public const TEXT = 2;
|
||||
public const IMAGE = 4;
|
||||
public const VOICE = 8;
|
||||
public const VIDEO = 16;
|
||||
public const SHORT_VIDEO = 32;
|
||||
public const LOCATION = 64;
|
||||
public const LINK = 128;
|
||||
public const DEVICE_EVENT = 256;
|
||||
public const DEVICE_TEXT = 512;
|
||||
public const FILE = 1024;
|
||||
public const TEXT_CARD = 2048;
|
||||
public const TRANSFER = 4096;
|
||||
public const EVENT = 1048576;
|
||||
public const MINIPROGRAM_PAGE = 2097152;
|
||||
public const MINIPROGRAM_NOTICE = 4194304;
|
||||
public const ALL = self::TEXT | self::IMAGE | self::VOICE | self::VIDEO | self::SHORT_VIDEO | self::LOCATION | self::LINK
|
||||
| self::DEVICE_EVENT | self::DEVICE_TEXT | self::FILE | self::TEXT_CARD | self::TRANSFER | self::EVENT
|
||||
| self::MINIPROGRAM_PAGE | self::MINIPROGRAM_NOTICE;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $type;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected int $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $to;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $from;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $properties = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $jsonAliases = [];
|
||||
|
||||
/**
|
||||
* Message constructor.
|
||||
*
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct(array $attributes = [])
|
||||
{
|
||||
$this->setAttributes($attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return type name message.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
*/
|
||||
public function setType(string $type)
|
||||
{
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic getter.
|
||||
*
|
||||
* @param string $property
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($property)
|
||||
{
|
||||
if (property_exists($this, $property)) {
|
||||
return $this->$property;
|
||||
}
|
||||
|
||||
return $this->getAttribute($property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic setter.
|
||||
*
|
||||
* @param string $property
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return Message
|
||||
*/
|
||||
public function __set($property, $value)
|
||||
{
|
||||
if (property_exists($this, $property)) {
|
||||
$this->$property = $value;
|
||||
} else {
|
||||
$this->setAttribute($property, $value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $appends
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function transformForJsonRequestWithoutType(array $appends = [])
|
||||
{
|
||||
return $this->transformForJsonRequest($appends, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $appends
|
||||
* @param bool $withType
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function transformForJsonRequest(array $appends = [], bool $withType = true): array
|
||||
{
|
||||
if (!$withType) {
|
||||
return $this->propertiesToArray([], $this->jsonAliases);
|
||||
}
|
||||
$messageType = $this->getType();
|
||||
$data = array_merge(['msgtype' => $messageType], $appends);
|
||||
|
||||
$data[$messageType] = array_merge($data[$messageType] ?? [], $this->propertiesToArray([], $this->jsonAliases));
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $appends
|
||||
* @param bool $returnAsArray
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function transformToXml(array $appends = [], bool $returnAsArray = false)
|
||||
{
|
||||
$data = array_merge(['MsgType' => $this->getType()], $this->toXmlArray(), $appends);
|
||||
|
||||
return $returnAsArray ? $data : XML::build($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @param array $aliases
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
protected function propertiesToArray(array $data, array $aliases = []): array
|
||||
{
|
||||
$this->checkRequiredAttributes();
|
||||
|
||||
foreach ($this->attributes as $property => $value) {
|
||||
if (is_null($value) && !$this->isRequired($property)) {
|
||||
continue;
|
||||
}
|
||||
$alias = array_search($property, $aliases, true);
|
||||
|
||||
$data[$alias ?: $property] = $this->get($property);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function toXmlArray()
|
||||
{
|
||||
throw new \RuntimeException(sprintf('Class "%s" cannot support transform to XML message.', __CLASS__));
|
||||
}
|
||||
}
|
||||
35
crmeb/services/wechat/message/MessageInterface.php
Normal file
35
crmeb/services/wechat/message/MessageInterface.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\message;
|
||||
|
||||
|
||||
interface MessageInterface
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string;
|
||||
|
||||
/**
|
||||
* @param array $appends
|
||||
* @param bool $withType
|
||||
* @return array
|
||||
*/
|
||||
public function transformForJsonRequest(array $appends = [], bool $withType = true): array;
|
||||
|
||||
/**
|
||||
* @return string|array
|
||||
*/
|
||||
public function transformToXml();
|
||||
}
|
||||
77
crmeb/services/wechat/message/News.php
Normal file
77
crmeb/services/wechat/message/News.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\message;
|
||||
|
||||
|
||||
class News extends Message
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $type = 'news';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $properties = [
|
||||
'items',
|
||||
];
|
||||
|
||||
/**
|
||||
* News constructor.
|
||||
*
|
||||
* @param array $items
|
||||
*/
|
||||
public function __construct(array $items = [])
|
||||
{
|
||||
parent::__construct(compact('items'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @param array $aliases
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function propertiesToArray(array $data, array $aliases = []): array
|
||||
{
|
||||
return ['articles' => array_map(function ($item) {
|
||||
if ($item instanceof NewsItem) {
|
||||
return $item->toJsonArray();
|
||||
}
|
||||
}, $this->get('items'))];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/26
|
||||
*/
|
||||
public function toXmlArray(): array
|
||||
{
|
||||
$items = [];
|
||||
|
||||
foreach ($this->get('items') as $item) {
|
||||
if ($item instanceof NewsItem) {
|
||||
$items[] = $item->toXmlArray();
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'ArticleCount' => count($items),
|
||||
'Articles' => $items,
|
||||
];
|
||||
}
|
||||
}
|
||||
75
crmeb/services/wechat/message/NewsItem.php
Normal file
75
crmeb/services/wechat/message/NewsItem.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\message;
|
||||
|
||||
/**
|
||||
* Class NewsItem
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/26
|
||||
* @package crmeb\services\wechat\message
|
||||
*/
|
||||
class NewsItem extends Message
|
||||
{
|
||||
/**
|
||||
* Messages type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $type = 'news';
|
||||
|
||||
/**
|
||||
* Properties.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $properties = [
|
||||
'title',
|
||||
'description',
|
||||
'url',
|
||||
'image',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/26
|
||||
*/
|
||||
public function toJsonArray(): array
|
||||
{
|
||||
return [
|
||||
'title' => $this->get('title'),
|
||||
'description' => $this->get('description'),
|
||||
'url' => $this->get('url'),
|
||||
'picurl' => $this->get('image'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/26
|
||||
*/
|
||||
public function toXmlArray(): array
|
||||
{
|
||||
return [
|
||||
'Title' => $this->get('title'),
|
||||
'Description' => $this->get('description'),
|
||||
'Url' => $this->get('url'),
|
||||
'PicUrl' => $this->get('image'),
|
||||
];
|
||||
}
|
||||
}
|
||||
62
crmeb/services/wechat/message/Raw.php
Normal file
62
crmeb/services/wechat/message/Raw.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\message;
|
||||
|
||||
/**
|
||||
* Class Raw
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/26
|
||||
* @package crmeb\services\wechat\message
|
||||
*/
|
||||
class Raw extends Message
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $type = 'raw';
|
||||
|
||||
/**
|
||||
* Properties.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $properties = ['content'];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $content
|
||||
*/
|
||||
public function __construct(string $content)
|
||||
{
|
||||
parent::__construct(['content' => strval($content)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $appends
|
||||
* @param bool $withType
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function transformForJsonRequest(array $appends = [], bool $withType = true): array
|
||||
{
|
||||
return json_decode($this->content, true) ?? [];
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->get('content') ?? '';
|
||||
}
|
||||
}
|
||||
59
crmeb/services/wechat/message/Text.php
Normal file
59
crmeb/services/wechat/message/Text.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\message;
|
||||
|
||||
/**
|
||||
* text消息
|
||||
* Class Text
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
* @package crmeb\services\wechat\message
|
||||
*/
|
||||
class Text extends Message
|
||||
{
|
||||
/**
|
||||
* Message type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $type = 'text';
|
||||
|
||||
/**
|
||||
* Properties.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $properties = ['content'];
|
||||
|
||||
/**
|
||||
* Text constructor.
|
||||
*
|
||||
* @param string $content
|
||||
*/
|
||||
public function __construct(string $content = '')
|
||||
{
|
||||
parent::__construct(compact('content'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toXmlArray(): array
|
||||
{
|
||||
return [
|
||||
'Content' => $this->get('content'),
|
||||
];
|
||||
}
|
||||
}
|
||||
43
crmeb/services/wechat/message/TextCard.php
Normal file
43
crmeb/services/wechat/message/TextCard.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\message;
|
||||
|
||||
/**
|
||||
* 卡卷
|
||||
* Class TextCard
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
* @package crmeb\services\wechat\message
|
||||
*/
|
||||
class TextCard extends Message
|
||||
{
|
||||
/**
|
||||
* Messages type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $type = 'textcard';
|
||||
|
||||
/**
|
||||
* Properties.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $properties = [
|
||||
'title',
|
||||
'description',
|
||||
'url',
|
||||
];
|
||||
}
|
||||
59
crmeb/services/wechat/message/Transfer.php
Normal file
59
crmeb/services/wechat/message/Transfer.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\message;
|
||||
|
||||
|
||||
class Transfer extends Message
|
||||
{
|
||||
/**
|
||||
* Messages type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $type = 'transfer_customer_service';
|
||||
|
||||
/**
|
||||
* Properties.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $properties = [
|
||||
'account',
|
||||
];
|
||||
|
||||
/**
|
||||
* Transfer constructor.
|
||||
*
|
||||
* @param string|null $account
|
||||
*/
|
||||
public function __construct(string $account = null)
|
||||
{
|
||||
parent::__construct(compact('account'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/26
|
||||
*/
|
||||
public function toXmlArray(): array
|
||||
{
|
||||
return empty($this->get('account')) ? [] : [
|
||||
'TransInfo' => [
|
||||
'KfAccount' => $this->get('account'),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
66
crmeb/services/wechat/message/Video.php
Normal file
66
crmeb/services/wechat/message/Video.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\message;
|
||||
|
||||
|
||||
class Video extends Media
|
||||
{
|
||||
/**
|
||||
**
|
||||
* Messages type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $type = 'video';
|
||||
|
||||
/**
|
||||
* Properties.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $properties = [
|
||||
'title',
|
||||
'description',
|
||||
'media_id',
|
||||
'thumb_media_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* Video constructor.
|
||||
*
|
||||
* @param string $mediaId
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct(string $mediaId, array $attributes = [])
|
||||
{
|
||||
parent::__construct($mediaId, 'video', $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/26
|
||||
*/
|
||||
public function toXmlArray(): array
|
||||
{
|
||||
return [
|
||||
'Video' => [
|
||||
'MediaId' => $this->get('media_id'),
|
||||
'Title' => $this->get('title'),
|
||||
'Description' => $this->get('description'),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
35
crmeb/services/wechat/message/Voice.php
Normal file
35
crmeb/services/wechat/message/Voice.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\message;
|
||||
|
||||
|
||||
class Voice extends Media
|
||||
{
|
||||
/**
|
||||
* Messages type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $type = 'voice';
|
||||
|
||||
/**
|
||||
* Properties.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $properties = [
|
||||
'media_id',
|
||||
'recognition',
|
||||
];
|
||||
}
|
||||
95
crmeb/services/wechat/util/AES.php
Normal file
95
crmeb/services/wechat/util/AES.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\util;
|
||||
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* AES 加密解密
|
||||
* Class AES
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/18
|
||||
* @package crmeb\services\wechat\util
|
||||
*/
|
||||
class AES
|
||||
{
|
||||
/**
|
||||
* 加密
|
||||
* @param string $text
|
||||
* @param string $key
|
||||
* @param string $iv
|
||||
* @param int $option
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function encrypt(string $text, string $key, string $iv, int $option = OPENSSL_RAW_DATA): string
|
||||
{
|
||||
self::validateKey($key);
|
||||
self::validateIv($iv);
|
||||
|
||||
return openssl_encrypt($text, self::getMode($key), $key, $option, $iv);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
* @param string $cipherText
|
||||
* @param string $key
|
||||
* @param string $iv
|
||||
* @param int $option
|
||||
* @param string|null $method
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function decrypt(string $cipherText, string $key, string $iv, int $option = OPENSSL_RAW_DATA, $method = null): string
|
||||
{
|
||||
self::validateKey($key);
|
||||
self::validateIv($iv);
|
||||
|
||||
return openssl_decrypt($cipherText, $method ?: self::getMode($key), $key, $option, $iv);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getMode(string $key)
|
||||
{
|
||||
return 'aes-' . (8 * strlen($key)) . '-cbc';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*/
|
||||
public static function validateKey(string $key)
|
||||
{
|
||||
if (!in_array(strlen($key), [16, 24, 32], true)) {
|
||||
throw new InvalidArgumentException(sprintf('Key length must be 16, 24, or 32 bytes; got key len (%s).', strlen($key)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $iv
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function validateIv(string $iv)
|
||||
{
|
||||
if (!empty($iv) && 16 !== strlen($iv)) {
|
||||
throw new InvalidArgumentException('IV length must be 16 bytes.');
|
||||
}
|
||||
}
|
||||
}
|
||||
81
crmeb/services/wechat/util/Helper.php
Normal file
81
crmeb/services/wechat/util/Helper.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\util;
|
||||
|
||||
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* 帮助类
|
||||
* Class Helper
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/18
|
||||
* @package crmeb\services\wechat\util
|
||||
*/
|
||||
class Helper
|
||||
{
|
||||
/**
|
||||
* 获取加密方式
|
||||
* @param string $signType
|
||||
* @param string $secretKey
|
||||
* @return Closure|string
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/18
|
||||
*/
|
||||
public static function getEncryptMethod(string $signType, string $secretKey = ''): string|Closure
|
||||
{
|
||||
if ('HMAC-SHA256' === $signType) {
|
||||
return function ($str) use ($secretKey) {
|
||||
return hash_hmac('sha256', $str, $secretKey);
|
||||
};
|
||||
}
|
||||
|
||||
return 'md5';
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成签名
|
||||
* @param array $attributes
|
||||
* @param string $key
|
||||
* @param string $encryptMethod
|
||||
* @return string
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/14
|
||||
*/
|
||||
public static function generateSign(array $attributes, string $key, string $encryptMethod = 'md5'): string
|
||||
{
|
||||
ksort($attributes);
|
||||
|
||||
$attributes['key'] = $key;
|
||||
|
||||
return strtoupper(call_user_func_array($encryptMethod, [urldecode(http_build_query($attributes))]));
|
||||
}
|
||||
|
||||
/**
|
||||
* 截取数据
|
||||
* @param string $tel
|
||||
* @return array|string|string[]
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2023/9/15
|
||||
*/
|
||||
public static function encryptTel(string $tel)
|
||||
{
|
||||
return substr_replace($tel, '****', 3, 4);
|
||||
}
|
||||
|
||||
}
|
||||
166
crmeb/services/wechat/util/XML.php
Normal file
166
crmeb/services/wechat/util/XML.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\util;
|
||||
|
||||
|
||||
class XML
|
||||
{
|
||||
/**
|
||||
* XML to array.
|
||||
*
|
||||
* @param string $xml XML string
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function parse(string $xml)
|
||||
{
|
||||
$backup = PHP_MAJOR_VERSION < 8 ? libxml_disable_entity_loader(true) : null;
|
||||
|
||||
$result = self::normalize(simplexml_load_string(self::sanitize($xml), 'SimpleXMLElement', LIBXML_COMPACT | LIBXML_NOCDATA | LIBXML_NOBLANKS));
|
||||
|
||||
PHP_MAJOR_VERSION < 8 && libxml_disable_entity_loader($backup);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* XML encode.
|
||||
*
|
||||
* @param mixed $data
|
||||
* @param string $root
|
||||
* @param string $item
|
||||
* @param string $attr
|
||||
* @param string $id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build(
|
||||
array $data,
|
||||
string $root = 'xml',
|
||||
string $item = 'item',
|
||||
string $attr = '',
|
||||
string $id = 'id'
|
||||
): string
|
||||
{
|
||||
if (is_array($attr)) {
|
||||
$_attr = [];
|
||||
|
||||
foreach ($attr as $key => $value) {
|
||||
$_attr[] = "{$key}=\"{$value}\"";
|
||||
}
|
||||
|
||||
$attr = implode(' ', $_attr);
|
||||
}
|
||||
|
||||
$attr = trim($attr);
|
||||
$attr = empty($attr) ? '' : " {$attr}";
|
||||
$xml = "<{$root}{$attr}>";
|
||||
$xml .= self::data2Xml($data, $item, $id);
|
||||
$xml .= "</{$root}>";
|
||||
|
||||
return $xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build CDATA.
|
||||
*
|
||||
* @param string $string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function cdata(string $string)
|
||||
{
|
||||
return sprintf('<![CDATA[%s]]>', $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Object to array.
|
||||
*
|
||||
*
|
||||
* @param mixed $obj
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function normalize(mixed $obj)
|
||||
{
|
||||
$result = null;
|
||||
|
||||
if (is_object($obj)) {
|
||||
$obj = (array)$obj;
|
||||
}
|
||||
|
||||
if (is_array($obj)) {
|
||||
foreach ($obj as $key => $value) {
|
||||
$res = self::normalize($value);
|
||||
if (('@attributes' === $key) && ($key)) {
|
||||
$result = $res; // @codeCoverageIgnore
|
||||
} else {
|
||||
$result[$key] = $res;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$result = $obj;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Array to XML.
|
||||
*
|
||||
* @param array $data
|
||||
* @param string $item
|
||||
* @param string $id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function data2Xml(array $data, string $item = 'item', string $id = 'id')
|
||||
{
|
||||
$xml = $attr = '';
|
||||
|
||||
foreach ($data as $key => $val) {
|
||||
if (is_numeric($key)) {
|
||||
$id && $attr = " {$id}=\"{$key}\"";
|
||||
$key = $item;
|
||||
}
|
||||
|
||||
$xml .= "<{$key}{$attr}>";
|
||||
|
||||
if ((is_array($val) || is_object($val))) {
|
||||
$xml .= self::data2Xml((array)$val, $item, $id);
|
||||
} else {
|
||||
$xml .= is_numeric($val) ? $val : self::cdata($val);
|
||||
}
|
||||
|
||||
$xml .= "</{$key}>";
|
||||
}
|
||||
|
||||
return $xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete invalid characters in XML.
|
||||
*
|
||||
* @see https://www.w3.org/TR/2008/REC-xml-20081126/#charsets - XML charset range
|
||||
* @see http://php.net/manual/en/regexp.reference.escape.php - escape in UTF-8 mode
|
||||
*
|
||||
* @param string $xml
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function sanitize(string $xml)
|
||||
{
|
||||
return preg_replace('/[^\x{9}\x{A}\x{D}\x{20}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]+/u', '', $xml);
|
||||
}
|
||||
}
|
||||
281
crmeb/services/wechat/v3pay/BaseClient.php
Normal file
281
crmeb/services/wechat/v3pay/BaseClient.php
Normal file
@@ -0,0 +1,281 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\v3pay;
|
||||
|
||||
|
||||
use crmeb\exceptions\PayException;
|
||||
use crmeb\services\wechat\config\V3PaymentConfig;
|
||||
use crmeb\services\wechat\WechatException;
|
||||
use think\exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Class BaseClient
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2022/9/30
|
||||
* @package crmeb\services\wechat\v3pay
|
||||
*/
|
||||
class BaseClient
|
||||
{
|
||||
|
||||
use Certficates;
|
||||
|
||||
const BASE_URL = 'https://api.mch.weixin.qq.com/';
|
||||
|
||||
const AUTH_TAG_LENGTH_BYTE = 16;
|
||||
|
||||
/**
|
||||
* BaseClient constructor.
|
||||
* @param V3PaymentConfig $config
|
||||
*/
|
||||
public function __construct(protected V3PaymentConfig $config)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* request.
|
||||
*
|
||||
* @param string $endpoint
|
||||
* @param string $method
|
||||
* @param array $options
|
||||
* @param bool $serial
|
||||
* @return mixed
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
*/
|
||||
public function request(string $endpoint, string $method = 'POST', array $options = [], bool $serial = true)
|
||||
{
|
||||
$body = $options['body'] ?? '';
|
||||
|
||||
if (isset($options['json'])) {
|
||||
$body = json_encode($options['json']);
|
||||
$options['body'] = $body;
|
||||
unset($options['json']);
|
||||
}
|
||||
|
||||
$headers = [
|
||||
'Content-Type' => 'application/json',
|
||||
'User-Agent' => 'curl',
|
||||
'Accept' => 'application/json',
|
||||
'Authorization' => $this->getAuthorization($endpoint, $method, $body),
|
||||
];
|
||||
|
||||
$options['headers'] = array_merge($headers, ($options['headers'] ?? []));
|
||||
|
||||
if ($serial) {
|
||||
if ($this->config->publicKey != '') {
|
||||
$options['headers']['Wechatpay-Serial'] = $this->config->publicKey;
|
||||
} else {
|
||||
$options['headers']['Wechatpay-Serial'] = $this->getCertficatescAttr('serial_no');
|
||||
}
|
||||
}
|
||||
return $this->_doRequestCurl($method, self::BASE_URL . $endpoint, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
* @param string $location
|
||||
* @param array $options
|
||||
* @return mixed
|
||||
*/
|
||||
private function _doRequestCurl(string $method, string $location, array $options = [])
|
||||
{
|
||||
$curl = curl_init();
|
||||
// POST数据设置
|
||||
if (strtolower($method) === 'post') {
|
||||
curl_setopt($curl, CURLOPT_POST, true);
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $options['data'] ?? $options['body'] ?? '');
|
||||
}
|
||||
// CURL头信息设置
|
||||
if (!empty($options['headers'])) {
|
||||
$headers = [];
|
||||
foreach ($options['headers'] as $k => $v) {
|
||||
$headers[] = "$k: $v";
|
||||
}
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
||||
}
|
||||
curl_setopt($curl, CURLOPT_URL, $location);
|
||||
curl_setopt($curl, CURLOPT_HEADER, true);
|
||||
curl_setopt($curl, CURLOPT_TIMEOUT, 60);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
|
||||
$content = curl_exec($curl);
|
||||
$headerSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
|
||||
curl_close($curl);
|
||||
|
||||
return json_decode(substr($content, $headerSize), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* To id card, mobile phone number and other fields sensitive information encryption.
|
||||
*
|
||||
* @param string $string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function encryptSensitiveInformation(string $string)
|
||||
{
|
||||
$certificates = $this->config->certPath;
|
||||
if (null === $certificates) {
|
||||
throw new WechatException('config certificate connot be empty.');
|
||||
}
|
||||
$encrypted = '';
|
||||
if (openssl_public_encrypt($string, $encrypted, $certificates, OPENSSL_PKCS1_OAEP_PADDING)) {
|
||||
//base64编码
|
||||
$sign = base64_encode($encrypted);
|
||||
} else {
|
||||
throw new WechatException('Encryption of sensitive information failed');
|
||||
}
|
||||
return $sign;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param string $method
|
||||
* @param string $body
|
||||
* @return string
|
||||
*/
|
||||
protected function getAuthorization(string $url, string $method, string $body)
|
||||
{
|
||||
$nonceStr = uniqid();
|
||||
$timestamp = time();
|
||||
$message = $method . "\n" .
|
||||
'/' . $url . "\n" .
|
||||
$timestamp . "\n" .
|
||||
$nonceStr . "\n" .
|
||||
$body . "\n";
|
||||
openssl_sign($message, $raw_sign, $this->getPrivateKey(), 'sha256WithRSAEncryption');
|
||||
$sign = base64_encode($raw_sign);
|
||||
$schema = 'WECHATPAY2-SHA256-RSA2048 ';
|
||||
$token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
|
||||
$this->config->mchId, $nonceStr, $timestamp, $this->config->serialNo, $sign);
|
||||
return $schema . $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商户私钥
|
||||
* @return bool|resource
|
||||
*/
|
||||
protected function getPrivateKey()
|
||||
{
|
||||
$key_path = $this->config->keyPath;
|
||||
if (!file_exists($key_path)) {
|
||||
throw new \InvalidArgumentException(
|
||||
"SSL certificate not found: {$key_path}"
|
||||
);
|
||||
}
|
||||
return openssl_pkey_get_private(file_get_contents($key_path));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商户公钥
|
||||
* @return bool|resource
|
||||
*/
|
||||
protected function getPublicKey()
|
||||
{
|
||||
if ($this->config->publicKey != '') {
|
||||
$key_path = $this->config->publicPem;
|
||||
} else {
|
||||
$key_path = $this->config->certPath;
|
||||
}
|
||||
if (!file_exists($key_path)) {
|
||||
throw new \InvalidArgumentException(
|
||||
"SSL certificate not found: {$key_path}"
|
||||
);
|
||||
}
|
||||
return openssl_pkey_get_public(file_get_contents($key_path));
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换url
|
||||
* @param string $url
|
||||
* @param $search
|
||||
* @param $replace
|
||||
* @return array|string|string[]
|
||||
*/
|
||||
public function getApiUrl(string $url, $search, $replace)
|
||||
{
|
||||
$newSearch = [];
|
||||
foreach ($search as $key) {
|
||||
$newSearch[] = '{' . $key . '}';
|
||||
}
|
||||
return str_replace($newSearch, $replace, $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $padding
|
||||
*/
|
||||
private static function paddingModeLimitedCheck(int $padding): void
|
||||
{
|
||||
if (!($padding === OPENSSL_PKCS1_OAEP_PADDING || $padding === OPENSSL_PKCS1_PADDING)) {
|
||||
throw new PayException(sprintf("Doesn't supported padding mode(%d), here only support OPENSSL_PKCS1_OAEP_PADDING or OPENSSL_PKCS1_PADDING.", $padding));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密数据
|
||||
* @param string $plaintext
|
||||
* @param int $padding
|
||||
* @return string
|
||||
*/
|
||||
public function encryptor(string $plaintext, int $padding = OPENSSL_PKCS1_OAEP_PADDING)
|
||||
{
|
||||
self::paddingModeLimitedCheck($padding);
|
||||
|
||||
if (!openssl_public_encrypt($plaintext, $encrypted, $this->getPublicKey(), $padding)) {
|
||||
throw new PayException('Encrypting the input $plaintext failed, please checking your $publicKey whether or nor correct.');
|
||||
}
|
||||
|
||||
return base64_encode($encrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* decrypt ciphertext.
|
||||
*
|
||||
* @param array $encryptCertificate
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function decrypt(array $encryptCertificate)
|
||||
{
|
||||
$ciphertext = base64_decode($encryptCertificate['ciphertext'], true);
|
||||
$associatedData = $encryptCertificate['associated_data'];
|
||||
$nonceStr = $encryptCertificate['nonce'];
|
||||
$aesKey = $this->config->key;
|
||||
|
||||
try {
|
||||
// ext-sodium (default installed on >= PHP 7.2)
|
||||
if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') && \sodium_crypto_aead_aes256gcm_is_available()) {
|
||||
return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $aesKey);
|
||||
}
|
||||
// ext-libsodium (need install libsodium-php 1.x via pecl)
|
||||
if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && \Sodium\crypto_aead_aes256gcm_is_available()) {
|
||||
return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $aesKey);
|
||||
}
|
||||
// openssl (PHP >= 7.1 support AEAD)
|
||||
if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) {
|
||||
$ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE);
|
||||
$authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE);
|
||||
return \openssl_decrypt($ctext, 'aes-256-gcm', $aesKey, \OPENSSL_RAW_DATA, $nonceStr, $authTag, $associatedData);
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
throw new InvalidArgumentException($exception->getMessage(), $exception->getCode());
|
||||
} catch (\SodiumException $exception) {
|
||||
throw new InvalidArgumentException($exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
throw new InvalidArgumentException('AEAD_AES_256_GCM 需要 PHP 7.1 以上或者安装 libsodium-php');
|
||||
}
|
||||
}
|
||||
|
||||
71
crmeb/services/wechat/v3pay/Certficates.php
Normal file
71
crmeb/services/wechat/v3pay/Certficates.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\v3pay;
|
||||
|
||||
|
||||
use crmeb\exceptions\PayException;
|
||||
use crmeb\services\wechat\config\V3PaymentConfig;
|
||||
use Psr\SimpleCache\InvalidArgumentException;
|
||||
use think\facade\Cache;
|
||||
|
||||
/**
|
||||
* Class Certficates
|
||||
* @package crmeb\services\easywechat\v3pay
|
||||
* @property V3PaymentConfig $config
|
||||
*/
|
||||
trait Certficates
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string|null $key
|
||||
* @return array|mixed|null
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function getCertficatescAttr(string $key = null)
|
||||
{
|
||||
$driver = Cache::store('file');
|
||||
$cacheKey = '_wx_v3' . $this->config->serialNo;
|
||||
if ($driver->has($cacheKey)) {
|
||||
$res = $driver->get($cacheKey);
|
||||
if ($key && $res) {
|
||||
return $res[$key] ?? null;
|
||||
} else {
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
$certficates = $this->getCertficates();
|
||||
$driver->set($cacheKey, $certficates, 3600 * 24 * 30);
|
||||
if ($key && $certficates) {
|
||||
return $certficates[$key] ?? null;
|
||||
}
|
||||
return $certficates;
|
||||
}
|
||||
|
||||
/**
|
||||
* get certficates.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCertficates()
|
||||
{
|
||||
$response = $this->request('v3/certificates', 'GET', [], false);
|
||||
if (isset($response['code'])) {
|
||||
throw new PayException($response['message']);
|
||||
}
|
||||
$certificates = $response['data'][0];
|
||||
$certificates['certificates'] = $this->decrypt($certificates['encrypt_certificate']);
|
||||
unset($certificates['encrypt_certificate']);
|
||||
return $certificates;
|
||||
}
|
||||
}
|
||||
526
crmeb/services/wechat/v3pay/PayClient.php
Normal file
526
crmeb/services/wechat/v3pay/PayClient.php
Normal file
@@ -0,0 +1,526 @@
|
||||
<?php
|
||||
/**
|
||||
* +----------------------------------------------------------------------
|
||||
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
* +----------------------------------------------------------------------
|
||||
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
||||
* +----------------------------------------------------------------------
|
||||
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
* +----------------------------------------------------------------------
|
||||
* | Author: CRMEB Team <admin@crmeb.com>
|
||||
* +----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace crmeb\services\wechat\v3pay;
|
||||
|
||||
|
||||
use crmeb\exceptions\PayException;
|
||||
use crmeb\services\wechat\config\V3PaymentConfig;
|
||||
use crmeb\services\wechat\Payment;
|
||||
use Psr\SimpleCache\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* v3支付
|
||||
* Class PayClient
|
||||
* @package crmeb\services\easywechat\v3pay
|
||||
*/
|
||||
class PayClient extends BaseClient
|
||||
{
|
||||
//app支付
|
||||
const API_APP_APY_URL = 'v3/pay/transactions/app';
|
||||
//二维码支付截图
|
||||
const API_NATIVE_URL = 'v3/pay/transactions/native';
|
||||
//h5支付接口
|
||||
const API_H5_URL = 'v3/pay/transactions/h5';
|
||||
//jsapi支付接口
|
||||
const API_JSAPI_URL = 'v3/pay/transactions/jsapi';
|
||||
//发起商家转账API
|
||||
const API_BATCHES_URL = 'v3/transfer/batches';
|
||||
//退款
|
||||
const API_REFUND_URL = 'v3/refund/domestic/refunds';
|
||||
//退款查询接口
|
||||
const API_REFUND_QUERY_URL = 'v3/refund/domestic/refunds/{out_refund_no}';
|
||||
//发起转账
|
||||
const API_TRANSFER_BILLS_URL = 'v3/fund-app/mch-transfer/transfer-bills';
|
||||
//撤销转账
|
||||
const API_TRANSFER_BILLS_CANCEL_URL = '/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/{out_bill_no}/cancel';
|
||||
//查询转账
|
||||
const API_TRANSFER_QUERY_URL = 'v3/fund-app/mch-transfer/transfer-bills/out-bill-no/{out_bill_no}';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $type = Payment::WEB;
|
||||
protected string $notify_url = '';
|
||||
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @return $this
|
||||
* @author 等风来
|
||||
* @email 136327134@qq.com
|
||||
* @date 2022/11/18
|
||||
*/
|
||||
public function setType(string $type)
|
||||
{
|
||||
$this->type = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 公众号jsapi支付下单
|
||||
* @param string $openid
|
||||
* @param string $outTradeNo
|
||||
* @param string $total
|
||||
* @param string $description
|
||||
* @param string $attach
|
||||
* @return mixed
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function jsapiPay(string $openid, string $outTradeNo, string $total, string $description, string $attach)
|
||||
{
|
||||
$appId = $this->config->wechatAppid;
|
||||
$res = $this->pay('jsapi', $appId, $outTradeNo, $total, $description, $attach, ['openid' => $openid]);
|
||||
return $this->configForJSSDKPayment($appId, $res['prepay_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序支付
|
||||
* @param string $openid
|
||||
* @param string $outTradeNo
|
||||
* @param string $total
|
||||
* @param string $description
|
||||
* @param string $attach
|
||||
* @return array|false|string
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function miniprogPay(string $openid, string $outTradeNo, string $total, string $description, string $attach)
|
||||
{
|
||||
$appId = $this->config->miniprogAppid;
|
||||
$res = $this->pay('jsapi', $appId, $outTradeNo, $total, $description, $attach, ['openid' => $openid]);
|
||||
return $this->configForJSSDKPayment($appId, $res['prepay_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* APP支付下单
|
||||
* @param string $outTradeNo
|
||||
* @param string $total
|
||||
* @param string $description
|
||||
* @param string $attach
|
||||
* @return mixed
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function appPay(string $outTradeNo, string $total, string $description, string $attach)
|
||||
{
|
||||
$res = $this->pay('app', $this->config->appAppid, $outTradeNo, $total, $description, $attach);
|
||||
return $this->configForAppPayment($res['prepay_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* native支付下单
|
||||
* @param string $outTradeNo
|
||||
* @param string $total
|
||||
* @param string $description
|
||||
* @param string $attach
|
||||
* @return mixed
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function nativePay(string $outTradeNo, string $total, string $description, string $attach)
|
||||
{
|
||||
return $this->pay('native', $this->config->wechatAppid, $outTradeNo, $total, $description, $attach);
|
||||
}
|
||||
|
||||
/**
|
||||
* h5支付下单
|
||||
* @param string $outTradeNo
|
||||
* @param string $total
|
||||
* @param string $description
|
||||
* @param string $attach
|
||||
* @return mixed
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function h5Pay(string $outTradeNo, string $total, string $description, string $attach)
|
||||
{
|
||||
$res = $this->pay('h5', $this->config->wechatAppid, $outTradeNo, $total, $description, $attach);
|
||||
return ['mweb_url' => $res['h5_url']];
|
||||
}
|
||||
|
||||
/**
|
||||
* 下单
|
||||
* @param string $type
|
||||
* @param string $appid
|
||||
* @param string $outTradeNo
|
||||
* @param string $total
|
||||
* @param string $description
|
||||
* @param string $attach
|
||||
* @param array $payer
|
||||
* @return mixed
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function pay(string $type, string $appid, string $outTradeNo, string $total, string $description, string $attach, array $payer = [])
|
||||
{
|
||||
$totalFee = (int)bcmul($total, '100');
|
||||
|
||||
$data = [
|
||||
'appid' => $appid,
|
||||
'mchid' => $this->config->mchId,
|
||||
'out_trade_no' => $outTradeNo,
|
||||
'attach' => $attach,
|
||||
'description' => $description,
|
||||
'notify_url' => $this->config->notifyUrl,
|
||||
'amount' => [
|
||||
'total' => $totalFee,
|
||||
'currency' => 'CNY'
|
||||
],
|
||||
];
|
||||
|
||||
if ($payer) {
|
||||
$data['payer'] = $payer;
|
||||
}
|
||||
|
||||
$url = '';
|
||||
switch ($type) {
|
||||
case 'h5':
|
||||
$url = self::API_H5_URL;
|
||||
$data['scene_info'] = [
|
||||
'payer_client_ip' => request()->ip(),
|
||||
'h5_info' => [
|
||||
'type' => 'Wap'
|
||||
]
|
||||
];
|
||||
break;
|
||||
case 'native':
|
||||
$url = self::API_NATIVE_URL;
|
||||
break;
|
||||
case 'app':
|
||||
$url = self::API_APP_APY_URL;
|
||||
break;
|
||||
case 'jsapi':
|
||||
$url = self::API_JSAPI_URL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$url) {
|
||||
throw new PayException('缺少请求地址');
|
||||
}
|
||||
|
||||
$res = $this->request($url, 'POST', ['json' => $data]);
|
||||
|
||||
if (!$res) {
|
||||
throw new PayException('微信支付:下单失败');
|
||||
}
|
||||
if (isset($res['code']) && isset($res['message'])) {
|
||||
throw new PayException($res['message']);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起转账新接口(2025年1月15日升级)
|
||||
* @param string $outBatchNo
|
||||
* @param string $amount
|
||||
* @param string $userName
|
||||
* @param string $remark
|
||||
* @param array $transferDetailList
|
||||
* @return void
|
||||
* User: liusl
|
||||
* DateTime: 2025/2/14 上午11:20
|
||||
*/
|
||||
/**
|
||||
* @param string $outBatchNo
|
||||
* @param string $amount
|
||||
* @param $openid
|
||||
* @param string $userName
|
||||
* @param string $remark
|
||||
* @param string $perception
|
||||
* @param array $transferDetailList
|
||||
* @return mixed
|
||||
* @throws InvalidArgumentException
|
||||
* User: liusl
|
||||
* DateTime: 2025/2/14 下午2:14
|
||||
*/
|
||||
public function transferBills(string $outBatchNo, string $amount, $openid, string $userName, string $remark, string $perception, array $transferDetailList, string $transfer_scene_id = '1000')
|
||||
{
|
||||
$data = [
|
||||
'transfer_amount' => (int)bcmul($amount, 100, 0),
|
||||
'out_bill_no' => $outBatchNo,
|
||||
'transfer_scene_id' => $transfer_scene_id,
|
||||
'openid' => $openid,
|
||||
'transfer_remark' => $remark,
|
||||
'notify_url' => sys_config('site_url') . '/api/pay/mchNotify/'.$this->type,
|
||||
'user_recv_perception' => '现金奖励',
|
||||
'transfer_scene_report_infos' => $transferDetailList,
|
||||
];
|
||||
|
||||
if ($amount >= 2000) {
|
||||
if (empty($userName)) {
|
||||
throw new PayException('明细金额大于等于2000时,收款人姓名必须填写');
|
||||
}
|
||||
$data['user_name'] = $this->encryptor($userName);
|
||||
}
|
||||
|
||||
$appidMap = [
|
||||
Payment::WEB => $this->config->wechatAppid,
|
||||
Payment::MINI => $this->config->miniprogAppid,
|
||||
Payment::APP => $this->config->appAppid,
|
||||
];
|
||||
|
||||
if (!isset($appidMap[$this->type])) {
|
||||
throw new PayException('暂时只支持微信用户、小程序用户、APP微信登录用户提现');
|
||||
}
|
||||
|
||||
$data['appid'] = $appidMap[$this->type];
|
||||
$res = $this->request(self::API_TRANSFER_BILLS_URL, 'POST', ['json' => $data]);
|
||||
if (!$res || isset($res['code'], $res['message'])) {
|
||||
throw new PayException($res['message'] ?? '微信支付:发起商家转账失败');
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起商家转账API
|
||||
* @param string $outBatchNo
|
||||
* @param string $amount
|
||||
* @param string $batchName
|
||||
* @param string $remark
|
||||
* @param array $transferDetailList
|
||||
* @return mixed
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function batches(string $outBatchNo, string $amount, string $batchName, string $remark, array $transferDetailList)
|
||||
{
|
||||
$totalFee = '0';
|
||||
$amount = bcadd($amount, '0', 2);
|
||||
foreach ($transferDetailList as &$item) {
|
||||
if ($item['transfer_amount'] >= 2000 && !empty($item['user_name'])) {
|
||||
throw new PayException('明细金额大于等于2000时,收款人姓名必须填写');
|
||||
}
|
||||
$totalFee = bcadd($totalFee, $item['transfer_amount'], 2);
|
||||
$item['transfer_amount'] = (int)bcmul($item['transfer_amount'], 100, 0);
|
||||
if (isset($item['user_name'])) {
|
||||
$item['user_name'] = $this->encryptor($item['user_name']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($totalFee !== $amount) {
|
||||
throw new PayException('转账明细金额总和和转账总金额不一致');
|
||||
}
|
||||
|
||||
$amount = (int)bcmul($amount, 100, 0);
|
||||
|
||||
$appid = null;
|
||||
if ($this->type === Payment::WEB) {
|
||||
$appid = $this->config->wechatAppid;
|
||||
} else if ($this->type === Payment::MINI) {
|
||||
$appid = $this->config->miniprogAppid;
|
||||
} else if ($this->type === Payment::APP) {
|
||||
$appid = $this->config->appAppid;
|
||||
}
|
||||
|
||||
if (!$appid) {
|
||||
throw new PayException('暂时只支持微信用户、小程序用户、APP微信登录用户提现');
|
||||
}
|
||||
|
||||
$data = [
|
||||
'appid' => $appid,
|
||||
'out_batch_no' => $outBatchNo,
|
||||
'batch_name' => $batchName,
|
||||
'batch_remark' => $remark,
|
||||
'total_amount' => $amount,
|
||||
'total_num' => count($transferDetailList),
|
||||
'transfer_detail_list' => $transferDetailList
|
||||
];
|
||||
|
||||
$res = $this->request(self::API_BATCHES_URL, 'POST', ['json' => $data]);
|
||||
|
||||
if (!$res) {
|
||||
throw new PayException('微信支付:发起商家转账失败');
|
||||
}
|
||||
|
||||
if (isset($res['code']) && isset($res['message'])) {
|
||||
throw new PayException($res['message']);
|
||||
}
|
||||
|
||||
return $res;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款
|
||||
* @param string $outTradeNo
|
||||
* @param array $options
|
||||
* @return mixed
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function refund(string $outTradeNo, array $options = [])
|
||||
{
|
||||
if (!isset($options['pay_price'])) {
|
||||
throw new PayException(400730);
|
||||
}
|
||||
$totalFee = floatval(bcmul($options['pay_price'], 100, 0));
|
||||
$refundFee = isset($options['refund_price']) ? floatval(bcmul($options['refund_price'], 100, 0)) : null;
|
||||
$refundReason = $options['desc'] ?? '';
|
||||
$refundNo = $options['refund_id'] ?? $outTradeNo;
|
||||
/*仅针对老资金流商户使用
|
||||
REFUND_SOURCE_UNSETTLED_FUNDS---未结算资金退款(默认使用未结算资金退款)
|
||||
REFUND_SOURCE_RECHARGE_FUNDS---可用余额退款
|
||||
*/
|
||||
$refundAccount = $opt['refund_account'] ?? 'AVAILABLE';
|
||||
|
||||
$data = [
|
||||
'transaction_id' => $outTradeNo,
|
||||
'out_refund_no' => $refundNo,
|
||||
'amount' => [
|
||||
'refund' => (int)$refundFee,
|
||||
'currency' => 'CNY',
|
||||
'total' => (int)$totalFee
|
||||
],
|
||||
'funds_account' => $refundAccount
|
||||
];
|
||||
|
||||
if ($refundReason) {
|
||||
$data['reason'] = $refundReason;
|
||||
}
|
||||
|
||||
$res = $this->request(self::API_REFUND_URL, 'POST', ['json' => $data]);
|
||||
|
||||
if (!$res) {
|
||||
throw new PayException('微信支付:发起退款失败');
|
||||
}
|
||||
|
||||
if (isset($res['code']) && isset($res['message'])) {
|
||||
throw new PayException($res['message']);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询退款
|
||||
* @param string $outRefundNo
|
||||
* @return mixed
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function queryRefund(string $outRefundNo)
|
||||
{
|
||||
$res = $this->request($this->getApiUrl(self::API_REFUND_QUERY_URL, ['out_refund_no'], [$outRefundNo]), 'GET');
|
||||
|
||||
if (!$res) {
|
||||
throw new PayException(500000);
|
||||
}
|
||||
|
||||
if (isset($res['code']) && isset($res['message'])) {
|
||||
throw new PayException($res['message']);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* jsapi支付
|
||||
* @param string $appid
|
||||
* @param string $prepayId
|
||||
* @param bool $json
|
||||
* @return array|false|string
|
||||
*/
|
||||
public function configForPayment(string $appid, string $prepayId, bool $json = true)
|
||||
{
|
||||
$params = [
|
||||
'appId' => $appid,
|
||||
'timeStamp' => strval(time()),
|
||||
'nonceStr' => uniqid(),
|
||||
'package' => "prepay_id=$prepayId",
|
||||
'signType' => 'RSA',
|
||||
];
|
||||
$message = $params['appId'] . "\n" .
|
||||
$params['timeStamp'] . "\n" .
|
||||
$params['nonceStr'] . "\n" .
|
||||
$params['package'] . "\n";
|
||||
openssl_sign($message, $raw_sign, $this->getPrivateKey(), 'sha256WithRSAEncryption');
|
||||
$sign = base64_encode($raw_sign);
|
||||
|
||||
$params['paySign'] = $sign;
|
||||
|
||||
return $json ? json_encode($params) : $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate app payment parameters.
|
||||
* @param string $prepayId
|
||||
* @return array
|
||||
*/
|
||||
public function configForAppPayment(string $prepayId): array
|
||||
{
|
||||
$params = [
|
||||
'appid' => $this->config->wechatAppid,
|
||||
'partnerid' => $this->config->mchId,
|
||||
'prepayid' => $prepayId,
|
||||
'noncestr' => uniqid(),
|
||||
'timestamp' => time(),
|
||||
'package' => 'Sign=WXPay',
|
||||
];
|
||||
$message = $params['appid'] . "\n" .
|
||||
$params['timestamp'] . "\n" .
|
||||
$params['noncestr'] . "\n" .
|
||||
$params['prepayid'] . "\n";
|
||||
openssl_sign($message, $raw_sign, $this->getPrivateKey(), 'sha256WithRSAEncryption');
|
||||
$sign = base64_encode($raw_sign);
|
||||
|
||||
$params['sign'] = $sign;
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序支付
|
||||
* @param string $appid
|
||||
* @param string $prepayId
|
||||
* @return array|false|string
|
||||
*/
|
||||
public function configForJSSDKPayment(string $appid, string $prepayId)
|
||||
{
|
||||
$config = $this->configForPayment($appid, $prepayId, false);
|
||||
|
||||
$config['timestamp'] = $config['timeStamp'];
|
||||
unset($config['timeStamp']);
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $callback
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function handleNotify($callback)
|
||||
{
|
||||
$request = request();
|
||||
$event_type = $request->post('event_type');
|
||||
|
||||
// 判断成功事件
|
||||
$successEvents = ['TRANSACTION.SUCCESS', 'MCHTRANSFER.BILL.FINISHED'];
|
||||
$success = in_array($event_type, $successEvents);
|
||||
|
||||
$data = $this->decrypt($request->post('resource', []));
|
||||
$handleResult = call_user_func_array($callback, [json_decode($data, true), $success]);
|
||||
|
||||
$response = [
|
||||
'code' => $handleResult === true ? 'SUCCESS' : 'FAIL',
|
||||
'message' => $handleResult === true ? 'OK' : $handleResult,
|
||||
];
|
||||
|
||||
return response($response, 200, [], 'json');
|
||||
}
|
||||
|
||||
public function queryTransferBills(string $outBillNo)
|
||||
{
|
||||
$res = $this->request($this->getApiUrl(self::API_TRANSFER_QUERY_URL, ['out_bill_no'], [$outBillNo]), 'GET');
|
||||
|
||||
if (!$res) {
|
||||
throw new PayException(500000);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user